commit 9314ffb79d1e9ea06a9b8345b6a0e3c8fdd5f4ec Author: unknown Date: Tue Feb 8 16:49:56 2022 -0500 Initial commit diff --git a/.github/workflows/build-eboot-and-release.yml b/.github/workflows/build-eboot-and-release.yml new file mode 100644 index 0000000..04343b5 --- /dev/null +++ b/.github/workflows/build-eboot-and-release.yml @@ -0,0 +1,69 @@ +name: Build EBOOTs and Publish Release +on: [push] +jobs: + Compile-EBOOTs: + runs-on: ubuntu-latest + container: + image: bmaupin/pspdev + steps: + - uses: actions/checkout@v2 + - name: Get apt ready + run: | + apt update + apt install -y zip + - name: Build + working-directory: ./ + run: | + cd source/libpspmath + make && make install + cd ../../ + make -f MakePHAT install + make -f MakePHAT clean + make -f MakeSLIM install + - name: Generate Build Date + id: date + run: echo "::set-output name=date::$(date +'%Y-%m-%d-%H-%M-%S')" + - name: Zip EBOOTs + working-directory: ./build + run: | + zip -r psp-nzp-eboots.zip EBOOT.PBP EBOOT2000.PBP + - name: Delete Old Release + uses: dev-drprasad/delete-tag-and-release@v0.2.0 + with: + delete_release: true + tag_name: bleeding-edge + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: bleeding-edge + release_name: Automated Release ${{ steps.date.outputs.date }} + body: | + This is a **bleeding edge** NZ:P PSP EBOOT release, stability is not guarenteed. + + To install: + - Grab the .ZIP archive (psp-nzp-eboots.zip) + - Extract the contents of the .ZIP archive to `PSP/GAME/nzportable`. + - If on PSP SLIM, delete `EBOOT.PBP` and rename `EBOOT2000.PBP` to `EBOOT.PBP`. + draft: true + prerelease: false + - name: Upload EBOOT Archive + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./build/psp-nzp-eboots.zip + asset_name: psp-nzp-eboots.zip + asset_content_type: application/zip + - name: Publish Release + uses: StuYarrow/publish-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + id: ${{ steps.create_release.outputs.id }} + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f1b9771 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +build/* +*.o +*.elf +*.PBP +*.prx +*.SFO +PARAM.SFO +EBOOT.PBP +EBOOT2000.PBP diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bf96377 --- /dev/null +++ b/LICENSE @@ -0,0 +1,87 @@ +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +Preamble +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + +a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + +b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + +c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + +a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/MakePHAT b/MakePHAT new file mode 100644 index 0000000..5b26406 --- /dev/null +++ b/MakePHAT @@ -0,0 +1,134 @@ +PSPSDK = $(shell psp-config --pspsdk-path) +PSPLIBSDIR = $(PSPSDK)/.. + +TARGET = nzportable + +PSP_EBOOT_TITLE = Nazi Zombies Portable Reboot + +PSP_EBOOT_ICON = source/psp/pics/icon.png +PSP_EBOOT_SND0 = source/psp/pics/snd0.at3 +PSP_EBOOT_PIC1 = source/psp/pics/pic.png + +PSP_FW_VERSION=660 + +MODE=-DKERNEL_MODE + + +BUILD_PRX = 1 + +COMMON_OBJS = \ + source/psp/battery.o \ + source/thread.o \ + source/psp/VramExt.o \ + source/psp/input.o \ + source/psp/main.o \ + source/psp/math.o \ + source/psp/sound.o \ + source/psp/system.o \ + source/psp/module.o \ + source/psp/network.o \ + source/psp/network_psp.o \ + source/psp/gethost.o \ + source/psp/fnmatch.o \ + source/psp/cd.o \ + source/psp/mp3.o \ + source/psp/Random.o \ + \ + source/chase.o \ + source/cl_demo.o \ + source/cl_input.o \ + source/cl_main.o \ + source/cl_parse.o \ + source/cl_tent.o \ + source/cl_slist.o \ + source/cmd.o \ + source/common.o \ + source/console.o \ + source/crc.o \ + source/cvar.o \ + source/host.o \ + source/host_cmd.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/snd_dma.o \ + source/snd_mem.o \ + source/snd_mix.o \ + source/cl_hud.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/crypter.o + +HARDWARE_VIDEO_ONLY_OBJS = \ + source/psp/wad3.o \ + source/psp/clipping.o \ + source/psp/vram.o \ + source/psp/video_hardware.o \ + source/psp/video_hardware_resample.o \ + source/psp/video_hardware_images.o \ + source/psp/video_hardware_fullbright.o \ + source/psp/video_hardware_hlmdl.o \ + source/psp/video_hardware_draw.o \ + source/psp/video_hardware_entity_fragment.o \ + source/psp/video_hardware_QMB.o \ + source/psp/video_hardware_decals_QMB.o \ + source/psp/video_hardware_part.o \ + source/psp/video_hardware_light.o \ + source/psp/video_hardware_main.o \ + source/psp/video_hardware_mesh.o \ + source/psp/video_hardware_mhex2.o \ + source/psp/video_hardware_misc.o \ + source/psp/video_hardware_model.o \ + source/psp/video_hardware_screen.o \ + source/psp/video_hardware_surface.o \ + source/psp/video_hardware_warp.o \ + source/psp/video_hardware_fog.o \ + source/psp/video_hardware_dxtn.o +HARDWARE_VIDEO_ONLY_FLAGS = -DPSP_HARDWARE_VIDEO + + + +OBJS = $(COMMON_OBJS) $(HARDWARE_VIDEO_ONLY_OBJS) + + +#LIBS = -lpspaudiolib -lpspaudio -lpspgum -lpspgu -lpsprtc -lpsppower -lpspwlan -lstdc++ -lm + +GU_LIBS = -lpspgum_vfpu -lpspvfpu -lpspgu -lpspvram +AUDIO_LIBS = -lpspaudiolib -lpspaudio -lpspmp3 source/psp/m33libs/libpspaudiocodec.a source/psp/m33libs/libpspkubridge.a + +MISC_LIBS = -lpsprtc -lpspmath -lpsppower -lpsphprm -ljpeg -lpng source/psp/m33libs/libz.a source/psp/iridlibs/libPerf.a +NET_LIBS = -lpspwlan -lpspnet_adhoc -lpspnet_adhocctl +STD_LIBS = -lstdc++ -lm -lc +LIBS = $(GU_LIBS) $(AUDIO_LIBS) $(MISC_LIBS) $(STD_LIBS) $(NET_LIBS) + +CFLAGS = -ffast-math -O3 -G0 -Wall -Did386="0" -DPSP $(MODE) $(HARDWARE_VIDEO_ONLY_FLAGS) -DSWIZZLE32 -DPSP_MP3_HWDECODE -DFULLBRIGHT -DHL_RENDER -Wno-strict-aliasing -DPSP_VFPU +CXXFLAGS = -fno-rtti -Wcast-qual -Wno-write-strings -Wno-sign-compare -Wno-strict-aliasing +ASFLAGS = $(CFLAGS) -c + +include $(PSPSDK)/lib/build.mak + +ifneq ($(VS_PATH),) +CC = vs-psp-gcc +CXX = vs-psp-g++ +endif + +install: EBOOT.PBP + mkdir -p build/exec/ + mv EBOOT.PBP build/ + mv *.elf build/exec/ + mv *.prx build/exec/ + mv *.SFO build/exec/ + @echo DONE diff --git a/MakeSLIM b/MakeSLIM new file mode 100644 index 0000000..4e54e19 --- /dev/null +++ b/MakeSLIM @@ -0,0 +1,136 @@ +PSPSDK = $(shell psp-config --pspsdk-path) +PSPLIBSDIR = $(PSPSDK)/.. + +TARGET = nzportable + +PSP_EBOOT_TITLE = Nazi Zombies Portable Reboot + +PSP_EBOOT_ICON = source/psp/pics/icon.png +PSP_EBOOT_SND0 = source/psp/pics/snd0.at3 +PSP_EBOOT_PIC1 = source/psp/pics/pic.png + +PSP_FW_VERSION=660 + +PSP_LARGE_MEMORY = 1 + +MODE=-DKERNEL_MODE + + +BUILD_PRX = 1 + +COMMON_OBJS = \ + source/psp/battery.o \ + source/thread.o \ + source/psp/VramExt.o \ + source/psp/input.o \ + source/psp/main.o \ + source/psp/math.o \ + source/psp/sound.o \ + source/psp/system.o \ + source/psp/module.o \ + source/psp/network.o \ + source/psp/network_psp.o \ + source/psp/gethost.o \ + source/psp/fnmatch.o \ + source/psp/cd.o \ + source/psp/mp3.o \ + source/psp/Random.o \ + \ + source/chase.o \ + source/cl_demo.o \ + source/cl_input.o \ + source/cl_main.o \ + source/cl_parse.o \ + source/cl_tent.o \ + source/cl_slist.o \ + source/cmd.o \ + source/common.o \ + source/console.o \ + source/crc.o \ + source/cvar.o \ + source/host.o \ + source/host_cmd.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/snd_dma.o \ + source/snd_mem.o \ + source/snd_mix.o \ + source/cl_hud.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/crypter.o + +HARDWARE_VIDEO_ONLY_OBJS = \ + source/psp/wad3.o \ + source/psp/clipping.o \ + source/psp/vram.o \ + source/psp/video_hardware.o \ + source/psp/video_hardware_resample.o \ + source/psp/video_hardware_images.o \ + source/psp/video_hardware_fullbright.o \ + source/psp/video_hardware_hlmdl.o \ + source/psp/video_hardware_draw.o \ + source/psp/video_hardware_entity_fragment.o \ + source/psp/video_hardware_QMB.o \ + source/psp/video_hardware_decals_QMB.o \ + source/psp/video_hardware_part.o \ + source/psp/video_hardware_light.o \ + source/psp/video_hardware_main.o \ + source/psp/video_hardware_mesh.o \ + source/psp/video_hardware_mhex2.o \ + source/psp/video_hardware_misc.o \ + source/psp/video_hardware_model.o \ + source/psp/video_hardware_screen.o \ + source/psp/video_hardware_surface.o \ + source/psp/video_hardware_warp.o \ + source/psp/video_hardware_fog.o \ + source/psp/video_hardware_dxtn.o +HARDWARE_VIDEO_ONLY_FLAGS = -DPSP_HARDWARE_VIDEO + + + +OBJS = $(COMMON_OBJS) $(HARDWARE_VIDEO_ONLY_OBJS) + + +#LIBS = -lpspaudiolib -lpspaudio -lpspgum -lpspgu -lpsprtc -lpsppower -lpspwlan -lstdc++ -lm + +GU_LIBS = -lpspgum_vfpu -lpspvfpu -lpspgu -lpspvram +AUDIO_LIBS = -lpspaudiolib -lpspaudio -lpspmp3 source/psp/m33libs/libpspaudiocodec.a source/psp/m33libs/libpspkubridge.a + +MISC_LIBS = -lpsprtc -lpsppower -lpspmath -lpsphprm -ljpeg -lpng source/psp/m33libs/libz.a source/psp/iridlibs/libPerf.a +NET_LIBS = -lpspwlan -lpspnet_adhoc -lpspnet_adhocctl +STD_LIBS = -lstdc++ -lm -lc +LIBS = $(GU_LIBS) $(AUDIO_LIBS) $(MISC_LIBS) $(STD_LIBS) $(NET_LIBS) + +CFLAGS = -ffast-math -O3 -G0 -Wall -Did386="0" -DPSP $(MODE) $(HARDWARE_VIDEO_ONLY_FLAGS) -DSWIZZLE32 -DSLIM -DPSP_MP3_HWDECODE -DFULLBRIGHT -DHL_RENDER -Wno-strict-aliasing -DPSP_VFPU +CXXFLAGS = -fno-rtti -Wcast-qual -Wno-write-strings -Wno-sign-compare -Wno-strict-aliasing +ASFLAGS = $(CFLAGS) -c + +include $(PSPSDK)/lib/build.mak + +ifneq ($(VS_PATH),) +CC = vs-psp-gcc +CXX = vs-psp-g++ +endif + +install: EBOOT.PBP + mkdir -p build/exec/ + mv EBOOT.PBP build/EBOOT2000.PBP + mv *.elf build/exec/ + mv *.prx build/exec/ + mv *.SFO build/exec/ + @echo DONE diff --git a/README.md b/README.md new file mode 100644 index 0000000..555c261 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Nazi Zombies: Portable dQuakePlus + +## About +This repository contains the PSP engine for NZ:P, based on dQuakePlus and containing optimizations from the NZ:P Team, adQuake, and Xash3D-PSP, as well as NZ:P-specific feature implementation. It has also been modified to build on the latest versions of the [PSPSDK](https://github.com/pspdev/pspsdk). + +## Building (Advanced) +Building requires a full install of [psptoolchain](https://github.com/pspdev/psptoolchain/). You can either follow the instructions on the GitHub repository or use a Docker container (we recommend [bmaupin](https://hub.docker.com/r/bmaupin/pspdev)'s)! + +With the psptoolchain installed, you now need to install `libpspmath`, which we have included in the GitHub repository: +```bash +cd source/libpspmath +make && make install +``` +Now you can navigate back to the root of the repository and build an `EBOOT`. + +```bash +cd ../../ +make -f MakePHAT install # for PSP PHAT/1000 +make -f MakeSLIM install # for any other model +``` + +We also provide prebuilt EBOOTs on the [Releases](https://github.com/nzp-team/dquakeplus/releases/tag/bleeding-edge) page. \ No newline at end of file diff --git a/source/anorm_dots.h b/source/anorm_dots.h new file mode 100644 index 0000000..2845fa2 --- /dev/null +++ b/source/anorm_dots.h @@ -0,0 +1,37 @@ +/* +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} +} diff --git a/source/anorms.h b/source/anorms.h new file mode 100644 index 0000000..11a9007 --- /dev/null +++ b/source/anorms.h @@ -0,0 +1,181 @@ +/* +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}, diff --git a/source/bspfile.h b/source/bspfile.h new file mode 100644 index 0000000..6751d8f --- /dev/null +++ b/source/bspfile.h @@ -0,0 +1,326 @@ +/* +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 8192 +#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 BSPVERSION 29 +#define HL_BSPVERSION 30 +#define NZP_BSPVERSION 1337 +#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 +} dnode_t; + +typedef struct +{ + int planenum; + short children[2]; // negative numbers are contents +} dclipnode_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 +} dedge_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 +} dface_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]; +} dleaf_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, char *key, char *value); +char *ValueForKey (entity_t *ent, char *key); +// will return "" if not present + +vec_t FloatForKey (entity_t *ent, char *key); +void GetVectorForKey (entity_t *ent, char *key, vec3_t vec); + +epair_t *ParseEpair (void); + +#endif diff --git a/source/cdaudio.h b/source/cdaudio.h new file mode 100644 index 0000000..5b30c47 --- /dev/null +++ b/source/cdaudio.h @@ -0,0 +1,31 @@ +/* +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, qboolean looping); +void CDAudio_Stop(void); +void CDAudio_Pause(void); +void CDAudio_Resume(void); +void CDAudio_Shutdown(void); +void CDAudio_Update(void); +void CDAudio_Next(void); +void CDAudio_Prev(void); +void CDAudio_PrintMusicList(void); +void CDAudio_Track(char* trackname); diff --git a/source/chase.c b/source/chase.c new file mode 100644 index 0000000..92f98ef --- /dev/null +++ b/source/chase.c @@ -0,0 +1,182 @@ +/* +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_t chase_back = {"chase_back", "100"}; +cvar_t chase_up = {"chase_up", "16"}; +cvar_t chase_right = {"chase_right", "0"}; +cvar_t chase_active = {"chase_active", "0"}; +cvar_t chase_roll = {"chase_roll", "0"}; +cvar_t chase_yaw = {"chase_yaw", "0"}; +cvar_t chase_pitch = {"chase_pitch", "0"}; + +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); + Cvar_RegisterVariable (&chase_roll); + Cvar_RegisterVariable (&chase_yaw); + Cvar_RegisterVariable (&chase_pitch); +} + +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); +} + +/* +#define NUM_TESTS 64 (delete) +#define CHASE_DEST_OFFSET 2.0f +*/ + +qboolean chase_nodraw; + +#define NUM_TESTS 64 +#define CHASE_DEST_OFFSET 2.0f + +void Chase_Update (void) +{ + int i; + float dist; + vec3_t forward, up, right; + vec3_t dest, stop; + int best; + int viewcontents; + + // 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; + + // take the contents of the view leaf + viewcontents = (Mod_PointInLeaf (r_refdef.vieworg, cl.worldmodel))->contents; + + for (best = 0; best < NUM_TESTS; best++) + { + float chase_newdest[3]; + + chase_newdest[0] = r_refdef.vieworg[0] + (chase_dest[0] - r_refdef.vieworg[0]) * best / NUM_TESTS; + chase_newdest[1] = r_refdef.vieworg[1] + (chase_dest[1] - r_refdef.vieworg[1]) * best / NUM_TESTS; + chase_newdest[2] = r_refdef.vieworg[2] + (chase_dest[2] - r_refdef.vieworg[2]) * best / NUM_TESTS; + + // check for a leaf hit with different contents + if ((Mod_PointInLeaf (chase_newdest, cl.worldmodel))->contents != viewcontents) + { + // go back to the previous best as this one is bad + // unless the first one was also bad, (viewleaf contents != viewleaf contents!!!) + if (best > 0) + best--; + else best = NUM_TESTS; + break; + } + } + + // certain surfaces can be viewed at an oblique enough angle that they are partially clipped + // by znear, so now we fix that too... + for (; best >= 0; best--) + { + // number of matches + int nummatches = 0; + + // adjust + chase_dest[0] = r_refdef.vieworg[0] + (chase_dest[0] - r_refdef.vieworg[0]) * best / NUM_TESTS; + chase_dest[1] = r_refdef.vieworg[1] + (chase_dest[1] - r_refdef.vieworg[1]) * best / NUM_TESTS; + chase_dest[2] = r_refdef.vieworg[2] + (chase_dest[2] - r_refdef.vieworg[2]) * best / NUM_TESTS; + + // move x to neg + chase_dest[0] -= CHASE_DEST_OFFSET; + if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++; + chase_dest[0] += CHASE_DEST_OFFSET; + + // move x to pos + chase_dest[0] += CHASE_DEST_OFFSET; + if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++; + chase_dest[0] -= CHASE_DEST_OFFSET; + + // move y to neg + chase_dest[1] -= CHASE_DEST_OFFSET; + if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++; + chase_dest[1] += CHASE_DEST_OFFSET; + + // move y to pos + chase_dest[1] += CHASE_DEST_OFFSET; + if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++; + chase_dest[1] -= CHASE_DEST_OFFSET; + + // move z to neg + chase_dest[2] -= CHASE_DEST_OFFSET; + if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++; + chase_dest[2] += CHASE_DEST_OFFSET; + + // move z to pos + chase_dest[2] += CHASE_DEST_OFFSET; + if ((Mod_PointInLeaf (chase_dest, cl.worldmodel))->contents == viewcontents) nummatches++; + chase_dest[2] -= CHASE_DEST_OFFSET; + + // all tests passed so we're good! + if (nummatches == 6) break; + } + + // 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; + + #ifdef PSP_VFPU + r_refdef.viewangles[PITCH] = -vfpu_atanf(stop[2] / dist) / M_PI * 180; + #else + r_refdef.viewangles[PITCH] = -atan(stop[2] / dist) / M_PI * 180; + #endif + + // move towards destination + VectorCopy (chase_dest, r_refdef.vieworg); +} + diff --git a/source/cl_demo.c b/source/cl_demo.c new file mode 100644 index 0000000..15c3c68 --- /dev/null +++ b/source/cl_demo.c @@ -0,0 +1,380 @@ +/* +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" + +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; + + Sys_FileClose(cls.demofile); + cls.demoplayback = false; + cls.demofile = -1; + cls.state = ca_disconnected; + + if (cls.timedemo) + 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); + Sys_FileWrite(cls.demofile, &len, 4); + for (i=0 ; i<3 ; i++) + { + f = LittleFloat (cl.viewangles[i]); + Sys_FileWrite(cls.demofile, &f, 4); + } + Sys_FileWrite(cls.demofile, net_message.data, net_message.cursize); +} + +/* +==================== +CL_GetMessage + +Handles recording and playback of demos, on top of NET_ code +==================== +*/ +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) // allways grab until fully connected + { + if (cls.timedemo) + { + if (host_framecount == cls.td_lastframe) + return 0; // allready 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) + cls.td_starttime = realtime; + } + else if ( /* cl.time > 0 && */ cl.time <= cl.mtime[0]) + { + return 0; // don't need another message yet + } + } + + // get the next message + Sys_FileRead(cls.demofile, &net_message.cursize, 4); + VectorCopy (cl.mviewangles[0], cl.mviewangles[1]); + for (i=0 ; i<3 ; i++) + { + r = Sys_FileRead(cls.demofile, &f, 4) / 4; + cl.mviewangles[0][i] = LittleFloat (f); + } + + net_message.cursize = LittleLong (net_message.cursize); + if (net_message.cursize > MAX_MSGLEN) + Sys_Error ("Demo message (0x%08x) > MAX_MSGLEN (%d)", net_message.cursize, MAX_MSGLEN); + r = Sys_FileRead(cls.demofile, net_message.data, net_message.cursize) / net_message.cursize; + 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 + Sys_FileClose(cls.demofile); + cls.demofile = -1; + 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; + char forcetrack[16]; + + 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 = Sys_FileOpenWrite(name); + if (cls.demofile < 0) + { + Con_Printf ("ERROR: couldn't open demo for writing.\n"); + return; + } + + cls.forcetrack = track; + sprintf(forcetrack, "%i\n", cls.forcetrack); + Sys_FileWrite(cls.demofile, forcetrack, strlen(forcetrack)); + + cls.demorecording = true; +} + + +/* +==================== +CL_PlayDemo_f + +play [demoname] +==================== +*/ +static int CL_FileGetChar(int handle) +{ + char c; + + if (Sys_FileRead(handle, &c, 1) != 1) + { + return EOF; + } + + return c; +} + +void CL_PlayDemo_f (void) +{ + char name[256]; + int c; + qboolean 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); + if (cls.demofile < 0) + { + Con_Printf ("ERROR: couldn't open demo for reading.\n"); + cls.demonum = -1; // stop demo loop + return; + } + + cls.demoplayback = true; + cls.state = ca_connected; + cls.forcetrack = 0; + + while ((c = CL_FileGetChar(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 + +==================== +*/ +void CL_FinishTimeDemo (void) +{ + int frames; + double time; + + cls.timedemo = false; + +// the first frame didn't count + frames = (host_framecount - cls.td_startframe) - 1; + time = realtime - cls.td_starttime; + if (time < 1) + time = 1; + 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 +} + diff --git a/source/cl_hud.c b/source/cl_hud.c new file mode 100644 index 0000000..ed5bce0 --- /dev/null +++ b/source/cl_hud.c @@ -0,0 +1,1423 @@ +/* +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_hud.c -- status bar code + +#include "quakedef.h" +#include + +#ifdef PSP_VFPU +#include +#endif + +qpic_t *sb_round[5]; +qpic_t *sb_round_num[10]; +qpic_t *sb_moneyback; +qpic_t *instapic; +qpic_t *x2pic; +qpic_t *revivepic; +qpic_t *jugpic; +qpic_t *floppic; +qpic_t *staminpic; +qpic_t *doublepic; +qpic_t *speedpic; +qpic_t *deadpic; +qpic_t *mulepic; +qpic_t *fragpic; +qpic_t *bettypic; + + +qpic_t *b_circle; +qpic_t *b_square; +qpic_t *b_cross; +qpic_t *b_triangle; +qpic_t *b_left; +qpic_t *b_right; +qpic_t *b_up; +qpic_t *b_down; +qpic_t *b_lt; +qpic_t *b_rt; +qpic_t *b_start; +qpic_t *b_select; +qpic_t *b_home; + +qpic_t *fx_blood_lu; +qpic_t *fx_blood_ru; +qpic_t *fx_blood_ld; +qpic_t *fx_blood_rd; + +qboolean sb_showscores; +qboolean domaxammo; + +int x_value, y_value; + +void M_DrawPic (int x, int y, qpic_t *pic); + +double HUD_Change_time;//hide hud when not chagned + +extern cvar_t waypoint_mode; + + +int old_points; +int current_points; +int point_change_interval; +int point_change_interval_neg; +int alphabling = 0; +float round_center_x; +float round_center_y; + +typedef struct +{ + int points; + int negative; + float x; + float y; + float move_x; + float move_y; + double alive_time; +} point_change_t; + +point_change_t point_change[10]; + +/* +=============== +HUD_Init +=============== +*/ +void HUD_Init (void) +{ + int i; + + for (i=0 ; i<5 ; i++) + { + sb_round[i] = Draw_CachePic (va("gfx/hud/r%i",i + 1)); + } + + for (i=0 ; i<10 ; i++) + { + sb_round_num[i] = Draw_CachePic (va("gfx/hud/r_num%i",i)); + } + + sb_moneyback = Draw_CachePic ("gfx/hud/moneyback"); + instapic = Draw_CachePic ("gfx/hud/in_kill"); + x2pic = Draw_CachePic ("gfx/hud/2x"); + + revivepic = Draw_CachePic ("gfx/hud/revive"); + jugpic = Draw_CachePic ("gfx/hud/jug"); + floppic = Draw_CachePic ("gfx/hud/flopper"); + staminpic = Draw_CachePic ("gfx/hud/stamin"); + doublepic = Draw_CachePic ("gfx/hud/double"); + speedpic = Draw_CachePic ("gfx/hud/speed"); + deadpic = Draw_CachePic ("gfx/hud/dead"); + mulepic = Draw_CachePic ("gfx/hud/mule"); + fragpic = Draw_CachePic ("gfx/hud/frag"); + bettypic = Draw_CachePic ("gfx/hud/betty"); + + b_circle = Draw_CachePic ("gfx/butticons/circle"); + b_square = Draw_CachePic ("gfx/butticons/square"); + b_cross = Draw_CachePic ("gfx/butticons/cross"); + b_triangle = Draw_CachePic ("gfx/butticons/triangle"); + b_left = Draw_CachePic ("gfx/butticons/left"); + b_right = Draw_CachePic ("gfx/butticons/right"); + b_up = Draw_CachePic ("gfx/butticons/up"); + b_down = Draw_CachePic ("gfx/butticons/down"); + b_lt = Draw_CachePic ("gfx/butticons/lt"); + b_rt = Draw_CachePic ("gfx/butticons/rt"); + b_start = Draw_CachePic ("gfx/butticons/start"); + b_select = Draw_CachePic ("gfx/butticons/select"); + b_home = Draw_CachePic ("gfx/butticons/home"); + + + fx_blood_lu = Draw_CachePic ("gfx/hud/blood"); + /*fx_blood_lu = Draw_CachePic ("gfx/hud/blood_tl"); + /fx_blood_ru = Draw_CachePic ("gfx/hud/blood_tr"); + fx_blood_ld = Draw_CachePic ("gfx/hud/blood_bl"); + fx_blood_rd = Draw_CachePic ("gfx/hud/blood_br");*/ + + Achievement_Init(); +} + +/* +=============== +HUD_NewMap +=============== +*/ +void HUD_NewMap (void) +{ + int i; + alphabling = 0; + + for (i=0 ; i<10 ; i++) + { + point_change[i].points = 0; + point_change[i].negative = 0; + point_change[i].x = 0.0; + point_change[i].y = 0.0; + point_change[i].move_x = 0.0; + point_change[i].move_y = 0.0; + point_change[i].alive_time = 0.0; + } + + old_points = 500; + current_points = 500; + point_change_interval = 0; + point_change_interval_neg = 0; + + round_center_x = (vid.width - sb_round[0]->width) /2; + round_center_y = (vid.height - sb_round[0]->height) /2; +} + + +/* +============= +HUD_itoa +============= +*/ +int HUD_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; +} + + +//============================================================================= + +int pointsort[MAX_SCOREBOARD]; + +char scoreboardtext[MAX_SCOREBOARD][20]; +int scoreboardtop[MAX_SCOREBOARD]; +int scoreboardbottom[MAX_SCOREBOARD]; +int scoreboardcount[MAX_SCOREBOARD]; +int scoreboardlines; + +/* +=============== +HUD_Sorpoints +=============== +*/ +void HUD_Sortpoints (void) +{ + int i, j, k; + +// sort by points + scoreboardlines = 0; + for (i=0 ; iname[0]) + continue; + + Draw_String (x, 78 + y, s->name); + + d = strlen (va("%i",s->kills)); + Draw_String (x + (20 - d)*8, 78 + y, va("%i",s->kills)); + + d = strlen (va("%i",s->points)); + Draw_String (x + (31 - d)*8, 78 + y, va("%i",s->points)); + y += 10; + } + +} + + +//============================================================================= + + //=============================================================================// + //===============================DRAW FUNCTIONS================================// +//=============================================================================// + +/* +================== +HUD_Points + +================== +*/ + + +void HUD_Parse_Point_Change (int points, int negative, int x_start, int y_start) +{ + int i, f; + char str[10]; + i=9; + while (i>0) + { + point_change[i] = point_change[i - 1]; + i--; + } + + point_change[i].points = points; + point_change[i].negative = negative; + + f = HUD_itoa (points, str); + point_change[i].x = x_start - 10.0 - 8.0*f; + point_change[i].y = y_start; + point_change[i].move_x = 1.0; + point_change[i].move_y = ((rand ()&0x7fff) / ((float)0x7fff)) - 0.5; + + point_change[i].alive_time = Sys_FloatTime() + 0.4; +} + +void HUD_Points (void) +{ + int i, k, l; + int x, y, f, xplus; + scoreboard_t *s; + char str[12]; + +// scores + HUD_Sortpoints (); + +// draw the text + l = scoreboardlines; + + + x = vid.width - sb_moneyback->width; + y = vid.height - 16 - fragpic->height - 4 - 16 - sb_moneyback->height; + for (i=0 ; iname[0]) + continue; + + // draw background + + // draw number + f = s->points; + if (f > current_points) + { + point_change_interval_neg = 0; + if (!point_change_interval) + { + point_change_interval = (int)(f - old_points)/55;; + } + current_points = old_points + point_change_interval; + if (f < current_points) + { + current_points = f; + point_change_interval = 0; + } + } + else if (f < current_points) + { + point_change_interval = 0; + if (!point_change_interval_neg) + { + point_change_interval_neg = (int)(old_points - f)/55; + } + current_points = old_points - point_change_interval_neg; + if (f > current_points) + { + current_points = f; + point_change_interval_neg = 0; + } + } + Draw_Pic (x, y, sb_moneyback); + xplus = HUD_itoa (f, str); + Draw_String (vid.width - (xplus*8) - 16, y + 3, va("%i", current_points)); + + if (old_points != f) + { + if (f > old_points) + HUD_Parse_Point_Change(f - old_points, 0, vid.width - (xplus*8) - 16, y + 3); + else + HUD_Parse_Point_Change(old_points - f, 1, vid.width - (xplus*8) - 16, y + 3); + + old_points = f; + } + + + + y += 10; + } +} + + +/* +================== +HUD_Point_Change + +================== +*/ +void HUD_Point_Change (void) +{ + int i; + + for (i=0 ; i<10 ; i++) + { + if (point_change[i].points) + { + if (point_change[i].negative) + Draw_ColoredString (point_change[i].x, point_change[i].y, va ("-%i", point_change[i].points), 255, 0, 0, 255, 1); + else + Draw_ColoredString (point_change[i].x, point_change[i].y, va ("+%i", point_change[i].points), 255, 255, 0, 255, 1); + point_change[i].y = point_change[i].y + point_change[i].move_y; + point_change[i].x = point_change[i].x - point_change[i].move_x; + if (point_change[i].alive_time && point_change[i].alive_time < Sys_FloatTime()) + { + point_change[i].points = 0; + point_change[i].negative = 0; + point_change[i].x = 0.0; + point_change[i].y = 0.0; + point_change[i].move_x = 0.0; + point_change[i].move_y = 0.0; + point_change[i].alive_time = 0.0; + } + } + } +} + + +/* +================== +HUD_Blood + +================== +*/ +void HUD_Blood (void) +{ + float alpha; + //blubswillrule: + //this function scales linearly from health = 0 to health = 100 + //alpha = (100.0 - (float)cl.stats[STAT_HEALTH])/100*255; + //but we want the picture to be fully visible at health = 20, so use this function instead + alpha = (100.0 - ((1.25 * (float) cl.stats[STAT_HEALTH]) - 25))/100*255; + + if (alpha <= 0.0) + return; + + #ifdef PSP_VFPU + float modifier = (vfpu_sinf(cl.time * 10) * 20) - 20;//always negative + #else + float modifier = (sin(cl.time * 10) * 20) - 20;//always negative + #endif + + if(modifier < -35.0) + modifier = -35.0; + + alpha += modifier; + + if(alpha < 0.0) + return; + float color = 255.0 + modifier; + + Draw_ColorPic(0,0,fx_blood_lu,color,color,color,alpha); + //Draw_ColorPic (0, 0, fx_blood_lu, 82, 6, 6, alpha); + /*Draw_ColorPic (0, vid.height - fx_blood_ru->height, fx_blood_ld, 82, 6, 6, alpha); + Draw_ColorPic (vid.width - fx_blood_ru->width, 0, fx_blood_ru, 82, 6, 6, alpha); + Draw_ColorPic (vid.width - fx_blood_ru->width, vid.height - fx_blood_ru->height, fx_blood_rd, 82, 6, 6, alpha);*/ +} + +/* +=============== +HUD_GetWorldText +=============== +*/ + +// modified from scatter's worldspawn parser +// FIXME - unoptimized, could probably save a bit of +// memory here in the future. +void HUD_WorldText(int alpha) +{ + // for parser + char key[128], value[4096]; + char *data; + + // first, parse worldspawn + data = COM_Parse(cl.worldmodel->entities); + + if (!data) + return; // err + if (com_token[0] != '{') + return; // err + + while(1) { + data = COM_Parse(data); + + if (!data) + return; // err + if (com_token[0] == '}') + break; // end of worldspawn + + if (com_token[0] == '_') + strcpy(key, com_token + 1); + else + strcpy(key, com_token); + + while (key[strlen(key)-1] == ' ') // remove trailing spaces + key[strlen(key)-1] = 0; + + data = COM_Parse(data); + if (!data) + return; // err + + strcpy(value, com_token); + + if (!strcmp("location", key)) // search for location key + { + Draw_ColoredString(4, vid.height/2 + 50, value, 255, 255, 255, alpha, 1); + } + if (!strcmp("date", key)) // search for date key + { + Draw_ColoredString(4, vid.height/2 + 60, value, 255, 255, 255, alpha, 1); + } + if (!strcmp("person", key)) // search for person key + { + Draw_ColoredString(4, vid.height/2 + 70, value, 255, 255, 255, alpha, 1); + } + } +} + +/* +=============== +HUD_MaxAmmo +=============== +*/ +int maxammoy; +int maxammoopac; + +void HUD_MaxAmmo(void) +{ + maxammoy -= cl.time * 0.003; + maxammoopac -= 5; + + Draw_ColoredString(vid.width/2 - strlen("MAX AMMO!")*4, maxammoy, "MAX AMMO!", 255, 255, 255, maxammoopac, 1); + + if (maxammoopac <= 0) { + domaxammo = false; + } +} + +/* +=============== +HUD_Rounds +=============== +*/ + +float color_shift[3]; +float color_shift_end[3]; +float color_shift_steps[3]; +int color_shift_init; +int blinking; +int textstate; +int value, value2; + +int reallydumbvalue; +int f222; +int maxammoy; +int maxammoopac; + +void HUD_Rounds (void) +{ + int i, x_offset, icon_num, savex; + int num[3]; + x_offset = 0; + savex = 0; + + // Round and Title text - moto + // extra creds to scatterbox for some x/y vals + // ------------------ + // First, fade from white to red, ~3s duration + if (!textstate) { + if (!value) + value = 255; + + Draw_ColoredString(vid.width/2 - strlen("Round")*8, 80, "Round", 255, value, value, 255, 2); + + value -= cl.time * 0.4; + + // prep values for next stage + if (value <= 0) { + value = 255; + value2 = 0; + textstate = 1; + } + } + // Now, fade out, and start fading worldtext in + // ~3s for fade out, + else if (textstate == 1) { + Draw_ColoredString(vid.width/2 - strlen("Round")*8, 80, "Round", 255, 0, 0, value, 2); + + HUD_WorldText(value2); + Draw_ColoredString(4, vid.height/2 + 40, "'Nazi Zombies'", 255, 255, 255, value2, 1); + + value -= cl.time * 0.4; + value2 += cl.time * 0.4; + + // prep values for next stage + if (value <= 0) { + value2 = 0; + textstate = 2; + } + } + // Hold world text for a few seconds + else if (textstate == 2) { + HUD_WorldText(255); + Draw_ColoredString(4, vid.height/2 + 40, "'Nazi Zombies'", 255, 255, 255, 255, 1); + + value2 += cl.time * 0.4; + + if (value2 >= 255) { + value2 = 255; + textstate = 3; + } + } + // Fade worldtext out, finally. + else if (textstate == 3) { + HUD_WorldText(value2); + Draw_ColoredString(4, vid.height/2 + 40, "'Nazi Zombies'", 255, 255, 255, value2, 1); + + value2 -= cl.time * 0.4; + + // prep values for next stage + if (value2 <= 0) { + textstate = -1; + } + } + // ------------------ + // End Round and Title text - moto + + if (cl.stats[STAT_ROUNDCHANGE] == 1)//this is the rounds icon at the middle of the screen + { + if (textstate == -1) { + value = 0; + value2 = 0; + textstate = 0; + } + + Draw_ColorPic ((vid.width - sb_round[0]->width) /2, (vid.height - sb_round[0]->height) /2, sb_round[0], 107, 1, 0, alphabling); + + alphabling = alphabling + 15; + + if (alphabling < 0) + alphabling = 0; + else if (alphabling > 255) + alphabling = 255; + } + else if (cl.stats[STAT_ROUNDCHANGE] == 2)//this is the rounds icon moving from middle + { + Draw_ColorPic (round_center_x, round_center_y, sb_round[0], 107, 1, 0, 255); + round_center_x = round_center_x - (229/108) - 0.2; + round_center_y = round_center_y + 1; + if (round_center_x <= 5) + round_center_x = 5; + if (round_center_y >= 220) + round_center_y = 220; + } + else if (cl.stats[STAT_ROUNDCHANGE] == 3)//shift to white + { + if (!color_shift_init) + { + color_shift[0] = 107; + color_shift[1] = 1; + color_shift[2] = 0; + for (i = 0; i < 3; i++) + { + color_shift_end[i] = 255; + color_shift_steps[i] = (color_shift_end[i] - color_shift[i])/60; + } + color_shift_init = 1; + } + for (i = 0; i < 3; i++) + { + if (color_shift[i] < color_shift_end[i]) + color_shift[i] = color_shift[i] + color_shift_steps[i]; + + if (color_shift[i] >= color_shift_end[i]) + color_shift[i] = color_shift_end[i]; + } + if (cl.stats[STAT_ROUNDS] > 0 && cl.stats[STAT_ROUNDS] < 11) + { + + for (i = 0; i < cl.stats[STAT_ROUNDS]; i++) + { + if (i == 4) + { + Draw_ColorPic (5, vid.height - sb_round[4]->height - 4, sb_round[4], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255); + savex = x_offset + 10; + x_offset = x_offset + 10; + continue; + } + if (i == 9) + { + Draw_ColorPic (5 + savex, vid.height - sb_round[4]->height - 4, sb_round[4], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255); + continue; + } + if (i > 4) + icon_num = i - 5; + else + icon_num = i; + + Draw_ColorPic (5 + x_offset, vid.height - sb_round[icon_num]->height - 4, sb_round[icon_num], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255); + + x_offset = x_offset + sb_round[icon_num]->width + 3; + } + } + else + { + if (cl.stats[STAT_ROUNDS] >= 100) + { + num[2] = (int)(cl.stats[STAT_ROUNDS]/100); + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[2]]->height - 4, sb_round_num[num[2]], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255); + x_offset = x_offset + sb_round_num[num[2]]->width - 8; + } + else + num[2] = 0; + if (cl.stats[STAT_ROUNDS] >= 10) + { + num[1] = (int)((cl.stats[STAT_ROUNDS] - num[2]*100)/10); + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[1]]->height - 4, sb_round_num[num[1]], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255); + x_offset = x_offset + sb_round_num[num[1]]->width - 8; + } + else + num[1] = 0; + + num[0] = cl.stats[STAT_ROUNDS] - num[2]*100 - num[1]*10; + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[0]]->height - 4, sb_round_num[num[0]], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255); + x_offset = x_offset + sb_round_num[num[0]]->width - 8; + } + } + else if (cl.stats[STAT_ROUNDCHANGE] == 4)//blink white + { + blinking = ((int)(realtime*1000)&510) - 255; + blinking = abs(blinking); + if (cl.stats[STAT_ROUNDS] > 0 && cl.stats[STAT_ROUNDS] < 11) + { + for (i = 0; i < cl.stats[STAT_ROUNDS]; i++) + { + if (i == 4) + { + Draw_ColorPic (5, vid.height - sb_round[4]->height - 4, sb_round[4], 255, 255, 255, blinking); + savex = x_offset + 10; + x_offset = x_offset + 10; + continue; + } + if (i == 9) + { + Draw_ColorPic (5 + savex, vid.height - sb_round[4]->height - 4, sb_round[4], 255, 255, 255, blinking); + continue; + } + if (i > 4) + icon_num = i - 5; + else + icon_num = i; + + Draw_ColorPic (5 + x_offset, vid.height - sb_round[icon_num]->height - 4, sb_round[icon_num], 255, 255, 255, blinking); + + x_offset = x_offset + sb_round[icon_num]->width + 3; + } + } + else + { + if (cl.stats[STAT_ROUNDS] >= 100) + { + num[2] = (int)(cl.stats[STAT_ROUNDS]/100); + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[2]]->height - 4, sb_round_num[num[2]], 255, 255, 255, blinking); + x_offset = x_offset + sb_round_num[num[2]]->width - 8; + } + else + num[2] = 0; + if (cl.stats[STAT_ROUNDS] >= 10) + { + num[1] = (int)((cl.stats[STAT_ROUNDS] - num[2]*100)/10); + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[1]]->height - 4, sb_round_num[num[1]], 255, 255, 255, blinking); + x_offset = x_offset + sb_round_num[num[1]]->width - 8; + } + else + num[1] = 0; + + num[0] = cl.stats[STAT_ROUNDS] - num[2]*100 - num[1]*10; + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[0]]->height - 4, sb_round_num[num[0]], 255, 255, 255, blinking); + x_offset = x_offset + sb_round_num[num[0]]->width - 8; + } + } + else if (cl.stats[STAT_ROUNDCHANGE] == 5)//blink white + { + if (blinking > 0) + blinking = blinking - 10; + if (blinking < 0) + blinking = 0; + if (cl.stats[STAT_ROUNDS] > 0 && cl.stats[STAT_ROUNDS] < 11) + { + for (i = 0; i < cl.stats[STAT_ROUNDS]; i++) + { + if (i == 4) + { + Draw_ColorPic (5, vid.height - sb_round[4]->height - 4, sb_round[4], 255, 255, 255, blinking); + savex = x_offset + 10; + x_offset = x_offset + 10; + continue; + } + if (i == 9) + { + Draw_ColorPic (5 + savex, vid.height - sb_round[4]->height - 4, sb_round[4], 255, 255, 255, blinking); + continue; + } + if (i > 4) + icon_num = i - 5; + else + icon_num = i; + + Draw_ColorPic (5 + x_offset, vid.height - sb_round[icon_num]->height - 4, sb_round[icon_num], 255, 255, 255, blinking); + + x_offset = x_offset + sb_round[icon_num]->width + 3; + } + } + else + { + if (cl.stats[STAT_ROUNDS] >= 100) + { + num[2] = (int)(cl.stats[STAT_ROUNDS]/100); + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[2]]->height - 4, sb_round_num[num[2]], 255, 255, 255, blinking); + x_offset = x_offset + sb_round_num[num[2]]->width - 8; + } + else + num[2] = 0; + if (cl.stats[STAT_ROUNDS] >= 10) + { + num[1] = (int)((cl.stats[STAT_ROUNDS] - num[2]*100)/10); + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[1]]->height - 4, sb_round_num[num[1]], 255, 255, 255, blinking); + x_offset = x_offset + sb_round_num[num[1]]->width - 8; + } + else + num[1] = 0; + + num[0] = cl.stats[STAT_ROUNDS] - num[2]*100 - num[1]*10; + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[0]]->height - 4, sb_round_num[num[0]], 255, 255, 255, blinking); + x_offset = x_offset + sb_round_num[num[0]]->width - 8; + } + } + else if (cl.stats[STAT_ROUNDCHANGE] == 6)//blink white while fading back + { + color_shift_init = 0; + blinking = ((int)(realtime*1000)&510) - 255; + blinking = abs(blinking); + if (cl.stats[STAT_ROUNDS] > 0 && cl.stats[STAT_ROUNDS] < 11) + { + for (i = 0; i < cl.stats[STAT_ROUNDS]; i++) + { + if (i == 4) + { + Draw_ColorPic (5, vid.height - sb_round[4]->height - 4, sb_round[4], 255, 255, 255, blinking); + savex = x_offset + 10; + x_offset = x_offset + 10; + continue; + } + if (i == 9) + { + Draw_ColorPic (5 + savex, vid.height - sb_round[4]->height - 4, sb_round[4], 255, 255, 255, blinking); + continue; + } + if (i > 4) + icon_num = i - 5; + else + icon_num = i; + + Draw_ColorPic (5 + x_offset, vid.height - sb_round[icon_num]->height - 4, sb_round[icon_num], 255, 255, 255, blinking); + + x_offset = x_offset + sb_round[icon_num]->width + 3; + } + } + else + { + if (cl.stats[STAT_ROUNDS] >= 100) + { + num[2] = (int)(cl.stats[STAT_ROUNDS]/100); + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[2]]->height - 4, sb_round_num[num[2]], 255, 255, 255, blinking); + x_offset = x_offset + sb_round_num[num[2]]->width - 8; + } + else + num[2] = 0; + if (cl.stats[STAT_ROUNDS] >= 10) + { + num[1] = (int)((cl.stats[STAT_ROUNDS] - num[2]*100)/10); + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[1]]->height - 4, sb_round_num[num[1]], 255, 255, 255, blinking); + x_offset = x_offset + sb_round_num[num[1]]->width - 8; + } + else + num[1] = 0; + + num[0] = cl.stats[STAT_ROUNDS] - num[2]*100 - num[1]*10; + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[0]]->height - 4, sb_round_num[num[0]], 255, 255, 255, blinking); + x_offset = x_offset + sb_round_num[num[0]]->width - 8; + } + } + else if (cl.stats[STAT_ROUNDCHANGE] == 7)//blink white while fading back + { + if (!color_shift_init) + { + color_shift_end[0] = 107; + color_shift_end[1] = 1; + color_shift_end[2] = 0; + for (i = 0; i < 3; i++) + { + color_shift[i] = 255; + color_shift_steps[i] = (color_shift[i] - color_shift_end[i])/60; + } + color_shift_init = 1; + } + for (i = 0; i < 3; i++) + { + if (color_shift[i] > color_shift_end[i]) + color_shift[i] = color_shift[i] - color_shift_steps[i]; + + if (color_shift[i] < color_shift_end[i]) + color_shift[i] = color_shift_end[i]; + } + if (cl.stats[STAT_ROUNDS] > 0 && cl.stats[STAT_ROUNDS] < 11) + { + for (i = 0; i < cl.stats[STAT_ROUNDS]; i++) + { + if (i == 4) + { + Draw_ColorPic (5, vid.height - sb_round[4]->height - 4, sb_round[4], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255); + savex = x_offset + 10; + x_offset = x_offset + 10; + continue; + } + if (i == 9) + { + Draw_ColorPic (5 + savex, vid.height - sb_round[4]->height - 4, sb_round[4], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255); + continue; + } + if (i > 4) + icon_num = i - 5; + else + icon_num = i; + + Draw_ColorPic (5 + x_offset, vid.height - sb_round[icon_num]->height - 4, sb_round[icon_num], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255); + + x_offset = x_offset + sb_round[icon_num]->width + 3; + } + } + else + { + if (cl.stats[STAT_ROUNDS] >= 100) + { + num[2] = (int)(cl.stats[STAT_ROUNDS]/100); + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[2]]->height - 4, sb_round_num[num[2]], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255); + x_offset = x_offset + sb_round_num[num[2]]->width - 8; + } + else + num[2] = 0; + if (cl.stats[STAT_ROUNDS] >= 10) + { + num[1] = (int)((cl.stats[STAT_ROUNDS] - num[2]*100)/10); + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[1]]->height - 4, sb_round_num[num[1]], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255); + x_offset = x_offset + sb_round_num[num[1]]->width - 8; + } + else + num[1] = 0; + + num[0] = cl.stats[STAT_ROUNDS] - num[2]*100 - num[1]*10; + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[0]]->height - 4, sb_round_num[num[0]], (int)color_shift[0], (int)color_shift[1], (int)color_shift[2], 255); + x_offset = x_offset + sb_round_num[num[0]]->width - 8; + } + } + else + { + color_shift[0] = 107; + color_shift[1] = 1; + color_shift[2] = 0; + color_shift_init = 0; + alphabling = 0; + if (cl.stats[STAT_ROUNDS] > 0 && cl.stats[STAT_ROUNDS] < 11) + { + for (i = 0; i < cl.stats[STAT_ROUNDS]; i++) + { + if (i == 4) + { + Draw_ColorPic (5, vid.height - sb_round[4]->height - 4, sb_round[4], 107, 1, 0, 255); + savex = x_offset + 10; + x_offset = x_offset + 10; + continue; + } + if (i == 9) + { + Draw_ColorPic (5 + savex, vid.height - sb_round[4]->height - 4, sb_round[4], 107, 1, 0, 255); + continue; + } + if (i > 4) + icon_num = i - 5; + else + icon_num = i; + + Draw_ColorPic (5 + x_offset, vid.height - sb_round[icon_num]->height - 4, sb_round[icon_num], 107, 1, 0, 255); + + x_offset = x_offset + sb_round[icon_num]->width + 3; + } + } + else + { + if (cl.stats[STAT_ROUNDS] >= 100) + { + num[2] = (int)(cl.stats[STAT_ROUNDS]/100); + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[2]]->height - 4, sb_round_num[num[2]], 107, 1, 0, 255); + x_offset = x_offset + sb_round_num[num[2]]->width - 8; + } + else + num[2] = 0; + if (cl.stats[STAT_ROUNDS] >= 10) + { + num[1] = (int)((cl.stats[STAT_ROUNDS] - num[2]*100)/10); + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[1]]->height - 4, sb_round_num[num[1]], 107, 1, 0, 255); + x_offset = x_offset + sb_round_num[num[1]]->width - 8; + } + else + num[1] = 0; + + num[0] = cl.stats[STAT_ROUNDS] - num[2]*100 - num[1]*10; + + if(cl.stats[STAT_ROUNDS] == 0) + return; + + Draw_ColorPic (2 + x_offset, vid.height - sb_round_num[num[0]]->height - 4, sb_round_num[num[0]], 107, 1, 0, 255); + x_offset = x_offset + sb_round_num[num[0]]->width - 8; + } + } +} + +/* +=============== +HUD_Perks +=============== +*/ +#define P_JUG 1 +#define P_DOUBLE 2 +#define P_SPEED 4 +#define P_REVIVE 8 +#define P_FLOP 16 +#define P_STAMIN 32 +#define P_DEAD 64 +#define P_MULE 128 + +int perk_order[9]; +int current_perk_order; + +void HUD_Perks (void) +{ + int i; + int y; + y = vid.height - sb_round[1]->height - jugpic->height -2; + + for (i = 0; i < 9; i++) + { + if (perk_order[i]) + { + if (perk_order[i] == P_JUG) + { + Draw_StretchPic (2, y, jugpic, 20, 20); + y = y - 22; + } + else if (perk_order[i] == P_DOUBLE) + { + Draw_StretchPic (2, y, doublepic, 20, 20); + y = y - 22; + } + else if (perk_order[i] == P_SPEED) + { + Draw_StretchPic (2, y, speedpic, 20, 20); + y = y - 22; + } + else if (perk_order[i] == P_REVIVE) + { + Draw_StretchPic (2, y, revivepic, 20, 20); + y = y - 22; + } + else if (perk_order[i] == P_FLOP) + { + Draw_StretchPic (2, y, floppic, 20, 20); + y = y - 22; + } + else if (perk_order[i] == P_STAMIN) + { + Draw_StretchPic (2, y, staminpic, 20, 20); + y = y - 22; + } + else if (perk_order[i] == P_DEAD) + { + Draw_StretchPic (2, y, deadpic, 20, 20); + y = y - 22; + } + else if (perk_order[i] == P_MULE) + { + Draw_StretchPic (2, y, mulepic, 20, 20); + y = y - 22; + } + } + } +} + +/* +=============== +HUD_Powerups +=============== +*/ +void HUD_Powerups (void) +{ + + if(cl.stats[STAT_X2]) + Draw_StretchPic ((vid.width/2) - 30,0, x2pic, 28, 28); + if(cl.stats[STAT_INSTA]) + Draw_StretchPic ((vid.width/2) + 2,0, instapic, 28, 28); +} + +/* +=============== +HUD_ProgressBar +=============== +*/ +void HUD_ProgressBar (void) +{ + float progressbar; + + if (cl.progress_bar) + { + progressbar = 100 - ((cl.progress_bar-sv.time)*10); + if (progressbar >= 100) + progressbar = 100; + Draw_FillByColor ((vid.width)/2 - 51, vid.height*0.75 - 1, 102, 5, GU_RGBA(0, 0, 0,100)); + Draw_FillByColor ((vid.width)/2 - 50, vid.height*0.75, progressbar, 3, GU_RGBA(255, 255, 255,100)); + + Draw_String ((vid.width - (88))/2, vid.height*0.75 + 10, "Reviving..."); + } +} + +/* +=============== +HUD_Achievement + +Achievements based on code by Arkage +=============== +*/ + + +int achievement; // the background image +int achievement_unlocked; +char achievement_text[MAX_QPATH]; +double achievement_time; +float smallsec; +int ach_pic; +void HUD_Achievement (void) +{ + if (achievement_unlocked == 1) + { + smallsec = smallsec + 0.7; + if (smallsec >= 55) + smallsec = 55; + //Background image + //Sbar_DrawPic (176, 5, achievement); + // The achievement text + Draw_AlphaPic (30, smallsec - 50, achievement_list[ach_pic].img, 0.7f); + } + + // Reset the achievement + if (Sys_FloatTime() >= achievement_time) + { + achievement_unlocked = 0; + } +} + +void HUD_Parse_Achievement (int ach) +{ + if (achievement_list[ach].unlocked) + return; + + achievement_unlocked = 1; + smallsec = 0; + achievement_time = Sys_FloatTime() + 10; + ach_pic = ach; + achievement_list[ach].unlocked = 1; + Save_Achivements(); +} + +/* +=============== +HUD_Ammo +=============== +*/ + +int GetLowAmmo(int weapon, int type) +{ + switch (weapon) + { + case W_COLT: if (type) return 2; else return 16; + case W_KAR: if (type) return 1; else return 10; + case W_KAR_SCOPE: if (type) return 1; else return 10; + case W_M1A1: if (type) return 4; else return 24; + case W_SAWNOFF: if (type) return 1; else return 12; + case W_DB: if (type) return 1; else return 12; + case W_THOMPSON: if (type) return 6; else return 40; + case W_BAR: if (type) return 6; else return 28; + default: return 0; + } +} + +int IsDualWeapon(int weapon) +{ + switch(weapon) { + case W_BIATCH: + case W_SNUFF: + return 1; + default: + return 0; + } + + return 0; +} + +void HUD_Ammo (void) +{ + char str[12]; + int xplus; + char *magstring; + + y_value = vid.height - 16; + + magstring = va("%i", cl.stats[STAT_CURRENTMAG]); + + xplus = HUD_itoa(cl.stats[STAT_CURRENTMAG], str); + + // Magazine + if (GetLowAmmo(cl.stats[STAT_ACTIVEWEAPON], 1) >= cl.stats[STAT_CURRENTMAG]) + Draw_ColoredString(vid.width - 42 - (xplus*8), y_value, magstring, 255, 0, 0, 255, 1); + else + Draw_ColoredString(vid.width - 42 - (xplus*8), y_value, magstring, 255, 255, 255, 255, 1); + + // Second mag for dual weps + if (IsDualWeapon(cl.stats[STAT_ACTIVEWEAPON])) { + magstring = va("%i", cl.stats[STAT_CURRENTMAG2]); + xplus = HUD_itoa(cl.stats[STAT_CURRENTMAG2], str); + + if (GetLowAmmo(cl.stats[STAT_ACTIVEWEAPON], 1) >= cl.stats[STAT_CURRENTMAG2]) + Draw_ColoredString(vid.width - 56 - (xplus*8), y_value, magstring, 255, 0, 0, 255, 1); + else + Draw_ColoredString(vid.width - 56 - (xplus*8), y_value, magstring, 255, 255, 255, 255, 1); + } + + // Reserve ammo + if (GetLowAmmo(cl.stats[STAT_ACTIVEWEAPON], 0) >= cl.stats[STAT_AMMO]) + { + Draw_ColoredString (vid.width - 42, y_value, "/", 255, 0, 0, 255, 1); + Draw_ColoredString (vid.width - 34, y_value, va ("%i",cl.stats[STAT_AMMO]), 255, 0, 0, 255, 1); + } + else + { + Draw_ColoredString (vid.width - 42, y_value, "/", 255, 255, 255, 255, 1); + Draw_ColoredString (vid.width - 34, y_value, va ("%i",cl.stats[STAT_AMMO]), 255, 255, 255, 255, 1); + } +} + +/* +=============== +HUD_AmmoString +=============== +*/ + +void HUD_AmmoString (void) +{ + if (GetLowAmmo(cl.stats[STAT_ACTIVEWEAPON], 1) >= cl.stats[STAT_CURRENTMAG]) + { + if (0 < cl.stats[STAT_AMMO] && cl.stats[STAT_CURRENTMAG] >= 0) { + Draw_ColoredString ((vid.width)/2, (vid.height)/2 + 40, "Reload", 255, 255, 255, 255, 1); + } else if (0 < cl.stats[STAT_CURRENTMAG]) { + Draw_ColoredString ((vid.width)/2, (vid.height)/2 + 40, "LOW AMMO", 255, 255, 0, 255, 1); + } else { + Draw_ColoredString ((vid.width)/2, (vid.height)/2 + 40, "NO AMMO", 255, 0, 0, 255, 1); + } + } +} + +/* +=============== +HUD_Grenades +=============== +*/ +#define UI_FRAG 1 +#define UI_BETTY 2 + +void HUD_Grenades (void) +{ + if (cl.stats[STAT_GRENADES]) + { + x_value = vid.width - 50; + y_value = vid.height - 16 - fragpic->height - 4; + } + if (cl.stats[STAT_GRENADES] & UI_FRAG) + { + Draw_StretchPic (x_value, y_value, fragpic, 22, 22); + if (cl.stats[STAT_PRIGRENADES] <= 0) + Draw_ColoredString (x_value + 12, y_value + 12, va ("%i",cl.stats[STAT_PRIGRENADES]), 255, 0, 0, 255, 1); + else + Draw_String (x_value + 12, y_value + 12, va ("%i",cl.stats[STAT_PRIGRENADES])); + } + if (cl.stats[STAT_GRENADES] & UI_BETTY) + { + Draw_StretchPic (x_value - fragpic->width - 5, y_value, bettypic, 22, 22); + Draw_String (x_value - fragpic->width + 7, y_value + 12, va ("%i",cl.stats[STAT_SECGRENADES])); + } +} + +/* +=============== +HUD_Weapon +=============== +*/ +void HUD_Weapon (void) +{ + char str[32]; + float l; + x_value = vid.width; + y_value = vid.height - 16 - fragpic->height - 4 - 16; + + strcpy(str, pr_strings+sv_player->v.Weapon_Name); + l = strlen(str); + + x_value = vid.width - 8 - l*8; + Draw_String (x_value, y_value, str); +} + +/* +=============== +HUD_Draw +=============== +*/ +void HUD_Draw (void) +{ + if (scr_con_current == vid.height) + return; // console is full screen + + if (key_dest == key_menu_pause) + return; + + scr_copyeverything = 1; + + + if (waypoint_mode.value) + { + Draw_String (vid.width - 112, 0, "WAYPOINTMODE"); + Draw_String (vid.width - 240, 8, "Press fire to create waypoint"); + Draw_String (vid.width - 232, 16, "Press use to select waypoint"); + Draw_String (vid.width - 216, 24, "Press aim to link waypoint"); + Draw_String (vid.width - 248, 32, "Press knife to remove waypoint"); + Draw_String (vid.width - 272, 40, "Press switch to move waypoint here"); + Draw_String (vid.width - 304, 48, "Press reload to make special waypoint"); + return; + } + + if (cl.stats[STAT_HEALTH] <= 0) + { + HUD_EndScreen (); + return; + } + + HUD_Blood(); + HUD_Rounds(); + HUD_Perks(); + HUD_Powerups(); + HUD_ProgressBar(); + if ((HUD_Change_time > Sys_FloatTime() || GetLowAmmo(cl.stats[STAT_ACTIVEWEAPON], 1) >= cl.stats[STAT_CURRENTMAG] || GetLowAmmo(cl.stats[STAT_ACTIVEWEAPON], 0) >= cl.stats[STAT_AMMO]) && cl.stats[STAT_HEALTH] >= 20) + { //these elements are only drawn when relevant for few seconds + HUD_Ammo(); + HUD_Grenades(); + HUD_Weapon(); + HUD_AmmoString(); + } + HUD_Points(); + HUD_Point_Change(); + HUD_Achievement(); + + if (domaxammo == true) { + if (maxammoopac <= 0) { + maxammoopac = 255; + maxammoy = 250; + } + HUD_MaxAmmo(); + } +} diff --git a/source/cl_hud.h b/source/cl_hud.h new file mode 100644 index 0000000..ac41220 --- /dev/null +++ b/source/cl_hud.h @@ -0,0 +1,53 @@ +/* +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. + +void HUD_Init (void); + +// call whenever any of the client stats represented on the sbar changes + +void HUD_Draw (void); +// called every frame by screen + +// called each frame after the level has been completed +void HUD_NewMap (void); + +extern double HUD_Change_time; + + + +//achievement stuff +#define MAX_ACHIEVEMENTS 5//23 +typedef struct achievement_list_s +{ + qpic_t *img; + int unlocked; + char name[64]; + char description[256]; + int progress; +} achievement_list_t; + +void Achievement_Init (void); +extern achievement_list_t achievement_list[MAX_ACHIEVEMENTS]; +extern qpic_t *achievement_locked; + +void HUD_Parse_Achievement (int ach); diff --git a/source/cl_input.c b/source/cl_input.c new file mode 100644 index 0000000..49d9fb1 --- /dev/null +++ b/source/cl_input.c @@ -0,0 +1,696 @@ +/* +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 + +=============================================================================== +*/ + + +kbutton_t in_klook;//Heffo - mlook cvar +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, in_grenade, in_reload, in_switch, in_knife, in_aim; +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 +} + +qboolean croshhairmoving = false; + +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(); +} Heffo - mlook cvar*/ + +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);Cbuf_AddText("impulse 24\n");} +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_GrenadeDown (void) {KeyDown(&in_grenade);} +void IN_GrenadeUp (void) {KeyUp(&in_grenade);} +void IN_SwitchDown (void) {KeyDown(&in_switch);} +void IN_SwitchUp (void) {KeyUp(&in_switch);} +void IN_ReloadDown (void) {KeyDown(&in_reload);} +void IN_ReloadUp (void) {KeyUp(&in_reload);} +void IN_KnifeDown (void) {KeyDown(&in_knife);} +void IN_KnifeUp (void) {KeyUp(&in_knife);} +void IN_AimDown (void) {KeyDown(&in_aim);} +void IN_AimUp (void) {KeyUp(&in_aim);} + +void IN_Impulse (void) {in_impulse=Q_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; + qboolean 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; +} + + + + +//========================================================================== + +cvar_t cl_upspeed = {"cl_upspeed","200"}; +float cl_forwardspeed; +float cl_backspeed; +float cl_sidespeed; + +cvar_t cl_movespeedkey = {"cl_movespeedkey","2.0"}; + +cvar_t cl_yawspeed = {"cl_yawspeed","140"}; +cvar_t cl_pitchspeed = {"cl_pitchspeed","150"}; + +cvar_t cl_anglespeedkey = {"cl_anglespeedkey","1.5"}; + +cvar_t in_mlook = {"in_mlook", "1", true}; //Heffo - mlook cvar +cvar_t in_aimassist = {"in_aimassist", "1", true}; + + +//Shpuld - Porting over lower sens for lower fov +extern cvar_t scr_fov; + +/* +================ +CL_AdjustAngles + +Moves the local angle positions +================ +*/ + + +extern int original_fov, final_fov; +void CL_AdjustAngles (void) +{ + float speed; + float up, down; + + if (in_speed.state & 1) + speed = host_frametime * cl_anglespeedkey.value; + else + speed = host_frametime; + + //shpuld begin + speed = speed * scr_fov.value/90; + //speed = speed*final_fov/original_fov; + //shpuld end + + + 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 (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 (); + + if (cl.viewangles[PITCH] > 80) + cl.viewangles[PITCH] = 80; + if (cl.viewangles[PITCH] < -70) + cl.viewangles[PITCH] = -70; + + if (cl.viewangles[ROLL] > 50) + cl.viewangles[ROLL] = 50; + if (cl.viewangles[ROLL] < -50) + cl.viewangles[ROLL] = -50; + +} + +/* +================ +CL_BaseMove + +Send the intended movement message to the server +================ +*/ + +cvar_t waypoint_mode; +float crosshair_opacity; +void CL_BaseMove (usercmd_t *cmd) +{ + if (cls.signon != SIGNONS)//BLUBS CHANGED HERE + return; + + CL_AdjustAngles (); + + Q_memset (cmd, 0, sizeof(*cmd)); + + // Moto - we handle movespeed in QC now. + cl_backspeed = cl_forwardspeed = cl_sidespeed = sv_player->v.maxspeed; + + // Throttle side and back speeds + cl_sidespeed *= 0.8; + cl_backspeed *= 0.7; + + if (waypoint_mode.value) + cl_backspeed = cl_forwardspeed = cl_sidespeed *= 1.5; + + if (in_strafe.state & 1) + { + cmd->sidemove += cl_sidespeed * CL_KeyState (&in_right); + cmd->sidemove -= cl_sidespeed * CL_KeyState (&in_left); + } + + // crosshair stuff + croshhairmoving = true; + crosshair_opacity -= 8; + if (crosshair_opacity <= 128) + crosshair_opacity = 128; + + cmd->sidemove += cl_sidespeed * CL_KeyState (&in_moveright); + cmd->sidemove -= cl_sidespeed * CL_KeyState (&in_moveleft); + + 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 * CL_KeyState (&in_forward); + cmd->forwardmove -= cl_backspeed * 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; + } + + // reset crosshair + if (!CL_KeyState (&in_moveright) && !CL_KeyState (&in_moveleft) && !CL_KeyState (&in_forward) && !CL_KeyState (&in_back)) { + croshhairmoving = false; + + crosshair_opacity += 22; + + if (crosshair_opacity >= 255) + crosshair_opacity = 255; + } +} + +int infront(edict_t *ent1, edict_t *ent2) +{ + vec3_t vec; + float dot; + VectorSubtract(ent2->v.origin,ent1->v.origin,vec); + VectorNormalize(vec); + + vec3_t temp_angle,temp_forward,temp_right,temp_up; + VectorCopy(cl.viewangles,temp_angle); + + AngleVectors(temp_angle,temp_forward,temp_right,temp_up); + + dot = DotProduct(vec,temp_forward); + if(dot > 0.98) + { + return 1; + } + return 0; +} + +int EN_Find(int num,char *string) +{ + edict_t *ed; + + int e; + e = num; + + for(e++; e < sv.num_edicts; e++) + { + ed = EDICT_NUM(e); + if(ed->free) + continue; + if(!strcmp(pr_strings + ed->v.classname,string)) + { + return e; + } + } + return 0; +} + +void CL_Aim_Snap(void) +{ + edict_t *z,*bz,*player; + int znum; + trace_t trace; + float bestDist = 10000; + vec3_t distVec, zOrg, pOrg; + //32 is v_ofs num + + bz = sv.edicts; + + int vofs = 32;//32 is actual v_ofs num + int aimOfs = -10;//30 is top of bbox, 20 is our goal, so -10 + //Zombie body bbox vert max = 30 + //20 is the offset of the height of the zombie that we're aiming at, 20 above the origin + //Crawler body bbox vert max = -15 + + //Equation = origin + bbox vertical offset - 20 + + player = EDICT_NUM(cl.viewentity); + VectorCopy(player->v.origin,pOrg); + pOrg[2] += vofs; + + if (cl.perks & 64) + znum = EN_Find(0,"ai_zombie_head"); + else + znum = EN_Find(0,"ai_zombie"); + + z = EDICT_NUM(znum); + VectorCopy(z->v.origin,zOrg); + zOrg[2] += z->v.maxs[2];//Setting to top of zomb ent + zOrg[2] += aimOfs; + + while(znum != 0) + { + if((z->v.health > 0) && infront(player,z)) + { + VectorCopy(z->v.origin,zOrg); + zOrg[2] += aimOfs; + VectorSubtract(pOrg,zOrg,distVec); + if(VectorLength(distVec) < bestDist) + { + trace = SV_Move (pOrg, vec3_origin, vec3_origin,zOrg, 1, player); + if (trace.fraction >= 1) + { + bestDist = VectorLength(distVec); + bz = z; + } + } + } + if (cl.perks & 64) + znum = EN_Find(znum,"ai_zombie_head"); + else + znum = EN_Find(znum,"ai_zombie"); + z = EDICT_NUM(znum); + } + + if(bz != sv.edicts) + { + VectorCopy(bz->v.origin,zOrg); + zOrg[2] += bz->v.maxs[2];//Setting to top of bbox + zOrg[2] += aimOfs; + VectorSubtract(zOrg,pOrg,distVec); + VectorNormalize(distVec); + vectoangles(distVec,distVec); + distVec[0] += (distVec[0] > 180)? -360 : 0;//Need to bound pitch around 0, from -180, to + 180 + distVec[0] *= -1;//inverting pitch + + if(distVec[0] < -70 || distVec[0] > 80) + return; + + VectorCopy(distVec,cl.viewangles); + } +} + + +/* +============== +CL_SendMove +============== +*/ +int zoom_snap; +float angledelta(float a); +float deltaPitch,deltaYaw; +void CL_SendMove (usercmd_t *cmd) +{ + int i; + long int bits; + sizebuf_t buf; + byte data[128]; + vec3_t tempv; + buf.maxsize = 128; + buf.cursize = 0; + buf.data = data; + + cl.cmd = *cmd; + + //==== Aim Assist Code ==== + if((cl.stats[STAT_ZOOM]==1 || cl.stats[STAT_ZOOM]==2) && ((in_aimassist.value) || (cl.perks & 64))) + { + if(!zoom_snap) + { + + CL_Aim_Snap(); + zoom_snap = 1; + } + } + else + zoom_snap = 0; + + //==== Sniper Scope Swaying ==== + if(cl.stats[STAT_ZOOM] == 2) + { + vec3_t vang; + + VectorCopy(cl.viewangles,vang); + + vang[0] -= deltaPitch; + vang[1] -= deltaYaw; + + #ifdef PSP_VFPU + deltaPitch =(vfpu_cosf(cl.time/0.7) + vfpu_cosf(cl.time) + vfpu_sinf(cl.time/1.1)) * 0.5; + deltaYaw = (vfpu_sinf(cl.time/0.4) + vfpu_cosf(cl.time/0.56) + vfpu_sinf(cl.time)) * 0.5; + #else + deltaPitch =(cos(cl.time/0.7) + cos(cl.time) + sin(cl.time/1.1)) * 0.5; + deltaYaw = (sin(cl.time/0.4) + cos(cl.time/0.56) + sin(cl.time)) * 0.5; + #endif + + vang[0] += deltaPitch; + vang[1] += deltaYaw; + vang[0] = angledelta(vang[0]); + vang[1] = angledelta(vang[1]); + + VectorCopy(vang,cl.viewangles); + //return 0; + } + +// +// send the movement message +// + MSG_WriteByte (&buf, clc_move); + + MSG_WriteFloat (&buf, cl.mtime[0]); // so server can get ping times + + VectorAdd(cl.gun_kick, cl.viewangles, tempv); + for (i=0 ; i<3 ; i++) + MSG_WriteFloat (&buf, tempv[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; + + if (in_grenade.state & 3) + bits |= 8; + in_grenade.state &= ~2; + + if (in_switch.state & 3) + bits |= 16; + in_switch.state &= ~2; + + if (in_reload.state & 3) + bits |= 32; + in_reload.state &= ~2; + + if (in_knife.state & 3) + bits |= 64; + in_knife.state &= ~2; + + if (in_use.state & 3) + bits |= 128; + in_use.state &= ~2; + + if (in_aim.state & 3) + bits |= 256; + in_aim.state &= ~2; + + MSG_WriteLong (&buf, bits); + + MSG_WriteByte (&buf, in_impulse); + in_impulse = 0; + +// +// deliver the message +// + if (cls.demoplayback) + return; + +// +// allways 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 ("+grenade", IN_GrenadeDown); + Cmd_AddCommand ("-grenade", IN_GrenadeUp); + Cmd_AddCommand ("+switch", IN_SwitchDown); + Cmd_AddCommand ("-switch", IN_SwitchUp); + Cmd_AddCommand ("+reload", IN_ReloadDown); + Cmd_AddCommand ("-reload", IN_ReloadUp); + Cmd_AddCommand ("+knife", IN_KnifeDown); + Cmd_AddCommand ("-knife", IN_KnifeUp); + Cmd_AddCommand ("+aim", IN_AimDown); + Cmd_AddCommand ("-aim", IN_AimUp); + Cmd_AddCommand ("impulse", IN_Impulse); + Cmd_AddCommand ("+klook", IN_KLookDown); + Cmd_AddCommand ("-klook", IN_KLookUp); + + + +} + diff --git a/source/cl_main.c b/source/cl_main.c new file mode 100644 index 0000000..c2226c6 --- /dev/null +++ b/source/cl_main.c @@ -0,0 +1,1106 @@ +/* +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 "cl_slist.h" + +// 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 waypoint_mode = {"waypoint_mode", "0", false};// waypoint mode active +cvar_t autosave_waypoint = {"autosave_waypoint", "0", false};// waypoint mode active +cvar_t cl_name = {"_cl_name", "player", true}; +cvar_t cl_truelightning = {"cl_truelightning", "1", true}; +cvar_t cl_shownet = {"cl_shownet","0"}; // can be 0, 1, or 2 +cvar_t cl_nolerp = {"cl_nolerp","0"}; +cvar_t cl_lightning_zadjust = {"cl_lightning_zadjust", "0", true}; + +cvar_t lookspring = {"lookspring","0", true}; +cvar_t lookstrafe = {"lookstrafe","0", true}; +cvar_t in_sensitivity = {"sensitivity","3", true}; +cvar_t in_tolerance = {"tolerance","0.25", true}; +cvar_t in_acceleration = {"acceleration","1.0", true}; + +cvar_t cl_rocket2grenade = {"cl_r2g", "0"}; +cvar_t cl_deadbodyfilter = {"cl_deadbodyfilter", "0"}; +cvar_t cl_gibfilter = {"cl_gibfilter", "0"}; + +cvar_t m_pitch = {"m_pitch","0.022", true}; +cvar_t m_yaw = {"m_yaw","0.022", true}; +cvar_t m_forward = {"m_forward","1", true}; +cvar_t m_side = {"m_side","0.8", true}; + +cvar_t in_disable_analog = {"in_disable_analog", "0", true}; +cvar_t in_analog_strafe = {"in_analog_strafe", "0", true}; +cvar_t in_x_axis_adjust = {"in_x_axis_adjust", "0", true}; +cvar_t in_y_axis_adjust = {"in_y_axis_adjust", "0", true}; + +//=================================================// +modelindex_t cl_modelindex[NUM_MODELINDEX]; // +char *cl_modelnames[NUM_MODELINDEX];// + // +tagentity_t q3player_body, q3player_head; // +//=================================================// +client_static_t cls; +client_state_t cl; +// FIXME: put these on hunk? +efrag_t cl_efrags[MAX_EFRAGS]; +entity_t cl_entities[MAX_EDICTS]; +entity_t cl_static_entities[MAX_STATIC_ENTITIES]; +lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; +dlight_t cl_dlights[MAX_DLIGHTS]; + +modelindex_t cl_modelindex[NUM_MODELINDEX]; + +int cl_numvisedicts; +entity_t *cl_visedicts[MAX_VISEDICTS]; + +void CL_ClearTEnts (void); + +/* +===================== +CL_ClearState + +===================== +*/ +void CL_ClearState (void) +{ + int i; + + if (!sv.active) + Host_ClearMemory (); + + CL_ClearTEnts (); +// 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)); + + memset (cl_static_entities, 0, sizeof(cl_static_entities)); +// +// allocate the efrags and chain together into a free list +// + cl.free_efrags = cl_efrags; + for (i=0 ; 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) +{ +#if 0 + static int old; + byte pal[768]; + int c; + + if (i == old) + return; + old = i; + + if (i==0) + VID_SetPalette (host_basepal); + else if (i==1) + { + for (c=0 ; c<768 ; c+=3) + { + pal[c] = 0; + pal[c+1] = 255; + pal[c+2] = 0; + } + VID_SetPalette (pal); + } + else + { + for (c=0 ; c<768 ; c+=3) + { + pal[c] = 0; + pal[c+1] = 0; + pal[c+2] = 255; + } + VID_SetPalette (pal); + } +#endif +} + +void CL_CopyPlayerInfo (entity_t *ent, entity_t *player) +{ + memcpy (&ent->baseline, &player->baseline, sizeof(entity_state_t)); + + ent->msgtime = player->msgtime; + memcpy (ent->msg_origins, player->msg_origins, sizeof(ent->msg_origins)); + VectorCopy (player->origin, ent->origin); + memcpy (ent->msg_angles, player->msg_angles, sizeof(ent->msg_angles)); + VectorCopy (player->angles, ent->angles); + + ent->model = (ent == &q3player_body.ent) ? cl.model_precache[cl_modelindex[mi_q3torso]] : cl.model_precache[cl_modelindex[mi_q3head]]; + + ent->efrag = player->efrag; + + ent->frame = player->frame; + ent->syncbase = player->syncbase; + ent->colormap = player->colormap; + ent->effects = player->effects; + ent->skinnum = player->skinnum; + ent->visframe = player->visframe; + ent->dlightframe = player->dlightframe; + ent->dlightbits = player->dlightbits; + + ent->trivial_accept = player->trivial_accept; + ent->topnode = player->topnode; + + ent->modelindex = (ent == &q3player_body.ent) ? cl_modelindex[mi_q3torso] : cl_modelindex[mi_q3head]; + + ent->noshadow = player->noshadow; + + ent->rendermode = player->rendermode; + ent->renderamt = player->renderamt; + //ent->rendercolor = player->rendercolor; +} + +/* +=============== +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; // LordHavoc: .lit support + 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; // LordHavoc: .lit support + return dl; + } + } + + dl = &cl_dlights[0]; + memset (dl, 0, sizeof(*dl)); + dl->key = key; + dl->color[0] = dl->color[1] = dl->color[2] = 1; // LordHavoc: .lit support + return dl; +} + +void CL_NewDlight (int key, vec3_t origin, float radius, float time, int type) +{ + dlight_t *dl; + + dl = CL_AllocDlight (key); + VectorCopy (origin, dl->origin); + dl->radius = radius; + dl->die = cl.time + time; + dl->type = type; + +} + +dlighttype_t SetDlightColor (float f, dlighttype_t def, qboolean random) +{ + dlighttype_t colors[NUM_DLIGHTTYPES-4] = {lt_red, lt_blue, lt_redblue, lt_green}; + + if ((int)f == 1) + return lt_red; + else if ((int)f == 2) + return lt_blue; + else if ((int)f == 3) + return lt_redblue; + else if ((int)f == 4) + return lt_green; + else if (((int)f == NUM_DLIGHTTYPES - 3) && random) + return colors[rand()%(NUM_DLIGHTTYPES-4)]; + else + return def; +} + +/* +=============== +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. +=============== +*/ +float CL_LerpPoint (void) +{ + float f, frac; + + f = cl.mtime[0] - cl.mtime[1]; + + if (!f || cl_nolerp.value || cls.timedemo || sv.active) + { + cl.ctime = 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.ctime - cl.mtime[1]) / f; + if (frac < 0) + { + if (frac < -0.01) + { + cl.ctime = cl.time = cl.mtime[1]; + } + frac = 0; + } + else if (frac > 1) + { + if (frac > 1.01) + { + cl.ctime = cl.time = cl.mtime[0]; + } + frac = 1; + } + + return frac; +} + + +extern cvar_t scr_fov; +/* +=============== +CL_RelinkEntities +=============== +*/ + + +void CL_RelinkEntities (void) +{ + entity_t *ent; + int i, j; + float frac, f, d; + vec3_t delta; + float bobjrotate; + vec3_t oldorg; + //model_t *model; + dlight_t *dl; + //vec3_t smokeorg, smokeorg2; + //float scale; +// 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)//blubs check here for interpolating zombies + 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->modelindex == cl_modelindex[mi_explo1] || ent->modelindex == cl_modelindex[mi_explo2]) + { + // software removal of sprites + if (r_explosiontype.value == 2 || r_explosiontype.value == 3) + continue; + + if (qmb_initialized && r_part_explosions.value) + continue; + } + + if (!(model = cl.model_precache[ent->modelindex])) + Host_Error ("CL_RelinkEntities: bad modelindex"); +*/ + +// rotate binary objects locally + + if (ent->model->flags & EF_ROTATE) + { + ent->angles[1] = bobjrotate; + + #ifdef PSP_VFPU + ent->origin[2] += (( vfpu_sinf(bobjrotate/90*M_PI) * 5) + 5 ); + #else + ent->origin[2] += (( sin(bobjrotate/90*M_PI) * 5) + 5 ); + #endif + } + + if (ent->effects & EF_BRIGHTFIELD) + R_EntityParticles (ent); + + if (ent->effects & EF_DARKFIELD) + R_DarkFieldParticles (ent); + + + if (ent->effects & EF_MUZZLEFLASH) + { + + if (i == cl.viewentity && qmb_initialized && r_part_muzzleflash.value) + { + vec3_t start, smokeorg, v_forward, v_right, v_up; + vec3_t tempangles; + float forward_offset, up_offset, right_offset; + + VectorAdd(cl.viewangles,CWeaponRot,tempangles); + VectorAdd(tempangles,cl.gun_kick,tempangles); + + + AngleVectors (tempangles, v_forward, v_right, v_up); + VectorCopy (cl_entities[cl.viewentity].origin, smokeorg); + smokeorg[2] += 32; + VectorCopy(smokeorg,start); + + right_offset = sv_player->v.Flash_Offset[0]; + up_offset = sv_player->v.Flash_Offset[1]; + forward_offset = sv_player->v.Flash_Offset[2]; + + right_offset = right_offset/1000; + up_offset = up_offset/1000; + forward_offset = forward_offset/1000; + + VectorMA (start, forward_offset, v_forward ,smokeorg); + VectorMA (smokeorg, up_offset, v_up ,smokeorg); + VectorMA (smokeorg, right_offset, v_right ,smokeorg); + VectorAdd(smokeorg,CWeaponOffset,smokeorg); + + if (sv_player->v.weapon != W_RAY && sv_player->v.weapon != W_PORTER) { + QMB_MuzzleFlash (smokeorg); + } else { + QMB_RayFlash(smokeorg, sv_player->v.weapon); + } + } + + } + + if (ent->modelindex != cl_modelindex[mi_player] && ent->model->modhint != MOD_PLAYER) + { + if (ent->effects & EF_BRIGHTLIGHT) + { + vec3_t tmp; + + dl = CL_AllocDlight (i); + VectorCopy (ent->origin, tmp); + tmp[2] += 16; + dl->color[0] = 1; + dl->color[1] = 0.8; + dl->color[2] = 0.5; + CL_NewDlight (i, tmp, 400 + (rand() & 31), 0.1, lt_default); + } + } + + 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.001; + 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.001; + } + + if (ent->effects & EF_BLUELIGHT) + { + dl = CL_AllocDlight (i); + VectorCopy (ent->origin, dl->origin); + dl->die = cl.time + 0.001; + dl->radius = 100; + dl->color[0] = 0.25; + dl->color[1] = 0.25; + dl->color[2] = 2; + } + + if (ent->effects & EF_REDLIGHT) + { + dl = CL_AllocDlight (i); + VectorCopy (ent->origin, dl->origin); + dl->die = cl.time + 0.001; + dl->radius = 100; + dl->color[0] = 2; + dl->color[1] = 0.25; + dl->color[2] = 0.25; + } + + if (ent->effects & EF_ORANGELIGHT) + { + dl = CL_AllocDlight (i); + VectorCopy (ent->origin, dl->origin); + dl->die = cl.time + 0.001; + dl->radius = 100; + dl->color[0] = 2; + dl->color[1] = 1; + dl->color[2] = 0; + } + + if (ent->effects & EF_GREENLIGHT) + { + dl = CL_AllocDlight (i); + VectorCopy (ent->origin, dl->origin); + dl->die = cl.time + 0.001; + dl->radius = 100; + dl->color[0] = 0.25; + dl->color[1] = 2; + dl->color[2] = 0.25; + } + if (ent->effects & EF_ORANGELIGHT) + { + dl = CL_AllocDlight (i); + VectorCopy (ent->origin, dl->origin); + dl->die = cl.time + 0.001; + dl->radius = 100; + dl->color[0] = 2; + dl->color[1] = 1; + dl->color[2] = 0; + } + + if (ent->effects & EF_GREENLIGHT) + { + dl = CL_AllocDlight (i); + VectorCopy (ent->origin, dl->origin); + dl->die = cl.time + 0.001; + dl->radius = 100; + dl->color[0] = 0.25; + dl->color[1] = 2; + dl->color[2] = 0.25; + } + + if (ent->effects & EF_PURPLELIGHT) + { + dl = CL_AllocDlight (i); + VectorCopy (ent->origin, dl->origin); + dl->die = cl.time + 0.001; + dl->radius = 100; + dl->color[0] = 2; + dl->color[1] = 0.25; + dl->color[2] = 2; + } + + if (ent->effects & EF_RAYGREEN) + { + R_RocketTrail (oldorg, ent->origin, 12); + dl = CL_AllocDlight (i); + VectorCopy (ent->origin, dl->origin); + dl->radius = 25; + dl->die = cl.time + 0.01; + dl->color[0] = 0; + dl->color[1] = 255; + dl->color[2] = 0; + dl->type = SetDlightColor (2, lt_rocket, true); + } + + if (ent->effects & EF_RAYRED) + { + R_RocketTrail (oldorg, ent->origin, 13); + dl = CL_AllocDlight (i); + VectorCopy (ent->origin, dl->origin); + dl->radius = 25; + dl->die = cl.time + 0.01; + dl->color[0] = 255; + dl->color[1] = 0; + dl->color[2] = 0; + dl->type = SetDlightColor (2, lt_rocket, true); + } + + if (!strcmp(ent->model->name, "progs/flame2.mdl")) + { + if (qmb_initialized && r_part_flames.value) + { + //QMB_BigTorchFlame (ent->origin); + if (qmb_initialized && r_part_trails.value) + R_RocketTrail (oldorg, ent->origin, LAVA_TRAIL); + } + } + + if ((!strcmp(ent->model->name, "progs/s_spike.mdl"))||(!strcmp(ent->model->name, "progs/spike.mdl"))) + { + if (qmb_initialized && r_part_trails.value) + { + R_RocketTrail (oldorg, ent->origin, NAIL_TRAIL); + } + } + + if (ent->model->flags) + { + if (ent->model->flags & EF_GIB) + R_RocketTrail (oldorg, ent->origin, 2); + else if (ent->model->flags & EF_ZOMGIB) + R_RocketTrail (oldorg, ent->origin, 4); + else if (ent->model->flags & EF_TRACER) + R_RocketTrail (oldorg, ent->origin, 3); + else if (ent->model->flags & EF_TRACER2) + R_RocketTrail (oldorg, ent->origin, 5); + else if (ent->model->flags & 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.2; + dl->color[1] = 0.1; + dl->color[2] = 0.5; + dl->type = SetDlightColor (2, lt_rocket, true); + } + else if (ent->model->flags & EF_GRENADE) + R_RocketTrail (oldorg, ent->origin, 1); + else if (ent->model->flags & EF_TRACER3) + R_RocketTrail (oldorg, ent->origin, 6); + } + + // Tomaz - QC Glow Begin + if (ISLMPOINT(ent)) + { + dl = CL_AllocDlight (i); + VectorCopy (ent->origin, dl->origin); + dl->radius = 150; + dl->die = cl.time + 0.001; + dl->color[0] = ent->rendercolor[0]; + dl->color[1] = ent->rendercolor[1]; + dl->color[2] = ent->rendercolor[2]; + } + // Tomaz - QC Glow End + + ent->forcelink = false; + + if (i == cl.viewentity && !chase_active.value) + continue; + + + if ( ent->effects & EF_NODRAW ) + continue; + + if (qmb_initialized) + { + /*if (ent->modelindex == cl_modelindex[mi_bubble]) + { + QMB_StaticBubble (ent); + continue; + } + else if (r_part_lightning.value && ent->modelindex == cl_modelindex[mi_shambler] && ent->frame >= 65 && ent->frame <= 68) + { + vec3_t liteorg; + + VectorCopy (ent->origin, liteorg); + liteorg[2] += 32; + QMB_ShamblerCharge (liteorg); + } + else */if (r_part_trails.value && ent->model->modhint == MOD_SPIKE) + { + if (!ent->traildrawn || !VectorL2Compare(ent->trail_origin, ent->origin, 140)) + { + VectorCopy (ent->origin, oldorg); //not present last frame or too far away + ent->traildrawn = true; + } + else + { + VectorCopy (ent->trail_origin, oldorg); + } + QMB_RocketTrail (oldorg, ent->origin, BUBBLE_TRAIL); + } + } + + if (cl_numvisedicts < MAX_VISEDICTS) + { + cl_visedicts[cl_numvisedicts] = ent; + cl_numvisedicts++; + } + } +} + +/* +=============== +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; + + //if(ret) + //{ + //Con_Printf("++++++++++++++Got a new server message!+++++++++\n"); + //} + cl.last_received_message = realtime; + //Con_Printf("ParseServerMessage \n"); + CL_ParseServerMessage (); + //} + + } while (ret && cls.state == ca_connected); + + + //Con_Printf("-------------Done with server messages---------------=====\n"); + + 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 + if (!in_disable_analog.value) + 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); + + SList_Init (); + SList_Load (); + + CL_InitInput (); + CL_InitTEnts (); +// +// register our commands +// + Cvar_RegisterVariable (&cl_name); + Cvar_RegisterVariable (&waypoint_mode); + Cvar_RegisterVariable (&autosave_waypoint); + Cvar_RegisterVariable (&cl_upspeed); + 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 (&cl_rocket2grenade); + Cvar_RegisterVariable (&cl_deadbodyfilter); + Cvar_RegisterVariable (&cl_gibfilter); + Cvar_RegisterVariable (&cl_lightning_zadjust); + Cvar_RegisterVariable (&cl_truelightning); + Cvar_RegisterVariable (&in_sensitivity); + Cvar_RegisterVariable (&in_mlook); //Heffo - mlook cvar + Cvar_RegisterVariable (&in_aimassist); + Cvar_RegisterVariable (&in_tolerance); + Cvar_RegisterVariable (&in_acceleration); + Cvar_RegisterVariable (&in_disable_analog); + Cvar_RegisterVariable (&in_analog_strafe); + Cvar_RegisterVariable (&in_x_axis_adjust); + Cvar_RegisterVariable (&in_y_axis_adjust); + + + 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); +} + diff --git a/source/cl_parse.c b/source/cl_parse.c new file mode 100644 index 0000000..671d7ae --- /dev/null +++ b/source/cl_parse.c @@ -0,0 +1,1382 @@ +/* +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 "quakedef.h" + +extern cvar_t bgmtype; + +extern qboolean domaxammo; + +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_updatepoints", // [byte] [short] + "svc_clientdata", // + "svc_stopsound", // + "", // [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_spawnstaticsound", + "svc_intermission", + "svc_finale", // [string] music [string] text + "svc_cdtrack", // [byte] track [byte] looptrack + "svc_sellscreen", + "svc_cutscene", + "svc_weaponfire", + "svc_hitmark", + "svc_skybox", // [string] skyname + "svc_useprint", + "svc_updatekills", + "svc_limbupdate", + "svc_fog", // 41 // [byte] start [byte] end [byte] red [byte] green [byte] blue [float] time + "svc_bspdecal", //42 // [string] name [byte] decal_size [coords] pos + "svc_achievement", //43 + "svc_songegg", //44 [string] track name + "svc_maxammo" //45 +}; + +//============================================================================= + +/* +=============== +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_ParseBSPDecal + +Spawn decals on map +Crow_bar. +================== +*/ +void CL_ParseBSPDecal (void) +{ + vec3_t pos; + int decal_size; + char *texname; + + texname = MSG_ReadString (); + decal_size = MSG_ReadByte (); + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + + if(!texname) + return; + + Con_Printf("BSPDECAL[tex: %s size: %i pos: %f %f %f]\n", texname, decal_size, pos[0], pos[1], pos[2]); + + R_SpawnDecalBSP(pos, texname, decal_size); +} + +/* +================== +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) +{ + double time; + static double lastmsg;//BLUBSFIX, this was a float + int ret; + sizebuf_t old; + byte olddata[8192]; + + if (sv.active) + { + //Con_Printf("Active Server...exit keepalive\n"); + return; // no need if server is local + } + if (cls.demoplayback) + { + //Con_Printf("Demo Playback...exit keepalive\n"); + return; + } + +// read messages from server, should just be nops + old = net_message; + 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) + return; + //Con_Printf("Time since last keepAlive msg = %f\n",time - lastmsg); + + 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); +} + +/* +================== +CL_ParseServerInfo +================== +*/ +void CL_ParseServerInfo (void) +{ + char *str, tempname[MAX_QPATH];; + int i; + int nummodels, numsounds; + char model_precache[MAX_MODELS][MAX_QPATH]; + char sound_precache[MAX_SOUNDS][MAX_QPATH]; + extern qboolean r_loadq3player; + + //void R_PreMapLoad (char *); + + //char mapname[MAX_QPATH]; + + Con_DPrintf ("Serverinfo packet received.\n"); + //Con_Printf ("Serverinfo packet received.\n"); +// +// wipe the client_state_t struct +// + CL_ClearState (); + +// parse protocol version number + i = MSG_ReadLong (); + if (i != PROTOCOL_VERSION) + { + Con_Printf ("Server returned version %i, not %i", i, PROTOCOL_VERSION); + 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 +// + +// precache models + for (i=0 ; i= MAX_MODELS) + { + Con_Printf ("Server sent too many model precaches -> can't load Q3 player model\n"); + Q_strncpyz (model_precache[cl_modelindex[mi_player]], cl_modelnames[mi_player], sizeof(model_precache[cl_modelindex[mi_player]])); + } + else + { + Q_strncpyz (model_precache[nummodels], "models/player/upper.md3", sizeof(model_precache[nummodels])); + cl_modelindex[mi_q3torso] = nummodels++; + Q_strncpyz (model_precache[nummodels], "models/player/head.md3", sizeof(model_precache[nummodels])); + cl_modelindex[mi_q3head] = nummodels++; + } + } + +// precache sounds + //Con_Printf("Got Sounds to load: "); + 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"); + return; + } + strcpy (sound_precache[numsounds], str); + S_TouchSound (str); + //Con_Printf("%i,",numsounds); + } + //Con_Printf("\n"); + //COM_StripExtension (COM_SkipPath(model_precache[1]), mapname); + //R_PreMapLoad (mapname); + +// +// now we try to load everything else until a cache allocation fails +// + + loading_num_step = loading_num_step +nummodels + numsounds; + + //Con_Printf("Loaded Model: "); + + 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 + } + + if (bits & U_FRAME) + ent->frame = MSG_ReadShort (); + 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"); + } + + if (bits & U_SKIN) + ent->skinnum = MSG_ReadByte(); + else + ent->skinnum = ent->baseline.skin; + + if (bits & U_FRAMETIME) + ent->iframetime = MSG_ReadFloat(); + else + ent->iframetime = 0.1; + + if (bits & U_EFFECTS) + ent->effects = MSG_ReadShort(); + 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]; +// Tomaz - QC Alpha Scale Glow Begin + if (bits & U_RENDERAMT) + ent->renderamt = MSG_ReadFloat(); + else + ent->renderamt = 0; + + if (bits & U_RENDERMODE) + ent->rendermode = MSG_ReadFloat(); + else + ent->rendermode = 0; + + if (bits & U_RENDERCOLOR1) + ent->rendercolor[0] = MSG_ReadFloat(); + else + ent->rendercolor[0] = 0; + + if (bits & U_RENDERCOLOR2) + ent->rendercolor[1] = MSG_ReadFloat(); + else + ent->rendercolor[1] = 0; + + if (bits & U_RENDERCOLOR3) + ent->rendercolor[2] = MSG_ReadFloat(); + else + ent->rendercolor[2] = 0; +// Tomaz - QC Alpha Scale Glow End + if ( bits & U_NOLERP )//there's no data for nolerp, it is the value itself + 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 +================== +*/ +extern int perk_order[9]; +extern int current_perk_order; +void CL_ParseClientdata (int bits) +{ + int i, s; + + 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; + + + if (bits & SU_PERKS) + i = MSG_ReadLong (); + else + i = 0; + if (cl.perks != i) + { + if (i & 1 && !(cl.perks & 1)) + { + perk_order[current_perk_order] = 1; + current_perk_order++; + } + if (i & 2 && !(cl.perks & 2)) + { + perk_order[current_perk_order] = 2; + current_perk_order++; + } + if (i & 4 && !(cl.perks & 4)) + { + perk_order[current_perk_order] = 4; + current_perk_order++; + } + if (i & 8 && !(cl.perks & 8)) + { + perk_order[current_perk_order] = 8; + current_perk_order++; + } + if (i & 16 && !(cl.perks & 16)) + { + perk_order[current_perk_order] = 16; + current_perk_order++; + } + if (i & 32 && !(cl.perks & 32)) + { + perk_order[current_perk_order] = 32; + current_perk_order++; + } + if (i & 64 && !(cl.perks & 64)) + { + perk_order[current_perk_order] = 64; + current_perk_order++; + } + if (i & 128 && !(cl.perks & 128)) + { + perk_order[current_perk_order] = 128; + current_perk_order++; + } + if (cl.perks & 1 && !(i & 1)) + { + for (s = 0; s < 9; s++) + { + if (perk_order[s] == 1) + { + perk_order[s] = 0; + while (perk_order[s+1]) + { + perk_order[s] = perk_order[s+1]; + perk_order[s+1] = 0; + } + break; + } + } + current_perk_order--; + } + if (cl.perks & 2 && !(i & 2)) + { + for (s = 0; s < 9; s++) + { + if (perk_order[s] == 2) + { + perk_order[s] = 0; + while (perk_order[s+1]) + { + perk_order[s] = perk_order[s+1]; + perk_order[s+1] = 0; + } + break; + } + } + current_perk_order--; + } + if (cl.perks & 4 && !(i & 4)) + { + for (s = 0; s < 9; s++) + { + if (perk_order[s] == 4) + { + perk_order[s] = 0; + while (perk_order[s+1]) + { + perk_order[s] = perk_order[s+1]; + perk_order[s+1] = 0; + } + break; + } + } + current_perk_order--; + } + if (cl.perks & 8 && !(i & 8)) + { + for (s = 0; s < 9; s++) + { + if (perk_order[s] == 8) + { + perk_order[s] = 0; + while (perk_order[s+1]) + { + perk_order[s] = perk_order[s+1]; + perk_order[s+1] = 0; + } + break; + } + } + current_perk_order--; + } + if (cl.perks & 16 && !(i & 16)) + { + for (s = 0; s < 9; s++) + { + if (perk_order[s] == 16) + { + perk_order[s] = 0; + while (perk_order[s+1]) + { + perk_order[s] = perk_order[s+1]; + perk_order[s+1] = 0; + } + break; + } + } + current_perk_order--; + } + if (cl.perks & 32 && !(i & 32)) + { + for (s = 0; s < 9; s++) + { + if (perk_order[s] == 32) + { + perk_order[s] = 0; + while (perk_order[s+1]) + { + perk_order[s] = perk_order[s+1]; + perk_order[s+1] = 0; + } + break; + } + } + current_perk_order--; + } + if (cl.perks & 64 && !(i & 64)) + { + for (s = 0; s < 9; s++) + { + if (perk_order[s] == 64) + { + perk_order[s] = 0; + while (perk_order[s+1]) + { + perk_order[s] = perk_order[s+1]; + perk_order[s+1] = 0; + } + break; + } + } + current_perk_order--; + } + if (cl.perks & 128 && !(i & 128)) + { + for (s = 0; s < 9; s++) + { + if (perk_order[s] == 128) + { + perk_order[s] = 0; + while (perk_order[s+1]) + { + perk_order[s] = perk_order[s+1]; + perk_order[s+1] = 0; + } + break; + } + } + current_perk_order--; + } + cl.perks = i; + } + + cl.onground = (bits & SU_ONGROUND) != 0; + cl.inwater = (bits & SU_INWATER) != 0; + + VectorCopy (cl.mvelocity[0], cl.mvelocity[1]); + for (i=0 ; i<3 ; i++) + { + if (bits & (SU_PUNCH1<= MAX_STATIC_ENTITIES) + Host_Error ("Too many static entities"); + ent = &cl_static_entities[i]; + cl.num_statics++; + CL_ParseBaseline (ent); + +// copy it to the current state + ent->model = 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); +} + +extern double Hitmark_Time; +extern int crosshair_spread; +extern double crosshair_spread_time; +double return_time; +/* +=================== +CL_ParseWeaponFire +=================== +*/ +void CL_ParseWeaponFire (void) +{ + vec3_t kick; + return_time = (double)6/MSG_ReadLong (); + crosshair_spread_time = return_time + sv.time; + + kick[0] = MSG_ReadCoord()/5; + kick[1] = MSG_ReadCoord()/5; + kick[2] = MSG_ReadCoord()/5; + + if (!(cl.perks & 64)) { + cl.gun_kick[0] += kick[0]; + cl.gun_kick[1] += kick[1]; + cl.gun_kick[2] += kick[2]; + } +} +/* +=================== +CL_ParseLimbUpdate +=================== +*/ +void CL_ParseLimbUpdate (void) +{ + int limb = MSG_ReadByte(); + int zombieent = MSG_ReadShort(); + int limbent = MSG_ReadShort(); + switch (limb) + { + case 0://head + cl_entities[zombieent].z_head = limbent; + break; + case 1://larm + cl_entities[zombieent].z_larm = limbent; + break; + case 2://rarm + cl_entities[zombieent].z_rarm = limbent; + break; + + } +} + + +#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)//checking if it's an entity update, if it is the 7th bit will be on, this is checking for that + { + SHOWNET("fast update"); + CL_ParseUpdate (cmd&127);//here we strip the cmd from the value of the 7th (128) bit, to only give the rest of the commands + continue; + } + + SHOWNET(svc_strings[cmd]); + + // other commands + switch (cmd) + { + default: + Host_Error ("CL_ParseServerMessage: Illegible server message (%i)\n", cmd); + break; + + case svc_nop: + 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_VERSION) + Host_Error ("CL_ParseServerMessage: Server is protocol %i instead of %i\n", i, PROTOCOL_VERSION); + 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_useprint: + SCR_UsePrint (MSG_ReadByte (),MSG_ReadShort (),MSG_ReadByte ()); + break; + case svc_maxammo: + domaxammo = true; + break; + case svc_stufftext: + Cbuf_AddText (MSG_ReadString ()); + 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"); + Q_strcpy (cl_lightstyle[i].map, MSG_ReadString()); + cl_lightstyle[i].length = Q_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: + 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_updatepoints: + i = MSG_ReadByte (); + if (i >= cl.maxclients) + Host_Error ("CL_ParseServerMessage: svc_updatepoints > MAX_SCOREBOARD"); + cl.scores[i].points = MSG_ReadLong (); + break; + + case svc_updatekills: + i = MSG_ReadByte (); + if (i >= cl.maxclients) + Host_Error ("CL_ParseServerMessage: svc_updatepoints > MAX_SCOREBOARD"); + cl.scores[i].kills = MSG_ReadShort (); + 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 (); + } + else + { + CDAudio_Resume (); + } + } + 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; + Con_DPrintf("Signon: %i \n",i); + CL_SignonReply (); + 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 (strcmpi(bgmtype.string,"cd") == 0) + { + if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) ) + CDAudio_Play ((byte)cls.forcetrack, true); + else + CDAudio_Play ((byte)cl.cdtrack, true); + } + else + CDAudio_Stop(); + 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; + + case svc_skybox: + Sky_LoadSkyBox(MSG_ReadString()); + break; + case svc_songegg: + CDAudio_Track(MSG_ReadString()); + break; + case svc_fog: + Fog_ParseServerMessage (); + break; + + case svc_achievement: + HUD_Parse_Achievement (MSG_ReadByte()); + break; + + case svc_hitmark: + Hitmark_Time = sv.time + 0.2; + break; + + case svc_weaponfire: + CL_ParseWeaponFire(); + break; + + case svc_limbupdate: + CL_ParseLimbUpdate(); + break; + + case svc_bspdecal: + CL_ParseBSPDecal (); + break; + } + } +} + diff --git a/source/cl_slist.c b/source/cl_slist.c new file mode 100644 index 0000000..bc5dc97 --- /dev/null +++ b/source/cl_slist.c @@ -0,0 +1,196 @@ +/* +Copyright (C) 1999,2000 contributors of the QuakeForge project + +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 "cl_slist.h" + +server_entry_t slist[MAX_SERVER_LIST]; +//--------------------------------------UTILS----------------------------------- +char *Cmd_MakeArgs (int start) +{ + int i, c; + + static char text[1024]; + + text[0] = 0; + c = Cmd_Argc(); + for (i = start; i < c; i++) { + if (i > start) + strncat (text, " ", sizeof(text) - strlen(text) - 1); + strncat (text, Cmd_Argv(i), sizeof(text) - strlen(text) - 1); + } + + return text; +} + +char *CreateSpaces(int amount) +{ + static char spaces[1024]; + int size; + + size = bound(1, amount, sizeof(spaces) - 1); + memset(spaces, ' ', size); + spaces[size] = 0; + + return spaces; +} +//------------------------------------------------------------------------------ +void SList_Init (void) +{ + memset (&slist, 0, sizeof(slist)); +} + +void SList_Shutdown (void) +{ + FILE *f; + + // if the first is empty already that means there isn't a single entry + if (!slist[0].server) + return; + + //if (!(f = fopen("servers.lst", "wt"))) + if (!(f = fopen(va("%s/servers.lst", com_gamedir), "wt"))) + { + Con_DPrintf ("Couldn't open servers.lst\n"); + return; + } + SList_Save (f); + fclose (f); +} + +void SList_Set (int i, char *addr, char *desc) +{ + if (i >= MAX_SERVER_LIST || i < 0) + Sys_Error ("SList_Set: Bad index %d", i); + + if (slist[i].server) + Z_Free (slist[i].server); + if (slist[i].description) + Z_Free (slist[i].description); + + slist[i].server = CopyString (addr); + slist[i].description = CopyString (desc); +} + +void SList_Reset_NoFree (int i) +{ + if (i >= MAX_SERVER_LIST || i < 0) + Sys_Error ("SList_Reset_NoFree: Bad index %d", i); + + slist[i].description = slist[i].server = NULL; +} + +void SList_Reset (int i) +{ + if (i >= MAX_SERVER_LIST || i < 0) + Sys_Error ("SList_Reset: Bad index %d", i); + + if (slist[i].server) + { + Z_Free (slist[i].server); + slist[i].server = NULL; + } + + if (slist[i].description) + { + Z_Free (slist[i].description); + slist[i].description = NULL; + } +} + +void SList_Switch (int a, int b) +{ + server_entry_t temp; + + if (a >= MAX_SERVER_LIST || a < 0) + Sys_Error ("SList_Switch: Bad index %d", a); + if (b >= MAX_SERVER_LIST || b < 0) + Sys_Error ("SList_Switch: Bad index %d", b); + + memcpy (&temp, &slist[a], sizeof(temp)); + memcpy (&slist[a], &slist[b], sizeof(temp)); + memcpy (&slist[b], &temp, sizeof(temp)); +} + +int SList_Length (void) +{ + int count; + + for (count = 0 ; count < MAX_SERVER_LIST && slist[count].server ; count++) + ; + + return count; +} + +void SList_Load (void) +{ + int c, len, argc, count; + char line[128], *desc, *addr; + FILE *f; + + if (!(f = fopen (va("%s/servers.lst", com_gamedir), "rt"))) + //if (!(f = fopen("servers.lst", "rt"))) + return; + + count = len = 0; + while ((c = getc(f))) + { + if (c == '\n' || c == '\r' || c == EOF) + { + if (c == '\r' && (c = getc(f)) != '\n' && c != EOF) + ungetc (c, f); + + line[len] = 0; + len = 0; + Cmd_TokenizeString (line); + + if ((argc = Cmd_Argc()) >= 1) + { + addr = Cmd_Argv(0); + desc = (argc >= 2) ? Cmd_Args() : "Unknown"; + SList_Set (count, addr, desc); + if (++count == MAX_SERVER_LIST) + break; + } + if (c == EOF) + break; //just in case an EOF follows a '\r' + } + else + { + if (len + 1 < sizeof(line)) + line[len++] = c; + } + } + + fclose (f); +} + +void SList_Save (FILE *f) +{ + int i; + + for (i=0 ; ientity == 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"); +} + +enum +{ + DECAL_NONE, + DECAL_BULLTMRK, + DECAL_BLOODMRK1, + DECAL_BLOODMRK2, + DECAL_BLOODMRK3, + DECAL_EXPLOMRK +}; +//============================================================================== +extern cvar_t r_decal_bullets; +extern cvar_t r_decal_explosions; + +extern int decal_blood1, decal_blood2, decal_blood3, decal_burn, decal_mark, decal_glow; +//============================================================================== +/* +================= +CL_ParseTEnt +================= +*/ +void CL_ParseTEnt (void) +{ + int type; + vec3_t pos; + 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 (); + if (r_part_spikes.value == 2 && !cl_q3gunshot_mod) + cl_q3gunshot_mod = Mod_ForName ("progs/bullet.md3", true); + 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 (); + if (r_part_spikes.value == 2 && !cl_q3gunshot_mod) + cl_q3gunshot_mod = Mod_ForName ("progs/bullet.md3", true); + 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 (); + if (r_part_spikes.value == 2 && !cl_q3gunshot_mod) + cl_q3gunshot_mod = Mod_ForName ("progs/bullet.md3", true); + //R00k--start + if (r_decal_bullets.value) + { + R_SpawnDecalStatic(pos, decal_mark, 8); + } + //R00k--end + + R_RunParticleEffect (pos, vec3_origin, 0, 10); + + break; + case TE_SUPERSPIKE: // super spike hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + if (r_part_spikes.value == 2 && !cl_q3gunshot_mod) + cl_q3gunshot_mod = Mod_ForName ("progs/bullet.md3", true); + R_RunParticleEffect (pos, vec3_origin, 0, 20); + + //R00k--start + if (r_decal_bullets.value) + { + R_SpawnDecalStatic(pos, decal_mark, 10); + } + //R00k--end + + break; + + case TE_GUNSHOT: // bullet hitting wall + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + if (r_part_gunshots.value == 2 && !cl_q3gunshot_mod) + cl_q3gunshot_mod = Mod_ForName ("progs/bullet.md3", true); + + //R00k--start + if (r_decal_bullets.value) + { + R_SpawnDecalStatic(pos, decal_mark, 8); + } + //R00k--end + 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); + if (r_decal_explosions.value) + { + R_SpawnDecalStatic(pos, decal_burn, 100); + } + 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, 0.5); + break; + case TE_RAYSPLASHGREEN: + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + dl = CL_AllocDlight (0); + VectorCopy (pos, dl->origin); + dl->radius = 65; + dl->die = cl.time + 0.3; + dl->decay = 300; + dl->color[0] = 0; + dl->color[1] = 255; + dl->color[2] = 0; + R_RunParticleEffect (pos, vec3_origin, 0, 256); + //S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 0.5); // NZPFIXME - add raygun hum + break; + case TE_RAYSPLASHRED: + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + dl = CL_AllocDlight (0); + VectorCopy (pos, dl->origin); + dl->radius = 65; + dl->die = cl.time + 0.3; + dl->decay = 300; + dl->color[0] = 255; + dl->color[1] = 0; + dl->color[2] = 0; + R_RunParticleEffect (pos, vec3_origin, 0, 512); + //S_StartSound (-1, 0, cl_sfx_r_exp3, pos, 1, 0.5); // NZPFIXME - add raygun hum + 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); + dl = CL_AllocDlight (0); + VectorCopy (pos, dl->origin); + dl->radius = 150; + dl->die = cl.time + 0.75; + dl->decay = 200; + break; + + case TE_TELEPORT: + pos[0] = MSG_ReadCoord (); + pos[1] = MSG_ReadCoord (); + pos[2] = MSG_ReadCoord (); + if (r_part_telesplash.value == 2 && !cl_q3teleport_mod) + cl_q3teleport_mod = Mod_ForName ("progs/telep.md3", true); + 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); + + if (r_decal_explosions.value) + { + R_SpawnDecalStatic(pos, decal_burn, 100); + } + + 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; + + 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; +} + +/* +================= +TraceLineN +================= +*/ +qboolean TraceLineN (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal) +{ + trace_t trace; + + memset (&trace, 0, sizeof(trace)); + if (!SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, 0, 1, start, end, &trace)) + { + if (trace.fraction < 1) + { + VectorCopy (trace.endpos, impact); + if (normal) + VectorCopy (trace.plane.normal, normal); + + return true; + } + } + + return false; +} + +void QMB_Lightning_Splash (vec3_t org); +extern cvar_t scr_ofsy; +/* +================= +CL_UpdateTEnts +================= +*/ +void CL_UpdateTEnts (void) +{ + int i; + beam_t *b; + vec3_t dist, org, beamstart; + float d; + entity_t *ent; + float yaw, pitch; + float forward; + + int j; + vec3_t beamend; +// qboolean sparks = false; + + 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); + + b->start[2] += cl.crouch + bound(-7, scr_ofsy.value, 4); + b->start[2] += bound(0, cl_lightning_zadjust.value, 20);//progs.dat aims from 20 for traceline + + if (cl_truelightning.value) + { + vec3_t forward, v, org, ang; + float f, delta; + trace_t trace; + + f = fmax(0, fmin(1, cl_truelightning.value)); + + VectorSubtract (playerbeam_end, cl_entities[cl.viewentity].origin, v); + //v[2] -= 22; // adjust for view height + v[2] -= cl.crouch; // + v[2] -= bound(0, cl_lightning_zadjust.value, 20); + + vectoangles (v, ang); + + // lerp pitch + ang[0] = -ang[0]; + if (ang[0] < -180) + ang[0] += 360; + ang[0] += (cl.viewangles[0] - ang[0]) * f; + + // lerp yaw + delta = cl.viewangles[1] - ang[1]; + if (delta > 180) + delta -= 360; + if (delta < -180) + delta += 360; + ang[1] += delta * f; + ang[2] = 0; + + AngleVectors (ang, forward, NULLVEC, NULLVEC); + VectorScale(forward, 600, forward); + VectorCopy(cl_entities[cl.viewentity].origin, org); + org[2] += bound(0, cl_lightning_zadjust.value, 20);//progs.dat aims from 20 for teaceline + VectorAdd(org, forward, b->end); + + memset (&trace, 0, sizeof(trace_t)); + if (!SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, 0, 1, org, b->end, &trace)) + VectorCopy(trace.endpos, b->end); + } + } + +/* + // 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) (atan2f(dist[1], dist[0]) * 180 / M_PI); + if (yaw < 0) + yaw += 360; + + forward = sqrtf (dist[0]*dist[0] + dist[1]*dist[1]); + pitch = (int) (atan2f(dist[2], forward) * 180 / M_PI); + if (pitch < 0) + pitch += 360; + } + // add new entities for the lightning + VectorCopy(b->start, org); + VectorCopy(b->start, beamstart); + d = VectorNormalize (dist); + VectorScale (dist, 30, dist); + + if (key_dest == key_game) + { + for ( ; d > 0 ; d -= 30) + { + if ((qmb_initialized && r_part_lightning.value) && (!cl.paused)) + { + VectorAdd(org, dist, beamend); + for (j=0 ; j<3 ; j++) + beamend[j] += ((rand()%10)-5); + QMB_LightningBeam (beamstart, beamend); + //if ((r_glowlg.value) && (r_dynamic.value)) + // CL_NewDlight (i, beamstart, 100, 0.1, lt_blue); + VectorCopy(beamend, beamstart); + } + else + { + if (!(ent = CL_NewTempEntity())) + return; + VectorCopy(org, ent->origin); + ent->model = b->model; + ent->angles[0] = pitch; + ent->angles[1] = yaw; + ent->angles[2] = rand() % 360; + } + VectorAdd(org, dist, org); + } + } +/* + // 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; + } +*/ + } + +} + + diff --git a/source/client.h b/source/client.h new file mode 100644 index 0000000..bac4374 --- /dev/null +++ b/source/client.h @@ -0,0 +1,433 @@ +/* +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; +} usercmd_t; + +typedef struct +{ + int length; + char map[MAX_STYLESTRING]; +} lightstyle_t; + +typedef struct +{ + char name[MAX_SCOREBOARDNAME]; + float entertime; + int points; + int maxpoints; + int kills; + int headshots; +} scoreboard_t; + +typedef struct +{ + int destcolor[3]; + int percent; // 0-256 +} cshift_t; + +typedef enum +{ + lt_default, lt_muzzleflash, lt_explosion, lt_rocket, + lt_red, lt_blue, lt_redblue, lt_green, NUM_DLIGHTTYPES, + lt_explosion2, lt_explosion3, lt_rayred, lt_raygreen +} dlighttype_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 32 +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; + qboolean dark; // subtracts light instead of adding + vec3_t color; //LordHavoc Lit. Support + int type; // color +} 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; + +// +// 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) + qboolean demorecording; + qboolean demoplayback; + qboolean timedemo; + int forcetrack; // -1 = use normal cd track + int 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 + +} client_static_t; + +extern client_static_t cls; + +typedef struct { + float lerptime; + float framechange; //marks time of last frame change - for halflife model sequencing. + float oldframechange; + float lerprate; //inverse rate... + vec3_t origin; + vec3_t angles; + //trailstate_t *trailstate; //when to next throw out a trail +// trailstate_t *emitstate; //when to next emit + unsigned short frame; +} lerpents_t; +// +// 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 perks; // Perk icons. + int progress_bar; // Perk icons. + 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 + vec3_t gun_kick; // temporary kick + +// pitch drifting vars + float idealpitch; + float pitchvel; + qboolean nodrift; + float driftmove; + double laststop; + + float viewheight; + float crouch; // local amount for smoothing stepups + + qboolean paused; // send over by server + qboolean onground; + qboolean 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 + double ctime; // joe: copy of cl.time, to avoid incidents caused by rewind + double thundertime; // R00k + double laser_point_time; + 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_entities[cl.viewentity] = player + int maxclients; + int gametype; + + lerpents_t *lerpents; + +// refresh related state + struct model_s *worldmodel; // cl_entities[0].model + struct efrag_s *free_efrags; + int num_entities; // held in cl_entities array + int num_statics; // held in cl_staticentities array + entity_t viewent; // the gun model + entity_t viewent2; // the second 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_maxfps; // dr_mabuse1981: maxfps setting + +extern cvar_t cl_upspeed; +extern float cl_forwardspeed; +extern float cl_backspeed; +extern float cl_sidespeed; + +extern cvar_t cl_movespeedkey; + +extern cvar_t cl_yawspeed; +extern cvar_t cl_pitchspeed; +extern cvar_t r_hlbsponly; +extern cvar_t cl_anglespeedkey; + +extern cvar_t cl_lightning_zadjust; +extern cvar_t cl_truelightning; + +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 in_sensitivity; +extern cvar_t in_tolerance; +extern cvar_t in_acceleration; +extern cvar_t in_analog_strafe; +extern cvar_t in_x_axis_adjust; +extern cvar_t in_y_axis_adjust; + +extern cvar_t in_mlook; //Heffo - mlook cvar + +extern cvar_t in_aimassist; + +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 +#define MAX_STATIC_ENTITIES 128 // torches, etc + +extern client_state_t cl; + +// FIXME, allocate dynamically +extern efrag_t cl_efrags[MAX_EFRAGS]; +extern entity_t cl_entities[MAX_EDICTS]; +extern entity_t cl_static_entities[MAX_STATIC_ENTITIES]; +extern lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; +extern dlight_t cl_dlights[MAX_DLIGHTS]; +extern entity_t cl_temp_entities[MAX_TEMP_ENTITIES]; +extern beam_t cl_beams[MAX_BEAMS]; + +//============================================================================= + +// +// cl_main +// +dlight_t *CL_AllocDlight (int key); +void CL_NewDlight (int key, vec3_t origin, float radius, float time, int type); +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]; + + + +extern tagentity_t q3player_body, q3player_head; + +// model indexes +typedef enum modelindex_s +{ + mi_player, + mi_eyes, + mi_flame0, + mi_flame1, + mi_flame2, + mi_q3torso, + mi_q3head, +/* + mi_vw_light, + mi_vw_nail1, + mi_vw_nail2, + mi_vw_rock1, + mi_vw_rock2, + mi_vw_shot1, + mi_vw_shot2, + mi_vw_player, +*/ + NUM_MODELINDEX +} modelindex_t; + +extern modelindex_t cl_modelindex[NUM_MODELINDEX]; +extern char *cl_modelnames[NUM_MODELINDEX]; + +// +// 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_klook;//Heffo - mlook cvar +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); + +// +// cl_parse.c +// +void CL_ParseServerMessage (void); + +// +// view +// +void V_StartPitchDrift (void); +void V_StopPitchDrift (void); + +void V_RenderView (void); +void V_UpdatePalette (void); +void V_Register (void); +void V_SetContentsColor (int contents); + + +// +// cl_tent +// +void CL_InitTEnts (void); +void CL_SignonReply (void); +entity_t *CL_NewTempEntity (void); +qboolean TraceLineN (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal); diff --git a/source/cmd.c b/source/cmd.c new file mode 100644 index 0000000..7f6d0b6 --- /dev/null +++ b/source/cmd.c @@ -0,0 +1,874 @@ +/* +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); + +#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; + +qboolean 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 (char *text) +{ + int l; + + l = Q_strlen (text); + + if (cmd_text.cursize + l >= cmd_text.maxsize) + { + Con_Printf ("Cbuf_AddText: overflow\n"); + return; + } + + SZ_Write (&cmd_text, text, Q_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; + int templen; + +// copy off any commands still remaining in the exec buffer + templen = cmd_text.cursize; + if (templen) + { + temp = Z_Malloc (templen); + Q_memcpy (temp, cmd_text.data, templen); + SZ_Clear (&cmd_text); + } + else + temp = NULL; // shut up compiler + +// 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[1024]; + int quotes; + + while (cmd_text.cursize) + { +// find a \n or ; line break + text = (char *)cmd_text.data; + + quotes = 0; + for (i=0 ; i< cmd_text.cursize ; i++) + { + if (text[i] == '"') + quotes++; + if ( !(quotes&1) && text[i] == ';') + break; // don't break if inside a quoted string + if (text[i] == '\n') + break; + } + + + memcpy (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; + Q_memcpy (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; + } + } +} + +/* +============================================================================== + + 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_LoadHunkFile (Cmd_Argv(1)); + 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 allready 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); + +// 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); +} + +/* +=============== +Cmd_Maps_f + +Listing maps by Crow_bar(c). +=============== +*/ +#include +void Cmd_Maps_f (void) +{ + char *s; + + if (Cmd_Argc() < 2) + { + Con_Printf("Usage: maps \n"); + Con_Printf("maps * for full listing\n"); + return; + } + + Con_Printf("-------------\n"); + + s = Cmd_Argv(1); + + SceUID dir = sceIoDopen(va("%s/maps", com_gamedir)); + + if(dir < 0) + { + return; + } + + SceIoDirent dirent; + + memset(&dirent, 0, sizeof(SceIoDirent)); + + while(sceIoDread(dir, &dirent) > 0) + { + if(dirent.d_name[0] == '.') + { + continue; + } + + if(!strcasecmp(COM_FileExtension (dirent.d_name),"bsp")) + { + if(s[0] == '*') + { + Con_Printf("%s\n",dirent.d_name); + } + else + { + int i = 0; + while(1) + { + if(s[i] == 0) + { + Con_Printf("%s\n",dirent.d_name); + break; + } +#if 0 + if(toupper(dirent.d_name[i]) != toupper(s[i])) +#else + if(dirent.d_name[i] != s[i]) +#endif + { + break; + } + i++; + } + } + } + memset(&dirent, 0, sizeof(SceIoDirent)); + } + sceIoDclose(dir); + +} + +/* +============================================================================= + + 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 + +// 2000-01-09 CmdList command by Maddes start +/* +======== +Cmd_List +======== +*/ +void Cmd_List_f (void) +{ + cmd_function_t *cmd; + char *partial; + int len; + int count; + + if (Cmd_Argc() > 1) + { + partial = Cmd_Argv (1); + len = Q_strlen(partial); + } + else + { + partial = NULL; + len = 0; + } + + count=0; + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + { + if (partial && Q_strncmp (partial,cmd->name, len)) + { + continue; + } + Con_Printf ("\"%s\"\n", cmd->name); + count++; + } + + Con_Printf ("%i command(s)", count); + if (partial) + { + Con_Printf (" beginning with \"%s\"", partial); + } + Con_Printf ("\n"); +} +// 2000-01-09 CmdList command by Maddes end + +// 2000-01-09 CvarList command by Maddes start +/* +========= +Cvar_List +========= +*/ +void Cvar_List_f (void) +{ + cvar_t *cvar; + char *partial; + int len; + int count; + + if (Cmd_Argc() > 1) + { + partial = Cmd_Argv (1); + len = Q_strlen(partial); + } + else + { + partial = NULL; + len = 0; + } + + count=0; + for (cvar=cvar_vars ; cvar ; cvar=cvar->next) + { + if (partial && Q_strncmp (partial,cvar->name, len)) + { + continue; + } + Con_Printf ("\"%s\" is \"%s\"\n", cvar->name, cvar->string); + count++; + } + + Con_Printf ("%i cvar(s)", count); + if (partial) + { + Con_Printf (" beginning with \"%s\"", partial); + } + Con_Printf ("\n"); +} +// 2000-01-09 CvarList command by Maddes end + + +/* +============ +Cmd_Init +============ +*/ +void Cmd_Init (void) +{ +// +// register our commands +// + Cmd_AddCommand ("cmdlist", Cmd_List_f); // 2000-01-09 CmdList command by Maddes + Cmd_AddCommand ("cvarlist", Cvar_List_f); // 2000-01-09 CvarList command by Maddes + 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 ("maps", Cmd_Maps_f); //Crow_bar +} + +/* +============ +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 (char *text) +{ + int i; + +// clear the args from the last string + for (i=0 ; inext) + { + if (!Q_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 = cmd_name; + cmd->function = function; + cmd->next = cmd_functions; + cmd_functions = cmd; +} + +/* +============ +Cmd_Exists +============ +*/ +qboolean Cmd_Exists (char *cmd_name) +{ + cmd_function_t *cmd; + + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + { + if (!Q_strcmp (cmd_name,cmd->name)) + return true; + } + + return false; +} + + + +/* +============ +Cmd_CompleteCommand +============ +*/ +char *Cmd_CompleteCommand (char *partial) +{ + cmd_function_t *cmd; + int len; + + len = Q_strlen(partial); + + if (!len) + return NULL; + +// check functions + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + if (!Q_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 (!Q_strcasecmp (cmd_argv[0],cmd->name)) + { + cmd->function (); + return; + } + } + +// check alias + for (a=cmd_alias ; a ; a=a->next) + { + if (!Q_strcasecmp (cmd_argv[0], a->name)) + { + Cbuf_InsertText (a->value); + return; + } + } + +// check cvars + if (!Cvar_Command ()) + Con_Printf ("Command \"%s\" not found, check QC...\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 ("Can't \"%s\", not connected\n", Cmd_Argv(0)); + return; + } + + if (cls.demoplayback) + return; // not really connected + + MSG_WriteByte (&cls.message, clc_stringcmd); + if (Q_strcasecmp(Cmd_Argv(0), "cmd") != 0) + { + SZ_Print (&cls.message, Cmd_Argv(0)); + SZ_Print (&cls.message, " "); + } + if (Cmd_Argc() > 1) + SZ_Print (&cls.message, Cmd_Args()); + else + SZ_Print (&cls.message, "\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 (! Q_strcasecmp (parm, Cmd_Argv (i))) + return i; + + return 0; +} diff --git a/source/cmd.h b/source/cmd.h new file mode 100644 index 0000000..1aa3d8d --- /dev/null +++ b/source/cmd.h @@ -0,0 +1,125 @@ +/* +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 nzp.rc\n"); Cbuf_Execute (); + +*/ + + +void Cbuf_Init (void); +// allocates an initial text buffer that will grow as needed + +void Cbuf_AddText (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; + +#define MAX_FILELENGTH 64 + +#define MAXCMDLINE 256 + +void Cmd_Init (void); + +void Cmd_AddCommand (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 + +qboolean Cmd_Exists (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 allways 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 (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 + diff --git a/source/common.c b/source/common.c new file mode 100644 index 0000000..4fa4ab0 --- /dev/null +++ b/source/common.c @@ -0,0 +1,2040 @@ +/* +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 + +#include "quakedef.h" +#include + +#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"}; + +qboolean msg_suppress_1 = 0; + +void COM_InitFilesystem (void); + +char com_token[1024]; +int com_argc; +char **com_argv; + +#define CMDLINE_LENGTH 256 +char com_cmdline[CMDLINE_LENGTH]; + +qboolean user_maps = true; +// 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 Q_memset (void *dest, int fill, int count) +{ + int i; + + if ( (((long)dest | count) & 3) == 0) + { + count >>= 2; + fill = fill | (fill<<8) | (fill<<16) | (fill<<24); + for (i=0 ; i>=2; + for (i=0 ; i= 'a' && c1 <= 'z') + c1 -= ('a' - 'A'); + if (c2 >= 'a' && c2 <= 'z') + c2 -= ('a' - 'A'); + if (c1 != c2) + return -1; // strings not equal + } + if (!c1) + return 0; // strings are equal +// s1++; +// s2++; + } + + return -1; +} + +int Q_strcasecmp (char *s1, char *s2) +{ + return Q_strncasecmp (s1, s2, 99999); +} + +int Q_atoi (char *str) +{ + int val; + int sign; + int c; + + if (*str == '-') + { + sign = -1; + str++; + } + else + sign = 1; + + val = 0; + +// +// check for hex +// + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) + { + str += 2; + while (1) + { + c = *str++; + if (c >= '0' && c <= '9') + val = (val<<4) + c - '0'; + else if (c >= 'a' && c <= 'f') + val = (val<<4) + c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val = (val<<4) + c - 'A' + 10; + else + return val*sign; + } + } + +// +// check for character +// + if (str[0] == '\'') + { + return sign * str[1]; + } + +// +// assume decimal +// + while (1) + { + c = *str++; + if (c <'0' || c > '9') + return val*sign; + val = val*10 + c - '0'; + } + + return 0; +} + + +float Q_atof (char *str) +{ + float val; + int sign; + int c; + int decimal, total; + + if (*str == '-') + { + sign = -1; + str++; + } + else + sign = 1; + + val = 0; + +// +// check for hex +// + if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X') ) + { + str += 2; + while (1) + { + c = *str++; + if (c >= '0' && c <= '9') + val = (val*16) + c - '0'; + else if (c >= 'a' && c <= 'f') + val = (val*16) + c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + val = (val*16) + c - 'A' + 10; + else + return val*sign; + } + } + +// +// check for character +// + if (str[0] == '\'') + { + return sign * str[1]; + } + +// +// assume decimal +// + decimal = -1; + total = 0; + while (1) + { + c = *str++; + if (c == '.') + { + decimal = total; + continue; + } + if (c <'0' || c > '9') + break; + val = val*10 + c - '0'; + total++; + } + + if (decimal == -1) + return val*sign; + while (total > decimal) + { + val /= 10; + total--; + } + + return val*sign; +} + +void Q_strncpyz (char *dest, char *src, size_t size) +{ + strncpy (dest, src, size - 1); + dest[size-1] = 0; +} + +void Q_snprintfz (char *dest, size_t size, char *fmt, ...) +{ + va_list argptr; + + va_start (argptr, fmt); + vsnprintf (dest, size, fmt, argptr); + va_end (argptr); + + dest[size-1] = 0; +} + +/* +============================================================================ + + BYTE ORDER FUNCTIONS + +============================================================================ +*/ + +qboolean 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; + + + buf = SZ_GetSpace (sb, 1); + buf[0] = c; +} + +void MSG_WriteByte (sizebuf_t *sb, int c) +{ + byte *buf; + + buf = SZ_GetSpace (sb, 1); + buf[0] = c; +} + +void MSG_WriteShort (sizebuf_t *sb, int c) +{ + byte *buf; + + + 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, char *s) +{ + if (!s) + SZ_Write (sb, "", 1); + else + SZ_Write (sb, s, Q_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); +} + +// +// reading functions +// +int msg_readcount; +qboolean 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); +} + + + +//=========================================================================== + +void SZ_Alloc (sizebuf_t *buf, int startsize) +{ + if (startsize < 256) + startsize = 256; + //if (startsize < 1024) + // startsize = 1024; + 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; + //buf->overflowed = false; +} + +void *SZ_GetSpace (sizebuf_t *buf, int length) +{ + void *data; + + if (buf->cursize + length > buf->maxsize) + { + if (!buf->allowoverflow) + Sys_Error ("SZ_GetSpace: overflow without allowoverflow set"); + + if (length > buf->maxsize) + Sys_Error ("SZ_GetSpace: %i is > full buffer size", 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, void *data, int length) +{ + Q_memcpy (SZ_GetSpace(buf,length),data,length); +} + +void SZ_Print (sizebuf_t *buf, char *data) +{ + int len; + + len = Q_strlen(data)+1; + +// byte * cast to keep VC++ happy + if (buf->data[buf->cursize-1]) + Q_memcpy ((byte *)SZ_GetSpace(buf, len),data,len); // no trailing 0 + else + Q_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_SkipPathWritable +============ +*/ +char *COM_SkipPathWritable (char *pathname) +{ + char *last; + char *p; + + last = p = pathname; + + while (*p) + { + if (*p == '/' || *p == '\\') + last = p + 1; + + p++; + } + + return last; +} + +/* +============ +COM_StripExtension +============ +*/ +void COM_StripExtension (char *in, char *out) +{ + while (*in && *in != '.') + *out++ = *in++; + *out = 0; +} + +/* +============ +COM_FileExtension +============ +*/ +char *COM_FileExtension (char *in) +{ + static char exten[8]; + int i; + + while (*in && *in != '.') + in++; + if (!*in) + return ""; + in++; + for (i=0 ; i<7 && *in ; i++,in++) + exten[i] = *in; + exten[i] = 0; + return exten; +} + +/* +============ +COM_FileBase +============ +*/ +void COM_FileBase (char *in, char *out) +{ + char *s, *s2; + + s = in + strlen(in) - 1; + + while (s != in && *s != '.') { + s--; + } + + // naievil -- added (s2 != in) in order to prevent progs.dat from returning a + // base name of string before the beginning of the original memory pointer! + for (s2 = s ; (s2 != in) && (*s2 != '/') ; s2--) + ; + + if (s-s2 < 2) + strcpy (out,"?model?"); + else + { + s--; + strncpy (out,s2+1, s-s2); + out[s-s2] = 0; + } +} + + +/* +================== +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 (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 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 data+1; + } + +// parse a regular word + do + { + com_token[len] = c; + data++; + len++; + c = *data; + if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':') + break; + } while (c>32); + + com_token[len] = 0; + return data; +} + + +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char *COM_ParseQ2 (char **data_p) +{ + int c; + int len; + char *data; + + data = *data_p; + len = 0; + com_token[0] = 0; + + if (!data) + { + *data_p = NULL; + return ""; + } + +// skip whitespace +skipwhite: + while ( (c = *data) <= ' ') + { + if (c == 0) + { + *data_p = NULL; + return ""; + } + 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; + *data_p = data; + return com_token; + } + if (len < MAX_TOKEN_CHARS) + { + com_token[len] = c; + len++; + } + } + } + +// parse a regular word + do + { + if (len < MAX_TOKEN_CHARS) + { + com_token[len] = c; + len++; + } + data++; + c = *data; + } while (c>32); + + if (len == MAX_TOKEN_CHARS) + { +// Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS); + len = 0; + } + com_token[len] = 0; + + *data_p = data; + return com_token; +} + +/* +================ +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 (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]; + + sprintf (name, "%s/%s", com_gamedir, filename); + //Q_snprintfz (name, MAX_OSPATH, "%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[4096]; + + 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); +} + +/* +=========== +COM_FindFile + +Finds the file in the search path. +Sets com_filesize and one of handle or file +=========== +*/ +int COM_FindFile (char *filename, int *handle, int *file) +{ + 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; + + 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 (developer.value == 2) + Sys_Printf("OpenCustomPack: %s : %s\n", pak->filename, filename); + + if (handle) + { + *handle = pak->handle; + Sys_FileSeek (pak->handle, pak->files[i].filepos); + } + else + { // open a new file on the pakfile + Sys_FileOpenRead(pak->filename, file); + if ((*file) >= 0) + Sys_FileSeek(*file, pak->files[i].filepos); + } + com_filesize = pak->files[i].filelen; + return com_filesize; + } + } + else + { + + sprintf (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); + + if (developer.value == 2) + Sys_Printf ("FindFile: %s\n",netpath); + + com_filesize = Sys_FileOpenRead (netpath, &i); + if (handle) + *handle = i; + else + { + Sys_FileClose (i); + Sys_FileOpenRead(netpath, file); + } + return com_filesize; + } + + } + Sys_Printf ("FindFile: can't find %s\n", filename); + + if (developer.value == 2) + Con_DPrintf ("FindFile: can't find %s\n", filename); + + if (handle) + *handle = -1; + else + *file = -1; + 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 (char *filename, int *handle) +{ + return COM_FindFile (filename, handle, NULL); +} + +/* +=========== +COM_FOpenFile + +If the requested file is inside a packfile, a new file will be opened +into the file. +=========== +*/ +int COM_FOpenFile (char *filename, int *file) +{ + return COM_FindFile (filename, NULL, file); +} + +/* +================= +FS_FOpenFile + +Finds the file in the search path. +Sets com_filesize and one of handle or file +================= +*/ + +int COM_filelength (FILE *f) +{ + int pos, end; + + pos = ftell (f); + fseek (f, 0, SEEK_END); + end = ftell (f); + fseek (f, pos, SEEK_SET); + + return end; +} + +int FS_FOpenFile (char *filename, FILE **file) +{ + searchpath_t *search; + pack_t *pak; + int i; + + *file = NULL; + + com_filesize = -1; + com_netpath[0] = 0; + +// search through the path, one element at a time + for (search = com_searchpaths ; 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! + { + if (developer.value) + Sys_Printf ("PackFile: %s : %s\n", pak->filename, filename); + // open a new file on the pakfile + if (!(*file = fopen(pak->filename, "rb"))) + Sys_Error ("Couldn't reopen %s", pak->filename); + fseek (*file, pak->files[i].filepos, SEEK_SET); + com_filesize = pak->files[i].filelen; + + Q_snprintfz (com_netpath, sizeof(com_netpath), "%s#%i", pak->filename, i); + return com_filesize; + } + } + } + else + { + // check a file in the directory tree + Q_snprintfz (com_netpath, sizeof(com_netpath), "%s/%s", search->filename, filename); + + if (!(*file = fopen(com_netpath, "rb"))) + continue; + + if (developer.value) + Sys_Printf ("FOpenFile: %s\n", com_netpath); + + com_filesize = COM_filelength (*file); + return com_filesize; + } + + } + + if (developer.value == 2) + Con_Printf ("COM_FOpenFile: can't find %s\n", filename); + + *file = NULL; + com_filesize = -1; + + return -1; +} + +/* +================= +FS_FindFile + +finds files in given path including inside paks as well +================= +*/ +qboolean FS_FindFile (char *filename) +{ + searchpath_t *search; + char netpath[MAX_OSPATH]; + pack_t *pak; + int i; + + for (search = com_searchpaths ; search ; search = search->next) + { + if (search->pack) + { + pak = search->pack; + for (i=0 ; inumfiles ; i++) + if (!strcmp(pak->files[i].name, filename)) + return true; + } + else + { + Q_snprintfz (netpath, sizeof(com_netpath), "%s/%s", search->filename, filename); + if (Sys_FileTime(netpath) != -1) + return true; + } + } + + return false; +} + +/* +============ +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. +Allways appends a 0 byte. +============ +*/ +cache_user_t *loadcache; +byte *loadbuf; +int loadsize; +byte *COM_LoadFile (char *path, int usehunk) +{ + 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); + if (h == -1) + return NULL; + +// extract the filename base name for hunk tag + COM_FileBase (path, base); + + if (usehunk == 1) + buf = Hunk_AllocName (len+1, base); + else if (usehunk == 2) + buf = Hunk_TempAlloc (len+1); + else if (usehunk == 0) + buf = Z_Malloc (len+1); + else if (usehunk == 3) + buf = Cache_Alloc (loadcache, len+1, base); + else if (usehunk == 4) + { + if (len+1 > loadsize) + buf = Hunk_TempAlloc (len+1); + else + buf = loadbuf; + } + else { + Sys_Error ("COM_LoadFile: bad usehunk"); + } + + 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 (char *path) +{ + return COM_LoadFile (path, 1); +} + +byte *COM_LoadTempFile (char *path) +{ + return COM_LoadFile (path, 2); +} + +void COM_LoadCacheFile (char *path, struct cache_user_s *cu) +{ + loadcache = cu; + COM_LoadFile (path, 3); +} + +// uses temp hunk if larger than bufsize +byte *COM_LoadStackFile (char *path, void *buffer, int bufsize) +{ + byte *buf; + + loadbuf = (byte *)buffer; + loadsize = bufsize; + buf = COM_LoadFile (path, 4); + + return buf; +} + +/* +================= +COM_LoadPackFile + +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;//[MAX_FILES_IN_PACK]; + unsigned short crc; + + info = malloc(sizeof(dpackfile_t) * MAX_FILES_IN_PACK); + + if (Sys_FileOpenRead (packfile, &packhandle) == -1) + { +// Con_Printf ("Couldn't open %s\n", packfile); + free(info); + 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) + { + free(info); + Sys_Error ("%s has %i files", packfile, numpackfiles); + } + + newfiles = Hunk_AllocName (numpackfiles * sizeof(packfile_t), "packfile"); + + 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; + free(info); + Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles); + return pack; +} + + +/* +int COM_FileOpenRead (char *path, FILE **hndl) +{ + FILE *f; + + if (!(f = fopen(path, "rb"))) { + *hndl = NULL; + return -1; + } + *hndl = f; + + return COM_filelength(f); +} + + +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; + pack_t *pack; + FILE *packhandle; + dpackfile_t *info; + + if (COM_FileOpenRead (packfile, &packhandle) == -1) + return NULL; + + fread (&header, 1, sizeof(header), packhandle); + 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); + + pack = malloc (sizeof (pack_t)); + strcpy (pack->filename, packfile); + pack->handle = packhandle; + pack->numfiles = header.dirlen / sizeof(dpackfile_t); + + pack->files = newfiles = malloc (pack->numfiles * sizeof(packfile_t)); + info = malloc (header.dirlen); + + fseek (packhandle, header.dirofs, SEEK_SET); + fread (info, 1, header.dirlen, packhandle); + + // parse the directory + for (i = 0; i < pack->numfiles; i++) { + strcpy (newfiles[i].name, info[i].name); + newfiles[i].filepos = LittleLong(info[i].filepos); + newfiles[i].filelen = LittleLong(info[i].filelen); + } + + free(info); + return pack; +} +*/ + +/* +================ +COM_AddGameDirectory + +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]; + + strcpy (com_gamedir, dir); +// +// add the directory to the search path +// + search = Hunk_Alloc (sizeof(searchpath_t)); + strcpy (search->filename, dir); + search->next = com_searchpaths; + com_searchpaths = search; + +// +// add any pak files in the format pak0.pak pak1.pak, ... +// + for (i=0 ; ; i++) + { + sprintf (pakfile, "%s/pak%i.pak", dir, i); + pak = COM_LoadPackFile (pakfile); + if (!pak) + break; + search = Hunk_Alloc (sizeof(searchpath_t)); + search->pack = pak; + search->next = com_searchpaths; + com_searchpaths = search; + } + +// +// add the contents of the parms.txt file to the end of the command line +// + +} + +/* +================ +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 (nzp) and DQDIRNAME for DQ extentions. Crow_bar +// + COM_AddGameDirectory (va("%s/"GAMENAME, basedir) ); + +// +// -game +// Adds basedir/gamedir as an override game +// + i = COM_CheckParm ("-game"); + if (i && i < com_argc-1) + { + 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_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; + } + } +} + + + diff --git a/source/common.h b/source/common.h new file mode 100644 index 0000000..2a13989 --- /dev/null +++ b/source/common.h @@ -0,0 +1,203 @@ +/* +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 + +#if !defined BYTE_DEFINED +typedef unsigned char byte; +#define BYTE_DEFINED 1 +#endif + +#undef true +#undef false + +#ifdef __cplusplus +typedef enum {qfalse, qtrue} qboolean; +#else +typedef enum {false, true} qboolean; +#endif + +#ifndef fmin +#define fmin(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef fmax +#define fmax(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#define bound(a, b, c) ((a) >= (c) ? (a) : (b) < (a) ? (a) : (b) > (c) ? (c) : (b)) + +//============================================================================ + +typedef struct sizebuf_s +{ + qboolean allowoverflow; // if false, do a Sys_Error + qboolean 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, 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 MAX_TOKEN_CHARS 128 // max length of an individual token + +//============================================================================ + +extern qboolean 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, char *s); +void MSG_WriteCoord (sizebuf_t *sb, float f); +void MSG_WriteAngle (sizebuf_t *sb, float f); + +extern int msg_readcount; +extern qboolean 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); + +//============================================================================ +void Q_strncpyz (char *dest, char *src, size_t size); +void Q_snprintfz (char *dest, size_t size, char *fmt, ...); +void Q_memset (void *dest, int fill, int count); +void Q_memcpy (void *dest, void *src, int count); +int Q_memcmp (void *m1, void *m2, int count); +void Q_strcpy (char *dest, char *src); +void Q_strncpy (char *dest, char *src, int count); +int Q_strlen (char *str); +char *Q_strrchr (char *s, char c); +void Q_strcat (char *dest, char *src); +int Q_strcmp (char *s1, char *s2); +int Q_strncmp (char *s1, char *s2, int count); +int Q_strcasecmp (char *s1, char *s2); +int Q_strncasecmp (char *s1, char *s2, int n); +int Q_atoi (char *str); +float Q_atof (char *str); + +extern char com_token[1024]; +extern qboolean com_eof; + +char *COM_Parse (char *data); +char *COM_ParseQ2 (char **data_p); + +extern int com_argc; +extern char **com_argv; + +int COM_CheckParm (char *parm); +void COM_Init (char *path); +void COM_InitArgv (int argc, char **argv); + +char *COM_SkipPath (char *pathname); +char *COM_SkipPathWritable (char *pathname); +void COM_StripExtension (char *in, char *out); +void COM_FileBase (char *in, char *out); +void COM_DefaultExtension (char *path, char *extension); + +char *va(char *format, ...); +char *CopyString (char *in); +// 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 (char *filename, int *hndl); +int COM_FOpenFile (char *filename, int *file); +void COM_CloseFile (int h); +void COM_CreatePath (char *path); +char *COM_FileExtension (char *in); +byte *COM_LoadStackFile (char *path, void *buffer, int bufsize); +byte *COM_LoadTempFile (char *path); +byte *COM_LoadHunkFile (char *path); +byte *COM_LoadFile (char *path, int usehunk); +void COM_LoadCacheFile (char *path, struct cache_user_s *cu); +//============================================================================ +qboolean FS_FindFile (char *filename); +int FS_FOpenFile (char *filename, FILE **file); +extern char com_netpath[MAX_OSPATH]; +//============================================================================ + +extern qboolean user_maps; diff --git a/source/console.c b/source/console.c new file mode 100644 index 0000000..72a75c6 --- /dev/null +++ b/source/console.c @@ -0,0 +1,679 @@ +/* +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 16384 + +qboolean 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_t con_notifytime = {"con_notifytime","3"}; //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; + +qboolean con_debuglog; + +#define MAXCMDLINE 256 +extern char key_lines[32][MAXCMDLINE]; +extern int edit_line; +extern int key_linepos; + + +qboolean con_initialized; + +int con_notifylines; // scan lines to clear for notify lines + +extern void M_Menu_Main_f (void); + +void M_OSK_Draw (void); +void Con_OSK_f (char *input, char *output, int outlen); +void Con_OSK_Key(int key); +void Con_DrawOSK(void); +extern qboolean console_enabled; +/* +================ +Con_ToggleConsole_f +================ +*/ +void Con_ToggleConsole_f (void) +{ + if (key_dest == key_console) + { + if (cls.state == ca_connected) + { + key_dest = key_game; + console_enabled = false; + key_lines[edit_line][1] = 0; // clear any typing + key_linepos = 1; + } + else + { + console_enabled = false; + 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) + Q_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 = 38; + con_linewidth = width; + con_totallines = CON_TEXTSIZE / con_linewidth; + Q_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; + + Q_memcpy (tbuf, con_text, CON_TEXTSIZE); + Q_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 4096 +// FIXME: make a buffer size safe vsprintf? +void Con_Printf (char *fmt, ...) +{ + va_list argptr; + char msg[MAXPRINTMSG]; + static qboolean 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/condebug.log",com_gamedir), "%s", msg); + + if (!con_initialized) + 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; + } + } +} + +/* +================ +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[MAXPRINTMSG]; + + if (!developer.value) + return; // don't confuse non-developers with techie stuff... + + va_start (argptr,fmt); + vsprintf (msg,fmt,argptr); + va_end (argptr); + + Con_Printf ("%s", msg); +} + + +/* +================== +Con_SafePrintf + +Okay to call even when the screen can't be updated +================== +*/ +void Con_SafePrintf (char *fmt, ...) +{ + va_list argptr; + char msg[1024]; + 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; +} + + +/* +============================================================================== + +DRAWING + +============================================================================== +*/ + + +/* +================ +Con_DrawInput + +The input line scrolls horizontally if typing goes beyond the right edge +================ +*/ +void Con_DrawInput (void) +{ + int y; + 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 + y = con_vislines-16; + + 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++) + Draw_Character ( (x+1)<<3, v, text[x]); + + v += 8; + } + + + if (key_dest == key_message) + { + clearnotify = 0; + scr_copytop = 1; + + x = 0; + + Draw_String (8, v, "say:"); + while(chat_buffer[x]) + { + Draw_Character ( (x+5)<<3, v, chat_buffer[x]); + x++; + } + Draw_Character ( (x+5)<<3, v, 10+((int)(realtime*con_cursorspeed)&1)); + v += 8; + } + + 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 +================ +*/ +qboolean console_enabled; +void Con_DrawConsole (int lines, qboolean drawinput) +{ + int i, x, y; + int rows; + char *text; + int j; + + if (lines <= 0) + return; + +// draw the background + Draw_ConsoleBackground (lines); + if (!console_enabled && !developer.value) + return; + +// draw the text + con_vislines = lines; + + rows = (lines-16)>>3; // rows of text to draw + y = lines - 16 - (rows<<3); // may start slightly negative + + for (i= con_current - rows + 1 ; i<=con_current ; i++, y+=8 ) + { + j = i - con_backscroll; + if (j<0) + j = 0; + text = con_text + (j % con_totallines)*con_linewidth; + + for (x=0 ; x> 8) ^ data]; +} + +unsigned short CRC_Value(unsigned short crcvalue) +{ + return crcvalue ^ CRC_XOR_VALUE; +} diff --git a/source/crc.h b/source/crc.h new file mode 100644 index 0000000..cad9772 --- /dev/null +++ b/source/crc.h @@ -0,0 +1,24 @@ +/* +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); diff --git a/source/crypter.c b/source/crypter.c new file mode 100644 index 0000000..68a8bb2 --- /dev/null +++ b/source/crypter.c @@ -0,0 +1,65 @@ +/* + DATA Decrypt +*/ +#include +#include +char rotate(char c, int key) +{ + int l = 'Z' - 'A'; + + c += key % l; + + if(c < 'A') + c += l; + + if(c > 'Z') + c -= l; + + return c; +} + +char encrypt(char c, int key) +{ + if(c >= 'a' && c <= 'z') + c = toupper(c); + + if(c >= 'A' && c <= 'Z') + c = rotate(c, key); + + return c; +} + +char decrypt(char c, int key) +{ + + if(c < 'A' || c > 'Z') + return c; + else + return rotate(c, key); + +} + +char *strencrypt(char *s, int key, int len) +{ + int i; + char *result = malloc(len); + for(i = 0; i < len; i++) + { + result[i] = encrypt(s[i], key); + + } + return result; + +} + +char *strdecrypt(char *s, int key, int len) +{ + int i; + char *result = malloc(len); + for(i = 0; i < len; i++) + { + result[i] = decrypt(s[i], -key); + } + return result; +} + diff --git a/source/crypter.h b/source/crypter.h new file mode 100644 index 0000000..55fc911 --- /dev/null +++ b/source/crypter.h @@ -0,0 +1,12 @@ +/* + RSA DATA Decrypt +*/ + +#ifndef RSA_H +#define RSA_H + +char *strencrypt(char *s, int key, int len); +char *strdecrypt(char *s, int key, int len); + +#endif + diff --git a/source/cvar.c b/source/cvar.c new file mode 100644 index 0000000..293b0c0 --- /dev/null +++ b/source/cvar.c @@ -0,0 +1,223 @@ +/* +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 (char *var_name) +{ + cvar_t *var; + + for (var=cvar_vars ; var ; var=var->next) + if (!Q_strcmp (var_name, var->name)) + return var; + + return NULL; +} + +/* +============ +Cvar_VariableValue +============ +*/ +float Cvar_VariableValue (char *var_name) +{ + cvar_t *var; + + var = Cvar_FindVar (var_name); + if (!var) + return 0; + return Q_atof (var->string); +} + + +/* +============ +Cvar_VariableString +============ +*/ +char *Cvar_VariableString (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 = Q_strlen(partial); + + if (!len) + return NULL; + +// check functions + for (cvar=cvar_vars ; cvar ; cvar=cvar->next) + if (!Q_strncmp (partial,cvar->name, len)) + return cvar->name; + + return NULL; +} + + +/* +============ +Cvar_Set +============ +*/ +void Cvar_Set (char *var_name, char *value) +{ + cvar_t *var; + qboolean 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; + } + + changed = Q_strcmp(var->string, value); + + Z_Free (var->string); // free the old value string + + var->string = Z_Malloc (Q_strlen(value)+1); + Q_strcpy (var->string, value); + var->value = Q_atof (var->string); + if (var->server && changed) + { + if (sv.active) + SV_BroadcastPrintf ("\"%s\" changed to \"%s\"\n", var->name, var->string); + } +} + +/* +============ +Cvar_SetValue +============ +*/ +void Cvar_SetValue (char *var_name, float value) +{ + char val[32]; + + sprintf (val, "%f",value); + Cvar_Set (var_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 allready been defined + if (Cvar_FindVar (variable->name)) + { + Con_Printf ("Can't register variable %s, allready defined\n", variable->name); + return; + } + +// 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 (Q_strlen(variable->string)+1); + Q_strcpy (variable->string, oldstr); + variable->value = Q_atof (variable->string); + +// link the variable in + variable->next = cvar_vars; + cvar_vars = variable; +} + +/* +============ +Cvar_Command + +Handles variable inspection and changing from the console +============ +*/ +qboolean 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; + + for (var = cvar_vars ; var ; var = var->next) + if (var->archive) + fprintf (f, "%s \"%s\"\n", var->name, var->string); +} + diff --git a/source/cvar.h b/source/cvar.h new file mode 100644 index 0000000..138fda8 --- /dev/null +++ b/source/cvar.h @@ -0,0 +1,96 @@ +/* +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 ,true 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",true}; + +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"); + +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. +*/ + +typedef struct cvar_s +{ + char *name; + char *string; + qboolean archive; // set to true to cause it to be saved to vars.rc + qboolean server; // notifies players when changed + float value; + struct cvar_s *next; +} cvar_t; + +void Cvar_RegisterVariable (cvar_t *variable); +// registers a cvar that allready has the name, string, and optionally the +// archive elements set. + +void Cvar_Set (char *var_name, char *value); +// equivelant to " " typed at the console + +void Cvar_SetValue (char *var_name, float value); +// expands value to a string and calls Cvar_Set + +float Cvar_VariableValue (char *var_name); +// returns 0 if not defined or non numeric + +char *Cvar_VariableString (char *var_name); +// returns an empty string if not defined + +char *Cvar_CompleteVariable (char *partial); +// attempts to match a partial variable name for command line completion +// returns NULL if nothing fits + +qboolean 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 (char *var_name); + +extern cvar_t *cvar_vars; diff --git a/source/draw.h b/source/draw.h new file mode 100644 index 0000000..c6169ee --- /dev/null +++ b/source/draw.h @@ -0,0 +1,51 @@ +/* +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 + +void Draw_Init (void); +void Draw_Character (int x, int y, int num); +void Draw_DebugChar (char num); +void Draw_Pic (int x, int y, qpic_t *pic); +void Draw_StretchPic (int x, int y, qpic_t *pic, int x_value, int y_value); +void Draw_ColorPic (int x, int y, qpic_t *pic, float r, float g , float b, float a); +void Draw_ColoredString (int x, int y, char *text, float r, float g, float b, float a, int scale); +void Draw_TransPic (int x, int y, qpic_t *pic); +void Draw_TransPicTranslate (int x, int y, qpic_t *pic, byte *translation); +void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha); +void Draw_ConsoleBackground (int lines); +void Draw_LoadingFill(void); +void Draw_Fill (int x, int y, int w, int h, int c); +void Draw_FillByColor (int x, int y, int w, int h, unsigned int c); +void Draw_FadeScreen (void); +void Draw_String (int x, int y, char *str); + +//other +void Clear_LoadingFill (void); +byte *StringToRGB (char *s); + +extern float loading_cur_step; +extern char loading_name[32]; +extern float loading_num_step; + +qpic_t *Draw_PicFromWad (char *name); +qpic_t *Draw_CachePic (char *path); +qpic_t *Draw_CacheImg (char *path); diff --git a/source/gnu.txt b/source/gnu.txt new file mode 100644 index 0000000..bf96377 --- /dev/null +++ b/source/gnu.txt @@ -0,0 +1,87 @@ +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +Preamble +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + +a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. + +b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. + +c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + +a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, + +c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/source/host.c b/source/host.c new file mode 100644 index 0000000..c7e533f --- /dev/null +++ b/source/host.c @@ -0,0 +1,976 @@ +/* +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 "thread.h" +#include "psp/module.h" +#include +#include + +/* + +A server can allways 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. +*/ + +quakeparms_t host_parms; + +qboolean 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; + +client_t *host_client; // current client + +jmp_buf host_abortserver; + +byte *host_basepal; +byte *host_colormap; +byte *host_q2pal; +byte *host_h2pal; + +cvar_t host_framerate = {"host_framerate","0"}; // set for slow motion +cvar_t host_speeds = {"host_speeds","0"}; // set for running times + +cvar_t sys_ticrate = {"sys_ticrate","0.05"}; +cvar_t serverprofile = {"serverprofile","0"}; + +cvar_t fraglimit = {"fraglimit","0",false,true}; +cvar_t timelimit = {"timelimit","0",false,true}; +cvar_t teamplay = {"teamplay","0",false,true}; + +cvar_t samelevel = {"samelevel","0"}; + +cvar_t show_fps = {"show_fps","0", true}; // set for running times - muff +cvar_t cl_maxfps = {"cl_maxfps", "30", true}; // dr_mabuse1981: maxfps setting +cvar_t show_bat = {"show_bat","0"}; // test +int fps_count; + +cvar_t developer = {"developer","0"}; + +cvar_t skill = {"skill","1"}; // 0 - 3 +cvar_t deathmatch = {"deathmatch","0"}; // 0, 1, or 2 +cvar_t coop = {"coop","0"}; // 0 or 1 + +cvar_t pausable = {"pausable","1"}; + +cvar_t temp1 = {"temp1","0"}; + +qboolean bmg_type_changed = false; + + +/* +================ +Host_EndGame +================ +*/ +void Host_EndGame (char *message, ...) +{ + va_list argptr; + char string[1024]; + + 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 + + if (cls.demonum != -1) + CL_NextDemo (); + else + CL_Disconnect (); + + Clear_LoadingFill (); + + longjmp (host_abortserver, 1); +} + +/* +================ +Host_Error + +This shuts down both the client and server +================ +*/ +void Host_Error (char *error, ...) +{ + va_list argptr; + char string[1024]; + static qboolean inerror = false; + + if (inerror) + Sys_Error ("Host_Error: recursively entered"); + inerror = true; + + SCR_EndLoadingPlaque (); // reenable screen updates + + 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 + + CL_Disconnect (); + cls.demonum = -1; + + Clear_LoadingFill (); + + inerror = false; + + longjmp (host_abortserver, 1); +} + +/* +================ +Host_FindMaxClients +================ +*/ +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 = Q_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 = Q_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 = Hunk_AllocName (svs.maxclientslimit*sizeof(client_t), "clients"); + + if (svs.maxclients > 1) + Cvar_SetValue ("deathmatch", 1.0); + else + Cvar_SetValue ("deathmatch", 0.0); +} + + +/* +======================= +Host_InitLocal +====================== +*/ +void Host_InitLocal (void) +{ + Host_InitCommands (); + + Cvar_RegisterVariable (&host_framerate); + Cvar_RegisterVariable (&host_speeds); + + Cvar_RegisterVariable (&sys_ticrate); + Cvar_RegisterVariable (&serverprofile); + + Cvar_RegisterVariable (&show_bat); // Crow_bar battery info + Cvar_RegisterVariable (&show_fps); // muff + Cvar_RegisterVariable (&cl_maxfps); // dr_mabuse1981: maxfps setting + Cvar_RegisterVariable (&fraglimit); + Cvar_RegisterVariable (&timelimit); + Cvar_RegisterVariable (&teamplay); + Cvar_RegisterVariable (&samelevel); + 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 (void) +{ + FILE *f; + +// dedicated servers initialize the host but don't parse and set the +// config.cfg cvars + if (host_initialized & !isDedicated) + { + f = fopen (va("%s/config.cfg",com_gamedir), "w"); + if (!f) + { + Con_Printf ("Couldn't write config.cfg.\n"); + return; + } + + Key_WriteBindings (f); + Key_WriteDTBindings (f); + Cvar_WriteVariables (f); + + fclose (f); + } +} + + +/* +================= +SV_ClientPrintf + +Sends text across to be displayed +FIXME: make this just a stuffed echo? +================= +*/ +void SV_ClientPrintf (char *fmt, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr,fmt); + vsprintf (string, fmt,argptr); + va_end (argptr); + + MSG_WriteByte (&host_client->message, svc_print); + MSG_WriteString (&host_client->message, string); +} + +/* +================= +SV_BroadcastPrintf + +Sends text to all active clients +================= +*/ +void SV_BroadcastPrintf (char *fmt, ...) +{ + va_list argptr; + char string[1024]; + int i; + + va_start (argptr,fmt); + vsprintf (string, fmt,argptr); + va_end (argptr); + + for (i=0 ; imessage, svc_stufftext); + MSG_WriteString (&host_client->message, string); +} + +/* +===================== +SV_DropClient + +Called when the player is getting totally kicked off the host +if (crash = true), don't bother sending signofs +===================== +*/ +void SV_DropClient (qboolean crash) +{ + int saveSelf; + int i; + client_t *client; + + 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); + } + +// 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_points = -999999; + host_client->old_kills = -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_updatepoints); + MSG_WriteByte (&client->message, host_client - svs.clients); + MSG_WriteLong (&client->message, 0); + MSG_WriteByte (&client->message, svc_updatekills); + MSG_WriteByte (&client->message, host_client - svs.clients); + MSG_WriteShort (&client->message, 0); + } +} + +/* +================== +Host_ShutdownServer + +This only happens at the end of a game, not between levels +================== +*/ +void Host_ShutdownServer(qboolean 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 = (byte *)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. +================ +*/ +extern int perk_order[8]; +extern int current_perk_order; +extern double Hitmark_Time, crosshair_spread_time; +extern float cur_spread; +extern float crosshair_offset_step; +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)); + perk_order[0] = 0; + perk_order[1] = 0; + perk_order[2] = 0; + perk_order[3] = 0; + perk_order[4] = 0; + perk_order[5] = 0; + perk_order[6] = 0; + perk_order[7] = 0; + cl.perks = 0; + current_perk_order = 0; + crosshair_spread_time = 0; + crosshair_offset_step = 0; + cur_spread = 0; + Hitmark_Time = 0; + +} + + +//============================================================================ + + +/* +=================== +Host_FilterTime + +Returns false if the time is too short to run a frame +=================== +*/ +qboolean Host_FilterTime (float time) +{ + realtime += time; + +/* dr_mabuse1981: old dquake values commented out + if (!cls.timedemo && realtime - oldrealtime < 1.0/250.0) + return false; // framerate is too high +*/ + + if (cl_maxfps.value < 1) Cvar_SetValue("cl_maxfps", 30); + if (!cls.timedemo && realtime - oldrealtime < 1.0/cl_maxfps.value) + return false; // framerate is too high + + host_frametime = realtime - oldrealtime; + oldrealtime = realtime; + + 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 + +================== +*/ +void Host_ServerFrame (void) +{ +// 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 (); +} + + +/* +================== +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 (); + if (host_speeds.value) + time2 = Sys_FloatTime (); +// update audio + if (cls.signon == SIGNONS) + { + Thread_UpdateSound(r_origin, vpn, vright, vup); + //S_Update (r_origin, vpn, vright, vup); + CL_DecayLights (); + } + else + Thread_UpdateSound(vec3_origin, vec3_origin, vec3_origin, vec3_origin); + //S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin); + + //if (bmg_type_changed == true) { + CDAudio_Update(); + // bmg_type_changed = false; + //} + + 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); + } + + //frame speed counter + fps_count++;//muff + host_framecount++; +} + +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 = Q_strlen(com_argv[i]) + 1; + Sys_FileWrite(vcrFile, &len, sizeof(int)); + Sys_FileWrite(vcrFile, com_argv[i], len); + } + } + +} + +void Preload (void) +{ + Mod_ForName ("models/player.mdl", true); + + Mod_ForName ("progs/ai/zb#.mdl",true); + Mod_ForName ("progs/ai/zal(.mdl",true); + Mod_ForName ("progs/ai/zar(.mdl",true); + Mod_ForName ("progs/ai/zh^.mdl",true); + Mod_ForName ("progs/ai/zbc#.mdl",true); + Mod_ForName ("progs/ai/zalc(.mdl",true); + Mod_ForName ("progs/ai/zarc(.mdl",true); + Mod_ForName ("progs/ai/zhc^.mdl",true); + + Mod_ForName ("progs/ai/zfull.mdl",true); + Mod_ForName ("progs/ai/zcfull.mdl",true); + + Mod_ForName ("progs/VModels/v_knife.mdl", true); + Mod_ForName ("progs/VModels/v_colt.mdl", true); + Mod_ForName ("progs/Misc/instakill!.mdl", true); + Mod_ForName ("progs/Misc/maxammo!.mdl", true); + Mod_ForName ("progs/Misc/nuke!.mdl", true); + Mod_ForName ("progs/Misc/carpenter!.mdl", true); + Mod_ForName ("progs/Misc/x2!.mdl", true); +} +/* +==================== +Host_Init +==================== +*/ +#include "cl_slist.h" + +void M_Start_Menu_f (void); +void Host_Init (quakeparms_t *parms) +{ + + minimum_memory = MINIMUM_MEMORY; + + 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 (); + Key_Init (); + Con_Init (); + M_Init (); + PR_Init (); + Mod_Init (); + NET_Init (); + SV_Init (); + Con_Printf ("PSP NZP v%4.1f (PBP: "__TIME__" "__DATE__")\n", (float)(VERSION)); + Con_Printf ("%4.1f megabyte heap \n",parms->memsize/ (1024*1024.0)); + Con_Printf ("%4.1f megabyte PSP application heap \n",1.0f*PSP_HEAP_SIZE_MB); + Con_Printf ("PSP Model: %s\n", Sys_GetPSPModel()); + Con_Printf ("VRAM Size: %i bytes\n", sceGeEdramGetSize()); + + R_InitTextures (); // needed even for dedicated servers + if (cls.state != ca_dedicated) + { + host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp"); + if (!host_basepal) + Sys_Error ("Couldn't load gfx/palette.lmp"); + + host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp"); + if (!host_colormap) + Sys_Error ("Couldn't load gfx/colormap.lmp"); + + host_q2pal = (byte *)COM_LoadHunkFile ("gfx/q2pal.lmp"); + if (!host_q2pal) + Sys_Error ("Couldn't load gfx/q2pal.lmp"); + + host_h2pal = (byte *)COM_LoadHunkFile ("gfx/h2pal.lmp"); + if (!host_h2pal) + Sys_Error ("Couldn't load gfx/h2pal.lmp"); + + IN_Init (); + VID_Init (host_basepal); + Draw_Init (); + SCR_Init (); + R_Init (); + S_Init (); + CDAudio_Init (); + HUD_Init (); + CL_Init (); + } + Preload(); + Cbuf_InsertText ("exec nzp.rc\n"); + + Hunk_AllocName (0, "-HOST_HUNKLEVEL-"); + host_hunklevel = Hunk_LowMark (); + + host_initialized = true; + M_Start_Menu_f(); + Sys_Printf ("========Quake Initialized=========\n"); + Con_Printf ("==========NZP Initialized=========\n"); +} + + +/* +=============== +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 qboolean isdown = false; + + if (isdown) + { + return; + } + isdown = true; + +// keep Con_Printf from trying to update the screen + scr_disabled_for_loading = true; + + Clear_LoadingFill (); + + SList_Shutdown(); + + Host_WriteConfiguration (); + + if (con_initialized) + History_Shutdown (); + + CDAudio_Shutdown (); + NET_Shutdown (); + S_Shutdown(); + IN_Shutdown (); + + if (cls.state != ca_dedicated) + { + VID_Shutdown(); + } +} + diff --git a/source/host_cmd.c b/source/host_cmd.c new file mode 100644 index 0000000..8390f9d --- /dev/null +++ b/source/host_cmd.c @@ -0,0 +1,1454 @@ +/* +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; + +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 (); +} + + +/* +================== +Host_Status_f +================== +*/ +void Host_Status_f (void) +{ + client_t *client; + int seconds; + int minutes; + int hours = 0; + int j; + void (*print) (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); + if (ipxAvailable) + print ("ipx: %s\n", my_ipx_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.points, hours, minutes, seconds); + print (" %s\n", client->netconnection->address); + print(" SignOn State: %i\n",cls.signon); + } +} + + +/* +================== +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"); +} + +qboolean 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 +} + +/* +===================== +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 (); +} + + +/* +=============================================================================== + +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 save file for writing.\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 save file for reading.\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; + } + 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 (); + } +} + + + +//============================================================================ + +/* +====================== +Host_Name_f +====================== +*/ +void Host_Name_f (void) +{ + 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; + + if (cmd_source == src_command) + { + if (Q_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 (Q_strcmp(host_client->name, newName) != 0) + Con_Printf ("%s renamed to %s\n", host_client->name, newName); + Q_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 ("Version %4.2f\n", VERSION); + Con_Printf ("Exe: "__TIME__" "__DATE__"\n"); +} + + + +void Host_Say(qboolean teamonly) +{ + client_t *client; + client_t *save; + int j; + char *p; + char text[64]; + qboolean 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[Q_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 - Q_strlen(text); // -2 for /n and null terminator + if (Q_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; + + Q_strcpy(text, host_client->name); + Q_strcat(text, ": "); + + p = Cmd_Args(); + +// remove quotes if present + if (*p == '"') + { + p++; + p[Q_strlen(p)-1] = 0; + } + +// check length & truncate if necessary + j = sizeof(text) - 2 - Q_strlen(text); // -2 for /n and null terminator + if (Q_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 (Q_strcasecmp(client->name, Cmd_Argv(1))) + continue; + host_client = client; + SV_ClientPrintf("%s", text); + break; + } + host_client = save; +} + +/* +================== +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 -- allready 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 (sv.paused) + { + SV_BroadcastPrintf ("%s paused the game\n", pr_strings + sv_player->v.netname); + } + else + { + SV_BroadcastPrintf ("%s unpaused the game\n",pr_strings + sv_player->v.netname); + } + + // 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 -- allready 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; + + + 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 -- allready spawned\n"); + return; + } + + host_client->nomap = false; + + +// run the entrance script + if (sv.loadgame) + { // loaded games are fully inited allready + // 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.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); + S_LocalSound ("sounds/rounds/splash.wav"); // since this won't execute in progs... + } + +// 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_updatepoints); + MSG_WriteByte (&host_client->message, i); + MSG_WriteLong (&host_client->message, client->old_points); + MSG_WriteByte (&host_client->message, svc_updatekills); + MSG_WriteByte (&host_client->message, i); + MSG_WriteShort (&host_client->message, client->old_kills); + } + +// 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_ROUNDS); + MSG_WriteByte (&host_client->message, pr_global_struct->rounds); + + MSG_WriteByte (&host_client->message, svc_updatestat); + MSG_WriteByte (&host_client->message, STAT_ROUNDCHANGE); + MSG_WriteByte (&host_client->message, pr_global_struct->rounds_change); + + MSG_WriteByte (&host_client->message, svc_updatestat); + MSG_WriteByte (&host_client->message, STAT_X2); + MSG_WriteByte (&host_client->message, sv_player->v.x2_icon); + + MSG_WriteByte (&host_client->message, svc_updatestat); + MSG_WriteByte (&host_client->message, STAT_INSTA); + MSG_WriteByte (&host_client->message, sv_player->v.insta_icon); + +// +// 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); + for (i=0 ; i < 2 ; i++) + MSG_WriteAngle (&host_client->message, ent->v.angles[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; + qboolean 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 && Q_strcmp(Cmd_Argv(1), "#") == 0) + { + i = Q_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 (Q_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 += Q_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; + + 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 'h': + sv_player->v.health = 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 ; i 1, it is autorepeating +qboolean keydown[256]; + +void Con_OSK_f (char *input, char *output, int outlen); +void Con_SetOSKActive(qboolean active); +qboolean Con_isSetOSKActive(void); +void Con_OSK_Key (int key); + +typedef struct +{ + char *name; + int keynum; +} keyname_t; + +keyname_t keynames[] = +{ + {"TAB", K_TAB}, + {"ENTER", K_ENTER}, + {"ESCAPE", K_ESCAPE}, + {"SPACE", K_SPACE}, + {"BACKSPACE", K_BACKSPACE}, + {"UPARROW", K_UPARROW}, + {"DOWNARROW", K_DOWNARROW}, + {"LEFTARROW", K_LEFTARROW}, + {"RIGHTARROW", K_RIGHTARROW}, + {"SELECT", K_SELECT}, + + {"ALT", K_ALT}, + {"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}, + {"NOTE", K_NOTE}, + {"SCREEN", K_SCREEN}, + {"END", K_END}, + + {"MOUSE1", K_MOUSE1}, + {"MOUSE2", K_MOUSE2}, + {"MOUSE3", K_MOUSE3}, + + {"TRIANGLE", K_JOY1}, + {"CIRCLE", K_JOY2}, + {"CROSS", K_JOY3}, + {"SQUARE", K_JOY4}, + {"LTRIGGER", K_AUX1}, + {"RTRIGGER", K_AUX2}, + {"AUX3", K_AUX3}, + {"AUX4", K_AUX4}, + {"AUX5", K_AUX5}, + {"AUX6", K_AUX6}, + {"AUX7", K_AUX7}, + {"AUX8", K_AUX8}, + {"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}, + + {"MWHEELUP", K_MWHEELUP}, + {"MWHEELDOWN", K_MWHEELDOWN}, + + {"SEMICOLON", ';'}, // because a raw semicolon seperates commands + + {NULL,0} +}; + +char consoleInput[MAXCMDLINE]; +qboolean consoleOskDone = false; + +/* +============================================================================== + + LINE TYPING INTO THE CONSOLE + +============================================================================== +*/ + + +/* +==================== +Key_Console + +Interactive line editing and console scrollback +==================== +*/ +extern qboolean console_enabled; +void Key_Console (int key) +{ + char *cmd; + + if (Con_isSetOSKActive()) + { + if (key == K_JOY1) + Con_OSK_Key (K_DEL); + else if (key == K_JOY2) + Con_OSK_Key (K_ESCAPE); + else if (key == K_JOY3) + Con_OSK_Key (K_ENTER); + else if (key == K_JOY4) + Con_OSK_Key (K_INS); + else + Con_OSK_Key (key); + return; + } + + if ( key == K_JOY4 || key == K_INS) + { + consoleOskDone = false; + Con_SetOSKActive(true); + Con_OSK_f(key_lines[edit_line]+1, consoleInput, 72); + return; + } + + if (key == K_JOY3 || key == K_ENTER) + { + Cbuf_AddText (key_lines[edit_line]+1); // skip the > + Cbuf_AddText ("\n"); + Con_Printf ("%s\n",key_lines[edit_line]); + edit_line = (edit_line + 1) & 31; + history_line = edit_line; + key_lines[edit_line][0] = ']'; + key_linepos = 1; + if (cls.state == ca_disconnected) + SCR_UpdateScreen (); // force an update, because the command + // may take some time + // for clientside cmds + if (cls.state == ca_connected){ + pr_global_struct->CMD_STRING = (key_lines[edit_line-1]+1 - pr_strings); + PR_ExecuteProgram (pr_global_struct->ParseClientCommand); + } + return; + } + + + + if (key == K_TAB || key == K_RIGHTARROW) + { // command completion + cmd = Cmd_CompleteCommand (key_lines[edit_line]+1); + if (!cmd) + cmd = Cvar_CompleteVariable (key_lines[edit_line]+1); + if (cmd) + { + Q_strcpy (key_lines[edit_line]+1, cmd); + key_linepos = Q_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; + Q_strcpy(key_lines[edit_line], key_lines[history_line]); + key_linepos = Q_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 + { + Q_strcpy(key_lines[edit_line], key_lines[history_line]); + key_linepos = Q_strlen(key_lines[edit_line]); + } + return; + } + + if (key == K_PGUP || key==K_MWHEELUP) + { + con_backscroll += 2; + if (con_backscroll > con_totallines - (vid.height>>3) - 1) + con_backscroll = con_totallines - (vid.height>>3) - 1; + return; + } + + if (key == K_PGDN || key==K_MWHEELDOWN) + { + 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_END) + { + console_enabled = false; + 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]; +qboolean team_message = false; + +void Key_Message (int key) +{ + static int chat_bufferlen = 0; + + if (key == K_ENTER) + { + 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_ESCAPE) + { + console_enabled = false; + key_dest = key_game; + chat_bufferlen = 0; + chat_buffer[0] = 0; + return; + } + + if (key < 32 || key > 127) + return; // non printable + + if (key == K_BACKSPACE) + { + 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 (!Q_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 = Q_strlen (binding); + new = Z_Malloc (l+1); + Q_strcpy (new, binding); + new[l] = 0; + keybindings[keynum] = new; +} + + +/* +=================== +Key_SetDTBinding +=================== +*/ +void Key_SetDTBinding (int keynum, char *binding) +{ + char *new; + int l; + + if (keynum == -1) + return; + +// free old bindings + if (dtbindings[keynum]) + { + Z_Free (dtbindings[keynum]); + dtbindings[keynum] = NULL; + } + +// allocate memory for new binding + l = Q_strlen (binding); + new = Z_Malloc (l+1); + Q_strcpy (new, binding); + new[l] = 0; + dtbindings[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[1024]; + + 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; + } + +// 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); +} +/* +=================== +Key_Binddt_f +=================== +*/ +void Key_Binddt_f (void) +{ + int i, c, b; + char cmd[1024]; + + c = Cmd_Argc(); + + if (c != 2 && c != 3) + { + Con_Printf ("binddt [command] : attach a command to a double tap 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 (dtbindings[b]) + Con_Printf ("\"%s\" = \"%s\"\n", Cmd_Argv(1), dtbindings[b] ); + else + Con_Printf ("\"%s\" is not bound\n", Cmd_Argv(1) ); + return; + } + +// 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_SetDTBinding (b, cmd); +} + +/* +============ +Key_WriteBindings + +Writes lines containing "bind key value" +============ +*/ +void Key_WriteBindings (FILE *f) +{ + int i; + + for (i=0 ; i<256 ; i++) + if (keybindings[i]) + if (*keybindings[i]) + fprintf (f, "bind \"%s\" \"%s\"\n", Key_KeynumToString(i), keybindings[i]); +} + +/* +============ +Key_WriteDTBindings + +Writes lines containing "binddt key value" +============ +*/ +void Key_WriteDTBindings (FILE *f) +{ + int i; + + for (i=0 ; i<256 ; i++) + if (dtbindings[i]) + if (*dtbindings[i]) + fprintf (f, "binddt \"%s\" \"%s\"\n", Key_KeynumToString(i), dtbindings[i]); +} + +// Added by dr_mabuse1981 { +void History_Init (void) +{ + int i, c; + FILE *hf; + + for (i = 0; i < CMDLINES; i++) { + key_lines[i][0] = ']'; + key_lines[i][1] = 0; + } + key_linepos = 1; + +// if (cl_savehistory.value) + if ((hf = fopen(HISTORY_FILE_NAME, "rt"))) + { + do + { + i = 1; + do + { + c = fgetc(hf); + key_lines[edit_line][i++] = c; + } while (c != '\n' && c != EOF && i < MAXCMDLINE); + key_lines[edit_line][i - 1] = 0; + edit_line = (edit_line + 1) & (CMDLINES - 1); + } while (c != EOF && edit_line < CMDLINES); + fclose(hf); + + history_line = edit_line = (edit_line - 1) & (CMDLINES - 1); + key_lines[edit_line][0] = ']'; + key_lines[edit_line][1] = 0; + } +} + +void History_Shutdown (void) +{ + int i; + FILE *hf; + +// if (cl_savehistory.value) + if ((hf = fopen(HISTORY_FILE_NAME, "wt"))) + { + i = edit_line; + do + { + i = (i + 1) & (CMDLINES - 1); + } while (i != edit_line && !key_lines[i][1]); + + do + { + // fprintf(hf, "%s\n", wcs2str(key_lines[i] + 1)); // Baker: I commented this line out because byte colored text isn't a feature in most ordinary engines + fprintf(hf, "%s\n", key_lines[i] + 1); + i = (i + 1) & (CMDLINES - 1); + } while (i != edit_line && key_lines[i][1]); + fclose(hf); + } +} +// } Added by dr_mabuse1981 + + +/* +=================== +Key_Init +=================== +*/ +void Key_Init (void) +{ + int i; + + History_Init (); + + #if 0 // This section of code is now done in History_Init + for (i=0 ; i<32 ; i++) + { + key_lines[i][0] = ']'; + key_lines[i][1] = 0; + } + key_linepos = 1; + #endif +// +// init ascii characters in console mode +// + for (i=32 ; i<128 ; i++) + consolekeys[i] = true; + consolekeys[K_ENTER] = true; + consolekeys[K_TAB] = true; + consolekeys[K_LEFTARROW] = true; + consolekeys[K_RIGHTARROW] = true; + consolekeys[K_UPARROW] = true; + consolekeys[K_DOWNARROW] = true; + consolekeys[K_BACKSPACE] = true; + consolekeys[K_PGUP] = true; + consolekeys[K_PGDN] = true; + consolekeys[K_SHIFT] = true; + consolekeys[K_MWHEELUP] = true; + consolekeys[K_MWHEELDOWN] = true; + consolekeys['`'] = false; + consolekeys['~'] = false; + consolekeys[K_INS] = true; + consolekeys[K_JOY1] = true; + consolekeys[K_JOY2] = true; + consolekeys[K_JOY3] = true; + consolekeys[K_JOY4] = true; + + for (i=0 ; i<256 ; i++) + keyshift[i] = i; + for (i='a' ; i<='z' ; i++) + keyshift[i] = i - 'a' + 'A'; + keyshift['1'] = '!'; + keyshift['2'] = '@'; + keyshift['3'] = '#'; + keyshift['4'] = '$'; + keyshift['5'] = '%'; + keyshift['6'] = '^'; + keyshift['7'] = '&'; + keyshift['8'] = '*'; + keyshift['9'] = '('; + keyshift['0'] = ')'; + keyshift['-'] = '_'; + keyshift['='] = '+'; + keyshift[','] = '<'; + keyshift['.'] = '>'; + keyshift['/'] = '?'; + keyshift[';'] = ':'; + keyshift['\''] = '"'; + keyshift['['] = '{'; + keyshift[']'] = '}'; + keyshift['`'] = '~'; + keyshift['\\'] = '|'; + + menubound[K_ESCAPE] = true; + for (i=0 ; i<12 ; i++) + menubound[K_F1+i] = true; + +// +// register our functions +// + Cmd_AddCommand ("bind",Key_Bind_f); + Cmd_AddCommand ("binddt",Key_Binddt_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! +=================== +*/ +int lastkey; +double lastkeytime; +int oldkey; +double oldkeytime; +void Key_Event (int key, qboolean down) +{ + char *kb; + char cmd[1024]; + + oldkey = lastkey; + keydown[key] = down; + lastkey = key; + if (Con_isSetOSKActive() && down) + { + if (key == K_JOY1) + Con_OSK_Key (K_DEL); + else if (key == K_JOY2) + Con_OSK_Key (K_ESCAPE); + else if (key == K_JOY3) + Con_OSK_Key (K_ENTER); + else if (key == K_JOY4) + Con_OSK_Key (K_INS); + else + Con_OSK_Key (key); + + if (!Con_isSetOSKActive()) + { + consoleOskDone = true; + strcpy(key_lines[edit_line]+1, consoleInput); + key_linepos = Q_strlen(key_lines[edit_line]); + consoleOskDone = false; + consoleInput[0] = 0; + return; + } + else + { + return; + } + } + 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) + { + oldkeytime = lastkeytime; + lastkeytime = Sys_FloatTime(); + key_repeats[key]++; + if (key != K_BACKSPACE && key != K_PAUSE && key_repeats[key] > 1) + { + return; // ignore most autorepeats + } + + if (key >= 200 && !keybindings[key]) + Con_Printf ("%s is unbound, hit START to set.\n", Key_KeynumToString (key) ); + } + + if (key == K_SHIFT) + shift_down = down; + +// +// handle escape specialy, so the user can never unbind it +// + if (key == K_ESCAPE) + { + if (!down) + return; + switch (key_dest) + { + case key_message: + Key_Message (key); + break; + case key_menu: + case key_menu_pause: + M_Keydown (key); + break; + case key_game: + case key_console: + console_enabled = false; + M_ToggleMenu_f (); + break; + default: + Sys_Error ("Bad key_dest"); + } + return; + } + +// +// 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); + } + } + return; + } + +// +// during demo playback, most keys bring up the main menu +// + if (cls.demoplayback && down && consolekeys[key] && key_dest == key_game) + { + M_ToggleMenu_f (); + return; + } + +// +// if not a consolekey, send to the interpreter no matter what mode is +// + if ( ((key_dest == key_menu || key_dest == key_menu_pause) && menubound[key]) + || (key_dest == key_console && !consolekeys[key]) + || (key_dest == key_game && ( !con_forcedup || !consolekeys[key] ) ) ) + { + if (oldkey == key && ((oldkeytime + 0.3) > lastkeytime)) + { + kb = dtbindings[key]; + if (kb) + { + if (kb[0] == '+') + { // button commands add keynum as a parm + sprintf (cmd, kb, key); + Cbuf_AddText (cmd); + } + else + { + Cbuf_AddText (kb); + Cbuf_AddText ("\n"); + } + } + oldkey = 0; + oldkeytime = 0; + lastkeytime = 0; + lastkey = 0; + } + + 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"); + } + } + return; + } + + if (!down) + 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: + case key_menu_pause: + M_Keydown (key); + break; + + case key_game: + case key_console: + Key_Console (key); + break; + default: + Sys_Error ("Bad key_dest"); + } +} + + +/* +=================== +Key_ClearStates +=================== +*/ +void Key_ClearStates (void) +{ + int i; + + for (i=0 ; i<256 ; i++) + { + keydown[i] = false; + key_repeats[i] = 0; + } +} + diff --git a/source/keys.h b/source/keys.h new file mode 100644 index 0000000..7dc3f22 --- /dev/null +++ b/source/keys.h @@ -0,0 +1,140 @@ +/* +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_ALT 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_SELECT 153 +#define K_NOTE 154 +#define K_SCREEN 155 + +#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 + +// +// aux keys are for multi-buttoned joysticks to generate so they can use +// the normal binding process +// +#define K_AUX1 207 +#define K_AUX2 208 +#define K_AUX3 209 +#define K_AUX4 210 +#define K_AUX5 211 +#define K_AUX6 212 +#define K_AUX7 213 +#define K_AUX8 214 +#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_menu_pause} keydest_t; + +extern keydest_t key_dest; +extern char *keybindings[256]; +extern char *dtbindings[256]; +extern int key_repeats[256]; +extern int key_count; // incremented every key event +extern int key_lastpress; +extern qboolean keydown[256]; +void Key_Event (int key, qboolean down); +void Key_Init (void); +void Key_WriteBindings (FILE *f); +void Key_WriteDTBindings (FILE *f); +void Key_SetBinding (int keynum, char *binding); +void Key_ClearStates (void); + +void History_Shutdown (void); + diff --git a/source/libpspmath/Makefile b/source/libpspmath/Makefile new file mode 100644 index 0000000..cb5c443 --- /dev/null +++ b/source/libpspmath/Makefile @@ -0,0 +1,62 @@ +TARGET_LIB = libpspmath.a +OBJS = \ + printMatrixFloat.o \ + vfpu_srand.o \ + vfpu_randf.o \ + vfpu_rand_8888.o \ + vfpu_identity_matrix.o \ + vfpu_translate_matrix.o \ + vfpu_perspective_matrix.o \ + vfpu_ortho_matrix.o \ + vfpu_sinf.o \ + vfpu_cosf.o \ + vfpu_tanf.o \ + vfpu_asinf.o \ + vfpu_acosf.o \ + vfpu_atanf.o \ + vfpu_sinhf.o \ + vfpu_coshf.o \ + vfpu_tanhf.o \ + vfpu_sincos.o \ + vfpu_expf.o \ + vfpu_logf.o \ + vfpu_fabsf.o \ + vfpu_sqrtf.o \ + vfpu_powf.o \ + vfpu_fmodf.o \ + vfpu_fminf.o \ + vfpu_fmaxf.o \ + vfpu_ease_in_out.o \ + vfpu_normalize_vector.o \ + vfpu_zero_vector.o \ + vfpu_scale_vector.o \ + vfpu_add_vector.o \ + vfpu_envmap_matrix.o \ + vfpu_sphere_to_cartesian.o \ + vfpu_quaternion_identity.o \ + vfpu_quaternion_copy.o \ + vfpu_quaternion_multiply.o \ + vfpu_quaternion_normalize.o \ + vfpu_quaternion_exp.o \ + vfpu_quaternion_ln.o \ + vfpu_quaternion_sample_linear.o \ + vfpu_quaternion_from_euler.o \ + vfpu_quaternion_to_matrix.o \ + vfpu_quaternion_sample_hermite.o \ + vfpu_quaternion_hermite_tangent.o + +INCDIR = +CFLAGS = -O2 -G0 -Wall +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +LIBDIR = +LDFLAGS = +LIBS= + +PSPSDK=$(shell psp-config --pspsdk-path) +include $(PSPSDK)/lib/build.mak + +install: + cp $(TARGET_LIB) $(PSPSDK)/lib + cp pspmath.h $(PSPSDK)/include \ No newline at end of file diff --git a/source/libpspmath/README.txt b/source/libpspmath/README.txt new file mode 100644 index 0000000..67e7f83 --- /dev/null +++ b/source/libpspmath/README.txt @@ -0,0 +1,5 @@ +this is a PSP VFPU accelerated math library required if the PSP_VFPU Dflag +is set. original library by mrmrice, made standalone by tufty +https://github.com/tufty/libpspmath + +- motolegacy (2/3/2021) diff --git a/source/libpspmath/libpspmath.a b/source/libpspmath/libpspmath.a new file mode 100644 index 0000000..8c19b40 Binary files /dev/null and b/source/libpspmath/libpspmath.a differ diff --git a/source/libpspmath/printMatrixFloat.c b/source/libpspmath/printMatrixFloat.c new file mode 100644 index 0000000..c383a7b --- /dev/null +++ b/source/libpspmath/printMatrixFloat.c @@ -0,0 +1,31 @@ +#include +#include "pspmath.h" + +void printMatrixFloat(int matid) { + float m[16]; + + #define SV(N) \ + asm("usv.q R"#N"00, 0 + %0\n" \ + "usv.q R"#N"01, 16 + %0\n" \ + "usv.q R"#N"02, 32 + %0\n" \ + "usv.q R"#N"03, 48 + %0\n" \ + : "=m"(m)) + + switch (matid) { + case 0: SV(0); break; + case 1: SV(1); break; + case 2: SV(2); break; + case 3: SV(3); break; + case 4: SV(4); break; + case 5: SV(5); break; + case 6: SV(6); break; + case 7: SV(7); break; + } + + printf("\n\n"); + printf(" C%d00 C%d10 C%d20 C%d30\n", matid, matid, matid, matid); + printf("R%d00: %0.6f %0.6f %0.6f %0.6f\n", matid, m[0], m[1], m[2], m[3]); + printf("R%d01: %0.6f %0.6f %0.6f %0.6f\n", matid, m[4], m[5], m[6], m[7]); + printf("R%d02: %0.6f %0.6f %0.6f %0.6f\n", matid, m[8], m[9], m[10], m[11]); + printf("R%d03: %0.6f %0.6f %0.6f %0.6f\n", matid, m[12], m[13], m[14], m[15]); +} diff --git a/source/libpspmath/pspmath.h b/source/libpspmath/pspmath.h new file mode 100644 index 0000000..6fe1ab5 --- /dev/null +++ b/source/libpspmath/pspmath.h @@ -0,0 +1,445 @@ +/* + * PSP Software Development Kit - http://www.pspdev.org + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * Copyright (c) 2007 Steve Galatis + * Copyright (c) 2007 Christophe Avoinme + * + * General purpose math library using vfpu optimized instructions. + * Most of the routines here run at least 5 to 20 times faster than the equivalent + * routines in libm + * + */ + +#ifndef _pspmath_h_ +#define _pspmath_h_ + +#include + +/** @defgroup VFPUMATH VFPU Math Library + * + */ + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef struct quat { + float x, y, z, w; +} __attribute__((aligned(16))) ScePspQuatMatrix; + +/** @addtogroup VFPUMATH */ +/*@{*/ + +/** + * Print a formatted block of 16 vfpu registers + * + * @param blockid - register block id (0 to 7) + * +**/ +void printVFPURegisters(int blockid); + +/** + * Set vfpu random generator seed + * + * @param x - seed value + * +**/ +void vfpu_srand(unsigned int x); + +/** + * Return random float value + * + * @param min - minimum value to return + * @param max - maximum value to return + * +**/ +float vfpu_randf(float min, float max); + +/** + * Return random color value in 8888 format + * This always sets the alpha channel value to 0xFF + * + * @param min - minimum value for each color channel (0..255) + * @param max - maximum value for each color channel (0..255) + * +**/ +unsigned int vfpu_rand_8888(int min, int max); + +/** + * Generate an identity matrix + * + * @param m - pointer to matrix to set + * +**/ +void vfpu_identity_matrix(ScePspFMatrix4 *m); + +/** + * Generate a translation matrix + * + * @param m - pointer to matrix + * @param x - translation on x axis + * @param y - translation on y axis + * @param z - translation on z axis + * +**/ +void vfpu_translate_matrix(ScePspFMatrix4 *m, float x, float y, float z); + +/** + * Generate a perspective projection matrix + * + * @param m - pointer to matrix + * @param fovy - vertical field of view + * @param aspect - aspect ratio of viewport + * @param near - near clipping plane + * @param far - far clipping plane + * +**/ +void vfpu_perspective_matrix(ScePspFMatrix4 *m, float fovy, float aspect, float near, float far); + +/** + * Generate an orthogonal projection matrix + * + * @param m - pointer to matrix + * @param left - coordinate for left edge of viewport + * @param right - coordinate for right edge of viewport + * @param bottom - coordinate for bottom edge of viewport + * @param top - coordinate for top edge of viewport + * @param near - near clipping plane + * @param far - far clipping plane + * +**/ +void vfpu_ortho_matrix(ScePspFMatrix4 *m, float left, float right, float bottom, float top, float near, float far); + +/** + * Calculate sine + * + * @param x - input in radians + * +**/ +float vfpu_sinf(float x); + +/** + * Calculate cosine + * + * @param x - input in radians + * +**/ +float vfpu_cosf(float x); + +/** + * Calculate tangent + * + * @param x - input in radians + * +**/ +float vfpu_tanf(float x); + +/** + * Calculate inverse sine (arcsin) + * + * @param x - input + * +**/ +float vfpu_asinf(float x); + +/** + * Calculate inverse cosine (arccos) + * + * @param x - input + * +**/ +float vfpu_acosf(float x); + +/** + * Calculate inverse tangent (arctan) + * + * @param x - input + * +**/ +float vfpu_atanf(float x); + +/** + * Calculate inverse tangent, with proper quadrant fixup + * + * @param x - input + * +**/ +float vfpu_atan2f(float x, float y); + +/** + * Calculate hyperbolic sine + * + * @param x - input + * +**/ +float vfpu_sinhf(float x); + +/** + * Calculate hyperbolic cosine + * + * @param x - input + * +**/ +float vfpu_coshf(float x); + +/** + * Calculate hyperbolic tangent + * + * @param x - input + * +**/ +float vfpu_tanhf(float x); + +/** + * Calculate sine and cosine + * + * @param r - input in radians + * @param s - pointer to float for sin + * @param c - pointer to float for cos +**/ +void vfpu_sincos(float r, float *s, float *c); + +/** + * Calculate exponent of x + * + * @param x - input + * +**/ +float vfpu_expf(float x); + +/** + * Calculate logarithm of x + * + * @param x - input + * +**/ +float vfpu_logf(float x); + +/** + * Calculate x raised to the power of y + * + * @param x - number to raise power of + * @param y - power to raise x by + * +**/ +float vfpu_powf(float x, float y); + +/** + * Calculate floating point remainder of x/y + * + * @param x - input + * @param y - input + * +**/ +float vfpu_fmodf(float x, float y); + +/** + * Calculate absolute floating point of x + * + * @param x - input + * +**/ +float vfpu_fabsf(float x); + +/** + * Calculate floating square root point of x + * + * @param x - input + * +**/ +float vfpu_sqrtf(float x); + +/** + * Perform a smooth acceleration/deceleration curve based on the input time value + * returns 0 to 1 + * + * @param t - input (0 to 1 float) + * +**/ +float vfpu_ease_in_out(float t); + + +/** + * Normalize a 3d vector, returning a unit vector of length = 1 + * + * @param v - pointer to vector to normalize + * +**/ +void vfpu_normalize_vector(ScePspFVector4 *v); + +/** + * Zero a 3d vector + * + * @param v - pointer to vector + * +**/ +void vfpu_zero_vector(ScePspFVector4 *v); + +/** + * Scale a 3d vector + * + * @param vout - pointer to result vector + * @param vin - pointer to vector to scale + * @param scale - float value to scale vector by + * +**/ +void vfpu_scale_vector(ScePspFVector4 *vout, ScePspFVector4 *vin, float scale); + +/** + * Add 2 3d vectors + * + * @param vout - pointer to result vector + * @param va - pointer to first vector to add + * @param vb - pointer to second vector to add +**/ +void vfpu_add_vector(ScePspFVector4 *vout, ScePspFVector4 *va, ScePspFVector4 *vb); + + +/** + * Generate rotation matrix for environment map. + * + * @param envmat - pointer to array of 2 vectors to store envmap matrix + * @param r - angle to rotate in radians +**/ +void vfpu_envmap_matrix(ScePspFVector4 *envmat, float r); + +/** + * Transform 3d vector by 4x4 matrix + * + * @param m - pointer to transformation matrix + * @param vin - pointer to vector to transform + * @param vout - pointer to result vector +**/ +void vfpu_transform_vector(ScePspFMatrix4 *m, ScePspFVector4 *vin, ScePspFVector4 *vout); + +/** + * Convert input sphere coordinates to cartesian coordinates + * + * @param az - azimuth angle (0 to PI*2) + * @param ze - zenith angle (0 to PI) + * @param rad - sphere radius + * @param x - pointer to float for x coordinate + * @param y - pointer to float for y coordinate + * @param z - pointer to float for z coordinate + * +**/ +void vfpu_sphere_to_cartesian(float az, float ze, float rad, float *x, float *y, float *z); + +/** + * Generate an identity quaternion + * + * @param q - pointer to quaternion + * + * this will set the quaternion's components to 0,0,0,1 + * +**/ +void vfpu_quaternion_identity(ScePspQuatMatrix *q); + +/** + * Copy a quaternion + * + * @param dst - pointer to quaternion to copy to + * @param src - pointer to quaternion to copy from + * +**/ +void vfpu_quaternion_copy(ScePspQuatMatrix *dst, ScePspQuatMatrix *src); + +/** + * Normalize a quaternion + * + * @param res - pointer to quaternion to normalize + * +**/ +void vfpu_quaternion_normalize(ScePspQuatMatrix *res); + +/** + * Multiply 2 quaternions + * + * @param qout - pointer to result quaternion + * @param a - pointer to first quaternion to multiply + * @param b - pointer to second quaternion to multiply + * +**/ +void vfpu_quaternion_multiply(ScePspQuatMatrix *qout, ScePspQuatMatrix *a, ScePspQuatMatrix *b); + +/** + * Make a quaternion from euler angles + * + * @param qout - pointer to output quaternion + * @param rx - rotation on x axis, in degrees + * @param ry - rotation on y axis, in degrees + * @param rz - rotation on z axis, in degrees +**/ +void vfpu_quaternion_from_euler(ScePspQuatMatrix *qout, float rx, float ry, float rz); + +/** + * Calculate exponent of a quaternion + * + * @param qout - pointer to output quaternion + * @param qin - pointer to input quaternion + * +**/ +void vfpu_quaternion_exp(ScePspQuatMatrix *qout, ScePspQuatMatrix *qin); + +/** + * Calculate logarithm of a quaternion + * + * @param qout - pointer to output quaternion + * @param qin - pointer to input quaternion + * +**/ +void vfpu_quaternion_ln(ScePspQuatMatrix *qout, ScePspQuatMatrix *qin); + +/** + * Return a sample from a linear interpolation of 2 quaternions + * + * @param qout - pointer to output quaternion + * @param a - pointer to starting quaternion + * @param b - pointer to ending quaternion + * @param t - time value to sample, from 0 to 1 + * +**/ +void vfpu_quaternion_sample_linear(ScePspQuatMatrix *qout, ScePspQuatMatrix *a, ScePspQuatMatrix *b, float t); + +/** + * Return a sample from a hermite spline interpolation + * + * @param qout - pointer to output quaternion + * @param a - pointer to start quaternion + * @param b - pointer to end quaternion + * @param at - pointer to tangent point for quaternion a + * @param bt - pointer to tangent point for quaternion b + * @param t - time value to sample, from 0 to 1 + * +**/ +void vfpu_quaternion_sample_hermite(ScePspQuatMatrix *qout, ScePspQuatMatrix *a, ScePspQuatMatrix *b, ScePspQuatMatrix *at, ScePspQuatMatrix *bt, float t); + +/** + * Return a tangent point for hermite spline interpolation + * + * @param qout - pointer to output quaternion + * @param p1 - pointer to p-1 on spline curve for tangent + * @param p2 - pointer to p+1 on spline curve for tangent + * @param bias - value to scale difference between endpoints. + * for example, 0.5 results in a catmull-rom spline tangent + * +**/ + +void vfpu_quaternion_hermite_tangent(ScePspQuatMatrix *qout, ScePspQuatMatrix *p1, ScePspQuatMatrix *p2, float bias); + +/** + * Convert quaternion to rotation matrix + * + * @param q - pointer to input quaternion + * @param m - pointer to output matrix + * +**/ +void vfpu_quaternion_to_matrix(ScePspQuatMatrix *q, ScePspFMatrix4 *m); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/source/libpspmath/vfpu_acosf.c b/source/libpspmath/vfpu_acosf.c new file mode 100644 index 0000000..dc86193 --- /dev/null +++ b/source/libpspmath/vfpu_acosf.c @@ -0,0 +1,14 @@ +#include "pspmath.h" + +float vfpu_acosf(float x) { + float result; + __asm__ volatile ( + "mtv %1, S000\n" + "vcst.s S001, VFPU_PI_2\n" + "vasin.s S000, S000\n" + "vocp.s S000, S000\n" + "vmul.s S000, S000, S001\n" + "mfv %0, S000\n" + : "=r"(result) : "r"(x)); + return result; +} diff --git a/source/libpspmath/vfpu_add_vector.c b/source/libpspmath/vfpu_add_vector.c new file mode 100644 index 0000000..c563e63 --- /dev/null +++ b/source/libpspmath/vfpu_add_vector.c @@ -0,0 +1,10 @@ +#include "pspmath.h" + +void vfpu_add_vector(ScePspFVector4 *vout, ScePspFVector4 *va, ScePspFVector4 *vb) { + __asm__ volatile ( + "lv.q C000, %1\n" + "lv.q C010, %2\n" + "vadd.t C020, C000, C010\n" + "sv.q C020, %0\n" + : "+m"(*vout): "m"(*va), "m"(*vb)); +} diff --git a/source/libpspmath/vfpu_asinf.c b/source/libpspmath/vfpu_asinf.c new file mode 100644 index 0000000..b636a1b --- /dev/null +++ b/source/libpspmath/vfpu_asinf.c @@ -0,0 +1,13 @@ +#include "pspmath.h" + +float vfpu_asinf(float x) { + float result; + __asm__ volatile ( + "mtv %1, S000\n" + "vcst.s S001, VFPU_PI_2\n" + "vasin.s S000, S000\n" + "vmul.s S000, S000, S001\n" + "mfv %0, S000\n" + : "=r"(result) : "r"(x)); + return result; +} diff --git a/source/libpspmath/vfpu_atanf.c b/source/libpspmath/vfpu_atanf.c new file mode 100644 index 0000000..18c85b0 --- /dev/null +++ b/source/libpspmath/vfpu_atanf.c @@ -0,0 +1,57 @@ +#include "pspmath.h" + +float vfpu_atanf(float x) { + float result; + // result = asinf(x/sqrt(x*x+1)) + __asm__ volatile ( + "mtv %1, S000\n" + "vmul.s S001, S000, S000\n" + "vadd.s S001, S001, S001[1]\n" + "vrsq.s S001, S001\n" + "vmul.s S000, S000, S001\n" + "vasin.s S000, S000\n" + "vcst.s S001, VFPU_PI_2\n" + "vmul.s S000, S000, S001\n" + "mfv %0, S000\n" + : "=r"(result) : "r"(x)); + return result; +} + + +#define PI 3.14159265358979f +#define PI_2 1.57079632679489f + +inline static float fabsf(float x) { + float r; + __asm__ volatile( "abs.s %0, %1" : "=f"(r) :"f"(x):"memory"); + return r; +} + +/* double a; + + if (fabs(x) >= fabs(y)) { + a = atan(y/x) ; + if (x < 0.0) { + if (y >= 0.0) a += _pi ; + else a -= _pi ; + } + } + else { + a = -atan(x/y) ; + if (y < 0.0) a -= _halfpi ; + else a += _halfpi ; + } + return a ;*/ + +float vfpu_atan2f(float y, float x) { + float r; + + if (fabsf(x) >= fabsf(y)) { + r = vfpu_atanf(y/x); + if (x < 0.0f) r += (y>=0.0f ? PI : -PI); + } else { + r = -vfpu_atanf(x/y); + r += (y < 0.0f ? -PI_2 : PI_2); + } + return r; +} diff --git a/source/libpspmath/vfpu_cosf.c b/source/libpspmath/vfpu_cosf.c new file mode 100644 index 0000000..ab4da46 --- /dev/null +++ b/source/libpspmath/vfpu_cosf.c @@ -0,0 +1,13 @@ +#include "pspmath.h" + +float vfpu_cosf(float rad) { + float result; + __asm__ volatile ( + "mtv %1, S000\n" + "vcst.s S001, VFPU_2_PI\n" + "vmul.s S000, S000, S001\n" + "vcos.s S000, S000\n" + "mfv %0, S000\n" + : "=r"(result) : "r"(rad)); + return result; +} diff --git a/source/libpspmath/vfpu_coshf.c b/source/libpspmath/vfpu_coshf.c new file mode 100644 index 0000000..5396114 --- /dev/null +++ b/source/libpspmath/vfpu_coshf.c @@ -0,0 +1,18 @@ +#include "pspmath.h" + +float vfpu_coshf(float x) { + float result; + __asm__ volatile ( + "mtv %1, S000\n" + "vcst.s S001, VFPU_LN2\n" + "vrcp.s S001, S001\n" + "vmov.s S002, S000[|x|]\n" + "vmul.s S002, S001, S002\n" + "vexp2.s S002, S002\n" + "vrcp.s S003, S002\n" + "vadd.s S002, S002, S003\n" + "vmul.s S002, S002, S002[1/2]\n" + "mfv %0, S002\n" + : "=r"(result) : "r"(x)); + return result; +} diff --git a/source/libpspmath/vfpu_ease_in_out.c b/source/libpspmath/vfpu_ease_in_out.c new file mode 100644 index 0000000..ceafb8a --- /dev/null +++ b/source/libpspmath/vfpu_ease_in_out.c @@ -0,0 +1,19 @@ +#include "pspmath.h" + +// simple ease in/out function +// input will be clamped to range of 0 < t < 1 + +float vfpu_ease_in_out(float t) { + float r; + __asm__ volatile ( + "mtv %1, S000\n" + "vmov.q C000[0:1,,,], C000[x, 2, 1, 1/2]\n" // C000 = [0x.x = 2.0 / dx + "vmul.s S111, S111[2], S021\n" // S110 = m->y.y = 2.0 / dy + "vmul.s S122, S122[2], S022[-x]\n" // S122 = m->z.z = -2.0 / dz + "vsub.t C130, C000[-x,-y,-z], C010\n" // C130 = m->w[x, y, z] = [-(right+left), -(top+bottom), -(far+near)] + // we do vsub here since -(a+b) => (-1*a) + (-1*b) => -a - b + "vmul.t C130, C130, C020\n" // C130 = [-(right+left)/dx, -(top+bottom)/dy, -(far+near)/dz] + "sv.q C100, 0 + %0\n" + "sv.q C110, 16 + %0\n" + "sv.q C120, 32 + %0\n" + "sv.q C130, 48 + %0\n" + :"=m"(*m) : "r"(left), "r"(right), "r"(bottom), "r"(top), "r"(near), "r"(far)); +} + + +void vfpu_transform_vector(ScePspFMatrix4 *m, ScePspFVector4 *vin, ScePspFVector4 *vout) { + __asm__ volatile ( + "lv.q C000, 0 + %1\n" + "lv.q C010, 16 + %1\n" + "lv.q C020, 32 + %1\n" + "lv.q C030, 48 + %1\n" + "lv.q C100, %2\n" + "vtfm4.q C110, M000, C100\n" + "sv.q C110, %0\n" + :"+m"(*vout): "m"(*m), "m"(*vin)); +} diff --git a/source/libpspmath/vfpu_perspective_matrix.c b/source/libpspmath/vfpu_perspective_matrix.c new file mode 100644 index 0000000..b71494d --- /dev/null +++ b/source/libpspmath/vfpu_perspective_matrix.c @@ -0,0 +1,31 @@ +#include "pspmath.h" + +void vfpu_perspective_matrix(ScePspFMatrix4 *m, float fovy, float aspect, float near, float far) { + __asm__ volatile ( + "vmzero.q M100\n" // set M100 to all zeros + "mtv %1, S000\n" // S000 = fovy + "viim.s S001, 90\n" // S002 = 90.0f + "vrcp.s S001, S001\n" // S002 = 1/90 + "vmul.s S000, S000, S000[1/2]\n" // S000 = fovy * 0.5 = fovy/2 + "vmul.s S000, S000, S001\n" // S000 = (fovy/2)/90 + "vrot.p C002, S000, [c, s]\n" // S002 = cos(angle), S003 = sin(angle) + "vdiv.s S100, S002, S003\n" // S100 = m->x.x = cotangent = cos(angle)/sin(angle) + "mtv %3, S001\n" // S001 = near + "mtv %4, S002\n" // S002 = far + "vsub.s S003, S001, S002\n" // S003 = deltaz = near-far + "vrcp.s S003, S003\n" // S003 = 1/deltaz + "mtv %2, S000\n" // S000 = aspect + "vmov.s S111, S100\n" // S111 = m->y.y = cotangent + "vdiv.s S100, S100, S000\n" // S100 = m->x.x = cotangent / aspect + "vadd.s S122, S001, S002\n" // S122 = m->z.z = far + near + "vmul.s S122, S122, S003\n" // S122 = m->z.z = (far+near)/deltaz + "vmul.s S132, S001, S002\n" // S132 = m->w.z = far * near + "vmul.s S132, S132, S132[2]\n" // S132 = m->w.z = 2 * (far*near) + "vmul.s S132, S132, S003\n" // S132 = m->w.z = 2 * (far*near) / deltaz + "vsub.s S123, S123, S123[1]\n" // S123 = m->z.w = -1.0 + "sv.q C100, 0 + %0\n" + "sv.q C110, 16 + %0\n" + "sv.q C120, 32 + %0\n" + "sv.q C130, 48 + %0\n" + :"=m"(*m): "r"(fovy),"r"(aspect),"r"(near),"r"(far)); +} diff --git a/source/libpspmath/vfpu_powf.c b/source/libpspmath/vfpu_powf.c new file mode 100644 index 0000000..a9e35d3 --- /dev/null +++ b/source/libpspmath/vfpu_powf.c @@ -0,0 +1,15 @@ +#include "pspmath.h" + +float vfpu_powf(float x, float y) { + float result; + // result = exp2f(y * log2f(x)); + __asm__ volatile ( + "mtv %1, S000\n" + "mtv %2, S001\n" + "vlog2.s S001, S001\n" + "vmul.s S000, S000, S001\n" + "vexp2.s S000, S000\n" + "mfv %0, S000\n" + : "=r"(result) : "r"(x), "r"(y)); + return result; +} diff --git a/source/libpspmath/vfpu_quaternion_copy.c b/source/libpspmath/vfpu_quaternion_copy.c new file mode 100644 index 0000000..1ec6e14 --- /dev/null +++ b/source/libpspmath/vfpu_quaternion_copy.c @@ -0,0 +1,8 @@ +#include "pspmath.h" + +void vfpu_quaternion_copy(ScePspQuatMatrix *dst, ScePspQuatMatrix *src) { + __asm__ volatile ( + "lv.q C000, %1\n" + "sv.q C000, %0\n" + :"+m"(*dst) : "m"(*src)); +} diff --git a/source/libpspmath/vfpu_quaternion_exp.c b/source/libpspmath/vfpu_quaternion_exp.c new file mode 100644 index 0000000..c92d45d --- /dev/null +++ b/source/libpspmath/vfpu_quaternion_exp.c @@ -0,0 +1,24 @@ +#include "pspmath.h" + +void vfpu_quaternion_exp(ScePspQuatMatrix *qout, ScePspQuatMatrix *qin) { + //float r = sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]); + //float et = exp(a[3]); + //float s = r>=0.00001f? et*sin(r)/r: 0.f; + //return quat(s*a[0],s*a[1],s*a[2],et*cos(r)); + __asm__ volatile ( + "lv.q C000, 0 + %1\n" // C000 = [x, y, z, w] + "vdot.t S010, C000, C000\n" // S010 = x^2 + y^2 + z^2 + "vsqrt.s S010, S010\n" // S010 = r = sqrt(x^2 + y^2 + z^2) + "vcst.s S011, VFPU_LN2\n" // S011 = ln(2) + "vrcp.s S011, S011\n" // S011 = 1/ln(2) + "vmul.s S011, S011, S003\n" // S011 = w*(1/ln(2)) + "vexp2.s S011, S011\n" // S011 = et = exp(w) + "vcst.s S012, VFPU_2_PI\n" // S012 = 2/PI + "vmul.s S012, S012, S010\n" // S012 = r * 2/PI + "vrot.p R003, S012, [c,s]\n" // S003 = cos(r), S013 = sin(r) + "vdiv.s S013, S013, S010\n" // S013 = sin(r)/r + "vscl.p R003, R003, S011\n" // S003 = et * cos(r), S013 = et * sin(r)/r + "vscl.t C000, C000, S013\n" // C000 = [s*x, s*y, s*z, et*cos(r)] + "sv.q C000, 0 + %0\n" + : "=m"(*qout) : "m"(*qin)); +} diff --git a/source/libpspmath/vfpu_quaternion_from_euler.c b/source/libpspmath/vfpu_quaternion_from_euler.c new file mode 100644 index 0000000..82373ae --- /dev/null +++ b/source/libpspmath/vfpu_quaternion_from_euler.c @@ -0,0 +1,34 @@ +#include "pspmath.h" + +#define PI 3.14159265358979323846f +const float piover180 = PI/180.0f; + +void vfpu_quaternion_from_euler(ScePspQuatMatrix *res, float x, float y, float z) { + __asm__ volatile ( + "mtv %1, S000\n\t" // S000 = x + "mtv %2, S001\n\t" // S001 = y + "mtv %3, S002\n\t" // S002 = z + "lv.s S010, 0(%4)\n" + "vscl.t C000, C000, S010\n\t" // x *= pi/180, y *= pi/180, z *= pi/180 + + "vfim.s S010, 0.5\n\t" // S010 = 0.5 + "vscl.t C000, C000, S010\n\t" // x *= 0.5, y *= 0.5, z *= 0.5 + "vcst.s S010, VFPU_2_PI\n\t" // load 2/PI into S010, S011 and S012 + "vscl.t C000, C000, S010\n\t" // x *= 2/PI, y *= 2/pi, z *= pi/2 + + "vrot.p C010, S000, [s, c]\n\t" // S010 = sr, S011 = cr + "vrot.p C020, S001, [s, c]\n\t" // S020 = sp, S021 = cp + "vrot.p C030, S002, [s, c]\n\t" // S030 = sy, S031 = cy + + // fear the madness of prefixes + "vmul.q R100, C010[x,y,y,x], C020[y,x,x,y]\n" + "vmul.q R100, R100, C030[y,x,y,x]\n" + "vmul.q R101, C010[y,x,y,x], C020[y,x,y,x]\n" + "vmul.q R101, R101, C030[x,y,y,x]\n" + "vadd.q C000, R100[x,z,0,0], R100[-y,w,0,0]\n" + "vadd.q C000, C000, R101[0,0,x,z]\n" + "vadd.q C000, C000, R101[0,0,-y,w]\n" + "usv.q C000, %0\n\t" + :"=m"(*res) : "r"(x), "r"(y), "r"(z), "r"(&piover180)); + vfpu_quaternion_normalize(res); +} diff --git a/source/libpspmath/vfpu_quaternion_hermite_tangent.c b/source/libpspmath/vfpu_quaternion_hermite_tangent.c new file mode 100644 index 0000000..38df7b9 --- /dev/null +++ b/source/libpspmath/vfpu_quaternion_hermite_tangent.c @@ -0,0 +1,21 @@ +#include "pspmath.h" + +void vfpu_quaternion_hermite_tangent(ScePspQuatMatrix *qout, ScePspQuatMatrix *p1, ScePspQuatMatrix *p2, float bias) { + __asm__ volatile ( + // load p1 and p2 + "lv.q C000, 0(%1)\n" + "lv.q C010, 0(%2)\n" + + // load bias + "mtv %3, S100\n" + + // C020 = C010 - C000 + "vsub.q C020, C010, C000\n" + + // scale C020 by bias + "vscl.q C020, C020, S100\n" + + // store result + "sv.q C020, 0(%0)\n" + :"+r"(qout): "r"(p1), "r"(p2), "r"(bias)); +} diff --git a/source/libpspmath/vfpu_quaternion_identity.c b/source/libpspmath/vfpu_quaternion_identity.c new file mode 100644 index 0000000..689f24f --- /dev/null +++ b/source/libpspmath/vfpu_quaternion_identity.c @@ -0,0 +1,8 @@ +#include "pspmath.h" + +void vfpu_quaternion_identity(ScePspQuatMatrix *q) { + __asm__ volatile ( + "vidt.q C030\n" // column is important here, we need the w component set to 1 + "sv.q C030, %0\n" + : "+m"(*q)); +} diff --git a/source/libpspmath/vfpu_quaternion_ln.c b/source/libpspmath/vfpu_quaternion_ln.c new file mode 100644 index 0000000..77f2370 --- /dev/null +++ b/source/libpspmath/vfpu_quaternion_ln.c @@ -0,0 +1,26 @@ +#include "pspmath.h" + +void vfpu_quaternion_ln(ScePspQuatMatrix *qout, ScePspQuatMatrix *qin) { + //float r = sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]); + //float t = r>0.00001f? atan2(r,a[3])/r: 0.f; + //return quat(t*a[0],t*a[1],t*a[2],0.5*log(norm(a))); + float r; + __asm__ volatile ( + "lv.q C010, 0 + %1\n" + "vdot.t S020, C010, C010\n" // r = x^2 + y^2 + z^2 + "vsqrt.s S020, S020\n" // r = sqrt(x^2 + y^2 + z^2) + "mfv %0, S020\n" + :"=r"(r): "m"(*qin)); + r = vfpu_atan2f(r, qin->w)/r; + __asm__ volatile ( + "mtv %1, S021\n" // t = atan2(r, w) / r + "vdot.q S022, C010, C010\n" // norm = x^2 + y^2 + z^2 + w^2 + "vcst.s S023, VFPU_LOG2E\n" + "vrcp.s S023, S023\n" + "vlog2.s S013, S022\n" + "vmul.s S013, S013, S023\n" + "vmul.s S013, S013, S013[1/2]\n" + "vscl.t C010, C010, S021\n" + "sv.q C010, 0 + %0\n" + : "=m"(*qout) : "r"(r)); +} diff --git a/source/libpspmath/vfpu_quaternion_multiply.c b/source/libpspmath/vfpu_quaternion_multiply.c new file mode 100644 index 0000000..f86e97b --- /dev/null +++ b/source/libpspmath/vfpu_quaternion_multiply.c @@ -0,0 +1,10 @@ +#include "pspmath.h" + +void vfpu_quaternion_multiply(ScePspQuatMatrix *qout, ScePspQuatMatrix *a, ScePspQuatMatrix *b) { + __asm__ volatile ( + "lv.q C000, %1\n" // load quaternion a + "lv.q C010, %2\n" // load quaternion b + "vqmul.q C020, C000, C010\n" // C000 = quat a * quat b (quaternion multiply) + "sv.q C020, %0\n" // store result + : "+m"(*qout) : "m"(*a), "m"(*b)); +} diff --git a/source/libpspmath/vfpu_quaternion_normalize.c b/source/libpspmath/vfpu_quaternion_normalize.c new file mode 100644 index 0000000..37f0673 --- /dev/null +++ b/source/libpspmath/vfpu_quaternion_normalize.c @@ -0,0 +1,11 @@ +#include "pspmath.h" + +void vfpu_quaternion_normalize(ScePspQuatMatrix *res) { + __asm__ volatile ( + "lv.q C000, %0\n" // load quaternion into C000 + "vdot.q S010, C000, C000\n" // S010 = x^2 + y^2 + z^2 + w^2 + "vrsq.s S010, S010\n" // S020 = 1.0 / sqrt(S100) + "vscl.q C000, C000, S010\n" // C000 = C000 * S010 (normalized quaternion) + "sv.q C000, %0\n" // store into quaternion result + : "+m"(*res)); +} diff --git a/source/libpspmath/vfpu_quaternion_sample_hermite.c b/source/libpspmath/vfpu_quaternion_sample_hermite.c new file mode 100644 index 0000000..e4cb9ab --- /dev/null +++ b/source/libpspmath/vfpu_quaternion_sample_hermite.c @@ -0,0 +1,67 @@ +#include "pspmath.h" + +// constants for the hermite curve functions +ScePspFMatrix4 hermite = { + { 2, -3, 0, 1 }, + { -2, 3, 0, 0 }, + { 1, -2, 1, 0 }, + { 1, -1, 0, 0 } +}; + +void vfpu_quaternion_sample_hermite(ScePspQuatMatrix *qout, ScePspQuatMatrix *a, ScePspQuatMatrix *b, ScePspQuatMatrix *at, ScePspQuatMatrix *bt, float t) { + __asm__ volatile ( + // load hermite transform matrix + "lv.q C000, 0(%6)\n" + "lv.q C010, 16(%6)\n" + "lv.q C020, 32(%6)\n" + "lv.q C030, 48(%6)\n" + + // load a, b, at, bt + "lv.q C100, 0(%1)\n" + "lv.q C110, 0(%2)\n" + "lv.q C120, 0(%3)\n" + "lv.q C130, 0(%4)\n" + + // C200 = [ t^3, t^2, t, 1] + "mtv %5, S202\n" + "vmul.s S201, S202, S202\n" + "vmul.s S200, S202, S201\n" + "vone.s S203\n" + + // multiply M000 by C200 + // C000 = [ 2*t^3, -3*t^2, 0, 1] + // C010 = [ -2*t^3, 3*t^2, 0, 0] + // C020 = [ t^3, -2*t^2, t, 0] + // C030 = [ t^3, -t^2, 0, 0] + + "vmul.q C000, C000, C200\n" + "vmul.q C010, C010, C200\n" + "vmul.q C020, C020, C200\n" + "vmul.q C030, C030, C200\n" + + // sum the terms + // S210 = 2*t^3 - 3*t^2 + 1 + // S211 = -2*t^3 + 3*t^2 + // S212 = t^3 - 2*t^2 + t + // S213 = t^3 - t^2 + + "vfad.q S210, C000\n" + "vfad.q S211, C010\n" + "vfad.q S212, C020\n" + "vfad.q S213, C030\n" + + // scale the qaternions with terms + "vscl.q C100, C100, S210\n" + "vscl.q C110, C110, S211\n" + "vscl.q C120, C120, S212\n" + "vscl.q C130, C130, S213\n" + + // sum the results + "vadd.q C100, C100, C110\n" + "vadd.q C100, C100, C120\n" + "vadd.q C100, C100, C130\n" + + // and return results + "sv.q C100, 0(%0)\n" + :"+r"(qout): "r"(a), "r"(b), "r"(at), "r"(bt), "r"(t), "r"(&hermite)); +} diff --git a/source/libpspmath/vfpu_quaternion_sample_linear.c b/source/libpspmath/vfpu_quaternion_sample_linear.c new file mode 100644 index 0000000..efe33ba --- /dev/null +++ b/source/libpspmath/vfpu_quaternion_sample_linear.c @@ -0,0 +1,14 @@ +#include "pspmath.h" + +void vfpu_quaternion_sample_linear(ScePspQuatMatrix *qout, ScePspQuatMatrix *a, ScePspQuatMatrix *b, float t) { + __asm__ volatile ( + "lv.q C000, 0 + %1\n" + "lv.q C010, 0 + %2\n" + "mtv %3, S020\n" + "vocp.s S021, S020\n" + "vscl.q C000, C000, S021\n" + "vscl.q C010, C010, S020\n" + "vadd.q C000, C000, C010\n" + "sv.q C000, 0 + %0\n" + : "=m"(*qout) : "m"(*a), "m"(*b), "r"(t)); +} diff --git a/source/libpspmath/vfpu_quaternion_to_matrix.c b/source/libpspmath/vfpu_quaternion_to_matrix.c new file mode 100644 index 0000000..a236cb5 --- /dev/null +++ b/source/libpspmath/vfpu_quaternion_to_matrix.c @@ -0,0 +1,34 @@ +#include "pspmath.h" + +void vfpu_quaternion_to_matrix(ScePspQuatMatrix *q, ScePspFMatrix4 *m) { + __asm__ volatile ( + "lv.q C000, %1\n" // C000 = [x, y, z, w ] + "vmul.q C010, C000, C000\n" // C010 = [x2, y2, z2, w2] + "vcrs.t C020, C000, C000\n" // C020 = [yz, xz, xy ] + "vmul.q C030, C000[x,y,z,1], C000[w,w,w,2]\n" // C030 = [wx, wy, wz ] + + "vadd.q C100, C020[0,z,y,0], C030[0,z,-y,0]\n" // C100 = [0, xy+wz, xz-wy] + "vadd.s S100, S011, S012\n" // C100 = [y2+z2, xy+wz, xz-wy] + + "vadd.q C110, C020[z,0,x,0], C030[-z,0,x,0]\n" // C110 = [xy-wz, 0, yz+wx] + "vadd.s S111, S010, S012\n" // C110 = [xy-wz, x2+z2, yz+wx] + + "vadd.q C120, C020[y,x,0,0], C030[y,-x,0,0]\n" // C120 = [xz+wy, yz-wx, 0 ] + "vadd.s S122, S010, S011\n" // C120 = [xz+wy, yz-wx, x2+y2] + + "vmscl.t M100, M100, S033\n" // C100 = [2*(y2+z2), 2*(xy+wz), 2*(xz-wy)] + // C110 = [2*(xy-wz), 2*(x2+z2), 2*(yz+wx)] + // C120 = [2*(xz+wy), 2*(yz-wx), 2*(x2+y2)] + + "vocp.s S100, S100\n" // C100 = [1-2*(y2+z2), 2*(xy+wz), 2*(xz-wy) ] + "vocp.s S111, S111\n" // C110 = [2*(xy-wz), 1-2*(x2+z2), 2*(yz+wx) ] + "vocp.s S122, S122\n" // C120 = [2*(xz+wy), 2*(yz-wx), 1-2*(x2+y2)] + + "vidt.q C130\n" // C130 = [0, 0, 0, 1] + + "sv.q R100, 0 + %0\n" + "sv.q R101, 16 + %0\n" + "sv.q R102, 32 + %0\n" + "sv.q R103, 48 + %0\n" + : "=m"(*m) : "m"(*q)); +} diff --git a/source/libpspmath/vfpu_rand_8888.c b/source/libpspmath/vfpu_rand_8888.c new file mode 100644 index 0000000..a68e8ab --- /dev/null +++ b/source/libpspmath/vfpu_rand_8888.c @@ -0,0 +1,24 @@ +#include "pspmath.h" + +unsigned int vfpu_rand_8888(int min, int max) { + unsigned int result; + __asm__ volatile ( + "mtv %1, S020\n" + "mtv %2, S021\n" + "vmov.t C000, C020[x, x, x]\n" + "vmov.t C010, C020[y, y, y]\n" + "vi2f.t C000, C000, 0\n" + "vi2f.t C010, C010, 0\n" + "vsub.t C010, C010, C000\n" + "vrndf1.t C020\n" + "vsub.t C020, C020, C020[1, 1, 1]\n" + "vmul.t C020, C020, C010\n" + "vadd.t C020, C020, C000\n" + "vf2iz.t C020, C020, 23\n" + "viim.s S023, 255\n" + "vf2iz.s S023, S023, 23\n" + "vi2uc.q S000, C020\n" + "mfv %0, S000\n" + :"=r"(result): "r"(min), "r"(max)); + return result; +} diff --git a/source/libpspmath/vfpu_randf.c b/source/libpspmath/vfpu_randf.c new file mode 100644 index 0000000..d951ecb --- /dev/null +++ b/source/libpspmath/vfpu_randf.c @@ -0,0 +1,17 @@ +#include "pspmath.h" + +float vfpu_randf(float min, float max) { + float result; + __asm__ volatile ( + "mtv %1, S000\n" + "mtv %2, S001\n" + "vsub.s S001, S001, S000\n" + "vrndf1.s S002\n" + "vone.s S003\n" + "vsub.s S002, S002, S003\n" + "vmul.s S001, S002, S001\n" + "vadd.s S000, S000, S001\n" + "mfv %0, S000\n" + : "=r"(result) : "r"(min), "r"(max)); + return result; +} diff --git a/source/libpspmath/vfpu_scale_vector.c b/source/libpspmath/vfpu_scale_vector.c new file mode 100644 index 0000000..2e57ca9 --- /dev/null +++ b/source/libpspmath/vfpu_scale_vector.c @@ -0,0 +1,10 @@ +#include "pspmath.h" + +void vfpu_scale_vector(ScePspFVector4 *vout, ScePspFVector4 *vin, float scale) { + __asm__ volatile ( + "lv.q C000, %1\n" + "mtv %2, S010\n" + "vscl.t C000, C000, S010\n" + "sv.q C000, %0\n" + : "=m"(*vout) : "m"(*vin), "r"(scale)); +} diff --git a/source/libpspmath/vfpu_sincos.c b/source/libpspmath/vfpu_sincos.c new file mode 100644 index 0000000..9ca1279 --- /dev/null +++ b/source/libpspmath/vfpu_sincos.c @@ -0,0 +1,12 @@ +#include "pspmath.h" + +void vfpu_sincos(float r, float *s, float *c) { + __asm__ volatile ( + "mtv %2, S002\n" + "vcst.s S003, VFPU_2_PI\n" + "vmul.s S002, S002, S003\n" + "vrot.p C000, S002, [s, c]\n" + "mfv %0, S000\n" + "mfv %1, S001\n" + : "=r"(*s), "=r"(*c): "r"(r)); +} diff --git a/source/libpspmath/vfpu_sinf.c b/source/libpspmath/vfpu_sinf.c new file mode 100644 index 0000000..1bbd517 --- /dev/null +++ b/source/libpspmath/vfpu_sinf.c @@ -0,0 +1,13 @@ +#include "pspmath.h" + +float vfpu_sinf(float rad) { + float result; + __asm__ volatile ( + "mtv %1, S000\n" + "vcst.s S001, VFPU_2_PI\n" + "vmul.s S000, S000, S001\n" + "vsin.s S000, S000\n" + "mfv %0, S000\n" + : "=r"(result) : "r"(rad)); + return result; +} diff --git a/source/libpspmath/vfpu_sinhf.c b/source/libpspmath/vfpu_sinhf.c new file mode 100644 index 0000000..5cd8e8a --- /dev/null +++ b/source/libpspmath/vfpu_sinhf.c @@ -0,0 +1,20 @@ +#include "pspmath.h" + +float vfpu_sinhf(float x) { + float result; + __asm__ volatile ( + "mtv %1, S000\n" + "vcst.s S001, VFPU_LN2\n" + "vrcp.s S001, S001\n" + "vmov.s S002, S000[|x|]\n" + "vcmp.s NE, S000, S002\n" + "vmul.s S002, S001, S002\n" + "vexp2.s S002, S002\n" + "vrcp.s S003, S002\n" + "vsub.s S002, S002, S003\n" + "vmul.s S002, S002, S002[1/2]\n" + "vcmov.s S002, S002[-x], 0\n" + "mfv %0, S002\n" + : "=r"(result) : "r"(x)); + return result; +} diff --git a/source/libpspmath/vfpu_sphere_to_cartesian.c b/source/libpspmath/vfpu_sphere_to_cartesian.c new file mode 100644 index 0000000..93c634e --- /dev/null +++ b/source/libpspmath/vfpu_sphere_to_cartesian.c @@ -0,0 +1,20 @@ +#include "pspmath.h" + +void vfpu_sphere_to_cartesian(float az, float ze, float rad, float *x, float *y, float *z) { + __asm__ volatile ( + "mtv %3, S000\n" + "mtv %4, S001\n" + "mtv %5, S002\n" + "vcst.s S003, VFPU_2_PI\n" // C000 = [az, ze, rad, 2/pi] + "vscl.p C000, C000, S003\n" // C000 = [az*2/pi, ze*2/pi, rad, 2/pi] + "vrot.p C010, S000, [s, c]\n" // C010 = [sin(az), cos(az), ?, ?] + "vrot.p C012, S001, [s, c]\n" // C010 = [sin(az), cos(az), sin(ze), cos(ze)] + "vmul.q C020, C010[y, 1, x, 0], C010[z, w, z, 0]\n" // C020 = [0, cos(az)*sin(ez), cos(ze), sin(az)*sin(ze)] + "vscl.t C020, C020, S002\n" // C020 = [0, r*cos(az)*sin(ez), r*cos(ze), r*sin(az)*sin(ze)] + //"sv.q C020, 0 + %0\n" + "sv.s S020, %0\n" + "sv.s S021, %1\n" + "sv.s S022, %2\n" + :"+m"(*x), "+m"(*y), "+m"(*z) + :"r"(az), "r"(ze), "r"(rad)); +} diff --git a/source/libpspmath/vfpu_sqrtf.c b/source/libpspmath/vfpu_sqrtf.c new file mode 100644 index 0000000..38bfba0 --- /dev/null +++ b/source/libpspmath/vfpu_sqrtf.c @@ -0,0 +1,11 @@ +#include "pspmath.h" + +float vfpu_sqrtf(float x) { + float result; + __asm__ volatile ( + "mtv %1, S000\n" + "vsqrt.s S000, S000\n" + "mfv %0, S000\n" + : "=r"(result) : "r"(x)); + return result; +} diff --git a/source/libpspmath/vfpu_srand.c b/source/libpspmath/vfpu_srand.c new file mode 100644 index 0000000..7fbfc70 --- /dev/null +++ b/source/libpspmath/vfpu_srand.c @@ -0,0 +1,5 @@ +#include "pspmath.h" + +void vfpu_srand(unsigned int x) { + __asm__ volatile ( "mtv %0, S000\n vrnds.s S000" : "=r"(x)); +} diff --git a/source/libpspmath/vfpu_tanf.c b/source/libpspmath/vfpu_tanf.c new file mode 100644 index 0000000..492ed65 --- /dev/null +++ b/source/libpspmath/vfpu_tanf.c @@ -0,0 +1,15 @@ +#include "pspmath.h" + +float vfpu_tanf(float x) { + float result; + // result = sin(x)/cos(x); + __asm__ volatile ( + "mtv %1, S000\n" + "vcst.s S001, VFPU_2_PI\n" + "vmul.s S000, S000, S001\n" + "vrot.p C002, S000, [s, c]\n" + "vdiv.s S000, S002, S003\n" + "mfv %0, S000\n" + : "=r"(result) : "r"(x)); + return result; +} diff --git a/source/libpspmath/vfpu_tanhf.c b/source/libpspmath/vfpu_tanhf.c new file mode 100644 index 0000000..ac41642 --- /dev/null +++ b/source/libpspmath/vfpu_tanhf.c @@ -0,0 +1,20 @@ +#include "pspmath.h" + +float vfpu_tanhf(float x) { + float result; + //y = exp(x+x); + //return (y-1)/(y+1); + __asm__ volatile ( + "mtv %0, S000\n" + "vadd.s S000, S000, S000\n" + "vcst.s S001, VFPU_LN2\n" + "vrcp.s S001, S001\n" + "vmul.s S000, S000, S001\n" + "vexp2.s S000, S000\n" + "vone.s S001\n" + "vbfy1.p C002, C000\n" + "vdiv.s S000, S003, S002\n" + "mfv %0, S000\n" + : "=r"(result): "r"(x)); + return result; +} diff --git a/source/libpspmath/vfpu_translate_matrix.c b/source/libpspmath/vfpu_translate_matrix.c new file mode 100644 index 0000000..ec613b7 --- /dev/null +++ b/source/libpspmath/vfpu_translate_matrix.c @@ -0,0 +1,19 @@ +#include "pspmath.h" + +void vfpu_translate_matrix(ScePspFMatrix4 *m, float x, float y, float z) +{ + __asm__ volatile ( + "vmidt.q M000\n" + "mtv %1, S030\n" + "mtv %2, S031\n" + "mtv %3, S032\n" + //"vmidt.q M100\n" + //"lv.q C200, %1\n" + //"vmov.t C130, C200\n" + //"vmmul.q M200, M100, M000\n" + "sv.q C000, 0 + %0\n" + "sv.q C010, 16 + %0\n" + "sv.q C020, 32 + %0\n" + "sv.q C030, 48 + %0\n" + : "=m"(*m) : "r"(x), "r"(y), "r"(z)); +} diff --git a/source/libpspmath/vfpu_zero_vector.c b/source/libpspmath/vfpu_zero_vector.c new file mode 100644 index 0000000..afbc88d --- /dev/null +++ b/source/libpspmath/vfpu_zero_vector.c @@ -0,0 +1,8 @@ +#include "pspmath.h" + +void vfpu_zero_vector(ScePspFVector4 *v) { + __asm__ volatile ( + "vzero.t C000\n" + "sv.q C000, %0\n" + : "+m"(*v)); +} diff --git a/source/mathlib.c b/source/mathlib.c new file mode 100644 index 0000000..d864e14 --- /dev/null +++ b/source/mathlib.c @@ -0,0 +1,953 @@ +/* +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 "quakedef.h" + +#ifdef PSP_VFPU +#include +#endif + +void Sys_Error (char *error, ...); + +vec3_t vec3_origin = {0,0,0}; +int nanmask = 255<<23; + +int _mathlib_temp_int1, _mathlib_temp_int2, _mathlib_temp_int3; +float _mathlib_temp_float1, _mathlib_temp_float2, _mathlib_temp_float3; +vec3_t _mathlib_temp_vec1, _mathlib_temp_vec2, _mathlib_temp_vec3; + + +/*-----------------------------------------------------------------*/ + +float rsqrt( float number ) +{ +#ifdef PSP_VFPU + float d; + __asm__ ( //from official pspsdk by sony + ".set push\n" // save assember option + ".set noreorder\n" // suppress reordering + "lv.s s000, %1\n" // s000 = s + "vrsq.s s000, s000\n" // s000 = 1 / sqrt(s000) + "sv.s s000, %0\n" // d = s000 + ".set pop\n" // restore assember option + : "=m"(d) + : "m"(number) + ); + return d; +#else + int i; + float x, y; + + if( number == 0.0f ) + return 0.0f; + + x = number * 0.5f; + i = *(int *)&number; // evil floating point bit level hacking + i = 0x5f3759df - (i >> 1); // what the fuck? + y = *(float *)&i; + y = y * (1.5f - (x * y * y)); // first iteration + + return y; +#endif +} + +/* +================= +SinCos +================= +*/ +void SinCos( float radians, float *sine, float *cosine ) +{ + #ifdef PSP_VFPU + vfpu_sincos(radians,sine,cosine); + #else + __asm__ volatile ( + "mtv %2, S002\n" + "vcst.s S003, VFPU_2_PI\n" + "vmul.s S002, S002, S003\n" + "vrot.p C000, S002, [s, c]\n" + "mfv %0, S000\n" + "mfv %1, S001\n" + : "=r"(*sine), "=r"(*cosine): "r"(radians)); + #endif +} + +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++ ) + { + #ifdef PSP_VFPU + if ( vfpu_fabsf( src[i] ) < minelem ) + #else + if ( fabsf( src[i] ) < minelem ) + #endif + { + pos = i; + #ifdef PSP_VFPU + minelem = vfpu_fabsf( src[i] ); + #else + minelem = fabsf( src[i] ); + #endif + } + } + 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; + + #ifdef PSP_VFPU + zrot[0][0] = vfpu_cosf( DEG2RAD( degrees ) ); + zrot[0][1] = vfpu_sinf( DEG2RAD( degrees ) ); + zrot[1][0] = -vfpu_sinf( DEG2RAD( degrees ) ); + zrot[1][1] = vfpu_cosf( DEG2RAD( degrees ) ); + #else + zrot[0][0] = cosf( DEG2RAD( degrees ) ); + zrot[0][1] = sinf( DEG2RAD( degrees ) ); + zrot[1][0] = -sinf( DEG2RAD( degrees ) ); + zrot[1][1] = cosf( DEG2RAD( degrees ) ); + #endif + + 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) +{ +#if 0 + if (a >= 0) + a -= 360*(int)(a/360); + else + a += 360*( 1 + (int)(-a/360) ); +#endif + 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"); +} + +/* +================== +BoxOnPlaneSide + +Returns 1, 2, or 1 + 2 +================== +*/ +// crow_bar's enhanced boxonplaneside +int BoxOnPlaneSide(vec3_t emins, vec3_t emaxs, mplane_t *p) +{ +#ifdef PSP_VFPU + int sides; + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "lv.s S000, 0 + %[normal]\n" // S000 = p->normal[0] + "lv.s S001, 4 + %[normal]\n" // S001 = p->normal[1] + "lv.s S002, 8 + %[normal]\n" // S002 = p->normal[2] + "vzero.p C030\n" // C030 = [0.0f, 0.0f] + "lv.s S032, %[dist]\n" // S032 = p->dist + "move $8, $0\n" // $8 = 0 + "beq %[signbits], $8, 0f\n" // jump to 0 + "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot ) + "beq %[signbits], $8, 1f\n" // jump to 1 + "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot ) + "beq %[signbits], $8, 2f\n" // jump to 2 + "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot ) + "beq %[signbits], $8, 3f\n" // jump to 3 + "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot ) + "beq %[signbits], $8, 4f\n" // jump to 4 + "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot ) + "beq %[signbits], $8, 5f\n" // jump to 5 + "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot ) + "beq %[signbits], $8, 6f\n" // jump to 6 + "addiu $8, $8, 1\n" // $8 = $8 + 1 ( delay slot ) + "beq %[signbits], $8, 7f\n" // jump to 7 + "nop\n" // ( delay slot ) + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "0:\n" +/* + 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]; +*/ + "lv.s S010, 0 + %[emaxs]\n" // S010 = emaxs[0] + "lv.s S011, 4 + %[emaxs]\n" // S011 = emaxs[1] + "lv.s S012, 8 + %[emaxs]\n" // S012 = emaxs[2] + "lv.s S020, 0 + %[emins]\n" // S020 = emins[0] + "lv.s S021, 4 + %[emins]\n" // S021 = emins[1] + "lv.s S022, 8 + %[emins]\n" // S022 = emins[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "1:\n" +/* + 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]; +*/ + "lv.s S010, 0 + %[emins]\n" // S010 = emins[0] + "lv.s S011, 4 + %[emaxs]\n" // S011 = emaxs[1] + "lv.s S012, 8 + %[emaxs]\n" // S012 = emaxs[2] + "lv.s S020, 0 + %[emaxs]\n" // S020 = emaxs[0] + "lv.s S021, 4 + %[emins]\n" // S021 = emins[1] + "lv.s S022, 8 + %[emins]\n" // S022 = emins[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "2:\n" +/* + 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]; +*/ + "lv.s S010, 0 + %[emaxs]\n" // S010 = emaxs[0] + "lv.s S011, 4 + %[emins]\n" // S011 = emins[1] + "lv.s S012, 8 + %[emaxs]\n" // S012 = emaxs[2] + "lv.s S020, 0 + %[emins]\n" // S020 = emins[0] + "lv.s S021, 4 + %[emaxs]\n" // S021 = emaxs[1] + "lv.s S022, 8 + %[emins]\n" // S022 = emins[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "3:\n" +/* + 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]; +*/ + "lv.s S010, 0 + %[emins]\n" // S010 = emins[0] + "lv.s S011, 4 + %[emins]\n" // S011 = emins[1] + "lv.s S012, 8 + %[emaxs]\n" // S012 = emaxs[2] + "lv.s S020, 0 + %[emaxs]\n" // S020 = emaxs[0] + "lv.s S021, 4 + %[emaxs]\n" // S021 = emaxs[1] + "lv.s S022, 8 + %[emins]\n" // S022 = emins[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "4:\n" +/* + 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]; +*/ + "lv.s S010, 0 + %[emaxs]\n" // S010 = emaxs[0] + "lv.s S011, 4 + %[emaxs]\n" // S011 = emaxs[1] + "lv.s S012, 8 + %[emins]\n" // S012 = emins[2] + "lv.s S020, 0 + %[emins]\n" // S020 = emins[0] + "lv.s S021, 4 + %[emins]\n" // S021 = emins[1] + "lv.s S022, 8 + %[emaxs]\n" // S022 = emaxs[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "5:\n" +/* + 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]; +*/ + "lv.s S010, 0 + %[emins]\n" // S010 = emins[0] + "lv.s S011, 4 + %[emaxs]\n" // S011 = emaxs[1] + "lv.s S012, 8 + %[emins]\n" // S012 = emins[2] + "lv.s S020, 0 + %[emaxs]\n" // S020 = emaxs[0] + "lv.s S021, 4 + %[emins]\n" // S021 = emins[1] + "lv.s S022, 8 + %[emaxs]\n" // S022 = emaxs[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "6:\n" +/* + 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]; +*/ + "lv.s S010, 0 + %[emaxs]\n" // S010 = emaxs[0] + "lv.s S011, 4 + %[emins]\n" // S011 = emins[1] + "lv.s S012, 8 + %[emins]\n" // S012 = emins[2] + "lv.s S020, 0 + %[emins]\n" // S020 = emins[0] + "lv.s S021, 4 + %[emaxs]\n" // S021 = emaxs[1] + "lv.s S022, 8 + %[emaxs]\n" // S022 = emaxs[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "j 8f\n" // jump to SetSides + "nop\n" // ( delay slot ) + "7:\n" +/* + 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]; +*/ + "lv.s S010, 0 + %[emins]\n" // S010 = emins[0] + "lv.s S011, 4 + %[emins]\n" // S011 = emins[1] + "lv.s S012, 8 + %[emins]\n" // S012 = emins[2] + "lv.s S020, 0 + %[emaxs]\n" // S020 = emaxs[0] + "lv.s S021, 4 + %[emaxs]\n" // S021 = emaxs[1] + "lv.s S022, 8 + %[emaxs]\n" // S022 = emaxs[2] + "vdot.t S030, C000, C010\n" // S030 = C000 * C010 + "vdot.t S031, C000, C020\n" // S030 = C000 * C020 + "8:\n" // SetSides +/* + if( dist1 >= p->dist ) + sides = 1; + if( dist2 < p->dist ) + sides |= 2; +*/ + "addiu %[sides], $0, 0\n" // sides = 0 + "vcmp.s LT, S030, S032\n" // S030 < S032 + "bvt 0, 9f\n" // if ( CC[0] == 1 ) jump to 9 + "nop\n" // ( delay slot ) + "addiu %[sides], %[sides], 1\n"// sides = 1 + "9:\n" + "vcmp.s GE, S031, S032\n" // S031 >= S032 + "bvt 0, 10f\n" // if ( CC[0] == 1 ) jump to 10 + "nop\n" // ( delay slot ) + "addiu %[sides], %[sides], 2\n"// sides = sides + 2 + "10:\n" + ".set pop\n" // restore assembler option + : [sides] "=r" ( sides ) + : [normal] "m" (*(p->normal)), + [emaxs] "m" ( *emaxs ), + [emins] "m" ( *emins ), + [signbits] "r" ( p->signbits ), + [dist] "m" ( p->dist ) + : "$8" + ); + return sides; +#else + int sides = 0; + float dist1, dist2; + + // 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: + // shut up compiler + dist1 = dist2 = 0; + break; + } + + if( dist1 >= p->dist ) + sides = 1; + if( dist2 < p->dist ) + sides |= 2; + + return sides; +#endif +} + + +void vectoangles (vec3_t vec, vec3_t ang) +{ + float forward, yaw, pitch; + + if (!vec[1] && !vec[0]) + { + yaw = 0; + pitch = (vec[2] > 0) ? 90 : 270; + } + else + { + #ifdef PSP_VFPU + yaw = vec[0] ? (vfpu_atan2f(vec[1], vec[0]) * 180 / M_PI) : (vec[1] > 0) ? 90 : 270; + #else + yaw = vec[0] ? (atan2(vec[1], vec[0]) * 180 / M_PI) : (vec[1] > 0) ? 90 : 270; + #endif + if (yaw < 0) + yaw += 360; + + #ifdef PSP_VFPU + forward = vfpu_sqrtf (vec[0] * vec[0] + vec[1] * vec[1]); + pitch = vfpu_atan2f (vec[2], forward) * 180 / M_PI; + #else + forward = sqrt (vec[0] * vec[0] + vec[1] * vec[1]); + pitch = atan2 (vec[2], forward) * 180 / M_PI; + #endif + if (pitch < 0) + pitch += 360; + } + + ang[0] = pitch; + ang[1] = yaw; + ang[2] = 0; +} + +void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) +{ + float angle; + float sr, sp, sy, cr, cp, cy; + + angle = angles[YAW] * (M_PI*2 / 360); + #ifdef PSP_VFPU + sy = vfpu_sinf(angle); + cy = vfpu_cosf(angle); + #else + sy = sinf(angle); + cy = cosf(angle); + #endif + angle = angles[PITCH] * (M_PI*2 / 360); + #ifdef PSP_VFPU + sp = vfpu_sinf(angle); + cp = vfpu_cosf(angle); + #else + sp = sinf(angle); + cp = cosf(angle); + #endif + angle = angles[ROLL] * (M_PI*2 / 360); + #ifdef PSP_VFPU + sr = vfpu_sinf(angle); + cr = vfpu_cosf(angle); + #else + sr = sinf(angle); + cr = cosf(angle); + #endif + + 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; +} + +float VectorLength (vec3_t v) +{ + #ifdef PSP_VFPU + return vfpu_sqrtf(DotProduct(v, v)); + #else + return sqrtf(DotProduct(v, v)); + #endif +} + +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]; +} + +vec_t Length(vec3_t v) +{ + #ifdef PSP_VFPU + return vfpu_sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + #else + return sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + #endif +} + +float VecLength2(vec3_t v1, vec3_t v2) +{ + vec3_t k; + VectorSubtract(v1, v2, k); + return sqrt(k[0]*k[0] + k[1]*k[1] + k[2]*k[2]); +} + +float VectorNormalize (vec3_t v) +{ + #ifdef PSP_VFPU + float length = vfpu_sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + #else + float length = sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + #endif + + if (length) + { + const float ilength = 1.0f / 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 (float numer, float denom, int *quotient, + int *rem) +{ + int q, r; + float x; + + if (denom <= 0.0) + Sys_Error ("FloorDivMod: bad denominator %d\n", denom); + + if (numer >= 0.0) + { + + x = floorf(numer / denom); + q = (int)x; + r = (int)floorf(numer - (x * denom)); + } + else + { + // + // perform operations with positive values, and fix mod to make floor-based + // + x = floorf(-numer / denom); + q = -(int)x; + r = (int)floorf(-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) + (((float)0x10000 * (float)0x1000000 / (float)val) + 0.5); +} + +#endif + +void VectorTransform (const vec3_t in1, matrix3x4 in2, vec3_t out) +{ + out[0] = DotProduct(in1, in2[0]) + in2[0][3]; + out[1] = DotProduct(in1, in2[1]) + in2[1][3]; + out[2] = DotProduct(in1, in2[2]) + in2[2][3]; +} + +void AngleQuaternion( const vec3_t angles, vec4_t quaternion ) +{ + float angle; + float sr, sp, sy, cr, cp, cy; + + // FIXME: rescale the inputs to 1/2 angle + angle = angles[2] * 0.5; + #ifdef PSP_VFPU + sy = vfpu_sinf(angle); + cy = vfpu_cosf(angle); + #else + sy = sin(angle); + cy = cos(angle); + #endif + angle = angles[1] * 0.5; + #ifdef PSP_VFPU + sp = vfpu_sinf(angle); + cp = vfpu_cosf(angle); + #else + sp = sin(angle); + cp = cos(angle); + #endif + angle = angles[0] * 0.5; + #ifdef PSP_VFPU + sr = vfpu_sinf(angle); + cr = vfpu_cosf(angle); + #else + sr = sin(angle); + cr = cos(angle); + #endif + + quaternion[0] = sr*cp*cy-cr*sp*sy; // X + quaternion[1] = cr*sp*cy+sr*cp*sy; // Y + quaternion[2] = cr*cp*sy-sr*sp*cy; // Z + quaternion[3] = cr*cp*cy+sr*sp*sy; // W +} + +void QuaternionMatrix( const vec4_t quaternion, float (*matrix)[4] ) +{ + + matrix[0][0] = 1.0 - 2.0 * quaternion[1] * quaternion[1] - 2.0 * quaternion[2] * quaternion[2]; + matrix[1][0] = 2.0 * quaternion[0] * quaternion[1] + 2.0 * quaternion[3] * quaternion[2]; + matrix[2][0] = 2.0 * quaternion[0] * quaternion[2] - 2.0 * quaternion[3] * quaternion[1]; + + matrix[0][1] = 2.0 * quaternion[0] * quaternion[1] - 2.0 * quaternion[3] * quaternion[2]; + matrix[1][1] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[2] * quaternion[2]; + matrix[2][1] = 2.0 * quaternion[1] * quaternion[2] + 2.0 * quaternion[3] * quaternion[0]; + + matrix[0][2] = 2.0 * quaternion[0] * quaternion[2] + 2.0 * quaternion[3] * quaternion[1]; + matrix[1][2] = 2.0 * quaternion[1] * quaternion[2] - 2.0 * quaternion[3] * quaternion[0]; + matrix[2][2] = 1.0 - 2.0 * quaternion[0] * quaternion[0] - 2.0 * quaternion[1] * quaternion[1]; +} + +void QuaternionSlerp( const vec4_t p, vec4_t q, float t, vec4_t qt ) +{ + int i; + float omega, cosom, sinom, sclp, sclq; + + // decide if one of the quaternions is backwards + float a = 0; + float b = 0; + for (i = 0; i < 4; i++) { + a += (p[i]-q[i])*(p[i]-q[i]); + b += (p[i]+q[i])*(p[i]+q[i]); + } + if (a > b) { + for (i = 0; i < 4; i++) { + q[i] = -q[i]; + } + } + + cosom = p[0]*q[0] + p[1]*q[1] + p[2]*q[2] + p[3]*q[3]; + + if ((1.0 + cosom) > 0.00000001) { + if ((1.0 - cosom) > 0.00000001) { + omega = acos( cosom ); + #ifdef PSP_VFPU + sinom = vfpu_sinf( omega ); + sclp = vfpu_sinf( (1.0 - t)*omega) / sinom; + sclq = vfpu_sinf( t*omega ) / sinom; + #else + sinom = sin( omega ); + sclp = sin( (1.0 - t)*omega) / sinom; + sclq = sin( t*omega ) / sinom; + #endif + } + else { + sclp = 1.0 - t; + sclq = t; + } + for (i = 0; i < 4; i++) { + qt[i] = sclp * p[i] + sclq * q[i]; + } + } + else { + qt[0] = -p[1]; + qt[1] = p[0]; + qt[2] = -p[3]; + qt[3] = p[2]; + #ifdef PSP_VFPU + sclp = vfpu_sinf( (1.0 - t) * 0.5 * M_PI); + sclq = vfpu_sinf( t * 0.5 * M_PI); + #else + sclp = sin( (1.0 - t) * 0.5 * M_PI); + sclq = sin( t * 0.5 * M_PI); + #endif + for (i = 0; i < 3; i++) { + qt[i] = sclp * p[i] + sclq * qt[i]; + } + } +} diff --git a/source/mathlib.h b/source/mathlib.h new file mode 100644 index 0000000..b5247d8 --- /dev/null +++ b/source/mathlib.h @@ -0,0 +1,201 @@ +/* +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 +#ifdef PSP_VFPU +#include +#endif + +typedef float vec_t; +typedef vec_t vec2_t[2]; +typedef vec_t vec3_t[3]; +typedef vec_t vec4_t[4]; +typedef vec_t vec5_t[5]; + +typedef byte byte_vec4_t[4]; + +typedef int fixed4_t; +typedef int fixed8_t; +typedef int fixed16_t; + +#ifndef M_PI +#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h +#endif + +struct mplane_s; + +extern vec3_t vec3_origin; +extern int nanmask; + +/* +#ifndef M_PI +#define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h +#endif + +#define M_PI_DIV_180 (M_PI / 180.0) //johnfitz +//#define DEG2RAD( a ) ( a * M_PI ) / 180.0F +#define DEG2RAD( a ) ( (a) * M_PI_DIV_180 ) //johnfitz + +//#else +*/ +#ifndef M_PI +#define M_PI = GU_PI // matches value in gcc v2 math.h +#endif + +#define M_PI_DIV_180 (M_PI / 180.0) //johnfitz +//#define DEG2RAD( a ) ( a * M_PI ) / 180.0F +#define DEG2RAD( a ) ( (a) * M_PI_DIV_180 ) //johnfitz +//#endif + +#define CLAMP(min, x, max) ((x) < (min) ? (min) : (x) > (max) ? (max) : (x)) //johnfitz + +#define lhrandom(MIN,MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN)) + +#define IS_NAN(x) (((*(int *)&x)&nanmask)==nanmask) + +#define DotProduct(x,y) (x[0]*y[0]+x[1]*y[1]+x[2]*y[2]) +#define VectorSubtract(a,b,c) {(c)[0]=(a)[0]-(b)[0];(c)[1]=(a)[1]-(b)[1];(c)[2]=(a)[2]-(b)[2];} +#define VectorAdd(a,b,c) {c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];} +#define VectorCopy(a,b) {b[0]=a[0];b[1]=a[1];b[2]=a[2];} +#define VectorClear(a) ((a)[0] = (a)[1] = (a)[2] = 0) +#define VectorNegate(a, b) ((b)[0] = -(a)[0], (b)[1] = -(a)[1], (b)[2] = -(a)[2]) +#define VectorSet(v, x, y, z) ((v)[0] = (x), (v)[1] = (y), (v)[2] = (z)) +#define VectorRandom(v) {do{(v)[0] = lhrandom(-1, 1);(v)[1] = lhrandom(-1, 1);(v)[2] = lhrandom(-1, 1);}while(DotProduct(v, v) > 1);} + +#define VSM(a,b,c) {c[0]=a[0]*b;c[1]=a[1]*b;c[2]=a[2]*b;} + +// MDave -- courtesy of johnfitz, lordhavoc +#define VectorNormalizeFast(_v)\ +{\ + float _y, _number;\ + _number = DotProduct(_v, _v);\ + if (_number != 0.0)\ + {\ + *((long *)&_y) = 0x5f3759df - ((* (long *) &_number) >> 1);\ + _y = _y * (1.5f - (_number * 0.5f * _y * _y));\ + VectorScale(_v, _y, _v);\ + }\ +} + +typedef float matrix3x4[3][4]; +typedef float matrix3x3[3][3]; + +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); + +void vectoangles (vec3_t vec, vec3_t ang); + +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 VectorLength (vec3_t v); +float VecLength2(vec3_t v1, vec3_t v2); +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 (float numer, float 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 VectorL2Compare(v, w, m) \ + (_mathlib_temp_float1 = (m) * (m), \ + _mathlib_temp_vec1[0] = (v)[0] - (w)[0], _mathlib_temp_vec1[1] = (v)[1] - (w)[1], _mathlib_temp_vec1[2] = (v)[2] - (w)[2],\ + _mathlib_temp_vec1[0] * _mathlib_temp_vec1[0] + \ + _mathlib_temp_vec1[1] * _mathlib_temp_vec1[1] + \ + _mathlib_temp_vec1[2] * _mathlib_temp_vec1[2] < _mathlib_temp_float1) + + +#define VectorSupCompare(v, w, m) \ + (_mathlib_temp_float1 = m, \ + (v)[0] - (w)[0] > -_mathlib_temp_float1 && (v)[0] - (w)[0] < _mathlib_temp_float1 && \ + (v)[1] - (w)[1] > -_mathlib_temp_float1 && (v)[1] - (w)[1] < _mathlib_temp_float1 && \ + (v)[2] - (w)[2] > -_mathlib_temp_float1 && (v)[2] - (w)[2] < _mathlib_temp_float1) + +/* +#define VectorNormalizeFast(_v) \ +do { \ + _mathlib_temp_float1 = DotProduct((_v), (_v)); \ + if (_mathlib_temp_float1) { \ + _mathlib_temp_float2 = 0.5f * _mathlib_temp_float1; \ + _mathlib_temp_int1 = *((int *) &_mathlib_temp_float1); \ + _mathlib_temp_int1 = 0x5f375a86 - (_mathlib_temp_int1 >> 1); \ + _mathlib_temp_float1 = *((float *) &_mathlib_temp_int1); \ + _mathlib_temp_float1 = _mathlib_temp_float1 * (1.5f - _mathlib_temp_float2 * _mathlib_temp_float1 * _mathlib_temp_float1); \ + VectorScale((_v), _mathlib_temp_float1, (_v)) \ + } \ +} while (0); +*/ + +#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))) + +#define VectorInterpolate(v1, _frac, v2, v) \ +do { \ + _mathlib_temp_float1 = _frac; \ + \ + (v)[0] = (v1)[0] + _mathlib_temp_float1 * ((v2)[0] - (v1)[0]);\ + (v)[1] = (v1)[1] + _mathlib_temp_float1 * ((v2)[1] - (v1)[1]);\ + (v)[2] = (v1)[2] + _mathlib_temp_float1 * ((v2)[2] - (v1)[2]);\ +} while(0) + +#define FloatInterpolate(f1, _frac, f2) \ + (_mathlib_temp_float1 = _frac, \ + (f1) + _mathlib_temp_float1 * ((f2) - (f1))) + +#define PlaneDist(point, plane) ( \ + (plane)->type < 3 ? (point)[(plane)->type] : DotProduct((point), (plane)->normal) \ +) + +#define PlaneDiff(point, plane) ( \ + (((plane)->type < 3) ? (point)[(plane)->type] - (plane)->dist : DotProduct((point), (plane)->normal) - (plane)->dist) \ +) + +// Prototypes added by PM. +void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees ); +void VectorTransform (const vec3_t in1, matrix3x4 in2, vec3_t out); +extern int _mathlib_temp_int1, _mathlib_temp_int2, _mathlib_temp_int3; +extern float _mathlib_temp_float1, _mathlib_temp_float2, _mathlib_temp_float3; +extern vec3_t _mathlib_temp_vec1, _mathlib_temp_vec2, _mathlib_temp_vec3; \ No newline at end of file diff --git a/source/matrixlib.c b/source/matrixlib.c new file mode 100644 index 0000000..df32c1d --- /dev/null +++ b/source/matrixlib.c @@ -0,0 +1,532 @@ +/* +matrixlib.c - internal matrixlib +Copyright (C) 2010 Uncle Mike + +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. +*/ + +#include "quakedef.h" + + +const matrix3x4 matrix3x4_identity = +{ +{ 1, 0, 0, 0 }, // PITCH [forward], org[0] +{ 0, 1, 0, 0 }, // YAW [right] , org[1] +{ 0, 0, 1, 0 }, // ROLL [up] , org[2] +}; + +/* +======================================================================== + + Matrix3x4 operations + +======================================================================== +*/ +void Matrix3x4_VectorTransform( const matrix3x4 in, const float v[3], float out[3] ) +{ + out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2] + in[0][3]; + out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2] + in[1][3]; + out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2] + in[2][3]; +} + +void Matrix3x4_VectorITransform( const matrix3x4 in, const float v[3], float out[3] ) +{ + vec3_t dir; + + dir[0] = v[0] - in[0][3]; + dir[1] = v[1] - in[1][3]; + dir[2] = v[2] - in[2][3]; + + out[0] = dir[0] * in[0][0] + dir[1] * in[1][0] + dir[2] * in[2][0]; + out[1] = dir[0] * in[0][1] + dir[1] * in[1][1] + dir[2] * in[2][1]; + out[2] = dir[0] * in[0][2] + dir[1] * in[1][2] + dir[2] * in[2][2]; +} + +void Matrix3x4_VectorRotate( const matrix3x4 in, const float v[3], float out[3] ) +{ + out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2]; + out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2]; + out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2]; +} + +void Matrix3x4_VectorIRotate( const matrix3x4 in, const float v[3], float out[3] ) +{ + out[0] = v[0] * in[0][0] + v[1] * in[1][0] + v[2] * in[2][0]; + out[1] = v[0] * in[0][1] + v[1] * in[1][1] + v[2] * in[2][1]; + out[2] = v[0] * in[0][2] + v[1] * in[1][2] + v[2] * in[2][2]; +} + +void Matrix3x4_ConcatTransforms( matrix3x4 out, const matrix3x4 in1, const matrix3x4 in2 ) +{ + 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]; +} + +void Matrix3x4_SetOrigin( matrix3x4 out, float x, float y, float z ) +{ + out[0][3] = x; + out[1][3] = y; + out[2][3] = z; +} + +void Matrix3x4_OriginFromMatrix( const matrix3x4 in, float *out ) +{ + out[0] = in[0][3]; + out[1] = in[1][3]; + out[2] = in[2][3]; +} + +void Matrix3x4_FromOriginQuat( matrix3x4 out, const vec4_t quaternion, const vec3_t origin ) +{ + out[0][0] = 1.0f - 2.0f * quaternion[1] * quaternion[1] - 2.0f * quaternion[2] * quaternion[2]; + out[1][0] = 2.0f * quaternion[0] * quaternion[1] + 2.0f * quaternion[3] * quaternion[2]; + out[2][0] = 2.0f * quaternion[0] * quaternion[2] - 2.0f * quaternion[3] * quaternion[1]; + + out[0][1] = 2.0f * quaternion[0] * quaternion[1] - 2.0f * quaternion[3] * quaternion[2]; + out[1][1] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[2] * quaternion[2]; + out[2][1] = 2.0f * quaternion[1] * quaternion[2] + 2.0f * quaternion[3] * quaternion[0]; + + out[0][2] = 2.0f * quaternion[0] * quaternion[2] + 2.0f * quaternion[3] * quaternion[1]; + out[1][2] = 2.0f * quaternion[1] * quaternion[2] - 2.0f * quaternion[3] * quaternion[0]; + out[2][2] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[1] * quaternion[1]; + + out[0][3] = origin[0]; + out[1][3] = origin[1]; + out[2][3] = origin[2]; +} + +void Matrix3x4_CreateFromEntity( matrix3x4 out, const vec3_t angles, const vec3_t origin, float scale ) +{ + float angle, sr, sp, sy, cr, cp, cy; + + if( angles[ROLL] ) + { + angle = angles[YAW] * (M_PI*2 / 360); + SinCos( angle, &sy, &cy ); + angle = angles[PITCH] * (M_PI*2 / 360); + SinCos( angle, &sp, &cp ); + angle = angles[ROLL] * (M_PI*2 / 360); + SinCos( angle, &sr, &cr ); + + out[0][0] = (cp*cy) * scale; + out[0][1] = (sr*sp*cy+cr*-sy) * scale; + out[0][2] = (cr*sp*cy+-sr*-sy) * scale; + out[0][3] = origin[0]; + out[1][0] = (cp*sy) * scale; + out[1][1] = (sr*sp*sy+cr*cy) * scale; + out[1][2] = (cr*sp*sy+-sr*cy) * scale; + out[1][3] = origin[1]; + out[2][0] = (-sp) * scale; + out[2][1] = (sr*cp) * scale; + out[2][2] = (cr*cp) * scale; + out[2][3] = origin[2]; + } + else if( angles[PITCH] ) + { + angle = angles[YAW] * (M_PI*2 / 360); + SinCos( angle, &sy, &cy ); + angle = angles[PITCH] * (M_PI*2 / 360); + SinCos( angle, &sp, &cp ); + + out[0][0] = (cp*cy) * scale; + out[0][1] = (-sy) * scale; + out[0][2] = (sp*cy) * scale; + out[0][3] = origin[0]; + out[1][0] = (cp*sy) * scale; + out[1][1] = (cy) * scale; + out[1][2] = (sp*sy) * scale; + out[1][3] = origin[1]; + out[2][0] = (-sp) * scale; + out[2][1] = 0; + out[2][2] = (cp) * scale; + out[2][3] = origin[2]; + } + else if( angles[YAW] ) + { + angle = angles[YAW] * (M_PI*2 / 360); + SinCos( angle, &sy, &cy ); + + out[0][0] = (cy) * scale; + out[0][1] = (-sy) * scale; + out[0][2] = 0; + out[0][3] = origin[0]; + out[1][0] = (sy) * scale; + out[1][1] = (cy) * scale; + out[1][2] = 0; + out[1][3] = origin[1]; + out[2][0] = 0; + out[2][1] = 0; + out[2][2] = scale; + out[2][3] = origin[2]; + } + else + { + out[0][0] = scale; + out[0][1] = 0; + out[0][2] = 0; + out[0][3] = origin[0]; + out[1][0] = 0; + out[1][1] = scale; + out[1][2] = 0; + out[1][3] = origin[1]; + out[2][0] = 0; + out[2][1] = 0; + out[2][2] = scale; + out[2][3] = origin[2]; + } +} + +void Matrix3x4_TransformPositivePlane( const matrix3x4 in, const vec3_t normal, float d, vec3_t out, float *dist ) +{ + #ifdef PSP_VFPU + float scale = vfpu_sqrtf( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] ); + #else + float scale = sqrtf( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] ); + #endif + float iscale = 1.0f / scale; + + out[0] = (normal[0] * in[0][0] + normal[1] * in[0][1] + normal[2] * in[0][2]) * iscale; + out[1] = (normal[0] * in[1][0] + normal[1] * in[1][1] + normal[2] * in[1][2]) * iscale; + out[2] = (normal[0] * in[2][0] + normal[1] * in[2][1] + normal[2] * in[2][2]) * iscale; + *dist = d * scale + ( out[0] * in[0][3] + out[1] * in[1][3] + out[2] * in[2][3] ); +} + +void Matrix3x4_Invert_Simple( matrix3x4 out, const matrix3x4 in1 ) +{ + // we only support uniform scaling, so assume the first row is enough + // (note the lack of sqrt here, because we're trying to undo the scaling, + // this means multiplying by the inverse scale twice - squaring it, which + // makes the sqrt a waste of time) + float scale = 1.0 / (in1[0][0] * in1[0][0] + in1[0][1] * in1[0][1] + in1[0][2] * in1[0][2]); + + // invert the rotation by transposing and multiplying by the squared + // recipricol of the input matrix scale as described above + out[0][0] = in1[0][0] * scale; + out[0][1] = in1[1][0] * scale; + out[0][2] = in1[2][0] * scale; + out[1][0] = in1[0][1] * scale; + out[1][1] = in1[1][1] * scale; + out[1][2] = in1[2][1] * scale; + out[2][0] = in1[0][2] * scale; + out[2][1] = in1[1][2] * scale; + out[2][2] = in1[2][2] * scale; + + // invert the translate + out[0][3] = -(in1[0][3] * out[0][0] + in1[1][3] * out[0][1] + in1[2][3] * out[0][2]); + out[1][3] = -(in1[0][3] * out[1][0] + in1[1][3] * out[1][1] + in1[2][3] * out[1][2]); + out[2][3] = -(in1[0][3] * out[2][0] + in1[1][3] * out[2][1] + in1[2][3] * out[2][2]); +} + +const matrix4x4 matrix4x4_identity = +{ +{ 1, 0, 0, 0 }, // PITCH +{ 0, 1, 0, 0 }, // YAW +{ 0, 0, 1, 0 }, // ROLL +{ 0, 0, 0, 1 }, // ORIGIN +}; + +/* +======================================================================== + + Matrix4x4 operations + +======================================================================== +*/ +void Matrix4x4_VectorTransform( const matrix4x4 in, const float v[3], float out[3] ) +{ + out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2] + in[0][3]; + out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2] + in[1][3]; + out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2] + in[2][3]; +} + +void Matrix4x4_VectorITransform( const matrix4x4 in, const float v[3], float out[3] ) +{ + vec3_t dir; + + dir[0] = v[0] - in[0][3]; + dir[1] = v[1] - in[1][3]; + dir[2] = v[2] - in[2][3]; + + out[0] = dir[0] * in[0][0] + dir[1] * in[1][0] + dir[2] * in[2][0]; + out[1] = dir[0] * in[0][1] + dir[1] * in[1][1] + dir[2] * in[2][1]; + out[2] = dir[0] * in[0][2] + dir[1] * in[1][2] + dir[2] * in[2][2]; +} + +void Matrix4x4_VectorRotate( const matrix4x4 in, const float v[3], float out[3] ) +{ + out[0] = v[0] * in[0][0] + v[1] * in[0][1] + v[2] * in[0][2]; + out[1] = v[0] * in[1][0] + v[1] * in[1][1] + v[2] * in[1][2]; + out[2] = v[0] * in[2][0] + v[1] * in[2][1] + v[2] * in[2][2]; +} + +void Matrix4x4_VectorIRotate( const matrix4x4 in, const float v[3], float out[3] ) +{ + out[0] = v[0] * in[0][0] + v[1] * in[1][0] + v[2] * in[2][0]; + out[1] = v[0] * in[0][1] + v[1] * in[1][1] + v[2] * in[2][1]; + out[2] = v[0] * in[0][2] + v[1] * in[1][2] + v[2] * in[2][2]; +} + +void Matrix4x4_ConcatTransforms( matrix4x4 out, const matrix4x4 in1, const matrix4x4 in2 ) +{ + 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]; +} + +void Matrix4x4_SetOrigin( matrix4x4 out, float x, float y, float z ) +{ + out[0][3] = x; + out[1][3] = y; + out[2][3] = z; +} + +void Matrix4x4_OriginFromMatrix( const matrix4x4 in, float *out ) +{ + out[0] = in[0][3]; + out[1] = in[1][3]; + out[2] = in[2][3]; +} + +void Matrix4x4_FromOriginQuat( matrix4x4 out, const vec4_t quaternion, const vec3_t origin ) +{ + out[0][0] = 1.0f - 2.0f * quaternion[1] * quaternion[1] - 2.0f * quaternion[2] * quaternion[2]; + out[1][0] = 2.0f * quaternion[0] * quaternion[1] + 2.0f * quaternion[3] * quaternion[2]; + out[2][0] = 2.0f * quaternion[0] * quaternion[2] - 2.0f * quaternion[3] * quaternion[1]; + out[0][3] = origin[0]; + out[0][1] = 2.0f * quaternion[0] * quaternion[1] - 2.0f * quaternion[3] * quaternion[2]; + out[1][1] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[2] * quaternion[2]; + out[2][1] = 2.0f * quaternion[1] * quaternion[2] + 2.0f * quaternion[3] * quaternion[0]; + out[1][3] = origin[1]; + out[0][2] = 2.0f * quaternion[0] * quaternion[2] + 2.0f * quaternion[3] * quaternion[1]; + out[1][2] = 2.0f * quaternion[1] * quaternion[2] - 2.0f * quaternion[3] * quaternion[0]; + out[2][2] = 1.0f - 2.0f * quaternion[0] * quaternion[0] - 2.0f * quaternion[1] * quaternion[1]; + out[2][3] = origin[2]; + out[3][0] = 0; + out[3][1] = 0; + out[3][2] = 0; + out[3][3] = 1; +} + +void Matrix4x4_CreateFromEntity( matrix4x4 out, const vec3_t angles, const vec3_t origin, float scale ) +{ + float angle, sr, sp, sy, cr, cp, cy; + + if( angles[ROLL] ) + { + angle = angles[YAW] * (M_PI*2 / 360); + SinCos( angle, &sy, &cy ); + angle = angles[PITCH] * (M_PI*2 / 360); + SinCos( angle, &sp, &cp ); + angle = angles[ROLL] * (M_PI*2 / 360); + SinCos( angle, &sr, &cr ); + + out[0][0] = (cp*cy) * scale; + out[0][1] = (sr*sp*cy+cr*-sy) * scale; + out[0][2] = (cr*sp*cy+-sr*-sy) * scale; + out[0][3] = origin[0]; + out[1][0] = (cp*sy) * scale; + out[1][1] = (sr*sp*sy+cr*cy) * scale; + out[1][2] = (cr*sp*sy+-sr*cy) * scale; + out[1][3] = origin[1]; + out[2][0] = (-sp) * scale; + out[2][1] = (sr*cp) * scale; + out[2][2] = (cr*cp) * scale; + out[2][3] = origin[2]; + out[3][0] = 0; + out[3][1] = 0; + out[3][2] = 0; + out[3][3] = 1; + } + else if( angles[PITCH] ) + { + angle = angles[YAW] * (M_PI*2 / 360); + SinCos( angle, &sy, &cy ); + angle = angles[PITCH] * (M_PI*2 / 360); + SinCos( angle, &sp, &cp ); + + out[0][0] = (cp*cy) * scale; + out[0][1] = (-sy) * scale; + out[0][2] = (sp*cy) * scale; + out[0][3] = origin[0]; + out[1][0] = (cp*sy) * scale; + out[1][1] = (cy) * scale; + out[1][2] = (sp*sy) * scale; + out[1][3] = origin[1]; + out[2][0] = (-sp) * scale; + out[2][1] = 0; + out[2][2] = (cp) * scale; + out[2][3] = origin[2]; + out[3][0] = 0; + out[3][1] = 0; + out[3][2] = 0; + out[3][3] = 1; + } + else if( angles[YAW] ) + { + angle = angles[YAW] * (M_PI*2 / 360); + SinCos( angle, &sy, &cy ); + + out[0][0] = (cy) * scale; + out[0][1] = (-sy) * scale; + out[0][2] = 0; + out[0][3] = origin[0]; + out[1][0] = (sy) * scale; + out[1][1] = (cy) * scale; + out[1][2] = 0; + out[1][3] = origin[1]; + out[2][0] = 0; + out[2][1] = 0; + out[2][2] = scale; + out[2][3] = origin[2]; + out[3][0] = 0; + out[3][1] = 0; + out[3][2] = 0; + out[3][3] = 1; + } + else + { + out[0][0] = scale; + out[0][1] = 0; + out[0][2] = 0; + out[0][3] = origin[0]; + out[1][0] = 0; + out[1][1] = scale; + out[1][2] = 0; + out[1][3] = origin[1]; + out[2][0] = 0; + out[2][1] = 0; + out[2][2] = scale; + out[2][3] = origin[2]; + out[3][0] = 0; + out[3][1] = 0; + out[3][2] = 0; + out[3][3] = 1; + } +} + +void Matrix4x4_ConvertToEntity( const matrix4x4 in, vec3_t angles, vec3_t origin ) +{ + #ifdef PSP_VFPU + float xyDist = vfpu_sqrtf( in[0][0] * in[0][0] + in[1][0] * in[1][0] ); + #else + float xyDist = sqrtf( in[0][0] * in[0][0] + in[1][0] * in[1][0] ); + #endif + + // enough here to get angles? + if( xyDist > 0.001f ) + { + #ifdef PSP_VFPU + angles[0] = RAD2DEG( vfpu_atan2f( -in[2][0], xyDist ) ); + angles[1] = RAD2DEG( vfpu_atan2f( in[1][0], in[0][0] ) ); + angles[2] = RAD2DEG( vfpu_atan2f( in[2][1], in[2][2] ) ); + #else + angles[0] = RAD2DEG( atan2( -in[2][0], xyDist ) ); + angles[1] = RAD2DEG( atan2( in[1][0], in[0][0] ) ); + angles[2] = RAD2DEG( atan2( in[2][1], in[2][2] ) ); + #endif + } + else // forward is mostly Z, gimbal lock + { + #ifdef PSP_VFPU + angles[0] = RAD2DEG( vfpu_atan2f( -in[2][0], xyDist ) ); + angles[1] = RAD2DEG( vfpu_atan2f( -in[0][1], in[1][1] ) ); + #else + angles[0] = RAD2DEG( atan2( -in[2][0], xyDist ) ); + angles[1] = RAD2DEG( atan2( -in[0][1], in[1][1] ) ); + #endif + angles[2] = 0; + } + + origin[0] = in[0][3]; + origin[1] = in[1][3]; + origin[2] = in[2][3]; +} + +void Matrix4x4_TransformPositivePlane( const matrix4x4 in, const vec3_t normal, float d, vec3_t out, float *dist ) +{ + #ifdef PSP_VFPU + float scale = vfpu_sqrtf( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] ); + #else + float scale = sqrtf( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] ); + #endif + float iscale = 1.0f / scale; + + out[0] = (normal[0] * in[0][0] + normal[1] * in[0][1] + normal[2] * in[0][2]) * iscale; + out[1] = (normal[0] * in[1][0] + normal[1] * in[1][1] + normal[2] * in[1][2]) * iscale; + out[2] = (normal[0] * in[2][0] + normal[1] * in[2][1] + normal[2] * in[2][2]) * iscale; + *dist = d * scale + ( out[0] * in[0][3] + out[1] * in[1][3] + out[2] * in[2][3] ); +} + +void Matrix4x4_TransformStandardPlane( const matrix4x4 in, const vec3_t normal, float d, vec3_t out, float *dist ) +{ + #ifdef PSP_VFPU + float scale = vfpu_sqrtf( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] ); + #else + float scale = sqrtf( in[0][0] * in[0][0] + in[0][1] * in[0][1] + in[0][2] * in[0][2] ); + #endif + float iscale = 1.0f / scale; + + out[0] = (normal[0] * in[0][0] + normal[1] * in[0][1] + normal[2] * in[0][2]) * iscale; + out[1] = (normal[0] * in[1][0] + normal[1] * in[1][1] + normal[2] * in[1][2]) * iscale; + out[2] = (normal[0] * in[2][0] + normal[1] * in[2][1] + normal[2] * in[2][2]) * iscale; + *dist = d * scale - ( out[0] * in[0][3] + out[1] * in[1][3] + out[2] * in[2][3] ); +} + +void Matrix4x4_Invert_Simple( matrix4x4 out, const matrix4x4 in1 ) +{ + // we only support uniform scaling, so assume the first row is enough + // (note the lack of sqrt here, because we're trying to undo the scaling, + // this means multiplying by the inverse scale twice - squaring it, which + // makes the sqrt a waste of time) + float scale = 1.0f / (in1[0][0] * in1[0][0] + in1[0][1] * in1[0][1] + in1[0][2] * in1[0][2]); + + // invert the rotation by transposing and multiplying by the squared + // recipricol of the input matrix scale as described above + out[0][0] = in1[0][0] * scale; + out[0][1] = in1[1][0] * scale; + out[0][2] = in1[2][0] * scale; + out[1][0] = in1[0][1] * scale; + out[1][1] = in1[1][1] * scale; + out[1][2] = in1[2][1] * scale; + out[2][0] = in1[0][2] * scale; + out[2][1] = in1[1][2] * scale; + out[2][2] = in1[2][2] * scale; + + // invert the translate + out[0][3] = -(in1[0][3] * out[0][0] + in1[1][3] * out[0][1] + in1[2][3] * out[0][2]); + out[1][3] = -(in1[0][3] * out[1][0] + in1[1][3] * out[1][1] + in1[2][3] * out[1][2]); + out[2][3] = -(in1[0][3] * out[2][0] + in1[1][3] * out[2][1] + in1[2][3] * out[2][2]); + + // don't know if there's anything worth doing here + out[3][0] = 0; + out[3][1] = 0; + out[3][2] = 0; + out[3][3] = 1; +} diff --git a/source/menu.c b/source/menu.c new file mode 100644 index 0000000..c19fb38 --- /dev/null +++ b/source/menu.c @@ -0,0 +1,5697 @@ +/* +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 +#include +#include +//#include +#include +#include +#include "net_dgrm.h" +#include "cl_slist.h" +#include "crypter.h" + + +extern cvar_t accesspoint; +extern cvar_t r_wateralpha; +extern cvar_t r_vsync; +extern cvar_t in_disable_analog; +extern cvar_t in_analog_strafe; +extern cvar_t in_x_axis_adjust; +extern cvar_t in_y_axis_adjust; +extern cvar_t crosshair; +extern cvar_t r_dithering; +extern cvar_t r_retro; +extern cvar_t waypoint_mode; + +extern qpic_t *b_square; +extern qpic_t *b_triangle; +extern qpic_t *b_cross; +extern qpic_t *b_circle; +extern qpic_t *b_lt; +extern qpic_t *b_rt; +extern qpic_t *b_start; +extern qpic_t *b_left; +extern qpic_t *b_right; + +extern qboolean bmg_type_changed; + +extern int loadingScreen; +extern int ShowBlslogo; + +// Backgrounds +qpic_t *menu_bk; + +// Map screens +qpic_t *menu_ndu; +qpic_t *menu_wh; +//qpic_t *menu_kn; +qpic_t *menu_ch; +//qpic_t *menu_wn; +qpic_t *menu_custom; +qpic_t *menu_cuthum; + + +typedef struct +{ + int occupied; + int map_allow_game_settings; + int map_use_thumbnail; + char* map_name; + char* map_name_pretty; + char* map_desc_1; + char* map_desc_2; + char* map_desc_3; + char* map_desc_4; + char* map_desc_5; + char* map_desc_6; + char* map_desc_7; + char* map_desc_8; + char* map_author; + char* map_thumbnail_path; +} usermap_t; + +SceIoStat custom_thumbnail_size; +usermap_t custom_maps[50]; + +enum +{ +m_none, +m_start, +m_main, +m_paused_menu, +m_map, +m_singleplayer, +m_multiplayer, +m_achievement, +m_setup, +m_net, +m_options, +m_audio, +m_gameplay, +m_video, +m_keys, +m_credits, +m_quit, +m_restart, +m_exit, +m_serialconfig, +m_modemconfig, +m_lanconfig, +m_gameoptions, +m_search, +m_slist, +m_sedit, +m_osk +} m_state; + +void M_Start_Menu_f (void); +void M_Paused_Menu_f (void); + void M_Menu_Restart_f (void); + void M_Menu_Exit_f (void); +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_ServerList_f (void); + void M_Menu_Achievement_f (void); + void M_Menu_Options_f (void); + void M_Menu_Keys_f (void); + void M_Menu_Audio_f (void); + void M_Menu_Gameplay_f (void); + void M_Menu_Screen_f (void); + void M_Menu_Credits_f (void); + void M_Menu_Quit_f (void); +void M_Menu_SerialConfig_f (void); + void M_Menu_ModemConfig_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_Setup_Draw (void); + void M_Net_Draw (void); + void M_Options_Draw (void); + void M_Keys_Draw (void); + void M_Credits_Draw (void); + void M_Quit_Draw (void); +void M_SerialConfig_Draw (void); + void M_ModemConfig_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_MultiPlayer_Key (int key); + void M_Setup_Key (int key); + void M_Net_Key (int key); +void M_Options_Key (int key); + void M_Keys_Key (int key); +void M_Credits_Key (int key); +void M_Quit_Key (int key); +void M_SerialConfig_Key (int key); +void M_ModemConfig_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); + +void Con_SetOSKActive(qboolean active); +void M_Menu_OSK_f (char *input, char *output, int outlen); + + +qboolean m_entersound; // play after drawing a frame, so caching + // won't disrupt the sound +qboolean m_recursiveDraw; + +int m_return_state; +qboolean 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); + +/* +================ +M_DrawCharacter + +Draws one solid graphics character +================ +*/ +void M_DrawCharacter (int cx, int line, int num) +{ + Draw_Character ( cx, line, num); +} +void M_DrawCharacter2 (int cx, int line, int num) +{ + Draw_Character ( cx + ((vid.width - 320)>>1), line, num); +} + +void M_PrintOld (int cx, int cy, char *str) +{ + while (*str) + { + M_DrawCharacter2 (cx, cy, (*str)+128); + str++; + cx += 8; + } +} + +void M_Print (int cx, int cy, char *str) +{ + char str2[80]; + + strcpy (str2,"&cD32");//831 + strcat (str2, str); + Draw_ColoredString (cx, cy, str2, 255, 255, 255, 255, 1); +} + +int M_ColorForMap (int m) +{ + return m < 128 ? m + 8 : m + 8; +} + +void M_PrintWhiteOld (int cx, int cy, char *str) +{ + while (*str) + { + M_DrawCharacter2 (cx, cy, *str); + str++; + cx += 8; + } +} +void M_PrintWhite (int cx, int cy, char *str) +{ + while (*str) + { + M_DrawCharacter (cx, cy, *str); + str++; + cx += 8; + } +} + +void M_DrawTransPic (int x, int y, qpic_t *pic) +{ + Draw_TransPic (x + ((vid.width - 320)>>1), y, pic); +} + +void M_DrawPic (int x, int y, qpic_t *pic) +{ + Draw_Pic (x + ((vid.width - 320)>>1), 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 + ((vid.width - 320)>>1), 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); +} + +void M_DrawCheckbox (int x, int y, int on) +{ + if (on) + Draw_String (x, y, "Enabled"); + else + Draw_String (x, y, "Disabled"); +} + +//============================================================================= + +/* +================ +M_ToggleMenu_f +================ +*/ +void M_ToggleMenu_f (void) +{ + m_entersound = true; + + if (key_dest == key_menu || key_dest == key_menu_pause) + { + if (m_state != m_main && m_state != m_paused_menu) + { + M_Menu_Main_f (); + return; + } + key_dest = key_game; + m_state = m_none; + return; + } + if (key_dest == key_console) + { + Con_ToggleConsole_f (); + } + else if (sv.active && (svs.maxclients > 1 || key_dest == key_game)) + { + M_Paused_Menu_f(); + } + else + { + M_Menu_Main_f (); + } +} + +//============================================================================= + +int M_Start_Cusor; +#define Max_Start_Iteams 5 + +void M_Start_Menu_f () +{ + key_dest = key_menu; + m_state = m_start; + m_entersound = true; + //loadingScreen = 0; + Load_Achivements(); +} + +static void M_Start_Menu_Draw () +{ + // Background + menu_bk = Draw_CacheImg("gfx/menu/menu_background"); + Draw_Pic (0, 0, menu_bk); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102)); + + Draw_ColoredString((vid.width)/2 - 44, (vid.height - 64), "Press Start", 255, 0, 0, 255, 1); +} + +void M_Start_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + S_LocalSound ("sounds/menu/enter.wav"); + Cbuf_AddText("togglemenu\n"); + break; + } +} +//============================================================================= + +int M_Paused_Cusor; +#define Max_Paused_Iteams 5 + +void M_Paused_Menu_f () +{ + key_dest = key_menu_pause; + m_state = m_paused_menu; + m_entersound = true; + loadingScreen = 0; + M_Paused_Cusor = 0; +} + +static void M_Paused_Menu_Draw () +{ + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102)); + + // Header + Draw_ColoredString(10, 10, "PAUSED", 255, 255, 255, 255, 2); + + if ((M_Paused_Cusor == 0)) + Draw_ColoredString(10, 135, "Resume", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 135, "Resume", 255, 255, 255, 255, 1); + + if ((M_Paused_Cusor == 1)) + Draw_ColoredString(10, 145, "Restart", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 145, "Restart", 255, 255, 255, 255, 1); + + if ((M_Paused_Cusor == 2)) + Draw_ColoredString(10, 155, "Settings", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 155, "Settings", 255, 255, 255, 255, 1); + + if (waypoint_mode.value) { + if ((M_Paused_Cusor == 3)) + Draw_ColoredString(10, 165, "Save Waypoints", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 165, "Save Waypoints", 255, 255, 255, 255, 1); + } else { + if ((M_Paused_Cusor == 3)) + Draw_ColoredString(10, 165, "Achievements", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 165, "Achievements", 255, 255, 255, 255, 1); + } + + if ((M_Paused_Cusor == 4)) + Draw_ColoredString(10, 175, "Main Menu", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 175, "Main Menu", 255, 255, 255, 255, 1); +} + +static void M_Paused_Menu_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + S_LocalSound ("sounds/menu/enter.wav"); + Cbuf_AddText("togglemenu\n"); + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (++M_Paused_Cusor >= Max_Paused_Iteams) + M_Paused_Cusor = 0; + break; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (--M_Paused_Cusor < 0) + M_Paused_Cusor = Max_Paused_Iteams - 1; + break; + + case K_ENTER: + m_entersound = true; + + switch (M_Paused_Cusor) + { + case 0: + key_dest = key_game; + m_state = m_none; + break; + case 1: + M_Menu_Restart_f(); + break; + case 2: + M_Menu_Options_f(); + key_dest = key_menu_pause; + break; + case 3: + if (waypoint_mode.value) + Cbuf_AddText("impulse 101\n"); + else + M_Menu_Achievement_f(); + key_dest = key_menu_pause; + break; + case 4: + M_Menu_Exit_f(); + break; + } + } +} + +//============================================================================= +/* MAIN MENU */ + +int m_main_cursor; +#define MAIN_ITEMS 5 + + +void M_Menu_Main_f (void) +{ + key_dest = key_menu; + m_state = m_main; + m_entersound = true; +} + + +void M_Main_Draw (void) +{ + // Background + Draw_Pic (0, 0, menu_bk); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102)); + + // Version String + Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1); + + // Header + Draw_ColoredString(10, 10, "MAIN MENU", 255, 255, 255, 255, 2); + + // Solo + if (m_main_cursor == 0) + Draw_ColoredString(10, 45, "Solo", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 45, "Solo", 255, 255, 255, 255, 1); + + + // Co-Op (Unfinished, so non-selectable) + Draw_ColoredString(10, 55, "Co-Op (Coming Soon!)", 128, 128, 128, 255, 1); + + // Divider + Draw_FillByColor(10, 68, 160, 2, GU_RGBA(130, 130, 130, 255)); + + if (m_main_cursor == 1) + Draw_ColoredString(10, 75, "Settings", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 75, "Settings", 255, 255, 255, 255, 1); + + if (m_main_cursor == 2) + Draw_ColoredString(10, 85, "Achievements", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 85, "Achievements", 255, 255, 255, 255, 1); + + // Divider + Draw_FillByColor(10, 98, 160, 2, GU_RGBA(130, 130, 130, 255)); + + if (m_main_cursor == 3) + Draw_ColoredString(10, 105, "Credits", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 105, "Credits", 255, 255, 255, 255, 1); + + // Divider + Draw_FillByColor(10, 118, 160, 2, GU_RGBA(130, 130, 130, 255)); + + if (m_main_cursor == 4) + Draw_ColoredString(10, 125, "Exit", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 125, "Exit", 255, 255, 255, 255, 1); + + // Descriptions + switch(m_main_cursor) { + case 0: // Solo + Draw_ColoredString(10, 230, "Take on the Hordes by yourself.", 255, 255, 255, 255, 1); + break; + case 1: // Settings + Draw_ColoredString(10, 230, "Adjust your Settings to Optimize your Experience.", 255, 255, 255, 255, 1); + break; + case 2: // Achievements + Draw_ColoredString(10, 230, "View locked or unlocked Achievements.", 255, 255, 255, 255, 1); + break; + case 3: // Credits + Draw_ColoredString(10, 230, "See who made NZ:P possible.", 255, 255, 255, 255, 1); + break; + case 4: // Exit + Draw_ColoredString(10, 230, "Return to XMB.", 255, 255, 255, 255, 1); + break; + } +} + + +void M_Main_Key (int key) +{ + switch (key) + { + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (++m_main_cursor >= MAIN_ITEMS) + m_main_cursor = 0; + break; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (--m_main_cursor < 0) + m_main_cursor = MAIN_ITEMS - 1; + break; + + case K_ENTER: + m_entersound = true; + + switch (m_main_cursor) + { + case 0: + M_Menu_SinglePlayer_f (); + break; + + /*case 1: + M_Menu_MultiPlayer_f (); + break;*/ + + case 1: + M_Menu_Options_f (); + break; + + case 2: + M_Menu_Achievement_f (); + break; + + case 3: + M_Menu_Credits_f (); + break; + + case 4: + M_Menu_Quit_f (); + break; + } + break; + } +} + + + + +//============================================================================= +/* RESTART MENU */ + +qboolean wasInMenus; + + +char *restartMessage [] = +{ + + " Are you sure you want", + " to restart this game? ", //msg:0 + " ", + " X :Yes O : No " +}; + + +void M_Menu_Restart_f (void) +{ + wasInMenus = (key_dest == key_menu_pause); + key_dest = key_menu_pause; + m_state = m_restart; + m_entersound = true; +} + + +void M_Restart_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + case 'n': + case 'N': + m_state = m_paused_menu; + m_entersound = true; + break; + + case 'Y': + case 'y': + case K_ENTER: + key_dest = key_game; + m_state = m_none; + // Cbuf_AddText ("restart\n"); // nai -- old, now do soft reset + PR_ExecuteProgram (pr_global_struct->Soft_Restart); + break; + + default: + break; + } + +} + + +void M_Restart_Draw (void) +{ + m_state = m_paused_menu; + m_recursiveDraw = true; + M_Draw (); + m_state = m_restart; + + M_DrawTextBox (56, 76, 24, 4); + M_PrintOld (64, 84, restartMessage[0]); + M_PrintOld (64, 92, restartMessage[1]); + M_PrintOld (64, 100, restartMessage[2]); + M_PrintOld (64, 108, restartMessage[3]); +} + + + + +//============================================================================= +/* EXIT MENU */ + + +char *exitMessage [] = +{ + + " Are you sure you want ", + "to quit to the Main Menu?", //msg:0 + " ", + " X :Yes O : No " +}; + + +void M_Menu_Exit_f (void) +{ + wasInMenus = (key_dest == key_menu_pause); + key_dest = key_menu_pause; + m_state = m_exit; + m_entersound = true; +} + + +void M_Exit_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + case 'n': + case 'N': + m_state = m_paused_menu; + m_entersound = true; + break; + + case 'Y': + case 'y': + case K_ENTER: + Cbuf_AddText("disconnect\n"); + M_Menu_Main_f(); + break; + + default: + break; + } + +} + + +void M_Exit_Draw (void) +{ + m_state = m_paused_menu; + m_recursiveDraw = true; + M_Draw (); + m_state = m_exit; + + M_DrawTextBox (56, 76, 24, 4); + M_PrintOld (64, 84, exitMessage[0]); + M_PrintOld (64, 92, exitMessage[1]); + M_PrintOld (64, 100, exitMessage[2]); + M_PrintOld (64, 108, exitMessage[3]); +} + +//============================================================================= +/* SINGLE PLAYER MENU */ + +int m_map_cursor; +int MAP_ITEMS; +int user_maps_num = 0; +int current_custom_map_page; +int custom_map_pages; +int multiplier; +char user_levels[256][MAX_QPATH]; + +void M_Menu_Map_f (void) +{ + key_dest = key_menu; + m_state = m_map; + m_entersound = true; + MAP_ITEMS = 13; + current_custom_map_page = 1; +} + + +void M_Map_Draw (void) +{ + // Background + Draw_Pic(0, 0, menu_bk); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102)); + + // Version String + Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1); + + // Header + Draw_ColoredString(10, 10, "CUSTOM MAPS", 255, 255, 255, 255, 2); + + int line_increment; + + line_increment = 0; + + if (current_custom_map_page > 1) + multiplier = (current_custom_map_page - 1) * 15; + else + multiplier = 0; + + for (int i = 0; i < 15; i++) { + if (custom_maps[i + multiplier].occupied == false) + continue; + + if (m_map_cursor == i) { + + if (custom_maps[i + multiplier].map_use_thumbnail == 1) { + menu_cuthum = Draw_CacheImg(custom_maps[i + multiplier].map_thumbnail_path); + Draw_Pic(256, 45, menu_cuthum); + } + + if (custom_maps[i + multiplier].map_name_pretty != 0) + Draw_ColoredString(10, 45 + (10 * i), custom_maps[i + multiplier].map_name_pretty, 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 45 + (10 * i), custom_maps[i + multiplier].map_name, 255, 0, 0, 255, 1); + + if (custom_maps[i + multiplier].map_desc_1 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_1, " ") != 0) { + Draw_ColoredString(215, 155, custom_maps[i + multiplier].map_desc_1, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_desc_2 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_2, " ") != 0) { + line_increment++; + Draw_ColoredString(215, 165, custom_maps[i + multiplier].map_desc_2, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_desc_3 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_3, " ") != 0) { + line_increment++; + Draw_ColoredString(215, 175, custom_maps[i + multiplier].map_desc_3, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_desc_4 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_4, " ") != 0) { + line_increment++; + Draw_ColoredString(215, 185, custom_maps[i + multiplier].map_desc_4, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_desc_5 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_5, " ") != 0) { + line_increment++; + Draw_ColoredString(215, 195, custom_maps[i + multiplier].map_desc_5, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_desc_6 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_6, " ") != 0) { + line_increment++; + Draw_ColoredString(215, 205, custom_maps[i + multiplier].map_desc_6, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_desc_7 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_7, " ") != 0) { + line_increment++; + Draw_ColoredString(215, 215, custom_maps[i + multiplier].map_desc_7, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_desc_8 != 0) { + if (strcmp(custom_maps[i + multiplier].map_desc_8, " ") != 0) { + line_increment++; + Draw_ColoredString(215, 225, custom_maps[i + multiplier].map_desc_8, 255, 255, 255, 255, 1); + } + } + if (custom_maps[i + multiplier].map_author != 0) { + if (strcmp(custom_maps[i + multiplier].map_author, " ") != 0) { + int y = 165 + (10 * line_increment); + Draw_ColoredString(215, y, custom_maps[i + multiplier].map_author, 255, 255, 0, 255, 1); + } + } + } else { + if (custom_maps[i + multiplier].map_name_pretty != 0) + Draw_ColoredString(10, 45 + (10 * i), custom_maps[i + multiplier].map_name_pretty, 255, 255, 255, 255, 1); + else + Draw_ColoredString(10, 45 + (10 * i), custom_maps[i + multiplier].map_name, 255, 255, 255, 255, 1); + } + } + + if (current_custom_map_page != custom_map_pages) { + if (m_map_cursor == 15) + Draw_ColoredString(10, 230, "Next Page", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 230, "Next Page", 255, 255, 255, 255, 1); + } else { + Draw_ColoredString(10, 230, "Next Page", 128, 128, 128, 255, 1); + } + + if (current_custom_map_page != 1) { + if (m_map_cursor == 16) + Draw_ColoredString(10, 240, "Previous Page", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 240, "Previous Page", 255, 255, 255, 255, 1); + } else { + Draw_ColoredString(10, 240, "Previous Page", 128, 128, 128, 255, 1); + } + + + + if (m_map_cursor == 17) + Draw_ColoredString(10, 250, "Back", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 250, "Back", 255, 255, 255, 255, 1); +} + + +void M_Map_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + M_Menu_SinglePlayer_f (); + break; + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + + m_map_cursor++; + + if (m_map_cursor < 14 && custom_maps[m_map_cursor + multiplier].occupied == false) { + m_map_cursor = 15; + } + + if (m_map_cursor == 15 && current_custom_map_page == custom_map_pages) + m_map_cursor = 16; + + if (m_map_cursor == 16 && current_custom_map_page == 1) + m_map_cursor = 17; + + if (m_map_cursor >= 18) + m_map_cursor = 0; + break; + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + + m_map_cursor--; + + if (m_map_cursor < 0) + m_map_cursor = 17; + + if (m_map_cursor == 16 && current_custom_map_page == 1) + m_map_cursor = 15; + + if (m_map_cursor == 15 && current_custom_map_page == custom_map_pages) + m_map_cursor = 14; + + if (m_map_cursor <= 14 && custom_maps[m_map_cursor + multiplier].occupied == false) { + for (int i = 14; i > -1; i--) { + if (custom_maps[i + multiplier].occupied == true) { + m_map_cursor = i; + break; + } + } + } + break; + case K_ENTER: + m_entersound = true; + if (m_map_cursor == 17) { + M_Menu_SinglePlayer_f (); + } else if (m_map_cursor == 16) { + current_custom_map_page--; + } else if (m_map_cursor == 15) { + current_custom_map_page++; + } else + { + key_dest = key_game; + if (sv.active) + Cbuf_AddText ("disconnect\n"); + Cbuf_AddText ("maxplayers 1\n"); + Cbuf_AddText (va("map %s\n", custom_maps[m_map_cursor + multiplier].map_name)); + //loadingScreen = 1; + } + break; + } +} + + +//============================================================================= +/* SINGLE PLAYER MENU */ + +int m_singleplayer_cursor; +#define SINGLEPLAYER_ITEMS 5 + + +void M_Menu_SinglePlayer_f (void) +{ + key_dest = key_menu; + m_state = m_singleplayer; + m_entersound = true; +} + + +void M_SinglePlayer_Draw (void) +{ + menu_ndu = Draw_CacheImg("gfx/menu/nacht_der_untoten"); + //menu_kn = Draw_CacheImg("gfx/menu/kino_der_toten"); + menu_wh = Draw_CacheImg("gfx/menu/warehouse"); + //menu_wn = Draw_CacheImg("gfx/menu/wahnsinn"); + menu_ch = Draw_CacheImg("gfx/menu/christmas_special"); + menu_custom = Draw_CacheImg("gfx/menu/custom"); + + // Background + Draw_Pic(0, 0, menu_bk); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102)); + + // Version String + Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1); + + // Header + Draw_ColoredString(10, 10, "SOLO", 255, 255, 255, 255, 2); + + // Nacht der Untoten + if (m_singleplayer_cursor == 0) + Draw_ColoredString(10, 45, "Nacht der Untoten", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 45, "Nacht der Untoten", 255, 255, 255, 255, 1); + + // Kino der Toten + Draw_ColoredString(10, 55, "Kino der Toten", 128, 128, 128, 255, 1); + + // Warehouse + if (m_singleplayer_cursor == 1) + Draw_ColoredString(10, 65, "Warehouse", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 65, "Warehouse", 255, 255, 255, 255, 1); + + // Wahnsinn + Draw_ColoredString(10, 75, "Wahnsinn", 128, 128, 128, 255, 1); + + // Christmas Special + if (m_singleplayer_cursor == 2) + Draw_ColoredString(10, 85, "Christmas Special", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 85, "Christmas Special", 255, 255, 255, 255, 1); + + // Divider + Draw_FillByColor(10, 98, 160, 2, GU_RGBA(130, 130, 130, 255)); + + // Custom Maps + if (m_singleplayer_cursor == 3) + Draw_ColoredString(10, 105, "Custom Maps", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 105, "Custom Maps", 255, 255, 255, 255, 1); + + // Back + if (m_singleplayer_cursor == 4) + Draw_ColoredString(10, 250, "Back", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 250, "Back", 255, 255, 255, 255, 1); + + // Map description & pic + switch(m_singleplayer_cursor) { + case 0: + Draw_Pic(256, 45, menu_ndu); + Draw_ColoredString(215, 155, "Lock and Load; Crashed Plane.", 255, 255, 255, 255, 1); + Draw_ColoredString(215, 165, "Divided. Thousands of Undead.", 255, 255, 255, 255, 1); + Draw_ColoredString(215, 175, "This is the Night of the Dead.", 255, 255, 255, 255, 1); + break; + case 1: + Draw_Pic(256, 45, menu_wh); + Draw_ColoredString(215, 155, "Old Warehouse full of Zombies!", 255, 255, 255, 255, 1); + Draw_ColoredString(215, 165, "Fight your way to the Power", 255, 255, 255, 255, 1); + Draw_ColoredString(215, 175, "Switch through the Hordes!", 255, 255, 255, 255, 1); + break; + case 2: + Draw_Pic(256, 45, menu_ch); + Draw_ColoredString(215, 155, "No Santa this year. Though we're", 255, 255, 255, 255, 1); + Draw_ColoredString(215, 165, "sure you will get presents from", 255, 255, 255, 255, 1); + Draw_ColoredString(215, 175, "the undead! Will you accept them?", 255, 255, 255, 255, 1); + break; + case 3: + Draw_Pic(256, 45, menu_custom); + Draw_ColoredString(215, 155, "Custom Maps made by Community", 255, 255, 255, 255, 1); + Draw_ColoredString(215, 165, "Members on the Fourm and on", 255, 255, 255, 255, 1); + Draw_ColoredString(215, 175, "Discord!", 255, 255, 255, 255, 1); + break; + } +} + + +void M_SinglePlayer_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + M_Menu_Main_f (); + break; + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (++m_singleplayer_cursor >= SINGLEPLAYER_ITEMS) + m_singleplayer_cursor = 0; + break; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (--m_singleplayer_cursor < 0) + m_singleplayer_cursor = SINGLEPLAYER_ITEMS - 1; + break; + + case K_ENTER: + m_entersound = true; + + switch (m_singleplayer_cursor) + { + case 0: + key_dest = key_game; + if (sv.active) + Cbuf_AddText ("disconnect\n"); + Cbuf_AddText ("maxplayers 1\n"); + Cbuf_AddText ("map ndu\n"); + loadingScreen = 1; + break; + case 1: + key_dest = key_game; + if (sv.active) + Cbuf_AddText ("disconnect\n"); + Cbuf_AddText ("maxplayers 1\n"); + Cbuf_AddText ("map warehouse\n"); + loadingScreen = 2; + break; + case 2: + key_dest = key_game; + if (sv.active) + Cbuf_AddText ("disconnect\n"); + Cbuf_AddText ("maxplayers 1\n"); + Cbuf_AddText ("map christmas_special\n"); + loadingScreen = 3; + break; + case 3: + M_Menu_Map_f (); + break; + case 4: + M_Menu_Main_f (); + break; + } + break; + } +} + +//============================================================================= +/* ACHIEVEMENT MENU */ + +int m_achievement_cursor; +int m_achievement_selected; +int m_achievement_scroll[2]; +int total_unlocked_achievements; +int total_locked_achievements; + + +achievement_list_t achievement_list[MAX_ACHIEVEMENTS]; +qpic_t *achievement_locked; + +void Achievement_Init (void) +{ + achievement_list[0].img = Draw_CachePic("gfx/achievement/ready"); + achievement_list[0].unlocked = 0; + achievement_list[0].progress = 0; + strcpy(achievement_list[0].name, "Ready.."); + strcpy(achievement_list[0].description, "Reach round 5"); + + achievement_list[1].img = Draw_CachePic("gfx/achievement/steady"); + achievement_list[1].unlocked = 0; + achievement_list[1].progress = 0; + strcpy(achievement_list[1].name, "Steady.."); + strcpy(achievement_list[1].description, "Reach round 10"); + + achievement_list[2].img = Draw_CachePic("gfx/achievement/go_hell_no"); + achievement_list[2].unlocked = 0; + achievement_list[2].progress = 0; + strcpy(achievement_list[2].name, "Go? Hell No..."); + strcpy(achievement_list[2].description, "Reach round 15"); + + achievement_list[3].img = Draw_CachePic("gfx/achievement/where_legs_go"); + achievement_list[3].unlocked = 0; + achievement_list[3].progress = 0; + strcpy(achievement_list[3].name, "Where Did Legs Go?"); + strcpy(achievement_list[3].description, "Turn a zombie into a crawler"); + + achievement_list[4].img = Draw_CachePic("gfx/achievement/the_f_bomb"); + achievement_list[4].unlocked = 0; + achievement_list[4].progress = 0; + strcpy(achievement_list[4].name, "The F Bomb"); + strcpy(achievement_list[4].description, "Use the Nuke power-up to kill a single Zombie"); + + /*achievement_list[3].img = Draw_CachePic("gfx/achievement/beast"); + achievement_list[3].unlocked = 0; + achievement_list[3].progress = 0; + strcpy(achievement_list[3].name, "Beast"); + strcpy(achievement_list[3].description, "Survive round after round 5 with knife and grenades only"); + + achievement_list[4].img = Draw_CachePic("gfx/achievement/survival"); + achievement_list[4].unlocked = 0; + achievement_list[4].progress = 0; + strcpy(achievement_list[4].name, "Survival Expert"); + strcpy(achievement_list[4].description, "Use pistol and knife only to reach round 10"); + + achievement_list[5].img = Draw_CachePic("gfx/achievement/boomstick"); + achievement_list[5].unlocked = 0; + achievement_list[5].progress = 0; + strcpy(achievement_list[5].name, "Boomstick"); + strcpy(achievement_list[5].description, "3 for 1 with shotgun blast"); + + achievement_list[6].img = Draw_CachePic("gfx/achievement/boom_headshots"); + achievement_list[6].unlocked = 0; + achievement_list[6].progress = 0; + strcpy(achievement_list[6].name, "Boom Headshots"); + strcpy(achievement_list[6].description, "Get 10 headshots"); + + achievement_list[7].img = Draw_CachePic("gfx/achievement/where_did"); + achievement_list[7].unlocked = 0; + achievement_list[7].progress = 0; + strcpy(achievement_list[7].name, "Where Did Legs Go?"); + strcpy(achievement_list[7].description, "Make a crawler zombie"); + + achievement_list[8].img = Draw_CachePic("gfx/achievement/keep_the_change"); + achievement_list[8].unlocked = 0; + achievement_list[8].progress = 0; + strcpy(achievement_list[8].name, "Keep The Change"); + strcpy(achievement_list[8].description, "Purchase everything"); + + achievement_list[9].img = Draw_CachePic("gfx/achievement/big_thanks"); + achievement_list[9].unlocked = 0; + achievement_list[9].progress = 0; + strcpy(achievement_list[9].name, "Big Thanks To Explosion"); + strcpy(achievement_list[9].description, "Get 10 kills with one grenade"); + + achievement_list[10].img = Draw_CachePic("gfx/achievement/warmed-up"); + achievement_list[10].unlocked = 0; + achievement_list[10].progress = 0; + strcpy(achievement_list[10].name, "Getting Warmed-Up"); + strcpy(achievement_list[10].description, "Achieve 10 achievements"); + + achievement_list[11].img = Draw_CachePic("gfx/achievement/mysterybox_maniac"); + achievement_list[11].unlocked = 0; + achievement_list[11].progress = 0; + strcpy(achievement_list[11].name, "Mysterybox Maniac"); + strcpy(achievement_list[11].description, "use mysterybox 20 times"); + + achievement_list[12].img = Draw_CachePic("gfx/achievement/instant_help"); + achievement_list[12].unlocked = 0; + achievement_list[12].progress = 0; + strcpy(achievement_list[12].name, "Instant Help"); + strcpy(achievement_list[12].description, "Kill 100 zombies with insta-kill"); + + achievement_list[13].img = Draw_CachePic("gfx/achievement/blow_the_bank"); + achievement_list[13].unlocked = 0; + achievement_list[13].progress = 0; + strcpy(achievement_list[13].name, "Blow The Bank"); + strcpy(achievement_list[13].description, "earn 1,000,000"); + + achievement_list[14].img = Draw_CachePic("gfx/achievement/ammo_cost"); + achievement_list[14].unlocked = 0; + achievement_list[14].progress = 0; + strcpy(achievement_list[14].name, "Ammo Cost Too Much"); + strcpy(achievement_list[14].description, "After round 5, don't fire a bullet for whole round"); + + achievement_list[15].img = Draw_CachePic("gfx/achievement/the_f_bomb"); + achievement_list[15].unlocked = 0; + achievement_list[15].progress = 0; + strcpy(achievement_list[15].name, "The F Bomb"); + strcpy(achievement_list[15].description, "Only nuke one zombie"); + + achievement_list[16].img = Draw_CachePic("gfx/achievement/why_are"); + achievement_list[16].unlocked = 0; + achievement_list[16].progress = 0; + strcpy(achievement_list[16].name, "Why Are We Waiting?"); + strcpy(achievement_list[16].description, "Stand still for 2 minutes"); + + achievement_list[17].img = Draw_CachePic("gfx/achievement/never_missed"); + achievement_list[17].unlocked = 0; + achievement_list[17].progress = 0; + strcpy(achievement_list[17].name, "Never Missed A Shot"); + strcpy(achievement_list[17].description, "Make it to round 5 without missing a shot"); + + achievement_list[18].img = Draw_CachePic("gfx/achievement/300_bastards_less"); + achievement_list[18].unlocked = 0; + achievement_list[18].progress = 0; + strcpy(achievement_list[18].name, "300 Bastards less"); + strcpy(achievement_list[18].description, "Kill 300 zombies"); + + achievement_list[19].img = Draw_CachePic("gfx/achievement/music_fan"); + achievement_list[19].unlocked = 0; + achievement_list[19].progress = 0; + strcpy(achievement_list[19].name, "Music Fan"); + strcpy(achievement_list[19].description, "Turn on radio 20 times"); + + achievement_list[20].img = Draw_CachePic("gfx/achievement/one_clip"); + achievement_list[20].unlocked = 0; + achievement_list[20].progress = 0; + strcpy(achievement_list[20].name, "One Clip"); + strcpy(achievement_list[20].description, "Complete a round with mg42 without reloading"); + + achievement_list[21].img = Draw_CachePic("gfx/achievement/one_20_20"); + achievement_list[21].unlocked = 0; + achievement_list[21].progress = 0; + strcpy(achievement_list[21].name, "One Clip, 20 Bullets, 20 Headshots"); + strcpy(achievement_list[21].description, "Score 20 headshots, with 20 bullets, and don't reload"); + + achievement_list[22].img = Draw_CachePic("gfx/achievement/and_stay_out"); + achievement_list[22].unlocked = 0; + achievement_list[22].progress = 0; + strcpy(achievement_list[22].name, "And Stay out"); + strcpy(achievement_list[22].description, "Don't let zombies in for 2 rounds");*/ + + achievement_locked = Draw_CachePic("gfx/achievement/achievement_locked"); + + m_achievement_scroll[0] = 0; + m_achievement_scroll[1] = 0; +} + + +void Load_Achivements (void) +{ + int achievement_file; + achievement_file = sceIoOpen(va("%s/data/ach.dat",com_gamedir), PSP_O_RDONLY, 0); + + if (achievement_file >= 0) { + char* buffer = (char*)calloc(2, sizeof(char)); + for (int i = 0; i < MAX_ACHIEVEMENTS; i++) { + sceIoRead(achievement_file, buffer, sizeof(char)*2); + achievement_list[i].unlocked = atoi(buffer); + sceIoRead(achievement_file, buffer, sizeof(char)*2); + achievement_list[i].progress = atoi(buffer); + } + } else { + achievement_file = sceIoOpen(va("%s/data/ach.dat",com_gamedir), PSP_O_WRONLY | PSP_O_CREAT | PSP_O_TRUNC, 0); + + for (int i = 0; i < MAX_ACHIEVEMENTS; i++) { + sceIoWrite(achievement_file, "0\n", sizeof(char)*2); + sceIoWrite(achievement_file, "0\n", sizeof(char)*2); + } + } + sceIoClose(achievement_file); +} +void Save_Achivements (void) +{ + int achievement_file; + achievement_file = sceIoOpen(va("%s/data/ach.dat",com_gamedir), PSP_O_WRONLY, 0); + + if (achievement_file >= 0) { + for (int i = 0; i < MAX_ACHIEVEMENTS; i++) { + char* buffer = va("%i\n", achievement_list[i].unlocked); + char* buffer2 = va("%i\n", achievement_list[i].progress); + sceIoWrite(achievement_file, va("%i\n", achievement_list[i].unlocked), strlen(buffer)); + sceIoWrite(achievement_file, va("%i\n", achievement_list[i].progress), strlen(buffer2)); + } + } else { + Load_Achivements(); + } +} + + +void M_Menu_Achievement_f (void) +{ + key_dest = key_menu; + m_state = m_achievement; + m_entersound = true; + //Load_Achivements(); +} + +void M_Achievement_Draw (void) +{ + int i, b, y; + int unlocked_achievement[MAX_ACHIEVEMENTS]; + int locked_achievement[MAX_ACHIEVEMENTS]; + int maxLenght = floor((vid.width - 155)/8); + int stringLenght; + char *description; + char *string_line = (char*) malloc(maxLenght); + int lines; + + y = 0; + total_unlocked_achievements = 0; + total_locked_achievements = 0; + + for (i = 0; i < MAX_ACHIEVEMENTS; i++) + { + unlocked_achievement[i] = -1; + locked_achievement[i] = -1; + if (achievement_list[i].unlocked) + { + unlocked_achievement[total_unlocked_achievements] = i; + total_unlocked_achievements += 1; + } + else + { + locked_achievement[total_locked_achievements] = i; + total_locked_achievements += 1; + } + } + + // Background + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102)); + + // Version String + Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1); + + if (!m_achievement_selected) + { + Draw_FillByColor(15, 8, 225, 12, GU_RGBA(204, 0, 0, 100)); + Draw_FillByColor(240, 8, 225, 12, GU_RGBA(0, 0, 0, 100)); + + if (total_unlocked_achievements <= 0) + { + Draw_FillByColor(15, 25, vid.width - 30, 60, GU_RGBA (0, 0, 0, 100)); + Draw_Pic (20, 30 + y, achievement_locked); + Draw_String (125, 30 + y, "No achievements unlocked :("); + } + else + { + for (i = 0; i < 3; i++) + { + if (unlocked_achievement[i + m_achievement_scroll[0]] >= 0) + { + Draw_FillByColor(15, 25 + y, vid.width - 30, 60, GU_RGBA (0, 0, 0, 100)); + + Draw_Pic (20, 30 + y, achievement_list[unlocked_achievement[i + m_achievement_scroll[0]]].img); + Draw_String (125, 30 + y, achievement_list[unlocked_achievement[i + m_achievement_scroll[0]]].name); + description = achievement_list[unlocked_achievement[i + m_achievement_scroll[0]]].description; + stringLenght = strlen(description); + lines = stringLenght/maxLenght; + if ((maxLenght % stringLenght) != 0) + lines++; + + for (b = 0; b < lines; b++) { + strncpy(string_line, description+maxLenght*b, (maxLenght-1)); + Draw_String (125, 40 + y + b*8, string_line); + } + + //if (stringLenght <= maxLenght) + // Draw_String (125, 40 + y, description); + + y += 75; + } + } + } + } + else + { + if (total_locked_achievements <= 0) + { + Draw_FillByColor(15, 25, vid.width - 30, 60, GU_RGBA (0, 0, 0, 100)); + Draw_Pic (20, 30 + y, achievement_locked); + Draw_String (125, 30 + y, "All achievements unlocked :)"); + } + + Draw_FillByColor(15, 8, 225, 12, GU_RGBA(0, 0, 0, 100)); + Draw_FillByColor(240, 8, 225, 12, GU_RGBA(204, 0, 0, 100)); + + for (i = 0; i < 3; i++) + { + if (locked_achievement[i + m_achievement_scroll[1]] >= 0) + { + Draw_FillByColor(15, 25 + y, vid.width - 30, 60, GU_RGBA (0, 0, 0, 100)); + + Draw_Pic (20, 30 + y, achievement_locked); + Draw_String (125, 30 + y, achievement_list[locked_achievement[i + m_achievement_scroll[1]]].name); + description = achievement_list[locked_achievement[i + m_achievement_scroll[1]]].description; + stringLenght = strlen(description); + lines = stringLenght/maxLenght; + if ((maxLenght % stringLenght) != 0) + lines++; + + for (b = 0; b < lines; b++) { + strncpy(string_line, description+maxLenght*b, (maxLenght-1)); + Draw_String (125, 40 + y + b*8, string_line); + } + + + //if (stringLenght <= maxLenght) + // Draw_String (125, 40 + y, description); + + y += 70; + } + } + } + + free(string_line); + Draw_String (15, 10, "Unlocked Achievements"); + Draw_String (vid.width - 167, 10, "Locked Achievements"); +} + +void M_Achievement_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + if (key_dest == key_menu_pause) + M_Paused_Menu_f(); + else + M_Menu_Main_f (); + break; + + case K_AUX1: + case K_LEFTARROW: + m_achievement_selected = 0; + break; + + case K_AUX2: + case K_RIGHTARROW: + m_achievement_selected = 1; + break; + + case K_UPARROW: + m_achievement_scroll[m_achievement_selected]--; + if (m_achievement_scroll[m_achievement_selected] < 0) + m_achievement_scroll[m_achievement_selected] = 0; + break; + + case K_DOWNARROW: + m_achievement_scroll[m_achievement_selected]++; + + if (m_achievement_selected) + { + if (m_achievement_scroll[1] > total_locked_achievements - 3) + m_achievement_scroll[1] = total_locked_achievements - 3; + } + else + { + if (m_achievement_scroll[0] > total_unlocked_achievements - 3) + m_achievement_scroll[0] = total_unlocked_achievements - 3; + } + if (m_achievement_scroll[m_achievement_selected] < 0) + m_achievement_scroll[m_achievement_selected] = 0; + break; + + case K_ENTER: + m_entersound = true; + switch (m_achievement_cursor) + { + } + } +} + +//============================================================================= +/* MULTIPLAYER MENU */ + +int m_multiplayer_cursor; + +#define MULTIPLAYER_ITEMS 6 + +void M_Menu_MultiPlayer_f (void) +{ + key_dest = key_menu; + m_state = m_multiplayer; + m_entersound = true; +} + +#include +void M_MultiPlayer_Draw (void) +{ + //int f; + + + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + //else + //Draw_AlphaPic (0, 0, pause_bk, 0.4); + //f = (int)(host_time * 10)%6; + + //M_DrawTransPic (54, 32 + m_multiplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) ); + + + if (m_multiplayer_cursor == 0) + M_Print ((vid.width - ((9 * 8) + 16)), (vid.height - 64), "Join Game"); + else + M_PrintWhite ((vid.width - ((9 * 8) + 16)), (vid.height - 64), "Join Game"); + + if (m_multiplayer_cursor == 1) + M_Print ((vid.width - ((9 * 8) + 16)), (vid.height - 56), "Host Game"); + else + M_PrintWhite ((vid.width - ((9 * 8) + 16)), (vid.height - 56), "Host Game"); + + if (m_multiplayer_cursor == 2) + M_Print ((vid.width - ((12 * 8) + 16)), (vid.height - 48), "Player Setup"); + else + M_PrintWhite ((vid.width - ((12 * 8) + 16)), (vid.height - 48), "Player Setup"); + + + if (m_multiplayer_cursor == 3) + M_Print ((vid.width - ((11 * 8) + 16)), (vid.height - 40), "Server List"); + else + M_PrintWhite ((vid.width - ((11 * 8) + 16)), (vid.height - 40), "Server List"); + + + // M_PrintOld (72, 97, "Slist "); + if (m_multiplayer_cursor == 4) + M_Print ((vid.width - ((14 * 8) + 16)), (vid.height - 32), "Infrastructure"); + else + M_PrintWhite ((vid.width - ((14 * 8) + 16)), (vid.height - 32), "Infrastructure"); + + if (m_multiplayer_cursor == 5) + M_Print ((vid.width - ((5 * 8) + 16)), (vid.height - 24), "Adhoc"); + else + M_PrintWhite ((vid.width - ((5 * 8) + 16)), (vid.height - 24), "Adhoc"); + + // M_PrintOld (72, 117, "Infrastructure "); + //M_DrawCheckboxOld (220, 117, tcpipAvailable && !tcpipAdhoc); + M_DrawCheckbox (220, vid.height - 32, tcpipAvailable && !tcpipAdhoc); + + //M_PrintOld (72, 137, "Adhoc "); + //M_DrawCheckboxOld (220, 137, tcpipAvailable && tcpipAdhoc); + M_DrawCheckbox (220, vid.height - 24, tcpipAvailable && tcpipAdhoc); + + if (serialAvailable || ipxAvailable || tcpipAvailable) + return; + + //M_PrintWhiteOld ((160) - ((35*8)/2), 180, "No Network Communications Available"); + M_PrintWhiteOld ((160) - ((35*8)/2), 180, "PSP MULTIPLAYER IS NOT OFFICALY SUPPORTED"); + M_PrintWhiteOld ((160) - ((35*8)/2), 188, "DO NOT ASK HOW TO PLAY ONLINE ON PSP"); + +} + + +void M_MultiPlayer_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + M_Menu_Main_f (); + break; + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (++m_multiplayer_cursor >= MULTIPLAYER_ITEMS) + m_multiplayer_cursor = 0; + break; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (--m_multiplayer_cursor < 0) + m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1; + break; + + case K_ENTER: + m_entersound = true; + switch (m_multiplayer_cursor) + { + case 0: + if ((serialAvailable || ipxAvailable || tcpipAvailable) && sceWlanDevIsPowerOn()) + { + //M_Menu_Net_f (); + M_Menu_LanConfig_f (); + } + break; + + case 1: + if ((serialAvailable || ipxAvailable || tcpipAvailable) && sceWlanDevIsPowerOn()) + { + //M_Menu_Net_f (); + M_Menu_LanConfig_f (); + } + break; + + case 2: + M_Menu_Setup_f (); + break; + + case 3: + M_Menu_ServerList_f(); + break; + + case 4: + Datagram_Shutdown(); + + tcpipAvailable = !tcpipAvailable; + + if(tcpipAvailable && sceWlanDevIsPowerOn()) + { + tcpipAdhoc = false; + net_driver_to_use = 0; + Datagram_Init(); + } + break; + case 5: + Datagram_Shutdown(); + + tcpipAvailable = !tcpipAvailable; + + if(tcpipAvailable && sceWlanDevIsPowerOn()) + { + tcpipAdhoc = true; + net_driver_to_use = 1; + Datagram_Init(); + } + break; + } + } +} + +//============================================================================= +/* SETUP MENU */ + +int setup_cursor = 5; +int setup_cursor_table[] = {40, 56, 72, 96, 120, 156}; + +char setup_hostname[16]; +char setup_myname[16]; + +// Define PSP specific variables +#define NUM_SETUP_CMDS 4 +extern int totalAccessPoints; +extern int accessPointNumber[100]; +char setup_accesspoint[64]; + +void M_Menu_Setup_f (void) +{ + key_dest = key_menu; + m_state = m_setup; + m_entersound = true; + Q_strcpy(setup_myname, cl_name.string); + Q_strcpy(setup_hostname, hostname.string); + //setup_accesspoint ; + + if(totalAccessPoints) + { + sceUtilityGetNetParam(accessPointNumber[(int)accesspoint.value], 0, (netData*)setup_accesspoint); + } +} + +void M_Setup_Draw (void) +{ + + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + + if (setup_cursor == 0) + M_Print (64, 72, "Access Point"); + else + M_PrintWhite (64,72, "Access Point"); + + //M_PrintOld (64, 40, "Access Point"); + //M_DrawTextBox (160, 32, 16, 1); + M_PrintOld (168, 72, setup_accesspoint); + + if (setup_cursor == 1) + M_Print (64, 92, "Hostname"); + else + M_PrintWhite (64,92, "Hostname"); + + M_DrawTextBox (160, 84, 16, 1); + M_PrintOld (168, 92, setup_hostname); + + if (setup_cursor == 2) + M_Print (64, 104, "Player Name"); + else + M_PrintWhite (64,104, "Player Name"); + + M_DrawTextBox (160, 96, 16, 1); + M_PrintOld (168, 104, setup_myname); + + if (setup_cursor == 3) + M_Print (186, 120, "Accept Changes"); + else + M_PrintWhite (186,120, "Accept Changes"); +} + + +void M_Setup_Key (int k) +{ + int l; + int offset = 0; + + switch (k) + { + case K_ESCAPE: + M_Menu_MultiPlayer_f (); + break; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + setup_cursor--; + if (setup_cursor < 0) + setup_cursor = NUM_SETUP_CMDS-1; + break; + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + setup_cursor++; + if (setup_cursor >= NUM_SETUP_CMDS) + setup_cursor = 0; + break; + + case K_LEFTARROW: + if (setup_cursor == 0) + { + S_LocalSound ("sounds/menu/navigate.wav"); + if(accesspoint.value > 1) + { + Cvar_SetValue("accesspoint", accesspoint.value-1); + //blubso + sceUtilityGetNetParam(accessPointNumber[(int)accesspoint.value], 0, (netData*)setup_accesspoint); + } + } + offset = 1; + if (setup_cursor < 2+offset) + return; + S_LocalSound ("sounds/menu/navigate.wav"); + break; + case K_RIGHTARROW: + if (setup_cursor == 0) + { + S_LocalSound ("sounds/menu/navigate.wav"); + if(accesspoint.value < totalAccessPoints) + { + Cvar_SetValue("accesspoint", accesspoint.value+1); + //blubso + sceUtilityGetNetParam(accessPointNumber[(int)accesspoint.value], 0, (netData*)setup_accesspoint); + } + } + + offset = 1; + + if (setup_cursor < 2+offset) + return; + + S_LocalSound ("sounds/menu/navigate.wav"); + break; + + case K_INS: + offset = 1; + if (setup_cursor == 0+offset) + { + M_Menu_OSK_f(setup_hostname, setup_hostname, 16); + break; + } + + if (setup_cursor == 1+offset) + { + M_Menu_OSK_f(setup_myname, setup_myname,16); + break; + } + break; + + case K_ENTER: + offset = 1; + if (setup_cursor == 0+offset || setup_cursor == 1+offset || setup_cursor== 0) + return; + + //if (setup_cursor == 2+offset || setup_cursor == 3+offset) + // goto forward; + + // if (setup_cursor < 4+offset) + // break; + + // setup_cursor == 4 (OK) + if (Q_strcmp(cl_name.string, setup_myname) != 0) + Cbuf_AddText ( va ("name \"%s\"\n", setup_myname) ); + if (Q_strcmp(hostname.string, setup_hostname) != 0) + Cvar_Set("hostname", setup_hostname); + m_entersound = true; + M_Menu_MultiPlayer_f (); + break; + + case K_BACKSPACE: + offset = 1; + if (setup_cursor == 0+offset) + { + if (strlen(setup_hostname)) + setup_hostname[strlen(setup_hostname)-1] = 0; + } + + if (setup_cursor == 1+offset) + { + if (strlen(setup_myname)) + setup_myname[strlen(setup_myname)-1] = 0; + } + break; + + default: + if (k < 32 || k > 127) + break; + + offset = 1; + + if (setup_cursor == 0+offset) + { + l = strlen(setup_hostname); + if (l < 15) + { + setup_hostname[l+1] = 0; + setup_hostname[l] = k; + } + } + if (setup_cursor == 1+offset) + { + l = strlen(setup_myname); + if (l < 15) + { + setup_myname[l+1] = 0; + setup_myname[l] = k; + } + } + } +} + +//============================================================================= +/* SERVER LIST MENU */ +void M_Menu_SEdit_f (void); + +#define MENU_X 50 +#define MENU_Y 21 +#define TITLE_Y 4 +#define STAT_Y 166 + +int slist_cursor = 0, slist_mins = 0, slist_maxs = 15, slist_state; + +void M_Menu_ServerList_f (void) +{ + key_dest = key_menu; + m_state = m_slist; + m_entersound = true; + + slist_state = 0; +} + +void M_ServerList_Draw (void) +{ + int serv, line; + + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + //else + //Draw_AlphaPic (0, 0, pause_bk, 0.4); + + M_DrawTextBox (MENU_X, TITLE_Y, 23, 1); + M_PrintWhiteOld (MENU_X + 60, TITLE_Y + 8, "Server List"); + + if (!slist[0].server) + { + M_PrintWhiteOld (84, MENU_Y + 16 + 16, "Empty server list"); + M_PrintOld (60, MENU_Y + 16 + 32, "Press TRIANGLE to add a server"); + M_PrintOld (60, MENU_Y + 16 + 40, "Press SQUARE to edit a server"); + M_PrintOld (60, MENU_Y + 16 + 48, "Press CIRCLE to exit"); + return; + } + + M_DrawTextBox (MENU_X, STAT_Y, 23, 1); + M_DrawTextBox (MENU_X, MENU_Y, 23, slist_maxs - slist_mins + 1); + for (serv = slist_mins, line = 1 ; serv <= slist_maxs && serv < MAX_SERVER_LIST && slist[serv].server ; serv++, line++) + M_PrintOld (MENU_X + 18, line * 8 + MENU_Y, va("%1.21s", slist[serv].description)); + M_PrintWhiteOld (MENU_X, STAT_Y - 4, "TRIANGLE = add server, SQUARE = edit"); + M_PrintWhiteOld (MENU_X + 18, STAT_Y + 8, va("%1.22s", slist[slist_cursor].server)); + M_DrawCharacter2 (MENU_X + 8, (slist_cursor - slist_mins + 1) * 8 + MENU_Y, 12+((int)(realtime*4)&1)); +} + +void M_ServerList_Key (int key) +{ + int slist_length; + + if (!slist[0].server && key != K_ESCAPE && key != K_DEL) + return; + + switch (key) + { + case K_ESCAPE: + M_Menu_MultiPlayer_f (); + break; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (slist_cursor > 0) + { + if (keydown[K_CTRL]) + SList_Switch (slist_cursor, slist_cursor - 1); + slist_cursor--; + } + break; + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (keydown[K_CTRL]) + { + if (slist_cursor != SList_Length() - 1) + { + SList_Switch (slist_cursor, slist_cursor + 1); + slist_cursor++; + } + } + else if (slist_cursor < MAX_SERVER_LIST - 1 && slist[slist_cursor+1].server) + slist_cursor++; + break; + + case K_HOME: + S_LocalSound ("sounds/menu/navigate.wav"); + slist_cursor = 0; + break; + + case K_END: + S_LocalSound ("sounds/menu/navigate.wav"); + slist_cursor = SList_Length() - 1; + break; + + case K_PGUP: + S_LocalSound ("sounds/menu/navigate.wav"); + slist_cursor -= (slist_maxs - slist_mins); + if (slist_cursor < 0) + slist_cursor = 0; + break; + + case K_PGDN: + S_LocalSound ("sounds/menu/navigate.wav"); + slist_cursor += (slist_maxs - slist_mins); + if (slist_cursor >= MAX_SERVER_LIST) + slist_cursor = MAX_SERVER_LIST - 1; + while (!slist[slist_cursor].server) + slist_cursor--; + break; + + case K_ENTER: + if (keydown[K_CTRL]) + { + M_Menu_SEdit_f (); + break; + } + m_state = m_main; + M_ToggleMenu_f (); + Cbuf_AddText (va("connect \"%s\"\n", slist[slist_cursor].server)); + break; + + //case 'e': + //case 'E': + case K_INS: + M_Menu_SEdit_f (); + break; + + case K_DEL: + S_LocalSound ("sounds/menu/enter.wav"); + if ((slist_length = SList_Length()) < MAX_SERVER_LIST) + { + if (keydown[K_CTRL] && slist_length > 0) + { + if (slist_cursor < slist_length - 1) + memmove (&slist[slist_cursor+2], &slist[slist_cursor+1], (slist_length - slist_cursor - 1) * sizeof(slist[0])); + SList_Reset_NoFree (slist_cursor + 1); + SList_Set (slist_cursor + 1, "127.0.0.1", ""); + if (slist_length) + slist_cursor++; + } + else + { + memmove (&slist[slist_cursor+1], &slist[slist_cursor], (slist_length - slist_cursor) * sizeof(slist[0])); + SList_Reset_NoFree (slist_cursor); + SList_Set (slist_cursor, "127.0.0.1", ""); + } + } + break; + + case K_LEFTARROW: + S_LocalSound("sounds/menu/enter.wav"); + if ((slist_length = SList_Length()) > 0) + { + SList_Reset (slist_cursor); + if (slist_cursor > 0 && slist_length - 1 == slist_cursor) + { + slist_cursor--; + } + else + { + memmove (&slist[slist_cursor], &slist[slist_cursor+1], (slist_length - slist_cursor - 1) * sizeof(slist[0])); + SList_Reset_NoFree (slist_length - 1); + } + } + break; + } + + if (slist_cursor < slist_mins) + { + slist_maxs -= (slist_mins - slist_cursor); + slist_mins = slist_cursor; + } + if (slist_cursor > slist_maxs) + { + slist_mins += (slist_cursor - slist_maxs); + slist_maxs = slist_cursor; + } +} + +#define SERV_X 60 +#define SERV_Y 64 +#define DESC_X 60 +#define DESC_Y 40 +#define SERV_L 23 +#define DESC_L 23 + +#define SLIST_BUFSIZE 128 + +static char slist_serv[SLIST_BUFSIZE], slist_desc[SLIST_BUFSIZE]; +static int slist_serv_max, slist_serv_min, slist_desc_max, slist_desc_min, sedit_state; + +void M_Menu_SEdit_f (void) +{ + int size; + + key_dest = key_menu; + m_state = m_sedit; + m_entersound = true; + + sedit_state = 0; + Q_strncpyz (slist_serv, slist[slist_cursor].server, sizeof(slist_serv)); + Q_strncpyz (slist_desc, slist[slist_cursor].description, sizeof(slist_desc)); + slist_serv_max = (size = strlen(slist_serv)) > SERV_L ? size : SERV_L; + slist_serv_min = slist_serv_max - SERV_L; + slist_desc_max = (size = strlen(slist_desc)) > DESC_L ? size : DESC_L; + slist_desc_min = slist_desc_max - DESC_L; +} + +void M_SEdit_Draw (void) +{ + + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + //else + //Draw_AlphaPic (0, 0, pause_bk, 0.4); + + M_DrawTextBox (SERV_X, SERV_Y, 23, 1); + M_DrawTextBox (DESC_X, DESC_Y, 23, 1); + M_PrintWhiteOld (SERV_X, SERV_Y - 4, "Hostname/IP:"); + M_PrintWhiteOld (DESC_X, DESC_Y - 4, "Description:"); + M_PrintOld (SERV_X + 9, SERV_Y + 8, va("%1.23s", slist_serv + slist_serv_min)); + M_PrintOld (DESC_X + 9, DESC_Y + 8, va("%1.23s", slist_desc + slist_desc_min)); + if (sedit_state == 0) + M_DrawCharacter2 (SERV_X + 9 + 8*(strlen(slist_serv) - slist_serv_min), SERV_Y + 8, 10+((int)(realtime*4)&1)); + else + M_DrawCharacter2 (DESC_X + 9 + 8*(strlen(slist_desc) - slist_desc_min), DESC_Y + 8, 10+((int)(realtime*4)&1)); +} + +void M_SEdit_Key (int key) +{ + int l; + + switch (key) + { + case K_ESCAPE: + M_Menu_ServerList_f (); + break; + + case K_ENTER: + SList_Set (slist_cursor, slist_serv, slist_desc); + M_Menu_ServerList_f (); + break; + + case K_UPARROW: + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + sedit_state = !sedit_state; + break; + + case K_INS: + if (sedit_state == 0) + M_Menu_OSK_f(slist_serv, slist_serv, 16); + else + M_Menu_OSK_f(slist_desc, slist_desc, 16); + break; + + case K_BACKSPACE: + if (sedit_state == 0) + { + if ((l = strlen(slist_serv))) + slist_serv[--l] = 0; + if (strlen(slist_serv) - 6 < slist_serv_min && slist_serv_min) + { + slist_serv_min--; + slist_serv_max--; + } + } + else + { + if ((l = strlen(slist_desc))) + slist_desc[--l] = 0; + if (strlen(slist_desc) - 6 < slist_desc_min && slist_desc_min) + { + slist_desc_min--; + slist_desc_max--; + } + } + break; + + default: + if (key < 32 || key > 127) + break; + + if (sedit_state == 0) + { + l = strlen (slist_serv); + if (l < SLIST_BUFSIZE - 1) + { + slist_serv[l+1] = 0; + slist_serv[l] = key; + l++; + } + if (l > slist_serv_max) + { + slist_serv_min++; + slist_serv_max++; + } + } + else + { + l = strlen (slist_desc); + if (l < SLIST_BUFSIZE - 1) + { + slist_desc[l+1] = 0; + slist_desc[l] = key; + l++; + } + if (l > slist_desc_max) + { + slist_desc_min++; + slist_desc_max++; + } + } + break; + } +} + +//============================================================================= +/* NET MENU */ + +int m_net_cursor; +int m_net_items; +int m_net_saveHeight; + +char *net_helpMessage [] = +{ +/* .........1.........2.... */ + " ", + " Two computers connected", + " through two modems. ", + " ", + + " ", + " Two computers connected", + " by a null-modem cable. ", + " ", + + " Novell network LANs ", + " or Windows 95 DOS-box. ", + " ", + "(LAN=Local Area Network)", + + " 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 = 4; + + 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; + + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + //else + //Draw_AlphaPic (0, 0, pause_bk, 0.4); + + f = 32; + qpic_t *p; + + if (serialAvailable) + { + p = Draw_CachePic ("gfx/netmen1.lmp"); + } + else + { + p = Draw_CachePic ("gfx/dim_modm.lmp"); + } + + if (p) + M_DrawTransPic (72, f, p); + + f += 19; + + if (serialAvailable) + { + p = Draw_CachePic ("gfx/netmen2.lmp"); + } + else + { + p = Draw_CachePic ("gfx/dim_drct.lmp"); + } + + if (p) + M_DrawTransPic (72, f, p); + + f += 19; + if (ipxAvailable) + p = Draw_CachePic ("gfx/netmen3.lmp"); + else + p = Draw_CachePic ("gfx/dim_ipx.lmp"); + M_DrawTransPic (72, f, p); + + f += 19; + if (tcpipAvailable) + p = Draw_CachePic ("gfx/netmen4.lmp"); + else + p = Draw_CachePic ("gfx/dim_tcp.lmp"); + M_DrawTransPic (72, f, p); + + if (m_net_items == 5) // JDC, could just be removed + { + f += 19; + p = Draw_CachePic ("gfx/netmen5.lmp"); + M_DrawTransPic (72, f, p); + } + + f = (320-26*8)/2; + M_DrawTextBox (f, 134, 24, 4); + f += 8; + M_PrintOld (f, 142, net_helpMessage[m_net_cursor*4+0]); + M_PrintOld (f, 150, net_helpMessage[m_net_cursor*4+1]); + M_PrintOld (f, 158, net_helpMessage[m_net_cursor*4+2]); + M_PrintOld (f, 166, net_helpMessage[m_net_cursor*4+3]); + + 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) +{ +again: + switch (k) + { + case K_ESCAPE: + M_Menu_MultiPlayer_f (); + break; + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (++m_net_cursor >= m_net_items) + m_net_cursor = 0; + break; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + if (--m_net_cursor < 0) + m_net_cursor = m_net_items - 1; + break; + + case K_ENTER: + m_entersound = true; + + switch (m_net_cursor) + { + case 0: + M_Menu_SerialConfig_f (); + break; + + case 1: + M_Menu_SerialConfig_f (); + break; + + case 2: + M_Menu_LanConfig_f (); + break; + + case 3: + M_Menu_LanConfig_f (); + break; + + case 4: +// multiprotocol + break; + } + } + + if (m_net_cursor == 0 && !serialAvailable) + goto again; + if (m_net_cursor == 1 && !serialAvailable) + goto again; + if (m_net_cursor == 2 && !ipxAvailable) + goto again; + if (m_net_cursor == 3 && !tcpipAvailable) + goto again; +} + +//============================================================================= +/* OPTIONS MENU */ +#define SLIDER_RANGE 10 + + +enum //video menu +{ + OPT_SHOWFPS, + OPT_MAXFPS, + OPT_FOV, + OPT_GAMMA, + OPT_DECALS, + OPT_PARTICLES, + OPT_FULLBRIGHT, + OPT_DITHERING, + OPT_RETRO, + VID_ITEMS +}; + +enum //audio menu +{ + OPT_MUSICVOL, + OPT_SNDVOL, + OPT_MUSICTYPE, + AUDIO_ITEMS +}; + +enum //gameplay menu +{ + OPT_CROSSHAIR, + OPT_AIMASSIST, + OPT_IN_SPEED, + OPT_IN_ACCELERATION, + OPT_INVMOUSE, + OPT_IN_TOLERANCE, + OPT_MOUSELOOK, + GAMEPLAY_ITEMS +}; + + + +extern cvar_t show_fps; +extern cvar_t show_bat; + +void M_AdjustSliders (int dir, int m_submenu, int options_cursor) +{ + S_LocalSound ("sounds/menu/navigate.wav"); + + + if (m_submenu == 0) + { + switch (options_cursor) + { + case OPT_GAMMA: // 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 ("gamma", v_gamma.value); + break; + case OPT_MAXFPS: + cl_maxfps.value = cl_maxfps.value + dir * 5; + if (cl_maxfps.value < 30) + cl_maxfps.value = 30; + if (cl_maxfps.value > 65) + cl_maxfps.value = 65; + Cvar_SetValue ("cl_maxfps", cl_maxfps.value); + break; + case OPT_FOV: + scr_fov.value += dir * 5; + if (scr_fov.value < 50) + scr_fov.value = 50; + if (scr_fov.value > 120 && dir > 0) + scr_fov.value = 120; + Cvar_SetValue ("scr_fov", scr_fov.value); + break; + case OPT_DECALS: + Cvar_SetValue ("nzp_decals", !nzp_decals.value); + break; + case OPT_PARTICLES: + Cvar_SetValue ("nzp_particles", !nzp_decals.value); + break; + case OPT_FULLBRIGHT: + Cvar_SetValue ("r_fullbright", !nzp_decals.value); + break; + case OPT_DITHERING: + Cvar_SetValue ("r_dithering", !r_dithering.value); + break; + case OPT_RETRO: + Cvar_SetValue ("r_retro", !r_retro.value); + break; + case OPT_SHOWFPS: + Cvar_SetValue ("show_fps", !show_fps.value); + break; + } + } + else if (m_submenu == 1) + { + switch (options_cursor) + { + case OPT_MUSICVOL: // 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 OPT_SNDVOL: // 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 OPT_MUSICTYPE: // bgm type + if (strcmp(bgmtype.string,"cd") == 0) + { + Cvar_Set("bgmtype","none"); + bmg_type_changed = true; + } + else + { + Cvar_Set("bgmtype","cd"); + bmg_type_changed = true; + } + break; + } + } + else if (m_submenu == 2) + { + switch (options_cursor) + { + case OPT_CROSSHAIR: + Cvar_SetValue ("crosshair", !crosshair.value); + break; + case OPT_AIMASSIST: + Cvar_SetValue ("in_aimassist", !in_aimassist.value); + break; + + case OPT_IN_SPEED: // mouse speed + in_sensitivity.value += dir * 0.5; + if (in_sensitivity.value < 1) + in_sensitivity.value = 1; + if (in_sensitivity.value > 11) + in_sensitivity.value = 11; + Cvar_SetValue ("sensitivity", in_sensitivity.value); + break; + + case OPT_IN_ACCELERATION: // mouse tolerance + in_acceleration.value -= dir * 0.25; + if (in_acceleration.value < 0.5) + in_acceleration.value = 0.5; + if (in_acceleration.value > 2) + in_acceleration.value = 2; + Cvar_SetValue ("acceleration", in_acceleration.value); + break; + + case OPT_IN_TOLERANCE: // mouse tolerance + in_tolerance.value += dir * 0.05; + if (in_tolerance.value < 0) + in_tolerance.value = 0; + if (in_tolerance.value > 1) + in_tolerance.value = 1; + Cvar_SetValue ("tolerance", in_tolerance.value); + break; + case OPT_INVMOUSE: // invert mouse + Cvar_SetValue ("m_pitch", -m_pitch.value); + break; + case OPT_MOUSELOOK: + Cvar_SetValue ("in_mlook", !in_mlook.value); + Cvar_SetValue ("in_analog_strafe", !in_analog_strafe.value); + break; + } + } +} + +void M_DrawSlider (int x, int y, float range) +{ + int i; + + if (range < 0) + range = 0; + if (range > 1) + range = 1; + Draw_Character (x-8, y, 128); + for (i=0 ; i= VID_ITEMS+1) + video_cursor = 0; + break; + + case K_LEFTARROW: + M_AdjustSliders (-1, 0, video_cursor); + break; + + case K_RIGHTARROW: + M_AdjustSliders (1, 0, video_cursor); + break; + } +} + + + +//============================================================================= +/* Audio Setting menu */ + +int audio_cursor; +void M_Menu_Audio_f (void) +{ + m_state = m_audio; + m_entersound = true; + audio_cursor = 0; +} + + +void M_Audio_Draw (void) +{ + float r; + + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + //else + //Draw_AlphaPic (0, 0, pause_bk, 0.4); + + + Draw_String (352, 30, "Sound Settings"); + + if (audio_cursor == OPT_MUSICVOL) + M_Print (172, 50+(OPT_MUSICVOL*8), " CD Music Volume"); + else + Draw_String (172, 50+(OPT_MUSICVOL*8), " CD Music Volume"); + r = bgmvolume.value; + M_DrawSlider (376, 50+(OPT_MUSICVOL*8), r); + + if (audio_cursor == OPT_SNDVOL) + M_Print (172, 50+(OPT_SNDVOL*8), " Sound Volume"); + else + Draw_String (172, 50+(OPT_SNDVOL*8), " Sound Volume"); + r = volume.value; + M_DrawSlider (376, 50+(OPT_SNDVOL*8), r); + + if (audio_cursor == OPT_MUSICTYPE) + M_Print (172, 50+(OPT_MUSICTYPE*8)," Music Type"); + else + Draw_String (172, 50+(OPT_MUSICTYPE*8)," Music Type"); + if (strcmp(bgmtype.string,"cd") == 0) + Draw_String (424, 50+(OPT_MUSICTYPE*8), "CD/MP3"); + else + Draw_String (432, 50+(OPT_MUSICTYPE*8), "None"); + + if (audio_cursor == AUDIO_ITEMS) + M_Print (432, 58+(AUDIO_ITEMS*8), "Back"); + else + Draw_String (432, 58+(AUDIO_ITEMS*8), "Back"); +} + + +void M_Audio_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + if (key_dest == key_menu_pause) + { + M_Menu_Options_f (); + key_dest = key_menu_pause; + } + else + M_Menu_Options_f (); + break; + + case K_ENTER: + m_entersound = true; + switch (audio_cursor) + { + case AUDIO_ITEMS: + if (key_dest == key_menu_pause) + { + M_Menu_Options_f (); + key_dest = key_menu_pause; + } + else + M_Menu_Options_f (); + break; + + default: + M_AdjustSliders (1, 1, audio_cursor); + break; + } + return; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + audio_cursor--; + if (audio_cursor < 0) + audio_cursor = AUDIO_ITEMS; + break; + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + audio_cursor++; + if (audio_cursor >= AUDIO_ITEMS+1) + audio_cursor = 0; + break; + + case K_LEFTARROW: + M_AdjustSliders (-1, 1, audio_cursor); + break; + + case K_RIGHTARROW: + M_AdjustSliders (1, 1, audio_cursor); + break; + } +} + +//============================================================================= +/* Gameplay Setting menu */ + +int gameplay_cursor; +void M_Menu_Gameplay_f (void) +{ + m_state = m_gameplay; + m_entersound = true; + gameplay_cursor = 0; +} + +/* + float r; + + // Background + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102)); + + // Version String + Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1); + + // Header + Draw_ColoredString(10, 10, "GRAPHICS SETTINGS", 255, 255, 255, 255, 2); + + // Show FPS + if (video_cursor == 0) + Draw_ColoredString(10, 45, "Show FPS", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 45, "Show FPS", 255, 255, 255, 255, 1); + M_DrawCheckbox(225, 45, show_fps.value); + + // Max FPS + if (video_cursor == 1) + Draw_ColoredString(10, 55, "Max FPS", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 55, "Max FPS", 255, 255, 255, 255, 1); + r = (cl_maxfps.value - 30.0)*(1.0/35.0); + M_DrawSlider (225, 55, r); + + // FOV + if (video_cursor == 2) + Draw_ColoredString(10, 65, "Field of View", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 65, "Field of View", 255, 255, 255, 255, 1); + r = (scr_fov.value - 50.0)*(1.0/70.0); + M_DrawSlider (225, 65, r); + + // Brightness + if (video_cursor == 3) + Draw_ColoredString(10, 75, "Brightness", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 75, "Brightness", 255, 255, 255, 255, 1); + r = (1.0 - v_gamma.value) / 0.5; + M_DrawSlider (225, 75, r); + + // Decals + if (video_cursor == 4) + Draw_ColoredString(10, 85, "Decals", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 85, "Decals", 255, 255, 255, 255, 1); + M_DrawCheckbox(225, 85, nzp_decals.value); + + // Particles + if (video_cursor == 5) + Draw_ColoredString(10, 95, "Particles", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 95, "Particles", 255, 255, 255, 255, 1); + M_DrawCheckbox(225, 95, nzp_particles.value); + + // Fullbright + if (video_cursor == 6) + Draw_ColoredString(10, 105, "Fullbright", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 105, "Fullbright", 255, 255, 255, 255, 1); + M_DrawCheckbox(225, 105, r_fullbright.value); + + // Dithering + if (video_cursor == 7) + Draw_ColoredString(10, 115, "Dithering", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 115, "Dithering", 255, 255, 255, 255, 1); + M_DrawCheckbox(225, 115, r_dithering.value); + + // Retro + if (video_cursor == 8) + Draw_ColoredString(10, 125, "Retro", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 125, "Retro", 255, 255, 255, 255, 1); + M_DrawCheckbox(225, 125, r_retro.value); + + // Back + if (video_cursor == 9) + Draw_ColoredString(10, 250, "Back", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 250, "Back", 255, 255, 255, 255, 1); + + // Descriptions + switch(video_cursor) { + case 0: // Show FPS + Draw_ColoredString(10, 230, "Toggle Framerate Overlay.", 255, 255, 255, 255, 1); + break; + case 1: // Max FPS + Draw_ColoredString(10, 230, "Increase of Decrease Max Frames per Second.", 255, 255, 255, 255, 1); + break; + case 2: // FOV + Draw_ColoredString(10, 230, "Adjust Game Field of View.", 255, 255, 255, 255, 1); + break; + case 3: // Brightness + Draw_ColoredString(10, 230, "Increase or Decrease Game Brightness.", 255, 255, 255, 255, 1); + break; + case 4: // Decals + Draw_ColoredString(10, 230, "Toggle Bullet and Explosive Decals.", 255, 255, 255, 255, 1); + break; + case 5: // Particles + Draw_ColoredString(10, 230, "Toggle Appearence of (most) Particles.", 255, 255, 255, 255, 1); + break; + case 6: // Fullbright + Draw_ColoredString(10, 230, "Toggle all non-realtime lights (Requires Map Restart).", 255, 255, 255, 255, 1); + break; + case 7: // Dithering + Draw_ColoredString(10, 230, "Toggle decrease in Color Banding.", 255, 255, 255, 255, 1); + break; + case 8: // Retro + Draw_ColoredString(10, 230, "Toggle texture filtering.", 255, 255, 255, 255, 1); + break; + } + */ + + +void M_Gameplay_Draw (void) +{ + float r; + + // Background + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102)); + + // Version String + Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1); + + // Header + Draw_ColoredString(10, 10, "CONTROL SETTINGS", 255, 255, 255, 255, 2); + + // Draw Crosshair + if (gameplay_cursor == OPT_CROSSHAIR) { + Draw_ColoredString(10, 45, "Draw Crosshair", 255, 0, 0, 255, 1); + } else { + Draw_ColoredString(10, 45, "Draw Crosshair", 255, 255, 255, 255, 1); + } + M_DrawCheckbox(225, 45, crosshair.value); + + // Aim Assist + if (gameplay_cursor == OPT_AIMASSIST) { + Draw_ColoredString(10, 55, "Aim Assist", 255, 0, 0, 255, 1); + } else { + Draw_ColoredString(10, 55, "Aim Assist", 255, 255, 255, 255, 1); + } + M_DrawCheckbox(225, 55, in_aimassist.value); + + // Look Sensitivity + if (gameplay_cursor == OPT_IN_SPEED) { + Draw_ColoredString(10, 65, "Look Sensitivity", 255, 0, 0, 255, 1); + } else { + Draw_ColoredString(10, 65, "Look Sensitivity", 255, 255, 255, 255, 1); + } + r = (in_sensitivity.value - 1)/10; + M_DrawSlider (225, 65, r); + + // Look Acceleration + if (gameplay_cursor == OPT_IN_ACCELERATION) { + Draw_ColoredString(10, 75, "Look Acceleration", 255, 0, 0, 255, 1); + } else { + Draw_ColoredString(10, 75, "Look Acceleration", 255, 255, 255, 255, 1); + } + r = 1.0f -((in_acceleration.value - 0.5f)/1.5f); + M_DrawSlider (225, 75, r); + + // Look Inversion + if (gameplay_cursor == OPT_INVMOUSE) { + Draw_ColoredString(10, 85, "Look Inversion", 255, 0, 0, 255, 1); + } else { + Draw_ColoredString(10, 85, "Look Inversion", 255, 255, 255, 255, 1); + } + M_DrawCheckbox(225, 85, m_pitch.value < 0); + + // A-Nub Deadzone + if (gameplay_cursor == OPT_IN_TOLERANCE) { + Draw_ColoredString(10, 95, "A-Nub Tolerance", 255, 0, 0, 255, 1); + } else { + Draw_ColoredString(10, 95, "A-Nub Tolerance", 255, 255, 255, 255, 1); + } + r = (in_tolerance.value )/1.0f; + M_DrawSlider (225, 95, r); + + // A-Nub Mode + if (gameplay_cursor == OPT_MOUSELOOK) { + Draw_ColoredString(10, 105, "A-Nub Mode", 255, 0, 0, 255, 1); + } else { + Draw_ColoredString(10, 105, "A-Nub Mode", 255, 255, 255, 255, 1); + } + if (in_mlook.value) { + Draw_ColoredString(225, 105, "Look", 255, 255, 255, 255, 1); + } else { + Draw_ColoredString(225, 105, "Move (Buggy!)", 255, 255, 255, 255, 1); + } + + // Back + if (gameplay_cursor == GAMEPLAY_ITEMS) { + Draw_ColoredString(10, 250, "Back", 255, 0, 0, 255, 1); + } else { + Draw_ColoredString(10, 250, "Back", 255, 255, 255, 255, 1); + } + + // Descriptions + switch(gameplay_cursor) { + case OPT_CROSSHAIR: + Draw_ColoredString(10, 230, "Toggle Crosshair in-game.", 255, 255, 255, 255, 1); + break; + case OPT_AIMASSIST: + Draw_ColoredString(10, 230, "Toggle Assisted Aim to improve Targeting.", 255, 255, 255, 255, 1); + break; + case OPT_IN_SPEED: + Draw_ColoredString(10, 230, "Adjust Look Sensitivity.", 255, 255, 255, 255, 1); + break; + case OPT_IN_ACCELERATION: + Draw_ColoredString(10, 230, "Adjust Acceleration when A-Nub Mode is set to 'Look'.", 255, 255, 255, 255, 1); + break; + case OPT_INVMOUSE: + Draw_ColoredString(10, 230, "Invert A-Nub Look.", 255, 255, 255, 255, 1); + break; + case OPT_IN_TOLERANCE: + Draw_ColoredString(10, 230, "Adjust how Tolerant the A-Nub is to change.", 255, 255, 255, 255, 1); + break; + case OPT_MOUSELOOK: + Draw_ColoredString(10, 230, "Toggle whether to use the A-Nub for Look or Movement.", 255, 255, 255, 255, 1); + break; + } + +} + + +void M_Gameplay_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + if (key_dest == key_menu_pause) + { + M_Menu_Options_f (); + key_dest = key_menu_pause; + } + else + M_Menu_Options_f (); + break; + + case K_ENTER: + m_entersound = true; + switch (gameplay_cursor) + { + case GAMEPLAY_ITEMS: + if (key_dest == key_menu_pause) + { + M_Menu_Options_f (); + key_dest = key_menu_pause; + } + else + M_Menu_Options_f (); + break; + default: + M_AdjustSliders (1, 2, gameplay_cursor); + break; + } + return; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + gameplay_cursor--; + if (gameplay_cursor < 0) + gameplay_cursor = GAMEPLAY_ITEMS; + break; + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + gameplay_cursor++; + if (gameplay_cursor >= GAMEPLAY_ITEMS+1) + gameplay_cursor = 0; + break; + + case K_LEFTARROW: + M_AdjustSliders (-1, 2, gameplay_cursor); + break; + + case K_RIGHTARROW: + M_AdjustSliders (1, 2, gameplay_cursor); + break; + } +} + +int options_cursor; +#define OPTIONS_ITEMS 5 + +void M_Menu_Options_f (void) +{ + key_dest = key_menu; + m_state = m_options; + m_entersound = true; +} + +void M_Options_Draw (void) +{ + // Background + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102)); + + // Version String + Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1); + + // Header + Draw_ColoredString(10, 10, "SETTINGS", 255, 255, 255, 255, 2); + + // Graphics Settings + if (options_cursor == 0) + Draw_ColoredString(10, 45, "Graphics Settings", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 45, "Graphics Settings", 255, 255, 255, 255, 1); + + // Controls + if (options_cursor == 1) + Draw_ColoredString(10, 55, "Controls", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 55, "Controls", 255, 255, 255, 255, 1); + + // Control Settings + if (options_cursor == 2) + Draw_ColoredString(10, 65, "Control Settings", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 65, "Control Settings", 255, 255, 255, 255, 1); + + // Divider + Draw_FillByColor(10, 78, 160, 2, GU_RGBA(130, 130, 130, 255)); + + // Console + if (options_cursor == 3) + Draw_ColoredString(10, 85, "Console", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 85, "Console", 255, 255, 255, 255, 1); + + // Back + if (options_cursor == 4) + Draw_ColoredString(10, 250, "Back", 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, 250, "Back", 255, 255, 255, 255, 1); + + // Descriptions + switch(options_cursor) { + case 0: // Graphics Settings + Draw_ColoredString(10, 230, "Adjust settings relating to Graphical Fidelity.", 255, 255, 255, 255, 1); + break; + case 1: // Controls + Draw_ColoredString(10, 230, "Customize your Control Scheme.", 255, 255, 255, 255, 1); + break; + case 2: // Control Settings + Draw_ColoredString(10, 230, "Adjust settings in relation to how NZ:P Controls.", 255, 255, 255, 255, 1); + break; + case 3: // Console + Draw_ColoredString(10, 230, "Open the Console to input Commands.", 255, 255, 255, 255, 1); + break; + } +} + + +extern qboolean console_enabled; +void M_Options_Key (int k) +{ + switch (k) + { + case K_ESCAPE: + if (key_dest == key_menu_pause) + M_Paused_Menu_f(); + else + M_Menu_Main_f (); + break; + + case K_ENTER: + m_entersound = true; + switch (options_cursor) + { + case 0: + M_Menu_Screen_f(); + break; + case 1: + M_Menu_Keys_f(); + break; + case 2: + M_Menu_Gameplay_f (); + break; + case 3: + m_state = m_none; + console_enabled = true; + Con_ToggleConsole_f (); + break; + case 4: + if (key_dest == key_menu_pause) + M_Paused_Menu_f(); + else + M_Menu_Main_f (); + break; + /*case 0: + M_Menu_Keys_f(); + break; + + case 1: + M_Menu_Screen_f (); + break; + + case 2: + M_Menu_Audio_f (); + break; + + case 3: + M_Menu_Gameplay_f (); + break; + + case 4: + m_state = m_none; + console_enabled = true; + Con_ToggleConsole_f (); + break; + + case 5: + if (key_dest == key_menu_pause) + M_Paused_Menu_f(); + else + M_Menu_Main_f (); + break;*/ + } + return; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + options_cursor--; + if (options_cursor < 0) { + options_cursor = OPTIONS_ITEMS-1; + } + break; + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + options_cursor++; + if (options_cursor >= OPTIONS_ITEMS) + options_cursor = 0; + break; + } +} + +//============================================================================= +/* KEYS MENU */ + +char *bindnames[][2] = +{ + {"+forward", "Walk Forward"}, + {"+back", "Walk Backward"}, + {"+moveleft", "Move Left"}, + {"+moveright", "Move Right"}, + {"+lookup", "Look Up"}, + {"+lookdown", "Look Down"}, + {"+left", "Look Left"}, + {"+right", "Look Right"}, + {"+jump", "Jump"}, + {"+attack", "Fire"}, + {"+aim", "Aim Down Sight"}, + {"+switch", "Switch Weapon"}, + {"+use", "Interact"}, + {"+reload", "Reload"}, + {"+knife", "Melee"}, + {"+grenade", "Grenade"} +}; + +#define NUMCOMMANDS (sizeof(bindnames)/sizeof(bindnames[0])) + +int keys_cursor; +int bind_grab; + +void M_Menu_Keys_f (void) +{ + 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, j; + int y; + char *b; + + // Background + if (key_dest != key_menu_pause) + Draw_Pic(0, 0, menu_bk); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102)); + + // Version String + Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1); + + // Header + Draw_ColoredString(10, 10, "CONTROLS", 255, 255, 255, 255, 2); + + if (bind_grab) + { + Draw_ColoredString(70, 230, "Press a button for this action, to cancel", 255, 255, 255, 255, 1); + Draw_Pic (268, 230, b_start); + } + else + { + Draw_ColoredString(48 + ((vid.width - 320)>>1), 230, "Press to change, to clear", 255, 255, 255, 255, 1); + Draw_Pic (96 + ((vid.width - 320)>>1), 230, b_cross); + Draw_Pic (200 + ((vid.width - 320)>>1), 230, b_square); + } + + // search for known bindings + for (i = 0; i < NUMCOMMANDS; i++) + { + y = 45 + 10 * i; + + if (i == keys_cursor) + Draw_ColoredString(10, y, bindnames[i][1], 255, 0, 0, 255, 1); + else + Draw_ColoredString(10, y, bindnames[i][1], 255, 255, 255, 255, 1); + + for (j = 0; j < 256; j++) + { + b = keybindings[j]; + + if (!b) { + continue; + } + + if (!strcmp (b, bindnames[i][0])) + { + Draw_Pic (140 + ((vid.width - 320)>>1), y, GetButtonIcon(bindnames[i][0])); + break; + } + } + } + + if (keys_cursor == NUMCOMMANDS) { + Draw_ColoredString(10, 250, "Back", 255, 0, 0, 255, 1); + } else { + Draw_ColoredString(10, 250, "Back", 255, 255, 255, 255, 1); + M_DrawCharacter2 (130, 45 + keys_cursor*10, 12+((int)(realtime*4)&1)); + } +} + + +void M_Keys_Key (int k) +{ + char cmd[80]; + int keys[2]; + + if (bind_grab) + { // defining a key + S_LocalSound ("sounds/menu/navigate.wav"); + if (k == K_ESCAPE) + { + bind_grab = false; + } + else if (k != '`') + { + sprintf (cmd, "bind \"%s\" \"%s\"\n", Key_KeynumToString (k), bindnames[keys_cursor][0]); + Cbuf_InsertText (cmd); + } + + bind_grab = false; + return; + } + + switch (k) + { + case K_ESCAPE: + if (key_dest == key_menu_pause) + { + M_Menu_Options_f (); + key_dest = key_menu_pause; + } + else + M_Menu_Options_f (); + break; + + case K_LEFTARROW: + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + keys_cursor--; + if (keys_cursor < 0) + keys_cursor = NUMCOMMANDS; + break; + + case K_DOWNARROW: + case K_RIGHTARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + keys_cursor++; + if (keys_cursor >= NUMCOMMANDS + 1) + keys_cursor = 0; + break; + + case K_ENTER: + S_LocalSound ("sounds/menu/enter.wav"); + if (keys_cursor != NUMCOMMANDS) { // go into bind mode + M_FindKeysForCommand (bindnames[keys_cursor][0], keys); + if (keys[1] != -1) + M_UnbindCommand (bindnames[keys_cursor][0]); + bind_grab = true; + } else { // return to menu + if (key_dest == key_menu_pause) + { + M_Menu_Options_f (); + key_dest = key_menu_pause; + } + else + M_Menu_Options_f (); + } + break; + + case K_BACKSPACE: // delete bindings + case K_DEL: // delete bindings + S_LocalSound ("sounds/menu/enter.wav"); + M_UnbindCommand (bindnames[keys_cursor][0]); + break; + } +} + +//============================================================================= +/* CREDITS MENU */ + + +void M_Menu_Credits_f (void) +{ + key_dest = key_menu; + m_state = m_credits; + m_entersound = true; +} + + + +void M_Credits_Draw (void) +{ + // Background + Draw_Pic(0, 0, menu_bk); + + // Fill black to make everything easier to see + Draw_FillByColor(0, 0, 480, 272, GU_RGBA(0, 0, 0, 102)); + + // Version String + Draw_ColoredString(vid.width - 40, 5, "v1.0", 255, 255, 255, 255, 1); + + // Header + Draw_ColoredString(10, 10, "CREDITS", 255, 255, 255, 255, 2); + + + Draw_ColoredString(10, 45, "Blubswillrule: Coding, Models, GFX, Sounds, Music", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 55, "Ju[s]tice: Maps, Models, GFX", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 65, "Jukki: Coding", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 75, "Biodude: Sounds", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 85, "Dr_Mabuse1981: Coding", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 95, "Naievil: Coding, NX Maintaining", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 105, "MotoLegacy: Coding, GFX, Music", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 115, "Derped_Crusader: Models, GFX", 255, 255, 255, 255, 1); + + Draw_ColoredString(10, 135, "Special Thanks:", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 145, "- Spike: FTEQW", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 155, "- Shpuld: Clean-CSQC", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 165, "- Crow_Bar: DQuake", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 175, "- st1x51: DQuakePlus", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 185, "- fgsfdsfgs: QuakespasmNX", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 195, "- Azenn: GFX help", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 205, "- tavo: Music help", 255, 255, 255, 255, 1); + Draw_ColoredString(10, 215, "- BCDeshiG: Heavy bug testing", 255, 255, 255, 255, 1); + + Draw_ColoredString(10, 250, "Back", 255, 0, 0, 255, 1); +} + + +void M_Credits_Key (int key) +{ + switch (key) + { + case K_ENTER: + case K_ESCAPE: + M_Menu_Main_f (); + break; + } +} + +//============================================================================= +/* QUIT MENU */ + +int msgNumber; +int m_quit_prevstate; + +int m_quit_cursor; +int m_quit_items; + + +char *quitMessage [] = +{ + + " Are you gonna quit ", + " this game just like ", //msg:0 + " everything else? ", + " ", + + " Milord, methinks that ", + " thou art a lowly ", //msg:1 + " quitter. Is this true? ", + " ", + + " Do I need to bust your ", + " face open for trying ", //msg:2 + " to quit? ", + " ", + + " Man, I oughta smack you", + " for trying to quit! ", //msg:3 + " Press X to get ", + " smacked out. ", + + " Press X to quit like a ", + " big loser in life. ", //msg:4 + " Press O to stay proud ", + " and successful! ", + + " If you press X to ", + " quit, I will summon ", //msg:5 + " Satan all over your ", + " hard drive! ", + + " Um, Asmodeus dislikes ", + " his children trying to ", //msg:6 + " quit. Press X to return", + " to your Tinkertoys. ", + + " Really quit? ", + " ", //msg:7 + " Press X to quit, ", + " or O to continue. ", + + " If you quit now, I'll ", + " throw a blanket-party ", //msg:8 + " for you next time! ", + " ", + + " You are Crazy? ", + " Press X for Yes ", //msg:9 + " or O for no! ", + " Global Random Ha Ha Ha " +}; + + +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; +/* + Con_Printf ("open source\n"); + + FILE *fd = fopen("credits.txt","rb"); + FILE *fd1 = fopen("credits.dat","wb"); + + fseek (fd, 0, SEEK_END); + int len = ftell (fd); + fseek (fd, 0, SEEK_SET); + + char *str = malloc(len); + + fread (str, 1, len, fd); + Con_Printf ("source: %s\n",str); + + char *out = strencrypt(str, 110901, len); + Con_Printf ("enc: %s\n", out); + fwrite(out, len, 1, fd1); + + Con_Printf ("write encrypted\n"); + + //char *out2 = strdecrypt(out, 110901, sizeof(strf)); + //Con_Printf ("dec: %s\n", out2); + + free(out); + //free(out2); +*/ +} + + +void M_Quit_Key (int key) +{ + switch (key) + { + case K_ESCAPE: + 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 'Y': + case 'y': + case K_ENTER: + key_dest = key_console; + Host_Quit_f (); + break; + + default: + break; + } + +} + + +void M_Quit_Draw (void) +{ + if (wasInMenus) + { + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + //else + //Draw_AlphaPic (0, 0, pause_bk, 0.4); + + m_state = m_quit_prevstate; + m_recursiveDraw = true; + M_Draw (); + m_state = m_quit; + } + + M_DrawTextBox (56, 76, 24, 4); + M_PrintOld (64, 84, quitMessage[msgNumber*4+0]); + M_PrintOld (64, 92, quitMessage[msgNumber*4+1]); + M_PrintOld (64, 100, quitMessage[msgNumber*4+2]); + M_PrintOld (64, 108, quitMessage[msgNumber*4+3]); +} +//============================================================================= +/* OSK IMPLEMENTATION */ +#define CHAR_SIZE 8 +#define MAX_Y 8 +#define MAX_X 12 + +#define MAX_CHAR_LINE 36 +#define MAX_CHAR 72 + +int osk_pos_x = 0; +int osk_pos_y = 0; +int max_len = 0; +int m_old_state = 0; + +char* osk_out_buff = NULL; +char osk_buffer[128]; + +char *osk_text [] = + { + " 1 2 3 4 5 6 7 8 9 0 - = ` ", + " q w e r t y u i o p [ ] ", + " a s d f g h j k l ; ' \\ ", + " z x c v b n m , . / ", + " ", + " ! @ # $ % ^ & * ( ) _ + ~ ", + " Q W E R T Y U I O P { } ", + " A S D F G H J K L : \" | ", + " Z X C V B N M < > ? " + }; + +char *osk_help [] = + { + "CONFIRM: ", + " SQUARE ", + "CANCEL: ", + " CIRCLE ", + "DELETE: ", + " TRIAGLE ", + "ADD CHAR:", + " CROSS ", + "" + }; + +void M_Menu_OSK_f (char *input, char *output, int outlen) +{ + key_dest = key_menu; + m_old_state = m_state; + m_state = m_osk; + m_entersound = false; + max_len = outlen; + strncpy(osk_buffer,input,max_len); + osk_buffer[outlen] = '\0'; + osk_out_buff = output; +} + +void Con_OSK_f (char *input, char *output, int outlen) +{ + max_len = outlen; + strncpy(osk_buffer,input,max_len); + osk_buffer[outlen] = '\0'; + osk_out_buff = output; +} + + +void M_OSK_Draw (void) +{ + int x,y; + int i; + + char *selected_line = osk_text[osk_pos_y]; + char selected_char[2]; + + selected_char[0] = selected_line[1+(2*osk_pos_x)]; + selected_char[1] = '\0'; + if (selected_char[0] == ' ' || selected_char[0] == '\t') + selected_char[0] = 'X'; + + y = 20; + x = 16; + + M_DrawTextBox (10, 10, 26, 10); + M_DrawTextBox (10+(26*CHAR_SIZE), 10, 10, 10); + M_DrawTextBox (10, 10+(10*CHAR_SIZE),36, 3); + + for(i=0;i<=MAX_Y;i++) + { + M_PrintWhiteOld (x, y+(CHAR_SIZE*i), osk_text[i]); + if (i % 2 == 0) + M_PrintOld (x+(27*CHAR_SIZE), y+(CHAR_SIZE*i), osk_help[i]); + else + M_PrintWhiteOld (x+(27*CHAR_SIZE), y+(CHAR_SIZE*i), osk_help[i]); + } + + int text_len = strlen(osk_buffer); + if (text_len > MAX_CHAR_LINE) { + + char oneline[MAX_CHAR_LINE+1]; + strncpy(oneline,osk_buffer,MAX_CHAR_LINE); + oneline[MAX_CHAR_LINE] = '\0'; + + M_PrintOld (x+4, y+4+(CHAR_SIZE*(MAX_Y+2)), oneline ); + + strncpy(oneline,osk_buffer+MAX_CHAR_LINE, text_len - MAX_CHAR_LINE); + oneline[text_len - MAX_CHAR_LINE] = '\0'; + + M_PrintOld (x+4, y+4+(CHAR_SIZE*(MAX_Y+3)), oneline ); + M_PrintWhiteOld (x+4+(CHAR_SIZE*(text_len - MAX_CHAR_LINE)), y+4+(CHAR_SIZE*(MAX_Y+3)),"_"); + } + else { + M_PrintOld (x+4, y+4+(CHAR_SIZE*(MAX_Y+2)), osk_buffer ); + M_PrintWhiteOld (x+4+(CHAR_SIZE*(text_len)), y+4+(CHAR_SIZE*(MAX_Y+2)),"_"); + } + M_PrintOld (x+((((osk_pos_x)*2)+1)*CHAR_SIZE), y+(osk_pos_y*CHAR_SIZE), selected_char); + +} + +void M_OSK_Key (int key)//blubswillrule: making console cursor wrap around +{ + switch (key) + { + case K_RIGHTARROW: + osk_pos_x++; + if (osk_pos_x > MAX_X) + osk_pos_x = 0;//MAX_X + break; + case K_LEFTARROW: + osk_pos_x--; + if (osk_pos_x < 0) + osk_pos_x = MAX_X;//0 + break; + case K_DOWNARROW: + osk_pos_y++; + if (osk_pos_y > MAX_Y) + osk_pos_y = 0;//MAX_Y + break; + case K_UPARROW: + osk_pos_y--; + if (osk_pos_y < 0) + osk_pos_y = MAX_Y;//0 + break; + case K_ENTER: + if (max_len > strlen(osk_buffer)) { + char *selected_line = osk_text[osk_pos_y]; + char selected_char[2]; + + selected_char[0] = selected_line[1+(2*osk_pos_x)]; + + if (selected_char[0] == '\t') + selected_char[0] = ' '; + + selected_char[1] = '\0'; + strcat(osk_buffer,selected_char); + } + break; + case K_DEL: + if (strlen(osk_buffer) > 0) { + osk_buffer[strlen(osk_buffer)-1] = '\0'; + } + break; + case K_INS: + strncpy(osk_out_buff,osk_buffer,max_len); + + m_state = m_old_state; + break; + case K_ESCAPE: + m_state = m_old_state; + break; + default: + break; + } +} + +void Con_OSK_Key (int key)////blubswillrule: making console cursor wrap around +{ + switch (key) + { + case K_RIGHTARROW: + osk_pos_x++; + if (osk_pos_x > MAX_X) + osk_pos_x = 0;//MAX_X + break; + case K_LEFTARROW: + osk_pos_x--; + if (osk_pos_x < 0) + osk_pos_x = MAX_X;//0 + break; + case K_DOWNARROW: + osk_pos_y++; + if (osk_pos_y > MAX_Y) + osk_pos_y = 0;//MAX_Y + break; + case K_UPARROW: + osk_pos_y--; + if (osk_pos_y < 0) + osk_pos_y = MAX_Y;//0 + break; + case K_ENTER: + if (max_len > strlen(osk_buffer)) { + char *selected_line = osk_text[osk_pos_y]; + char selected_char[2]; + + selected_char[0] = selected_line[1+(2*osk_pos_x)]; + + if (selected_char[0] == '\t') + selected_char[0] = ' '; + + selected_char[1] = '\0'; + strcat(osk_buffer,selected_char); + } + break; + case K_DEL: + if (strlen(osk_buffer) > 0) { + osk_buffer[strlen(osk_buffer)-1] = '\0'; + } + break; + case K_INS: + strncpy(osk_out_buff,osk_buffer,max_len); + Con_SetOSKActive(false); + break; + case K_ESCAPE: + Con_SetOSKActive(false); + break; + default: + break; + } +} + +//============================================================================= + +/* SERIAL CONFIG MENU */ + +int serialConfig_cursor; +int serialConfig_cursor_table[] = {48, 64, 80, 96, 112, 132}; +#define NUM_SERIALCONFIG_CMDS 6 + +static int ISA_uarts[] = {0x3f8,0x2f8,0x3e8,0x2e8}; +static int ISA_IRQs[] = {4,3,4,3}; +int serialConfig_baudrate[] = {9600,14400,19200,28800,38400,57600}; + +int serialConfig_comport; +int serialConfig_irq ; +int serialConfig_baud; +char serialConfig_phone[16]; + +void M_Menu_SerialConfig_f (void) +{ + int n; + int port; + int baudrate; + qboolean useModem; + + key_dest = key_menu; + m_state = m_serialconfig; + m_entersound = true; + if (JoiningGame && SerialConfig) + serialConfig_cursor = 4; + else + serialConfig_cursor = 5; + + (*GetComPortConfig) (0, &port, &serialConfig_irq, &baudrate, &useModem); + + // map uart's port to COMx + for (n = 0; n < 4; n++) + if (ISA_uarts[n] == port) + break; + if (n == 4) + { + n = 0; + serialConfig_irq = 4; + } + serialConfig_comport = n + 1; + + // map baudrate to index + for (n = 0; n < 6; n++) + if (serialConfig_baudrate[n] == baudrate) + break; + if (n == 6) + n = 5; + serialConfig_baud = n; + + m_return_onerror = false; + m_return_reason[0] = 0; +} + + +void M_SerialConfig_Draw (void) +{ + int basex; + char *startJoin; + char *directModem; + + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + //else + //Draw_AlphaPic (0, 0, pause_bk, 0.4); + + basex = (320)/2; + + if (StartingGame) + startJoin = "New Game"; + else + startJoin = "Join Game"; + if (SerialConfig) + directModem = "Modem"; + else + directModem = "Direct Connect"; + M_PrintOld (basex, 32, va ("%s - %s", startJoin, directModem)); + basex += 8; + + M_PrintOld (basex, serialConfig_cursor_table[0], "Port"); + M_DrawTextBox (160, 40, 4, 1); + M_PrintOld (168, serialConfig_cursor_table[0], va("COM%u", serialConfig_comport)); + + M_PrintOld (basex, serialConfig_cursor_table[1], "IRQ"); + M_DrawTextBox (160, serialConfig_cursor_table[1]-8, 1, 1); + M_PrintOld (168, serialConfig_cursor_table[1], va("%u", serialConfig_irq)); + + M_PrintOld (basex, serialConfig_cursor_table[2], "Baud"); + M_DrawTextBox (160, serialConfig_cursor_table[2]-8, 5, 1); + M_PrintOld (168, serialConfig_cursor_table[2], va("%u", serialConfig_baudrate[serialConfig_baud])); + + if (SerialConfig) + { + M_PrintOld (basex, serialConfig_cursor_table[3], "Modem Setup..."); + if (JoiningGame) + { + M_PrintOld (basex, serialConfig_cursor_table[4], "Phone number"); + M_DrawTextBox (160, serialConfig_cursor_table[4]-8, 16, 1); + M_PrintOld (168, serialConfig_cursor_table[4], serialConfig_phone); + } + } + + if (JoiningGame) + { + M_DrawTextBox (basex, serialConfig_cursor_table[5]-8, 7, 1); + M_PrintOld (basex+8, serialConfig_cursor_table[5], "Connect"); + } + else + { + M_DrawTextBox (basex, serialConfig_cursor_table[5]-8, 2, 1); + M_PrintOld (basex+8, serialConfig_cursor_table[5], "OK"); + } + + M_DrawCharacter2 (basex-8, serialConfig_cursor_table [serialConfig_cursor], 12+((int)(realtime*4)&1)); + + if (serialConfig_cursor == 4) + M_DrawCharacter2 (168 + 8*strlen(serialConfig_phone), serialConfig_cursor_table [serialConfig_cursor], 10+((int)(realtime*4)&1)); + + if (*m_return_reason) + M_PrintWhiteOld (basex, 148, m_return_reason); +} + + +void M_SerialConfig_Key (int key) +{ + int l; + + switch (key) + { + case K_ESCAPE: + M_Menu_Net_f (); + break; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + serialConfig_cursor--; + if (serialConfig_cursor < 0) + serialConfig_cursor = NUM_SERIALCONFIG_CMDS-1; + break; + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + serialConfig_cursor++; + if (serialConfig_cursor >= NUM_SERIALCONFIG_CMDS) + serialConfig_cursor = 0; + break; + + case K_LEFTARROW: + if (serialConfig_cursor > 2) + break; + S_LocalSound ("sounds/menu/navigate.wav"); + + if (serialConfig_cursor == 0) + { + serialConfig_comport--; + if (serialConfig_comport == 0) + serialConfig_comport = 4; + serialConfig_irq = ISA_IRQs[serialConfig_comport-1]; + } + + if (serialConfig_cursor == 1) + { + serialConfig_irq--; + if (serialConfig_irq == 6) + serialConfig_irq = 5; + if (serialConfig_irq == 1) + serialConfig_irq = 7; + } + + if (serialConfig_cursor == 2) + { + serialConfig_baud--; + if (serialConfig_baud < 0) + serialConfig_baud = 5; + } + + break; + + case K_RIGHTARROW: + if (serialConfig_cursor > 2) + break; +forward: + S_LocalSound ("sounds/menu/navigate.wav"); + + if (serialConfig_cursor == 0) + { + serialConfig_comport++; + if (serialConfig_comport > 4) + serialConfig_comport = 1; + serialConfig_irq = ISA_IRQs[serialConfig_comport-1]; + } + + if (serialConfig_cursor == 1) + { + serialConfig_irq++; + if (serialConfig_irq == 6) + serialConfig_irq = 7; + if (serialConfig_irq == 8) + serialConfig_irq = 2; + } + + if (serialConfig_cursor == 2) + { + serialConfig_baud++; + if (serialConfig_baud > 5) + serialConfig_baud = 0; + } + + break; + + case K_ENTER: + if (serialConfig_cursor < 3) + goto forward; + + m_entersound = true; + + if (serialConfig_cursor == 3) + { + (*SetComPortConfig) (0, ISA_uarts[serialConfig_comport-1], serialConfig_irq, serialConfig_baudrate[serialConfig_baud], SerialConfig); + + M_Menu_ModemConfig_f (); + break; + } + + if (serialConfig_cursor == 4) + { + serialConfig_cursor = 5; + break; + } + + // serialConfig_cursor == 5 (OK/CONNECT) + (*SetComPortConfig) (0, ISA_uarts[serialConfig_comport-1], serialConfig_irq, serialConfig_baudrate[serialConfig_baud], SerialConfig); + + M_ConfigureNetSubsystem (); + + if (StartingGame) + { + M_Menu_GameOptions_f (); + break; + } + + m_return_state = m_state; + m_return_onerror = true; + key_dest = key_game; + m_state = m_none; + + if (SerialConfig) + Cbuf_AddText (va ("connect \"%s\"\n", serialConfig_phone)); + else + Cbuf_AddText ("connect\n"); + break; + + case K_BACKSPACE: + if (serialConfig_cursor == 4) + { + if (strlen(serialConfig_phone)) + serialConfig_phone[strlen(serialConfig_phone)-1] = 0; + } + break; + + default: + if (key < 32 || key > 127) + break; + if (serialConfig_cursor == 4) + { + l = strlen(serialConfig_phone); + if (l < 15) + { + serialConfig_phone[l+1] = 0; + serialConfig_phone[l] = key; + } + } + } + + if (DirectConfig && (serialConfig_cursor == 3 || serialConfig_cursor == 4)) + { + if (key == K_UPARROW) + serialConfig_cursor = 2; + else + serialConfig_cursor = 5; + } + if (SerialConfig && StartingGame && serialConfig_cursor == 4) + { + if (key == K_UPARROW) + serialConfig_cursor = 3; + else + serialConfig_cursor = 5; + } +} + +//============================================================================= +/* MODEM CONFIG MENU */ + +int modemConfig_cursor; +int modemConfig_cursor_table [] = {40, 56, 88, 120, 156}; +#define NUM_MODEMCONFIG_CMDS 5 + +char modemConfig_dialing; +char modemConfig_clear [16]; +char modemConfig_init [32]; +char modemConfig_hangup [16]; + +void M_Menu_ModemConfig_f (void) +{ + key_dest = key_menu; + m_state = m_modemconfig; + m_entersound = true; + (*GetModemConfig) (0, &modemConfig_dialing, modemConfig_clear, modemConfig_init, modemConfig_hangup); +} + + +void M_ModemConfig_Draw (void) +{ + int basex; + + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + //else + //Draw_AlphaPic (0, 0, pause_bk, 0.4); + + basex = (320)/2; + basex += 8; + + if (modemConfig_dialing == 'P') + M_PrintOld (basex, modemConfig_cursor_table[0], "Pulse Dialing"); + else + M_PrintOld (basex, modemConfig_cursor_table[0], "Touch Tone Dialing"); + + M_PrintOld (basex, modemConfig_cursor_table[1], "Clear"); + M_DrawTextBox (basex, modemConfig_cursor_table[1]+4, 16, 1); + M_PrintOld (basex+8, modemConfig_cursor_table[1]+12, modemConfig_clear); + if (modemConfig_cursor == 1) + M_DrawCharacter2 (basex+8 + 8*strlen(modemConfig_clear), modemConfig_cursor_table[1]+12, 10+((int)(realtime*4)&1)); + + M_PrintOld (basex, modemConfig_cursor_table[2], "Init"); + M_DrawTextBox (basex, modemConfig_cursor_table[2]+4, 30, 1); + M_PrintOld (basex+8, modemConfig_cursor_table[2]+12, modemConfig_init); + if (modemConfig_cursor == 2) + M_DrawCharacter2 (basex+8 + 8*strlen(modemConfig_init), modemConfig_cursor_table[2]+12, 10+((int)(realtime*4)&1)); + + M_PrintOld (basex, modemConfig_cursor_table[3], "Hangup"); + M_DrawTextBox (basex, modemConfig_cursor_table[3]+4, 16, 1); + M_PrintOld (basex+8, modemConfig_cursor_table[3]+12, modemConfig_hangup); + if (modemConfig_cursor == 3) + M_DrawCharacter2 (basex+8 + 8*strlen(modemConfig_hangup), modemConfig_cursor_table[3]+12, 10+((int)(realtime*4)&1)); + + M_DrawTextBox (basex, modemConfig_cursor_table[4]-8, 2, 1); + M_PrintOld (basex+8, modemConfig_cursor_table[4], "OK"); + + M_DrawCharacter2 (basex-8, modemConfig_cursor_table [modemConfig_cursor], 12+((int)(realtime*4)&1)); +} + + +void M_ModemConfig_Key (int key) +{ + int l; + + switch (key) + { + case K_ESCAPE: + M_Menu_SerialConfig_f (); + break; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + modemConfig_cursor--; + if (modemConfig_cursor < 0) + modemConfig_cursor = NUM_MODEMCONFIG_CMDS-1; + break; + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + modemConfig_cursor++; + if (modemConfig_cursor >= NUM_MODEMCONFIG_CMDS) + modemConfig_cursor = 0; + break; + + case K_LEFTARROW: + case K_RIGHTARROW: + if (modemConfig_cursor == 0) + { + if (modemConfig_dialing == 'P') + modemConfig_dialing = 'T'; + else + modemConfig_dialing = 'P'; + S_LocalSound ("sounds/menu/navigate.wav"); + } + break; + + case K_ENTER: + if (modemConfig_cursor == 0) + { + if (modemConfig_dialing == 'P') + modemConfig_dialing = 'T'; + else + modemConfig_dialing = 'P'; + m_entersound = true; + } + + if (modemConfig_cursor == 4) + { + (*SetModemConfig) (0, va ("%c", modemConfig_dialing), modemConfig_clear, modemConfig_init, modemConfig_hangup); + m_entersound = true; + M_Menu_SerialConfig_f (); + } + break; + + case K_BACKSPACE: + if (modemConfig_cursor == 1) + { + if (strlen(modemConfig_clear)) + modemConfig_clear[strlen(modemConfig_clear)-1] = 0; + } + + if (modemConfig_cursor == 2) + { + if (strlen(modemConfig_init)) + modemConfig_init[strlen(modemConfig_init)-1] = 0; + } + + if (modemConfig_cursor == 3) + { + if (strlen(modemConfig_hangup)) + modemConfig_hangup[strlen(modemConfig_hangup)-1] = 0; + } + break; + + default: + if (key < 32 || key > 127) + break; + + if (modemConfig_cursor == 1) + { + l = strlen(modemConfig_clear); + if (l < 15) + { + modemConfig_clear[l+1] = 0; + modemConfig_clear[l] = key; + } + } + + if (modemConfig_cursor == 2) + { + l = strlen(modemConfig_init); + if (l < 29) + { + modemConfig_init[l+1] = 0; + modemConfig_init[l] = key; + } + } + + if (modemConfig_cursor == 3) + { + l = strlen(modemConfig_hangup); + if (l < 15) + { + modemConfig_hangup[l+1] = 0; + modemConfig_hangup[l] = key; + } + } + } +} + +//============================================================================= +/* LAN CONFIG MENU */ + +int lanConfig_cursor = -1; +int lanConfig_cursor_table [] = {72, 92, 124}; +#define NUM_LANCONFIG_CMDS 3 + +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 = 2; + else + lanConfig_cursor = 1; + } + if (StartingGame && lanConfig_cursor == 2) + lanConfig_cursor = 1; + lanConfig_port = DEFAULTnet_hostport; + sprintf(lanConfig_portname, "%u", lanConfig_port); + + m_return_onerror = false; + m_return_reason[0] = 0; +} + + +void M_LanConfig_Draw (void) +{ + int basex; + char *startJoin; + char *protocol; + + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + //else + //Draw_AlphaPic (0, 0, pause_bk, 0.4); + + basex = (320)/2; + + if (StartingGame) + startJoin = "New Game"; + else + startJoin = "Join Game"; + if (IPXConfig) + protocol = "IPX"; + else + protocol = "TCP/IP"; + M_PrintOld (basex, 32, va ("%s - %s", startJoin, protocol)); + basex += 8; + + M_PrintOld (basex, 52, "Address:"); + if (IPXConfig) + M_PrintOld (basex+9*8, 52, my_ipx_address); + else + M_PrintOld (basex+9*8, 52, my_tcpip_address); + + M_PrintOld (basex, lanConfig_cursor_table[0], "Port"); + M_DrawTextBox (basex+8*8, lanConfig_cursor_table[0]-8, 6, 1); + M_PrintOld (basex+9*8, lanConfig_cursor_table[0], lanConfig_portname); + + if (JoiningGame) + { + M_PrintOld (basex, lanConfig_cursor_table[1], "Search for local games..."); + M_PrintOld (basex, 108, "Join game at:"); + M_DrawTextBox (basex+8, lanConfig_cursor_table[2]-8, 22, 1); + M_PrintOld (basex+16, lanConfig_cursor_table[2], lanConfig_joinname); + } + else + { + M_DrawTextBox (basex, lanConfig_cursor_table[1]-8, 2, 1); + M_PrintOld (basex+8, lanConfig_cursor_table[1], "OK"); + } + + M_DrawCharacter2 (basex-8, lanConfig_cursor_table [lanConfig_cursor], 12+((int)(realtime*4)&1)); + + if (lanConfig_cursor == 0) + M_DrawCharacter2 (basex+9*8 + 8*strlen(lanConfig_portname), lanConfig_cursor_table [0], 10+((int)(realtime*4)&1)); + + if (lanConfig_cursor == 2) + M_DrawCharacter2 (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [2], 10+((int)(realtime*4)&1)); + + if (*m_return_reason) + M_PrintWhiteOld (basex, 148, m_return_reason); +} + + +void M_LanConfig_Key (int key) +{ + int l; + + switch (key) + { + case K_ESCAPE: + M_Menu_Net_f (); + break; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + lanConfig_cursor--; + if (lanConfig_cursor < 0) + lanConfig_cursor = NUM_LANCONFIG_CMDS-1; + break; + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + lanConfig_cursor++; + if (lanConfig_cursor >= NUM_LANCONFIG_CMDS) + lanConfig_cursor = 0; + break; + + case K_INS: + if (lanConfig_cursor == 0) + { + M_Menu_OSK_f(lanConfig_portname, lanConfig_portname, 6); + break; + } + + if (lanConfig_cursor == 2) + { + M_Menu_OSK_f(lanConfig_joinname, lanConfig_joinname, 22); + break; + } + break; + + case K_ENTER: + if (lanConfig_cursor == 0) + break; + + m_entersound = true; + + M_ConfigureNetSubsystem (); + + if (lanConfig_cursor == 1) + { + if (StartingGame) + { + M_Menu_GameOptions_f (); + break; + } + M_Menu_Search_f(); + break; + } + + if (lanConfig_cursor == 2) + { + 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; + } + + break; + + case K_BACKSPACE: + if (lanConfig_cursor == 0) + { + if (strlen(lanConfig_portname)) + lanConfig_portname[strlen(lanConfig_portname)-1] = 0; + } + + if (lanConfig_cursor == 2) + { + if (strlen(lanConfig_joinname)) + lanConfig_joinname[strlen(lanConfig_joinname)-1] = 0; + } + break; + + default: + if (key < 32 || key > 127) + break; + + if (lanConfig_cursor == 2) + { + 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 == 2) + { + if (key == K_UPARROW) + lanConfig_cursor = 1; + else + lanConfig_cursor = 0; + } + + l = Q_atoi(lanConfig_portname); + if (l > 65535) + l = lanConfig_port; + else + lanConfig_port = l; + sprintf(lanConfig_portname, "%u", lanConfig_port); +} + +//============================================================================= +/* 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"} +}; + +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} +}; + +int startepisode; +int startlevel; +int maxplayers; +qboolean m_serverInfoMessage = false; +double m_serverInfoMessageTime; + +//==================== Map Find System By Crow_bar ============================= + +void Map_Finder(void) +{ + +#ifdef KERNEL_MODE + SceUID dir = sceIoDopen(va("%s/maps", com_gamedir)); +#else + SceUID dir = sceIoDopen(va("nzp/maps")); +#endif // KERNEL_MODE + + if(dir < 0) + { + Sys_Error ("Map_Finder"); + return; + } + + SceIoDirent dirent; + + memset(&dirent, 0, sizeof(SceIoDirent)); + + for (int i = 0; i < 50; i++) { + custom_maps[i].occupied = false; + } + + while(sceIoDread(dir, &dirent) > 0) + { + if(dirent.d_name[0] == '.') + { + continue; + } + + if(!strcmp(COM_FileExtension(dirent.d_name),"bsp")|| !strcmp(COM_FileExtension(dirent.d_name),"BSP")) + { + char ntype[32]; + + COM_StripExtension(dirent.d_name, ntype); + custom_maps[user_maps_num].occupied = true; + custom_maps[user_maps_num].map_name = malloc(sizeof(char)*32); + sprintf(custom_maps[user_maps_num].map_name, "%s", ntype); + + char* setting_path; + int setting_file; + SceIoStat setting_info; + + setting_path = malloc(sizeof(char)*64); + custom_maps[user_maps_num].map_thumbnail_path = malloc(sizeof(char)*64); +#ifdef KERNEL_MODE + strcpy(setting_path, va("%s/maps/", com_gamedir)); +#else + strcpy(setting_path, va("nzp/maps/")); +#endif // KERNEL_MODE + strcpy(custom_maps[user_maps_num].map_thumbnail_path, "gfx/menu/custom/"); + strcat(setting_path, custom_maps[user_maps_num].map_name); + strcat(custom_maps[user_maps_num].map_thumbnail_path, custom_maps[user_maps_num].map_name); + strcat(setting_path, ".txt"); + + sceIoGetstat(setting_path, &setting_info); + setting_file = sceIoOpen(setting_path, PSP_O_RDONLY, 0); + + if (setting_file >= 0) { + + int state; + state = 0; + int value; + + custom_maps[user_maps_num].map_name_pretty = malloc(sizeof(char)*32); + custom_maps[user_maps_num].map_desc_1 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_desc_2 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_desc_3 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_desc_4 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_desc_5 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_desc_6 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_desc_7 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_desc_8 = malloc(sizeof(char)*40); + custom_maps[user_maps_num].map_author = malloc(sizeof(char)*40); + + char* buffer = (char*)calloc(setting_info.st_size+1, sizeof(char)); + sceIoRead(setting_file, buffer, setting_info.st_size); + + strtok(buffer, "\n"); + while(buffer != NULL) { + switch(state) { + case 0: strcpy(custom_maps[user_maps_num].map_name_pretty, buffer); break; + case 1: strcpy(custom_maps[user_maps_num].map_desc_1, buffer); break; + case 2: strcpy(custom_maps[user_maps_num].map_desc_2, buffer); break; + case 3: strcpy(custom_maps[user_maps_num].map_desc_3, buffer); break; + case 4: strcpy(custom_maps[user_maps_num].map_desc_4, buffer); break; + case 5: strcpy(custom_maps[user_maps_num].map_desc_5, buffer); break; + case 6: strcpy(custom_maps[user_maps_num].map_desc_6, buffer); break; + case 7: strcpy(custom_maps[user_maps_num].map_desc_7, buffer); break; + case 8: strcpy(custom_maps[user_maps_num].map_desc_8, buffer); break; + case 9: strcpy(custom_maps[user_maps_num].map_author, buffer); break; + case 10: value = 0; sscanf(buffer, "%d", &value); custom_maps[user_maps_num].map_use_thumbnail = value; break; + case 11: value = 0; sscanf(buffer, "%d", &value); custom_maps[user_maps_num].map_allow_game_settings = value; break; + default: break; + } + state++; + buffer = strtok(NULL, "\n"); + } + free(buffer); + buffer = 0; + sceIoClose(setting_file); + } + user_maps_num++; + } + memset(&dirent, 0, sizeof(SceIoDirent)); + } + sceIoDclose(dir); + + custom_map_pages = (int)ceil((double)(user_maps_num + 1)/15); +} +//============================================================================== +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) +{ + int x; + + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + //else + //Draw_AlphaPic (0, 0, pause_bk, 0.4); + + M_DrawTextBox (152, 32, 10, 1); + M_PrintOld (160, 40, "begin game"); + + M_PrintOld (0, 56, " Max players"); + M_PrintOld (160, 56, va("%i", maxplayers) ); + + M_PrintOld (0, 64, " Game Type"); + if (coop.value) + M_PrintOld (160, 64, "Cooperative"); + else + M_PrintOld (160, 64, "Deathmatch"); + + M_PrintOld (0, 72, " Teamplay"); + + char *msg; + + switch((int)teamplay.value) + { + case 1: msg = "No Friendly Fire"; break; + case 2: msg = "Friendly Fire"; break; + default: msg = "Off"; break; + } + M_PrintOld (160, 72, msg); + + + M_PrintOld (0, 80, " Skill"); + if (skill.value == 0) + M_PrintOld (160, 80, "Easy difficulty"); + else if (skill.value == 1) + M_PrintOld (160, 80, "Normal difficulty"); + else if (skill.value == 2) + M_PrintOld (160, 80, "Hard difficulty"); + else + M_PrintOld (160, 80, "Nightmare difficulty"); + + M_PrintOld (0, 88, " Frag Limit"); + if (fraglimit.value == 0) + M_PrintOld (160, 88, "none"); + else + M_PrintOld (160, 88, va("%i points", (int)fraglimit.value)); + + M_PrintOld (0, 96, " Time Limit"); + if (timelimit.value == 0) + M_PrintOld (160, 96, "none"); + else + M_PrintOld (160, 96, va("%i minutes", (int)timelimit.value)); + + M_PrintOld (0, 112, " Episode"); + + if(user_maps) + M_PrintOld (160, 112, "User Maps"); + else + M_PrintOld (160, 112, episodes[startepisode].description); + + M_PrintOld (0, 120, " Level"); + + if(user_maps) + { + M_PrintOld (160, 120, user_levels[startlevel]); + } + else + { + M_PrintOld (160, 120, levels[episodes[startepisode].firstLevel + startlevel].description); + M_PrintOld (160, 128, levels[episodes[startepisode].firstLevel + startlevel].name); + } + +// line cursor + M_DrawCharacter2 (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; + M_DrawTextBox (x, 138, 24, 4); + x += 8; + M_PrintOld (x, 146, " More than 4 players "); + M_PrintOld (x, 154, " requires using command "); + M_PrintOld (x, 162, "line parameters; please "); + M_PrintOld (x, 170, " see techinfo.txt. "); + } + else + { + m_serverInfoMessage = false; + } + } +} + + +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: + 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; + if (user_maps) + { + count = 1; + } + else + { + count = 2; + } + if (startepisode < 0) + startepisode = count - 1; + + if (startepisode >= count) + startepisode = 0; + + startlevel = 0; + break; + + case 8: + startlevel += dir; + if(user_maps) + count = user_maps_num; + 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_ESCAPE: + M_Menu_Net_f (); + break; + + case K_UPARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + gameoptions_cursor--; + if (gameoptions_cursor < 0) + gameoptions_cursor = NUM_GAMEOPTIONS-1; + break; + + case K_DOWNARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + gameoptions_cursor++; + if (gameoptions_cursor >= NUM_GAMEOPTIONS) + gameoptions_cursor = 0; + break; + + case K_LEFTARROW: + if (gameoptions_cursor == 0) + break; + S_LocalSound ("sounds/menu/navigate.wav"); + M_NetStart_Change (-1); + break; + + case K_RIGHTARROW: + if (gameoptions_cursor == 0) + break; + S_LocalSound ("sounds/menu/navigate.wav"); + M_NetStart_Change (1); + break; + + case K_ENTER: + S_LocalSound ("sounds/menu/enter.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(user_maps) + Cbuf_AddText ( va ("map %s\n", user_levels[startlevel])); + else + Cbuf_AddText ( va ("map %s\n", levels[episodes[startepisode].firstLevel + startlevel].name) ); + return; + } + + M_NetStart_Change (1); + break; + } +} + +//============================================================================= +/* SEARCH MENU */ + +qboolean 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) +{ + int x; + + if (key_dest != key_menu_pause) + Draw_Pic (0, 0, menu_bk); + //else + //Draw_AlphaPic (0, 0, pause_bk, 0.4); + + x = (320/2) - ((12*8)/2) + 4; + M_DrawTextBox (x-8, 32, 12, 1); + M_PrintOld (x, 40, "Searching..."); + + if(slistInProgress) + { + NET_Poll(); + return; + } + + if (! searchComplete) + { + searchComplete = true; + searchCompleteTime = realtime; + } + + if (hostCacheCount) + { + M_Menu_ServerList_f (); + return; + } + + M_PrintWhiteOld ((320/2) - ((22*8)/2), 64, "No Quake servers found"); + if ((realtime - searchCompleteTime) < 3.0) + return; + + M_Menu_LanConfig_f (); +} + + +void M_Search_Key (int key) +{ +} + +//============================================================================= +/* SLIST MENU + +int slist_cursor; +qboolean 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) + { + Q_memcpy(&temp, &hostcache[j], sizeof(hostcache_t)); + Q_memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t)); + Q_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_PrintOld (16, 32 + 8*n, string); + } + M_DrawCharacter2 (0, 32 + slist_cursor*8, 12+((int)(realtime*4)&1)); + + if (*m_return_reason) + M_PrintWhiteOld (16, 148, m_return_reason); +} + + +void M_ServerList_Key (int k) +{ + switch (k) + { + case K_ESCAPE: + M_Menu_LanConfig_f (); + break; + + case K_SPACE: + M_Menu_Search_f (); + break; + + case K_UPARROW: + case K_LEFTARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + slist_cursor--; + if (slist_cursor < 0) + slist_cursor = hostCacheCount - 1; + break; + + case K_DOWNARROW: + case K_RIGHTARROW: + S_LocalSound ("sounds/menu/navigate.wav"); + slist_cursor++; + if (slist_cursor >= hostCacheCount) + slist_cursor = 0; + break; + + case K_ENTER: + S_LocalSound ("sounds/menu/enter.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_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_slist", M_Menu_ServerList_f); + Cmd_AddCommand ("credits", M_Menu_Credits_f); + Cmd_AddCommand ("menu_quit", M_Menu_Quit_f); + Cmd_AddCommand ("savea", Save_Achivements); + Cmd_AddCommand ("loada", Load_Achivements); + + Map_Finder(); + +} + + +void M_Draw (void) +{ + if (m_state == m_none || (key_dest != key_menu && key_dest != key_menu_pause)) + return; + + if (!m_recursiveDraw) + { + scr_copyeverything = 1; + + if (scr_con_current) + { + Draw_ConsoleBackground (vid.height); + VID_UnlockBuffer (); + S_ExtraUpdate (); + VID_LockBuffer (); + } + else + Draw_FadeScreen (); + + scr_fullupdate = 0; + } + else + { + m_recursiveDraw = false; + } + + switch (m_state) + { + case m_none: + break; + + case m_start: + M_Start_Menu_Draw(); + break; + + case m_paused_menu: + M_Paused_Menu_Draw(); + break; + + case m_main: + M_Main_Draw (); + break; + + case m_map: + M_Map_Draw (); + break; + + case m_singleplayer: + M_SinglePlayer_Draw (); + break; + + case m_multiplayer: + M_MultiPlayer_Draw (); + break; + + case m_achievement: + M_Achievement_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_keys: + M_Keys_Draw (); + break; + + case m_video: + M_Screen_Draw (); + break; + + case m_audio: + M_Audio_Draw (); + break; + + case m_gameplay: + M_Gameplay_Draw (); + break; + + case m_credits: + M_Credits_Draw (); + break; + + case m_quit: + M_Quit_Draw (); + break; + + case m_restart: + M_Restart_Draw (); + break; + + case m_exit: + M_Exit_Draw (); + break; + + case m_serialconfig: + M_SerialConfig_Draw (); + break; + + case m_modemconfig: + M_ModemConfig_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_sedit: + M_SEdit_Draw (); + break; + + case m_osk: + M_OSK_Draw(); + break; + } + + if (m_entersound) + { + S_LocalSound ("sounds/menu/enter.wav"); + m_entersound = false; + } + + VID_UnlockBuffer (); + S_ExtraUpdate (); + VID_LockBuffer (); +} + + +void M_Keydown (int key) +{ + switch (m_state) + { + case m_none: + return; + + case m_paused_menu: + M_Paused_Menu_Key (key); + break; + + case m_start: + M_Start_Key (key); + break; + case m_main: + M_Main_Key (key); + return; + + case m_map: + M_Map_Key (key); + return; + + case m_singleplayer: + M_SinglePlayer_Key (key); + return; + + case m_multiplayer: + M_MultiPlayer_Key (key); + return; + + case m_achievement: + M_Achievement_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_keys: + M_Keys_Key (key); + return; + + case m_video: + M_Screen_Key (key); + return; + + case m_audio: + M_Audio_Key (key); + return; + + case m_gameplay: + M_Gameplay_Key (key); + return; + + case m_credits: + M_Credits_Key (key); + return; + + case m_quit: + M_Quit_Key (key); + return; + + case m_restart: + M_Restart_Key (key); + return; + + case m_exit: + M_Exit_Key (key); + return; + + case m_serialconfig: + M_SerialConfig_Key (key); + return; + + case m_modemconfig: + M_ModemConfig_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_sedit: + M_SEdit_Key (key); + break; + + case m_osk: + M_OSK_Key(key); + } +} + + +void M_ConfigureNetSubsystem(void) +{ +// enable/disable net systems to match desired config + + Cbuf_AddText ("stopdemo\n"); + if (SerialConfig || DirectConfig) + { + Cbuf_AddText ("com1 enable\n"); + } + + if (IPXConfig || TCPIPConfig) + net_hostport = lanConfig_port; +} + diff --git a/source/menu.h b/source/menu.h new file mode 100644 index 0000000..e6c36c7 --- /dev/null +++ b/source/menu.h @@ -0,0 +1,40 @@ +/* +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); + + +void Save_Achivements(void); +void Load_Achivements(void); diff --git a/source/modelgen.h b/source/modelgen.h new file mode 100644 index 0000000..6273a8d --- /dev/null +++ b/source/modelgen.h @@ -0,0 +1,161 @@ +/* +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_NEWVERSION 50 + +#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; + +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 num_st_verts; +} newmdl_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; + +typedef struct dnewtriangle_s +{ + int facesfront; + unsigned short vertindex[3]; + unsigned short stindex[3]; +} dnewtriangle_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 { + aliasframetype_t type; +} daliasframetype_t; + +typedef struct { + aliasskintype_t type; +} daliasskintype_t; + +#define IDPOLYHEADER (('O'<<24)+('P'<<16)+('D'<<8)+'I') // little-endian "IDPO" +#define RAPOLYHEADER (('O'<<24)+('P'<<16)+('A'<<8)+'R') // little-endian "RAPO" diff --git a/source/net.h b/source/net.h new file mode 100644 index 0000000..cbead36 --- /dev/null +++ b/source/net.h @@ -0,0 +1,336 @@ +/* +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 +{ + unsigned char sa_len; + unsigned char sa_family; + unsigned char sa_data[14]; +}; + + +#define NET_NAMELEN 64 + +// HACK HACK HACK - certain maps will hit this limit.. no clue why.. so let's just +// double it lol +#define NET_MAXMESSAGE /*8192*/16384 +#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 + +typedef struct qsocket_s +{ + struct qsocket_s *next; + double connecttime; + double lastMessageTime; + double lastSendTime; + + qboolean disconnected; + qboolean canSend; + qboolean 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]; + +} qsocket_t; + +extern qsocket_t *net_activeSockets; +extern qsocket_t *net_freeSockets; +extern int net_numsockets; + +typedef struct +{ + char *name; + qboolean initialized; + int controlSock; + int (*Init) (void); + void (*Shutdown) (void); + void (*Listen) (qboolean 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; + qboolean initialized; + int (*Init) (void); + void (*Listen) (qboolean state); + void (*SearchForHosts) (qboolean 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); + qboolean (*CanSendMessage) (qsocket_t *sock); + qboolean (*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]; + +#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 + +//============================================================================ +// +// 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 + +qboolean 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 qboolean serialAvailable; +extern qboolean ipxAvailable; +extern qboolean tcpipAvailable; +extern qboolean tcpipAdhoc; + +extern char my_ipx_address[NET_NAMELEN]; +extern char my_tcpip_address[NET_NAMELEN]; +extern void (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean *useModem); +extern void (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem); +extern void (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup); +extern void (*SetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup); + +extern qboolean slistInProgress; +extern qboolean slistSilent; +extern qboolean slistLocal; + +void NET_Slist_f (void); diff --git a/source/net_dgrm.c b/source/net_dgrm.c new file mode 100644 index 0000000..8508c7a --- /dev/null +++ b/source/net_dgrm.c @@ -0,0 +1,1286 @@ +/* +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 + + +#include "pspkernel.h" + +#include "quakedef.h" +#include "net_dgrm.h" + +// 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 qboolean m_return_onerror; +extern char m_return_reason[32]; + + +#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 + + + + + +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 + + Q_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++); + Q_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++); + Q_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); + Q_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; +} + + +qboolean Datagram_CanSendMessage (qsocket_t *sock) +{ + if (sock->sendNext) + SendMessageNext (sock); + + return sock->canSend; +} + + +qboolean 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++); + Q_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 0 + if (!sock->canSend) + if ((net_time - sock->lastSendTime) > 1.0) + ReSendMessage (sock); +#endif + + 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; + } + + if (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 (flags & NETFLAG_CTL) + continue; + + sequence = BigLong(packetBuffer.sequence); + packetsReceived++; + + 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) + { + Q_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; + } + + Q_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 (Q_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 (Q_strcasecmp(Cmd_Argv(1), s->address) == 0) + break; + if (s == NULL) + for (s = net_freeSockets; s; s = s->next) + if (Q_strcasecmp(Cmd_Argv(1), s->address) == 0) + break; + if (s == NULL) + return; + PrintStats(s); + } +} + + +static qboolean 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 points; + 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(); + Q_strcpy(name, MSG_ReadString()); + points = MSG_ReadLong(); + connectTime = MSG_ReadLong(); + Q_strcpy(address, MSG_ReadString()); + + } + + 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 (Q_strcasecmp (host, hostcache[n].name) == 0) + { + if (hostcache[n].driver != myDriverLevel) + continue; + net_landriverlevel = hostcache[n].ldriver; + max = hostcache[n].maxusers; + Q_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 qboolean 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; + + Q_strcpy(name, MSG_ReadString()); + if (name[0] == 0) + goto Done; + Q_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 (Q_strcasecmp (host, hostcache[n].name) == 0) + { + if (hostcache[n].driver != myDriverLevel) + continue; + net_landriverlevel = hostcache[n].ldriver; + Q_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 Datagram_Init () +{ + int i; + int csock; + + myDriverLevel = net_driverlevel; + if(!host_initialized) + Cmd_AddCommand ("net_stats", NET_Stats_f); + + if (COM_CheckParm("-nolan")) + return -1; + //0 for Infrastructure + //1 for adhoc for net_driver_to_use + i = net_driver_to_use;//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(!host_initialized) + { + Cmd_AddCommand ("test", Test_f); + Cmd_AddCommand ("test2", Test2_f); + } + + 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 (qboolean 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; + + 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) + { + if (Q_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, hostname.string); + 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, (int)client->edict->v.points); + 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->server) + 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 (Q_strcmp(MSG_ReadString(), "QUAKE") != 0) + return NULL; + + if (MSG_ReadByte() != NET_PROTOCOL_VERSION) + { + 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, "Incompatible version.\n"); + *((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; + } + + // 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; + } + } + + // allocate a QSocket + sock = NET_NewQSocket (); + if (sock == NULL) + { + // no room; try to let him know + 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, "Server is full.\n"); + *((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; + } + + // 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 + sock->socket = newsock; + sock->landriver = net_landriverlevel; + sock->addr = clientaddr; + Q_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); + MSG_WriteLong(&net_message, dfunc.GetSocketPort(&newaddr)); +// MSG_WriteString(&net_message, dfunc.AddrToString(&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 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 (qboolean 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++; + Q_strcpy(hostcache[n].name, MSG_ReadString()); + Q_strcpy(hostcache[n].map, MSG_ReadString()); + hostcache[n].users = MSG_ReadByte(); + hostcache[n].maxusers = MSG_ReadByte(); + if (MSG_ReadByte() != NET_PROTOCOL_VERSION) + { + Q_strcpy(hostcache[n].cname, hostcache[n].name); + hostcache[n].cname[14] = 0; + Q_strcpy(hostcache[n].name, "*"); + Q_strcat(hostcache[n].name, hostcache[n].cname); + } + Q_memcpy(&hostcache[n].addr, &readaddr, sizeof(struct qsockaddr)); + hostcache[n].driver = net_driverlevel; + hostcache[n].ldriver = net_landriverlevel; + Q_strcpy(hostcache[n].cname, dfunc.AddrToString(&readaddr)); + + // check for a name conflict + for (i = 0; i < hostCacheCount; i++) + { + if (i == n) + continue; + if (Q_strcasecmp (hostcache[n].name, hostcache[i].name) == 0) + { + i = Q_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 (qboolean 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); + *((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; + } + } + sceKernelDelayThread(10); + } + while (ret == 0 && (SetNetTime() - start_time) < 5.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); + Q_strcpy(m_return_reason, reason); + goto ErrorReturn; + } + + if (ret == -1) + { + reason = "Network Error"; + Con_Printf("%s\n", reason); + Q_strcpy(m_return_reason, reason); + goto ErrorReturn; + } + + ret = MSG_ReadByte(); + if (ret == CCREP_REJECT) + { + reason = MSG_ReadString(); + Con_Printf(reason); + Q_strncpy(m_return_reason, reason, 31); + goto ErrorReturn; + } + + if (ret == CCREP_ACCEPT) + { + Q_memcpy(&sock->addr, &sendaddr, sizeof(struct qsockaddr)); + dfunc.SetSocketPort (&sock->addr, MSG_ReadLong()); + } + else + { + reason = "Bad Response"; + Con_Printf("%s\n", reason); + Q_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); + Q_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; + + 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; +} diff --git a/source/net_dgrm.h b/source/net_dgrm.h new file mode 100644 index 0000000..4c1745a --- /dev/null +++ b/source/net_dgrm.h @@ -0,0 +1,35 @@ +/* +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 + +int net_driver_to_use; + +int Datagram_Init (void); +void Datagram_Listen (qboolean state); +void Datagram_SearchForHosts (qboolean 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); +qboolean Datagram_CanSendMessage (qsocket_t *sock); +qboolean Datagram_CanSendUnreliableMessage (qsocket_t *sock); +void Datagram_Close (qsocket_t *sock); +void Datagram_Shutdown (void); diff --git a/source/net_loop.c b/source/net_loop.c new file mode 100644 index 0000000..35aa370 --- /dev/null +++ b/source/net_loop.c @@ -0,0 +1,245 @@ +/* +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" + +qboolean 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 (qboolean state) +{ +} + + +void Loop_SearchForHosts (qboolean xmit) +{ + if (!sv.active) + return; + + hostCacheCount = 1; + if (Q_strcmp(hostname.string, "UNNAMED") == 0) + Q_strcpy(hostcache[0].name, "local"); + else + Q_strcpy(hostcache[0].name, hostname.string); + Q_strcpy(hostcache[0].map, sv.name); + hostcache[0].users = net_activeconnections; + hostcache[0].maxusers = svs.maxclients; + hostcache[0].driver = net_driverlevel; + Q_strcpy(hostcache[0].cname, "local"); +} + + +qsocket_t *Loop_Connect (char *host) +{ + if (Q_strcmp(host,"local") != 0) + return NULL; + + localconnectpending = true; + + if (!loop_client) + { + if ((loop_client = NET_NewQSocket ()) == NULL) + { + Con_Printf("Loop_Connect: no qsocket available\n"); + return NULL; + } + Q_strcpy (loop_client->address, "localhost"); + } + loop_client->receiveMessageLength = 0; + loop_client->sendMessageLength = 0; + loop_client->canSend = true; + + if (!loop_server) + { + if ((loop_server = NET_NewQSocket ()) == NULL) + { + Con_Printf("Loop_Connect: no qsocket available\n"); + return NULL; + } + Q_strcpy (loop_server->address, "LOCAL"); + } + loop_server->receiveMessageLength = 0; + loop_server->sendMessageLength = 0; + loop_server->canSend = true; + + 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) + Q_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 + Q_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 + Q_memcpy(buffer, data->data, data->cursize); + *bufferLength = IntAlign(*bufferLength + data->cursize + 4); + return 1; +} + + +qboolean Loop_CanSendMessage (qsocket_t *sock) +{ + if (!sock->driverdata) + return false; + return sock->canSend; +} + + +qboolean 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; +} diff --git a/source/net_loop.h b/source/net_loop.h new file mode 100644 index 0000000..90cdb2c --- /dev/null +++ b/source/net_loop.h @@ -0,0 +1,33 @@ +/* +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 (qboolean state); +void Loop_SearchForHosts (qboolean 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); +qboolean Loop_CanSendMessage (qsocket_t *sock); +qboolean Loop_CanSendUnreliableMessage (qsocket_t *sock); +void Loop_Close (qsocket_t *sock); +void Loop_Shutdown (void); diff --git a/source/net_main.c b/source/net_main.c new file mode 100644 index 0000000..4ee0a62 --- /dev/null +++ b/source/net_main.c @@ -0,0 +1,987 @@ +/* +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" + +qsocket_t *net_activeSockets = NULL; +qsocket_t *net_freeSockets = NULL; +int net_numsockets = 0; + +qboolean serialAvailable = false; +qboolean ipxAvailable = false; +qboolean tcpipAvailable = false; +qboolean tcpipAdhoc = false; + +int net_hostport; +int DEFAULTnet_hostport = 26000; + +char my_ipx_address[NET_NAMELEN]; +char my_tcpip_address[NET_NAMELEN]; + +void (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean *useModem); +void (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem); +void (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup); +void (*SetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup); + +static qboolean listening = false; + +qboolean slistInProgress = false; +qboolean slistSilent = false; +qboolean 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; + +cvar_t net_messagetimeout = {"net_messagetimeout","300"}; +cvar_t hostname = {"hostname", "Unnamed Server"}; + +qboolean configRestored = false; +cvar_t config_com_port = {"_config_com_port", "0x3f8", true}; +cvar_t config_com_irq = {"_config_com_irq", "4", true}; +cvar_t config_com_baud = {"_config_com_baud", "57600", true}; +cvar_t config_com_modem = {"_config_com_modem", "1", true}; +cvar_t config_modem_dialtype = {"_config_modem_dialtype", "T", true}; +cvar_t config_modem_clear = {"_config_modem_clear", "ATZ", true}; +cvar_t config_modem_init = {"_config_modem_init", "", true}; +cvar_t config_modem_hangup = {"_config_modem_hangup", "AT H", true}; + +int vcrFile = -1; +qboolean 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; + Q_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 = Q_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"); + Cvar_Set ("coop", "0"); + } + else + { + if (coop.value) + 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 = Q_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 (Q_strcasecmp (host, "local") == 0) + { + numdrivers = 1; + goto JustDoIt; + } + + if (hostCacheCount) + { + for (n = 0; n < hostCacheCount; n++) + if (Q_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 (Q_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. +================== +*/ +qboolean 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); + } + //Con_Printf("Cansend = %i \n",r);//blubs : this is why it doesn't send the next signonreply + //return r; //blubs, not sure if this will fuck us up later + //if(r != 1) + // Con_Printf("Error, couldn't send msg, r:%i\n",r); + return r; +} + + +int NET_SendToAll(sizebuf_t *data, int blocktime) +{ + double start; + int i; + int count = 0; + qboolean state1 [MAX_SCOREBOARD]; + qboolean 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 = Q_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); + Cvar_RegisterVariable (&config_com_port); + Cvar_RegisterVariable (&config_com_irq); + Cvar_RegisterVariable (&config_com_baud); + Cvar_RegisterVariable (&config_com_modem); + Cvar_RegisterVariable (&config_modem_dialtype); + Cvar_RegisterVariable (&config_modem_clear); + Cvar_RegisterVariable (&config_modem_init); + Cvar_RegisterVariable (&config_modem_hangup); + + if(!host_initialized) + { + 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; + qboolean useModem; + + if (!configRestored) + { + if (serialAvailable) + { + if (config_com_modem.value == 1.0) + useModem = true; + else + useModem = false; + SetComPortConfig (0, (int)config_com_port.value, (int)config_com_irq.value, (int)config_com_baud.value, useModem); + SetModemConfig (0, config_modem_dialtype.string, config_modem_clear.string, config_modem_init.string, config_modem_hangup.string); + } + configRestored = true; + } + + 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; +} + diff --git a/source/net_none.c b/source/net_none.c new file mode 100644 index 0000000..c00c975 --- /dev/null +++ b/source/net_none.c @@ -0,0 +1,46 @@ +/* +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; diff --git a/source/net_vcr.c b/source/net_vcr.c new file mode 100644 index 0000000..ba8f40d --- /dev/null +++ b/source/net_vcr.c @@ -0,0 +1,167 @@ +/* +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 (qboolean 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; +} + + +qboolean VCR_CanSendMessage (qsocket_t *sock) +{ + qboolean 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 (qboolean 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; +} diff --git a/source/net_vcr.h b/source/net_vcr.h new file mode 100644 index 0000000..95c2f34 --- /dev/null +++ b/source/net_vcr.h @@ -0,0 +1,37 @@ +/* +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 (qboolean state); +void VCR_SearchForHosts (qboolean 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); +qboolean VCR_CanSendMessage (qsocket_t *sock); +void VCR_Close (qsocket_t *sock); +void VCR_Shutdown (void); diff --git a/source/nonintel.c b/source/nonintel.c new file mode 100644 index 0000000..9e59039 --- /dev/null +++ b/source/nonintel.c @@ -0,0 +1,64 @@ +/* +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 + diff --git a/source/pr_cmds.c b/source/pr_cmds.c new file mode 100644 index 0000000..80557a3 --- /dev/null +++ b/source/pr_cmds.c @@ -0,0 +1,3462 @@ +/* +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 PR_MAX_TEMPSTRING 2048 // 2001-10-25 Enhanced temp string handling by Maddes +#define RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e)) + +/* +=============================================================================== + BUILT-IN FUNCTIONS +=============================================================================== +*/ +char pr_varstring_temp[PR_MAX_TEMPSTRING]; // 2001-10-25 Enhanced temp string handling by Maddes +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 SetMinMaxSize (edict_t *e, float *min, float *max, qboolean 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; + + xvector[0] = cosf(a); + xvector[1] = sinf(a); + yvector[0] = -sinf(a); + yvector[1] = cosf(a); + + 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_useprint + +Print a text depending on what it is fed with + +useprint(entity client, float type, float cost, float weapon) +================= +*/ +void PF_useprint (void) +{ + client_t *client; + int entnum, type, cost, weapon; + + entnum = G_EDICTNUM(OFS_PARM0); + type = G_FLOAT(OFS_PARM1); + cost = G_FLOAT(OFS_PARM2); + weapon = G_FLOAT(OFS_PARM3); + + + if (entnum < 1 || entnum > svs.maxclients) + { + Con_Printf ("tried to sprint to a non-client\n"); + return; + } + + client = &svs.clients[entnum-1]; + + MSG_WriteByte (&client->message,svc_useprint); + MSG_WriteByte (&client->message,type); + MSG_WriteShort (&client->message,cost); + MSG_WriteByte (&client->message,weapon); + //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 = sqrtf(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 = sqrtf(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) (atan2f(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) (atan2f(value1[1], value1[0]) * 180 / M_PI); + if (yaw < 0) + yaw += 360; + + forward = sqrtf (value1[0]*value1[0] + value1[1]*value1[1]); + pitch = (int) (atan2f(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 +allready 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); +} + + +extern trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore); + +void PF_TraceToss (void) +{ + trace_t trace; + edict_t *ent, *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); +} + +int TraceMove(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *ent)//engine-sides +{ + if(start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) + { + return 1; + } + vec3_t forward, up; + float HorDist; + vec3_t HorGoal; + vec3_t tempHorGoal; + + up[0] = 0; up[1] = 0; up[2] = 1; + HorGoal[0] = end[0]; HorGoal[1] = end[1]; HorGoal[2] = start[2]; + + VectorSubtract(HorGoal,start,forward); + HorDist = VectorLength(forward); + VectorNormalize(forward); + + vec3_t CurrentPos; + + VectorCopy(start,CurrentPos); + VectorCopy(HorGoal,tempHorGoal); + + float CurrentDist = 0;//2d distance from initial 3d positionvector + + trace_t trace1, trace2; + float tempDist; + vec3_t tempVec; + vec3_t tempVec2; + float i; + int STEPSIZEB = 18;//other declaration isn't declared yet + float SLOPELEN = 10.4;//18/tan(60) = 10.4, the the length of the triangle formed by the max walkable slope of 60 degrees. + int skip = 0; + int LoopBreak = 0; + + while(CurrentDist < HorDist) + { + if(LoopBreak > 20)//was 50, decreased this quite a bit. now it's 260 meters + { + //Con_Printf("AI Warning: There is a ledge that is greater than 650 meters.\n"); + return -1; + } + + trace1 = SV_Move(CurrentPos, mins, maxs, tempHorGoal, MOVE_NOMONSTERS, ent); + + VectorSubtract(tempHorGoal,CurrentPos,tempVec); + tempDist = trace1.fraction * VectorLength(tempVec); + //Check if we fell along the path + for(i = (maxs[0] * 1); i < tempDist; i += (maxs[0] * 1)) + { + VectorScale(forward,i,tempVec); + VectorAdd(tempVec,CurrentPos,tempVec); + VectorScale(up,-500,tempVec2);//500 inches is about 13 meters + VectorAdd(tempVec,tempVec2,tempVec2); + trace2 = SV_Move(tempVec, mins, maxs, tempVec2, MOVE_NOMONSTERS, ent); + if(trace2.fraction > 0) + { + VectorScale(up,trace2.fraction * -100,tempVec2); + VectorAdd(tempVec,tempVec2,CurrentPos); + VectorAdd(tempHorGoal,tempVec2,tempHorGoal); + skip = 1; + CurrentDist += i; + if(trace2.fraction == 1) + { + //We fell the full 13 meters!, we need to be careful here, + //because if we're checking over the void, then we could be stuck in an infinite loop and crash the game + //So we're going to keep track of how many times we fall 13 meters + LoopBreak++; + } + else + { + LoopBreak = 0; + } + break; + } + } + //If we fell at any location along path, then we don't try to step up + if(skip == 1) + { + trace2.fraction = 0; + skip = 0; + continue; + } + //We need to advance it as much as possible along path before step up + if(trace1.fraction > 0 && trace1.fraction < 1) + { + VectorCopy(trace1.endpos,CurrentPos); + trace1.fraction = 0; + } + //Check step up + if(trace1.fraction < 1) + { + VectorScale(up,STEPSIZEB,tempVec2); + VectorAdd(CurrentPos,tempVec2,tempVec); + VectorAdd(tempHorGoal,tempVec2,tempVec2); + trace2 = SV_Move(tempVec, mins, maxs, tempVec2, MOVE_NOMONSTERS, ent); + //10.4 is minimum length for a slope of 60 degrees, we need to at least advance this much to know the surface is walkable + VectorSubtract(tempVec2,tempVec,tempVec2); + if(trace2.fraction > (trace1.fraction + (SLOPELEN/VectorLength(tempVec2))) || trace2.fraction == 1) + { + VectorCopy(tempVec,CurrentPos); + tempHorGoal[2] = CurrentPos[2]; + continue; + } + else + { + return 0;//stepping up didn't advance so we've hit a wall, we failed + } + } + if(trace1.fraction == 1)//we've made it horizontally to our goal... so check if we've made it vertically... + { + if((end[2] - tempHorGoal[2] < STEPSIZEB) && (end[2] - tempHorGoal[2]) > -1 * STEPSIZEB) + return 1; + else return 0; + } + } + return 0; +} + + +void PF_tracemove(void)//progs side +{ + float *start, *end, *mins, *maxs; + int nomonsters; + edict_t *ent; + + start = G_VECTOR(OFS_PARM0); + mins = G_VECTOR(OFS_PARM1); + maxs = G_VECTOR(OFS_PARM2); + end = G_VECTOR(OFS_PARM3); + nomonsters = G_FLOAT(OFS_PARM4); + ent = G_EDICT(OFS_PARM5); + + Con_DPrintf ("TraceMove start, "); + G_INT(OFS_RETURN) = TraceMove(start, mins, maxs, end,nomonsters,ent); + Con_DPrintf ("TM end\n"); + return; +} + +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); +} +/* +================= +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]; // 2001-10-25 Enhanced temp string handling by Maddes + +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) = fabsf(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); +} + +// 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_strtrim + +string strtrim (string) +================= +*/ +void PF_strtrim (void) +{ + char *m; + m = G_STRING(OFS_PARM0); + + char *c; + c = m; + + while (c != '\0' && *c == ' ') + c++; + m = c; + + c = m + strlen (m) - 1; + while (c >= m) + { + if (*c == ' ') + *c = '\0'; + else + break; + c--; + } + G_INT(OFS_RETURN) = m - pr_strings; +}; + +/* +================= +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)); +} + +/* +================= +PF_Set_Zombie + +void Set_Zombie (entity) +================= +*/ +void PF_Set_Zombie (void) +{ + //int entitynum = G_EDICT(OFS_PARM0); +} + + +/* +================= +Main_Waypoint functin + +This is where the magic happens +================= +*/ + +int closedset[MAX_WAYPOINTS]; // The set of nodes already evaluated. +int openset[MAX_WAYPOINTS];//Actual sorted open list +int opensetRef[MAX_WAYPOINTS];//Reference values of open list +int opensetLength;//equivalent of javaScript's array[].length; +#define MaxZombies 16 + +zombie_ai zombie_list[MaxZombies]; +//Debug// +void printSortedOpenSet() +{ + Con_Printf("Sorted!: "); + int qr; + for(qr = 0; qr < opensetLength; qr++) + { + Con_Printf("%i, ",(int)waypoints[openset[qr]].f_score); + } + Con_Printf("\n"); +} +//------// + + +void RemoveWayFromList (int listnumber, int waynum) +{ + if(listnumber == 1) + { + //Con_DPrintf ("RemoveWayFromList: closedset[%i] = %i\n", waynum, 0); + closedset[waynum] = 0; + return; + } + + int i; + int s; + if(listnumber == 2) + { + for(i = 0; i < opensetLength; i++) + { + if(openset[i] == waynum) + { + openset[i] = 0; + opensetRef[waynum] = 0; + + for(s = i; s < opensetLength; s++) + { + openset[s] = openset[s+1]; + } + opensetLength -= 1; + return; + } + } + } +} + +void CompareOpenLists() +{ + int refCount, count; + refCount = 0; + count = 0; + int i; + for(i = 0; i < MAX_WAYPOINTS; i++) + { + if(openset[i]) + count++; + if(opensetRef[i]) + refCount++; + } + if(count != refCount || count != opensetLength || refCount != opensetLength) + Con_Printf("%i%i%i\n",count, refCount,opensetLength); +} + +int AddWayToList (int listnumber, int waynum)//blubs binary sorting +{ + if(listnumber == 1)//closed list + { + //Con_DPrintf ("AddWayToList: closedset[%i] = %i\n", waynum, 1); + closedset[waynum] = 1; + return 1; + } + + if(listnumber == 2)//openlist + { + int min, max, test; + min = -1; + max = opensetLength; + float wayVal = waypoints[waynum].f_score; + + while(max > min) + { + if(max - min == 1) + { + int i; + for(i = opensetLength; i > max ; i--) + { + openset[i] = openset[i-1]; + } + openset[max] = waynum; + opensetLength += 1; + opensetRef[waynum] = 1; + //printSortedOpenSet(); for debug only + return max; + } + test = (int)((min + max)/2); + if(wayVal > waypoints[openset[test]].f_score) + { + min = test; + } + else if(wayVal < waypoints[openset[test]].f_score) + { + max = test; + } + if(wayVal == waypoints[openset[test]].f_score) + { + max = test; + min = test - 1; + } + } + } + return -1; +} + +int GetLowestFromOpenSet() +{ + return openset[0]; +} + +int CheckIfEmptyList (int listnumber) +{ + int i; + + for (i = 0; i < MAX_WAYPOINTS; i++) + { + if (listnumber == 1) + { + if (closedset[i]) + { + //Con_DPrintf ("CheckIfEmptyList: closedset[%i]\n", i); + return 0; + } + } + else if (listnumber == 2) + { + if (openset[i]) + { + //Con_DPrintf ("CheckIfEmptyList: openset[%i]\n", i); + return 0; + } + } + } + return 1; +} + +int CheckIfWayInList (int listnumber, int waynum) +{ + if(listnumber == 1) + { + if(closedset[waynum]) + { + //Con_DPrintf ("CheckIfWayInList: closedset[%i] = %i\n", waynum, 1); + return 1; + } + } + if(listnumber == 2) + { + if(opensetRef[waynum]) + { + //Con_DPrintf ("CheckIfWayInList: openset[%i] = %i\n", waynum, 1); + return 1; + } + } + return 0; +} + +float heuristic_cost_estimate (int start_way, int end_way) +{ + //for now we will just look the distance between. + return VecLength2(waypoints[start_way].origin, waypoints[end_way].origin); +} + +int proces_list[MAX_WAYPOINTS]; +void reconstruct_path(int start_node, int current_node) +{ + int i, s, current; + current = current_node; + s = 0; +/* + if (current_node == waypoints[current_node].came_from) + p := reconstruct_path(came_from, came_from[current_node]) + return (p + current_node) + else + return current_node*/ + Con_DPrintf ("\n"); + Con_DPrintf ("reconstruct_path: start_node = %i, current_node = %i\n\n", start_node, current_node); + for (i = 0;i < MAX_WAYPOINTS; i++) + { + //if (closedset[i]) + // Con_DPrintf ("reconstruct_path: closedset[%i] = %i\n", i, closedset[i]); + proces_list[i] = 0; + } + proces_list[s] = -1;//-1 means the enemy is the last waypoint + s = 1; + while (1) + { + //Con_DPrintf("\nreconstruct_path: current = %i, waypoints[current].came_from = %i\n", current, waypoints[current].came_from); + proces_list[s] = current;//blubs, we now add the first waypoint to the path list + if (current == start_node) + { + Con_DPrintf("reconstruct_path: path done!\n"); + break; + } + if (CheckIfWayInList (1, waypoints[current].came_from)) + { + //Con_DPrintf("reconstruct_path: waypoints[current].came_from %i is in list!\n", waypoints[current].came_from); + for (i = 0;i < 8; i++) + { + //Con_DPrintf("reconstruct_path for loop: waypoints[waypoints[current].came_from].target_id[i] = %i, current = %i\n", waypoints[waypoints[current].came_from].target_id[i], current) + if (waypoints[waypoints[current].came_from].target_id[i] == current) + { + //Con_DPrintf("reconstruct_path: current %i is viable target!\n", current); + current = waypoints[current].came_from;//woohoo, this waypoint is viable. So set it now as the current one + break; + } + } + } + else + { + //Con_DPrintf("reconstruct_path: skipped waypoint %i\n", waypoints[current].came_from); + break; + } + s++; + } + Con_DPrintf("\nreconstruct_path: dumping the final list\n"); + for (s = MAX_WAYPOINTS - 1; s > -1; s--) + { + //if (proces_list[s]) + //Con_DPrintf("reconstruct_path final: s = %i, proces_list[s] = %i\n", s, proces_list[s]); + } +} + +int Pathfind (int start_way, int end_way)//note thease 2 are ARRAY locations. Not the waypoints names. +{ + int current, last_way;//current is for the waypoint array, last_way is a way that was used last + float tentative_g_score, tentative_f_score; + int i; + last_way = 0; + for (i = 0; i < MAX_WAYPOINTS;i++)// clear all the waypoints + { + openset[i] = 0; + opensetRef[i] = 0; + closedset[i] = 0; + waypoints[i].f_score = 0; + waypoints[i].g_score = 0; + waypoints[i].came_from = 0; + } + opensetLength = 0; + + waypoints[start_way].g_score = 0; // Cost from start along best known path. + // Estimated total cost from start to goal through y. + waypoints[start_way].f_score = waypoints[start_way].g_score + heuristic_cost_estimate(start_way, end_way); + + AddWayToList (2, start_way);// The set of tentative nodes to be evaluated, initially containing the start node + + while (!CheckIfEmptyList (2)) + { + //Con_DPrintf("\n"); + current = GetLowestFromOpenSet(); + //Con_DPrintf("Pathfind current: %i, f_score: %f, g_score: %f\n", current, waypoints[current].f_score, waypoints[current].g_score); + if (current == end_way) + { + Con_DPrintf("Pathfind goal reached\n"); + reconstruct_path(start_way, end_way); + return 1; + } + AddWayToList (1, current); + RemoveWayFromList (2, current); + + for (i = 0;i < 8; i++) + { + + //Con_DPrintf("Pathfind for start\n"); + + if (!waypoints[waypoints[current].target_id[i]].open) + { + //if (waypoints[current].target_id[i]) + //Con_DPrintf("Pathfind for: %i, waypoints[waypoints[current].target_id[i]].open = %i, current = %i\n", waypoints[current].target_id[i], waypoints[waypoints[current].target_id[i]].open, current); + continue; + } + + tentative_g_score = waypoints[current].g_score + waypoints[current].dist[i]; + tentative_f_score = tentative_g_score + heuristic_cost_estimate(waypoints[current].target_id[i], end_way); + //Con_DPrintf("Pathfind for: %i, t_f_score: %f, t_g_score: %f\n", waypoints[current].target_id[i], tentative_f_score, tentative_g_score); + + //if (CheckIfWayInList (1, waypoints[current].target_id[i]) && tentative_f_score >= waypoints[waypoints[current].target_id[i]].f_score) + if (CheckIfWayInList (1, waypoints[current].target_id[i]))//it was the above, but why do we care about this waypoint if it's already in the closed list? we never check 2 waypoints twice m8, the first iteration that we reach this waypoint is also the fastest way, so lets not EVER check it again. + { + //if (CheckIfWayInList (1, waypoints[current].target_id[i])) + //Con_DPrintf("Pathfind: waypoint %i in closed list\n", waypoints[current].target_id[i]); + continue; + } + + if(tentative_f_score < waypoints[waypoints[current].target_id[i]].f_score) + { + //Con_DPrintf("Pathfind waypoint is better\n"); + waypoints[waypoints[current].target_id[i]].g_score = tentative_g_score; + waypoints[waypoints[current].target_id[i]].f_score = tentative_f_score; + } + + if (!CheckIfWayInList (2, waypoints[current].target_id[i])) + { + //Con_DPrintf("Pathfind waypoint not in list\n"); + waypoints[waypoints[current].target_id[i]].g_score = tentative_g_score; + waypoints[waypoints[current].target_id[i]].f_score = tentative_f_score; + + waypoints[waypoints[current].target_id[i]].came_from = current; + AddWayToList (2, waypoints[current].target_id[i]); + //Con_DPrintf("Pathfind: %i added to the openset with waypoints[current].came_from = %i, current = %i\n", waypoints[current].target_id[i], waypoints[current].came_from, current); + } + } + last_way = current; + } + return 0; +} +/* +================= +Get_Waypoint_Near + +vector Get_Waypoint_Near (entity) +================= +*/ +void Get_Waypoint_Near (void) +{ + float best_dist; + float dist; + int i, best; + trace_t trace; + edict_t *ent; + + best = 0; + Con_DPrintf("Starting Get_Waypoint_Near\n"); + ent = G_EDICT(OFS_PARM0); + best_dist = 1000000000; + dist = 0; + + for (i = 0; i < MAX_WAYPOINTS; i++) + { + if (waypoints[i].open) + { + trace = SV_Move (ent->v.origin, vec3_origin, vec3_origin, waypoints[i].origin, 1, ent); + dist = VecLength2(waypoints[i].origin, ent->v.origin); + + //Con_DPrintf("Waypoint: %i, distance: %f, fraction: %f\n", i, dist, trace.fraction); + if (trace.fraction >= 1) + { + if(dist < best_dist) + { + best_dist = dist; + best = i; + } + } + } + } + Con_DPrintf("'%5.1f %5.1f %5.1f', %f is %f, (%i, %i)\n", waypoints[best].origin[0],waypoints[best].origin[1], waypoints[best].origin[2], best_dist, dist, i, best); + VectorCopy (waypoints[best].origin, G_VECTOR(OFS_RETURN)); +} + +/* +================= +Open_Waypoint + +void Open_Waypoint (string, string, string, string, string, string, string, string) +================= +*/ +void Open_Waypoint (void) +{ + int i, t; + char *p = G_STRING(OFS_PARM0); + + //Con_DPrintf("Open_Waypoint\n"); + for (i = 1; i < MAX_WAYPOINTS; i++) + { + if (waypoints[i].special[0])//no need to open without tag + { + if (!strcmp(p, waypoints[i].special)) + { + waypoints[i].open = 1; + //Con_DPrintf("Open_Waypoint: %i, opened\n", i); + t = 1; + } + else + continue; + } + } + //if (t == 0) + //{ + //Con_DPrintf("Open_Waypoint: no waypoints opened\n"); + //} +} + +/* +================= +Close_Waypoint + +void Close_Waypoint (string, string, string, string, string, string, string, string) + +moto - basically a carbon copy of open_waypoint lol +================= +*/ +void Close_Waypoint (void) +{ + int i, t; + char *p = G_STRING(OFS_PARM0); + + for (i = 1; i < MAX_WAYPOINTS; i++) + { + if (waypoints[i].special[0])//no need to open without tag + { + if (!strcmp(p, waypoints[i].special)) + { + waypoints[i].open = 0; + t = 1; + } + else + continue; + } + } +} + +/* +================= +Do_Pathfind + +float Do_Pathfind (entity zombie, entity target) +================= +*/ +void Do_Pathfind (void) +{ + float best_dist; + float dist; + int i, s, best, best_target; + trace_t trace; + edict_t *ent; + edict_t *zombie; + int entnum; + + entnum = G_EDICTNUM(OFS_PARM0); + + best = 0; + Con_DPrintf("Starting Do_Pathfind\n"); //we first need to look for closest point for both zombie and the player + zombie = G_EDICT(OFS_PARM0); + ent = G_EDICT(OFS_PARM1); + + best_dist = 1000000000; + dist = 0; + + for (i = 0; i < MAX_WAYPOINTS; i++) + { + if (waypoints[i].used && waypoints[i].open) + { + trace = SV_Move (zombie->v.origin, vec3_origin, vec3_origin, waypoints[i].origin, 1, zombie); + if (trace.fraction >= 1) + { + dist = VecLength2(waypoints[i].origin, zombie->v.origin); + + if(dist < best_dist) + { + best_dist = dist; + best = i; + } + } + } + } + + best_dist = 1000000000; + dist = 0; + best_target = 0; + for (i = 0; i < MAX_WAYPOINTS; i++) + { + if (waypoints[i].used && waypoints[i].open) + { + trace = SV_Move (ent->v.origin, vec3_origin, vec3_origin, waypoints[i].origin, 1, ent); + if (trace.fraction >= 1) + { + dist = VecLength2(waypoints[i].origin, ent->v.origin); + + if(dist < best_dist) + { + best_dist = dist; + best_target = i; + } + } + } + } + Con_DPrintf("Starting waypoint: %i, Ending waypoint: %i\n", best, best_target); + if (Pathfind(best, best_target)) + { + for (i = 0; i < MaxZombies; i++) + { + if (entnum == zombie_list[i].zombienum) + { + for (s = 0; s < MAX_WAYPOINTS; s++) + { + zombie_list[i].pathlist[s] = proces_list[s]; + } + break; + } + if (i == MaxZombies - 1)//zombie was not in list + { + for (i = 0; i < MaxZombies; i++) + { + if (!zombie_list[i].zombienum) + { + zombie_list[i].zombienum = entnum; + for (s = 0; s < MAX_WAYPOINTS; s++) + { + zombie_list[i].pathlist[s] = proces_list[s]; + } + break; + } + } + break; + } + } + + if(zombie_list[i].pathlist[2] == 0 && zombie_list[i].pathlist[1] != 0)//then we are at player's waypoint! + { + Con_DPrintf("We are at player's waypoint already!\n"); + G_FLOAT(OFS_RETURN) = -1; + return; + } + + Con_DPrintf("Path found!\n"); + G_FLOAT(OFS_RETURN) = 1; + } + else + { + Con_DPrintf("Path not found!\n"); + G_FLOAT(OFS_RETURN) = 0; + } +} + + +/* +================= +Get_Next_Waypoint This function will return the next waypoint in zombies path and then remove it from the list + +vector Get_Next_Waypoint (entity) +================= +*/ +void Get_Next_Waypoint (void) +{ + int i, s; + s = 0;//useless initialize, because compiler likes to yell at me + int entnum; + edict_t *ent;//blubs added + vec3_t move; + float *start,*mins, *maxs; + int currentWay = 0; + //int zomb = 0; + int skippedWays = 0; + + move [0] = 0; + move [1] = 0; + move [2] = 0; + + entnum = G_EDICTNUM(OFS_PARM0); + ent = G_EDICT(OFS_PARM0);//blubsadded + start = G_VECTOR(OFS_PARM1); + mins = G_VECTOR(OFS_PARM2); + maxs = G_VECTOR(OFS_PARM3); + + mins[0] -= 2; + mins[1] -= 2; + + maxs[0] += 2; + maxs[1] += 2; + + + for (i = 0; i < MaxZombies; i++) + { + if (entnum == zombie_list[i].zombienum) + { + for (s = MAX_WAYPOINTS - 1; s > -1; s--) + { + if (zombie_list[i].pathlist[s]) + { + zombie_list[i].pathlist[s] = 0;//This is get_next, so remove our current waypoint from the list. + + if(s == 1) + { + VectorCopy (move, G_VECTOR(OFS_RETURN));//we are at our last waypoint, so just return 0,0,0, this should never happen anyways, because we'll make pathfind return something else + Con_Printf("Warning, only one waypoint in path!\n"); + return; + } + s-= 1; + currentWay = s;//We want the next waypoint + break; + } + } + break; + } + } + //s is the index in our path, so if s == 1 + + if(s == -1 || s == 0) + { + //-1? + //then that means only player was in path, this is just in case... + //we are at our last waypoint, so just return 0,0,0, this should never happen anyways, because we'll make pathfind return something else + //0? + //only 1 waypoint left in path, we can't possibly smooth the path in this scenario. + //next waypoint in any case is going to be player, so.... + VectorCopy (move, G_VECTOR(OFS_RETURN)); + return; + } + + int iterations = 5;//that's how many segments per waypoint, pretty important number + float Scale = 0.5; + float curScale = 1; + float Scalar = Scale; + float TraceResult; + vec3_t toAdd; + vec3_t curStart; + vec3_t temp; + int q; + VectorCopy(waypoints[zombie_list[i].pathlist[currentWay]].origin,temp); + VectorCopy(temp,move); + + while(1) + { + //Con_Printf("Main Vector Start: %f, %f, %f Vector End: %f, %f, %f\n",start[0],start[1],start[2],waypoints[zombie_list[i].pathlist[currentWay]].origin[0],waypoints[zombie_list[i].pathlist[currentWay]].origin[1],waypoints[zombie_list[i].pathlist[currentWay]].origin[2]); + TraceResult = TraceMove(start,mins,maxs,waypoints[zombie_list[i].pathlist[currentWay]].origin,MOVE_NOMONSTERS,ent); + if(TraceResult == 1) + { + VectorCopy(waypoints[zombie_list[i].pathlist[currentWay]].origin,move); + if(currentWay == 1)//we're at the end of the list, we better not go out of bounds, was 0, now 1, since 0 is for player index + { + break; + } + currentWay -= 1; + skippedWays += 1; + } + else + { + if(skippedWays > 0) + { + VectorCopy(waypoints[zombie_list[i].pathlist[currentWay + 1]].origin,temp); + VectorCopy(temp,curStart); + VectorSubtract(waypoints[zombie_list[i].pathlist[currentWay]].origin,curStart,toAdd); + for(q = 0;q < iterations; q++) + { + curScale *= Scalar; + VectorScale(toAdd,curScale,temp); + VectorAdd(temp,curStart,temp); + TraceResult = TraceMove(start,mins,maxs,temp,MOVE_NOMONSTERS,ent); + if(TraceResult ==1) + { + Scalar = Scale + 1; + VectorCopy(temp,move); + } + else + { + Scalar = Scale; + } + } + } + break; + } + } + + Con_DPrintf("Get Next Way returns: list[%i], waypoint:%i\n",s,waypoints[zombie_list[i].pathlist[s]]); + + //VectorCopy(waypoints[zombie_list[i].pathlist[s]].origin,move); //for old get_next_way + zombie_list[i].pathlist[s] = 0; + + //Con_Printf("Skipped %i waypoints, we're moving to the %f percentage in between 2 waypoints\n",skippedWays,curScale); + //Con_DPrintf("'%5.1f %5.1f %5.1f'\n", move[0], move[1], move[2]); + VectorCopy (move, G_VECTOR(OFS_RETURN)); +} +/* +================= +Get_First_Waypoint This function will return the waypoint waypoint in zombies path and then remove it from the list + +vector Get_First_Waypoint (entity) +================= +*/ +void Get_First_Waypoint (void) +{ + int i, s; + s = 0;//useless initialize, because compiler likes to yell at me + int entnum; + edict_t *ent;//blubs added + vec3_t move; + float *start,*mins, *maxs; + int currentWay = 0; + //int zomb = 0; + int skippedWays = 0; + + move [0] = 0; + move [1] = 0; + move [2] = 0; + + entnum = G_EDICTNUM(OFS_PARM0); + ent = G_EDICT(OFS_PARM0);//blubsadded + start = G_VECTOR(OFS_PARM1); + mins = G_VECTOR(OFS_PARM2); + maxs = G_VECTOR(OFS_PARM3); + + mins[0] -= 2; + mins[1] -= 2; + + maxs[0] += 2; + maxs[1] += 2; + + + for (i = 0; i < MaxZombies; i++) + { + if (entnum == zombie_list[i].zombienum) + { + for (s = MAX_WAYPOINTS - 1; s > -1; s--) + { + if (zombie_list[i].pathlist[s]) + { + currentWay = s; + break; + } + } + break; + } + } + + if(s == 0) + { + //0? + //currentway is player, just return world + VectorCopy (move, G_VECTOR(OFS_RETURN)); + return; + } + //1? only one way in list, we can't possibly smooth list when we only have one... + + int iterations = 5;//that's how many segments per waypoint, pretty important number + float Scale = 0.5; + float curScale = 1; + float Scalar = Scale; + float TraceResult; + vec3_t toAdd; + vec3_t curStart; + vec3_t temp; + int q; + VectorCopy(waypoints[zombie_list[i].pathlist[currentWay]].origin,temp); + VectorCopy(temp,move); + + while(1) + { + //Con_Printf("Main Vector Start: %f, %f, %f Vector End: %f, %f, %f\n",start[0],start[1],start[2],waypoints[zombie_list[i].pathlist[currentWay]].origin[0],waypoints[zombie_list[i].pathlist[currentWay]].origin[1],waypoints[zombie_list[i].pathlist[currentWay]].origin[2]); + TraceResult = TraceMove(start,mins,maxs,waypoints[zombie_list[i].pathlist[currentWay]].origin,MOVE_NOMONSTERS,ent); + if(TraceResult == 1) + { + VectorCopy(waypoints[zombie_list[i].pathlist[currentWay]].origin,move); + if(currentWay == 1)//we're at the end of the list, we better not go out of bounds//was 0, now 1 since 0 is for enemy + { + break; + } + currentWay -= 1; + skippedWays += 1; + } + else + { + if(skippedWays > 0) + { + VectorCopy(waypoints[zombie_list[i].pathlist[currentWay + 1]].origin,temp); + VectorCopy(temp,curStart); + VectorSubtract(waypoints[zombie_list[i].pathlist[currentWay]].origin,curStart,toAdd); + for(q = 0;q < iterations; q++) + { + curScale *= Scalar; + VectorScale(toAdd,curScale,temp); + VectorAdd(temp,curStart,temp); + //Con_Printf("subVector Start: %f, %f, %f Vector End: %f, %f, %f\n",start[0],start[1],start[2],temp[0],temp[1],temp[2]); + TraceResult = TraceMove(start,mins,maxs,temp,MOVE_NOMONSTERS,ent); + if(TraceResult ==1) + { + Scalar = Scale + 1; + VectorCopy(temp,move); + } + else + { + Scalar = Scale; + }//we need a way to go back to the other value if it doesn't work!, so lets work with temp, but RETURN a different value other than temp! + } + } + break; + } + } + + Con_DPrintf("Get First Way returns: %i\n",s); + //VectorCopy(waypoints[zombie_list[i].pathlist[s]].origin,move);//for old get_first_way + zombie_list[i].pathlist[s] = 0; + //Con_Printf("Skipped %i waypoints, we're moving to the %f percentage in between 2 waypoints\n",skippedWays,curScale); + //Con_DPrintf("'%5.1f %5.1f %5.1f'\n", move[0], move[1], move[2]); + VectorCopy (move, G_VECTOR(OFS_RETURN)); +} + + +// 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 + +// 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); +} + +// entity (entity start, .float field, float match) findfloat = #98; +void PF_FindFloat (void) +{ + int e; + int f; + float s, t; + edict_t *ed; + + e = G_EDICTNUM(OFS_PARM0); + f = G_INT(OFS_PARM1); + s = G_FLOAT(OFS_PARM2); + if (!s) + PR_RunError ("PF_FindFloat: bad search float"); + + for (e++ ; e < sv.num_edicts ; e++) + { + ed = EDICT_NUM(e); + if (ed->free) + continue; + t = E_FLOAT(ed,f); + if (!t) + continue; + if (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); + + //if(!strcmp(pr_strings + ent->v.classname, "ai_zombie")) + //{ + // VectorCopy(ent->v.origin,PROG_TO_EDICT(ent->v.head)->v.origin); + // VectorCopy(ent->v.origin,PROG_TO_EDICT(ent->v.rarm)->v.origin); + // VectorCopy(ent->v.origin,PROG_TO_EDICT(ent->v.larm)->v.origin); + //} + +// 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) = floorf(G_FLOAT(OFS_PARM0)); +} +void PF_ceil (void) +{ + G_FLOAT(OFS_RETURN) = ceilf(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_t sv_aim = {"sv_aim", "0.93"}; +void PF_aim (void) +{ + edict_t *ent, *check, *bestent; + vec3_t start, dir, end, bestdir, tempv; + int i, j; + trace_t tr; + float dist, bestdist; + float speed; + + ent = G_EDICT(OFS_PARM0); + speed = G_FLOAT(OFS_PARM1); + VectorAdd(ent->v.origin, ent->v.view_ofs, tempv); + VectorCopy (tempv, 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; + // dr_mabuse1981: PSP controls sucks ass + 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); +// dr_mabuse1981: bullets shouldnt go offset anymore (fix from Quartal engine) +// dist = DotProduct (dir, pr_global_struct->v_forward); +// VectorScale (pr_global_struct->v_forward, dist, end); +// end[2] = dir[2]; + end[0] = dir[0]; + end[1] = dir[1]; + 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); +} + +/* +============== +PF_GetSoundLen + +Get the lenght of the sound (useful for things like radio) +============== +*/ +void PF_GetSoundLen (void) +{ + + char *name; + + name = G_STRING(OFS_PARM0); + + char namebuffer[256]; + byte *data; + wavinfo_t info; + byte stackbuf[1*1024]; // avoid dirtying the cache heap + +//Con_Printf ("S_LoadSound: %x\n", (int)stackbuf); +// load it in + Q_strcpy(namebuffer, ""); + Q_strcat(namebuffer, name); + + data = COM_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf)); + + if (!data) + { + Con_Printf ("Couldn't load %s\n", namebuffer); + G_FLOAT(OFS_RETURN) = -1; + return; + } + + info = GetWavinfo (name, data, com_filesize); + if (info.channels != 1) + { + Con_Printf ("%s is a stereo sample\n",name); + G_FLOAT(OFS_RETURN) = -1; + return; + } + + G_FLOAT(OFS_RETURN) = (float)info.samples/(float)info.rate; +} + +/* +============== +PF_changepitch +============== +*/ +void PF_changepitch (void) +{ + float ideal, current, move, speed; + edict_t *ent; + eval_t *val; + + ent = G_EDICT(OFS_PARM0); + current = anglemod(ent->v.angles[0]); + if ((val = GETEDICTFIELDVALUE(ent, eval_idealpitch))) + { + ideal = val->_float; + } + else + { + PR_RunError ("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch"); + return; + } + + if ((val = GETEDICTFIELDVALUE(ent, eval_pitch_speed))) + { + speed = val->_float; + } + else + { + PR_RunError ("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch"); + return; + } + + 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); +} + +/* +=============================================================================== + +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) +{ + 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)); +} + + + +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) = sqrtf(G_FLOAT(OFS_PARM0)); +} + +void PF_Fixme (void) +{ + //PR_RunError ("unimplemented bulitin"); + Con_DPrintf("unimplemented bulitin"); +} + +/* +================= +PF_SongEgg + +plays designated easter egg track + +songegg(trackname) +================= +*/ +void PF_SongEgg (void) +{ + char trackname; + + trackname = G_STRING(OFS_PARM0); + + MSG_WriteByte (&sv.reliable_datagram, svc_songegg); + MSG_WriteString (&sv.reliable_datagram, trackname); +} + +/* +================= +PF_MaxAmmo + +activates max ammo text in HUD + +nzp_maxammo() +================= +*/ +void PF_MaxAmmo(void) +{ + MSG_WriteByte(&sv.reliable_datagram, svc_maxammo); +} + + +/* +================= +PF_achievement + +unlocks the achievement number for entity + +achievement(clientent, value) +================= +*/ +void PF_achievement (void) +{ + int ach; + client_t *client; + int entnum; + + entnum = G_EDICTNUM(OFS_PARM0); + ach = G_FLOAT(OFS_PARM1); + + if (entnum < 1 || entnum > svs.maxclients) + { + Con_DPrintf ("tried to unlock ach to a non-client\n"); + return; + } + + //Con_Printf (va("Achievement? %i\n", ach)); // JPG + client = &svs.clients[entnum-1]; + + MSG_WriteByte (&client->message,svc_achievement); + MSG_WriteByte (&client->message, ach); +} + + +/* +================= +PF_updateLimb + +updates zombies limb + +PF_updateLimb(zombieent, value. limbent) +================= +*/ +void PF_updateLimb (void) +{ + int limb; + int zombieent, limbent; + + zombieent = G_EDICTNUM(OFS_PARM0); + limb = G_FLOAT(OFS_PARM1); + limbent = G_EDICTNUM(OFS_PARM2); + MSG_WriteByte (&sv.reliable_datagram, svc_limbupdate); + MSG_WriteByte (&sv.reliable_datagram, limb); + MSG_WriteShort (&sv.reliable_datagram, zombieent); + MSG_WriteShort (&sv.reliable_datagram, limbent); +} + +// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes start +/* +================= +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) && (!(Q_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; +} + +/* +================= +PF_tokenize + +float tokenize (string) = #441 +================= +*/ +//KRIMZON_SV_PARSECLIENTCOMMAND added both of these +// refined to work on psp on 2017-DEC-09 +void PF_tokenize (void) +{ + char *m = G_STRING(OFS_PARM0); + Cmd_TokenizeString(m); + G_FLOAT(OFS_RETURN) = Cmd_Argc(); +}; + +/* +================= +PF_argv + +string argv (float num) = #442 +================= +*/ +void PF_ArgV (void) +{ + char tempc[256]; + strcpy(tempc, Cmd_Argv(G_FLOAT(OFS_PARM0))); + G_INT(OFS_RETURN) = tempc - pr_strings; +} + + +// 2001-09-14 Enhanced BuiltIn Function System (EBFS) 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/ +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 + { 33, "updateLimb", PF_updateLimb }, + { 34, "droptofloor", PF_droptofloor }, + { 35, "lightstyle", PF_lightstyle }, + { 36, "rint", PF_rint }, + { 37, "floor", PF_floor }, + { 38, "ceil", PF_ceil }, +// { 39, "fixme", PF_Fixme }, + { 40, "checkbottom", PF_checkbottom }, + { 41, "pointcontents", PF_pointcontents }, +// { 42, "fixme", PF_Fixme }, + { 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 }, + { 50, "getSoundLen", PF_GetSoundLen }, + { 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 }, + { 65, "etos", PF_etos }, + + { 67, "movetogoal", SV_MoveToGoal }, + { 68, "precache_file", PF_precache_file }, + { 69, "makestatic", PF_makestatic }, + + { 70, "changelevel", PF_changelevel }, + { 71, "movetoorigin", SV_MoveToOrigin }, + + { 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 }, + { 79, "achievement", PF_achievement }, + + { 81, "stof", PF_stof }, // 2001-09-20 QuakeC string manipulation by FrikaC/Maddes + + { 82, "Set_Zombie", PF_Set_Zombie }, //set zombies stats + { 83, "Get_Waypoint_Near", Get_Waypoint_Near }, //returns the cords for closest waypoint + { 84, "Do_Pathfind", Do_Pathfind }, //starts the magic + { 85, "Open_Waypoint", Open_Waypoint }, //Opens a waypoint + { 86, "Get_Next_Waypoint", Get_Next_Waypoint }, //Get next waypoint + { 87, "useprint", PF_useprint }, //Print with button + { 88, "Get_First_Waypoint", Get_First_Waypoint },//Get the first waypoint in the list + { 89, "Close_Waypoint", Close_Waypoint }, //Closes a waypoint + +// 2001-11-15 DarkPlaces general builtin functions by Lord Havoc start +// not implemented yet + + + { 90, "tracebox", PF_tracebox }, + { 99, "tracemove", PF_tracemove },//blubs improved tracebox +/* { 91, "randomvec", PF_randomvec }, + { 92, "getlight", PF_GetLight }, // not implemented yet + { 93, "cvar_create", PF_cvar_create }, // 2001-09-18 New BuiltIn Function: cvar_create() by Maddes + { 94, "fmin", PF_fmin }, + { 95, "fmax", PF_fmax }, + { 96, "fbound", PF_fbound }, + { 97, "fpow", PF_fpow }, + */{ 98, "findfloat", PF_FindFloat },/* + { PR_DEFAULT_FUNCNO_EXTENSION_FIND, "extension_find", PF_extension_find }, // 2001-10-20 Extension System by Lord Havoc/Maddes + { 0, "registercvar", PF_cvar_create }, // 0 indicates that this entry is just for remapping (because of name change) + { 0, "checkextension", PF_extension_find }, +*/ +// 2001-11-15 DarkPlaces general builtin functions by Lord Havoc end + + { PR_DEFAULT_FUNCNO_BUILTIN_FIND, "builtin_find", PF_builtin_find }, // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes + +// not implemented yet +/* + { 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 + + { 103, "cvar_string", PF_cvar_string }, // 2001-09-16 New BuiltIn Function: cvar_string() by Maddes + + { 105, "cvar_free", PF_cvar_free }, // 2001-09-18 New BuiltIn Function: cvar_free() by Maddes + + { 106, "NVS_InitSVCMsg", PF_NVS_InitSVCMsg }, // 2000-05-02 NVS SVC by Maddes + + { 107, "WriteFloat", PF_WriteFloat }, // 2001-09-16 New BuiltIn Function: WriteFloat() by Maddes + + { 108, "etof", PF_etof }, // 2001-09-25 New BuiltIn Function: etof() by Maddes + + { 109, "ftoe", PF_ftoe }, // 2001-09-25 New BuiltIn Function: ftoe() by Maddes +*/ + +// 2001-09-20 QuakeC file access by FrikaC/Maddes start +// not implemented yet + + { 110, "fopen", PF_fopen }, + { 111, "fclose", PF_fclose }, + { 112, "fgets", PF_fgets }, + { 113, "fputs", PF_fputs }, + { 0, "open", PF_fopen }, // 0 indicates that this entry is just for remapping (because of name and number change) + { 0, "close", PF_fclose }, + { 0, "read", PF_fgets }, + { 0, "write", PF_fputs }, + +// 2001-09-20 QuakeC file access by FrikaC/Maddes end + +// 2001-09-20 QuakeC string manipulation by FrikaC/Maddes start +// not implemented yet + + { 114, "strlen", PF_strlen }, + { 115, "strcat", PF_strcat }, + { 116, "substring", PF_substring }, + { 117, "stov", PF_stov }, + { 118, "strzone", PF_strzone }, + { 119, "strunzone", PF_strunzone }, + { 120, "strtrim", PF_strtrim }, + { 0, "zone", PF_strzone }, // 0 indicates that this entry is just for remapping (because of name and number change) + { 0, "unzone", PF_strunzone }, + +// 2001-09-20 QuakeC string manipulation by FrikaC/Maddes end + +// 2001-11-15 DarkPlaces general builtin functions by Lord Havoc start +// not implemented yet +/* + { 400, "copyentity", PF_... }, + { 401, "setcolor", PF_... }, + { 402, "findchain", PF_... }, + { 403, "findchainfloat", PF_... }, + { 404, "effect", PF_... }, + { 405, "te_blood", PF_... }, + { 406, "te_bloodshower", PF_... }, + { 407, "te_explosionrgb", PF_... }, + { 408, "te_particlecube", PF_... }, + { 409, "te_particlerain", PF_... }, + { 410, "te_particlesnow", PF_... }, + { 411, "te_spark", PF_... }, + { 412, "te_gunshotquad", PF_... }, + { 413, "te_spikequad", PF_... }, + { 414, "te_superspikequad", PF_... }, + { 415, "te_explosionquad", PF_... }, + { 416, "te_smallflash", PF_... } + { 417, "te_customflash", PF_... }, + { 418, "te_gunshot", PF_... }, + { 419, "te_spike", PF_... }, + { 420, "te_superspike", PF_... }, + { 421, "te_explosion", PF_... }, + { 422, "te_tarexplosion", PF_... }, + { 423, "te_wizspike", PF_... }, + { 424, "te_knightspike", PF_... }, + { 425, "te_lavasplash", PF_... } + { 426, "te_teleport", PF_... }, + { 427, "te_explosion2", PF_... }, + { 428, "te_lightning1", PF_... }, + { 429, "te_lightning2", PF_... }, + { 430, "te_lightning3", PF_... }, + { 431, "te_beam", PF_... }, + { 432, "vectorvectors", PF_... },*/ + { 441, "tokenize", PF_tokenize }, + { 442, "argv", PF_ArgV }, + + { 500, "songegg", PF_SongEgg }, + { 501, "nzp_maxammo", PF_MaxAmmo } + +// 2001-11-15 DarkPlaces general builtin functions by Lord Havoc end + +}; + +int pr_ebfs_numbuiltins = sizeof(pr_ebfs_builtins)/sizeof(pr_ebfs_builtins[0]); +// 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end + diff --git a/source/pr_comp.h b/source/pr_comp.h new file mode 100644 index 0000000..b237ed5 --- /dev/null +++ b/source/pr_comp.h @@ -0,0 +1,180 @@ +/* +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; + 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; + diff --git a/source/pr_edict.c b/source/pr_edict.c new file mode 100644 index 0000000..167a7d1 --- /dev/null +++ b/source/pr_edict.c @@ -0,0 +1,1339 @@ +/* +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 + +// 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 + +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); +qboolean 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}; + +#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, ""}}; + +// evaluation shortcuts +int eval_gravity; +int eval_idealpitch, eval_pitch_speed; + +// Half_life modes. Crow_bar +int eval_renderamt, eval_rendermode, eval_rendercolor; + +ddef_t *ED_FindField (char *name); + +int FindFieldOffset (char *field) +{ + ddef_t *d; + + if (!(d = ED_FindField(field))) + return 0; + + return d->ofs*4; +} + +void FindEdictFieldOffsets (void) +{ + eval_gravity = FindFieldOffset ("gravity"); + + eval_idealpitch = FindFieldOffset ("idealpitch"); + eval_pitch_speed = FindFieldOffset ("pitch_speed"); + eval_renderamt = FindFieldOffset ("renderamt"); + eval_rendermode = FindFieldOffset ("rendermode"); + eval_rendercolor = FindFieldOffset ("rendercolor"); +} + +/* +================= +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 +============= +*/ +qboolean 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; + qboolean anglehack; + qboolean 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; + + key = ED_FindField (keyname); + if (!key) + { + if (strcmp (keyname, "compiler") && + strcmp (keyname, "r_skycolor") && + strcmp (keyname, "sequence") && + strcmp (keyname, "message2") && //dirty hack to keep the console clean. Dem lazy mappers + strcmp (keyname, "mangle") && + strcmp (keyname, "Maxrange") && + strcmp (keyname, "light_lev") && + strcmp (keyname, "fog")) + Con_Printf ("'%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); +} + +func_t EndFrame; +/* +=============== +PR_LoadProgs +=============== +*/ +void PR_LoadProgs (void) +{ + dfunction_t *f; + 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 ("progs.dat has wrong version number (%i should be %i)", progs->version, PROG_VERSION); + if (progs->crc != PROGHEADER_CRC) + Con_Printf ("WARNING!!! progs.dat system vars have been modified, progdefs.h is out of date. Current one is %i\n", progs->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 (!(Q_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 +// not implemented yet +/* + 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 (); + EndFrame = 0; + + if ((f = ED_FindFunction ("EndFrame")) != NULL) + EndFrame = (func_t)(f - pr_functions); +} + +// 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 && Q_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); + 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); + Cmd_AddCommand ("builtinlist", PR_BuiltInList_f); // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes + // 2001-09-14 Enhanced BuiltIn Function System (EBFS) by Maddes end +} + + + +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; +} diff --git a/source/pr_exec.c b/source/pr_exec.c new file mode 100644 index 0000000..41d9f5f --- /dev/null +++ b/source/pr_exec.c @@ -0,0 +1,673 @@ +/* +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 "thread.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; + + +qboolean pr_trace; +dfunction_t *pr_xfunction; +int pr_xstatement; + + +int pr_argc; + +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[1024]; + + 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 + + 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 = 100000; + 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); + + 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); + a = (eval_t *)((int *)&ed->v + b->_int); + c->_int = a->_int; + break; + + case OP_LOAD_V: + ed = PROG_TO_EDICT(a->edict); + 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 += st->b - 1; // offset the s++ + break; + + case OP_IF: + if (a->_int) + s += st->b - 1; // offset the s++ + break; + + case OP_GOTO: + s += 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\nUse 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); + ed->v.nextthink = pr_global_struct->time + 0.1; + 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); + } +} + +} diff --git a/source/progdefs.h b/source/progdefs.h new file mode 100644 index 0000000..1061081 --- /dev/null +++ b/source/progdefs.h @@ -0,0 +1,169 @@ + +/* 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 rounds; + float rounds_change; + 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 EndFrame; + func_t PlayerPreThink; + func_t PlayerPostThink; + func_t ClientKill; + func_t ClientConnect; + func_t PutClientInServer; + func_t ClientDisconnect; + func_t SetNewParms; + func_t SetChangeParms; + func_t ParseClientCommand; + string_t CMD_STRING; + func_t Soft_Restart; +} 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 iframetime; + 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 points; + float kills; + float weapon; + string_t weaponmodel; + string_t weapon2model; + float weaponframe; + float weapon2frame; + float currentammo; + float currentmag; + float zoom; + float weaponskin; + float weapon2skin; + float primary_grenades; + float secondary_grenades; + float grenades; + float perks; + float takedamage; + int chain; + float deadflag; + vec3_t view_ofs; + float button0; + float button1; + float button2; + float button3; + float button4; + float button5; + float button6; + float button7; + float button8; + 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 waterlevel; + float watertype; + float ideal_yaw; + float yaw_speed; + int aiment; + int head; + int larm; + int rarm; + int goalentity; + float spawnflags; + string_t target; + string_t targetname; + float bleed_out; + float progress_bar; + 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 x2_icon; + float insta_icon; + vec3_t ADS_Offset; + vec3_t Flash_Offset; + float Flash_Size; + string_t Weapon_Name; + string_t Weapon_Name_Touch; + float currentmag2; + float maxspeed; + float facingenemy; +} entvars_t; + +#define PROGHEADER_CRC 14116 diff --git a/source/progs.h b/source/progs.h new file mode 100644 index 0000000..6985511 --- /dev/null +++ b/source/progs.h @@ -0,0 +1,158 @@ +/* +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 +{ + qboolean 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 int eval_gravity; +extern int eval_idealpitch, eval_pitch_speed; +// Half_life modes. Crow_bar +extern int eval_renderamt, eval_rendermode, eval_rendercolor; + +//============================================================================ + +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 (void); + +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 + +extern int pr_argc; + +extern qboolean 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) diff --git a/source/protocol.h b/source/protocol.h new file mode 100644 index 0000000..d5d79e9 --- /dev/null +++ b/source/protocol.h @@ -0,0 +1,185 @@ +/* +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 + +#define PROTOCOL_VERSION 15 + +// if the high bit of the servercmd is set, the low bits are fast update flags: +#define U_MOREBITS (1<<0) +#define U_ORIGIN1 (1<<1) +#define U_ORIGIN2 (1<<2) +#define U_ORIGIN3 (1<<3) +#define U_ANGLE2 (1<<4) +#define U_NOLERP (1<<5) // don't interpolate movement +#define U_FRAME (1<<6) +#define U_SIGNAL (1<<7) // just differentiates from other updates + +// svc_update can pass all of the fast update bits, plus more +#define U_EXTEND1 (1<<8) +#define U_ANGLE1 (1<<9) +#define U_ANGLE3 (1<<10) +#define U_MODEL (1<<11) +#define U_COLORMAP (1<<12) +#define U_SKIN (1<<13) +#define U_EFFECTS (1<<14) + + +// Tomaz - QC Alpha Scale Glow Control Begin +#define U_LONGENTITY (1<<15)//blubs here, U_EXTEND1 used to be here, but it needs to be in the byte above, so moved it to the 1<<8 position, and moved the rest down +#define U_RENDERMODE (1<<16) +#define U_RENDERAMT (1<<17) +#define U_RENDERCOLOR1 (1<<18) +#define U_RENDERCOLOR2 (1<<19) +#define U_RENDERCOLOR3 (1<<20) +#define U_EXTEND2 (1<<21) // another byte to follow +#define U_FRAMETIME (1<<22) // another byte to follow +// Tomaz - QC Alpha Scale Glow Control End + + +#define SU_VIEWHEIGHT (1<<0) +#define SU_IDEALPITCH (1<<1) +#define SU_PUNCH1 (1<<2) +#define SU_PUNCH2 (1<<3) +#define SU_PUNCH3 (1<<4) +#define SU_VELOCITY1 (1<<5) +#define SU_VELOCITY2 (1<<6) +#define SU_VELOCITY3 (1<<7) +#define SU_WEAPONSKIN (1<<7) +#define SU_PERKS (1<<9) +#define SU_ONGROUND (1<<10) // no data follows, the bit is it +#define SU_INWATER (1<<11) // no data follows, the bit is it +#define SU_WEAPONFRAME (1<<12) +#define SU_WEAPON (1<<14) +#define SU_PRIGRENADES (1<<15) +#define SU_SECGRENADES (1<<16) +#define SU_GRENADES (1<<13) + +// a sound with no channel is a local only sound +#define SND_VOLUME (1<<0) // a byte +#define SND_ATTENUATION (1<<1) // a byte +#define SND_LOOPING (1<<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_updatepoints 14 // [byte] [short] +#define svc_clientdata 15 // +#define svc_stopsound 16 // +#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_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 +#define svc_weaponfire 35 +#define svc_hitmark 36 +#define svc_skybox 37 // [string] skyname +#define svc_useprint 38 // [string] to put in center of the screen +#define svc_updatekills 39 // [string] to put in center of the screen +#define svc_limbupdate 40 +#define svc_fog 41 // [byte] start [byte] end [byte] red [byte] green [byte] blue [float] time +#define svc_bspdecal 42 // [string] name [byte] decal_size [coords] pos +#define svc_achievement 43 // [string] name [byte] decal_size [coords] pos +#define svc_songegg 44 // [string] track name +#define svc_maxammo 45 + + +// +// 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 +#define TE_RAYSPLASHGREEN 14 +#define TE_RAYSPLASHRED 15 diff --git a/source/psp/.bash_history b/source/psp/.bash_history new file mode 100644 index 0000000..8530933 --- /dev/null +++ b/source/psp/.bash_history @@ -0,0 +1,2 @@ +make -f makephat clean install +make -f makephat install \ No newline at end of file diff --git a/source/psp/MakePHAT b/source/psp/MakePHAT new file mode 100644 index 0000000..cac4d65 --- /dev/null +++ b/source/psp/MakePHAT @@ -0,0 +1,130 @@ +PSPSDK = $(shell psp-config --pspsdk-path) +PSPLIBSDIR = $(PSPSDK)/.. + +TARGET = nzportable + +PSP_EBOOT_TITLE = Nazi Zombies Portable Reboot + +PSP_EBOOT_ICON = pics/icon.png +PSP_EBOOT_SND0 = pics/snd0.at3 +PSP_EBOOT_PIC1 = pics/pic.png + +PSP_FW_VERSION=660 + +MODE=-DKERNEL_MODE + + +BUILD_PRX = 1 + +COMMON_OBJS = \ + battery.o \ + ../thread.o \ + VramExt.o \ + input.o \ + main.o \ + math.o \ + sound.o \ + system.o \ + module.o \ + network.o \ + network_psp.o \ + gethost.o \ + fnmatch.o \ + cd.o \ + mp3.o \ + Random.o \ + \ + ../chase.o \ + ../cl_demo.o \ + ../cl_input.o \ + ../cl_main.o \ + ../cl_parse.o \ + ../cl_tent.o \ + ../cl_slist.o \ + ../cmd.o \ + ../common.o \ + ../console.o \ + ../crc.o \ + ../cvar.o \ + ../host.o \ + ../host_cmd.o \ + ../keys.o \ + ../mathlib.o \ + ../menu.o \ + ../net_dgrm.o \ + ../net_loop.o \ + ../net_main.o \ + ../net_vcr.o \ + ../pr_cmds.o \ + ../pr_edict.o \ + ../pr_exec.o \ + ../snd_dma.o \ + ../snd_mem.o \ + ../snd_mix.o \ + ../cl_hud.o \ + ../sv_main.o \ + ../sv_move.o \ + ../sv_phys.o \ + ../sv_user.o \ + ../view.o \ + ../wad.o \ + ../world.o \ + ../zone.o \ + ../crypter.o + +HARDWARE_VIDEO_ONLY_OBJS = \ + wad3.o \ + clipping.o \ + vram.o \ + video_hardware.o \ + video_hardware_resample.o \ + video_hardware_images.o \ + video_hardware_fullbright.o \ + video_hardware_hlmdl.o \ + video_hardware_draw.o \ + video_hardware_entity_fragment.o \ + video_hardware_QMB.o \ + video_hardware_decals_QMB.o \ + video_hardware_part.o \ + video_hardware_light.o \ + video_hardware_main.o \ + video_hardware_mesh.o \ + video_hardware_mhex2.o \ + video_hardware_misc.o \ + video_hardware_model.o \ + video_hardware_screen.o \ + video_hardware_surface.o \ + video_hardware_warp.o \ + video_hardware_fog.o \ + video_hardware_dxtn.o +HARDWARE_VIDEO_ONLY_FLAGS = -DPSP_HARDWARE_VIDEO + + + +OBJS = $(COMMON_OBJS) $(HARDWARE_VIDEO_ONLY_OBJS) + + +#LIBS = -lpspaudiolib -lpspaudio -lpspgum -lpspgu -lpsprtc -lpsppower -lpspwlan -lstdc++ -lm + +GU_LIBS = -lpspgum_vfpu -lpspvfpu -lpspgu -lpspvram +AUDIO_LIBS = -lpspaudiolib -lpspaudio -lpspmp3 m33libs/libpspaudiocodec.a m33libs/libpspkubridge.a + +MISC_LIBS = -lpsprtc -lpspmath -lpsppower -lpsphprm -ljpeg -lpng m33libs/libz.a iridlibs/libPerf.a +NET_LIBS = -lpspwlan -lpspnet_adhoc -lpspnet_adhocctl +STD_LIBS = -lstdc++ -lm -lc +LIBS = $(GU_LIBS) $(AUDIO_LIBS) $(MISC_LIBS) $(STD_LIBS) $(NET_LIBS) + +CFLAGS = -ffast-math -O3 -G0 -Wall -Did386="0" -DPSP $(MODE) $(HARDWARE_VIDEO_ONLY_FLAGS) -DSWIZZLE32 -DPSP_MP3_HWDECODE -DFULLBRIGHT -DHL_RENDER -Wno-strict-aliasing -DPSP_VFPU +CXXFLAGS = -fno-rtti -Wcast-qual -Wno-write-strings -Wno-sign-compare -Wno-strict-aliasing +ASFLAGS = $(CFLAGS) -c + +include $(PSPSDK)/lib/build.mak + +ifneq ($(VS_PATH),) +CC = vs-psp-gcc +CXX = vs-psp-g++ +endif + +install: EBOOT.PBP + mv EBOOT.PBP ../../../../psp/nzportable/ + @echo DONE diff --git a/source/psp/MakeSLIM b/source/psp/MakeSLIM new file mode 100644 index 0000000..7dda2e3 --- /dev/null +++ b/source/psp/MakeSLIM @@ -0,0 +1,132 @@ +PSPSDK = $(shell psp-config --pspsdk-path) +PSPLIBSDIR = $(PSPSDK)/.. + +TARGET = nzportable + +PSP_EBOOT_TITLE = Nazi Zombies Portable Reboot + +PSP_EBOOT_ICON = pics/icon.png +PSP_EBOOT_SND0 = pics/snd0.at3 +PSP_EBOOT_PIC1 = pics/pic.png + +PSP_FW_VERSION=660 + +PSP_LARGE_MEMORY = 1 + +MODE=-DKERNEL_MODE + + +BUILD_PRX = 1 + +COMMON_OBJS = \ + battery.o \ + ../thread.o \ + VramExt.o \ + input.o \ + main.o \ + math.o \ + sound.o \ + system.o \ + module.o \ + network.o \ + network_psp.o \ + gethost.o \ + fnmatch.o \ + cd.o \ + mp3.o \ + Random.o \ + \ + ../chase.o \ + ../cl_demo.o \ + ../cl_input.o \ + ../cl_main.o \ + ../cl_parse.o \ + ../cl_tent.o \ + ../cl_slist.o \ + ../cmd.o \ + ../common.o \ + ../console.o \ + ../crc.o \ + ../cvar.o \ + ../host.o \ + ../host_cmd.o \ + ../keys.o \ + ../mathlib.o \ + ../menu.o \ + ../net_dgrm.o \ + ../net_loop.o \ + ../net_main.o \ + ../net_vcr.o \ + ../pr_cmds.o \ + ../pr_edict.o \ + ../pr_exec.o \ + ../snd_dma.o \ + ../snd_mem.o \ + ../snd_mix.o \ + ../cl_hud.o \ + ../sv_main.o \ + ../sv_move.o \ + ../sv_phys.o \ + ../sv_user.o \ + ../view.o \ + ../wad.o \ + ../world.o \ + ../zone.o \ + ../crypter.o + +HARDWARE_VIDEO_ONLY_OBJS = \ + wad3.o \ + clipping.o \ + vram.o \ + video_hardware.o \ + video_hardware_resample.o \ + video_hardware_images.o \ + video_hardware_fullbright.o \ + video_hardware_hlmdl.o \ + video_hardware_draw.o \ + video_hardware_entity_fragment.o \ + video_hardware_QMB.o \ + video_hardware_decals_QMB.o \ + video_hardware_part.o \ + video_hardware_light.o \ + video_hardware_main.o \ + video_hardware_mesh.o \ + video_hardware_mhex2.o \ + video_hardware_misc.o \ + video_hardware_model.o \ + video_hardware_screen.o \ + video_hardware_surface.o \ + video_hardware_warp.o \ + video_hardware_fog.o \ + video_hardware_dxtn.o +HARDWARE_VIDEO_ONLY_FLAGS = -DPSP_HARDWARE_VIDEO + + + +OBJS = $(COMMON_OBJS) $(HARDWARE_VIDEO_ONLY_OBJS) + + +#LIBS = -lpspaudiolib -lpspaudio -lpspgum -lpspgu -lpsprtc -lpsppower -lpspwlan -lstdc++ -lm + +GU_LIBS = -lpspgum_vfpu -lpspvfpu -lpspgu -lpspvram +AUDIO_LIBS = -lpspaudiolib -lpspaudio -lpspmp3 m33libs/libpspaudiocodec.a m33libs/libpspkubridge.a + +MISC_LIBS = -lpsprtc -lpsppower -lpspmath -lpsphprm -ljpeg -lpng m33libs/libz.a iridlibs/libPerf.a +NET_LIBS = -lpspwlan -lpspnet_adhoc -lpspnet_adhocctl +STD_LIBS = -lstdc++ -lm -lc +LIBS = $(GU_LIBS) $(AUDIO_LIBS) $(MISC_LIBS) $(STD_LIBS) $(NET_LIBS) + +CFLAGS = -ffast-math -O3 -G0 -Wall -Did386="0" -DPSP $(MODE) $(HARDWARE_VIDEO_ONLY_FLAGS) -DSWIZZLE32 -DSLIM -DPSP_MP3_HWDECODE -DFULLBRIGHT -DHL_RENDER -Wno-strict-aliasing -DPSP_VFPU +CXXFLAGS = -fno-rtti -Wcast-qual -Wno-write-strings -Wno-sign-compare -Wno-strict-aliasing +ASFLAGS = $(CFLAGS) -c + +include $(PSPSDK)/lib/build.mak + +ifneq ($(VS_PATH),) +CC = vs-psp-gcc +CXX = vs-psp-g++ +endif + +install: EBOOT.PBP + mv EBOOT.PBP ../../../../psp/nzportable/EBOOT2000.PBP + @echo DONE diff --git a/source/psp/Nazi-Zombies-Portable.cbp b/source/psp/Nazi-Zombies-Portable.cbp new file mode 100644 index 0000000..8115965 --- /dev/null +++ b/source/psp/Nazi-Zombies-Portable.cbp @@ -0,0 +1,290 @@ + + + + + + diff --git a/source/psp/Nazi-Zombies-Portable.layout b/source/psp/Nazi-Zombies-Portable.layout new file mode 100644 index 0000000..3cc9da4 --- /dev/null +++ b/source/psp/Nazi-Zombies-Portable.layout @@ -0,0 +1,444 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/psp/Random.cpp b/source/psp/Random.cpp new file mode 100644 index 0000000..53cce65 --- /dev/null +++ b/source/psp/Random.cpp @@ -0,0 +1,118 @@ +#include +#include "Random.h" + +#define IA 16807 +#define IM 2147483647 +#define IQ 127773 +#define IR 2836 +#define NTAB 32 +#define NDIV (1+(IM-1)/NTAB) + +static long idum = 0; + +void SeedRandomNumberGenerator(long lSeed) +{ + if (lSeed) + { + idum = lSeed; + } + else + { + idum = -time(NULL); + } + if (1000 < idum) + { + idum = -idum; + } + else if (-1000 < idum) + { + idum -= 22261048; + } +} + +long ran1(void) +{ + int j; + long k; + static long iy=0; + static long iv[NTAB]; + + if (idum <= 0 || !iy) + { + if (-(idum) < 1) idum=1; + else idum = -(idum); + for (j=NTAB+7;j>=0;j--) + { + k=(idum)/IQ; + idum=IA*(idum-k*IQ)-IR*k; + if (idum < 0) idum += IM; + if (j < NTAB) iv[j] = idum; + } + iy=iv[0]; + } + k=(idum)/IQ; + idum=IA*(idum-k*IQ)-IR*k; + if (idum < 0) idum += IM; + j=iy/NDIV; + iy=iv[j]; + iv[j] = idum; + + return iy; +} + +// fran1 -- return a random floating-point number on the interval [0,1) +// +#define AM (1.0/IM) +#define EPS 1.2e-7 +#define RNMX (1.0-EPS) +float fran1(void) +{ + float temp = (float)AM*ran1(); + if (temp > RNMX) return (float)RNMX; + else return temp; +} + +float RandomFloat( float flLow, float flHigh ) +{ + if (idum == 0) + { + SeedRandomNumberGenerator(0); + } + + float fl = fran1(); // float in [0,1) + return (fl * (flHigh-flLow)) + flLow; // float in [low,high) +} + +long RandomLong( long lLow, long lHigh ) +{ + if (idum == 0) + { + SeedRandomNumberGenerator(0); + } + + unsigned long maxAcceptable; + unsigned long x = lHigh-lLow+1; + unsigned long n; + if (x <= 0 || MAX_RANDOM_RANGE < x-1) + { + return lLow; + } + + // The following maps a uniform distribution on the interval [0,MAX_RANDOM_RANGE] + // to a smaller, client-specified range of [0,x-1] in a way that doesn't bias + // the uniform distribution unfavorably. Even for a worst case x, the loop is + // guaranteed to be taken no more than half the time, so for that worst case x, + // the average number of times through the loop is 2. For cases where x is + // much smaller than MAX_RANDOM_RANGE, the average number of times through the + // loop is very close to 1. + // + maxAcceptable = MAX_RANDOM_RANGE - ((MAX_RANDOM_RANGE+1) % x ); + do + { + n = ran1(); + } while (n > maxAcceptable); + + return lLow + (n % x); +} + + diff --git a/source/psp/Random.h b/source/psp/Random.h new file mode 100644 index 0000000..d290f70 --- /dev/null +++ b/source/psp/Random.h @@ -0,0 +1,18 @@ +#ifndef RANDOM_H +#define RANDOM_H + +// the random number seeding is automatic +#define MAX_RANDOM_RANGE 0x7FFFFFFFUL + +// restarts random generator +// setting lSeed to 0 causes the current time to be used as the seed +// random number generator will automatically seed itself on first use with current time if this is not called +void SeedRandomNumberGenerator(long lSeed = 0); + +// returns a random integer of range [low, high] +long RandomLong( long lLow, long lHigh ); + +// returns a random float of range [low, high) +float RandomFloat( float flLow, float flHigh ); + +#endif // RANDOM_H diff --git a/source/psp/VramExt.S b/source/psp/VramExt.S new file mode 100644 index 0000000..0b0c1d5 --- /dev/null +++ b/source/psp/VramExt.S @@ -0,0 +1,7 @@ + .set noreorder + +#include "pspstub.s" + + STUB_START "VramExt",0x40090000,0x00010005 + STUB_FUNC 0xA5853E1E,VramSetSize + STUB_END \ No newline at end of file diff --git a/source/psp/battery.cpp b/source/psp/battery.cpp new file mode 100644 index 0000000..cac813b --- /dev/null +++ b/source/psp/battery.cpp @@ -0,0 +1,107 @@ +/* +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 + +extern "C" +{ +#include "../quakedef.h" +} + +extern cvar_t show_bat; + +#include "battery.hpp" + +namespace quake +{ + namespace battery + { + // The previous battery level. + static int lastLevel = scePowerGetBatteryLifePercent(); + static bool lastCharging = scePowerGetBatteryChargingStatus(); + + void check() + { + // No battery? + if (!scePowerIsBatteryExist()) + { + // Don't report anything. + return; + } + + if (show_bat.value) + return; + + // Get the new battery status. + const int level = scePowerGetBatteryLifePercent(); + const bool charging = scePowerGetBatteryChargingStatus(); + + // Is the level not sensible? + if ((level < 0) || (level > 100)) + { + // Hopefully it will be sensible soon. + return; + } + + // Has the battery status changed? + if ((level != lastLevel) || (charging != lastCharging)) + { + // Charging? + if (charging) + { + // Inform the player. + Con_Printf("Battery %d%% (charging)\n", + level); + } + else + { + // How much time is left? + const int timeLeft = scePowerGetBatteryLifeTime(); + + // Is the time sensible? + if (timeLeft > 0) + { + // Convert the time to something readable. + const int hoursLeft = timeLeft / 60; + const int minutesLeft = timeLeft % 60; + + // Inform the player. + Con_Printf("Battery %d%% (%d hour%s %d minute%s)\n", + level, + hoursLeft, + (hoursLeft == 1) ? "" : "s", // Handle pluralisation of hour(s). + minutesLeft, + (minutesLeft == 1) ? "" : "s"); // Handle pluralisation of minute(s). + } + else + { + // It's a silly time, just report the battery level. + Con_Printf("Battery %d%%\n", + level); + } + } + + // Remember the status for next frame. + lastLevel = level; + lastCharging = charging; + } + } + } +} diff --git a/source/psp/battery.hpp b/source/psp/battery.hpp new file mode 100644 index 0000000..40d26c8 --- /dev/null +++ b/source/psp/battery.hpp @@ -0,0 +1,33 @@ +/* +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 QUAKE_BATTERY_HPP +#define QUAKE_BATTERY_HPP + +namespace quake +{ + namespace battery + { + // Checks the battery level and prints it to the console if it's changed. + void check(); + } +} + +#endif diff --git a/source/psp/cd.cpp b/source/psp/cd.cpp new file mode 100644 index 0000000..159d5e8 --- /dev/null +++ b/source/psp/cd.cpp @@ -0,0 +1,289 @@ +/* +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 "mp3.h" + +extern "C" +{ +#include "../quakedef.h" +} + +extern cvar_t bgmtype; +extern cvar_t bgmvolume; + + +namespace quake +{ + namespace cd + { + struct Sample + { + short left; + short right; + }; + +// static int file = -1; + static int last_track = 1; + + static bool playing = false; + static bool paused = false; + static bool enabled = false; + static float cdvolume = 0; + } +} + +using namespace quake; +using namespace quake::cd; + +static void CD_f (void) +{ + char *command; + + if (Cmd_Argc() < 2) + { + Con_Printf("commands:"); + Con_Printf("on, off, reset, remap, \n"); + Con_Printf("play, stop, loop, pause, resume\n"); + Con_Printf("eject, close, info\n"); + return; + } + + command = Cmd_Argv (1); + + if (Q_strcasecmp(command, "on") == 0) + { + enabled = true; + return; + } + + if (Q_strcasecmp(command, "off") == 0) + { + if (playing) + CDAudio_Stop(); + enabled = false; + return; + } + + if (Q_strcasecmp(command, "reset") == 0) + { + enabled = true; + if (playing) + CDAudio_Stop(); + return; + } + + if (Q_strcasecmp(command, "remap") == 0) + { + return; + } + + if (Q_strcasecmp(command, "close") == 0) + { + return; + } + + if (Q_strcasecmp(command, "play") == 0) + { + CDAudio_Play((byte)atoi(Cmd_Argv (2)), (qboolean) false); + return; + } + + if (Q_strcasecmp(command, "loop") == 0) + { + CDAudio_Play((byte)atoi(Cmd_Argv (2)), (qboolean) true); + return; + } + + if (Q_strcasecmp(command, "stop") == 0) + { + CDAudio_Stop(); + return; + } + + if (Q_strcasecmp(command, "pause") == 0) + { + CDAudio_Pause(); + return; + } + + if (Q_strcasecmp(command, "resume") == 0) + { + CDAudio_Resume(); + return; + } + + if (Q_strcasecmp(command, "eject") == 0) + { + if (playing) + CDAudio_Stop(); + return; + } + + if (Q_strcasecmp(command, "info") == 0) + { + Con_Printf("MP3 Player By Crow_bar\n"); + Con_Printf("Based On sceMp3 Lib\n"); + Con_Printf("Additional fixed by\n"); + Con_Printf("dr_mabuse1981 and Baker.\n"); + Con_Printf("\n"); + return; + } +} + +void CDAudio_VolumeChange(float bgmvolume) +{ + int volume = (int) (bgmvolume * (float) PSP_VOLUME_MAX); + //pspAudioSetVolume(1, volume, volume); + mp3_volume = volume; + cdvolume = bgmvolume; + +// Con_Printf("Volume changed to : %i\n", mp3_volume); + +} + +extern "C" int sceKernelDelayThread(int delay); + +void CDAudio_Track(char* trackname) +{ + //CDAudio_Stop(); + + char path[256]; + + sprintf(path, "%s\\sounds\\stream\\%s", host_parms.basedir, trackname); + + Sys_Error(path); +} + +void CDAudio_Play(byte track, qboolean looping) +{ + last_track = track; + CDAudio_Stop(); + + if (track < 1) + track = 1; + + char path[256]; + //snprintf(path, sizeof(path), "%s/%s/music/track%02u.mp3", host_parms.basedir, kurok ? "kurok" : "id1", track); + sprintf(path, "%s\\stream\\stream%02u.mp3", host_parms.basedir, track); + + int ret; + ret = mp3_start_play(path, 0); + + if(ret != 2) + { +// Con_Printf("Playing %s\n", path); + playing = true; + } + else + { + Con_Printf("Couldn't find %s\n", path); + playing = false; + Cvar_Set("bgmtype","none"); + CDAudio_VolumeChange(0); + } + + + CDAudio_VolumeChange(bgmvolume.value); +} + +void CDAudio_Stop(void) +{ + mp3_job_started = 0; + +// file = -1; + playing = false; + CDAudio_VolumeChange(0); +} + +void CDAudio_Pause(void) +{ + CDAudio_VolumeChange(0); + paused = true; +} + +void CDAudio_Resume(void) +{ + CDAudio_VolumeChange(bgmvolume.value); + paused = false; +} + +void CDAudio_Update(void) +{ + + //if (bgmvolume.value != mp3_volume) + // CDAudio_VolumeChange(bgmvolume.value); + //if(changeMp3Volume) CDAudio_VolumeChange(bgmvolume.value); + + if (strcmpi(bgmtype.string,"cd") == 0) { + if (playing == false) { + CDAudio_Play(last_track, (qboolean) false); + } + if (paused == true) { + CDAudio_Resume(); + } + + } else { + if (paused == false) { + CDAudio_Pause(); + } + if (playing == true) { + CDAudio_Stop(); + } + } +} + +int CDAudio_Init(void) +{ + if (cls.state == ca_dedicated) + return -1; + + if (COM_CheckParm("-nocdaudio")) + return -1; + + mp3_init(); + sceKernelDelayThread(5*10000); + + enabled = true; + + Cmd_AddCommand ("cd", CD_f); + + Con_Printf("MP3 library Initialized\n"); + + return 0; +} + +void CDAudio_Shutdown(void) +{ + Con_Printf("CDAudio_Shutdown\n"); + + CDAudio_Stop(); + + sceKernelDelayThread(5*10000); +// mp3_deinit(); + +} + diff --git a/source/psp/clipping.cpp b/source/psp/clipping.cpp new file mode 100644 index 0000000..55fd7ed --- /dev/null +++ b/source/psp/clipping.cpp @@ -0,0 +1,474 @@ +/* +Copyright (C) 2007 Peter Mackay and Chris Swindle. +Copyright (C) 2021 Sergey Galushko + +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. + +*/ + +#define CLIPPING_DEBUGGING 0 +#define CLIP_LEFT 1 +#define CLIP_RIGHT 1 +#define CLIP_BOTTOM 1 +#define CLIP_TOP 1 +#define CLIP_NEAR 0 +#define CLIP_FAR 0 + +#include "clipping.hpp" + +#include +#include +#include + +#include "math.hpp" + +extern "C" +{ +#include "../quakedef.h" +} + +namespace quake +{ + namespace clipping + { + // Plane types are sorted, most likely to clip first. + enum plane_index + { +#if CLIP_BOTTOM + plane_index_bottom, +#endif +#if CLIP_LEFT + plane_index_left, +#endif +#if CLIP_RIGHT + plane_index_right, +#endif +#if CLIP_TOP + plane_index_top, +#endif +#if CLIP_NEAR + plane_index_near, +#endif +#if CLIP_FAR + plane_index_far, +#endif + plane_count + }; + + // Types. + typedef ScePspFVector4 plane_type; + typedef plane_type frustum_t[plane_count]; + + // Transformed frustum. + static ScePspFMatrix4 projection_view_matrix __attribute__((aligned(16))); + static frustum_t projection_view_frustum __attribute__((aligned(16))); + static frustum_t clipping_frustum __attribute__((aligned(16))); + + // The temporary working buffers. + static const std::size_t max_clipped_vertices = 32; + static glvert_t work_buffer[2][max_clipped_vertices] __attribute__((aligned(16))); + + static inline void calculate_frustum(const ScePspFMatrix4& clip, frustum_t* frustum) + { + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + #if CLIP_NEAR_FAR + "lv.q C000, 0(%6)\n" // C000 = clip->x + "lv.q C010, 16(%6)\n" // C010 = clip->y + "lv.q C020, 32(%6)\n" // C020 = clip->z + "lv.q C030, 48(%6)\n" // C030 = clip->w + #else + "lv.q C000, 0(%4)\n" // C000 = clip->x + "lv.q C010, 16(%4)\n" // C010 = clip->y + "lv.q C020, 32(%4)\n" // C020 = clip->z + "lv.q C030, 48(%4)\n" // C030 = clip->w + #endif + /* Extract the BOTTOM plane */ + "vadd.s S100, S003, S001\n" // S100 = clip->x.w + clip->x.y + "vadd.s S101, S013, S011\n" // S101 = clip->y.w + clip->y.y + "vadd.s S102, S023, S021\n" // S102 = clip->z.w + clip->z.y + "vadd.s S103, S033, S031\n" // S103 = clip->w.w + clip->w.y + "vdot.q S110, C100, C100\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103 + "vzero.s S111\n" // S111 = 0 + "vcmp.s EZ, S110\n" // CC[0] = ( S110 == 0.0f ) + "vrsq.s S110, S110\n" // S110 = 1.0 / sqrt( S110 ) + "vcmovt.s S110, S111, 0\n" // if ( CC[0] ) S110 = S111 + "vscl.q C100[-1:1,-1:1,-1:1,-1:1], C100, S110\n" // C100 = C100 * S110 + "sv.q C100, %0\n" // Store plane from register + /* Extract the LEFT plane */ + "vadd.s S100, S003, S000\n" // S100 = clip->x.w + clip->x.x + "vadd.s S101, S013, S010\n" // S101 = clip->y.w + clip->y.x + "vadd.s S102, S023, S020\n" // S102 = clip->z.w + clip->z.x + "vadd.s S103, S033, S030\n" // S103 = clip->w.w + clip->w.x + "vdot.q S110, C100, C100\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103 + "vzero.s S111\n" // S111 = 0 + "vcmp.s EZ, S110\n" // CC[0] = ( S110 == 0.0f ) + "vrsq.s S110, S110\n" // S110 = 1.0 / sqrt( S110 ) + "vcmovt.s S110, S111, 0\n" // if ( CC[0] ) S110 = S111 + "vscl.q C100[-1:1,-1:1,-1:1,-1:1], C100, S110\n" // C100 = C100 * S110 + "sv.q C100, %1\n" // Store plane from register + /* Extract the RIGHT plane */ + "vsub.s S100, S003, S000\n" // S100 = clip->x.w - clip->x.x + "vsub.s S101, S013, S010\n" // S101 = clip->y.w - clip->y.x + "vsub.s S102, S023, S020\n" // S102 = clip->z.w - clip->z.x + "vsub.s S103, S033, S030\n" // S103 = clip->w.w - clip->w.x + "vdot.q S110, C100, C100\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103 + "vzero.s S111\n" // S111 = 0 + "vcmp.s EZ, S110\n" // CC[0] = ( S110 == 0.0f ) + "vrsq.s S110, S110\n" // S110 = 1.0 / sqrt( S110 ) + "vcmovt.s S110, S111, 0\n" // if ( CC[0] ) S110 = S111 + "vscl.q C100[-1:1,-1:1,-1:1,-1:1], C100, S110\n" // C100 = C100 * S110 + "sv.q C100, %2\n" // Store plane from register + /* Extract the TOP plane */ + "vsub.s S100, S003, S001\n" // S100 = clip->x.w - clip->x.y + "vsub.s S101, S013, S011\n" // S101 = clip->y.w - clip->y.y + "vsub.s S102, S023, S021\n" // S102 = clip->z.w - clip->z.y + "vsub.s S103, S033, S031\n" // S103 = clip->w.w - clip->w.y + "vdot.q S110, C100, C100\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103 + "vzero.s S111\n" // S111 = 0 + "vcmp.s EZ, S110\n" // CC[0] = ( S110 == 0.0f ) + "vrsq.s S110, S110\n" // S110 = 1.0 / sqrt( S110 ) + "vcmovt.s S110, S111, 0\n" // if ( CC[0] ) S110 = S111 + "vscl.q C100[-1:1,-1:1,-1:1,-1:1], C100, S110\n" // C100 = C100 * S110 + "sv.q C100, %3\n" // Store plane from register + #if CLIP_NEAR_FAR + /* Extract the NEAR plane */ + "vadd.s S100, S003, S002\n" // S100 = clip->x.w + clip->x.z + "vadd.s S101, S013, S012\n" // S101 = clip->y.w + clip->y.z + "vadd.s S102, S023, S022\n" // S102 = clip->z.w + clip->z.z + "vadd.s S103, S033, S032\n" // S103 = clip->w.w + clip->w.z + "vdot.q S110, C100, C100\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103 + "vzero.s S111\n" // S111 = 0 + "vcmp.s EZ, S110\n" // CC[0] = ( S110 == 0.0f ) + "vrsq.s S110, S110\n" // S110 = 1.0 / sqrt( S110 ) + "vcmovt.s S110, S111, 0\n" // if ( CC[0] ) S110 = S111 + "vscl.q C100[-1:1,-1:1,-1:1,-1:1], C100, S110\n" // C100 = C100 * S110 + "sv.q C100, %4\n" // Store plane from register + /* Extract the FAR plane */ + "vsub.s S100, S003, S002\n" // S100 = clip->x.w - clip->x.z + "vsub.s S101, S013, S012\n" // S101 = clip->y.w - clip->y.z + "vsub.s S102, S023, S022\n" // S102 = clip->z.w - clip->z.z + "vsub.s S103, S033, S032\n" // S103 = clip->w.w - clip->w.z + "vdot.q S110, C100, C100\n" // S110 = S100*S100 + S101*S101 + S102*S102 + S103*S103 + "vzero.s S111\n" // S111 = 0 + "vcmp.s EZ, S110\n" // CC[0] = ( S110 == 0.0f ) + "vrsq.s S110, S110\n" // S110 = 1.0 / sqrt( S110 ) + "vcmovt.s S110, S111, 0\n" // if ( CC[0] ) S110 = S111 + "vscl.q C100[-1:1,-1:1,-1:1,-1:1], C100, S110\n" // C100 = C100 * S110 + "sv.q C100, %5\n" // Store plane from register + #endif + ".set pop\n" // Restore assembler option + : "=m"( ( *frustum )[plane_index_bottom] ), + "=m"( ( *frustum )[plane_index_left] ), + "=m"( ( *frustum )[plane_index_right] ), + #if CLIP_NEAR_FAR + "=m"( ( *frustum )[plane_index_top] ), + "=m"( ( *frustum )[plane_index_near] ), + "=m"( ( *frustum )[plane_index_far] ) + #else + "=m"( ( *frustum )[plane_index_top] ) + #endif + : "r"( &clip ) + ); + } + + void begin_frame() + { + // Get the projection matrix. + sceGumMatrixMode(GU_PROJECTION); + ScePspFMatrix4 proj; + sceGumStoreMatrix(&proj); + + // Get the view matrix. + sceGumMatrixMode(GU_VIEW); + ScePspFMatrix4 view; + sceGumStoreMatrix(&view); + + // Restore matrix mode. + sceGumMatrixMode(GU_MODEL); + + // Combine the two matrices (multiply projection by view). + gumMultMatrix(&projection_view_matrix, &proj, &view); + + // Calculate and cache the clipping frustum. + calculate_frustum(projection_view_matrix, &projection_view_frustum); + + __asm__ volatile ( + "ulv.q C700, %4\n" // Load plane into register + "ulv.q C710, %5\n" // Load plane into register + "ulv.q C720, %6\n" // Load plane into register + "ulv.q C730, %7\n" // Load plane into register + "sv.q C700, %0\n" // Store plane from register + "sv.q C710, %1\n" // Store plane from register + "sv.q C720, %2\n" // Store plane from register + "sv.q C730, %3\n" // Store plane from register + : "=m"( clipping_frustum[plane_index_bottom] ), + "=m"( clipping_frustum[plane_index_left] ), + "=m"( clipping_frustum[plane_index_right] ), + "=m"( clipping_frustum[plane_index_top] ) + : "m"( projection_view_frustum[plane_index_bottom] ), + "m"( projection_view_frustum[plane_index_left] ), + "m"( projection_view_frustum[plane_index_right] ), + "m"( projection_view_frustum[plane_index_top] ) + ); + } + + void begin_brush_model() + { + // Get the model matrix. + ScePspFMatrix4 model_matrix; + sceGumStoreMatrix(&model_matrix); + + // Combine the matrices (multiply projection-view by model). + ScePspFMatrix4 projection_view_model_matrix; + gumMultMatrix(&projection_view_model_matrix, &projection_view_matrix, &model_matrix); + + // Calculate the clipping frustum. + calculate_frustum(projection_view_model_matrix, &clipping_frustum); + + __asm__ volatile ( + "ulv.q C700, %0\n" // Load plane into register + "ulv.q C710, %1\n" // Load plane into register + "ulv.q C720, %2\n" // Load plane into register + "ulv.q C730, %3\n" // Load plane into register + :: "m"(clipping_frustum[plane_index_bottom]), + "m"(clipping_frustum[plane_index_left]), + "m"(clipping_frustum[plane_index_right]), + "m"(clipping_frustum[plane_index_top]) + ); + } + + void end_brush_model() + { + // Restore the clipping frustum. + __asm__ volatile ( + "ulv.q C700, %4\n" // Load plane into register + "ulv.q C710, %5\n" // Load plane into register + "ulv.q C720, %6\n" // Load plane into register + "ulv.q C730, %7\n" // Load plane into register + "sv.q C700, %0\n" // Store plane from register + "sv.q C710, %1\n" // Store plane from register + "sv.q C720, %2\n" // Store plane from register + "sv.q C730, %3\n" // Store plane from register + : "=m"( clipping_frustum[plane_index_bottom] ), + "=m"( clipping_frustum[plane_index_left] ), + "=m"( clipping_frustum[plane_index_right] ), + "=m"( clipping_frustum[plane_index_top] ) + : "m"( projection_view_frustum[plane_index_bottom] ), + "m"( projection_view_frustum[plane_index_left] ), + "m"( projection_view_frustum[plane_index_right] ), + "m"( projection_view_frustum[plane_index_top] ) + ); + } + + // Is clipping required? + bool is_clipping_required(const struct glvert_s* vertices, std::size_t vertex_count) + { + int res; + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "move $8, %1\n" // $8 = &vertices[0] + "move $9, %2\n" // $9 = vertex_count + "li $10, 20\n" // $10 = 20( sizeof(glvert_t) ) + "mul $10, $10, $9\n" // $10 = $10 * $9 + "addu $10, $10, $8\n" // $10 = $10 + $8 + "0:\n" // loop + "ulv.q C610, 8($8)\n" // Load vertex into register skip tex cord 8 byte + "vone.s S613\n" // Now set the 4th entry to be 1 as that is just random + "vdot.q S620, C700, C610\n" // S620 = v[i] * frustrum[0] + "vdot.q S621, C710, C610\n" // S621 = v[i] * frustrum[1] + "vdot.q S622, C720, C610\n" // S622 = v[i] * frustrum[2] + "vdot.q S623, C730, C610\n" // S623 = v[i] * frustrum[3] + "vzero.q C610\n" // C610 = [0.0f, 0.0f, 0.0f. 0.0f] + "addiu %0, $0, 1\n" // res = 1 + "vcmp.q LT, C620, C610\n" // S620 < 0.0f || S621 < 0.0f || S622 < 0.0f || S623 < 0.0f + "bvt 4, 1f\n" // if ( CC[4] == 1 ) jump to exit + "addiu $8, $8, 20\n" // $8 = $8 + 20( sizeof(glvert_t) ) + "bne $10, $8, 0b\n" // if ( $9 != 0 ) jump to loop + "move %0, $0\n" // res = 0 + "1:\n" // exit + ".set pop\n" // Restore assembler option + : "=r"(res) + : "r"(vertices), "r"(vertex_count) + : "$8", "$9", "$10" + ); + return (res == 1) ? true : false; + } + + // Clips a polygon against a plane. + // http://hpcc.engin.umich.edu/CFD/users/charlton/Thesis/html/node90.html + static void clip_to_plane( + const plane_type& plane, + const glvert_t* const unclipped_vertices, + std::size_t const unclipped_vertex_count, + glvert_t* const clipped_vertices, + std::size_t* const clipped_vertex_count) + { + __asm__ ( + ".set push\n" // save assembler option + ".set noreorder\n" // suppress reordering + "move $8, %1\n" // $8 = uv[0] is S + "addiu $9, $8, 20\n" // $9 = uv[1] is P + "move $10, %2\n" // $10 = uvc + "li $11, 20\n" // $11 = 20( sizeof( gu_vert_t ) ) + "mul $11, $11, $10\n" // $11 = $11 * $10 + "addu $11, $11, $8\n" // $11 = $11 + $8 + "move $12, %3\n" // $12 = &cv[0] + "move %0, $0\n" // cvc = 0 + "ulv.q C100, %4\n" // Load plane into register + "vzero.s S110\n" // Set zero for cmp + "0:\n" // loop + "ulv.q C000, 0($8)\n" // Load vertex S TEX(8b) into register + "vzero.p C002\n" // Set the 3th and 4th entry C000 to zero + "ulv.q C010, 8($8)\n" // Load vertex S XYZ(12b) into register + "vzero.s S013\n" // Set the 4th entry C010 to zero + "ulv.q C020, 0($9)\n" // Load vertex P TEX(8b) into register + "vzero.p C022\n" // Set the 3th and 4th entry C020 to zero + "ulv.q C030, 8($9)\n" // Load vertex P XYZ(12b) into register + "vzero.s S033\n" // Set the 4th entry C030 to zero + "vdot.q S111, C100, C010\n" // S111 = plane * S + "vdot.q S112, C100, C030\n" // S112 = plane * P + "vadd.s S111, S111, S103\n" // S111 = S111 + plane->w is S dist + "vadd.s S112, S112, S103\n" // S112 = S112 + plane->w is P dist + "vcmp.s LE, S111, S110\n" // (S dist <= 0.0f) + "bvt 0, 2f\n" // if ( CC[0] == 1 ) jump to 2f + "nop\n" // ( delay slot ) + "vcmp.s LE, S112, S110\n" // (P dist <= 0.0f) + "bvt 0, 1f\n" // if ( CC[0] == 1 ) jump to 1f + "nop\n" // ( delay slot ) + "sv.s S020, 0($12)\n" // cv->uv[0] = C020 TEX U + "sv.s S021, 4($12)\n" // cv->uv[1] = C021 TEX V + "sv.s S030, 8($12)\n" // cv->xyz[0] = C030 XYZ X + "sv.s S031, 12($12)\n" // cv->xyz[1] = C031 XYZ Y + "sv.s S032, 16($12)\n" // cv->xyz[2] = C032 XYZ Z + "addiu $12, $12, 20\n" // $12 = $12 + 20( sizeof( gu_vert_t ) ) + "j 3f\n" // jump to next + "addiu %0, %0, 1\n" // cvc += 1 ( delay slot ) + "1:\n" + "vsub.s S112, S111, S112\n" // S112 = ( S dist - P dist ) + "vdiv.s S112, S111, S112\n" // S112 = S111 / S112 + "vsub.q C120, C020, C000\n" // C120 = C020(P TEX) - C000(S TEX) + "vsub.q C130, C030, C010\n" // C130 = C030(P XYZ) - C010(S XYZ) + "vscl.q C120, C120, S112\n" // C120 = C020 * S112 + "vscl.q C130, C130, S112\n" // C130 = C030 * S112 + "vadd.q C120, C120, C000\n" // C120 = C120 + C000(S TEX) + "vadd.q C130, C130, C010\n" // C130 = C130 + C010(S XYZ) + "sv.s S120, 0($12)\n" // cv->uv[0] = S120 TEX U + "sv.s S121, 4($12)\n" // cv->uv[1] = S121 TEX V + "sv.s S130, 8($12)\n" // cv->xyz[0] = S130 XYZ X + "sv.s S131, 12($12)\n" // cv->xyz[1] = S131 XYZ Y + "sv.s S132, 16($12)\n" // cv->xyz[2] = S132 XYZ Z + "addiu $12, $12, 20\n" // $12 = $12 + 20( sizeof( gu_vert_t ) ) + "j 3f\n" // jump to next + "addiu %0, %0, 1\n" // cvc += 1 ( delay slot ) + "2:\n" + "vcmp.s LE, S112, S110\n" // (P dist <= 0.0f) + "bvt 0, 3f\n" // if ( CC[0] == 1 ) jump to next + "nop\n" // ( delay slot ) + "vsub.s S112, S111, S112\n" // S112 = ( S dist - P dist ) + "vdiv.s S112, S111, S112\n" // S112 = S111 / S112 + "vsub.q C120, C020, C000\n" // C120 = C020(P TEX) - C000(S TEX) + "vsub.q C130, C030, C010\n" // C130 = C030(P XYZ) - C010(S XYZ) + "vscl.q C120, C120, S112\n" // C120 = C020 * S112 + "vscl.q C130, C130, S112\n" // C130 = C030 * S112 + "vadd.q C120, C120, C000\n" // C120 = C120 + C000(S TEX) + "vadd.q C130, C130, C010\n" // C130 = C130 + C010(S XYZ) + "sv.s S120, 0($12)\n" // cv->uv[0] = S120 TEX U + "sv.s S121, 4($12)\n" // cv->uv[1] = S121 TEX V + "sv.s S130, 8($12)\n" // cv->xyz[0] = S130 XYZ X + "sv.s S131, 12($12)\n" // cv->xyz[1] = S131 XYZ Y + "sv.s S132, 16($12)\n" // cv->xyz[2] = S132 XYZ Z + "addiu $12, $12, 20\n" // $12 = $12 + 20( sizeof( gu_vert_t ) ) + "addiu %0, %0, 1\n" // cvc += 1 + "sv.s S020, 0($12)\n" // cv->uv[0] = S020 TEX U + "sv.s S021, 4($12)\n" // cv->uv[1] = S021 TEX V + "sv.s S030, 8($12)\n" // cv->xyz[0] = S030 XYZ X + "sv.s S031, 12($12)\n" // cv->xyz[1] = S031 XYZ Y + "sv.s S032, 16($12)\n" // cv->xyz[2] = S032 XYZ Z + "addiu $12, $12, 20\n" // $12 = $12 + 20( sizeof( gu_vert_t ) ) + "addiu %0, %0, 1\n" // cvc += 1 + "3:\n" // next + "addiu $8, $8, 20\n" // $8 = $8 + 20( sizeof( gu_vert_t ) ) + "addiu $9, $8, 20\n" // $9 = $8 + 20( sizeof( gu_vert_t ) ) + "bne $9, $11, 4f\n" // if ( $11 != $9 ) jump to next + "nop\n" + "move $9, %1\n" // $9 = &uv[0] set P + "4:\n" + "bne $11, $8, 0b\n" // if ( $11 != $8 ) jump to loop + "nop\n" // ( delay slot ) + ".set pop\n" // Restore assembler option + : "+r"( *clipped_vertex_count ) + : "r"( unclipped_vertices ), "r"( unclipped_vertex_count ), "r"( clipped_vertices ), "m"( plane ) + : "$8", "$9", "$10", "$11", "$12" + ); + } + + // Clips a polygon against the frustum. + void clip( + const struct glvert_s* unclipped_vertices, + std::size_t unclipped_vertex_count, + const struct glvert_s** clipped_vertices, + std::size_t* clipped_vertex_count) + { + // No vertices to clip? + if (!unclipped_vertex_count) + { + // Error. + Sys_Error("Calling clip with zero vertices"); + } + + // Set up constants. + const plane_type* const last_plane = &clipping_frustum[plane_count]; + + // Set up the work buffer pointers. + const glvert_t* src = unclipped_vertices; + glvert_t* dst = work_buffer[0]; + std::size_t vertex_count = unclipped_vertex_count; + + // For each frustum plane... + for (const plane_type* plane = &clipping_frustum[0]; plane != last_plane; ++plane) + { + // Clip the poly against this frustum plane. + clip_to_plane(*plane, src, vertex_count, dst, &vertex_count); + + // No vertices left to clip? + if (!vertex_count) + { + // Quit early. + *clipped_vertex_count = 0; + return; + } + + // Use the next pair of buffers. + src = dst; + if (dst == work_buffer[0]) + { + dst = work_buffer[1]; + } + else + { + dst = work_buffer[0]; + } + } + + // Fill in the return data. + *clipped_vertices = src; + *clipped_vertex_count = vertex_count; + } + } +} diff --git a/source/psp/clipping.hpp b/source/psp/clipping.hpp new file mode 100644 index 0000000..3a786e4 --- /dev/null +++ b/source/psp/clipping.hpp @@ -0,0 +1,53 @@ +/* +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 QUAKE_CLIPPING_HPP +#define QUAKE_CLIPPING_HPP + +#include + +struct glvert_s; + +namespace quake +{ + namespace clipping + { + // Calculates clipping planes from the GU view and projection matrices. + void begin_frame(); + + // Calculates clipping planes from the GU view and projection matrices. + void begin_brush_model(); + + // Resets the frustum to camera space. + void end_brush_model(); + + // Is clipping required? + bool is_clipping_required(const struct glvert_s* vertices, std::size_t vertex_count); + + // Clips a polygon. + void clip( + const struct glvert_s* unclipped_vertices, + std::size_t unclipped_vertex_count, + const struct glvert_s** clipped_vertices, + std::size_t* clipped_vertex_count); + } +} + +#endif diff --git a/source/psp/dist/gpl.txt b/source/psp/dist/gpl.txt new file mode 100644 index 0000000..e37680c --- /dev/null +++ b/source/psp/dist/gpl.txt @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/source/psp/dist/readme.html b/source/psp/dist/readme.html new file mode 100644 index 0000000..a76c707 --- /dev/null +++ b/source/psp/dist/readme.html @@ -0,0 +1,117 @@ + + + PSP Quake + + + +

PSP Quake

+ +

Credits

+
    +
  • Original game by id Software.
  • +
  • Ported to the PSP by Peter Mackay and Chris Swindle.
  • +
  • Ripped off and uncredited by several others! :-)
  • +
+ +

Links

+
    +
  • Our home Page at SourceForge. You can get the latest version and the source code there.
  • +
+ +
+ +

Getting the game to run

+

Everybody!

+

To run Quake on your PSP, you need the data files from either the shareware version of Quake, or the full version.

+
    +
  1. Get the data files: +
      +
    1. Buy the full version of Quake here, or on eBay; or
    2. +
    3. Download the demo version of Quake for free here.
    4. +
    +
  2. +
  3. Copy the ID1 directory from the demo to Quake/ID1.
  4. +
+ +

Firmware 3.10 OE-A2 and compatible

+
    +
  • Copy the Quake directory to /PSP/GAME/Quake on your PSP.
  • +
+ +

Other firmware version?

+
    +
  • Only custom firmwares are supported at the moment.
  • +
+ +
+ +

Troubleshooting

+ +

Known in-game error messages

+ + + + + + + + + + + +
MessageCauseFix
W_LoadWadFile: couldn't load gfx.wadYou've not put the ID directory on your memory stick.Read this file again from the top.
+ +

Q & A

+ + + + + + + + + + + + + + + + + +
QuestionAnswer
Where do you get the full version of Quake?You buy it. It's quite hard to find nowadays, but you should be able to get it on eBay without too much difficulty. I did.
Where do you get the demo version of Quake?You download it. We have made it available here.
Why don't you bundle the shareware data files with your release?Because the demo's license says not to do that. We have to distribute the complete demo, not just the data files. We did however package it up as .rar and .zip, making it a lot easier to get at the files compared to id's DOS installer.
+ +

Found a bug or want something added?

+

Don't just bitch about it on a forum - we may not read it. Please add it to our SourceForge Bug Tracker.

+

Notes:

+
    +
  • Feature Requests are not Bugs, so please add things to the right tracker.
  • +
  • Check the "Closed" bugs in the tracker before adding yours. These are the ones which will be fixed in the next version. Duplicates will be deleted and the submitters potentially ridiculed.
  • +
+ +
+ +

Want to make a Quake-derived project?

+
    +
  1. Get our source code from SourceForge.
  2. +
  3. Credit us in your readme and release announcement. It's only fair.
  4. +
  5. Release your source. You have to because Quake's license says so.
  6. +
  7. Don't bundle data files from the full version of Quake with your game.
  8. +
+ +
+ +

Greets

+
    +
  • Hazel, for putting up with me working on this instea of giving you attention - Pete.
  • +
  • All the PSPSDK, PS2DEV and #PSPDEV guys.
  • +
  • Dark-AleX.
  • +
  • All the Noobz guys.
  • +
+ +
+ +

End of file. Thanks for making it this far!

+ + + diff --git a/source/psp/files-collected b/source/psp/files-collected new file mode 100644 index 0000000..64e229e Binary files /dev/null and b/source/psp/files-collected differ diff --git a/source/psp/fnmatch.cpp b/source/psp/fnmatch.cpp new file mode 100644 index 0000000..5331043 --- /dev/null +++ b/source/psp/fnmatch.cpp @@ -0,0 +1,142 @@ + +#define FNM_NOMATCH (1) /* Match failed. */ +#define FNM_NOSYS (2) /* Function not implemented. */ + +#define FNM_NOESCAPE (0x01) /* Disable backslash escaping. */ +#define FNM_PATHNAME (0x02) /* Slash must be matched by slash. */ +#define FNM_PERIOD (0x04) /* Period must be matched by period. */ +#define FNM_CASEFOLD (0x08) /* Pattern is matched case-insensitive */ +#define FNM_LEADING_DIR (0x10) /* Ignore / after Imatch. */ + +#include +#include +#include "fnmatch.h" + +#define EOS '\0' + +static const char *rangematch(const char *, int, int); + +static inline int +foldcase(int ch, int flags) +{ + + if ((flags & FNM_CASEFOLD) != 0 && isupper(ch)) + return (tolower(ch)); + return (ch); +} + +#define FOLDCASE(ch, flags) foldcase((unsigned char)(ch), (flags)) + +int fnmatch(const char *pattern, const char *string, int flags) +{ + const char *stringstart; + char c, test; + + for (stringstart = string;;) + switch (c = FOLDCASE(*pattern++, flags)) { + case EOS: + if ((flags & FNM_LEADING_DIR) && *string == '/') + return (0); + return (*string == EOS ? 0 : FNM_NOMATCH); + case '?': + if (*string == EOS) + return (FNM_NOMATCH); + if (*string == '/' && (flags & FNM_PATHNAME)) + return (FNM_NOMATCH); + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + ++string; + break; + case '*': + c = FOLDCASE(*pattern, flags); + /* Collapse multiple stars. */ + while (c == '*') + c = FOLDCASE(*++pattern, flags); + + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + + /* Optimize for pattern with * at end or before /. */ + if (c == EOS) { + if (flags & FNM_PATHNAME) + return ((flags & FNM_LEADING_DIR) || + strchr(string, '/') == NULL ? + 0 : FNM_NOMATCH); + else + return (0); + } else if (c == '/' && flags & FNM_PATHNAME) { + if ((string = strchr(string, '/')) == NULL) + return (FNM_NOMATCH); + break; + } + + /* General case, use recursion. */ + while ((test = FOLDCASE(*string, flags)) != EOS) { + if (!fnmatch(pattern, string, + flags & ~FNM_PERIOD)) + return (0); + if (test == '/' && flags & FNM_PATHNAME) + break; + ++string; + } + return (FNM_NOMATCH); + case '[': + if (*string == EOS) + return (FNM_NOMATCH); + if (*string == '/' && flags & FNM_PATHNAME) + return (FNM_NOMATCH); + if ((pattern = + rangematch(pattern, FOLDCASE(*string, flags), + flags)) == NULL) + return (FNM_NOMATCH); + ++string; + break; + case '\\': + if (!(flags & FNM_NOESCAPE)) { + if ((c = FOLDCASE(*pattern++, flags)) == EOS) { + c = '\\'; + --pattern; + } + } + /* FALLTHROUGH */ + default: + if (c != FOLDCASE(*string++, flags)) + return (FNM_NOMATCH); + break; + } + /* NOTREACHED */ +} + +static const char * rangematch(const char *pattern, int test, int flags) +{ + int negate, ok; + char c, c2; + + if ((negate = (*pattern == '!' || *pattern == '^')) != 0) + ++pattern; + + for (ok = 0; (c = FOLDCASE(*pattern++, flags)) != ']';) { + if (c == '\\' && !(flags & FNM_NOESCAPE)) + c = FOLDCASE(*pattern++, flags); + if (c == EOS) + return (NULL); + if (*pattern == '-' + && (c2 = FOLDCASE(*(pattern+1), flags)) != EOS && + c2 != ']') { + pattern += 2; + if (c2 == '\\' && !(flags & FNM_NOESCAPE)) + c2 = FOLDCASE(*pattern++, flags); + if (c2 == EOS) + return (NULL); + if (c <= test && test <= c2) + ok = 1; + } else if (c == test) + ok = 1; + } + return (ok == negate ? NULL : pattern); +} + diff --git a/source/psp/fnmatch.h b/source/psp/fnmatch.h new file mode 100644 index 0000000..dcfdc5e --- /dev/null +++ b/source/psp/fnmatch.h @@ -0,0 +1,10 @@ +#define FNM_NOMATCH (1) /* Match failed. */ +#define FNM_NOSYS (2) /* Function not implemented. */ + +#define FNM_NOESCAPE (0x01) /* Disable backslash escaping. */ +#define FNM_PATHNAME (0x02) /* Slash must be matched by slash. */ +#define FNM_PERIOD (0x04) /* Period must be matched by period. */ +#define FNM_CASEFOLD (0x08) /* Pattern is matched case-insensitive */ +#define FNM_LEADING_DIR (0x10) /* Ignore / after Imatch. */ + +int fnmatch(const char *pattern, const char *string, int flags); diff --git a/source/psp/gethost.cpp b/source/psp/gethost.cpp new file mode 100644 index 0000000..f9b975c --- /dev/null +++ b/source/psp/gethost.cpp @@ -0,0 +1,115 @@ + +// Copied from newlib as the current newlib did not include it + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_NAME 512 + +#ifdef F_h_errno +int h_errno = NETDB_SUCCESS; +#endif + +struct hostent *gethostbyaddr(const void *addr, int len, int type) +{ + static struct hostent ent; + static char * aliases[1] = { NULL }; + char buf[1024]; + static char sname[MAX_NAME] = ""; + static struct in_addr saddr = { 0 }; + static char *addrlist[2] = { (char *) &saddr, NULL }; + int rid; + int err; + + if((len != sizeof(struct in_addr)) || (type != AF_INET) || (addr == NULL)) + { + h_errno = HOST_NOT_FOUND; + return NULL; + } + + memcpy(&saddr, addr, len); + + if(sceNetResolverCreate(&rid, buf, sizeof(buf)) < 0) + { + h_errno = NO_RECOVERY; + return NULL; + } + + err = sceNetResolverStartAtoN(rid, &saddr, sname, sizeof(sname), 2, 3); + sceNetResolverStop(rid); + sceNetResolverDelete(rid); + if(err < 0) + { + h_errno = HOST_NOT_FOUND; + return NULL; + } + + ent.h_name = sname; + ent.h_aliases = aliases; + ent.h_addrtype = AF_INET; + ent.h_length = sizeof(struct in_addr); + ent.h_addr_list = addrlist; + ent.h_addr = (char *) &saddr; + + return &ent; +} + +struct hostent *gethostbyname(const char *name) +{ + static struct hostent ent; + char buf[1024]; + static char sname[MAX_NAME] = ""; + static struct in_addr saddr = { 0 }; + static char *addrlist[2] = { (char *) &saddr, NULL }; + int rid; + + if(sceNetInetInetAton(name, &saddr) == 0) + { + int err; + + if(sceNetResolverCreate(&rid, buf, sizeof(buf)) < 0) + { + h_errno = NO_RECOVERY; + return NULL; + } + + err = sceNetResolverStartNtoA(rid, name, &saddr, 2, 3); + sceNetResolverDelete(rid); + if(err < 0) + { + h_errno = HOST_NOT_FOUND; + return NULL; + } + + } + + snprintf(sname, MAX_NAME, "%s", name); + ent.h_name = sname; + ent.h_aliases = 0; + ent.h_addrtype = AF_INET; + ent.h_length = sizeof(struct in_addr); + ent.h_addr_list = addrlist; + ent.h_addr = (char *) &saddr; + + return &ent; +} + +int gethostname(char *name, int namelen) +{ + memset(name, 0, namelen); + if(sceUtilityGetSystemParamString(PSP_SYSTEMPARAM_ID_STRING_NICKNAME, + name, namelen) == PSP_SYSTEMPARAM_RETVAL_FAIL) + { + strcpy(name, "UNNAMED"); + return -1; + } + + return 1; +} diff --git a/source/psp/gethost.hpp b/source/psp/gethost.hpp new file mode 100644 index 0000000..680e56d --- /dev/null +++ b/source/psp/gethost.hpp @@ -0,0 +1,9 @@ + +#ifndef GETHOST +#define GETHOST + +struct hostent *gethostbyaddr(const void *addr, int len, int type); +struct hostent *gethostbyname(const char *name); +int gethostname(char *name, int namelen); + +#endif diff --git a/source/psp/input.cpp b/source/psp/input.cpp new file mode 100644 index 0000000..58a4446 --- /dev/null +++ b/source/psp/input.cpp @@ -0,0 +1,362 @@ +/* +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 + +extern "C" +{ +#include "../quakedef.h" +} + +namespace quake +{ + namespace input + { + // A map from button mask to Quake key. + static const unsigned int buttonCount = sizeof(unsigned int) * 8; + typedef int ButtonToKeyMap[buttonCount]; + static ButtonToKeyMap buttonToGameKeyMap; + static ButtonToKeyMap buttonToConsoleKeyMap; + static ButtonToKeyMap buttonToMessageKeyMap; + static ButtonToKeyMap buttonToMenuKeyMap; + + // The previous key state (for checking if things changed). + static SceCtrlData lastPad; + static bool readyToBindKeys = false; + + static unsigned int buttonMaskToShift(unsigned int mask) + { + // Bad mask? + if (!mask) + return 0; + + // Shift right until we hit a 1. + unsigned int shift = 0; + while ((mask & 1) == 0) + { + mask >>= 1; + ++shift; + } + return shift; + } + } +} + +// Quake globals. +extern "C" int bind_grab; + +using namespace quake; +using namespace quake::input; + +extern cvar_t in_analog_strafe; +extern cvar_t in_x_axis_adjust; +extern cvar_t in_y_axis_adjust; +extern cvar_t in_mlook; //Heffo - mlook cvar + +void IN_Init (void) +{ + // Set up the controller. + sceCtrlSetSamplingCycle(0); + sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG); + + // Japanese users would prefer to have X as cancel and O as OK. + //unsigned int okButton = PSP_CTRL_CIRCLE; + //unsigned int cancelButton = PSP_CTRL_CROSS; + unsigned int okButton = PSP_CTRL_CROSS; + unsigned int cancelButton = PSP_CTRL_CIRCLE; + + + // Build the button to Quake key maps. + // Common keys: + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_SELECT)] = K_SELECT; + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_START)] = K_ESCAPE; + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_HOME)] = K_HOME; + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_NOTE)] = K_NOTE; + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_SCREEN)] = K_SCREEN; + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_UP)] = K_UPARROW; + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_RIGHT)] = K_RIGHTARROW; + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_DOWN)] = K_DOWNARROW; + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_LEFT)] = K_LEFTARROW; + memcpy(buttonToConsoleKeyMap, buttonToGameKeyMap, sizeof(ButtonToKeyMap)); + memcpy(buttonToMessageKeyMap, buttonToGameKeyMap, sizeof(ButtonToKeyMap)); + memcpy(buttonToMenuKeyMap, buttonToGameKeyMap, sizeof(ButtonToKeyMap)); + + // Game keys: + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_LTRIGGER)] = K_AUX1; + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_RTRIGGER)] = K_AUX2; + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_TRIANGLE)] = K_JOY1; + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_CIRCLE)] = K_JOY2; + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_CROSS)] = K_JOY3; + buttonToGameKeyMap[buttonMaskToShift(PSP_CTRL_SQUARE)] = K_JOY4; + + // Console keys: + buttonToConsoleKeyMap[buttonMaskToShift(PSP_CTRL_LTRIGGER)] = K_PGUP; + buttonToConsoleKeyMap[buttonMaskToShift(PSP_CTRL_RTRIGGER)] = K_PGDN; + buttonToConsoleKeyMap[buttonMaskToShift(okButton)] = K_ENTER; + buttonToConsoleKeyMap[buttonMaskToShift(cancelButton)] = K_ESCAPE; + buttonToConsoleKeyMap[buttonMaskToShift(PSP_CTRL_TRIANGLE)] = K_DEL; + buttonToConsoleKeyMap[buttonMaskToShift(PSP_CTRL_SQUARE)] = K_INS; + + // Message keys: + memcpy(buttonToMessageKeyMap, buttonToConsoleKeyMap, sizeof(ButtonToKeyMap)); + + // Menu keys: + buttonToMenuKeyMap[buttonMaskToShift(PSP_CTRL_SQUARE)] = K_INS; + buttonToMenuKeyMap[buttonMaskToShift(cancelButton)] = K_ESCAPE; + buttonToMenuKeyMap[buttonMaskToShift(okButton)] = K_ENTER; + buttonToMenuKeyMap[buttonMaskToShift(PSP_CTRL_TRIANGLE)] = K_DEL; + buttonToMenuKeyMap[buttonMaskToShift(PSP_CTRL_LTRIGGER)] = K_AUX1; + buttonToMenuKeyMap[buttonMaskToShift(PSP_CTRL_RTRIGGER)] = K_AUX2; +} + +void IN_Shutdown (void) +{ + +} + +void IN_Commands (void) +{ + // Changed in or out of key binding mode? + if ((bind_grab != 0) != readyToBindKeys) + { + // Was in key binding mode? + if (readyToBindKeys) + { + // Just left key binding mode. + // Release all keys which are pressed. + for (unsigned int button = 0; button < buttonCount; ++button) + { + // Was the button pressed? + if (lastPad.Buttons & (1 << button)) + { + // Is the button in the map? + const int key = buttonToGameKeyMap[button]; + if (key) + { + // Send a release event. + Key_Event(key, qfalse); + } + } + } + + // We're not ready to bind keys any more. + readyToBindKeys = false; + } + else + { + // Entering key binding mode. + // Release all keys which are pressed. + for (unsigned int button = 0; button < buttonCount; ++button) + { + // Was the button pressed? + if (lastPad.Buttons & (1 << button)) + { + // Is the button in the map? + const int key = buttonToMenuKeyMap[button]; + if (key) + { + // Send a release event. + Key_Event(key, qfalse); + } + } + } + + // We're now ready to bind keys. + readyToBindKeys = true; + } + } + + // Use a different key mapping depending on where inputs are going to go. + const ButtonToKeyMap* buttonToKeyMap = 0; + switch (key_dest) + { + case key_game: + buttonToKeyMap = &buttonToGameKeyMap; + break; + + case key_console: + buttonToKeyMap = &buttonToConsoleKeyMap; + break; + + case key_message: + buttonToKeyMap = &buttonToMessageKeyMap; + break; + + case key_menu: + case key_menu_pause: + // Binding keys? + if (readyToBindKeys) + { + buttonToKeyMap = &buttonToGameKeyMap; + } + else + { + buttonToKeyMap = &buttonToMenuKeyMap; + } + break; + + default: + Sys_Error("Unhandled key destination %d", key_dest); + return; + } + + // Read the pad state. + SceCtrlData pad; + sceCtrlPeekBufferPositive(&pad, 1); + + // Find out which buttons have changed. + SceCtrlData deltaPad; + deltaPad.Buttons = pad.Buttons ^ lastPad.Buttons; + deltaPad.Lx = pad.Lx - lastPad.Lx; + deltaPad.Ly = pad.Ly - lastPad.Ly; + deltaPad.TimeStamp = pad.TimeStamp - lastPad.TimeStamp; + + // Handle buttons which have changed. + for (unsigned int button = 0; button < buttonCount; ++button) + { + // Has the button changed? + const unsigned int buttonMask = 1 << button; + + if (deltaPad.Buttons & buttonMask) + { + // Is the button in the map? + const int key = (*buttonToKeyMap)[button]; + if (key) + { + // Send an event. + const qboolean state = (pad.Buttons & buttonMask) ? qtrue : qfalse; + Key_Event(key, state); + } + } + } + + // Remember the pad state for next time. + lastPad = pad; +} + +float IN_CalcInput(int axis, float speed, float tolerance, float acceleration) { + + float value = ((float) axis / 128.0f) - 1.0f; + + if (value == 0.0f) { + return 0.0f; + } + + float abs_value = fabs(value); + + if (abs_value < tolerance) { + return 0.0f; + } + + abs_value -= tolerance; + abs_value /= (1.0f - tolerance); + abs_value = powf(abs_value, acceleration); + abs_value *= speed; + + if (value < 0.0f) { + value = -abs_value; + } else { + value = abs_value; + } + return value; +} + +extern cvar_t scr_fov; +extern int original_fov, final_fov; +void IN_Move (usercmd_t *cmd) +{ + unsigned char analog_strafe = 0; + // Don't let the pitch drift back to centre if analog nub look is on. + if (in_mlook.value) + V_StopPitchDrift(); + else { + if (in_analog_strafe.value || (in_strafe.state & 1)) { + analog_strafe = 1; + } + } + // Read the pad state. + SceCtrlData pad; + sceCtrlPeekBufferPositive(&pad, 1); + + // Convert the inputs to floats in the range [-1, 1]. + // Implement the dead zone. + float speed; + float deadZone = in_tolerance.value; + float acceleration = in_acceleration.value; + int x_adjust = in_x_axis_adjust.value; + int y_adjust = in_y_axis_adjust.value; + + //shpuld begin + if (!analog_strafe) { + speed = in_sensitivity.value; + + // ==== Aim Assist + ==== + // cut look speed in half when facing enemy, unless + // mag is empty + if ((in_aimassist.value) && (sv_player->v.facingenemy == 1) && cl.stats[STAT_CURRENTMAG] > 0) { + speed *= 0.5; + } + // additionally, slice look speed when ADS/scopes + if (cl.stats[STAT_ZOOM] == 1) + speed *= 0.5; + else if (cl.stats[STAT_ZOOM] == 2) + speed *= 0.25; + } else { + speed = sv_player->v.maxspeed/150; + if (cl.stats[STAT_ZOOM] == 1) + speed *= 2; + else if (cl.stats[STAT_ZOOM] == 2) + speed *= 4; + } + //shpuld end + + float x = IN_CalcInput(pad.Lx+x_adjust, speed, deadZone, acceleration); + float y = IN_CalcInput(pad.Ly+y_adjust, speed, deadZone, acceleration); + + // Set the yaw. + + // Analog nub look? + if (!analog_strafe) { + const float yawScale = 30.0f; + cl.viewangles[YAW] -= yawScale * x * host_frametime; + + if (in_mlook.value) + { + // Set the pitch. + const bool invertPitch = m_pitch.value < 0; + const float pitchScale = yawScale * (invertPitch ? -1 : 1); + cl.viewangles[PITCH] += pitchScale * y * host_frametime; + + // Don't look too far up or down. + if (cl.viewangles[PITCH] > 80.0f) + cl.viewangles[PITCH] = 80.0f; + if (cl.viewangles[PITCH] < -70.0f) + cl.viewangles[PITCH] = -70.0f; + } + else + { + // Move using up and down. + cmd->forwardmove -= cl_forwardspeed * y; + } + } else { + cmd->sidemove += cl_sidespeed * x; + cmd->forwardmove -= cl_forwardspeed * y; + } +} diff --git a/source/psp/iridlibs/cred.txt b/source/psp/iridlibs/cred.txt new file mode 100644 index 0000000..897e129 --- /dev/null +++ b/source/psp/iridlibs/cred.txt @@ -0,0 +1 @@ +creds to iridescentrose diff --git a/source/psp/iridlibs/driver/me.h b/source/psp/iridlibs/driver/me.h new file mode 100644 index 0000000..2157085 --- /dev/null +++ b/source/psp/iridlibs/driver/me.h @@ -0,0 +1,37 @@ +#ifndef me_h +#define me_h + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct me_struct +{ + int start; + int done; + int (*func)(int); // function ptr - func takes an int argument and returns int + int param; // function argument + int result; // function return value + int precache_len; // amount of space to invalidate before running func, -1 = all + void *precache_addr; // address of space to invalidate before running func + int postcache_len; // amount of space to flush after running func, -1 = all + void *postcache_addr; // address of space to flush after running func + unsigned int signals; +}; + +int InitME(volatile struct me_struct *mei ); +void KillME(volatile struct me_struct *mei ); +int CallME(volatile struct me_struct *mei, int func, int param, int prelen, void *preadr, int postlen, void *postadr); +int WaitME(volatile struct me_struct *mei); +int BeginME(volatile struct me_struct *mei, int func, int param, int prelen, void *preadr, int postlen, void *postadr); +int CheckME(volatile struct me_struct *mei); +unsigned int SignalME(volatile struct me_struct *mei, unsigned int sigmask, unsigned int sigset); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/psp/iridlibs/libME.a b/source/psp/iridlibs/libME.a new file mode 100644 index 0000000..02ed73b Binary files /dev/null and b/source/psp/iridlibs/libME.a differ diff --git a/source/psp/iridlibs/libPerf.a b/source/psp/iridlibs/libPerf.a new file mode 100644 index 0000000..369c8ca Binary files /dev/null and b/source/psp/iridlibs/libPerf.a differ diff --git a/source/psp/iridlibs/melib.h b/source/psp/iridlibs/melib.h new file mode 100644 index 0000000..682f28f --- /dev/null +++ b/source/psp/iridlibs/melib.h @@ -0,0 +1,66 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include + +/** +* The mode of execution for a specific job. +*/ +#define MELIB_EXEC_DEFAULT 0x0 /** Executes on the ME, unless Dynamic Rebalancing is turned on */ +#define MELIB_EXEC_CPU 0x1 /** Executes specifically on the main CPU, regardless of job mode. Always runs synchronously (gives a small delay between jobs).*/ +#define MELIB_EXEC_ME 0x2 /** Executes specifically on the Media Engine, regardless of job mode. Always runs asynchronously.*/ + + +/** +* This structure is used to determine job execution. +* Given the properties in this structure, the manager will execute correctly +*/ +struct JobInfo { + unsigned char id; /** This ID is purely useless for now - but may be used in the future for performance tracking */ + unsigned char execMode; /** Uses execution mode to specify where the code will run and/or if said code can be dynamically rebalanced */ +}; + +struct Job; + +/** +* Job Data is an integer pointer to an address with the data. +*/ +typedef int JobData; + +/** +* This typedef defines a JobFunction as an integer function with given data. +*/ +typedef int (*JobFunction)(JobData ptr); + +/** +* This structure is used to give job information alongside the job itself and the data needed. +*/ +struct Job { + struct JobInfo jobInfo; + JobFunction function; + JobData data; +}; + +/** +* JobManager class. This class only can have one instance for the ME. +*/ + +void J_Init(bool dynamicRebalance); /** Initialize the job manager with the option to dynamically rebalance loads. */ +void J_Cleanup(); /** Cleans up and ends execution. */ + +void J_AddJob(struct Job* job); /** Adds a job to the queue. If the queue is full (max 256) then it will force a dispatch before adding more. */ +void J_ClearJob(); /** Clears and deletes all jobs */ + +void J_DispatchJobs(float cpuTime); /** Starts a thread to dispatch jobs and execute! CPU Time is the time used by the rest of the system for Dynamic Rebalancing - it's unused otherwise. */ + + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/source/psp/iridlibs/perflib.h b/source/psp/iridlibs/perflib.h new file mode 100644 index 0000000..6c57125 --- /dev/null +++ b/source/psp/iridlibs/perflib.h @@ -0,0 +1,82 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * This method clears the internal timers and mechanisms of PFL + * @param frameRelative - setting this to true means that you compare percentage of GPU vs CPU. This is useful for finding bottlenecks. + */ + void PFL_Init(bool frameRelative); + + /** + * This method begins a record of when the CPU has begun doing tasks. This should be put immediately at the beginning of your game loop. + */ + void PFL_BeginCPURecord(); + + /** + * This method ends a record of when the CPU has finished doing tasks. This should be put before the final rendering call of your game loop. + */ + void PFL_EndCPURecord(); + + /** + * This method extracts the CPU time taken between the last Start and End of the CPU performance recording (in milliseconds). + */ + double PFL_GetCPUTime(); + + /** + * This method helps in calculation of the CPU and GPU percentages by setting a targeted framerate. Percentages above 100 mean that the CPU and or GPU is taking longer than expected. This only matters in non-full profiling mode. + */ + void PFL_SetTargetFrameRate(int framerate); + + /** + * Returns the current frame rate target. This only matters in non-full profiling mode. + */ + int PFL_GetTargetFrameRate(); + + /** + * This method extracts the CPU time taken between the last Start and End of the CPU performance recording. + */ + double PFL_GetCPUPercentage(); + + /** + * This method begins a record of when the GPU begins doing tasks. This should be put immediately after sceGuFinish() and before sceGuSync(0, 0) + */ + void PFL_BeginGPURecord(); + + /** + * This method ends a record of when the GPU has finished doing tasks. This should be put immediately after sceGuSync(0, 0) + */ + void PFL_EndGPURecord(); + + /** + * This method begins a record of when the system is waiting for a VBlank. This should be put immediately before sceDisplayWaitVblankStart() and swap buffers. + */ + void PFL_BeginVBLRecord(); + + /** + * This method begins a record of when the system has finished a VBlank. This should be put immediately after sceGuSwapBuffers(). + */ + void PFL_EndVBLRecord(); + + /** + * This method extracts the total GPU time taken. + */ + double PFL_GetGPUTime(); + + /** + * This method extracts the total GPU time as a percentage. + */ + double PFL_GetGPUPercentage(); + + /** + * This method extracts the total VBL time taken. + */ + double PFL_GetVBLTime(); + + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/source/psp/m33libs/kubridge.h b/source/psp/m33libs/kubridge.h new file mode 100644 index 0000000..aa8a065 --- /dev/null +++ b/source/psp/m33libs/kubridge.h @@ -0,0 +1,98 @@ +#ifndef __KULIBRARY__ + +#define __KULIBRARY__ + +#include +#include +#include +#include + +/** + * Functions to let user mode access certain functions only available in + * kernel mode +*/ + +/** + * Load a module using ModuleMgrForKernel. + * + * @param path - The path to the module to load. + * @param flags - Unused, always 0 . + * @param option - Pointer to a mod_param_t structure. Can be NULL. + * + * @returns The UID of the loaded module on success, otherwise one of ::PspKernelErrorCodes. + */ +SceUID kuKernelLoadModule(const char *path, int flags, SceKernelLMOption *option); + + +/** + * Load a module with a specific apitype + * + * @param apìtype - The apitype + * @param path - The path to the module to load. + * @param flags - Unused, always 0 . + * @param option - Pointer to a mod_param_t structure. Can be NULL. + * + * @returns The UID of the loaded module on success, otherwise one of ::PspKernelErrorCodes. + */ +SceUID kuKernelLoadModuleWithApitype2(int apitype, const char *path, int flags, SceKernelLMOption *option); + +/** + * Gets the api type + * + * @returns the api type in which the system has booted +*/ +int kuKernelInitApitype(); + +/** + * Gets the filename of the executable to be launched after all modules of the api. + * + * @param initfilename - String where copy the initfilename + * @returns 0 on success +*/ +int kuKernelInitFileName(char *initfilename); + +/** + * + * Gets the device in which the application was launched. + * + * @returns the device code, one of PSPBootFrom values. +*/ +int kuKernelBootFrom(); + +/** + * Get the key configuration in which the system has booted. + * + * @returns the key configuration code, one of PSPKeyConfig values +*/ +int kuKernelInitKeyConfig(); + +/** + * Get the user level of the current thread + * + * @return The user level, < 0 on error + */ +int kuKernelGetUserLevel(void); + +/** + * Set the protection of a block of ddr memory + * + * @param addr - Address to set protection on + * @param size - Size of block + * @param prot - Protection bitmask + * + * @return < 0 on error + */ +int kuKernelSetDdrMemoryProtection(void *addr, int size, int prot); + +/** + * Gets the model of the PSP from user mode. + * This function is available since 3.60 M33. + * In previous version, use the kernel function sceKernelGetModel + * + * @return one of PspModel values +*/ +int kuKernelGetModel(void); + + +#endif + diff --git a/source/psp/m33libs/libpng.a b/source/psp/m33libs/libpng.a new file mode 100644 index 0000000..098840f Binary files /dev/null and b/source/psp/m33libs/libpng.a differ diff --git a/source/psp/m33libs/libpspaudiocodec.a b/source/psp/m33libs/libpspaudiocodec.a new file mode 100644 index 0000000..56a287d Binary files /dev/null and b/source/psp/m33libs/libpspaudiocodec.a differ diff --git a/source/psp/m33libs/libpspkubridge.a b/source/psp/m33libs/libpspkubridge.a new file mode 100644 index 0000000..779c54f Binary files /dev/null and b/source/psp/m33libs/libpspkubridge.a differ diff --git a/source/psp/m33libs/libz.a b/source/psp/m33libs/libz.a new file mode 100644 index 0000000..a040180 Binary files /dev/null and b/source/psp/m33libs/libz.a differ diff --git a/source/psp/main.cpp b/source/psp/main.cpp new file mode 100644 index 0000000..09035fa --- /dev/null +++ b/source/psp/main.cpp @@ -0,0 +1,794 @@ +/* +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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. + +*/ + +#define CONSOLE_DEBUG 0 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" +{ +#include "../quakedef.h" +#include "../thread.h" +#include "m33libs/kubridge.h" +#include "iridlibs/perflib.h" +void VramSetSize(int kb); +} + +#include "battery.hpp" +#include "system.hpp" +#include "module.h" + +// Running a dedicated server? +qboolean isDedicated = qfalse; + +qboolean depthfl = qfalse; + +extern int com_argc; +extern char **com_argv; + +int sys_psp_model; + +void Sys_ReadCommandLineFile (char* netpath); + +/* + MOTO - Can't go above heap of 10MB on PHAT models.. which is an issue, in theory + we should be able to cap at a heap size 14MB.. oh well. +*/ + +#define printf pspDebugScreenPrintf +#define MIN_HEAP_MB 6 +#define MAX_HEAP_MB (PSP_HEAP_SIZE_MB-1) + +namespace quake +{ + namespace main + { + // Clock speeds. + extern const int cpuClockSpeed = scePowerGetCpuClockFrequencyInt(); + extern const int ramClockSpeed = cpuClockSpeed; + extern const int busClockSpeed = scePowerGetBusClockFrequencyInt(); + +#ifdef PSP_SOFTWARE_VIDEO + #ifdef SLIM + // How big a heap to allocate. + static size_t heapSize = 16 * 1024 * 1024; + #else + static size_t heapSize = 17 * 1024 * 1024; + #endif +#endif // PSP_SOFTWARE_VIDEO +#ifdef KERNEL_MODE + #ifdef SLIM + // How big a heap to allocate. + static size_t heapSize = 34 * 1024 * 1024; + #else + static size_t heapSize = 10 * 1024 * 1024; + #endif +#else + #ifdef SLIM + // How big a heap to allocate. + static size_t heapSize = 34 * 1024 * 1024; + #else + static size_t heapSize = 10 * 1024 * 1024; + #endif +#endif + + // Should the main loop stop running? + static volatile bool quit = false; + + // Is the PSP in suspend mode? + static volatile bool suspended = false; + + static int exitCallback(int arg1, int arg2, void* common) + { + // Signal the main thread to stop. + quit = true; + return 0; + } + + static int powerCallback(int unknown, int powerInfo, void* common) + { + if (powerInfo & PSP_POWER_CB_POWER_SWITCH || powerInfo & PSP_POWER_CB_SUSPENDING) + { + suspended = true; + } + else if (powerInfo & PSP_POWER_CB_RESUMING) + { + } + else if (powerInfo & PSP_POWER_CB_RESUME_COMPLETE) + { + suspended = false; + } + + return 0; + } + + static int callback_thread(SceSize args, void *argp) + { + // Register the exit callback. + const SceUID exitCallbackID = sceKernelCreateCallback("exitCallback", exitCallback, NULL); + sceKernelRegisterExitCallback(exitCallbackID); + + // Register the power callback. + const SceUID powerCallbackID = sceKernelCreateCallback("powerCallback", powerCallback, NULL); + scePowerRegisterCallback(0, powerCallbackID); + + // Sleep and handle callbacks. + sceKernelSleepThreadCB(); + return 0; + } + + static int setUpCallbackThread(void) + { + const int thid = sceKernelCreateThread("update_thread", callback_thread, 0x11, 0xFA0, PSP_THREAD_ATTR_USER, 0); + if (thid >= 0) + sceKernelStartThread(thid, 0, 0); + return thid; + } +//#endif + + static void disableFloatingPointExceptions() + { +#ifndef _WIN32 + asm( + ".set push\n" + ".set noreorder\n" + "cfc1 $2, $31\n" // Get the FP Status Register. + "lui $8, 0x80\n" // (?) + "and $8, $2, $8\n" // Mask off all bits except for 23 of FCR. (? - where does the 0x80 come in?) + "ctc1 $8, $31\n" // Set the FP Status Register. + ".set pop\n" + : // No inputs. + : // No outputs. + : "$2", "$8" // Clobbered registers. + ); +#endif + } + } +} + +using namespace quake; +using namespace quake::main; + +#define MAX_NUM_ARGVS 50 +#define MAX_ARG_LENGTH 64 +static char *empty_string = ""; +char *f_argv[MAX_NUM_ARGVS]; +int f_argc; +int user_main(SceSize argc, void* argp); + +int CheckParm (char **args, int argc, char *parm) +{ + int i; + + for (i=1 ; i= 0) { + SceIoDirent* p_dir_de = new SceIoDirent; + int res = -1; + + do { + res = sceIoDread(dir_fd, p_dir_de); + if ((res > 0) && (p_dir_de->d_stat.st_attr & FIO_SO_IFDIR)) { + if (!(stricmp(p_dir_de->d_name, ".") == 0 || stricmp(p_dir_de->d_name, "..") == 0 || + stricmp(p_dir_de->d_name, "mp3") == 0 || stricmp(p_dir_de->d_name, "id1") == 0)) { + dirs[j++] = strdup( p_dir_de->d_name); + } + } + } while (res > 0); + delete p_dir_de; + } + sceIoDclose(dir_fd); + //-- dirs setup + + menu_max[0] = j-1; + + if (CheckParm(args, f_argc, "-cpu333")) + menu_cur[1] = 1; + + if (CheckParm(args, f_argc, "-heap")) { + int idx = CheckParm(args, f_argc, "-heap"); + + if (idx+1 < f_argc) { + for (int i = 0 ;i < MAX_HEAP_MB - MIN_HEAP_MB; i++) { + if (stricmp(heaps[i], args[idx+1]) == 0) { + menu_cur[2] = i; + } + + if (args[idx+1][0] == '-') { + args[idx][0] = '\0'; + } + } + } + } else { + for (int i = 0 ;i < MAX_HEAP_MB - MIN_HEAP_MB; i++) { + sprintf(temp_str, "%d", heapSize/(1024*1024)); + if (stricmp(heaps[i],temp_str) == 0) { + menu_cur[2] = i; + } + } + } + + if (CheckParm(args, f_argc, "-game")) { + int idx = CheckParm(args, f_argc, "-game"); + + if (idx+1 < f_argc) { + for (int i = 0 ;i < j; i++) { + if (stricmp(args[idx+1],dirs[i]) == 0) { + menu_cur[0] = i; + } + + if (args[idx+1][0] == '-') { + args[idx][0] = '\0'; + } + } + } + } + + while(!done) { + sceDisplayWaitVblankStart(); + sceDisplayWaitVblankStart(); + sceDisplayWaitVblankStart(); + sceDisplayWaitVblankStart(); + sceDisplayWaitVblankStart(); + sceDisplayWaitVblankStart(); + + pspDebugScreenSetXY(0, 2); + sceCtrlReadBufferPositive(&pad, 1); + + pspDebugScreenSetTextColor(0xffffff); + + printf("DQuake v%4.2f \n", (float)VERSION); + printf("---------------- \n"); + printf("Command line file : %s \n", cmdlinePath); + printf("Startup directory : %s \n", currentDirectory); + printf("Game directory : %s \n", gameDirectory); + printf("\n"); + printf("Press [X]Cross to Continue [O]Circle to Quit\n"); + printf("\n"); + + pspDebugScreenSetTextColor(0x00ffff); + + if (j > -1) { + if (menu_item_cur == 0) + pspDebugScreenSetTextColor(0x00ff00); + printf("Mod directory : [%d/%d] '%s' \t \n", 1+menu_cur[0], 1+menu_max[0], dirs[menu_cur[0]]); + pspDebugScreenSetTextColor(0x00ffff); + } + if (menu_item_cur == 1) + pspDebugScreenSetTextColor(0x00ff00); + printf("CPU Speed : [%d/%d] '%s' \t \n", 1+menu_cur[1], 1+menu_max[1], cpus[menu_cur[1]]); + pspDebugScreenSetTextColor(0x00ffff); + + if (menu_item_cur == 2) + pspDebugScreenSetTextColor(0x00ff00); + printf("Heap Size : [%d/%d] '%s' \t \n", 1+menu_cur[2], 1+menu_max[2], heaps[menu_cur[2]]); + pspDebugScreenSetTextColor(0xffffff); + + if (pad.Buttons != 0) { + if (pad.Buttons & PSP_CTRL_CIRCLE){ + pspDebugScreenSetTextColor(0x0000ff); + printf("\n"); + printf("Shutting down...\n"); + printf("\n"); + pspDebugScreenSetTextColor(0x00ffff); + Sys_Quit(); + } + if (pad.Buttons & PSP_CTRL_CROSS){ + done = true; + } + if (pad.Buttons & PSP_CTRL_UP){ + menu_item_cur--; + menu_item_cur = (menu_item_cur < menu_item_min ? menu_item_min : menu_item_cur); + } + if (pad.Buttons & PSP_CTRL_DOWN){ + menu_item_cur++; + menu_item_cur = (menu_item_cur > menu_item_max ? menu_item_max : menu_item_cur); + } + if (pad.Buttons & PSP_CTRL_LEFT){ + menu_cur[menu_item_cur]--; + menu_cur[menu_item_cur] = (menu_cur[menu_item_cur] < menu_min[menu_item_cur] ? menu_min[menu_item_cur] : menu_cur[menu_item_cur]); + } + if (pad.Buttons & PSP_CTRL_RIGHT){ + menu_cur[menu_item_cur]++; + menu_cur[menu_item_cur] = (menu_cur[menu_item_cur] > menu_max[menu_item_cur] ? menu_max[menu_item_cur] : menu_cur[menu_item_cur]); + } + } + if(sceHprmIsRemoteExist()){ + unsigned int hprmkey; + sceHprmPeekCurrentKey(&hprmkey); + if (hprmkey != 0){ + if (hprmkey & PSP_HPRM_BACK){ + menu_cur[menu_item_cur]--; + menu_cur[menu_item_cur] = (menu_cur[menu_item_cur] < menu_min[menu_item_cur] ? menu_min[menu_item_cur] : menu_cur[menu_item_cur]); + } + if (hprmkey & PSP_HPRM_FORWARD){ + menu_cur[menu_item_cur]++; + menu_cur[menu_item_cur] = (menu_cur[menu_item_cur] > menu_max[menu_item_cur] ? menu_max[menu_item_cur] : menu_cur[menu_item_cur]); + } + if (hprmkey & PSP_HPRM_PLAYPAUSE){ + done = true; + } + } + } + + } + if (CheckParm(args, f_argc, "-game")) { + int idx = CheckParm(args, f_argc, "-game"); + if (stricmp(args[idx+1],dirs[menu_cur[0]]) != 0) { + + if (strlen(dirs[menu_cur[0]]) > 0) { + int len2 = strlen(dirs[menu_cur[0]]); + args[idx+1] = new char[len2+1]; + strcpy(args[idx+1], dirs[menu_cur[0]]); + } else { + strcpy(args[idx], ""); + strcpy(args[idx+1], ""); + } + } + } + else { + if (strlen(dirs[menu_cur[0]]) > 0) { + int len1 = strlen("-game"); + int len2 = strlen(dirs[menu_cur[0]]); + + args[f_argc] = new char[len1+1]; + args[f_argc+1] = new char[len2+1]; + + strcpy(args[f_argc], "-game"); + strcpy(args[f_argc+1], dirs[menu_cur[0]]); + + f_argc += 2; + } + } + + if (stricmp(cpus[menu_cur[1]],"333") == 0) { + + if (!CheckParm(args, f_argc, "-cpu333")) { + int len1 = strlen("-cpu333"); + + args[f_argc] = new char[len1+1]; + strcpy(args[f_argc], "-cpu333"); + + f_argc += 1; + } + } else { + if (CheckParm(args, f_argc, "-cpu333")) { + int idx = CheckParm(args, f_argc, "-cpu333"); + strcpy(args[idx], "-cpu222"); + } + } + + if (CheckParm(args, f_argc, "-heap")) { + + int idx = CheckParm(args, f_argc, "-heap"); + + int len2 = strlen(heaps[menu_cur[2]]); + + args[idx+1] = new char[len2+1]; + strcpy(args[idx+1], heaps[menu_cur[2]]); + } + else { + int len1 = strlen("-heap"); + int len2 = strlen(heaps[menu_cur[2]]); + + args[f_argc] = new char[len1+1]; + args[f_argc+1] = new char[len2+1]; + + strcpy(args[f_argc], "-heap"); + strcpy(args[f_argc+1], heaps[menu_cur[2]]); + + f_argc += 2; + } + + // get rid of temp. alloc memory + for (int i = 0 ;i < MAX_HEAP_MB - MIN_HEAP_MB; i++) { + free(heaps[i]); + } + for (int i = 0 ;i < j; i++) { + free(dirs[i]); + } + free(cpus[0]); + free(cpus[1]); + pspDebugScreenClear(); + } +} + +int ctrl_kernel = 0; +SceUID mod[2]; +char mod_names[2][64]; + +void InitExtModules (void) +{ + char currentDirectory[1024]; + char gameDirectory[1024]; + char path_f[256]; + + memset(gameDirectory, 0, sizeof(gameDirectory)); + memset(currentDirectory, 0, sizeof(currentDirectory)); + getcwd(currentDirectory, sizeof(currentDirectory) - 1); + + strcpy(path_f,currentDirectory); + strcat(path_f,"/hooks/vramext.prx"); + sprintf(mod_names[1], path_f); + + mod[1] = pspSdkLoadStartModule(mod_names[1], PSP_MEMORY_PARTITION_KERNEL); + if (mod[1] >= 0) + { + if (kuKernelGetModel() != 0) + VramSetSize(4096); + else + VramSetSize(2048); + } +} + +void ShutdownExtModules (void) +{ + if(mod[0] < 0) + return; + + sceKernelStopModule(mod[0], 0, 0, 0, 0); + sceKernelUnloadModule(mod[0]); +} + +int main(int argc, char *argv[]) +{ +#ifdef KERNEL_MODE + // Load the network modules + + // create user thread, tweek stack size here if necessary + SceUID thid = sceKernelCreateThread("User Mode Thread", user_main, + 0x20, // default priority + 512 * 1024, // stack size (256KB is regular default) + PSP_THREAD_ATTR_USBWLAN | PSP_THREAD_ATTR_USER | PSP_THREAD_ATTR_VFPU , NULL); + + // start user thread, then wait for it to do everything else + sceKernelStartThread(thid, 0, NULL); + sceKernelWaitThreadEnd(thid, NULL); + + sceKernelExitGame(); +} + +int user_main(SceSize argc, void* argp) + { +#endif + // Set up the callback thread, this is not appropriate for use with + // the loader, so don't bother calling it as it apparently seems to + // cause problems with firmware 2.0+ + setUpCallbackThread(); + + + // Disable floating point exceptions. + // If this isn't done, Quake crashes from (presumably) divide by zero + // operations. + disableFloatingPointExceptions(); + + // init perflib + PFL_Init(true); + + // Initialise the Common module. + InitExtModules (); + + // Get the current working dir. + char currentDirectory[1024]; + char gameDirectory[1024]; + + memset(gameDirectory, 0, sizeof(gameDirectory)); + memset(currentDirectory, 0, sizeof(currentDirectory)); + getcwd(currentDirectory, sizeof(currentDirectory) - 1); + + char path_f[256]; + strcpy(path_f,currentDirectory); + strcat(path_f,"/setup.ini"); + Sys_ReadCommandLineFile(path_f); + + char *args[MAX_NUM_ARGVS]; + + for (int k =0; k < f_argc; k++) { + int len = strlen(f_argv[k]); + args[k] = new char[len+1]; + strcpy(args[k], f_argv[k]); + } + + if (CheckParm(args, f_argc,"-32depth")) + depthfl = qtrue; + + if (CheckParm(args, f_argc, "-gamedir")) + { + char* tempStr = args[CheckParm(args, f_argc,"-gamedir")+1]; + strncpy(gameDirectory, tempStr, sizeof(gameDirectory)-1); + } + else + { + strncpy(gameDirectory,currentDirectory,sizeof(gameDirectory)-1); + } + + ///// + StartUpParams(args, f_argc, path_f, currentDirectory, gameDirectory); + + if (CheckParm(args, f_argc, "-heap")) { + char* tempStr = args[CheckParm(args, f_argc,"-heap")+1]; + int heapSizeMB = atoi(tempStr); + + if (heapSizeMB < MIN_HEAP_MB ) + heapSizeMB = MIN_HEAP_MB; + + if (heapSizeMB > MAX_HEAP_MB ) + heapSizeMB = MAX_HEAP_MB; + + heapSize = heapSizeMB * 1024 * 1024; + } + // Allocate the heap. + std::vector heap(heapSize, 0); + +#if CONSOLE_DEBUG + if (f_argc > 1) { + args[f_argc++] = "-condebug"; + COM_InitArgv(f_argc, args); + } + else { + args[0] = ""; + args[1] = "-condebug"; + COM_InitArgv(2, args); + } +#else + if (f_argc > 1) + COM_InitArgv(f_argc, args); + else { + args[0] = ""; + COM_InitArgv(0, NULL); + } +#endif + +#ifdef PSP_SOFTWARE_VIDEO + // Bump up the clock frequency. + if (!COM_CheckParm("-cpu222")) { + scePowerSetClockFrequency(333, 333, 166); + //PFL_SetCPUFrequency(222); + } +#else + if (COM_CheckParm("-cpu333")) { + scePowerSetClockFrequency(333, 333, 166); + //PFL_SetCPUFrequency(333); + } +#endif + + // Catch exceptions from here. + try + { + // Initialise the Host module. + quakeparms_t parms; + memset(&parms, 0, sizeof(parms)); + parms.argc = com_argc; + parms.argv = com_argv; + parms.basedir = gameDirectory; + parms.memsize = heap.size(); + parms.membase = &heap.at(0); + Host_Init(&parms); + + // Precalculate the tick rate. + const float oneOverTickRate = 1.0f / sceRtcGetTickResolution(); + + // Record the time that the main loop started. + u64 lastTicks; + sceRtcGetCurrentTick(&lastTicks); + + // Set up threads + Sys_InitThreads(); + + // Enter the main loop. + while (!quit) + { + // start cpu profiling + PFL_BeginCPURecord(); + + // Handle suspend & resume. + if (suspended) + { + // Suspend. + S_ClearBuffer(); + quake::system::suspend(); + + // Wait for resume. + while (suspended && !quit) + { + sceDisplayWaitVblankStart(); + } + + // Resume. + quake::system::resume(); + + // Reset the clock. + sceRtcGetCurrentTick(&lastTicks); + } + + // What is the time now? + u64 ticks; + sceRtcGetCurrentTick(&ticks); + + // How much time has elapsed? + const unsigned int deltaTicks = ticks - lastTicks; + const float deltaSeconds = deltaTicks * oneOverTickRate; + + // Check the battery status. + battery::check(); + + // Run the frame. + Host_Frame(deltaSeconds); + // Remember the time for next frame. + lastTicks = ticks; + + // end cpu profiling + PFL_EndCPURecord(); + } + } + catch (const std::exception& e) + { + // Report the error and quit. + Sys_Error("C++ Exception: %s", e.what()); + return 0; + } + + // Quit. + Sys_Quit(); + return 0; +} + +void Sys_ReadCommandLineFile (char* netpath) +{ + int in; + int remaining, count; + char buf[4096]; + int argc = 1; + + remaining = Sys_FileOpenRead (netpath, &in); + + if (in > 0 && remaining > 0) { + count = Sys_FileRead (in, buf, 4096); + f_argv[0] = empty_string; + + char* lpCmdLine = buf; + + while (*lpCmdLine && (argc < MAX_NUM_ARGVS)) + { + while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126))) + lpCmdLine++; + + if (*lpCmdLine) + { + f_argv[argc] = lpCmdLine; + argc++; + + while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126))) + lpCmdLine++; + + if (*lpCmdLine) + { + *lpCmdLine = 0; + lpCmdLine++; + } + } + } + f_argc = argc; + } else { + f_argc = 0; + } + + if (in > 0) + Sys_FileClose (in); +} + +char* Sys_GetPSPModel(void) +{ + // check for the vita prx + int vitaprx = sceIoOpen("flash0:/kd/registry.prx", PSP_O_RDONLY | PSP_O_WRONLY, 0777); + + if (vitaprx >= 0) { + sceIoClose(vitaprx); + return "PS Vita (PSP2)"; + } + + // normal psp models + char* modelstring; + + sys_psp_model = kuKernelGetModel(); + switch(sys_psp_model) { + case 0: + modelstring = "PSP Phat (1000)"; + break; + case 1: + modelstring = "PSP Slim (2000)"; + break; + case 2: + modelstring = "PSP Brite (3000)"; + break; + case 4: + modelstring = "PSP GO (4000)"; + break; + case 10: + modelstring = "PSP Street (E1000)"; + break; + default: + break; + } + + return modelstring; +} \ No newline at end of file diff --git a/source/psp/math.cpp b/source/psp/math.cpp new file mode 100644 index 0000000..97dc2b2 --- /dev/null +++ b/source/psp/math.cpp @@ -0,0 +1,50 @@ +/* +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 "math.hpp" + +namespace quake +{ + namespace math + { + void multiply(const ScePspFMatrix4& a, const ScePspFMatrix4& b, ScePspFMatrix4* out) + { + out->x.x = a.x.x * b.x.x + a.x.y * b.y.x + a.x.z * b.z.x + a.x.w * b.w.x; + out->x.y = a.x.x * b.x.y + a.x.y * b.y.y + a.x.z * b.z.y + a.x.w * b.w.y; + out->x.z = a.x.x * b.x.z + a.x.y * b.y.z + a.x.z * b.z.z + a.x.w * b.w.z; + out->x.w = a.x.x * b.x.w + a.x.y * b.y.w + a.x.z * b.z.w + a.x.w * b.w.w; + + out->y.x = a.y.x * b.x.x + a.y.y * b.y.x + a.y.z * b.z.x + a.y.w * b.w.x; + out->y.y = a.y.x * b.x.y + a.y.y * b.y.y + a.y.z * b.z.y + a.y.w * b.w.y; + out->y.z = a.y.x * b.x.z + a.y.y * b.y.z + a.y.z * b.z.z + a.y.w * b.w.z; + out->y.w = a.y.x * b.x.w + a.y.y * b.y.w + a.y.z * b.z.w + a.y.w * b.w.w; + + out->z.x = a.z.x * b.x.x + a.z.y * b.y.x + a.z.z * b.z.x + a.z.w * b.w.x; + out->z.y = a.z.x * b.x.y + a.z.y * b.y.y + a.z.z * b.z.y + a.z.w * b.w.y; + out->z.z = a.z.x * b.x.z + a.z.y * b.y.z + a.z.z * b.z.z + a.z.w * b.w.z; + out->z.w = a.z.x * b.x.w + a.z.y * b.y.w + a.z.z * b.z.w + a.z.w * b.w.w; + + out->w.x = a.w.x * b.x.x + a.w.y * b.y.x + a.w.z * b.z.x + a.w.w * b.w.x; + out->w.y = a.w.x * b.x.y + a.w.y * b.y.y + a.w.z * b.z.y + a.w.w * b.w.y; + out->w.z = a.w.x * b.x.z + a.w.y * b.y.z + a.w.z * b.z.z + a.w.w * b.w.z; + out->w.w = a.w.x * b.x.w + a.w.y * b.y.w + a.w.z * b.z.w + a.w.w * b.w.w; + } + } +} diff --git a/source/psp/math.hpp b/source/psp/math.hpp new file mode 100644 index 0000000..621ffbd --- /dev/null +++ b/source/psp/math.hpp @@ -0,0 +1,54 @@ +/* +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 QUAKE_MATH_HPP +#define QUAKE_MATH_HPP + +#include +#include + +#ifdef PSP_VFPU +#include +#endif + +namespace quake +{ + namespace math + { + static inline float dot(const ScePspFVector4& a, const ScePspFVector4& b) + { + return (a.x * b.x) + (a.y * b.y) + (a.z * b.z); + } + + static inline void normalise(ScePspFVector4* v) + { + const float scale = 1.0f / sqrtf(dot(*v, *v)); + + v->x *= scale; + v->y *= scale; + v->z *= scale; + v->w *= scale; + } + + void multiply(const ScePspFMatrix4& a, const ScePspFMatrix4& b, ScePspFMatrix4* out); + } +} + +#endif diff --git a/source/psp/module.cpp b/source/psp/module.cpp new file mode 100644 index 0000000..9d851b3 --- /dev/null +++ b/source/psp/module.cpp @@ -0,0 +1,28 @@ +/* +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 "module.h" + +// Set up the module info. +PSP_MODULE_INFO("NZPortable", 0, 1, 1); +PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER | PSP_THREAD_ATTR_VFPU); +PSP_HEAP_SIZE_KB(-1024); \ No newline at end of file diff --git a/source/psp/module.h b/source/psp/module.h new file mode 100644 index 0000000..9d9440a --- /dev/null +++ b/source/psp/module.h @@ -0,0 +1,5 @@ +#ifdef SLIM +#define PSP_HEAP_SIZE_MB 42 +#else +#define PSP_HEAP_SIZE_MB 18 +#endif \ No newline at end of file diff --git a/source/psp/mp3.c b/source/psp/mp3.c new file mode 100644 index 0000000..ff0e1c4 --- /dev/null +++ b/source/psp/mp3.c @@ -0,0 +1,399 @@ +/* + * PSP Software Development Kit - http://www.pspdev.org + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * main.c - Basic PRX template + * + * Copyright (c) 2005 Marcus R. Brown + * Copyright (c) 2005 James Forshaw + * Copyright (c) 2005 John Kelley + * 2012 dr_mabuse1981: well, i just kicked out Crow_bars shitty and buggy mp3 player + * and re-added the kurok one again and added bakers mp3 fix ;) + * + * $Id: main.c 1888 2006-05-01 08:47:04Z tyranid $ + * $HeadURL$ + */ +#include +#include +#include +#include +#include +#include +#include "m33libs/kubridge.h" + +int mp3_last_error = 0; + +static int initialized = 0; +static SceUID thread_job_sem = -1; +static SceUID thread_busy_sem = -1; +static int thread_exit = 0; + +int done = 0; + +int mp3_job_started = 0; + +int mp3_volume; + +static int mp3_src_pos = 0; + +int first_run = 0; + +// MPEG-1, layer 3 +static int bitrates[] = { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }; +//static int samplerates[] = { 44100, 48000, 32000, 0 }; + +#define MIN_INFRAME_SIZE 96 +#define IN_BUFFER_SIZE (2*1024) + +static unsigned long mp3_codec_struct[65];// __attribute__((aligned(64))); + +static unsigned char mp3_src_buffer[2][IN_BUFFER_SIZE];// __attribute__((aligned(64))); +static short mp3_mix_buffer[2][1152*2];// __attribute__((aligned(64))); +static int working_buf = 0; + +static const char *mp3_fname = NULL; +static SceUID mp3_handle = -1; +static int mp3_src_size = 0; + +static int decode_thread(SceSize args, void *argp); + + +static void psp_sem_lock(SceUID sem) +{ + int ret = sceKernelWaitSema(sem, 1, 0); + if (ret < 0) printf("sceKernelWaitSema(%08x) failed with %08x\n", sem, ret); +} + +static void psp_sem_unlock(SceUID sem) +{ + int ret = sceKernelSignalSema(sem, 1); + if (ret < 0) printf("sceKernelSignalSema(%08x) failed with %08x\n", sem, ret); +} + +// only accepts MPEG-1, layer3 +static int find_sync_word(unsigned char *data, int len) +{ + int i; + for (i = 0; i < len-1; i++) + { + if ( data[i+0] != 0xff) continue; + if ((data[i+1] & 0xfe) == 0xfa) return i; + i++; + } + return -1; +} + +// Baker: called only by decode_thread +static int read_next_frame(int which_buffer) +{ + int i, bytes_read, frame_offset; + int bitrate, padding, frame_size = 0; + + for (i = 0; i < 32; i++) + { + bytes_read = sceIoRead(mp3_handle, mp3_src_buffer[which_buffer], sizeof(mp3_src_buffer[which_buffer])); + mp3_src_pos += bytes_read; + if (bytes_read < MIN_INFRAME_SIZE) + { + // Baker: end of file hit, restart + re-read + mp3_src_pos = 0; + sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET); + bytes_read = sceIoRead(mp3_handle, mp3_src_buffer[which_buffer], sizeof(mp3_src_buffer[which_buffer])); + mp3_src_pos += bytes_read; + + } + frame_offset = find_sync_word(mp3_src_buffer[which_buffer], bytes_read); + if (frame_offset < 0) { + printf("missing syncword, foffs=%i\n", mp3_src_pos - bytes_read); + mp3_src_pos--; + sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET); + continue; + } + if (bytes_read - frame_offset < 4) { + printf("syncword @ EOB, foffs=%i\n", mp3_src_pos - bytes_read); + mp3_src_pos--; + sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET); + continue; + } + + bitrate = mp3_src_buffer[which_buffer][frame_offset+2] >> 4; + padding = (mp3_src_buffer[which_buffer][frame_offset+2] & 2) >> 1; + + frame_size = 144000*bitrates[bitrate]/44100 + padding; + if (frame_size <= 0) { + printf("bad frame, foffs=%i\n", mp3_src_pos - bytes_read); + continue; // bad frame + } + + if (bytes_read - frame_offset < frame_size) + { + printf("unfit, foffs=%i\n", mp3_src_pos - bytes_read); + mp3_src_pos -= bytes_read - frame_offset; + if (mp3_src_size - mp3_src_pos < frame_size) { + mp3_src_pos = mp3_src_size; + return 0; // EOF + } + sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET); + continue; // didn't fit, re-read.. + } + + if (frame_offset) { + //printf("unaligned, foffs=%i, offs=%i\n", mp3_src_pos - bytes_read, frame_offset); + memmove(mp3_src_buffer[which_buffer], mp3_src_buffer[which_buffer] + frame_offset, frame_size); + } + + // align for next frame read + mp3_src_pos -= bytes_read - (frame_offset + frame_size); + sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET); + + break; + } + + return frame_size > 0 ? frame_size : -1; +} + + +static SceUID load_start_module(const char *prxname) +{ + SceUID mod, mod1; + int status, ret; + + mod = pspSdkLoadStartModule(prxname, PSP_MEMORY_PARTITION_KERNEL); + if (mod < 0) { + printf("failed to load %s (%08x), trying kuKernelLoadModule\n", prxname, mod); + mod1 = kuKernelLoadModule(prxname, 0, NULL); + if (mod1 < 0) printf("kuKernelLoadModule failed with %08x\n", mod1); + else { + ret = sceKernelStartModule(mod1, 0, NULL, &status, 0); + if (ret < 0) printf("sceKernelStartModule failed with %08x\n", ret); + else mod = mod1; + } + } + return mod; +} + + +int mp3_init(void) +{ + SceUID thid, mod; + int ret; + + /* load modules */ + /* <= 1.5 (and probably some other, not sure which) fw need this to for audiocodec to work, + * so if it fails, assume we are just on new enough firmware and continue.. */ + load_start_module("flash0:/kd/me_for_vsh.prx"); + + if (sceKernelDevkitVersion() < 0x02070010) + mod = load_start_module("flash0:/kd/audiocodec.prx"); + else mod = load_start_module("flash0:/kd/avcodec.prx"); + if (mod < 0) { + ret = mod = load_start_module("flash0:/kd/audiocodec_260.prx"); // last chance.. + if (mod < 0) goto fail; + } + + /* audiocodec init */ + memset(mp3_codec_struct, 0, sizeof(mp3_codec_struct)); + ret = sceAudiocodecCheckNeedMem(mp3_codec_struct, 0x1002); + if (ret < 0) { + printf("sceAudiocodecCheckNeedMem failed with %08x\n", ret); + goto fail; + } + + ret = sceAudiocodecGetEDRAM(mp3_codec_struct, 0x1002); + if (ret < 0) { + printf("sceAudiocodecGetEDRAM failed with %08x\n", ret); + goto fail; + } + + ret = sceAudiocodecInit(mp3_codec_struct, 0x1002); + if (ret < 0) { + printf("sceAudiocodecInit failed with %08x\n", ret); + goto fail1; + } + + /* thread and stuff */ + thread_job_sem = sceKernelCreateSema("p_mp3job_sem", 0, 0, 1, NULL); + if (thread_job_sem < 0) { + printf("sceKernelCreateSema() failed: %08x\n", thread_job_sem); + ret = thread_job_sem; + goto fail1; + } + + thread_busy_sem = sceKernelCreateSema("p_mp3busy_sem", 0, 1, 1, NULL); + if (thread_busy_sem < 0) { + printf("sceKernelCreateSema() failed: %08x\n", thread_busy_sem); + ret = thread_busy_sem; + goto fail2; + } + + thread_exit = 0; + thid = sceKernelCreateThread("mp3decode_thread", decode_thread, 30, 0x2000, 0, 0); /* use slightly higher prio then main */ + if (thid < 0) { + printf("failed to create decode thread: %08x\n", thid); + ret = thid; + goto fail3; + } + ret = sceKernelStartThread(thid, 0, 0); + if (ret < 0) { + printf("failed to start decode thread: %08x\n", ret); + goto fail3; + } + + mp3_last_error = 0; + initialized = 1; + return 0; + +fail3: + sceKernelDeleteSema(thread_busy_sem); + thread_busy_sem = -1; +fail2: + sceKernelDeleteSema(thread_job_sem); + thread_job_sem = -1; +fail1: + sceAudiocodecReleaseEDRAM(mp3_codec_struct); +fail: + mp3_last_error = ret; + initialized = 0; + return 1; +} + +void mp3_deinit(void) +{ + printf("mp3_deinit, initialized=%i\n", initialized); + + if (!initialized) return; + thread_exit = 1; + psp_sem_lock(thread_busy_sem); + psp_sem_unlock(thread_busy_sem); + + sceKernelSignalSema(thread_job_sem, 1); + sceKernelDelayThread(100*1000); + + if (mp3_handle >= 0) sceIoClose(mp3_handle); + mp3_handle = -1; + mp3_fname = NULL; + + psp_sem_lock(thread_job_sem); + psp_sem_unlock(thread_job_sem); + + sceKernelDeleteSema(thread_busy_sem); + thread_busy_sem = -1; + sceKernelDeleteSema(thread_job_sem); + thread_job_sem = -1; + sceAudiocodecReleaseEDRAM(mp3_codec_struct); + initialized = 0; + +} + + +short mp3_output_buffer[4][1152 * 2] __attribute__((aligned(64))); +int mp3_output_index = 0; + +// may overflow stack? +static int decode_thread(SceSize args, void *argp) +{ + int ret, frame_size; + + int audio_channel = sceAudioChReserve(1, 1152, PSP_AUDIO_FORMAT_STEREO); + + printf("decode_thread started with id %08x, priority %i\n", + sceKernelGetThreadId(), sceKernelGetThreadCurrentPriority()); + + while (!thread_exit) + { + psp_sem_lock(thread_job_sem); + + if (thread_exit) { + psp_sem_unlock(thread_job_sem); + break; + } + + psp_sem_lock(thread_busy_sem); + + frame_size = read_next_frame(working_buf); + + while (mp3_job_started) + { + + if (thread_exit) break; + + if(frame_size > 0) + { + mp3_codec_struct[6] = (unsigned long)mp3_src_buffer[working_buf]; + mp3_codec_struct[8] = (unsigned long)mp3_mix_buffer[working_buf]; + mp3_codec_struct[7] = mp3_codec_struct[10] = frame_size; + mp3_codec_struct[9] = 1152 * 4; + + ret = sceAudiocodecDecode(mp3_codec_struct, 0x1002); + if (ret < 0) printf("sceAudiocodecDecode failed with %08x\n", ret); + + memcpy(mp3_output_buffer[mp3_output_index], mp3_mix_buffer[working_buf], 1152*4); + sceAudioOutputBlocking(audio_channel, mp3_volume, mp3_output_buffer[mp3_output_index]); + mp3_output_index = (mp3_output_index+1)%4; + + memset(mp3_mix_buffer, 0, 1152*2*2); + + frame_size = read_next_frame(working_buf); + } + + + } + + if (!mp3_job_started) { + + if (mp3_handle >= 0) sceIoClose(mp3_handle); + mp3_handle = -1; + mp3_fname = NULL; + } + + psp_sem_unlock(thread_busy_sem); + + + } + + printf("leaving decode thread\n"); + sceKernelExitDeleteThread(0); + return 0; +} + +static int mp3_samples_ready = 0, mp3_buffer_offs = 0, mp3_play_bufsel = 0; + +int mp3_start_play(char *fname, int pos) +{ + + printf("mp3_start_play(%s) @ %i\n", fname, pos); + psp_sem_lock(thread_busy_sem); + + if (mp3_fname != fname || mp3_handle < 0) + { + if (mp3_handle >= 0) sceIoClose(mp3_handle); + mp3_handle = sceIoOpen(fname, PSP_O_RDONLY, 0777); + if (mp3_handle < 0) { + printf("sceIoOpen(%s) failed\n", fname); + psp_sem_unlock(thread_busy_sem); + sceIoClose(mp3_handle); + return 2; + } + mp3_src_size = sceIoLseek32(mp3_handle, 0, PSP_SEEK_END); + mp3_fname = fname; + } + + // seek.. + mp3_src_pos = (int) (((float)pos / 1023.0f) * (float)mp3_src_size); + sceIoLseek32(mp3_handle, mp3_src_pos, PSP_SEEK_SET); + printf("seek %i: %i/%i\n", pos, mp3_src_pos, mp3_src_size); + + mp3_job_started = 1; + mp3_samples_ready = mp3_buffer_offs = mp3_play_bufsel = 0; + working_buf = 0; + + /* send a request to decode first frame */ + psp_sem_unlock(thread_busy_sem); + psp_sem_unlock(thread_job_sem); + sceKernelDelayThread(1); // reschedule + + return 0; +} + + diff --git a/source/psp/mp3.h b/source/psp/mp3.h new file mode 100644 index 0000000..015e9cc --- /dev/null +++ b/source/psp/mp3.h @@ -0,0 +1,9 @@ +// additional stuff for PSP mp3 decoder implementation + +extern "C" int mp3_init(void); +extern "C" void mp3_deinit(void); +extern "C" int mp3_start_play(char *fname, int pos); + +extern "C" int mp3_job_started; + +int mp3_volume; diff --git a/source/psp/network.cpp b/source/psp/network.cpp new file mode 100644 index 0000000..530c8fc --- /dev/null +++ b/source/psp/network.cpp @@ -0,0 +1,122 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 "network_psp.hpp" + +extern "C" +{ +#include "../net_dgrm.h" +#include "../net_loop.h" +} + +net_driver_t net_drivers[MAX_NET_DRIVERS] = +{ + { + "Loopback", + qfalse, + 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", + qfalse, + 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; + +using namespace quake; +using namespace quake::network; + +net_landriver_t net_landrivers[MAX_NET_DRIVERS] = +{ + { + "Infrastructure", + qfalse, + 0, + infrastructure::init, + infrastructure::shut_down, + infrastructure::listen, + infrastructure::open_socket, + infrastructure::close_socket, + infrastructure::connect, + infrastructure::check_new_connections, + infrastructure::read, + infrastructure::write, + infrastructure::broadcast, + infrastructure::addr_to_string, + infrastructure::string_to_addr, + infrastructure::get_socket_addr, + infrastructure::get_name_from_addr, + infrastructure::get_addr_from_name, + infrastructure::addr_compare, + infrastructure::get_socket_port, + infrastructure::set_socket_port + }, + { + "Adhoc", + qfalse, + 0, + adhoc::init, + adhoc::shut_down, + adhoc::listen, + adhoc::open_socket, + adhoc::close_socket, + adhoc::connect, + adhoc::check_new_connections, + adhoc::read, + adhoc::write, + adhoc::broadcast, + adhoc::addr_to_string, + adhoc::string_to_addr, + adhoc::get_socket_addr, + adhoc::get_name_from_addr, + adhoc::get_addr_from_name, + adhoc::addr_compare, + adhoc::get_socket_port, + adhoc::set_socket_port + } +}; + +int net_numlandrivers = 2; diff --git a/source/psp/network_infrastructure.cpp b/source/psp/network_infrastructure.cpp new file mode 100644 index 0000000..c971c89 --- /dev/null +++ b/source/psp/network_infrastructure.cpp @@ -0,0 +1,555 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 + +#include "network_infrastructure.hpp" + +#include // inet_addr +#include // sockaddr_in +#include // PSP socket API. +#include +#include +#include +#include + +#include + +#include + +#define MAXHOSTNAMELEN 64 +#define EWOULDBLOCK 111 +#define ECONNREFUSED 11 + +int totalAccessPoints = 0; +cvar_t accesspoint = {"accesspoint", "1", qtrue}; +int accessPointNumber[100]; + +#ifdef PROFILE +#define sceUtilityLoadNetModule +#define sceUtilityUnLoadNetModule +#endif + +namespace quake +{ + namespace network + { + namespace infrastructure + { + static int accept_socket = -1; // socket for fielding new connections + static int control_socket = -1; + static int broadcast_socket = 0; + static struct qsockaddr broadcast_addr; + static int my_addr = 0; + + int init (void) + { + if(totalAccessPoints == 0) + { + int iNetIndex; + memset(accessPointNumber, 0, sizeof(accessPointNumber)); + for (iNetIndex = 1; iNetIndex < 100; iNetIndex++) // skip the 0th connection + { + if (sceUtilityCheckNetParam(iNetIndex) == 0) + { + totalAccessPoints++; + accessPointNumber[totalAccessPoints] = iNetIndex; + } + } + if(accesspoint.value > totalAccessPoints) + Cvar_SetValue("accesspoint", 1); + } + + if(!host_initialized) + { + Cvar_RegisterVariable(&accesspoint); + } + + if(!tcpipAvailable) + return -1; + + char szMyIPAddr[32]; + struct qsockaddr addr; + char *colon; + + char buff[MAXHOSTNAMELEN]; + + S_ClearBuffer (); // so dma doesn't loop current sound + + // Load the network modules when they are required + sceUtilityLoadNetModule(PSP_NET_MODULE_COMMON); + sceUtilityLoadNetModule(PSP_NET_MODULE_INET); + + // Initialise the network. + const int err = pspSdkInetInit(); + if (err) + { + Con_Printf("Couldn't initialise the network %08X\n", err); + sceUtilityUnloadNetModule(PSP_NET_MODULE_INET); + sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON); + return -1; + } + + if(!connect_to_apctl(accessPointNumber[(int)accesspoint.value])) + { + Con_Printf("Unable to connect to access point\n"); + pspSdkInetTerm(); + sceUtilityUnloadNetModule(PSP_NET_MODULE_INET); + sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON); + + return -1; + } + + // connected, get my IPADDR and run test + if (sceNetApctlGetInfo(8, (SceNetApctlInfo*)szMyIPAddr) != 0) + strcpy(szMyIPAddr, "unknown IP address"); + + // determine my name & address + gethostname(buff, MAXHOSTNAMELEN); + + my_addr = inet_addr(szMyIPAddr); + + // if the quake hostname isn't set, set it to the machine name + if (Q_strcmp(hostname.string, "UNNAMED") == 0) + { + buff[15] = 0; + Cvar_Set ("hostname", buff); + } + + if ((control_socket = open_socket(0)) == -1) + { + pspSdkInetTerm(); + sceUtilityUnloadNetModule(PSP_NET_MODULE_INET); + sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON); + Sys_Error("init: Unable to open control socket\n"); + } + + ((struct sockaddr_in *)&broadcast_addr)->sin_family = AF_INET; + ((struct sockaddr_in *)&broadcast_addr)->sin_addr.s_addr = INADDR_BROADCAST; + ((struct sockaddr_in *)&broadcast_addr)->sin_port = htons(net_hostport); + + get_socket_addr (control_socket, &addr); + Q_strcpy(my_tcpip_address, addr_to_string(&addr)); + colon = Q_strrchr (my_tcpip_address, ':'); + if (colon) + *colon = 0; + + Con_Printf("UDP Initialized\n"); + tcpipAvailable = qtrue; + + return control_socket; + } + + //============================================================================= + + void shut_down (void) + { + listen(qfalse); + close_socket(control_socket); + + pspSdkInetTerm(); + + // Now to unload the network modules, no need to keep them loaded all the time + sceUtilityUnloadNetModule(PSP_NET_MODULE_INET); + sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON); + } + + //============================================================================= + + void listen (qboolean state) + { + // enable listening + if (state) + { + if (accept_socket != -1) + return; + if ((accept_socket = open_socket(net_hostport)) == -1) + Sys_Error ("listen: Unable to open accept socket\n"); + return; + } + + // disable listening + if (accept_socket == -1) + return; + close_socket(accept_socket); + + accept_socket = -1; + } + + //============================================================================= + + int open_socket (int port) + { + int newsocket; + struct sockaddr_in address; + + if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + return -1; + + int val = 1; + if(setsockopt(newsocket, SOL_SOCKET, SO_NONBLOCK, &val, sizeof(val)) < 0) + goto ErrorReturn; + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(port); + if( bind (newsocket, (sockaddr *)&address, sizeof(address)) == -1) + goto ErrorReturn; + + printf("got here4\n"); + return newsocket; + + ErrorReturn: + close(newsocket); + return -1; + } + + //============================================================================= + + int close_socket (int socket) + { + if (socket == broadcast_socket) + broadcast_socket = 0; + return close(socket); + } + + int connect (int socket, struct qsockaddr *addr) + { + return 0; + } + + //============================================================================= + + int check_new_connections (void) + { + byte buf[1000]; + + if (accept_socket == -1) + return -1; + + // Peek at the message and if there is a message waiting then return the + // socket + int ret = recvfrom(accept_socket, buf, 1000, MSG_PEEK, NULL, NULL); + + if(ret > 0) + return accept_socket; + + return -1; + } + + //============================================================================= + + int 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, (socklen_t*)&addrlen); + if (ret == -1) + { + int errno = sceNetInetGetErrno(); + if(errno == EWOULDBLOCK || errno == ECONNREFUSED) + { + return 0; + } + } + + return ret; + } + + //============================================================================= + + static int make_socket_broadcast_capable (int socket) + { + int i = 1; + + // make this socket broadcast capable + if (setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i)) < 0) + return -1; + broadcast_socket = socket; + + return 0; + } + + //============================================================================= + + int broadcast (int socket, byte *buf, int len) + { + int ret; + + if (socket != broadcast_socket) + { + if (broadcast_socket != 0) + Sys_Error("Attempted to use multiple broadcasts sockets\n"); + ret = make_socket_broadcast_capable (socket); + if (ret == -1) + { + Con_Printf("Unable to make socket broadcast capable\n"); + return ret; + } + } + + return write(socket, buf, len, &broadcast_addr); + } + + //============================================================================= + + int write (int socket, byte *buf, int len, struct qsockaddr *addr) + { + int ret; + + ret = sendto (socket, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); + if (ret == -1) + { + int errno = sceNetInetGetErrno(); + if(errno == EWOULDBLOCK || errno == ECONNREFUSED) + { + return 0; + } + Con_Printf("Failed to send message, errno=%08X\n", errno); + } + return ret; + } + + //============================================================================= + + char* addr_to_string (struct qsockaddr *addr) + { + static char buffer[22]; + int haddr; + + haddr = ntohl(((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, ntohs(((struct sockaddr_in *)addr)->sin_port)); + return buffer; + } + + //============================================================================= + + int string_to_addr (char *string, struct qsockaddr *addr) + { + 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 = htonl(ipaddr); + ((struct sockaddr_in *)addr)->sin_port = htons(hp); + return 0; + } + + //============================================================================= + + int get_socket_addr (int socket, struct qsockaddr *addr) + { + socklen_t addrlen = sizeof(struct qsockaddr); + unsigned int a; + + Q_memset(addr, 0, sizeof(struct qsockaddr)); + getsockname(socket, (struct sockaddr *)addr, &addrlen); + a = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + if (a == 0 || a == inet_addr("127.0.0.1")) + ((struct sockaddr_in *)addr)->sin_addr.s_addr = my_addr; + + return 0; + } + + //============================================================================= + + int get_name_from_addr (struct qsockaddr *addr, char *name) + { + struct hostent *hostentry; + + hostentry = gethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr), AF_INET); + if (hostentry) + { + Q_strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1); + return 0; + } + + Q_strcpy (name, addr_to_string (addr)); + return 0; + } + + //============================================================================= + + int get_addr_from_name(char *name, struct qsockaddr *addr) + { + struct hostent *hostentry; + + if (name[0] >= '0' && name[0] <= '9') + return PartialIPAddress (name, addr); + + hostentry = gethostbyname (name); + if (!hostentry) + return -1; + + addr->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_port = htons(net_hostport); + ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0]; + + return 0; + } + + //============================================================================= + + int addr_compare (struct qsockaddr *addr1, struct qsockaddr *addr2) + { + if (addr1->sa_family != addr2->sa_family) + return -1; + + if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr) + return -1; + + if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port) + return 1; + + return 0; + } + + //============================================================================= + + int get_socket_port (struct qsockaddr *addr) + { + return ntohs(((struct sockaddr_in *)addr)->sin_port); + } + + + int set_socket_port (struct qsockaddr *addr, int port) + { + ((struct sockaddr_in *)addr)->sin_port = htons(port); + return 0; + } + + /* + ============ + PartialIPAddress + + this lets you type only as much of the net address as required, using + the local network components to fill in the rest + ============ + */ + 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 = Q_atoi(b); + else + port = net_hostport; + + hostaddr->sa_family = AF_INET; + ((struct sockaddr_in *)hostaddr)->sin_port = htons((short)port); + ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (my_addr & htonl(mask)) | htonl(addr); + + return 0; + } + //============================================================================= + /* Connect to an access point */ + int connect_to_apctl(int config) + { + int err; + int stateLast = -1; + int timeout = 0; + + /* Connect using the first profile */ + err = sceNetApctlConnect(config); + if (err != 0) + { + return 0; + } + + while (1) + { + int state; + err = sceNetApctlGetState(&state); + if (err != 0) + { + break; + } + if (state > stateLast) + { + stateLast = state; + timeout = 0; + } + if (state == 4) + break; // connected with static IP + + // wait a little before polling again + sceKernelDelayThread(50*1000); // 50ms + + timeout++; + if(timeout > 200) + { + Con_Printf("Timeout connecting to access point. State=%d\n", state); + return 0; + } + } + + if(err != 0) + { + return 0; + } + + return 1; + } + //============================================================================= + } + } +} diff --git a/source/psp/network_infrastructure.hpp b/source/psp/network_infrastructure.hpp new file mode 100644 index 0000000..1d70c55 --- /dev/null +++ b/source/psp/network_infrastructure.hpp @@ -0,0 +1,61 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 QUAKE_NETWORK_INFRASTRUCTURE_HPP +#define QUAKE_NETWORK_INFRASTRUCTURE_HPP + +extern "C" +{ +#include "../quakedef.h" +} + +namespace quake +{ + namespace network + { + namespace infrastructure + { + int init (void); + void shut_down (void); + void listen (qboolean state); + int open_socket (int port); + int close_socket (int socket); + int connect (int socket, struct qsockaddr *addr); + int check_new_connections (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* addr_to_string (struct qsockaddr *addr); + int string_to_addr (char *string, struct qsockaddr *addr); + int get_socket_addr (int socket, struct qsockaddr *addr); + int get_name_from_addr (struct qsockaddr *addr, char *name); + int get_addr_from_name (char *name, struct qsockaddr *addr); + int addr_compare (struct qsockaddr *addr1, struct qsockaddr *addr2); + int get_socket_port (struct qsockaddr *addr); + int set_socket_port (struct qsockaddr *addr, int port); + + int connect_to_apctl(int config); + int PartialIPAddress (char *in, struct qsockaddr *hostaddr); + } + } +} + +#endif diff --git a/source/psp/network_psp.cpp b/source/psp/network_psp.cpp new file mode 100644 index 0000000..0e1b5d8 --- /dev/null +++ b/source/psp/network_psp.cpp @@ -0,0 +1,946 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 +#include +#include +#include + +#include "network_psp.hpp" + +#include // inet_addr +#include // sockaddr_in +#include // PSP socket API. +#include +#include +#include +#include + +#include + +#include + +#define ntohs(x) __builtin_bswap16(x) +#define ntohl(x) __builtin_bswap32(x) +#define htons(x) __builtin_bswap16(x) +#define htonl(x) __builtin_bswap32(x) + +#define ADHOC_NET 29 +#define ADHOC_EWOULDBLOCK 0x80410709 + +#define MAXHOSTNAMELEN 64 +#define EWOULDBLOCK 111 +#define ECONNREFUSED 11 + +int totalAccessPoints = 0; +cvar_t accesspoint = {"accesspoint", "1", qtrue}; +int accessPointNumber[100]; + + +typedef struct sockaddr_adhoc +{ + char len; + short family; + u16 port; + char mac[6]; + char zero[6]; +} sockaddr_adhoc; + +#ifdef PROFILE +#define sceUtilityLoadNetModule +#define sceUtilityUnLoadNetModule +#endif + +static pdpStatStruct gPdpStat; +pdpStatStruct *findPdpStat(int socket, pdpStatStruct *pdpStat) +{ + if(socket == pdpStat->pdpId) { + memcpy(&gPdpStat, pdpStat, sizeof(pdpStatStruct)); + return &gPdpStat; + } + if(pdpStat->next) return findPdpStat(socket, pdpStat->next); + return (pdpStatStruct *)-1; +} + + +namespace quake +{ + namespace network + { + namespace infrastructure + { + static int accept_socket = -1; // socket for fielding new connections + static int control_socket = -1; + static int broadcast_socket = 0; + static struct qsockaddr broadcast_addr; + static int my_addr = 0; + + int init (void) + { + if(totalAccessPoints == 0) + { + int iNetIndex; + memset(accessPointNumber, 0, sizeof(accessPointNumber)); + for (iNetIndex = 1; iNetIndex < 100; iNetIndex++) // skip the 0th connection + { + if (sceUtilityCheckNetParam(iNetIndex) == 0) + { + totalAccessPoints++; + accessPointNumber[totalAccessPoints] = iNetIndex; + } + } + if(accesspoint.value > totalAccessPoints) + Cvar_SetValue("accesspoint", 1); + } + + if(!host_initialized) + { + Cvar_RegisterVariable(&accesspoint); + } + + if(!tcpipAvailable) + return -1; + + char szMyIPAddr[32]; + struct qsockaddr addr; + char *colon; + + char buff[MAXHOSTNAMELEN]; + + S_ClearBuffer (); // so dma doesn't loop current sound + + // Load the network modules when they are required + const int err1 = sceUtilityLoadNetModule(PSP_NET_MODULE_COMMON); + if(err1 < 0) + { + Con_Printf("Couldn't load MODULE_COMMON: %08x\n",err1); + } + const int err2 = sceUtilityLoadNetModule(PSP_NET_MODULE_INET); + if(err2 < 0) + { + Con_Printf("Couldn't load MODULE_INET: %09x\n ",err2); + } + + // Initialise the network. + const int err = pspSdkInetInit(); + if (err) + { + Con_Printf("Couldn't initialize the network %08X\n", err); + sceUtilityUnloadNetModule(PSP_NET_MODULE_INET); + sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON); + return -1; + } + + if(!connect_to_apctl(accessPointNumber[(int)accesspoint.value])) + { + Con_Printf("Unable to connect to access point\n"); + pspSdkInetTerm(); + sceUtilityUnloadNetModule(PSP_NET_MODULE_INET); + sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON); + + return -1; + } + + // connected, get my IPADDR and run test + if (sceNetApctlGetInfo(8, (SceNetApctlInfo*)szMyIPAddr) != 0) + strcpy(szMyIPAddr, "unknown IP address"); + + // determine my name & address + gethostname(buff, MAXHOSTNAMELEN); + + my_addr = inet_addr(szMyIPAddr); + + // if the quake hostname isn't set, set it to the machine name + if (Q_strcmp(hostname.string, "UNNAMED") == 0) + { + buff[15] = 0; + Cvar_Set ("hostname", buff); + } + + if ((control_socket = open_socket(0)) == -1) + { + pspSdkInetTerm(); + sceUtilityUnloadNetModule(PSP_NET_MODULE_INET); + sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON); + Sys_Error("init: Unable to open control socket\n"); + } + + ((struct sockaddr_in *)&broadcast_addr)->sin_family = AF_INET; + ((struct sockaddr_in *)&broadcast_addr)->sin_addr.s_addr = INADDR_BROADCAST; + ((struct sockaddr_in *)&broadcast_addr)->sin_port = htons(net_hostport); + + get_socket_addr (control_socket, &addr); + Q_strcpy(my_tcpip_address, addr_to_string(&addr)); + colon = Q_strrchr (my_tcpip_address, ':'); + if (colon) + *colon = 0; + + Con_Printf("UDP Initialized\n"); + tcpipAvailable = qtrue; + + return control_socket; + } + + //============================================================================= + + void shut_down (void) + { + listen(qfalse); + close_socket(control_socket); + + pspSdkInetTerm(); + + // Now to unload the network modules, no need to keep them loaded all the time + sceUtilityUnloadNetModule(PSP_NET_MODULE_INET); + sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON); + } + + //============================================================================= + + void listen (qboolean state) + { + // enable listening + if (state) + { + if (accept_socket != -1) + return; + if ((accept_socket = open_socket(net_hostport)) == -1) + Sys_Error ("listen: Unable to open accept socket\n"); + return; + } + + // disable listening + if (accept_socket == -1) + return; + close_socket(accept_socket); + + accept_socket = -1; + } + + //============================================================================= + + int open_socket (int port) + { + int newsocket; + struct sockaddr_in address; + + if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + return -1; + + int val = 1; + if(setsockopt(newsocket, SOL_SOCKET, SO_NONBLOCK, &val, sizeof(val)) < 0) + goto ErrorReturn; + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(port); + if( bind (newsocket, (sockaddr *)&address, sizeof(address)) == -1) + goto ErrorReturn; + + printf("got here4\n"); + return newsocket; + + ErrorReturn: + close(newsocket); + return -1; + } + + //============================================================================= + + int close_socket (int socket) + { + if (socket == broadcast_socket) + broadcast_socket = 0; + return close(socket); + } + + int connect (int socket, struct qsockaddr *addr) + { + return 0; + } + + //============================================================================= + + int check_new_connections (void) + { + byte buf[1000]; + + if (accept_socket == -1) + return -1; + + // Peek at the message and if there is a message waiting then return the + // socket + int ret = recvfrom(accept_socket, buf, 1000, MSG_PEEK, NULL, NULL); + + if(ret > 0) + return accept_socket; + + return -1; + } + + //============================================================================= + + int 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, (socklen_t*)&addrlen); + if (ret == -1) + { + int errno = sceNetInetGetErrno(); + if(errno == EWOULDBLOCK || errno == ECONNREFUSED) + { + return 0; + } + } + + return ret; + } + + //============================================================================= + + static int make_socket_broadcast_capable (int socket) + { + int i = 1; + + // make this socket broadcast capable + if (setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i)) < 0) + return -1; + broadcast_socket = socket; + + return 0; + } + + //============================================================================= + + int broadcast (int socket, byte *buf, int len) + { + int ret; + + if (socket != broadcast_socket) + { + if (broadcast_socket != 0) + Sys_Error("Attempted to use multiple broadcasts sockets\n"); + ret = make_socket_broadcast_capable (socket); + if (ret == -1) + { + Con_Printf("Unable to make socket broadcast capable\n"); + return ret; + } + } + + return write(socket, buf, len, &broadcast_addr); + } + + //============================================================================= + + int write (int socket, byte *buf, int len, struct qsockaddr *addr) + { + int ret; + + ret = sendto (socket, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); + //ret = sceNetInetSendto (socket, buf, len, 0, (struct sockaddr *)addr, sizeof(struct qsockaddr)); + if (ret == -1) + { + int errno = sceNetInetGetErrno(); + if(errno == EWOULDBLOCK || errno == ECONNREFUSED) + { + return 0; + } + Con_Printf("Failed to send message, errno=%08X\n", errno); + } + return ret; + } + + //============================================================================= + + char* addr_to_string (struct qsockaddr *addr) + { + static char buffer[22]; + int haddr; + + haddr = ntohl(((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, ntohs(((struct sockaddr_in *)addr)->sin_port)); + return buffer; + } + + //============================================================================= + + int string_to_addr (char *string, struct qsockaddr *addr) + { + 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 = htonl(ipaddr); + ((struct sockaddr_in *)addr)->sin_port = htons(hp); + return 0; + } + + //============================================================================= + + int get_socket_addr (int socket, struct qsockaddr *addr) + { + socklen_t addrlen = sizeof(struct qsockaddr); + unsigned int a; + + Q_memset(addr, 0, sizeof(struct qsockaddr)); + getsockname(socket, (struct sockaddr *)addr, &addrlen); + a = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + if (a == 0 || a == inet_addr("127.0.0.1")) + ((struct sockaddr_in *)addr)->sin_addr.s_addr = my_addr; + + return 0; + } + + //============================================================================= + + int get_name_from_addr (struct qsockaddr *addr, char *name) + { + struct hostent *hostentry; + + hostentry = gethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr), AF_INET); + if (hostentry) + { + Q_strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1); + return 0; + } + + Q_strcpy (name, addr_to_string (addr)); + return 0; + } + + //============================================================================= + + int get_addr_from_name(char *name, struct qsockaddr *addr) + { + struct hostent *hostentry; + + if (name[0] >= '0' && name[0] <= '9') + return PartialIPAddress (name, addr); + + hostentry = gethostbyname (name); + if (!hostentry) + return -1; + + addr->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_port = htons(net_hostport); + ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0]; + + return 0; + } + + //============================================================================= + + int addr_compare (struct qsockaddr *addr1, struct qsockaddr *addr2) + { + if (addr1->sa_family != addr2->sa_family) + return -1; + + if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr) + return -1; + + if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port) + return 1; + + return 0; + } + + //============================================================================= + + int get_socket_port (struct qsockaddr *addr) + { + return ntohs(((struct sockaddr_in *)addr)->sin_port); + } + + + int set_socket_port (struct qsockaddr *addr, int port) + { + ((struct sockaddr_in *)addr)->sin_port = htons(port); + return 0; + } + + /* + ============ + PartialIPAddress + + this lets you type only as much of the net address as required, using + the local network components to fill in the rest + ============ + */ + 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 = Q_atoi(b); + else + port = net_hostport; + + hostaddr->sa_family = AF_INET; + ((struct sockaddr_in *)hostaddr)->sin_port = htons((short)port); + ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (my_addr & htonl(mask)) | htonl(addr); + + return 0; + } + //============================================================================= + /* Connect to an access point */ + int connect_to_apctl(int config) + { + int err; + int stateLast = -1; + int timeout = 0; + + /* Connect using the first profile */ + err = sceNetApctlConnect(config); + if (err != 0) + { + return 0; + } + + while (1) + { + int state; + err = sceNetApctlGetState(&state); + if (err != 0) + { + break; + } + if (state > stateLast) + { + stateLast = state; + timeout = 0; + } + if (state == 4) + break; // connected with static IP + + // wait a little before polling again + sceKernelDelayThread(50*1000); // 50ms + + timeout++; + if(timeout > 200) + { + Con_Printf("Timeout connecting to access point. State=%d\n", state); + return 0; + } + } + + if(err != 0) + { + return 0; + } + + return 1; + } + //============================================================================= + } + namespace adhoc + { + static int accept_socket = -1; // socket for fielding new connections + static int control_socket = -1; + static int broadcast_socket = 0; + static struct qsockaddr broadcast_addr; + + int init (void) + { + if(!tcpipAvailable) return -1; + + struct qsockaddr addr; + char *colon; + char buff[MAXHOSTNAMELEN]; + int i,rc; + + S_ClearBuffer (); + + sceUtilityLoadNetModule(PSP_NET_MODULE_COMMON); + sceUtilityLoadNetModule(PSP_NET_MODULE_ADHOC); + + int stateLast = -1; + rc = pspSdkAdhocInit("ULUS00443"); + if(rc < 0) { + Con_Printf("Couldn't initialise the network %08X\n", rc); + sceUtilityUnloadNetModule(PSP_NET_MODULE_ADHOC); + sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON); + return -1; + } + + rc = sceNetAdhocctlConnect((const char *)"quake"); + if(rc < 0) { + Con_Printf("Couldn't initialise the network %08X\n", rc); + pspSdkAdhocTerm(); + sceUtilityUnloadNetModule(PSP_NET_MODULE_ADHOC); + sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON); + return -1; + } + + while (1) { + int state; + rc = sceNetAdhocctlGetState(&state); + if (rc != 0) { + Con_Printf("Couldn't initialise the network %08X\n", rc); + pspSdkAdhocTerm(); + sceUtilityUnloadNetModule(PSP_NET_MODULE_ADHOC); + sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON); + return -1; + } + if (state > stateLast) { + stateLast = state; + } + if (state == 1) { + break; + } + sceKernelDelayThread(50*1000); // 50ms + } + + gethostname(buff, MAXHOSTNAMELEN); + if (Q_strcmp(hostname.string, "UNNAMED") == 0) + { + buff[15] = 0; + Cvar_Set ("hostname", buff); + } + + if ((control_socket = open_socket(0)) == -1) { + pspSdkAdhocTerm(); + sceUtilityUnloadNetModule(PSP_NET_MODULE_ADHOC); + sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON); + Sys_Error("init: Unable to open control socket\n"); + } + + ((sockaddr_adhoc *)&broadcast_addr)->family = ADHOC_NET; + for(i=0; i<6; i++) ((sockaddr_adhoc *)&broadcast_addr)->mac[i] = 0xFF; + ((sockaddr_adhoc *)&broadcast_addr)->port = net_hostport; + + get_socket_addr (control_socket, &addr); + Q_strcpy(my_tcpip_address, addr_to_string(&addr)); + colon = Q_strrchr(my_tcpip_address, ':'); + if (colon) *colon = 0; + + listen(qtrue); + + Con_Printf("AdHoc Initialized\n"); + tcpipAvailable = qtrue; + tcpipAdhoc = qtrue; + return control_socket; + } + + //============================================================================= + + void shut_down (void) + { + listen(qfalse); + close_socket(control_socket); + pspSdkAdhocTerm(); + + // Now to unload the network modules, no need to keep them loaded all the time + sceUtilityUnloadNetModule(PSP_NET_MODULE_ADHOC); + sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON); + } + + //============================================================================= + + void listen (qboolean state) + { + // enable listening + if (state) + { + if (accept_socket != -1) + return; + if ((accept_socket = open_socket(net_hostport)) == -1) + Sys_Error ("listen: Unable to open accept socket\n"); + return; + } + + // disable listening + if (accept_socket == -1) + return; + close_socket(accept_socket); + accept_socket = -1; + } + + //============================================================================= + + int open_socket (int port) + { + u8 mac[8]; + sceWlanGetEtherAddr(mac); + int rc = sceNetAdhocPdpCreate(mac, port, 0x2000, 0); + if(rc < 0) return -1; + return rc; + } + + //============================================================================= + + int close_socket (int socket) + { + if (socket == broadcast_socket) broadcast_socket = 0; + return sceNetAdhocPdpDelete(socket, 0); + } + + int connect (int socket, struct qsockaddr *addr) + { + return 0; + } + + //============================================================================= + + int check_new_connections (void) + { + pdpStatStruct pdpStat[20]; + int length = sizeof(pdpStatStruct) * 20; + + if (accept_socket == -1) + return -1; + + int err = sceNetAdhocGetPdpStat(&length, pdpStat); + if(err < 0) return -1; + + pdpStatStruct *tempPdp = findPdpStat(accept_socket, pdpStat); + + if(tempPdp < 0) return -1; + + if(tempPdp->rcvdData > 0) return accept_socket; + + return -1; + } + + //============================================================================= + + int read (int socket, byte *buf, int len, struct qsockaddr *addr) + { + unsigned short port; + int datalength = len; + unsigned int ret; + + sceKernelDelayThread(1); + + ret = sceNetAdhocPdpRecv(socket, (unsigned char *)((sockaddr_adhoc *)addr)->mac, &port, buf, &datalength, 0, 1); + if(ret == ADHOC_EWOULDBLOCK) return 0; + + ((sockaddr_adhoc *)addr)->port = port; + + return datalength; + } + + //============================================================================= + + static int make_socket_broadcast_capable (int socket) + { + broadcast_socket = socket; + return 0; + } + + //============================================================================= + + int broadcast (int socket, byte *buf, int len) + { + int ret; + + if (socket != broadcast_socket) + { + if (broadcast_socket != 0) + Sys_Error("Attempted to use multiple broadcasts sockets\n"); + ret = make_socket_broadcast_capable (socket); + if (ret == -1) + { + Con_Printf("Unable to make socket broadcast capable\n"); + return ret; + } + } + + return write(socket, buf, len, &broadcast_addr); + } + + //============================================================================= + + int write (int socket, byte *buf, int len, struct qsockaddr *addr) + { + int ret = -1; + + ret = sceNetAdhocPdpSend(socket, (unsigned char*)((sockaddr_adhoc *)addr)->mac, ((sockaddr_adhoc *)addr)->port, buf, len, 0, 1); + + //if(ret < 0) Con_Printf("Failed to send message, errno=%08X\n", ret);//blubs + return ret; + } + + //============================================================================= + + char* addr_to_string (struct qsockaddr *addr) + { + static char buffer[22]; + + sceNetEtherNtostr((unsigned char *)((sockaddr_adhoc *)addr)->mac, buffer); + sprintf(buffer + strlen(buffer), ":%d", ((sockaddr_adhoc *)addr)->port); + return buffer; + } + + //============================================================================= + + int string_to_addr (char *string, struct qsockaddr *addr) + { + int ha1, ha2, ha3, ha4, ha5, ha6, hp; + + sscanf(string, "%x:%x:%x:%x:%x:%x:%d", &ha1, &ha2, &ha3, &ha4, &ha5, &ha6, &hp); + 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 get_socket_addr (int socket, struct qsockaddr *addr) + { + pdpStatStruct pdpStat[20]; + int length = sizeof(pdpStatStruct) * 20; + + int err = sceNetAdhocGetPdpStat(&length, pdpStat); + if(err<0) return -1; + + pdpStatStruct *tempPdp = findPdpStat(socket, pdpStat); + if(tempPdp < 0) return -1; + + memcpy(((struct sockaddr_adhoc *)addr)->mac, tempPdp->mac, 6); + ((struct sockaddr_adhoc *)addr)->port = tempPdp->port; + addr->sa_family = ADHOC_NET; + return 0; + } + + //============================================================================= + + int get_name_from_addr (struct qsockaddr *addr, char *name) + { + strcpy(name, addr_to_string(addr)); + return 0; + } + + //============================================================================= + + int get_addr_from_name(char *name, struct qsockaddr *addr) + { + return string_to_addr(name, addr); + } + + //============================================================================= + + int addr_compare (struct qsockaddr *addr1, struct qsockaddr *addr2) + { + //if (addr1->sa_family != addr2->sa_family) return -1; + 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; + return 0; + } + + //============================================================================= + + int get_socket_port (struct qsockaddr *addr) + { + return ((struct sockaddr_adhoc *)addr)->port; + } + + + int set_socket_port (struct qsockaddr *addr, int port) + { + ((struct sockaddr_adhoc *)addr)->port = port; + return 0; + } + + //============================================================================= + + int pspSdkAdhocInit(char *product) + { + u32 retVal; + struct productStruct temp; + + retVal = sceNetInit(0x20000, 0x20, 0x1000, 0x20, 0x1000); + if (retVal != 0) return retVal; + + retVal = sceNetAdhocInit(); + if (retVal != 0) return retVal; + + strcpy(temp.product, product); + temp.unknown = 0; + + retVal = sceNetAdhocctlInit(0x2000, 0x20, &temp); + if (retVal != 0) return retVal; + + return 0; + } + + //============================================================================= + + void pspSdkAdhocTerm() + { + sceNetAdhocctlTerm(); + sceNetAdhocTerm(); + sceNetTerm(); + } + + //============================================================================= + + } + } +} diff --git a/source/psp/network_psp.hpp b/source/psp/network_psp.hpp new file mode 100644 index 0000000..ba6297a --- /dev/null +++ b/source/psp/network_psp.hpp @@ -0,0 +1,84 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 QUAKE_NETWORK_PSP_HPP +#define QUAKE_NETWORK_PSP_HPP + +extern "C" +{ +#include "../quakedef.h" +} + +namespace quake +{ + namespace network + { + namespace infrastructure + { + int init (void); + void shut_down (void); + void listen (qboolean state); + int open_socket (int port); + int close_socket (int socket); + int connect (int socket, struct qsockaddr *addr); + int check_new_connections (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* addr_to_string (struct qsockaddr *addr); + int string_to_addr (char *string, struct qsockaddr *addr); + int get_socket_addr (int socket, struct qsockaddr *addr); + int get_name_from_addr (struct qsockaddr *addr, char *name); + int get_addr_from_name (char *name, struct qsockaddr *addr); + int addr_compare (struct qsockaddr *addr1, struct qsockaddr *addr2); + int get_socket_port (struct qsockaddr *addr); + int set_socket_port (struct qsockaddr *addr, int port); + + int connect_to_apctl(int config); + int PartialIPAddress (char *in, struct qsockaddr *hostaddr); + } + namespace adhoc + { + int init (void); + void shut_down (void); + void listen (qboolean state); + int open_socket (int port); + int close_socket (int socket); + int connect (int socket, struct qsockaddr *addr); + int check_new_connections (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* addr_to_string (struct qsockaddr *addr); + int string_to_addr (char *string, struct qsockaddr *addr); + int get_socket_addr (int socket, struct qsockaddr *addr); + int get_name_from_addr (struct qsockaddr *addr, char *name); + int get_addr_from_name (char *name, struct qsockaddr *addr); + int addr_compare (struct qsockaddr *addr1, struct qsockaddr *addr2); + int get_socket_port (struct qsockaddr *addr); + int set_socket_port (struct qsockaddr *addr, int port); + int pspSdkAdhocInit(char *product); + void pspSdkAdhocTerm(); + } + } +} + +#endif diff --git a/source/psp/pics/icon.png b/source/psp/pics/icon.png new file mode 100644 index 0000000..885fc65 Binary files /dev/null and b/source/psp/pics/icon.png differ diff --git a/source/psp/pics/pic.png b/source/psp/pics/pic.png new file mode 100644 index 0000000..872a6eb Binary files /dev/null and b/source/psp/pics/pic.png differ diff --git a/source/psp/pics/snd0.at3 b/source/psp/pics/snd0.at3 new file mode 100644 index 0000000..bee6d4d Binary files /dev/null and b/source/psp/pics/snd0.at3 differ diff --git a/source/psp/readme.txt b/source/psp/readme.txt new file mode 100644 index 0000000..df71d1b --- /dev/null +++ b/source/psp/readme.txt @@ -0,0 +1,12 @@ +used parts from: +QRack by R00K, +JoeQuake by joseph, +FitzQuake by john fitzburg, +Kurok by mdave, +FuhQuake, +FteQuake, +and DarkPlaces! +Many not fixed bugs! + + +Crow_Bar. 2009 diff --git a/source/psp/sound.cpp b/source/psp/sound.cpp new file mode 100644 index 0000000..e77dae2 --- /dev/null +++ b/source/psp/sound.cpp @@ -0,0 +1,177 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 + +extern "C" +{ +#include "../quakedef.h" +} + +namespace quake +{ + namespace sound + { + struct Sample + { + short left; + short right; + }; + + static const unsigned int channelCount = 2; + static const unsigned int inputBufferSize = 16384; + static const unsigned int inputFrequency = 11025; + static const unsigned int outputFrequency = 44100; + static const unsigned int inputSamplesPerOutputSample = outputFrequency / inputFrequency; + static Sample inputBuffer[inputBufferSize]; + static volatile unsigned int samplesRead; + + static inline void copySamples(const Sample* first, const Sample* last, Sample* destination) + { + switch (inputSamplesPerOutputSample) + { + case 1: + memcpy(destination, first, (last - first) * sizeof(Sample)); + break; + + case 2: + for (const Sample* source = first; source != last; ++source) + { + const Sample sample = *source; + *destination++ = sample; + *destination++ = sample; + } + break; + + case 4: + for (const Sample* source = first; source != last; ++source) + { + const Sample sample = *source; + *destination++ = sample; + *destination++ = sample; + *destination++ = sample; + *destination++ = sample; + } + break; + + default: + break; + } + } + + static void fillOutputBuffer(void* buffer, unsigned int samplesToWrite, void* userData) + { + // Where are we writing to? + Sample* const destination = static_cast (buffer); + + // Where are we reading from? + const Sample* const firstSampleToRead = &inputBuffer[samplesRead]; + + // How many samples to read? + const unsigned int samplesToRead = samplesToWrite / inputSamplesPerOutputSample; + + // Going to wrap past the end of the input buffer? + const unsigned int samplesBeforeEndOfInput = inputBufferSize - samplesRead; + if (samplesToRead > samplesBeforeEndOfInput) + { + // Yes, so write the first chunk from the end of the input buffer. + copySamples( + firstSampleToRead, + firstSampleToRead + samplesBeforeEndOfInput, + &destination[0]); + + // Write the second chunk from the start of the input buffer. + const unsigned int samplesToReadFromBeginning = samplesToRead - samplesBeforeEndOfInput; + copySamples( + &inputBuffer[0], + &inputBuffer[samplesToReadFromBeginning], + &destination[samplesBeforeEndOfInput * inputSamplesPerOutputSample]); + } + else + { + // No wrapping, just copy. + copySamples( + firstSampleToRead, + firstSampleToRead + samplesToRead, + &destination[0]); + } + + // Update the read offset. + samplesRead = (samplesRead + samplesToRead) % inputBufferSize; + } + } +} + +using namespace quake; +using namespace quake::sound; + +qboolean SNDDMA_Init(void) +{ + // Set up Quake's audio. + shm = &sn; + shm->channels = channelCount; + shm->samplebits = 16; + shm->speed = inputFrequency; + shm->soundalive = qtrue; + shm->splitbuffer = qfalse; + shm->samples = inputBufferSize * channelCount; + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) inputBuffer; + + // Initialise the audio system. This initialises it for the CD audio module + // too. + pspAudioInit(); + + // Set the channel callback. + // Sound effects use channel 0, CD audio uses channel 1. + pspAudioSetChannelCallback(0, fillOutputBuffer, 0); + + return qtrue; +} + +void SNDDMA_Shutdown(void) +{ + // Clear the mixing buffer so we don't get any noise during cleanup. + memset(inputBuffer, 0, sizeof(inputBuffer)); + + // Clear the channel callback. + pspAudioSetChannelCallback(0, 0, 0); + + // Stop the audio system? + pspAudioEndPre(); + + // Insert a false delay so the thread can be cleaned up. + sceKernelDelayThread(50 * 1000); + + // Shut down the audio system. + pspAudioEnd(); +} + +int SNDDMA_GetDMAPos(void) +{ + return samplesRead * channelCount; +} + +void SNDDMA_Submit(void) +{ +} diff --git a/source/psp/system.cpp b/source/psp/system.cpp new file mode 100644 index 0000000..6990fa6 --- /dev/null +++ b/source/psp/system.cpp @@ -0,0 +1,526 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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. + +*/ + +// +// 12/5/2020 - Use ADQuake's system.cpp and its move from std to sceIO for file I/O +// because std::fclose() is bunked -- motolegacy +// creds to st1x51 +// + +#define ENABLE_PRINTF 0 + +#include "system.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef PROFILE +#include +#endif + +extern "C" +{ +#include "../sys.h" +#include "../quakedef.h" +} +#include "fnmatch.h" + +void CDAudio_Stop(void); + +namespace quake +{ + namespace main + { + extern const int cpuClockSpeed; + extern const int ramClockSpeed; + extern const int busClockSpeed; + } + + namespace system + { + struct file + { + // Set on open. + char name[MAX_OSPATH + 1]; + bool write; +#if 0 + // Set on open, suspend, restore. + FILE* handle; +#else + SceUID handle; +#endif + // Set on suspend. + SceOff offset; + }; + + static bool debugScreenInitialized = false; + static const std::size_t file_count = 64; + static file files[file_count]; + + void suspend() + { + Con_Printf("Suspend requested \n"); + CDAudio_Pause(); + // Close each file. + for (std::size_t file_index = 0; file_index < file_count; ++file_index) + { + // Is the file in use? + file& file = files[file_index]; + if (file.name[0]) + { + // Save the offset; +#if 0 + file.offset = ftell(file.handle); +#else + file.offset = sceIoLseek(file.handle, 0, SEEK_CUR); +#endif + // Close the file. +#if 0 + fclose(file.handle); +#else + sceIoClose(file.handle); +#endif + file.handle = 0; + } + } + + Con_Printf("Filesystem sunspended\n"); + } + + void resume() + { + Con_Printf("Resume requested\n"); + // Check each file. + for (std::size_t file_index = 0; file_index < file_count; ++file_index) + { + // Is the file in use? + file& file = files[file_index]; + if (file.name[0]) + { + // Reopen the file. This can repeatedly fail, so we keep trying. +#if 0 + const char* mode = file.write ? "ab" : "rb"; +#else + int mode = file.write ? PSP_O_APPEND : PSP_O_RDONLY; +#endif +#if 0 + do + { + file.handle = fopen(file.name, mode); + } + while (!file.handle); + + // Restore the offset; + if (fseek(file.handle, file.offset, SEEK_SET) != 0) + { + throw std::runtime_error("Couldn't seek in file"); + } +#else + file.handle = sceIoOpen(file.name, mode, 0777); + if(file.handle < 0) + { + throw std::runtime_error("Couldn't open file"); + } + + // Restore the offset; + if (sceIoLseek(file.handle, file.offset, SEEK_SET) != 0) + { + throw std::runtime_error("Couldn't seek in file"); + } + +#endif + } + } + + CDAudio_Resume(); + Con_Printf("Filesystem resumed\n"); + + } + } +} + +using namespace quake; +using namespace quake::system; + +int Sys_FileOpenRead (char *path, int *hndl) +{ + // Find an unused file. + for (std::size_t file_index = 1; file_index < file_count; ++file_index) + { + // Is the file in use? + file& file = files[file_index]; + if (file.name[0]) + { + continue; + } + + // Open the file. +#if 0 + file.handle = fopen(path, "rb"); + if (!file.handle) +#else + file.handle = sceIoOpen(path, PSP_O_RDONLY, 0777); + if (file.handle < 0) +#endif + { + *hndl = -1; + return -1; + } + + // Get the length. +#if 0 + if (fseek(file.handle, 0, SEEK_END) != 0) + { + Sys_Error("fseek failed"); + } + const long length = ftell(file.handle); + if (fseek(file.handle, 0, SEEK_SET) != 0) + { + Sys_Error("fseek failed"); + } +#else + SceOff pos = sceIoLseek(file.handle, 0, SEEK_CUR); + const long length = sceIoLseek(file.handle, 0, SEEK_END); + sceIoLseek(file.handle, pos, SEEK_SET); +#endif + // The file is now in use! + Q_strncpy(file.name, path, MAX_OSPATH); + file.write = false; + + // Done. + *hndl = file_index; + return length; + } + + Sys_Error("Out of file slots"); + return -1; +} + +int Sys_FileOpenWrite (char *path) +{ + // Find an unused file. + for (std::size_t file_index = 1; file_index < file_count; ++file_index) + { + // Is the file in use? + file& file = files[file_index]; + if (file.name[0]) + { + continue; + } + + // Open the file. +#if 0 + file.handle = fopen(path, "wb"); + if (!file.handle) +#else + file.handle = sceIoOpen(path, PSP_O_WRONLY, 0777); + if (file.handle < 0) +#endif + { + return -1; + } + + // The file is now in use! + Q_strncpy(file.name, path, MAX_OSPATH); + file.write = true; + + // Done. + return file_index; + } + + Sys_Error("Out of file slots"); + return -1; +} + +void Sys_FileClose (int handle) +{ + // Close the file. + file& file = files[handle]; +#if 0 + fclose(file.handle); +#else + sceIoClose(file.handle); +#endif + file.handle = 0; + file.name[0] = 0; +} + +void Sys_FileSeek (int handle, int position) +{ + file& file = files[handle]; +#if 0 + if (fseek(file.handle, position, SEEK_SET) != 0) + { + Sys_Error("fseek failed"); + } +#else + sceIoLseek(file.handle, position, SEEK_SET); +#endif +} + +int Sys_FileRead (int handle, void *dest, int count) +{ + file& file = files[handle]; +#if 0 + return fread(dest, 1, count, file.handle); +#else + return sceIoRead(file.handle, dest, count); +#endif +} + +int Sys_FileWrite (int handle, void *data, int count) +{ + file& file = files[handle]; +#if 0 + return fwrite(data, 1, count, file.handle); +#else + return sceIoWrite(file.handle, data, count); +#endif +} + +int Sys_FileTime (char *path) +{ + /* +#ifdef _WIN32 + return -1; +#else + */ + struct stat s; + memset(&s, 0, sizeof(s)); + + if (stat(path, &s) < 0) + { + return -1; + } + + return s.st_ctime; + /* +#endif + */ +} + +void Sys_mkdir (char *path) +{ + Sys_Printf("Mkdir: %s\n", path); + sceIoMkdir(path, 0x777); +} + +void Sys_Error (char *error, ...) +{ + // Clear the sound buffer. + S_ClearBuffer(); + + // Put the error message in a buffer. + va_list args; + va_start(args, error); + char buffer[1024]; + memset(buffer, 0, sizeof(buffer)); + vsnprintf(buffer, sizeof(buffer) - 1, error, args); + va_end(args); + + Con_Printf(buffer); + // Print the error message to the debug screen. + if (!debugScreenInitialized) + { + pspDebugScreenInit(); + debugScreenInitialized = true; + } + pspDebugScreenSetTextColor(0xffffff); + pspDebugScreenPrintf("The following error occurred:\n"); + pspDebugScreenSetTextColor(0x0000ff); + pspDebugScreenPrintData(buffer, strlen(buffer)); + pspDebugScreenSetTextColor(0xffffff); + pspDebugScreenPrintf("\n\nPress CROSS to quit.\n"); + + // Wait for a button press. + sceCtrlSetSamplingCycle(0); + sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG); + SceCtrlData pad; + do { + sceCtrlReadBufferPositive(&pad, 1); + } while (pad.Buttons & PSP_CTRL_CROSS); + do { + sceCtrlReadBufferPositive(&pad, 1); + } while ((pad.Buttons & PSP_CTRL_CROSS) == 0); + do { + sceCtrlReadBufferPositive(&pad, 1); + } while (pad.Buttons & PSP_CTRL_CROSS); + + // Quit. + pspDebugScreenPrintf("Shutting down...\n"); + Sys_Quit(); +} + +void Sys_Printf (char *fmt, ...) +{ +#if ENABLE_PRINTF + char buffer[1024]; + + va_list args; + va_start(args, fmt); + memset(buffer, 0, sizeof(buffer)); + vsnprintf(buffer, sizeof(buffer) - 1, fmt, args); + va_end(args); + + if (!debugScreenInitialized) + { + pspDebugScreenInit(); + debugScreenInitialized = true; + } + pspDebugScreenPrintData(buffer, strlen(buffer)); +#endif +} + +void Sys_Quit (void) +{ + // Shut down the host system. + if (host_initialized) + { + Host_Shutdown(); + } + + // Restore the old clock frequency. + scePowerSetClockFrequency(main::cpuClockSpeed, main::ramClockSpeed, main::busClockSpeed); + + // Insert a false delay so files and stuff can be saved before the kernel kills us. + sceKernelDelayThread(50 * 1000); +#ifdef PROFILE + gprof_cleanup(); +#endif + // Exit. + sceKernelExitGame(); +} + +double Sys_FloatTime (void) +{ + u64 ticks; + sceRtcGetCurrentTick(&ticks); + return ticks * 0.000001; +} + +char *Sys_ConsoleInput (void) +{ + return 0; +} + +void Sys_SendKeyEvents (void) +{ +} + +void Sys_LowFPPrecision (void) +{ +} + +void Sys_HighFPPrecision (void) +{ +} + +/* +================================================= +simplified findfirst/findnext implementation: +Sys_FindFirstFile and Sys_FindNextFile return +filenames only, not a dirent struct. this is +what we presently need in this engine. +================================================= +*/ + +static DIR *finddir; +static struct dirent *finddata; +static char *findpath, *findpattern; + +char *Sys_FindFirstFile (char *path, char *pattern); +void Sys_FindClose (void); +char *Sys_FindNextFile (void); + +char *Sys_FindFirstFile (char *path, char *pattern) +{ + size_t tmp_len; + + if (finddir) + Sys_Error ("Sys_FindFirst without FindClose"); + + finddir = opendir (path); + if (!finddir) + return NULL; + + tmp_len = strlen (pattern); + findpattern = (char*) malloc (tmp_len + 1); + if (!findpattern) + return NULL; + strcpy (findpattern, pattern); + findpattern[tmp_len] = '\0'; + tmp_len = strlen (path); + findpath = (char*) malloc (tmp_len + 1); + if (!findpath) + return NULL; + strcpy (findpath, path); + findpath[tmp_len] = '\0'; + + return Sys_FindNextFile(); +} + +char *Sys_FindNextFile (void) +{ + struct stat test; + + if (!finddir) + return NULL; + + do { + finddata = readdir(finddir); + if (finddata != NULL) + { + if (!fnmatch (findpattern, finddata->d_name, FNM_PATHNAME)) + { + if ( (stat(va("%s/%s", findpath, finddata->d_name), &test) == 0) && S_ISREG(test.st_mode) ) + return finddata->d_name; + } + } + } while (finddata != NULL); + + return NULL; +} + +void Sys_FindClose (void) +{ + if (finddir != NULL) + closedir(finddir); + if (findpath != NULL) + free (findpath); + if (findpattern != NULL) + free (findpattern); + finddir = NULL; + findpath = NULL; + findpattern = NULL; +} + diff --git a/source/psp/system.hpp b/source/psp/system.hpp new file mode 100644 index 0000000..2b17226 --- /dev/null +++ b/source/psp/system.hpp @@ -0,0 +1,37 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 QUAKE_SYSTEM_HPP +#define QUAKE_SYSTEM_HPP + +namespace quake +{ + namespace system + { + // Prepares the system for suspension. + void suspend(); + + // Restores the system after suspension. + void resume(); + } +} + +#endif diff --git a/source/psp/video_hardware.cpp b/source/psp/video_hardware.cpp new file mode 100644 index 0000000..2a1604e --- /dev/null +++ b/source/psp/video_hardware.cpp @@ -0,0 +1,694 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 +#include +#include +#include +#include +#include +extern "C" +{ +#include "../quakedef.h" +} + +#include + +#include "video_hardware_images.h" + +#include "iridlibs/perflib.h" + +#ifdef _WIN32 +# define ALIGNED(x) +#else +# define ALIGNED(x) __attribute__((aligned(x))) +#endif + +#define DLIST_SIZE_KB 512 //256 + +namespace quake +{ + namespace video + { + // Types. + //ScePspRGB565; + //ScePspRGBA8888 + typedef ScePspRGBA8888 pixel; + typedef u8 texel; + typedef u16 depth_value; + + // Constants. + static const std::size_t screen_width = 480; + static const std::size_t screen_height = 272; + static const std::size_t palette_size = 256; + static pixel* display_buffer = 0; + static pixel* draw_buffer = 0; + static depth_value* depth_buffer = 0; + + //! The GU display list. + //! @note Aligned to 64 bytes so it doesn't share a cache line with anything. + unsigned int ALIGNED(64) display_list[DLIST_SIZE_KB * 1024]; + } +} + +using namespace quake; +using namespace quake::video; + +extern qboolean depthfl; + +void* framebuffer; + +// Regular globals. +ScePspRGBA8888 ALIGNED(16) d_8to24table[palette_size]; +ScePspRGBA8888 ALIGNED(16) d_8to24tableLM[palette_size]; +ScePspRGBA8888 ALIGNED(16) d_8to24tableQ2[palette_size]; +ScePspRGBA8888 ALIGNED(16) d_8to24tableH2[palette_size]; +int reloaded_pallete = 1; + +extern cvar_t r_vsync; +extern cvar_t r_dithering; + +void VID_ConvectPalette(ScePspRGBA8888 *out, unsigned char* in) +{ + // Convert the palette to PSP format. + for (ScePspRGBA8888* color = &out[0]; color < &out[palette_size]; ++color) + { + const unsigned int r = *in++; + const unsigned int g = *in++; + const unsigned int b = *in++; + *color = GU_RGBA(r, g, b, 0xff); + } +} +void VID_InitPaleteLM() +{ + // Convert the palette to PSP format. + ScePspRGBA8888* color2 = &d_8to24tableLM[0]; + for (unsigned int i=0; color2 < &d_8to24tableLM[palette_size]; ++color2, i++) + { + *color2 = GU_RGBA(i, i, i, i); + } +} + +void VID_SetPaletteLM() +{ + // Upload the palette. + sceGuClutMode(GU_PSM_8888, 0, palette_size - 1, 0); + sceKernelDcacheWritebackRange(d_8to24tableLM, sizeof(d_8to24tableLM)); + sceGuClutLoad(palette_size / 8, d_8to24tableLM); + reloaded_pallete = 1; +} + +void VID_SetPaletteTX() +{ + // Upload the palette. + sceGuClutMode(GU_PSM_8888, 0, palette_size - 1, 0); + sceKernelDcacheWritebackRange(d_8to24table, sizeof(d_8to24table)); + sceGuClutLoad(palette_size / 8, d_8to24table); + reloaded_pallete = 1; +} + +void VID_SetPalette4(unsigned char* clut4pal) { + sceGuClutMode(GU_PSM_8888, 0, 0xFF, 0); + sceKernelDcacheWritebackRange(clut4pal, sizeof(clut4pal)); + sceGuClutLoad(2, clut4pal); +} + +void VID_SetPalette(unsigned char* palette) +{ + // Convert the palette to PSP format. + for (ScePspRGBA8888* color = &d_8to24table[0]; color < &d_8to24table[palette_size]; ++color) + { + const unsigned int r = *palette++; + const unsigned int g = *palette++; + const unsigned int b = *palette++; + *color = GU_RGBA(r, g, b, 0xff); + } + + // Color 255 is transparent black. + // This is a bit of a dirty hack. + d_8to24table[255] = 0; + + // Upload the palette. + sceGuClutMode(GU_PSM_8888, 0, palette_size - 1, 0); + sceKernelDcacheWritebackRange(d_8to24table, sizeof(d_8to24table)); + sceGuClutLoad(palette_size / 8, d_8to24table); + reloaded_pallete = 1; +} + +void VID_ShiftPalette(unsigned char* palette) +{ + VID_SetPalette(palette); +} + +void VID_Init(unsigned char* palette) +{ + Sys_Printf("VID_Init\n"); + + // Allocate the buffers. + display_buffer = static_cast(valloc(screen_height * 512 * sizeof(pixel))); + if (!display_buffer) + { + Sys_Error("Couldn't allocate display buffer"); + } + + draw_buffer = static_cast(valloc(screen_height * 512 * sizeof(pixel))); + if (!draw_buffer) + { + Sys_Error("Couldn't allocate draw buffer"); + } + + depth_buffer = static_cast(valloc(screen_height * 512 * sizeof(depth_value))); + if (!depth_buffer) + { + Sys_Error("Couldn't allocate depth buffer"); + } + + // Initialise the GU. + sceGuInit(); + + // Set up the GU. + sceGuStart(GU_DIRECT, display_list); + { + //sceGuDrawBuffer(GU_PSM_5650, vrelptr(draw_buffer), 512); + sceGuDrawBuffer(GU_PSM_8888, vrelptr(draw_buffer), 512); + sceGuDispBuffer(screen_width, screen_height, vrelptr(display_buffer), 512); + sceGuDepthBuffer(vrelptr(depth_buffer), 512); + + // Set the rendering offset and viewport. + sceGuOffset(2048 - (screen_width / 2), 2048 - (screen_height / 2)); + sceGuViewport(2048, 2048, screen_width, screen_height); + + // Set up scissoring. + sceGuEnable(GU_SCISSOR_TEST); + sceGuScissor(0, 0, screen_width, screen_height); + + // Set up texturing. + sceGuEnable(GU_TEXTURE_2D); + + // Set up clearing. + sceGuClearDepth(65535); + sceGuClearColor(GU_RGBA(0x10,0x20,0x40,0xff)); + + // Set up depth. + sceGuDepthRange(0, 65535); + sceGuDepthFunc(GU_LEQUAL); + sceGuEnable(GU_DEPTH_TEST); + + // Set the matrices. + sceGumMatrixMode(GU_PROJECTION); + sceGumLoadIdentity(); + sceGumUpdateMatrix(); + sceGumMatrixMode(GU_VIEW); + sceGumLoadIdentity(); + sceGumUpdateMatrix(); + sceGumMatrixMode(GU_MODEL); + sceGumLoadIdentity(); + sceGumUpdateMatrix(); + + // Set up culling. + sceGuFrontFace(GU_CW); + sceGuEnable(GU_CULL_FACE); + sceGuEnable(GU_CLIP_PLANES); + + // Set up blending. + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + } + + sceGuFinish(); + sceGuSync(0,0); + + // Turn on the display. + sceDisplayWaitVblankStart(); + sceGuDisplay(GU_TRUE); + + // Set up Quake's video parameters. + vid.aspect = (static_cast(screen_height) / static_cast(screen_width)) * (4.0f / 3.0f); + vid.buffer = 0; + vid.colormap = host_colormap; + vid.colormap16 = 0; + vid.conbuffer = 0; + vid.conheight = screen_height; + vid.conrowbytes = 0; + vid.conwidth = screen_width; + vid.direct = 0; + vid.fullbright = palette_size - LittleLong(*((int *) vid.colormap + 2048)); + vid.height = screen_height; + vid.maxwarpheight = screen_width; + vid.maxwarpwidth = screen_height; + vid.numpages = INT_MAX; + vid.recalc_refdef = 0; + vid.rowbytes = 0; + vid.width = screen_width; + + // Start a render. + sceGuStart(GU_DIRECT, display_list); + + // Set the palette. + VID_SetPalette(palette); + VID_InitPaleteLM(); + VID_ConvectPalette(d_8to24tableQ2, host_q2pal); + VID_ConvectPalette(d_8to24tableH2, host_h2pal); +} + +void VID_Shutdown(void) +{ + // Finish rendering. + sceGuFinish(); + sceGuSync(0,0); + + // Shut down the display. + sceGuTerm(); + + // Free the buffers. + vfree(depth_buffer); + depth_buffer = 0; + vfree(draw_buffer); + draw_buffer = 0; + vfree(display_buffer); + display_buffer = 0; +} + +void GL_BeginRendering (int *x, int *y, int *width, int *height) +{ + PFL_BeginGPURecord(); + *x = 2048; + *y = 2048; + *width = screen_width; + *height = screen_height; + + if (r_dithering.value) + sceGuEnable(GU_DITHER); + else + sceGuDisable(GU_DITHER); +} + +void GL_EndRendering (void) +{ + // Finish rendering. + sceGuFinish(); + sceGuSync(0, 0); + PFL_EndGPURecord(); + + // At the moment only do this if we are in network mode, once we get above + // 60fps we might as well leave it on for all games + if (tcpipAvailable || r_vsync.value) + sceDisplayWaitVblankStart(); + + // Switch the buffers. + sceGuSwapBuffers(); + std::swap(draw_buffer, display_buffer); + + // Start a new render. + sceGuStart(GU_DIRECT, display_list); +} + +void GL_GetPixelsBGR(byte *buffer, int width, int height, int i) +{ + for (int y = 0; y < height; ++y) + { + const pixel* src = display_buffer + ((height - y - 1) * 512); + for (int x = 0; x < width; ++x) + { + const pixel argb = *src++; +#if 0 + buffer[i++] = ((argb >> 11) & 0x1f) << 3; //B + buffer[i++] = ((argb >> 5 ) & 0x3f) << 2; //G + buffer[i++] = (argb & 0x1f) << 3; //R +#else + buffer[i++] = (argb >> 16) & 0xff;//B + buffer[i++] = (argb >> 8) & 0xff; //G + buffer[i++] = argb & 0xff; //R +#endif + } + } + +} + +void GL_GetPixelsRGB(byte *buffer, int width, int height, int i) +{ + for (int y = 0; y < height; ++y) + { + const pixel* src = display_buffer + ((height - y - 1) * 512); + for (int x = 0; x < width; ++x) + { + const pixel argb = *src++; +#if 0 + buffer[i++] = (argb & 0x1f) << 3; //R + buffer[i++] = ((argb >> 5 ) & 0x3f) << 2; //G + buffer[i++] = ((argb >> 11) & 0x1f) << 3; //B +#else + buffer[i++] = ((argb)& 0xff); //R + buffer[i++] = ((argb >> 8)& 0xff); //G + buffer[i++] = ((argb >> 16)& 0xff); //B +#endif + } + } + +} + +void GL_GetPixelsRGBA(byte *buffer, int width, int height, int i) +{ + for (int y = 0; y < height; ++y) + { + const pixel* src = display_buffer + ((height - y - 1) * 512); + for (int x = 0; x < width; ++x) + { + const pixel argb = *src++; +#if 0 + buffer[i++] = (argb & 0x1f) << 3; //R + buffer[i++] = ((argb >> 5 ) & 0x3f) << 2; //G + buffer[i++] = ((argb >> 11) & 0x1f) << 3; //B + buffer[i++] = 0xff; //A +#else + buffer[i++] = ((argb)& 0xff); //R + buffer[i++] = ((argb >> 8)& 0xff); //G + buffer[i++] = ((argb >> 16)& 0xff); //B + buffer[i++] = ((argb >> 24)& 0xff); //A +#endif + } + } + +} + +u32 GL_GetDrawBuffer(void) +{ + return (u32)vrelptr(draw_buffer); +} + +void D_StartParticles (void) +{ + sceGuDepthMask(GU_TRUE); + if (r_particles_simple.value == qtrue) + { + sceGuDisable(GU_TEXTURE_2D); + } + else + { + sceGuDisable(GU_FOG); + GL_Bind(particletexture); + sceGuEnable(GU_BLEND); + sceGuDepthMask(GU_TRUE); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0xFFFFFFFF); + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); + } +} + +void D_EndParticles (void) +{ + sceGuDepthMask(GU_FALSE); + if (r_particles_simple.value == qtrue) + { + sceGuEnable(GU_TEXTURE_2D); + } + else + { + sceGuEnable(GU_FOG); + sceGuDisable(GU_BLEND); + sceGuDepthMask(GU_FALSE); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + } +} + + + +static int BufIdx = 0; +static int BufSize = 0; + +psp_particle* D_CreateBuffer (int size) +{ + psp_particle* const vertices = static_cast(sceGuGetMemory(size*sizeof(psp_particle))); + + BufSize = size; + BufIdx = 0; + + return vertices; +} + + +void D_DeleteBuffer (psp_particle* vertices) +{ + if (BufIdx > 0) + { + sceGuDrawArray(GU_SPRITES, GU_VERTEX_32BITF|GU_TEXTURE_32BITF|GU_COLOR_8888, BufIdx, 0, vertices); + BufIdx = 0; + BufSize = -1; + } + +} + +int D_DrawParticleBuffered (psp_particle* vertices, particle2_t *pparticle, vec3_t up, vec3_t right, float scale) +{ + unsigned int color = d_8to24table[static_cast(pparticle->color)]; + int i = BufIdx; + + vertices[i].first.x = pparticle->org[0]; + vertices[i].first.y = pparticle->org[1]; + vertices[i].first.z = pparticle->org[2]; + vertices[i].first.s = 0.0; + vertices[i].first.t = 0.0; + vertices[i].first.color = color; + + vertices[i].second.x = pparticle->org[0] + scale*(up[0] + right[0]); + vertices[i].second.y = pparticle->org[1] + scale*(up[1] + right[1]); + vertices[i].second.z = pparticle->org[2] + scale*(up[2] + right[2]); + vertices[i].second.s = 1.0; + vertices[i].second.t = 1.0; + vertices[i].second.color = color; + + BufIdx++; + + if (BufIdx >= BufSize) + { + sceGuDrawArray(GU_SPRITES, GU_VERTEX_32BITF|GU_TEXTURE_32BITF|GU_COLOR_8888, BufSize, 0, vertices); + BufIdx = 0; + BufSize = -1; + return -1; + } + + return BufIdx; +} + + +void D_DrawParticle (particle2_t *pparticle, vec3_t up, vec3_t right, float scale) +{ + unsigned int color = d_8to24table[static_cast(pparticle->color)]; + + struct part_vertex + { + float s, t; + unsigned int color; + float x, y, z; + }; + + part_vertex* const vertices = static_cast(sceGuGetMemory(2*sizeof(part_vertex))); + + vertices[0].x = pparticle->org[0]; + vertices[0].y = pparticle->org[1]; + vertices[0].z = pparticle->org[2]; + vertices[0].s = 0.0; + vertices[0].t = 0.0; + vertices[0].color = color; + + vertices[1].x = pparticle->org[0] + scale*(up[0] + right[0]); + vertices[1].y = pparticle->org[1] + scale*(up[1] + right[1]); + vertices[1].z = pparticle->org[2] + scale*(up[2] + right[2]); + vertices[1].s = 1.0; + vertices[1].t = 1.0; + vertices[1].color = color; + + sceGuDrawArray(GU_SPRITES, GU_VERTEX_32BITF|GU_TEXTURE_32BITF|GU_COLOR_8888, 2, 0, vertices); + +} + + +/* +============================================================================== + + 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; + +int Image_WriteJPEG (char *filename, int compression, byte *pixels, int width, int height); + +/* +================== +SCR_ScreenShot_f +================== +*/ +void SCR_ScreenShot_f (void) +{ + byte *buffer; + char name[80]; + char checkname[MAX_OSPATH]; + int buffersize = glwidth * glheight * 3; +// +// find a file name to save it to +// + strcpy(name,"shots/DQshot00.jpg"); + + int i; + + for (i=0 ; i<=100 ; i++) + { + name[12] = i/10 + '0'; + name[13] = i%10 + '0'; + sprintf (checkname, "%s/%s", com_gamedir, name); + if (Sys_FileTime(checkname) == -1) + break; // file doesn't exist + } + + if (i==100) + { + Con_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n"); + return; + } + + buffer = static_cast(malloc(buffersize)); + memset(buffer, 0, buffersize); + + GL_GetPixelsRGB(buffer, glwidth, glheight, 0); + + Image_WriteJPEG (name, 75, buffer + buffersize - 3 * glwidth, -glwidth, glheight); + + free (buffer); + + Con_Printf ("Wrote %s\n", name); +} + +#if 0 +//======================Utility dialog functions================================ + +pspUtilityMsgDialogParams dialog; + +static void ConfigureDialog(pspUtilityMsgDialogParams *dialog, size_t dialog_size) +{ + memset(dialog, 0, dialog_size); + + dialog->base.size = dialog_size; + sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_LANGUAGE, + &dialog->base.language); // Prompt language + sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_UNKNOWN, + &dialog->base.buttonSwap); // X/O button swap + + dialog->base.graphicsThread = 0x11; + dialog->base.accessThread = 0x13; + dialog->base.fontThread = 0x12; + dialog->base.soundThread = 0x10; +} + +void ShowErrorDialog(const unsigned int error) +{ + ConfigureDialog(&dialog, sizeof(dialog)); + dialog.mode = PSP_UTILITY_MSGDIALOG_MODE_ERROR; + dialog.options = PSP_UTILITY_MSGDIALOG_OPTION_ERROR; + dialog.errorValue = error; + + sceUtilityMsgDialogInitStart(&dialog); + + for(;;) + { + + + switch(sceUtilityMsgDialogGetStatus()) + { + + case PSP_UTILITY_DIALOG_VISIBLE: + sceUtilityMsgDialogUpdate(1); + break; + + case PSP_UTILITY_DIALOG_QUIT: + sceUtilityMsgDialogShutdownStart(); + break; + + case PSP_UTILITY_DIALOG_NONE: + return; + + } + + ..upd main frame + } +} + +void ShowMessageDialog(const char *message, int enableYesno) +{ + ConfigureDialog(&dialog, sizeof(dialog)); + dialog.mode = PSP_UTILITY_MSGDIALOG_MODE_TEXT; + dialog.options = PSP_UTILITY_MSGDIALOG_OPTION_TEXT; + + if(enableYesno) + dialog.options |= PSP_UTILITY_MSGDIALOG_OPTION_YESNO_BUTTONS|PSP_UTILITY_MSGDIALOG_OPTION_DEFAULT_NO; + + strcpy(dialog.message, message); + + sceUtilityMsgDialogInitStart(&dialog); + + for(;;) + { + + + + switch(sceUtilityMsgDialogGetStatus()) + { + + case 2: + sceUtilityMsgDialogUpdate(1); + break; + + case 3: + sceUtilityMsgDialogShutdownStart(); + break; + + case 0: + return; + + } + + ..upd main frame + } + +} + +void M_Exit_f (void) +{ + ShowMessageDialog(" Really quit? ", 1); + + if(dialog.buttonPressed == PSP_UTILITY_MSGDIALOG_RESULT_YES) + { + CL_Disconnect (); + Host_ShutdownServer(qfalse); + Sys_Quit (); + } +} +#endif diff --git a/source/psp/video_hardware.h b/source/psp/video_hardware.h new file mode 100644 index 0000000..1fa5ec8 --- /dev/null +++ b/source/psp/video_hardware.h @@ -0,0 +1,419 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 + +void GL_BeginRendering (int *x, int *y, int *width, int *height); +void GL_EndRendering (void); +u32 GL_GetDrawBuffer(void); + +void GL_Upload8(int texture_index, const byte *data, int width, int height); +void GL_Upload16(int texture_index, const byte *data, int width, int height); +int GL_LoadTexture(const char *identifier, int width, int height, const byte *data, qboolean stretch_to_power_of_two, int filter, int mipmap_level); +// CLUT4 +int GL_LoadTexture4(const char *identifier, unsigned int width, unsigned int height, const byte *data, int filter); + +int GL_LoadTextureLM (const char *identifier, int width, int height, const byte *data, int bpp, int filter, qboolean update, int forcopy); +int GL_LoadImages (const char *identifier, int width, int height, const byte *data, qboolean stretch_to_power_of_two, int filter, int mipmap_level, int bpp); +int GL_LoadTexturePixels (byte *data, char *identifier, int width, int height, int mode); +int loadtextureimage (char* filename, int matchwidth, int matchheight, qboolean complain, int filter); +int GL_LoadPaletteTexture (const char *identifier, int width, int height, const byte *data, byte *palette, int paltype, qboolean stretch_to_power_of_two, int filter, int mipmap_level); + +//Crow_bar +void GL_GetPixelsBGR (byte *buffer, int width, int height, int i); +void GL_GetPixelsRGB (byte *buffer, int width, int height, int i); +void GL_GetPixelsRGBA(byte *buffer, int width, int height, int i); + + +#define PAL_RGB 24 +#define PAL_RGBA 32 +#define PAL_Q2 64 //Quake II palette +#define PAL_H2 65 //Hexen II palette + +int GL_LoadPalTex (const char *identifier, int width, int height, const byte *data, qboolean stretch_to_power_of_two, int filter, int mipmap_level, byte *palette, int paltype); + +int GL_LoadPalletedTexture (byte *in, char *identifier, int width, int height, int mode); + +void GL_UnloadTexture (const int texture_index); + +extern int glx, gly, glwidth, glheight; + +/* +--------------------------------- +half-life Render Modes. Crow_bar +--------------------------------- +*/ + +#define TEX_COLOR 1 +#define TEX_TEXTURE 2 +#define TEX_GLOW 3 +#define TEX_SOLID 4 +#define TEX_ADDITIVE 5 +#define TEX_LMPOINT 6 //for light point + +#define ISCOLOR(ent) ((ent)->rendermode == TEX_COLOR && ((ent)->rendercolor[0] <= 1|| \ + (ent)->rendercolor[1] <= 1|| \ + (ent)->rendercolor[2] <= 1)) + +#define ISTEXTURE(ent) ((ent)->rendermode == TEX_TEXTURE && (ent)->renderamt > 0 && (ent)->renderamt <= 1) +#define ISGLOW(ent) ((ent)->rendermode == TEX_GLOW && (ent)->renderamt > 0 && (ent)->renderamt <= 1) +#define ISSOLID(ent) ((ent)->rendermode == TEX_SOLID && (ent)->renderamt > 0 && (ent)->renderamt <= 1) +#define ISADDITIVE(ent) ((ent)->rendermode == TEX_ADDITIVE && (ent)->renderamt > 0 && (ent)->renderamt <= 1) + +#define ISLMPOINT(ent) ((ent)->rendermode == TEX_LMPOINT && ((ent)->rendercolor[0] <= 1|| \ + (ent)->rendercolor[1] <= 1|| \ + (ent)->rendercolor[2] <= 1)) +/* +--------------------------------- +//half-life Render Modes +--------------------------------- +*/ + +#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 + +//#ifdef SLIM +#define MAX_LIGHTMAPS 64 +//#else +//#define MAX_LIGHTMAPS 16 +//#endif + + +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 enum +{ + pm_classic, pm_qmb, pm_quake3, pm_mixed +} part_mode_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 particle2_s +{ +// driver-usable fields + vec3_t org; + float color; +// drivers never touch the following fields + struct particle2_s *next; + vec3_t vel; + float ramp; + float die; + ptype_t type; +} particle2_t; + +//==================================================== + + +extern entity_t r_worldentity; +extern qboolean 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 int skyimage[6]; // Where sky images are stored +extern int lightmap_index[MAX_LIGHTMAPS]; // Where lightmaps are stored + +extern int reloaded_pallete; + +extern qboolean envmap; +extern int currenttexture; +extern int cnttextures[2]; +extern int particletexture; +extern int playertextures; +//extern int playertextures[MAX_SCOREBOARD]; + +extern int skytexturenum; // index in cl.loadmodel, not gl texture object + +extern cvar_t scr_conheight; +extern cvar_t scr_fov; + +extern cvar_t r_partalpha; + +extern cvar_t r_maxrange; + +extern cvar_t r_restexf; +extern cvar_t r_texcompr; + +extern cvar_t r_skyfog; +extern cvar_t r_skyvflip; +extern cvar_t r_skydis; + +extern cvar_t r_caustics; +extern cvar_t r_detail; +extern cvar_t r_detail_mipmaps; +extern cvar_t r_detail_mipmaps_func; +extern cvar_t r_detail_mipmaps_bias; +extern cvar_t r_model_brightness; +extern cvar_t r_farclip; +extern cvar_t r_loadq3models; + +extern cvar_t r_i_model_animation; +extern cvar_t r_i_model_transform; +extern cvar_t r_ipolations; +extern cvar_t r_asynch; + +extern cvar_t cl_loadmapcfg; +extern cvar_t r_fastsky; +extern cvar_t r_skycolor; +extern cvar_t r_waterripple; +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 r_tex_scale_down; +extern cvar_t r_particles_simple; +extern cvar_t r_vsync; +extern cvar_t r_mipmaps; +extern cvar_t r_mipmaps_func; +extern cvar_t r_mipmaps_bias; +extern cvar_t r_retro; // dr_mabuse1981: "retro filter" (makes textures drawn with GU_NEAREST). +extern cvar_t gl_keeptjunctions; +extern cvar_t r_waterwarp; + +extern cvar_t r_showbboxes; +extern cvar_t r_showbboxes_full; + +extern cvar_t r_polyblend; + +extern cvar_t r_showtris; +extern cvar_t r_showtris_full; + +extern cvar_t r_laserpoint; +extern cvar_t r_particle_count; +extern cvar_t r_part_explosions; +extern cvar_t r_part_trails; +extern cvar_t r_part_sparks; +extern cvar_t r_part_spikes; +extern cvar_t r_part_gunshots; +extern cvar_t r_part_blood; +extern cvar_t r_part_telesplash; +extern cvar_t r_part_blobs; +extern cvar_t r_part_lavasplash; +extern cvar_t r_part_flames; +extern cvar_t r_part_lightning; +extern cvar_t r_part_flies; +extern cvar_t r_particle_count; +extern cvar_t r_bounceparticles; +extern cvar_t r_explosiontype; +extern cvar_t r_part_muzzleflash; +extern cvar_t r_flametype; +extern cvar_t r_bounceparticles; +extern cvar_t r_decal_blood; +extern cvar_t r_decal_bullets; +extern cvar_t r_decal_sparks; +extern cvar_t r_decal_explosions; +extern cvar_t r_coronas; + +// MotoLegacy - simplified cvars for decals/particles (5/27/2020) +extern cvar_t nzp_particles; +extern cvar_t nzp_decals; + + +extern int mirrortexturenum; // quake texturenum, not gltexturenum +extern qboolean mirror; +extern mplane_t *mirror_plane; + +extern ScePspFMatrix4 r_world_matrix; + +void GL_Bind (int texture_index); +void GL_BindLM (int texture_index); +void GL_Copy (int texture_index, int sx, int sy, int dx, int dy, int w, int h); + +// Added by PM +int R_LightPoint (vec3_t p); +void R_DrawBrushModel (entity_t *e); +void R_AnimateLight (void); +void R_DrawWorld (void); +void R_RenderDlights (void); +void R_DrawParticles (void); +void R_DrawWaterSurfaces (void); +void R_RenderBrushPoly (msurface_t *fa); +void R_InitParticles (void); +void R_ClearParticles (void); +void GL_BuildLightmaps (void); +void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr); +void GL_Set2D (void); +void GL_SubdivideSurface (msurface_t *fa); +void GL_Surface (msurface_t *fa); // dr_mabuse1981: fuck you lag. +void EmitWaterPolys (msurface_t *fa); +void EmitSkyPolys (msurface_t *fa); +void EmitReflectivePolys (msurface_t *fa); +void EmitScrollPolys (msurface_t *fa); +void EmitBothSkyLayers (msurface_t *fa); +void EmitUnderWaterPolys (void); +void EmitDetailPolys (void); +void R_DrawSkyChain (msurface_t *s); +qboolean R_CullBox (vec3_t emins, vec3_t emaxs); +qboolean R_CullSphere (vec3_t centre, float radius); +void R_MarkLights (dlight_t *light, int bit, mnode_t *node); +void R_RotateForEntity (entity_t *e, int shadow); +void R_BlendedRotateForEntity (entity_t *e, int shadow); +void R_RotateForViewEntity (entity_t *ent); //clone (R_RotateForEntity) +void R_RotateForTagEntity (tagentity_t *tagent, md3tag_t *tag, float *m); //for q3 models +void R_StoreEfrags (efrag_t **ppefrag); +void D_StartParticles (void); +// void D_DrawParticle (particle_t *pparticle); +void D_DrawParticle (particle2_t *pparticle, vec3_t up, vec3_t right, float scale); +void D_EndParticles (void); + +void Fog_Init (void); +void Fog_NewMap (void); + +void Sky_LoadSkyBox (char *name); +void Sky_NewMap (void); +void Sky_Init (void); +void R_ClearSkyBox (void); +void R_DrawSkyBox (void); + +//----------------------------------------------------- +void QMB_InitParticles (void); +void QMB_ClearParticles (void); +void QMB_DrawParticles (void); +void QMB_Q3TorchFlame (vec3_t org, float size); +void QMB_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count); +void QMB_RocketTrail (vec3_t start, vec3_t end, trail_type_t type); +void QMB_RayFlash (vec3_t org, float weapon); +void QMB_BlobExplosion (vec3_t org); +void QMB_ParticleExplosion (vec3_t org); +void QMB_LavaSplash (vec3_t org); +void QMB_TeleportSplash (vec3_t org); +void QMB_InfernoFlame (vec3_t org); +void QMB_StaticBubble (entity_t *ent); +void QMB_ColorMappedExplosion (vec3_t org, int colorStart, int colorLength); +void QMB_TorchFlame (vec3_t org); +void QMB_FlameGt (vec3_t org, float size, float time); +void QMB_BigTorchFlame (vec3_t org); +void QMB_ShamblerCharge (vec3_t org); +void QMB_LightningBeam (vec3_t start, vec3_t end); +//void QMB_GenSparks (vec3_t org, byte col[3], float count, float size, float life); +void QMB_EntityParticles (entity_t *ent); +void QMB_MuzzleFlash (vec3_t org); +void QMB_MuzzleFlashLG (vec3_t org); +void QMB_Q3Gunshot (vec3_t org, int skinnum, float alpha); +void QMB_Q3Teleport (vec3_t org, float alpha); +void QMB_Q3TorchFlame (vec3_t org, float size); + +extern qboolean qmb_initialized; + +void R_SpawnDecal (vec3_t center, vec3_t normal, vec3_t tangent, int tex, int size, int isbsp); +void R_SpawnDecalStatic(vec3_t org, int tex, int size); +void R_SpawnDecalBSP (vec3_t org, char *texname, int size); + +void CheckParticles (void); + +void UnloadWads (void); //By Crow_bar +#if 0 +void ShowErrorDialog(const unsigned int error); +void ShowMessageDialog(const char *message, int enableYesno); +#endif +//==================================================== + +void Fog_ParseServerMessage (void); + +typedef struct { + float s, t; + unsigned int color; + float x, y, z; +} part_vertex; + +typedef struct { + part_vertex first, second; +} psp_particle; + + +psp_particle* D_CreateBuffer (int size); +void D_DeleteBuffer (psp_particle* vertices); +int D_DrawParticleBuffered (psp_particle* vertices, particle2_t *pparticle, vec3_t up, vec3_t right, float scale); + + +extern int zombie_skins[2][2]; \ No newline at end of file diff --git a/source/psp/video_hardware_QMB.cpp b/source/psp/video_hardware_QMB.cpp new file mode 100644 index 0000000..90cd8d4 --- /dev/null +++ b/source/psp/video_hardware_QMB.cpp @@ -0,0 +1,3518 @@ +/* +Copyright (C) 2002-2003, Dr Labman, A. Nourai +Copyright (C) 2009, Crow_bar psp port + +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_rpart.c + +extern "C" +{ +#include "../quakedef.h" +//entity_t *CL_NewTempEntity (void); +} + +#include +#include + +//#define DEFAULT_NUM_PARTICLES 8192 +#define ABSOLUTE_MIN_PARTICLES 64 +#ifdef SLIM +#define ABSOLUTE_MAX_PARTICLES 12288 +#else +#define ABSOLUTE_MAX_PARTICLES 6144 +#endif +extern int decal_blood1, decal_blood2, decal_blood3, decal_q3blood, decal_burn, decal_mark, decal_glow; + +typedef byte col_t[4]; + +typedef enum +{ + p_spark, + p_rayspark, + p_raysmoke, + p_smoke, + p_fire, + p_fire2, + p_bubble, + p_lavasplash, + p_gunblast, + p_chunk, + p_shockwave, + p_inferno_flame, + p_inferno_trail, + p_sparkray, + p_staticbubble, + p_trailpart, + p_dpsmoke, + p_dpfire, + p_teleflare, + p_blood1, + p_blood2, + p_blood3, + p_bloodcloud, + p_q3blood, + p_flame, + p_lavatrail, + p_bubble2, + p_rain, + p_streak, + p_streaktrail, + p_streakwave, + p_lightningbeam, + p_glow, + p_alphatrail,//R00k + p_torch_flame, + p_flare, + p_dot, + p_muzzleflash, + p_muzzleflash2, + p_muzzleflash3, + p_muzzleflash8, + p_fly, + p_q3blood_trail, + p_q3blood_decal, + p_q3rocketsmoke, + p_q3grenadesmoke, + p_q3explosion, + p_q3flame, + p_q3gunshot, + p_q3teleport, + num_particletypes +} part_type_t; + +typedef enum +{ + pm_static, + pm_normal, + pm_bounce, + pm_die, + pm_nophysics, + pm_float, + pm_rain, + pm_streak, + pm_streakwave, +} part_move_t; + +typedef enum +{ + ptex_none, + ptex_smoke, + ptex_bubble, + ptex_generic, + ptex_dpsmoke, + ptex_lava, + ptex_blueflare, + ptex_blood1, + ptex_blood2, + ptex_blood3, + ptex_lightning, + ptex_flame, + ptex_muzzleflash, + ptex_muzzleflash2, + ptex_muzzleflash3, + ptex_muzzleflash8, + ptex_bloodcloud, + ptex_fly, + ptex_q3blood, + ptex_q3blood_trail, + ptex_q3smoke, + ptex_q3explosion, + ptex_q3flame, + num_particletextures +} part_tex_t; + +typedef enum +{ + pd_spark, + pd_sparkray, + pd_billboard, + pd_billboard_vel, + pd_hide, + pd_beam, + pd_q3flame, + pd_q3gunshot, + pd_q3teleport +} part_draw_t; + +typedef struct particle_s +{ + struct particle_s *next; + vec3_t org, endorg; + col_t color; + float growth; + vec3_t vel; + float rotangle; + float rotspeed; + float size; + float start; + float die; + byte hit; + byte texindex; + byte bounces; +} particle_t; + +typedef struct particle_type_s +{ + particle_t *start; + part_type_t id; + part_draw_t drawtype; + int SrcBlend; + int DstBlend; + part_tex_t texture; + float startalpha; + float grav; + float accel; + part_move_t move; + float custom; +} particle_type_t; + +#define MAX_PTEX_COMPONENTS 8 + +typedef struct particle_texture_s +{ + int texnum; + int components; + float coords[MAX_PTEX_COMPONENTS][4]; +} particle_texture_t; + +static float sint[7] = {0.000000, 0.781832, 0.974928, 0.433884, -0.433884, -0.974928, -0.781832}; +static float cost[7] = {1.000000, 0.623490, -0.222521, -0.900969, -0.900969, -0.222521, 0.623490}; + +static particle_t *particles, *free_particles; +static particle_type_t particle_types[num_particletypes];//R00k +static int particle_type_index[num_particletypes]; +static particle_texture_t particle_textures[num_particletextures]; + +int lightning_texture;//Vult +float varray_vertex[16];//Vult +void R_CalcBeamVerts (float *vert, vec3_t org1, vec3_t org2, float width);//Vult + +vec3_t NULLVEC = {0,0,0};//r00k + +static int r_numparticles; +static vec3_t zerodir = {22, 22, 22}; +static int particle_count = 0; +static float particle_time; +qboolean qmb_initialized = qfalse; +int particle_mode = 0; // 0: classic (default), 1: QMB, 2: mixed + +qboolean OnChange_gl_particle_count (cvar_t *var, char *string) +{ + float f; + + f = bound(ABSOLUTE_MIN_PARTICLES, (atoi(string)), ABSOLUTE_MAX_PARTICLES); + Cvar_SetValue("r_particle_count", f); + + QMB_ClearParticles (); // also re-allocc particles + + return qtrue; +} + +extern cvar_t cl_gun_offset; +#ifdef SLIM +cvar_t r_particle_count = {"r_particle_count", "4096", qtrue}; +#else +cvar_t r_particle_count = {"r_particle_count", "2048", qtrue}; +#endif +cvar_t r_bounceparticles = {"r_bounceparticles", "1",qtrue}; +cvar_t r_decal_blood = {"r_decal_blood", "1",qtrue}; +cvar_t r_decal_bullets = {"r_decal_bullets","1",qtrue}; +cvar_t r_decal_sparks = {"r_decal_sparks","1",qtrue}; +cvar_t r_decal_explosions = {"r_decal_explosions","1",qtrue}; + +int decals_enabled; + +void R_CalcBeamVerts (float *vert, vec3_t org1, vec3_t org2, float width); + +extern cvar_t sv_gravity; + +static byte *ColorForParticle (part_type_t type) +{ + static col_t color; + int lambda; + + switch (type) + { + case p_spark: + color[0] = 224 + (rand() & 31); + color[1] = 100 + (rand() & 31); + color[2] = 50; + break; + + case p_torch_flame: + case p_glow: + color[0] = color[1] = color[2] = 255; + break; + + case p_smoke: + color[0] = color[1] = color[2] = 128; + color[3] = 64; + break; + + case p_q3rocketsmoke: + case p_q3grenadesmoke: + color[0] = color[1] = color[2] = 160; + break; + + case p_q3explosion: + case p_q3flame: + case p_q3gunshot: // not used + case p_q3teleport: // not used + color[0] = color[1] = color[2] = 255; + break; + + case p_fire: + color[0] = 255; + color[1] = 122; + color[2] = 62; + break; + case p_fire2: + color[0] = 80; + color[1] = 80; + color[2] = 80; + color[3] = 64; + break; + + case p_bubble: + case p_bubble2: + case p_staticbubble: + color[0] = color[1] = color[2] = 192 + (rand() & 63); + break; + + case p_teleflare: + case p_lavasplash: + color[0] = color[1] = color[2] = 128 + (rand() & 127); + break; + + case p_gunblast: + color[0]= 224 + (rand() & 31); + color[1] = 170 + (rand() & 31); + color[2] = 0; + break; + + case p_chunk: + color[0] = color[1] = color[2] = (32 + (rand() & 127)); + break; + + case p_shockwave: + color[0] = color[1] = color[2] = 64 + (rand() & 31); + break; + + case p_inferno_flame: + case p_inferno_trail: + color[0] = 255; + color[1] = 77; + color[2] = 13; + break; + + case p_sparkray: + color[0] = 255; + color[1] = 102; + color[2] = 25; + break; + + case p_dpsmoke: + color[0] = color[1] = color[2] = 48 + (((rand() & 0xFF) * 48) >> 8); + break; + + case p_dpfire: + lambda = rand() & 0xFF; + color[0] = 160 + ((lambda * 48) >> 8); + color[1] = 16 + ((lambda * 148) >> 8); + color[2] = 16 + ((lambda * 16) >> 8); + break; + + case p_blood1: + case p_blood2: + color[0] = 30; + color[1] = 5; + color[2] = 5; + color[3] = 255; + break; + + case p_blood3: + color[0] = (50 + (rand() & 31)); + color[1] = color[2] = 0; + color[3] = 200; + break; + + case p_q3blood: + case p_q3blood_trail: + case p_q3blood_decal: + color[0] = 180; + color[1] = color[2] = 0; + break; + + case p_flame: + color[0] = 255; + color[1] = 100; + color[2] = 25; + color[3] = 128; + break; + + case p_lavatrail: + color[0] = 255; + color[1] = 102; + color[2] = 25; + color[3] = 255; + break; + + default: + //assert (!"ColorForParticle: unexpected type"); + break; + } + + return color; +} + + +#define ADD_PARTICLE_TEXTURE(_ptex, _texnum, _texindex, _components, _s1, _t1, _s2, _t2)\ +do { \ + particle_textures[_ptex].texnum = _texnum; \ + particle_textures[_ptex].components = _components; \ + particle_textures[_ptex].coords[_texindex][0] = (_s1 + 1) / max_s; \ + particle_textures[_ptex].coords[_texindex][1] = (_t1 + 1) / max_t; \ + particle_textures[_ptex].coords[_texindex][2] = (_s2 - 1) / max_s; \ + particle_textures[_ptex].coords[_texindex][3] = (_t2 - 1) / max_t; \ +} while(0) + +#define ADD_PARTICLE_TYPE(_id, _drawtype, _SrcBlend, _DstBlend, _texture, _startalpha, _grav, _accel, _move, _custom)\ +do {\ + particle_types[count].id = (_id);\ + particle_types[count].drawtype = (_drawtype);\ + particle_types[count].SrcBlend = (_SrcBlend);\ + particle_types[count].DstBlend = (_DstBlend);\ + particle_types[count].texture = (_texture);\ + particle_types[count].startalpha = (_startalpha);\ + particle_types[count].grav = 9.8 * (_grav);\ + particle_types[count].accel = (_accel);\ + particle_types[count].move = (_move);\ + particle_types[count].custom = (_custom);\ + particle_type_index[_id] = count;\ + count++;\ +} while(0) + +void QMB_AllocParticles (void) +{ + extern cvar_t r_particle_count; + + r_numparticles = bound(ABSOLUTE_MIN_PARTICLES, r_particle_count.value, ABSOLUTE_MAX_PARTICLES); + + //if (particles) + // Con_Printf("QMB_AllocParticles: internal error >particles<\n"); + + if(r_numparticles < 1) + Con_Printf("QMB_AllocParticles: internal error >num particles<\n"); + + // can't alloc on Hunk, using native memory + particles = (particle_t *) malloc (r_numparticles * sizeof(particle_t)); +} + +void QMB_InitParticles (void) +{ + int i, j, ti, count = 0, particleimage; + float max_s, max_t; //For ADD_PARTICLE_TEXTURE + + particle_mode = pm_classic; + + Cvar_RegisterVariable (&r_bounceparticles); + Cvar_RegisterVariable (&r_flametype); + Cvar_RegisterVariable (&r_decal_blood); + Cvar_RegisterVariable (&r_decal_bullets); + Cvar_RegisterVariable (&r_decal_sparks); + Cvar_RegisterVariable (&r_decal_explosions); + Cvar_RegisterVariable (&r_particle_count); + + loading_num_step = loading_num_step + 24; + + if (!(particleimage = loadtextureimage("textures/particles/particlefont", 0, 0, qfalse, GU_LINEAR))) + { + Clear_LoadingFill (); + return; + } + + loading_cur_step++; + strcpy(loading_name, "Particles"); + SCR_UpdateScreen (); + + max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_none, 0, 0, 1, 0, 0, 0, 0); + ADD_PARTICLE_TEXTURE(ptex_blood1, particleimage, 0, 1, 0, 0, 64, 64); + ADD_PARTICLE_TEXTURE(ptex_blood2, particleimage, 0, 1, 64, 0, 128, 64); + ADD_PARTICLE_TEXTURE(ptex_lava, particleimage, 0, 1, 128, 0, 192, 64); + ADD_PARTICLE_TEXTURE(ptex_blueflare, particleimage, 0, 1, 192, 0, 256, 64); + ADD_PARTICLE_TEXTURE(ptex_generic, particleimage, 0, 1, 0, 96, 96, 192); + ADD_PARTICLE_TEXTURE(ptex_smoke, particleimage, 0, 1, 96, 96, 192, 192); + ADD_PARTICLE_TEXTURE(ptex_blood3, particleimage, 0, 1, 192, 96, 256, 160); + ADD_PARTICLE_TEXTURE(ptex_bubble, particleimage, 0, 1, 192, 160, 224, 192); + + for (i=0 ; i<8 ; i++) + ADD_PARTICLE_TEXTURE(ptex_dpsmoke, particleimage, i, 8, i * 32, 64, (i + 1) * 32, 96); + + loading_cur_step++; + SCR_UpdateScreen (); + + // load the rest of the images + if(!(particleimage = loadtextureimage("textures/particles/q3particlefont", 0, 0, qfalse, GU_LINEAR))) + { + Clear_LoadingFill (); + return; + } + + max_s = 384.0; max_t = 192.0; + for (i = 0, ti = 0 ; i < 2 ; i++) + for (j = 0 ; j < 4 ; j++, ti++) + ADD_PARTICLE_TEXTURE(ptex_q3explosion, particleimage, ti, 8, j * 64, i * 64, (j + 1) * 64, (i + 1) * 64); + + + loading_cur_step++; + SCR_UpdateScreen (); + + for (i = 0 ; i < 5 ; i++) + ADD_PARTICLE_TEXTURE(ptex_q3blood, particleimage, i, 5, i * 64, 128, (i + 1) * 64, 192); + ADD_PARTICLE_TEXTURE(ptex_q3smoke, particleimage, 0, 1, 256, 0, 384, 128); + ADD_PARTICLE_TEXTURE(ptex_q3blood_trail, particleimage, 0, 1, 320, 128, 384, 192); + + loading_cur_step++; + SCR_UpdateScreen (); + + max_s = max_t = 128.0; + + if (!(particleimage = loadtextureimage("textures/particles/flame", 0, 0, qfalse, GU_LINEAR))) + { + Clear_LoadingFill (); + return; + } + + /*max_s = max_t = 128.0; + for (i = 0, ti = 0 ; i < 2 ; i++) + for (j = 0 ; j < 2 ; j++, ti++) + ADD_PARTICLE_TEXTURE(ptex_q3flame, particleimage, ti, 8, j * 64, i * 64, (j + 1) * 64, (i + 1) * 64);*/ + max_s = max_t = 64.0; + ADD_PARTICLE_TEXTURE(ptex_q3flame, particleimage, 0, 1, 0, 0, 64, 64); + loading_cur_step++; + SCR_UpdateScreen (); + + if (!(particleimage = loadtextureimage("textures/particles/inferno", 0, 0, qfalse, GU_LINEAR))) + { + Clear_LoadingFill (); + return; + } + max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_flame, particleimage, 0, 1, 0, 0, 256, 256); + + loading_cur_step++; + SCR_UpdateScreen (); + + if (!(particleimage = loadtextureimage("textures/particles/zing1", 0, 0, qfalse, GU_LINEAR))) + { + Clear_LoadingFill (); + return; + } + + max_s = 256.0; max_t = 128.0; + ADD_PARTICLE_TEXTURE(ptex_lightning, particleimage, 0, 1, 0, 0, 256, 128);//R00k changed + + loading_cur_step++; + SCR_UpdateScreen (); + max_s = max_t = 128.0; + + if (!(particleimage = loadtextureimage("textures/mzfl/mzfl0", 0, 0, qfalse, GU_LINEAR))) + { + Clear_LoadingFill (); + return; + } + //max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_muzzleflash, particleimage, 0, 1, 0, 0, 128, 128); + + loading_cur_step++; + SCR_UpdateScreen (); + + if (!(particleimage = loadtextureimage("textures/mzfl/mzfl1", 0, 0, qfalse, GU_LINEAR))) + { + Clear_LoadingFill (); + return; + } + //max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_muzzleflash2, particleimage, 0, 1, 0, 0, 128, 128); + + loading_cur_step++; + SCR_UpdateScreen (); + if (!(particleimage = loadtextureimage("textures/mzfl/mzfl2", 0, 0, qfalse, GU_LINEAR))) + { + Clear_LoadingFill (); + return; + } + //max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_muzzleflash3, particleimage, 0, 1, 0, 0, 128, 128); + + loading_cur_step++; + SCR_UpdateScreen (); + + if (!(particleimage = loadtextureimage("textures/mzfl/muzzleflash8", 0, 0, qfalse, GU_LINEAR))) + { + Clear_LoadingFill (); + return; + } + //max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_muzzleflash8, particleimage, 0, 1, 0, 0, 128, 128); + + loading_cur_step++; + SCR_UpdateScreen (); + + max_s = max_t = 64.0; + if (!(particleimage = loadtextureimage("textures/particles/bloodcloud", 0, 0, qfalse, GU_LINEAR))) + { + Clear_LoadingFill (); + return; + } + //max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_bloodcloud, particleimage, 0, 1, 0, 0, 64, 64); + + loading_cur_step++; + SCR_UpdateScreen (); +/* + if (!(particleimage = loadtextureimage("textures/particles/blood1", 0, 0, qfalse, GU_LINEAR))) + { + Clear_LoadingFill (); + return; + } + + max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_blood1, particleimage, 0, 1, 0, 0, 256, 256); + + loading_cur_step++; + SCR_UpdateScreen (); + + if (!(particleimage = loadtextureimage("textures/particles/blood2", 0, 0, qfalse, GU_LINEAR))) + { + Clear_LoadingFill (); + return; + } + max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_blood2, particleimage, 0, 1, 0, 0, 256, 256); + + loading_cur_step++; + SCR_UpdateScreen (); + +*/ + if (!(particleimage = loadtextureimage("textures/particles/fly", 0, 0, qfalse, GU_LINEAR))) + { + Clear_LoadingFill (); + return; + } + max_s = max_t = 256.0; + ADD_PARTICLE_TEXTURE(ptex_fly, particleimage, 0, 1, 0, 0, 256, 256); + + loading_cur_step++; + SCR_UpdateScreen (); + + QMB_AllocParticles (); + + ADD_PARTICLE_TYPE(p_spark, pd_spark, GU_SRC_ALPHA, GU_FIX, ptex_none, 255, -8, 0, pm_normal, 1.3); + ADD_PARTICLE_TYPE(p_gunblast, pd_spark, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_none, 255, 0, 0, pm_normal, 1.3); + ADD_PARTICLE_TYPE(p_sparkray, pd_sparkray, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_none, 255, -0, 0, pm_nophysics, 0); + ADD_PARTICLE_TYPE(p_fire, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_smoke, 204, 0, -2.95, pm_die, 0); + + loading_cur_step++; + SCR_UpdateScreen (); + + ADD_PARTICLE_TYPE(p_fire2, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_smoke, 204, 0, -2.95, pm_die, 0); + ADD_PARTICLE_TYPE(p_chunk, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 255, -16, 0, pm_bounce, 1.475); + ADD_PARTICLE_TYPE(p_shockwave, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 255, 0, -4.85, pm_nophysics, 0); + ADD_PARTICLE_TYPE(p_inferno_flame, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 153, 0, 0, pm_static, 0); + + loading_cur_step++; + SCR_UpdateScreen (); + + ADD_PARTICLE_TYPE(p_inferno_trail, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 204, 0, 0, pm_die, 0); + ADD_PARTICLE_TYPE(p_trailpart, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 230, 0, 0, pm_static, 0); + ADD_PARTICLE_TYPE(p_smoke, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_smoke, 140, 3, 0, pm_normal, 0); + ADD_PARTICLE_TYPE(p_raysmoke, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_smoke, 140, 3, 0, pm_normal, 0); + ADD_PARTICLE_TYPE(p_dpfire, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_dpsmoke, 144, 0, 0, pm_die, 0); + + loading_cur_step++; + SCR_UpdateScreen (); + + ADD_PARTICLE_TYPE(p_dpsmoke, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_dpsmoke, 85, 3, 0, pm_die, 0); + ADD_PARTICLE_TYPE(p_teleflare, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_blueflare, 255, 0, 0, pm_die, 0); + ADD_PARTICLE_TYPE(p_flare, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_bubble, 255, 0, 0, pm_die, 0); + ADD_PARTICLE_TYPE(p_fly, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_fly, 255, 0, 0, pm_die, 0); + + loading_cur_step++; + SCR_UpdateScreen(); + + ADD_PARTICLE_TYPE(p_dot, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 255, 0, 0, pm_static, 0); + ADD_PARTICLE_TYPE(p_blood1, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_COLOR, ptex_blood1, 255, -20, 0, pm_die, 0); + ADD_PARTICLE_TYPE(p_blood2, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_blood3, 255, -45, 0, pm_normal, 0.018);//disisgonnabethegibchunks + ADD_PARTICLE_TYPE(p_blood3, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_blood3, 255, -30, 0, pm_normal, 0); + + loading_cur_step++; + SCR_UpdateScreen(); + + ADD_PARTICLE_TYPE(p_lavasplash, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_lava, 170, 0, 0, pm_nophysics, 0); + ADD_PARTICLE_TYPE(p_bubble, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_bubble, 204, 8, 0, pm_float, 0); + ADD_PARTICLE_TYPE(p_staticbubble, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_bubble, 204, 0, 0, pm_static, 0); + ADD_PARTICLE_TYPE(p_flame, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 200, 10, 0, pm_die, 0); + + loading_cur_step++; + SCR_UpdateScreen(); + + ADD_PARTICLE_TYPE(p_lavatrail, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_dpsmoke, 255, 3, 0, pm_normal, 0);//R00k + ADD_PARTICLE_TYPE(p_bubble2, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_bubble, 204, 1, 0, pm_float, 0); + ADD_PARTICLE_TYPE(p_glow, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 204, 0, 0, pm_die, 0); + ADD_PARTICLE_TYPE(p_alphatrail, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 100, 0, 0, pm_static, 0); + + loading_cur_step++; + SCR_UpdateScreen(); + + ADD_PARTICLE_TYPE(p_torch_flame, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_flame, 255, 12, 0, pm_die, 0); + ADD_PARTICLE_TYPE(p_streak, pd_hide, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_none, 255, -64, 0, pm_streak, 1.5); + ADD_PARTICLE_TYPE(p_streakwave, pd_hide, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_none, 255, 0, 0, pm_streakwave, 0); + ADD_PARTICLE_TYPE(p_streaktrail, pd_beam, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_none, 255, 0, 0, pm_die, 0); + + loading_cur_step++; + SCR_UpdateScreen(); + + ADD_PARTICLE_TYPE(p_lightningbeam, pd_beam, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_lightning, 255, 0, 0, pm_die, 0); + ADD_PARTICLE_TYPE(p_muzzleflash, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_muzzleflash, 255, 0, 0, pm_static, 0); + ADD_PARTICLE_TYPE(p_muzzleflash2, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_muzzleflash2, 255, 0, 0, pm_static, 0); + ADD_PARTICLE_TYPE(p_muzzleflash3, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_muzzleflash3, 255, 0, 0, pm_static, 0); + ADD_PARTICLE_TYPE(p_muzzleflash8, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_muzzleflash8, 220, 0, 0, pm_static, 0); + ADD_PARTICLE_TYPE(p_rain, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_generic, 255, -16, 0, pm_rain, 0); + + loading_cur_step++; + SCR_UpdateScreen(); + + //shpuldeditedthis(GI_ONE_MINUS_DST_ALPHA->GL_ONE_MINUS_SRC_ALPHA) (edited one right after this comment) + ADD_PARTICLE_TYPE(p_bloodcloud, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_bloodcloud, 255, -2, 0, pm_normal, 0); + ADD_PARTICLE_TYPE(p_q3blood, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_q3blood, 255, 0, 0, pm_static, -1); + ADD_PARTICLE_TYPE(p_q3blood_trail, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_q3blood_trail, 255, -1.5, 0, pm_die, -1); + ADD_PARTICLE_TYPE(p_q3rocketsmoke, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_q3smoke, 80, 0, 0, pm_die, 0); + + loading_cur_step++; + SCR_UpdateScreen(); + + ADD_PARTICLE_TYPE(p_q3grenadesmoke, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, ptex_q3smoke, 80, 0, 0, pm_die, 0); + ADD_PARTICLE_TYPE(p_q3explosion, pd_billboard, GU_SRC_ALPHA, GU_ONE_MINUS_DST_ALPHA, ptex_q3explosion, 204, 0, 0, pm_static, -1); + //old: ADD_PARTICLE_TYPE(p_q3flame, pd_q3flame, GU_SRC_ALPHA, GU_ONE_MINUS_DST_ALPHA, ptex_q3flame, 204, 0, 0, pm_static, -1); + ADD_PARTICLE_TYPE(p_q3flame, pd_billboard, GU_SRC_ALPHA, GU_FIX, ptex_q3flame, 180, 0.66, 0, pm_nophysics, 0); + ADD_PARTICLE_TYPE(p_q3gunshot, pd_q3gunshot, GU_SRC_ALPHA, GU_ONE_MINUS_DST_ALPHA, ptex_none, 255, 0, 0, pm_static, -1); + ADD_PARTICLE_TYPE(p_q3teleport, pd_q3teleport, GU_SRC_ALPHA, GU_ONE_MINUS_DST_ALPHA, ptex_none, 255, 0, 0, pm_static, -1); + + loading_cur_step++; + strcpy(loading_name, "particles"); + SCR_UpdateScreen (); + + Clear_LoadingFill (); + + qmb_initialized = qtrue; +} + +#define INIT_NEW_PARTICLE(_pt, _p, _color, _size, _time) \ + _p = free_particles; \ + free_particles = _p->next; \ + _p->next = _pt->start; \ + _pt->start = _p; \ + _p->size = _size; \ + _p->hit = 0; \ + _p->start = cl.time; \ + _p->die = _p->start + _time; \ + _p->growth = 0; \ + _p->rotspeed = 0; \ + _p->texindex = (rand() % particle_textures[_pt->texture].components); \ + _p->bounces = 0; \ + VectorCopy(_color, _p->color); + +__inline static void AddParticle (part_type_t type, vec3_t org, int count, float size, float time, col_t col, vec3_t dir) +{ + byte *color; + int i, j, k; + float tempSize; //stage; + particle_t *p; + particle_type_t *pt; + static unsigned long q3blood_texindex = 0; + + if (!qmb_initialized) + Sys_Error ("QMB particle added without initialization"); + + //assert (size > 0 && time > 0); + + if (type < 0 || type >= num_particletypes) + Sys_Error ("AddParticle: Invalid type (%d)", type); + + pt = &particle_types[particle_type_index[type]]; + + for (i=0 ; i < count && free_particles ; i++) + { + color = col ? col : ColorForParticle (type); + + INIT_NEW_PARTICLE(pt, p, color, size, time); + + switch (type) + { + case p_spark: + p->size = 1.175; + VectorCopy (org, p->org); + tempSize = size * 2; + p->vel[0] = (rand() % (int)tempSize) - ((int)tempSize / 2); + p->vel[1] = (rand() % (int)tempSize) - ((int)tempSize / 2); + p->vel[2] = (rand() % (int)tempSize) - ((int)tempSize / 2); + break; + case p_rayspark: + p->size = 1.175; + VectorCopy (org, p->org); + tempSize = size * 2; + p->vel[0] = (rand() % (int)tempSize) - ((int)tempSize/6); + p->vel[1] = (rand() % (int)tempSize) - ((int)tempSize/6); + p->vel[2] = /*(rand() % (int)tempSize) - (*/(int)tempSize; + break; + case p_raysmoke: + for (j=0 ; j<3 ; j++) + p->org[j] = org[j] + ((rand() & 31) - 16) / 2.0; + + p->vel[0] = ((rand() % 10)+2); + p->vel[1] = ((rand() % 10)+2); + p->vel[2] = ((rand() % 10)+2)*5; + p->growth = 7.5; + break; + case p_smoke: + for (j=0 ; j<3 ; j++) + p->org[j] = org[j] + ((rand() & 31) - 16) / 2.0; + for (j=0 ; j<3 ; j++) + p->vel[j] = ((rand() % 10) - 5) / 20.0; + p->growth = 4.5; + break; + case p_fire: + VectorCopy (org, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() & 159) - 80; + p->org[0] = org[0] + ((rand() & 63) - 32); + p->org[1] = org[1] + ((rand() & 63) - 32); + p->org[2] = org[2] + ((rand() & 63) - 10); + break; + + case p_fire2: + VectorCopy (org, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() & 199) - 100; + p->org[0] = org[0] + ((rand() & 99) - 50); + p->org[1] = org[1] + ((rand() & 99) - 50); + p->org[2] = org[2] + ((rand() & 99) - 18); + p->growth = 12; + break; + + case p_bubble: + p->start += (rand() & 15) / 36.0; + p->org[0] = org[0] + ((rand() & 31) - 16); + p->org[1] = org[1] + ((rand() & 31) - 16); + p->org[2] = org[2] + ((rand() & 63) - 32); + VectorClear (p->vel); + break; + + case p_lavasplash: + case p_streak: + case p_streakwave: + case p_shockwave: + case p_q3teleport: + VectorCopy (org, p->org); + VectorCopy (dir, p->vel); + break; + + case p_gunblast: + p->size = 1; + VectorCopy (org, p->org); + p->vel[0] = (rand() & 159) - 80; + p->vel[1] = (rand() & 159) - 80; + p->vel[2] = (rand() & 159) - 80; + break; + + case p_chunk: + VectorCopy (org, p->org); + p->vel[0] = (rand() % 40) - 20; + p->vel[1] = (rand() % 40) - 20; + p->vel[2] = (rand() % 40) - 5; + break; + + case p_rain: + VectorCopy(org, p->org); + p->vel[0] = (rand() % 180) - 90; + p->vel[1] = (rand() % 180) - 90; + p->vel[2] = (rand() % -100 - 1200); + break; + + case p_inferno_trail: + for (j=0 ; j<3 ; j++) + p->org[j] = org[j] + (rand() & 15) - 8; + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() & 3) - 2; + p->growth = -1.5; + break; + + case p_inferno_flame: + VectorCopy (org, p->org); + VectorClear (p->vel); + p->growth = -30; + break; + + case p_q3explosion: + p->texindex = 0; + VectorCopy (org, p->org); + VectorClear (p->vel); + p->growth = 50; + for (j=1 ; j<8 ; j++) + { + INIT_NEW_PARTICLE(pt, p, color, size, time); + p->size = size + j * 2; + p->start = cl.time + (j * time / 2.0); + p->die = p->start + time; + p->texindex = j; + VectorCopy (org, p->org); + VectorClear (p->vel); + p->growth = 50; + } + break; + + case p_sparkray: + VectorCopy (org, p->endorg); + VectorCopy (dir, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() & 127) - 64; + p->growth = -16; + break; + + case p_bloodcloud: //shpuld + VectorCopy (org, p->org); + p->vel[0] = (rand() & 39) - 20; + p->vel[1] = (rand() & 39) - 20; + p->vel[2] = (rand() & 39) - 20; + p->growth = 24; + break; + + case p_staticbubble: + VectorCopy (org, p->org); + VectorClear (p->vel); + break; + + case p_muzzleflash: + case p_muzzleflash2: + case p_muzzleflash3: + case p_muzzleflash8: + VectorCopy (org, p->org); + p->rotspeed = (rand() & 45) - 90; + //p->size = size * (rand() % 6) / 4;//r00k + p->size = size * (0.75 +((0.05 * (rand() % 20)) * 0.5));//blubs: resultant size range: [size * 0.75, size * 1.25) + break; + + case p_teleflare: + case p_flare: + case p_fly: + VectorCopy (org, p->org); + VectorCopy (dir, p->vel); + p->growth = 1.75; + break; + + case p_blood1: + p->size = size * (rand() % 2) + 0.50;//r00k + for (j=0 ; j<3 ; j++) + p->org[j] = org[j] + (rand() & 15) - 8; + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() & 63) - 32; + break; + + case p_blood2: //shpuld + VectorCopy (org, p->org); + p->vel[0] = (rand() & 200) - 100; + p->vel[1] = (rand() & 200) - 100; + p->vel[2] = (rand() & 250) - 70; + //p->growth = 24; + break; + + case p_blood3: + p->size = size * (rand() % 20) / 5.0; + VectorCopy (org, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() % 40) - 20; + break; + + case p_q3blood: + p->texindex = q3blood_texindex++ % 5; + for (k=0 ; k<3 ; k++) + p->org[k] = org[k] + (rand() & 15) - 8; + VectorClear (p->vel); + for (j=1 ; j<3 ; j++) + { + INIT_NEW_PARTICLE(pt, p, color, size, time); + p->start = cl.time + (j * time); + p->die = p->start + time; + p->texindex = q3blood_texindex++ % 5; + for (k=0 ; k<3 ; k++) + p->org[k] = org[k] + (rand() & 15) - 8; + VectorClear (p->vel); + } + break; + + case p_flame: + VectorCopy (org, p->org); + p->growth = -p->size / 2; + VectorClear (p->vel); + for (j=0 ; j<2 ; j++) + p->vel[j] = (rand() % 6) - 3; + break; + + case p_q3flame: //shpuld + VectorCopy (org, p->org); + p->vel[0] = (rand() & 3) - 2; + p->vel[1] = (rand() & 3) - 2; + p->vel[2] = (rand() & 2); + p->growth = 6; + break; + + case p_q3gunshot: + p->texindex = 0; // used for animation here + VectorCopy (org, p->org); + for (j=1 ; j<3 ; j++) + { + INIT_NEW_PARTICLE(pt, p, color, size, time); + p->start = cl.time + (j * time / 2.0); + p->die = p->start + time; + p->texindex = j + 1; + VectorCopy (org, p->org); + } + break; + + case p_torch_flame: + for (j=0 ; j<3 ; j++) + p->org[j] = org[j] + (rand() & 3) - 2; + p->vel[0] = rand() % 15 - 8; + p->vel[1] = rand() % 15 - 8; + p->vel[2] = rand() % 15; + p->rotspeed = (rand() & 31) + 32; + break; + + case p_dot: + case p_glow: + VectorCopy (org, p->org); + VectorCopy (dir, p->vel); + p->growth = -1.5; + break; + + case p_streaktrail: + case p_lightningbeam: + VectorCopy(org, p->org); + VectorCopy(dir, p->endorg); + VectorClear(p->vel); + p->growth = -p->size/time; + p->bounces = color[3]; + break; + + default: + //assert (!"AddParticle: unexpected type"); + break; + } + } +} + +__inline static void AddParticleTrail (part_type_t type, vec3_t start, vec3_t end, float size, float time, col_t col) +{ + byte *color; + int i, j, num_particles; + float count, length; + vec3_t point, delta; + particle_t *p; + particle_type_t *pt; + static float rotangle = 0; + count = 0; + + if (!qmb_initialized) + Sys_Error ("QMB particle added without initialization"); + + //assert (size > 0 && time > 0); + + if (type < 0 || type >= num_particletypes) + Sys_Error ("AddParticle: Invalid type (%d)", type); + + pt = &particle_types[particle_type_index[type]]; + + VectorCopy(start, point); + VectorSubtract(end, start, delta); + if (!(length = VectorLength(delta))) + return; + + switch (type) + { + + case p_q3blood_trail: + case p_q3rocketsmoke: + count = length / 40.0; + break; + + case p_q3grenadesmoke: + count = length / 12.0; + break; + + case p_alphatrail: + case p_trailpart: + case p_lavatrail: + count = length / 1.1; + break; + + case p_blood3: + count = length / 8; + break; + + case p_bubble: + case p_bubble2: + count = length / 5.0; + break; + + case p_smoke: + count = length / 3.8; + break; + + case p_dpsmoke: + count = length / 2.5; + break; + + case p_dpfire: + count = length / 2.8; + break; + + default: + //assert (!"AddParticleTrail: unexpected type"); + break; + } + + if (!(num_particles = (int)count)) + num_particles = 1; + + VectorScale(delta, 1.0 / num_particles, delta); + + for (i=0 ; i < num_particles && free_particles ; i++) + { + color = col ? col : ColorForParticle (type); + INIT_NEW_PARTICLE(pt, p, color, size, time); + + switch (type) + { + case p_alphatrail: + case p_trailpart: + VectorCopy (point, p->org); + VectorClear (p->vel); + p->growth = -size / time; + break; + + case p_blood3: + VectorCopy (point, p->org); + for (j=0 ; j<3 ; j++) + p->org[j] += ((rand() & 15) - 8) / 8.0; + for (j=0 ; j<3 ; j++) + p->vel[j] = ((rand() & 15) - 8) / 2.0; + p->size = size * (rand() % 20) / 10.0; + p->growth = 6; + break; + + case p_q3blood_trail: + VectorCopy (point, p->org); + for (j=0 ; j<3 ; j++) + p->org[j] += ((rand() & 15) - 8) / 8.0; + for (j=0 ; j<3 ; j++) + p->vel[j] = ((rand() & 15) - 8) / 2.0; + p->growth = 6; + break; + + case p_bubble2: + VectorCopy(point, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() % 10) - 5; + break; + + //R00k added + case p_bubble: + VectorCopy (point, p->org); + + for (j=0 ; j<3 ; j++) + p->org[j] += ((rand() & 15) - 8) / 8.0; + + for (j=0 ; j<3 ; j++) + p->vel[j] = ((rand() & 15) - 8) / 2.0; + + p->size = size * (rand() % 20) / 10.0; + p->growth = 1; + break; + + case p_smoke: + VectorCopy (point, p->org); + for (j=0 ; j<3 ; j++) + p->org[j] += ((rand() & 7) - 4) / 8.0; + p->vel[0] = p->vel[1] = 0; + p->vel[2] = rand() & 3; + p->growth = 4.5; + p->rotspeed = (rand() & 63) + 96; + break; + + case p_q3rocketsmoke: + case p_q3grenadesmoke: + VectorCopy (point, p->org); + for (j=0 ; j<3 ; j++) + p->org[j] += ((rand() & 7) - 4) / 8.0; + VectorClear (p->vel); + p->growth = 30; + if (rotangle >= 360) + rotangle = 0; + p->rotangle = rotangle; + rotangle += 30; + break; + + case p_dpsmoke: + VectorCopy (point, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() % 10) - 5; + p->growth = 3; + p->rotspeed = (rand() & 63) + 96; + break; + + case p_dpfire: + VectorCopy (point, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = (rand() % 40) - 20; + break; + + case p_lavatrail: + VectorCopy (point, p->org); + for (j=0 ; j<3 ; j++) + p->org[j] += ((rand() & 7) - 4); + p->vel[0] = p->vel[1] = 0; + p->vel[2] = rand() & 3; + break; + + default: + //assert (!"AddParticleTrail: unexpected type"); + break; + } + + VectorAdd(point, delta, point); + } +} + +void QMB_ClearParticles (void) +{ + int i; + + if (!qmb_initialized) + return; + + free (particles); // free + QMB_AllocParticles (); // and alloc again + particle_count = 0; + memset (particles, 0, r_numparticles * sizeof(particle_t)); + free_particles = &particles[0]; + + for (i=0 ; i+1 < r_numparticles ; i++) + particles[i].next = &particles[i + 1]; + particles[r_numparticles-1].next = NULL; + + for (i=0 ; i < num_particletypes ; i++) + particle_types[i].start = NULL; +} + +inline static void QMB_UpdateParticles(void) +{ + int i, c; + float grav, bounce, frametime, distance[3]; + vec3_t oldorg, stop, normal; + particle_type_t *pt; + particle_t *p, *kill; + + if (!qmb_initialized) + return; + + particle_count = 0; + frametime = fabs(cl.ctime - cl.oldtime); + grav = sv_gravity.value / 800.0; + + for (i=0 ; istart) + { + p = pt->start; + while (p && p->next) + { + kill = p->next; + if (kill->die <= particle_time) + { + p->next = kill->next; + kill->next = free_particles; + free_particles = kill; + } + else + { + p = p->next; + } + } + if (pt->start->die <= particle_time) + { + kill = pt->start; + pt->start = kill->next; + kill->next = free_particles; + free_particles = kill; + } + } + + for (p = pt->start ; (p); p = p->next) + { + if (particle_time < p->start) + continue; + + particle_count++; + + p->size += p->growth * frametime; + + if (p->size <= 0) + { + p->die = 0; + continue; + } + VectorCopy (p->org, oldorg); + VectorSubtract(r_refdef.vieworg,oldorg, distance); + + if (VectorLength(distance) >= r_farclip.value) + p->die = 0; + + switch (pt->id) + { + + case p_q3blood: // avoid alpha for q3blood + p->color[3] = 255; + break; + + case p_q3explosion: + case p_q3gunshot: + if (particle_time < (p->start + (p->die - p->start) / 2.0)) + { + if (pt->id == p_q3gunshot && !p->texindex) + p->color[3] = 255; + else + p->color[3] = pt->startalpha * ((particle_time - p->start) / (p->die - p->start) * 2); + } + else + { + p->color[3] = pt->startalpha * ((p->die - particle_time) / (p->die - p->start) * 2); + } + break; + case p_streaktrail://R00k + case p_lightningbeam: + p->color[3] = p->bounces * ((p->die - particle_time) / (p->die - p->start)); + break; + + //shpuld + case p_q3flame: + p->color[3] = pt->startalpha * ((p->die - particle_time) / (p->die - p->start)); + p->color[0] = p->color[1] = p->color[2] = pt->startalpha * ((p->die - particle_time) / (p->die - p->start)); + break; + + default: + p->color[3] = pt->startalpha * ((p->die - particle_time) / (p->die - p->start)); + break; + } + + p->rotangle += p->rotspeed * frametime; + + if (p->hit) + continue; + + p->vel[2] += pt->grav * grav * frametime; + + VectorScale (p->vel, 1 + pt->accel * frametime, p->vel); + + switch (pt->move) + { + case pm_static: + break; + + case pm_normal: + VectorCopy (p->org, oldorg); + VectorMA (p->org, frametime, p->vel, p->org); + + if (CONTENTS_SOLID == TruePointContents(p->org)) + { + p->hit = 1; + + if ((pt->id == p_blood3)&&(r_decal_blood.value) && (decals_enabled) && (particle_mode)) + { + TraceLineN(oldorg, p->org, stop, normal); + + if ((stop != p->org)&&(VectorLength(stop)!=0)) + { + vec3_t tangent; + VectorCopy(stop, p->org); + VectorCopy(normal, p->vel); + CrossProduct(normal,p->vel,tangent); + R_SpawnDecal(p->org, normal, tangent, decal_blood3, 12, 0); + } + p->die = 0; + } + VectorCopy (oldorg, p->org); + VectorClear (p->vel); + } + + break; + + case pm_float: + VectorMA (p->org, frametime, p->vel, p->org); + p->org[2] += p->size + 1; + if (!ISUNDERWATER(TruePointContents(p->org))) + p->die = 0; + p->org[2] -= p->size + 1; + break; + + case pm_nophysics: + VectorMA (p->org, frametime, p->vel, p->org); + break; + + case pm_die: + VectorCopy (p->org, oldorg); + VectorMA (p->org, frametime, p->vel, p->org); + + if (CONTENTS_SOLID == TruePointContents(p->org)) + { + if ((decals_enabled) && (particle_mode)) + { + TraceLineN(oldorg, p->org, stop, normal); + + if ((stop != p->org)&&(VectorLength(stop)!=0)) + { + vec3_t tangent; + + VectorCopy(stop, p->org); + VectorCopy(normal, p->vel); + CrossProduct(normal,p->vel,tangent); +/* + if ((pt->id == p_blood1)&&(r_decal_blood.value)) + { + R_SpawnDecal(p->org, normal, tangent, decal_blood1, 12, 0); + } + else + { + if ((pt->id == p_blood2)&&(r_decal_blood.value)) + { + R_SpawnDecal(p->org, normal, tangent, decal_blood2, 12, 0); + } + } +*/ + if ((pt->id == p_fire || pt->id == p_dpfire) && r_decal_explosions.value) + R_SpawnDecal (p->org, normal, tangent, decal_burn, 32, 0); + else if (pt->id == p_blood1 && r_decal_blood.value) + R_SpawnDecal (p->org, normal, tangent, decal_blood1, 12, 0); + else if (pt->id == p_blood2 && r_decal_blood.value) + R_SpawnDecal (p->org, normal, tangent, decal_blood2, 12, 0); + else if (pt->id == p_q3blood_trail && r_decal_blood.value) + R_SpawnDecal (p->org, normal, tangent, decal_q3blood, 48, 0); + + } + } + VectorCopy (oldorg, p->org); + VectorClear (p->vel);//R00k added Needed? + p->die = 0; + } + + break; + + case pm_bounce: + if (!r_bounceparticles.value || p->bounces) + { + VectorMA(p->org, frametime, p->vel, p->org); + if (CONTENTS_SOLID == TruePointContents(p->org)) + { + p->die = 0; + } + } + else + { + VectorCopy (p->org, oldorg); + VectorMA (p->org, frametime, p->vel, p->org); + + if (CONTENTS_SOLID == TruePointContents(p->org)) + { + if (TraceLineN(oldorg, p->org, stop, normal)) + { + VectorCopy (stop, p->org); + bounce = -pt->custom * DotProduct(p->vel, normal); + VectorMA(p->vel, bounce, normal, p->vel); + p->bounces++; + } + } + + } + break; + + case pm_streak: + VectorCopy(p->org, oldorg); + VectorMA(p->org, frametime, p->vel, p->org); + if (CONTENTS_SOLID == TruePointContents(p->org)) + { + if (TraceLineN(oldorg, p->org, stop, normal)) + { + VectorCopy(stop, p->org); + bounce = -pt->custom * DotProduct(p->vel, normal); + VectorMA(p->vel, bounce, normal, p->vel); + } + } + + AddParticle (p_streaktrail, oldorg, 1, p->size, 0.2, p->color, p->org); + + if (!VectorLength(p->vel)) + p->die = 0; + break; + + case pm_rain: + VectorCopy(p->org, oldorg); + VectorMA(p->org, frametime, p->vel, p->org); + + VectorSubtract(r_refdef.vieworg,oldorg, distance); + + if (VectorLength(distance) < r_farclip.value) + { + if ((rand()%10+1 > 6)) + AddParticle (p_streaktrail, oldorg, 1, ((rand() % 1) + 0.5), 0.2, p->color, p->org); + + c = TruePointContents(p->org); + + if ((CONTENTS_SOLID == c) || (ISUNDERWATER(c))) + { + VectorClear (p->vel); + p->die = 0; + } + } + break; + + case pm_streakwave: + VectorCopy(p->org, oldorg); + VectorMA(p->org, frametime, p->vel, p->org); + AddParticle (p_streaktrail, oldorg, 1, p->size, 0.5, p->color, p->org); + p->vel[0] = 19 * p->vel[0] / 20; + p->vel[1] = 19 * p->vel[1] / 20; + p->vel[2] = 19 * p->vel[2] / 20; + break; + + default: + //assert (!"QMB_UpdateParticles: unexpected pt->move"); + break; + } + } + } +} + +//from darkplaces engine - finds which corner of a particle goes where, so I don't have to :D +void R_CalcBeamVerts (float *vert, vec3_t org1, vec3_t org2, float width) +{ + vec3_t right1, right2, diff, normal; + + VectorSubtract (org2, org1, normal); + VectorNormalize (normal); + + //width = width / 2; + // calculate 'right' vector for start + VectorSubtract (r_origin, org1, diff); + VectorNormalize (diff); + CrossProduct (normal, diff, right1); + + // calculate 'right' vector for end + VectorSubtract (r_origin, org2, diff); + VectorNormalize (diff); + CrossProduct (normal, diff, right2); + + vert[ 0] = org1[0] + width * right1[0]; + vert[ 1] = org1[1] + width * right1[1]; + vert[ 2] = org1[2] + width * right1[2]; + vert[ 4] = org1[0] - width * right1[0]; + vert[ 5] = org1[1] - width * right1[1]; + vert[ 6] = org1[2] - width * right1[2]; + vert[ 8] = org2[0] - width * right2[0]; + vert[ 9] = org2[1] - width * right2[1]; + vert[10] = org2[2] - width * right2[2]; + vert[12] = org2[0] + width * right2[0]; + vert[13] = org2[1] + width * right2[1]; + vert[14] = org2[2] + width * right2[2]; +} + +#define DRAW_PARTICLE_BILLBOARD(_ptex, _p, _coord) \ + sceGumPushMatrix (); \ + \ + const ScePspFVector3 translation = \ + { \ + _p->org[0], _p->org[1], _p->org[2] \ + }; \ + sceGumTranslate(&translation); \ + \ + const ScePspFVector3 scale = \ + { \ + _p->size, _p->size, _p->size \ + }; \ + sceGumScale(&scale); \ + \ + if (_p->rotspeed || pt->id == p_q3rocketsmoke || pt->id == p_q3grenadesmoke)\ + { \ + const ScePspFVector3 rotation = \ + { \ + vpn[0] * (GU_PI / 180.0f), \ + vpn[1] * (GU_PI / 180.0f), \ + vpn[2] * (GU_PI / 180.0f) \ + }; \ + sceGumRotateZYX(&rotation); \ + } \ + sceGuColor(GU_RGBA(_p->color[0], _p->color[1], _p->color[2], _p->color[3]));\ + \ + sceGumUpdateMatrix(); \ + \ + struct vertex \ + { \ + float u, v; \ + float x, y, z; \ + }; \ + vertex* const df = static_cast(sceGuGetMemory(sizeof(vertex) * 4));\ + \ + df[0].u = _ptex->coords[_p->texindex][0]; df[0].v = _ptex->coords[_p->texindex][3];\ + df[0].x = _coord[0][0]; df[0].y = _coord[0][1]; df[0].z = _coord[0][2]; \ + df[1].u = _ptex->coords[_p->texindex][0]; df[1].v = _ptex->coords[_p->texindex][1];\ + df[1].x = _coord[1][0]; df[1].y = _coord[1][1]; df[1].z = _coord[1][2]; \ + df[2].u = _ptex->coords[_p->texindex][2]; df[2].v = _ptex->coords[_p->texindex][1];\ + df[2].x = _coord[2][0]; df[2].y = _coord[2][1]; df[2].z = _coord[2][2]; \ + df[3].u = _ptex->coords[_p->texindex][2]; df[3].v = _ptex->coords[_p->texindex][3];\ + df[3].x = _coord[3][0]; df[3].y = _coord[3][1]; df[3].z = _coord[3][2]; \ + sceGuDrawArray(GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, 4, 0, df); \ + sceGuColor(GU_RGBA(0xff,0xff,0xff,0xff)); \ + \ + sceGumPopMatrix (); \ + sceGumUpdateMatrix(); + +//extern ScePspFMatrix4 r_world_matrix[16]; +void QMB_DrawParticles (void) +{ + int j, i; + vec3_t up, right, billboard[4], velcoord[4], neworg; + particle_t *p; + particle_type_t *pt; + particle_texture_t *ptex; + + float varray_vertex[16]; + vec3_t distance; + + if (!qmb_initialized) + return; + + particle_time = cl.time; + + if (!cl.paused) + QMB_UpdateParticles (); + + VectorAdd (vup, vright, billboard[2]); + VectorSubtract (vright, vup, billboard[3]); + VectorNegate (billboard[2], billboard[0]); + VectorNegate (billboard[3], billboard[1]); + + sceGuDepthMask (GU_TRUE); + sceGuEnable (GU_BLEND); + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); + sceGuShadeModel (GU_SMOOTH); + + for (i = 0 ; i < num_particletypes ; i++) + { + pt = &particle_types[i]; + + if (!pt->start) + continue; + + sceGuBlendFunc (GU_ADD, pt->SrcBlend, pt->DstBlend, 0, 0xFFFFFFFF); //edited fixed value to 1 from 0 - shpuld + + switch (pt->drawtype) + { + case pd_hide: + break; + case pd_beam: + ptex = &particle_textures[pt->texture]; + GL_Bind (ptex->texnum); + for (p = pt->start ; p ; p = p->next) + { + if (particle_time < p->start || particle_time >= p->die) + continue; + + VectorSubtract(r_refdef.vieworg, p->org, distance); + if (VectorLength(distance) > r_farclip.value) + continue; + // Allocate the vertices. + struct vertex + { + float u, v; + float x, y, z; + }; + + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * 4)); + + sceGuColor(GU_RGBA(p->color[0], p->color[1], p->color[2], p->color[3])); + R_CalcBeamVerts (varray_vertex, p->org, p->endorg, p->size / 3.0); + + out[0].u = 1; + out[0].v = 0; + + out[0].x = varray_vertex[0]; + out[0].y = varray_vertex[1]; + out[0].z = varray_vertex[2]; + + out[1].u = 1; + out[1].v = 1; + + out[1].x = varray_vertex[4]; + out[1].y = varray_vertex[5]; + out[1].z = varray_vertex[6]; + + out[2].u = 0; + out[2].v = 1; + + out[2].x = varray_vertex[8]; + out[2].y = varray_vertex[9]; + out[2].z = varray_vertex[10]; + + out[3].u = 0; + out[3].v = 0; + + out[3].x = varray_vertex[12]; + out[3].y = varray_vertex[13]; + out[3].z = varray_vertex[14]; + + sceGuDrawArray(GU_TRIANGLE_FAN,GU_TEXTURE_32BITF | GU_VERTEX_32BITF,4, 0, out); + sceGuColor(GU_RGBA(0xff,0xff,0xff,0xff)); //return to normal color + } + break; + case pd_spark: + sceGuDisable (GU_TEXTURE_2D); + for (p = pt->start ; p ; p = p->next) + { + if (particle_time < p->start || particle_time >= p->die) + continue; + + VectorSubtract(r_refdef.vieworg, p->org, distance); + if (VectorLength(distance) > r_farclip.value) + continue; + + struct vertex + { + vec3_t xyz; + }; + + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * 9)); + + sceGuColor(GU_RGBA(p->color[0], p->color[1], p->color[2], p->color[3])); + + for (int gh=0 ; gh<3 ; gh++) + out[0].xyz[gh] = p->org[gh]; + + sceGuColor(GU_RGBA(p->color[0] >> 1, p->color[1] >> 1, p->color[2] >> 1, p->color[3] >> 1)); + + int vt = 1; + + for (j=7; j>=0 ; j--) + { + + for (int k=0 ; k<3 ; k++) + out[vt].xyz[k] = p->org[k] - p->vel[k] / 8 + vright[k] * cost[1%7] * p->size + vup[k] * sint[j%7] * p->size; + vt = vt + 1; + } + sceGuDrawArray(GU_TRIANGLE_FAN, GU_VERTEX_32BITF, 9, 0, out); + + sceGuColor(GU_RGBA(0xff,0xff,0xff,0xff)); //return to normal color + } + sceGuEnable (GU_TEXTURE_2D); + break; + case pd_sparkray: + sceGuDisable (GU_TEXTURE_2D); + for (p = pt->start ; p ; p = p->next) + { + if (particle_time < p->start || particle_time >= p->die) + continue; + + VectorSubtract(r_refdef.vieworg, p->org, distance); + if (VectorLength(distance) > r_farclip.value) + continue; + + if (!TraceLineN(p->endorg, p->org, neworg, NULLVEC)) + VectorCopy(p->org, neworg); + + //R00k added -start- + //sceGuEnable (GU_BLEND); + //p->color[3] = bound(0, 0.3, 1) * 255; + //R00k added -end- + //glColor4ubv (p->color); + + struct vertex + { + vec3_t xyz; + }; + + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * 9)); + + sceGuColor(GU_RGBA(p->color[0], p->color[1], p->color[2], p->color[3])); + + for (int gh=0 ; gh<3 ; gh++) + out[0].xyz[gh] = p->endorg[gh]; + + + sceGuColor(GU_RGBA(p->color[0] >> 1, p->color[1] >> 1, p->color[2] >> 1, p->color[3] >> 1)); + + int vt = 1; + + for (j=7 ; j>=0 ; j--) + { + for (int k=0 ; k<3 ; k++) + out[vt].xyz[k] = neworg[k] + vright[k] * cost[j%7] * p->size + vup[k] * sint[j%7] * p->size; + + vt = vt + 1; + } + sceGuDrawArray(GU_TRIANGLE_FAN, GU_VERTEX_32BITF, 9, 0, out); + + sceGuColor(GU_RGBA(0xff,0xff,0xff,0xff)); //return to normal color + + } + sceGuEnable (GU_TEXTURE_2D); + break; + + case pd_billboard: + ptex = &particle_textures[pt->texture]; + GL_Bind (ptex->texnum); + + for (p = pt->start ; p ; p = p->next) + { + if (particle_time < p->start || particle_time >= p->die) + continue; + + for (j = 0 ; j < cl.maxclients ; j++) + { + if (pt->custom != -1 && VectorSupCompare(p->org, cl_entities[1+j].origin, 40)) + { + p->die = 0; + continue; + } + } + + if(pt->texture == ptex_muzzleflash || pt->texture == ptex_muzzleflash2 || pt->texture == ptex_muzzleflash3) + sceGuDepthRange(0, 19660); + + DRAW_PARTICLE_BILLBOARD(ptex, p, billboard); + + if(pt->texture == ptex_muzzleflash || pt->texture == ptex_muzzleflash2 || pt->texture == ptex_muzzleflash3) + sceGuDepthRange(0, 65535); + } + break; + + case pd_billboard_vel: + ptex = &particle_textures[pt->texture]; + GL_Bind (ptex->texnum); + for (p = pt->start ; p ; p = p->next) + { + if (particle_time < p->start || particle_time >= p->die) + continue; + + VectorCopy (p->vel, up); + CrossProduct (vpn, up, right); + VectorNormalizeFast (right); + VectorScale (up, pt->custom, up); + + VectorAdd (up, right, velcoord[2]); + VectorSubtract (right, up, velcoord[3]); + VectorNegate (velcoord[2], velcoord[0]); + VectorNegate (velcoord[3], velcoord[1]); + DRAW_PARTICLE_BILLBOARD(ptex, p, velcoord); + } + break; + + case pd_q3flame: + ptex = &particle_textures[pt->texture]; + GL_Bind (ptex->texnum); + for (p = pt->start ; p ; p = p->next) + { + float varray_vertex[16]; + float xhalf = p->size / 2.0, yhalf = p->size; + // vec3_t org, v, end, normal; + + if (particle_time < p->start || particle_time >= p->die) + continue; + + sceGuDisable (GU_CULL_FACE); + + for (j=0 ; j<2 ; j++) + { + sceGumPushMatrix (); + + const ScePspFVector3 translation = + { + p->org[0], p->org[1], p->org[2] + }; + sceGumTranslate(&translation); + + //glRotatef (!j ? 45 : -45, 0, 0, 1); + + sceGumRotateZ(!j ? 45 : -45 * (GU_PI / 180.0f)); + + sceGuColor(GU_RGBA(p->color[0], p->color[1], p->color[2], p->color[3])); + + sceGumUpdateMatrix(); + + // sigh. The best would be if the flames were always orthogonal to their surfaces + // but I'm afraid it's impossible to get that work (w/o progs modification of course) + varray_vertex[0] = 0; + varray_vertex[1] = xhalf; + varray_vertex[2] = -yhalf; + varray_vertex[4] = 0; + varray_vertex[5] = xhalf; + varray_vertex[6] = yhalf; + varray_vertex[8] = 0; + varray_vertex[9] = -xhalf; + varray_vertex[10] = yhalf; + varray_vertex[12] = 0; + varray_vertex[13] = -xhalf; + varray_vertex[14] = -yhalf; + + struct vertex + { + float u, v; + float x, y, z; + }; + + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * 4)); + + out[0].u = ptex->coords[p->texindex][0]; + out[0].v = ptex->coords[p->texindex][3]; + out[0].x = varray_vertex[0]; + out[0].y = varray_vertex[1]; + out[0].z = varray_vertex[2]; + + + out[1].u = ptex->coords[p->texindex][0]; + out[1].v = ptex->coords[p->texindex][1]; + out[1].x = varray_vertex[4]; + out[1].y = varray_vertex[5]; + out[1].z = varray_vertex[6]; + + + out[2].u = ptex->coords[p->texindex][2]; + out[2].v = ptex->coords[p->texindex][1]; + out[2].x = varray_vertex[8]; + out[2].y = varray_vertex[9]; + out[2].z = varray_vertex[10]; + + + out[3].u = ptex->coords[p->texindex][2]; + out[3].v = ptex->coords[p->texindex][3]; + out[3].x = varray_vertex[12]; + out[3].y = varray_vertex[13]; + out[3].z = varray_vertex[14]; + + sceGuDrawArray(GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF, 4, 0, out); + + sceGumPopMatrix (); + sceGumUpdateMatrix(); + } + sceGuEnable (GU_CULL_FACE); + sceGuColor(GU_RGBA(0xff,0xff,0xff,0xff)); //return to normal color + } + break; + + case pd_q3gunshot: + for (p = pt->start ; p ; p = p->next) + QMB_Q3Gunshot (p->org, (int)p->texindex, (float)p->color[3] / 255.0); + break; + + case pd_q3teleport: + for (p = pt->start ; p ; p = p->next) + QMB_Q3Teleport (p->org, (float)p->color[3] / 255.0); + break; + default: + //assert (!"QMB_DrawParticles: unexpected drawtype"); + break; + } + } + + sceGuDepthMask (GU_FALSE); + sceGuDisable (GU_BLEND); + sceGuBlendFunc (GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuShadeModel (GU_FLAT); +} + +void QMB_Shockwave_Splash(vec3_t org, int radius) +{ + float theta; + vec3_t angle; + + angle[2] = 0; + + for (theta = 0; theta < 6.283185307179586476925286766559; theta += 0.069813170079773183076947630739545) + { + #ifdef PSP_VFPU + angle[0] = vfpu_cosf(theta) * radius; + angle[1] = vfpu_sinf(theta) * radius; + #else + angle[0] = cos(theta) * radius; + angle[1] = sin(theta) * radius; + #endif + AddParticle(p_shockwave, org, 1, 2, 0.625f, NULL, angle); + } +} + +extern sfx_t *cl_sfx_thunder; +void QMB_LetItRain(void) +{ + int i; + msurface_t *surf; + glpoly_t *p; + col_t color; + float distance[3], point[3]; + float frametime = fabs(cl.ctime - cl.oldtime); + + color[0] = color[1] = color[2] = + (rand() % 25 + 15); + color[2] += 5; + + if (!qmb_initialized) + return; + + if (!particle_mode) + return; + + if (frametime) + { + for (i = 0, surf = cl.worldmodel->surfaces; i < cl.worldmodel->numsurfaces; i++, surf++) + { + if (!(surf->flags & SURF_DRAWSKY)) continue; + + for (p = surf->polys; p; p = p->next) + { + VectorSubtract(r_refdef.vieworg,p->midpoint, distance); + + if (VectorLength(distance) > r_farclip.value) + continue; + + //R00k let's make some thunder... + /*if (cl.time > cl.thundertime) + { + S_StartSound (-1, 0, cl_sfx_thunder, p->midpoint, 1, 1); + cl.thundertime = cl.time + (rand()% 60 + 1); + }*/ + + point[0] = p->midpoint[0] + (rand() % 80 - 40); + point[1] = p->midpoint[1] + (rand() % 80 - 40); + point[2] = p->midpoint[2]; + + AddParticle(p_rain, point, 1, 0.75, 5, color, zerodir); + } + } + } +}; + +//R00k: revamped to coincide with classic particle style... + +void QMB_ParticleExplosion (vec3_t org) +{ + if (r_explosiontype.value == 2)//no explosion what so ever + return; + + if (ISUNDERWATER(TruePointContents(org))) + { + AddParticle (p_bubble, org, 6, 3.0, 2.5, NULL, zerodir); + AddParticle (p_bubble, org, 4, 2.35, 2.5, NULL, zerodir); + + if (r_part_explosions.value == 2) + { + AddParticle (p_q3explosion, org, 1, 36, 0.2, NULL, zerodir); + } + else + { + if (r_part_explosions.value == 2) + { + AddParticle (p_q3explosion, org, 1, 36, 0.2, NULL, zerodir); + } + else + { + AddParticle (p_fire, org, 16, 120, 1, NULL, zerodir); + if (r_explosiontype.value != 1) + { + AddParticle (p_spark, org, 50, 250, 0.925f, NULL, zerodir); + AddParticle (p_spark, org, 25, 150, 0.925f, NULL, zerodir); + } + } + } + } + else + + { + /* original + if (r_explosiontype.value != 3) + { + if (r_flametype.value < 1)//R00k + { + AddParticle (p_fire2, org, 16, 18, 1, NULL, zerodir); + } + else + { + AddParticle (p_fire, org, 16, 18, 1, NULL, zerodir); + } + } + + if ((r_explosiontype.value == 0) || (r_explosiontype.value == 1) || (r_explosiontype.value == 3)) + { + AddParticle (p_spark, org, 50, 250, 0.925f, NULL, zerodir); + AddParticle (p_spark, org, 25, 150, 0.925f, NULL, zerodir); + } + */ + + //shpuld + AddParticle (p_fire, org, 10, 40, 0.5, NULL, zerodir); + AddParticle (p_fire2, org, 14, 36, 1.8, NULL, zerodir); + } +} + +void d8to24col (col_t colourv, int colour) +{ + byte *colourByte; + + colourByte = (byte *)&d_8to24table[colour]; + colourv[0] = colourByte[0]; + colourv[1] = colourByte[1]; + colourv[2] = colourByte[2]; +} + +__inline static void AddColoredParticle (part_type_t type, vec3_t org, int count, float size, float time, int colorStart, int colorLength, vec3_t dir) +{ + col_t color; + int i, j, colorMod = 0; + float tempSize; + particle_t *p; + particle_type_t *pt; + + + if (!qmb_initialized) + Sys_Error ("QMB particle added without initialization"); + + //assert (size > 0 && time > 0); + + if (type < 0 || type >= num_particletypes) + Sys_Error ("AddColoredParticle: Invalid type (%d)", type); + + pt = &particle_types[particle_type_index[type]]; + + for (i=0 ; i < count && free_particles ; i++) + { + d8to24col (color, colorStart + (colorMod % colorLength)); + colorMod++; + INIT_NEW_PARTICLE(pt, p, color, size, time); + + switch (type) + { + case p_spark: + p->size = 1.175; + VectorCopy (org, p->org); + tempSize = size * 2; + p->vel[0] = (rand() % (int)tempSize) - ((int)tempSize / 4); + p->vel[1] = (rand() % (int)tempSize) - ((int)tempSize / 4); + p->vel[2] = (rand() % (int)tempSize) - ((int)tempSize / 6); + break; + + case p_fire: + VectorCopy (org, p->org); + for (j=0 ; j<3 ; j++) + p->vel[j] = ((rand() % 160) - 80) * (size / 25.0); + break; + + default: + //assert (!"AddColoredParticle: unexpected type"); + break; + } + } +} + +void QMB_ColorMappedExplosion (vec3_t org, int colorStart, int colorLength) +{ + + if (ISUNDERWATER(TruePointContents(org))) + { + //AddColoredParticle (p_fire, org, 16, 18, 1, colorStart, colorLength, zerodir); + AddParticle (p_bubble, org, 6, 3.0, 2.5, NULL, zerodir); + AddParticle (p_bubble, org, 4, 2.35, 2.5, NULL, zerodir); + if (r_explosiontype.value != 2) + { + AddColoredParticle (p_spark, org, 50, 100, 0.5, colorStart, colorLength, zerodir); + AddColoredParticle (p_spark, org, 25, 60, 0.5, colorStart, colorLength, zerodir); + } + } + else + { + if (r_flametype.value < 1)//R00k + { + AddColoredParticle (p_fire2, org, 16, 18, 1, colorStart, colorLength, zerodir); + } + else + { + AddColoredParticle (p_fire, org, 16, 18, 1, colorStart, colorLength, zerodir); + } + + if (r_explosiontype.value < 2) + { + AddColoredParticle (p_spark, org, 50, 250, 0.625f, colorStart, colorLength, zerodir); + if (r_explosiontype.value < 1) + AddColoredParticle (p_spark, org, 25, 150, 0.625f, colorStart, colorLength, zerodir); + } + } +} + +/* Original + +void QMB_Blood_Splat(part_type_t type, vec3_t org) //R00k :) +{ + int j; + col_t color; + vec3_t neworg, angle; + + VectorClear (angle); + + color[0]=100; + color[1]=0; + color[2]=0; + color[3]=255; + + AddParticle(p_bloodcloud, org, 1, 6, 0.5, color, zerodir); + + for (j=0 ; j<4 ; j++) + { + AngleVectors (angle, NULLVEC, NULLVEC, neworg); + VectorMA (org, 70, neworg, neworg); + + AddParticle (type, org, 5, 1, 2, color, neworg); + angle[1] += 360 / 4; + } +}*/ + +void QMB_Blood_Splat(part_type_t type, vec3_t org) //Shpuldified +{ + int j; + col_t color; + vec3_t neworg, angle; + + VectorClear (angle); + + color[0]=100; + color[1]=0; + color[2]=0; + color[3]=255; + + if(type == p_blood1) + { + AddParticle(p_bloodcloud, org, 3, 5, 0.3, color, zerodir); + } + + else if(type == p_blood2) + { + AddParticle(p_bloodcloud, org, 3, 7, 0.3, color, zerodir); + color[0] = 40; + AddParticle(p_blood2, org, 16, 3, 1.0, color, zerodir); + } + + else //p_blood3, trail? + { + AddParticle(p_bloodcloud, org, 3, 5, 0.6, color, zerodir); + for (j=0 ; j<4 ; j++) + { + AngleVectors (angle, NULLVEC, NULLVEC, neworg); + VectorMA (org, 70, neworg, neworg); + + AddParticle (type, org, 5, 1, 2, color, neworg); + angle[1] += 360 / 4; + } + } +} + +void QMB_RunParticleEffect (vec3_t org, vec3_t dir, int col, int count) +{ + col_t color; + vec3_t neworg, newdir; + int i, j, particlecount; + int contents;//R00k Added + + if (col == 73) + { + QMB_Blood_Splat(p_blood1, org); + return; + } + else if (col == 225) + { + QMB_Blood_Splat(p_blood2, org); + return; + } + else if (col == 20 && count == 30) + { + color[0] = color[2] = 51; + color[1] = 255; + AddParticle (p_chunk, org, 1, 1, 0.75, color, zerodir); + AddParticle (p_spark, org, 12, 75, 0.4, color, zerodir); + return; + } + else if (col == 226 && count == 20) + { + color[0] = 230; + color[1] = 204; + color[2] = 26; + AddParticle (p_chunk, org, 1, 1, 0.75, color, zerodir); + AddParticle (p_spark, org, 12, 75, 0.4, color, zerodir); + return; + } + else if(col == 111) //we will use this color for flames + { + color[0] = color[1] = color[2] = 255; + AddParticle (p_q3flame, org, 3, 3, 2, color, dir); + return; + } + else if(col == 112) //we will use this color for big flames + { + color[0] = color[1] = color[2] = 255; + AddParticle (p_q3flame, org, 3, 6, 2, color, dir); + return; + } + + switch (count) + { + case 9: + case 10://nailgun + { + color[0] = 200; color[1] = 200; color[2] = 125; + + if (r_part_spikes.value == 2) + AddParticle (p_q3gunshot, org, 1, 1, 0.3, NULL, zerodir); + else + AddParticle (p_spark, org, 6, 70, 0.6, NULL, zerodir); + + AddParticle (p_chunk, org, 3, 1, 0.75, NULL, zerodir); + + contents = TruePointContents (org);//R00k Added + + if (ISUNDERWATER(contents))//R00k + { + AddParticle (p_bubble, org, 1, 2, 0.825f + ((rand() % 10) - 5) / 40.0, NULL, zerodir); + } + else + + { + AddParticle (p_smoke, org, 1, 4, 0.825f + ((rand() % 10) - 5) / 40.0, NULL, zerodir); + } + } + break; + + case 20://super nailgun + color[0] = 200; color[1] = 200; color[2] = 125; + + if (r_part_spikes.value == 2) + AddParticle (p_q3gunshot, org, 1, 1, 0.3, NULL, zerodir); + else + //AddParticle (p_spark, org, 2, 85, 0.4, color, zerodir); + AddParticle (p_spark, org, 22, 100, 0.2, NULL, zerodir); + + //AddParticle (p_chunk, org, 6, 2, 0.75, NULL, zerodir); + + contents = TruePointContents (org);//R00k Added + + if (ISUNDERWATER(contents))//R00k + { + AddParticle (p_bubble, org, 1, 2, 0.825f + ((rand() % 10) - 5) / 40.0, NULL, zerodir); + } + else + + { + AddParticle (p_smoke, org, 3, 12, 1.225f + ((rand() % 10) - 5) / 40.0, NULL, zerodir); + } + break; + + case 24:// gunshot + if (r_part_gunshots.value == 2) + { + AddParticle (p_q3gunshot, org, 1, 1, 0.3, NULL, zerodir); + } + else + { + particlecount = count >> 1; + AddParticle (p_gunblast, org, 1, 1.04, 0.2, NULL, zerodir); + for (i=0 ; i>3) ; i++) + { + for (j=0 ; j<3 ; j++) + neworg[j] = org[j] + ((rand() % 24) - 12); + newdir[0] = dir[0] * (10 + (rand() % 5)); + newdir[1] = dir[1] * (10 + (rand() % 5)); + newdir[2] = dir[2] * 15; + d8to24col (color, (col & ~7) + (rand() & 7)); + AddParticle (p_glow, neworg, 1, 3.5, 0.5 + 0.1 * (rand() % 3), color, newdir); + } + return; + } + } +*/ + particlecount = fmax(1, count>>1); + for (i=0 ; iv.view_ofs[2] >= 0) + org[2] -= 32 - sv_player->v.view_ofs[2]; + else + org[2] -= 32 + abs(sv_player->v.view_ofs[2]); + /*if (sv_player->v.view_ofs[2] == 8) { + org[2] -= 24; + } else if (sv_player->v.view_ofs[2] == -10) { + org[2] -= 42; + }*/ + + float size; + + if(!(ISUNDERWATER(TruePointContents(org)))) + { + size = sv_player->v.Flash_Size; + + if(size == 0 || cl.stats[STAT_ZOOM] == 2) + return; + switch(rand() % 3 + 1) + { + case 1: + AddParticle (p_muzzleflash, org, 1, size, 0.04 * frametime, color, zerodir); + break; + case 2: + AddParticle (p_muzzleflash2, org, 1, size, 0.04 * frametime, color, zerodir); + break; + case 3: + AddParticle (p_muzzleflash3, org, 1, size, 0.04 * frametime, color, zerodir); + break; + default: + AddParticle (p_muzzleflash, org, 1, size, 0.04 * frametime, color, zerodir); + break; + } + } +} + +void QMB_MuzzleFlashLG(vec3_t org) +{ + float frametime = fabs(cl.ctime - cl.oldtime); + col_t color; + + color[0] = color[1] = 20; + color[2] = 200; + + if (!qmb_initialized) + return; + + if (!particle_mode) + return; + + if (!(ISUNDERWATER(TruePointContents(org)))) + { + AddParticle (p_muzzleflash8, org, 1, 8, 0.4 * frametime, color, zerodir); + } +} + +void QMB_RocketTrail (vec3_t start, vec3_t end, trail_type_t type) +{ + col_t color; + + switch (type) + { + case GRENADE_TRAIL://r00K mODIFIED + + if (ISUNDERWATER(TruePointContents(start))) + { + AddParticleTrail (p_bubble, start, end, 1, 0.30, NULL); + } + else + + { + if (r_part_trails.value > 1) + { + color[0] = 15; color[1] = 15; color[2] = 10; + AddParticleTrail (p_alphatrail, start, end, 8, 1, color); + } + else + AddParticleTrail (p_smoke, start, end, 1.45, 0.825, NULL); + } + break; + + case BLOOD_TRAIL: + case SLIGHT_BLOOD_TRAIL: + if (r_part_trails.value == 2) + AddParticleTrail (p_q3blood_trail, start, end, 15, 2, NULL); + else + AddParticleTrail (p_blood3, start, end, type == BLOOD_TRAIL ? 1.35 : 2.4, 2, NULL); + break; + + case TRACER1_TRAIL: + color[0] = color[2] = 0; + color[1] = 124; + AddParticleTrail (p_trailpart, start, end, 3.75, 0.5, color); + break; + + case TRACER2_TRAIL: + color[0] = 255; + color[1] = 77; + color[2] = 0; + AddParticleTrail (p_trailpart, start, end, 1.75, 0.2, color); + break; + + case VOOR_TRAIL: + color[0] = 77; + color[1] = 0; + color[2] = 255; + AddParticleTrail (p_trailpart, start, end, 3.75, 0.5, color); + break; + + case ALT_ROCKET_TRAIL: + + if (ISUNDERWATER(TruePointContents(start))) + { + AddParticleTrail (p_bubble, start, end, 1, 2.5, NULL); + } + else + + { + if (r_part_trails.value > 1) + { + color[0] = 15; color[1] = 15; color[2] = 10; + AddParticleTrail (p_alphatrail, start, end, 8, 1, color); + } + else + { + AddParticleTrail (p_dpfire, start, end, 3, 0.26, NULL); + AddParticleTrail (p_dpsmoke, start, end, 3, 0.825, NULL); + } + } + break; + + case LAVA_TRAIL: + AddParticleTrail (p_lavatrail, start, end, 5, 0.25, NULL); + AddParticleTrail (p_dpsmoke, start, end, 5, 0.825, NULL); + break; + + case BUBBLE_TRAIL: + + if (ISUNDERWATER(TruePointContents(start))) + AddParticleTrail (p_bubble2, start, end, 1.5, 0.825, NULL); + + break; + + case NEHAHRA_SMOKE: + AddParticleTrail (p_smoke, start, end, 0.8, 0.825, NULL); + break; + case RAYGREEN_TRAIL: + color[0] = 0; + color[1] = 255; + color[2] = 0; + AddParticleTrail (p_alphatrail, start, end, 8, 0.6, color); + break; + case RAYRED_TRAIL: + color[0] = 255; + color[1] = 0; + color[2] = 0; + AddParticleTrail (p_alphatrail, start, end, 8, 0.6, color); + break; + case ROCKET_TRAIL: + default: + color[0] = 255; + color[1] = 56; + color[2] = 9; +// AddParticleTrail (p_trailpart, start, end, 6.2, 0.31, color); + if (ISUNDERWATER(TruePointContents(start))) + { + AddParticleTrail (p_trailpart, start, end, 1, 0.30, color); + AddParticleTrail (p_bubble, start, end, 1, 2.5, NULL); + } + else + + { + if (r_part_trails.value > 1) + { + color[0] = 15; color[1] = 15; color[2] = 10; + AddParticleTrail (p_alphatrail, start, end, 8, 3, color); + } + else + { + AddParticleTrail (p_trailpart, start, end, 6.2, 0.31, color); + AddParticleTrail (p_smoke, start, end, 1.8, 0.825, NULL); + } + } + break; + case NAIL_TRAIL://R00k added + + if (ISUNDERWATER(TruePointContents(start))) + { + AddParticleTrail (p_bubble, start, end, 0.25, 0.50, NULL); + } + else + + { + color[0] = 15; color[1] = 15; color[2] = 15; + AddParticleTrail (p_alphatrail, start, end, 1, 0.25, color); + } + break; + } +} +/* +void QMB_AxeSwoosh(vec3_t start) +{ + col_t color; + vec3_t end, mid; + + mid[0] = 15;mid[1] = 15;mid[2] = 15; + VectorSubtract(start,mid,end); + + if (ISUNDERWATER(TruePointContents(start))) + { + AddParticleTrail (p_bubble, start, end, 0.25, 0.50, NULL); + } + else + { + color[0] = 255; color[1] = 255; color[2] = 255; + AddParticleTrail (p_alphatrail, start, end, 50, 0.5, color); + // AddParticle (p_spark, start, 50, 250, 0.925f, NULL, zerodir); + } +}; +*/ + +void QMB_BlobExplosion (vec3_t org) +{ + float theta; + col_t color; + vec3_t neworg, vel; + + color[0] = 60; + color[1] = 100; + color[2] = 240; + AddParticle (p_spark, org, 44, 250, 1.15, color, zerodir); + + color[0] = 90; + color[1] = 47; + color[2] = 207; + AddParticle (p_fire, org, 15, 30, 1.4, color, zerodir); + + vel[2] = 0; + for (theta = 0 ; theta < 6.28318530717958647692528676655901 ; theta += 0.0897597901025655210989326680937001) + { + color[0] = (60 + (rand() & 15)); + color[1] = (65 + (rand() & 15)); + color[2] = (200 + (rand() & 15)); + + #ifdef PSP_VFPU + vel[0] = vfpu_cosf(theta) * 125; + vel[1] = vfpu_sinf(theta) * 125; + neworg[0] = org[0] + vfpu_cosf(theta) * 6; + neworg[1] = org[1] + vfpu_sinf(theta) * 6; + #else + vel[0] = cos(theta) * 125; + vel[1] = sin(theta) * 125; + neworg[0] = org[0] + cos(theta) * 6; + neworg[1] = org[1] + sin(theta) * 6; + #endif + neworg[2] = org[2] + 0 - 10; + AddParticle (p_shockwave, neworg, 1, 4, 0.8, color, vel); + neworg[2] = org[2] + 0 + 10; + AddParticle (p_shockwave, neworg, 1, 4, 0.8, color, vel); + + vel[0] *= 1.15; + vel[1] *= 1.15; + #ifdef PSP_VFPU + neworg[0] = org[0] + vfpu_cosf(theta) * 13; + neworg[1] = org[1] + vfpu_sinf(theta) * 13; + #else + neworg[0] = org[0] + cos(theta) * 13; + neworg[1] = org[1] + sin(theta) * 13; + #endif + neworg[2] = org[2] + 0; + AddParticle (p_shockwave, neworg, 1, 6, 1.0, color, vel); + } +} + +void QMB_LavaSplash (vec3_t org) +{ + int i, j; + float vel; + vec3_t dir, neworg; + + for (i=-16 ; i<16; i++) + { + for (j=-16 ; j<16 ; j++) + { + dir[0] = j * 8 + (rand() & 7); + dir[1] = i * 8 + (rand() & 7); + dir[2] = 256; + + neworg[0] = org[0] + dir[0]; + neworg[1] = org[1] + dir[1]; + neworg[2] = org[2] + (rand() & 63); + + VectorNormalizeFast (dir); + vel = 50 + (rand() & 63); + VectorScale (dir, vel, dir); + + AddParticle (p_lavasplash, neworg, 1, 4.5, 2.6 + (rand() & 31) * 0.02, NULL, dir); + } + } +} + +void QMB_TeleportSplash (vec3_t org) +{ + int i, j, k; + vec3_t neworg, angle; + col_t color; + + if (r_part_telesplash.value == 2) + { + AddParticle (p_q3teleport, org, 1, 1, 1.0, NULL, zerodir); + return; + } + + //QMB_Shockwave_Splash(org, 120); + for (i=-12 ; i<=12 ; i+=6) + { + for (j=-12 ; j<=12 ; j+=6) + { + for (k=-24 ; k<=32 ; k+=8) + { + neworg[0] = org[0] + i + (rand() & 3) - 1; + neworg[1] = org[1] + j + (rand() & 3) - 1; + neworg[2] = org[2] + k + (rand() & 3) - 1; + angle[0] = (rand() & 15) - 7; + angle[1] = (rand() & 15) - 7; + angle[2] = (rand() % 160) - 80; + AddParticle (p_teleflare, neworg, 1, 1.8, 0.30 + (rand() & 7) * 0.02, NULL, angle); + } + } + } + + VectorSet (color, 140, 140, 255); + VectorClear (angle); + for (i=0 ; i<5 ; i++) + { + angle[2] = 0; + for (j=0 ; j<5 ; j++) + { + AngleVectors (angle, NULLVEC, NULLVEC, neworg); + VectorMA (org, 70, neworg, neworg); + AddParticle (p_sparkray, org, 1, 6 + (i & 3), 5, color, neworg); + angle[2] += 360 / 5; + } + angle[0] += 180 / 5; + } +} + + +void QMB_InfernoFlame (vec3_t org) +{ + float frametime = fabs(cl.ctime - cl.oldtime); + + if (ISUNDERWATER(TruePointContents(org))) + return; + + if (frametime) + { + if (r_flametype.value < 1) + { + AddParticle (p_torch_flame, org, 1, 5, 0.5, NULL, zerodir);//R00k + } + else + { + AddParticle (p_inferno_flame, org, 1, 30, 13.125 * frametime, NULL, zerodir); + AddParticle (p_inferno_trail, org, 2, 1.75, 45.0 * frametime, NULL, zerodir); + AddParticle (p_inferno_trail, org, 2, 1.0, 52.5 * frametime, NULL, zerodir); + } + } +} + +void QMB_StaticBubble (entity_t *ent) +{ + AddParticle (p_staticbubble, ent->origin, 1, ent->frame == 1 ? 1.85 : 2.9, 0.001, NULL, zerodir); +} + +void QMB_TorchFlame (vec3_t org) +{ + if (fabs(cl.ctime - cl.oldtime)) + AddParticle (p_torch_flame, org, 2, 2.5, 0.5, NULL, zerodir); +} + +void QMB_FlameGt (vec3_t org, float size, float time) +{ + if (fabs(cl.ctime - cl.oldtime)) + AddParticle (p_flame, org, 1, size, time, NULL, zerodir); +} + +void QMB_BigTorchFlame (vec3_t org) +{ + if (fabs(cl.ctime - cl.oldtime)) + AddParticle (p_torch_flame, org, 2, 7, 0.5, NULL, zerodir); +} + +void QMB_Q3TorchFlame (vec3_t org, float size) +{ + static double flametime = 0; + + if (flametime + 0.125 < cl.time || flametime >= cl.time) + flametime = cl.time; + else + return; + + if (fabs(cl.ctime - cl.oldtime)) + AddParticle (p_q3flame, org, 1, size, 0.25, NULL, zerodir); +} + +void QMB_ShamblerCharge (vec3_t org) +{ + vec3_t pos, vec, dir; + col_t col = {60, 100, 240, 128}; + float time, len; + int i; + + for (i=0 ; i<5 ; i++) + { + VectorClear(vec); + VectorClear(dir); + + VectorCopy(org, pos); + pos[0] += (rand() % 200) - 100; + pos[1] += (rand() % 200) - 100; + pos[2] += (rand() % 200) - 100; + + VectorSubtract(pos, org, vec); + len = VectorLength (vec); + VectorNormalize (vec); + VectorMA(dir, -200, vec, dir); + time = len / 200; + + AddParticle (p_streakwave, pos, 1, 3, time, col, dir); + } +} + +void QMB_LaserSight (void) +{ + float frametime = fabs(cl.time - cl.oldtime); + col_t color; + int c; + + extern cvar_t r_laserpoint; + extern cvar_t scr_ofsx; + //extern cvar_t cl_gun_offset; + + //blubs-- Issue with this is that there's no particle texture assigned, need to either create, or fix rendering of null particle texture. + vec3_t dest, start, stop, forward, right,up; + trace_t trace; + + if (!particle_mode) + return; + + if (frametime) + { + if (qmb_initialized) + { + VectorClear(stop); + AngleVectors (r_refdef.viewangles, forward, right, up); + VectorCopy(cl_entities[cl.viewentity].origin, start); + + start[2] += 16; + start[2] += cl.crouch + bound(-7, scr_ofsx.value, 4); + + VectorMA (start, 0, right, start); + VectorMA (start, 4096, forward, dest); + + c = lt_default; + + switch ((int)r_laserpoint.value) + { + case 1: + color[0] = color[0] = 000;color[1] = 000;color[2] = 255;color[3] = 50;//B + c = lt_blue; + break; + case 2: + color[0] = 255;color[1] = 000;color[2] = 000;color[3] = 50;//R + c = lt_red; + break; + case 3: + color[0] = 255;color[1] = 255;color[2] = 000;color[3] = 50;//Y + //c = lt_yellow; + c = lt_red; + break; + case 4: + color[0] = 000;color[1] = 255;color[2] = 000;color[3] = 50;//G + c = lt_green; + break; + } + + memset (&trace, 0, sizeof(trace_t)); + trace.fraction = 1; + SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, 0, 1, start, dest, &trace); + + start[2]+=cl.crouch; + AddParticle (p_streaktrail, start, 1, 2, 0.02, color, trace.endpos);// draw the line + //the 2 value above is size + + if (trace.fraction != 1) + { + color[3] = 200; + AddParticle (p_dot, trace.endpos, 1, 4, 0.01, color, zerodir);//pinpoint on wall + if ((cl.maxclients < 2) && (cl.time > cl.laser_point_time)) + { + CL_NewDlight (0, trace.endpos, (rand() % 10 + 30), 0.02, c); + cl.laser_point_time = cl.time + 0.02; + } + } + } + } +} + +void QMB_Lightning_Splash(vec3_t org) +{ + int i, j; + vec3_t neworg, angle; + col_t color; + col_t col2 = {200, 100, 100, 255}; + + VectorSet (color, 40, 40, 128); + VectorClear (angle); + + for (i=0 ; i<5 ; i++) + { + angle[2] = 0; + for (j=0 ; j<5 ; j++) + { + AngleVectors (angle, NULLVEC, NULLVEC, neworg); + VectorMA (org, 20, neworg, neworg); + AddParticle (p_spark, org, 2, 85, 0.05f, NULL, zerodir); + AddParticle (p_spark, org, 2, 100, 0.1f, col2, neworg); + angle[2] += 360 / 5; + } + angle[0] += 180 / 5; + } + color[0] = 224 + (rand() & 31); + color[1] = 100 + (rand() & 31); + color[2] = 0; + + AddParticle (p_spark, org, 1, 70, 0.2, color, zerodir); +} + +void QMB_LightningBeam (vec3_t start, vec3_t end) +{ + float frametime = fabs(cl.time - cl.oldtime); + col_t color = {255,255,255,255}; + trace_t trace; + + if (frametime) + { + if (qmb_initialized && r_part_lightning.value ) + { + if (qmb_initialized && r_part_sparks.value) + { + memset (&trace, 0, sizeof(trace_t)); + if (!SV_RecursiveHullCheck(cl.worldmodel->hulls, 0, 0, 1, start, end, &trace)) + { + if (trace.fraction < 1) + { + VectorCopy (trace.endpos, end); + if ((r_decal_sparks.value) && (particle_mode) && (decals_enabled)) + { + R_SpawnDecalStatic(end, decal_glow, 10); + } + QMB_Lightning_Splash (end); + } + } + } + + //R00k v1.84 moved down here + AddParticle(p_lightningbeam, start, 1, 80, host_frametime * 2, color, end); + } + } +} + +void R_DrawQ3Model (entity_t *ent); + +void QMB_Q3Gunshot (vec3_t org, int skinnum, float alpha) +{ + vec3_t neworg, normal, v, newend; + entity_t *ent; + extern model_t *cl_q3gunshot_mod; + + if (!(ent = CL_NewTempEntity())) + return; + + VectorCopy (org, ent->origin); + ent->model = cl_q3gunshot_mod; + + VectorCopy (cl_entities[cl.viewentity].origin, neworg); + VectorSubtract (ent->origin, neworg, v); + VectorScale (v, 2, v); + VectorAdd (neworg, v, newend); + + if (TraceLineN(neworg, newend, newend, normal)) + vectoangles (normal, ent->angles); + + ent->skinnum = skinnum; + ent->rendermode = TEX_ADDITIVE; + ent->renderamt = alpha; + + R_DrawQ3Model (ent); +} + +void QMB_Q3Teleport (vec3_t org, float alpha) +{ + entity_t *ent; + extern model_t *cl_q3teleport_mod; + + if (!(ent = CL_NewTempEntity())) + return; + + VectorCopy (org, ent->origin); + ent->model = cl_q3teleport_mod; + ent->rendermode = TEX_ADDITIVE; + ent->renderamt = alpha; + + R_DrawQ3Model (ent); +} + +#define NUMVERTEXNORMALS 162 + +extern float r_avertexnormals[NUMVERTEXNORMALS][3]; +extern vec3_t avelocities[NUMVERTEXNORMALS]; + +/* +=============== +R_EntityParticles +=============== +*/ +void QMB_EntityParticles (entity_t *ent) +{ + int i; + float angle, dist, sr, sp, sy, cr, cp, cy; + vec3_t forward, org; + col_t color = {255,255,0,100}; + + dist = 64; + + if (!avelocities[0][0]) + for (i=0 ; iorigin[0] + r_avertexnormals[i][0]*dist + forward[0]*16; + org[1] = ent->origin[1] + r_avertexnormals[i][1]*dist + forward[1]*16; + org[2] = ent->origin[2] + r_avertexnormals[i][2]*dist + forward[2]*16; + AddParticle (p_flare, org, 1, 2,0.005, color, forward); + } +} + +//Modified from Quake2 +void QMB_FlyParticles (vec3_t origin, int count) +{ + float frametime = fabs(cl.time - cl.oldtime); + int i; + float angle, sr, sp, sy, cr, cp, cy; + vec3_t forward, org; + float dist = 64; + col_t color = {255,255,255,100}; + + if (frametime) + { + if (count > NUMVERTEXNORMALS) + count = NUMVERTEXNORMALS; + + if (!avelocities[0][0]) + { + for (i=0 ; i +#include + +#include "clipping.hpp" + + +using namespace std; +using namespace quake; + +list TempDecalTextureList; + +#ifdef SLIM +#define DEFAULT_NUM_DECALS 1024 //*4 +#else +#define DEFAULT_NUM_DECALS 256 +#endif + +#define ABSOLUTE_MIN_DECALS 256 +#define ABSOLUTE_MAX_DECALS 32768 +#define MAX_DECAL_VERTICES 128 +#define MAX_DECAL_TRIANGLES 64 + +typedef struct decal_s +{ + vec3_t origin; + vec3_t normal; + vec3_t tangent; + float radius; + int bspdecal; + + struct decal_s *next; + float die; + float starttime; + + int srcblend; + int dstblend; + + int texture; + + // geometry of decal + int vertexCount, triangleCount; + vec3_t vertexArray[MAX_DECAL_VERTICES]; + float texcoordArray[MAX_DECAL_VERTICES][2]; + int triangleArray[MAX_DECAL_TRIANGLES][3]; +} decal_t; + +extern int decal_blood1, decal_blood2, decal_blood3, decal_q3blood, decal_burn, decal_mark, decal_glow; + +static decal_t *decals, *active_decals, *free_decals; +static int r_numdecals; + +static plane_t leftPlane, rightPlane, bottomPlane, topPlane, backPlane, frontPlane; + +cvar_t r_decaltime = {"r_decaltime", "12"}; +cvar_t r_decal_viewdistance = {"r_decal_viewdistance", "1024"}; + +void DecalClipLeaf (decal_t *dec, mleaf_t *leaf); +void DecalWalkBsp_R (decal_t *dec, mnode_t *node); +int DecalClipPolygonAgainstPlane (plane_t *plane, int vertexCount, vec3_t *vertex, vec3_t *newVertex); +int DecalClipPolygon (int vertexCount, vec3_t *vertices, vec3_t *newVertex); +/* +#define lhrandom(MIN, MAX) ((rand() & 32767) * (((MAX)-(MIN)) * (1.0f / 32767.0f)) + (MIN)) + +#define VectorRandom(v) \ +{ \ + do { \ + (v)[0] = lhrandom(-1, 1); \ + (v)[1] = lhrandom(-1, 1); \ + (v)[2] = lhrandom(-1, 1); \ + } while (DotProduct(v, v) > 1); \ +} +*/ +float RandomMinMax (float min, float max) +{ + return min + ((rand() % 10000) / 10000.0) * (max - min); +} + +/* +=============== +R_InitDecals +=============== +*/ +void R_InitDecals (void) +{ + int i; + + Cvar_RegisterVariable (&r_decaltime); + Cvar_RegisterVariable (&r_decal_viewdistance); + + if (!qmb_initialized) + return; +/* + decal_blood1 = loadtextureimage("textures/decals/blood_splat01", 0, 0, qfalse, GU_LINEAR); + decal_blood2 = loadtextureimage("textures/decals/blood_splat02", 0, 0, qfalse, GU_LINEAR); + decal_blood3 = loadtextureimage("textures/decals/blood_splat03", 0, 0, qfalse, GU_LINEAR); + decal_q3blood = loadtextureimage("textures/decals/blood_stain", 0, 0, qfalse, GU_LINEAR); + decal_burn = loadtextureimage("textures/decals/explo_burn01", 0, 0, qfalse, GU_LINEAR); + decal_mark = loadtextureimage("textures/decals/particle_burn01", 0, 0, qfalse, GU_LINEAR); + decal_glow = loadtextureimage("textures/decals/glow2", 0, 0, qfalse, GU_LINEAR); + + decal_blood1 = GL_LoadTextureImage ("textures/decals/blood_splat01", "decals:blood_splat01", 128, 128, TEX_ALPHA | TEX_COMPLAIN); + decal_blood2 = GL_LoadTextureImage ("textures/decals/blood_splat02", "decals:blood_splat02", 128, 128, TEX_ALPHA | TEX_COMPLAIN); + decal_blood3 = GL_LoadTextureImage ("textures/decals/blood_splat03", "decals:blood_splat03", 128, 128, TEX_ALPHA | TEX_COMPLAIN); + decal_q3blood = GL_LoadTextureImage ("textures/decals/blood_stain", "decals:blood_stain", 64, 64, TEX_ALPHA | TEX_COMPLAIN); + decal_burn = GL_LoadTextureImage ("textures/decals/explo_burn01", "decals:explo_burn01", 128, 128, TEX_ALPHA | TEX_COMPLAIN); + decal_mark = GL_LoadTextureImage ("textures/decals/particle_burn01", "decals:particle_burn01", 64, 64, TEX_ALPHA | TEX_COMPLAIN); + decal_glow = GL_LoadTextureImage ("textures/decals/glow2", "decals:glow2", 64, 64, TEX_ALPHA | TEX_COMPLAIN); +*/ + if ((i = COM_CheckParm("-decals")) && i + 1 < com_argc) + { + r_numdecals = Q_atoi(com_argv[i+1]); + r_numdecals = bound(ABSOLUTE_MIN_DECALS, r_numdecals, ABSOLUTE_MAX_DECALS); + } + else + { + r_numdecals = DEFAULT_NUM_DECALS; + } + + decals = (decal_t*)Hunk_AllocName (r_numdecals * sizeof(decal_t), "decals"); +} + +/* +=============== +R_ClearDecals +=============== +*/ +static int wadreload = 1; +void R_ClearDecals (void) +{ + int i; + + if (!qmb_initialized) + return; + + memset (decals, 0, r_numdecals * sizeof(decal_t)); + free_decals = &decals[0]; + active_decals = NULL; + + for (i = 0 ; i < r_numdecals ; i++) + decals[i].next = &decals[i+1]; + + decals[r_numdecals-1].next = NULL; + + while (TempDecalTextureList.size() > 0) + { + int index = TempDecalTextureList.front(); + TempDecalTextureList.pop_front(); + GL_UnloadTexture(index); + } + wadreload = 1; +} + +void R_SpawnDecal (vec3_t center, vec3_t normal, vec3_t tangent, int tex, int size, int isbsp) +{ + int a; + float width, height, depth, d, one_over_w, one_over_h; + vec3_t binormal, test = {0.5, 0.5, 0.5}; + decal_t *dec; + + if (!qmb_initialized) + return; + + // allocate decal + if (!free_decals) + return; + + dec = free_decals; + free_decals = dec->next; + dec->next = active_decals; + active_decals = dec; + + VectorNormalize (test); + CrossProduct (normal, test, tangent); + + VectorCopy (center, dec->origin); + VectorCopy (tangent, dec->tangent); + VectorCopy (normal, dec->normal); + VectorNormalize (tangent); + VectorNormalize (normal); + CrossProduct (normal, tangent, binormal); + VectorNormalize (binormal); + + width = RandomMinMax (size * 0.5, size); + height = width; + depth = width * 0.5; + dec->radius = fmax(fmax(width, height), depth); + dec->starttime = cl.time; + dec->bspdecal = isbsp; + dec->die = (isbsp ? 0 : cl.time + r_decaltime.value); + dec->texture = tex; + + // Calculate boundary planes + d = DotProduct (center, tangent); + VectorCopy (tangent, leftPlane.normal); + leftPlane.dist = -(width * 0.5 - d); + VectorNegate (tangent, tangent); + VectorCopy (tangent, rightPlane.normal); + VectorNegate (tangent, tangent); + rightPlane.dist = -(width * 0.5 + d); + + d = DotProduct (center, binormal); + VectorCopy (binormal, bottomPlane.normal); + bottomPlane.dist = -(height * 0.5 - d); + VectorNegate (binormal, binormal); + VectorCopy (binormal, topPlane.normal); + VectorNegate (binormal, binormal); + topPlane.dist = -(height * 0.5 + d); + + d = DotProduct (center, normal); + VectorCopy (normal, backPlane.normal); + backPlane.dist = -(depth - d); + VectorNegate (normal, normal); + VectorCopy (normal, frontPlane.normal); + VectorNegate (normal, normal); + frontPlane.dist = -(depth + d); + + // Begin with empty mesh + dec->vertexCount = 0; + dec->triangleCount = 0; + + // Clip decal to bsp + DecalWalkBsp_R (dec, cl.worldmodel->nodes); + + // This happens when a decal is to far from any surface or the surface is to steeply sloped + if (dec->triangleCount == 0) + { // deallocate decal + active_decals = dec->next; + dec->next = free_decals; + free_decals = dec; + return; + } + + // Assign texture mapping coordinates + one_over_w = 1.0F / width; + one_over_h = 1.0F / height; + for (a = 0 ; a < dec->vertexCount ; a++) + { + float s, t; + vec3_t v; + + VectorSubtract (dec->vertexArray[a], center, v); + s = DotProduct (v, tangent) * one_over_w + 0.5F; + t = DotProduct (v, binormal) * one_over_h + 0.5F; + dec->texcoordArray[a][0] = s; + dec->texcoordArray[a][1] = t; + } +} +/* +//back up, pre-blubs +void _R_SpawnDecalStatic (vec3_t org, int tex, int size) +{ + int i; + float frac, bestfrac; + vec3_t tangent, v, bestorg, normal, bestnormal, org2; + + if (!qmb_initialized) + return; + + VectorClear (bestorg); + VectorClear (bestnormal); + + bestfrac = 10; + for (i = 0 ; i < 14 ; i++) + { + VectorRandom (org2); + VectorMA (org, 20, org2, org2); + TraceLineN (org, org2, v, normal); + frac = 0.5; + + if (bestfrac > frac) + { + bestfrac = frac; + VectorCopy (v, bestorg); + VectorCopy (normal, bestnormal); + CrossProduct (normal, bestnormal, tangent); + } + + if (bestnormal && bestorg) + { + continue; + } + else + { + VectorNegate (org2, org2); + VectorMA (org, 20, org2, org2); + TraceLineN (org, org2, v, normal); + VectorCopy (v, bestorg); + VectorCopy (normal, bestnormal); + CrossProduct (normal, bestnormal, tangent); + } + + if (bestnormal && bestorg) + continue; + + } + + if (bestfrac < 1) + R_SpawnDecal (bestorg, bestnormal, tangent, tex, size, 0); +}*/ + +//Revamped by blubs +void R_SpawnDecalStatic (vec3_t org, int tex, int size) +{ + int i; + float frac, bestfrac; + vec3_t tangent, v, bestorg, normal, bestnormal, org2; + vec3_t tempVec; + + if (!qmb_initialized) + return; + + VectorClear (bestorg); + VectorClear (bestnormal); + VectorClear(tempVec); + + bestfrac = 10; + for (i = 0 ; i < 26 ; i++) + { + //Reference: i = 0: check straight up, i = 1: check straight down + //1 < i < 10: Check sideways in increments of 45 degrees + //9 < i < 18: Check angled 45 degrees down in increments of 45 degrees + //17 < i : Check angled 45 degrees up in increments of 45 degrees + org2[0] = (((((i - 2) % 8) < 2) || (((i - 2) % 8) == 7)) ? 1 : 0 ) + ((((i - 2) % 8) > 2 && ((i - 2) % 8) < 6) ? -1 : 0 ); + org2[1] = ((((i - 2) % 8) > 0 && ((i - 2) % 8) < 4) ? 1 : 0 ) + ((((i - 2) % 8) > 4 && ((i - 2) % 8) < 7) ? -1 : 0 ); + org2[2] = ((i == 0) ? 1 : 0) + ((i == 1) ? -1 : 0) + (((i > 9) && (i < 18)) ? 1 : 0) + ((i > 17) ? -1 : 0); + + VectorCopy(org,tempVec); + VectorMA(tempVec, -0.1,org2,tempVec); + + VectorMA (org, 20, org2, org2); + TraceLineN (tempVec, org2, v, normal); + + VectorSubtract(org2,tempVec,org2);//goal + VectorSubtract(v,tempVec,tempVec);//collision + + if(VectorLength(org2) == 0) + return; + + frac = VectorLength(tempVec) / VectorLength(org2); + + if(frac < 1 && frac < bestfrac) + { + bestfrac = frac; + VectorCopy(v,bestorg); + VectorCopy(normal, bestnormal); + CrossProduct(normal,bestnormal,tangent); + } + } + + if (bestfrac < 1) + R_SpawnDecal (bestorg, bestnormal, tangent, tex, size, 0); +} + + +//Crow_bar. +void R_SpawnDecalBSP (vec3_t org, char *texname, int size) +{ + int i; + float frac, bestfrac; + vec3_t tangent, v, bestorg, normal, bestnormal, org2; + vec3_t tempVec; + + if (!qmb_initialized) + return; + + int tex = loadtextureimage(va("decals/%s",texname), 0, 0, qfalse, GU_LINEAR); + if(!tex) //find in decals.wad + { + if(wadreload) + { + WAD3_LoadTextureWadFile ("decals.wad"); + wadreload = 0; + } + tex = WAD3_LoadTextureName(texname); + if(!tex) + { + return; + } + } + + //don't free static decals + if((tex != decal_q3blood) && + (tex != decal_blood1) && + (tex != decal_blood2) && + (tex != decal_blood3) && + (tex != decal_q3blood) && + (tex != decal_burn) && + (tex != decal_mark) && + (tex != decal_glow)) + { + TempDecalTextureList.push_back(tex); //write to list ,for unload + } + + VectorClear (bestorg); + VectorClear (bestnormal); + VectorClear(tempVec); + + bestfrac = 10; + for (i = 0 ; i < 26 ; i++) + { + //Reference: i = 0: check straight up, i = 1: check straight down + //1 < i < 10: Check sideways in increments of 45 degrees + //9 < i < 18: Check angled 45 degrees down in increments of 45 degrees + //17 < i : Check angled 45 degrees up in increments of 45 degrees + org2[0] = (((((i - 2) % 8) < 2) || (((i - 2) % 8) == 7)) ? 1 : 0 ) + ((((i - 2) % 8) > 2 && ((i - 2) % 8) < 6) ? -1 : 0 ); + org2[1] = ((((i - 2) % 8) > 0 && ((i - 2) % 8) < 4) ? 1 : 0 ) + ((((i - 2) % 8) > 4 && ((i - 2) % 8) < 7) ? -1 : 0 ); + org2[2] = ((i == 0) ? 1 : 0) + ((i == 1) ? -1 : 0) + (((i > 9) && (i < 18)) ? 1 : 0) + ((i > 17) ? -1 : 0); + + VectorCopy(org,tempVec); + VectorMA(tempVec, -0.1,org2,tempVec); + + VectorMA (org, 20, org2, org2); + TraceLineN (tempVec, org2, v, normal); + + VectorSubtract(org2,tempVec,org2);//goal + VectorSubtract(v,tempVec,tempVec);//collision + + if(VectorLength(org2) == 0) + return; + + frac = VectorLength(tempVec) / VectorLength(org2); + + if(frac < 1 && frac < bestfrac) + { + bestfrac = frac; + VectorCopy(v,bestorg); + VectorCopy(normal, bestnormal); + CrossProduct(normal,bestnormal,tangent); + } + } + + if (bestfrac < 1) + R_SpawnDecal (bestorg, bestnormal, tangent, tex, size, 1); +} + +void DecalWalkBsp_R (decal_t *dec, mnode_t *node) +{ + float dist; + mplane_t *plane; + mleaf_t *leaf; + + if (node->contents < 0) + { //we are in a leaf + leaf = (mleaf_t *)node; + DecalClipLeaf (dec, leaf); + return; + } + + plane = node->plane; + dist = DotProduct (dec->origin, plane->normal) - plane->dist; + + if (dist > dec->radius) + { + DecalWalkBsp_R (dec, node->children[0]); + return; + } + if (dist < -dec->radius) + { + DecalWalkBsp_R (dec, node->children[1]); + return; + } + + DecalWalkBsp_R (dec, node->children[0]); + DecalWalkBsp_R (dec, node->children[1]); +} + +qboolean DecalAddPolygon (decal_t *dec, int vertcount, vec3_t *vertices) +{ + int a, b, count, *triangle; + + count = dec->vertexCount; + if (count + vertcount >= MAX_DECAL_VERTICES) + return qfalse; + + if (dec->triangleCount + vertcount - 2 >= MAX_DECAL_TRIANGLES) + return qfalse; + + // Add polygon as a triangle fan + triangle = &dec->triangleArray[dec->triangleCount][0]; + for (a = 2 ; a < vertcount ; a++) + { + dec->triangleArray[dec->triangleCount][0] = count; + dec->triangleArray[dec->triangleCount][1] = (count + a - 1); + dec->triangleArray[dec->triangleCount][2] = (count + a ); + dec->triangleCount++; + } + + // Assign vertex colors + for (b = 0 ; b < vertcount ; b++) + { + VectorCopy(vertices[b], dec->vertexArray[count]); + count++; + } + + dec->vertexCount = count; + return qtrue; +} + +const double decalEpsilon = 0.001; + +void DecalClipLeaf (decal_t *dec, mleaf_t *leaf) +{ + int c; + vec3_t newVertex[64], t3; + msurface_t **surf; + + c = leaf->nummarksurfaces; + surf = leaf->firstmarksurface; + + // for all surfaces in the leaf + for (c = 0 ; c < leaf->nummarksurfaces ; c++, surf++) + { + int i, count; + glpoly_t *poly; + + poly = (*surf)->polys; + for (i = 0 ; i < poly->numverts ; i++) + { + newVertex[i][0] = poly->verts[i].xyz[0]; + newVertex[i][1] = poly->verts[i].xyz[1]; + newVertex[i][2] = poly->verts[i].xyz[2]; + } + + VectorCopy ((*surf)->plane->normal, t3); + + if ((*surf)->flags & SURF_PLANEBACK) + VectorNegate (t3, t3); + + // avoid backfacing and ortogonal facing faces to recieve decal parts + if (DotProduct(dec->normal, t3) > decalEpsilon) + { + count = DecalClipPolygon (poly->numverts, newVertex, newVertex); + if (count != 0 && !DecalAddPolygon(dec, count, newVertex)) + break; + } + } +} + +int DecalClipPolygon (int vertexCount, vec3_t *vertices, vec3_t *newVertex) +{ + vec3_t tempVertex[64]; + + // Clip against all six planes + int count = DecalClipPolygonAgainstPlane (&leftPlane, vertexCount, vertices, tempVertex); + if (count != 0) + { + count = DecalClipPolygonAgainstPlane (&rightPlane, count, tempVertex, newVertex); + if (count != 0) + { + count = DecalClipPolygonAgainstPlane (&bottomPlane, count, newVertex, tempVertex); + if (count != 0) + { + count = DecalClipPolygonAgainstPlane (&topPlane, count, tempVertex, newVertex); + if (count != 0) + { + count = DecalClipPolygonAgainstPlane (&backPlane, count, newVertex, tempVertex); + if (count != 0) + { + count = DecalClipPolygonAgainstPlane (&frontPlane, count, tempVertex, newVertex); + } + } + } + } + } + + return count; +} + +int DecalClipPolygonAgainstPlane (plane_t *plane, int vertexCount, vec3_t *vertex, vec3_t *newVertex) +{ + int a, b, c, count, negativeCount = 0; + float t; + bool negative[65]; + vec3_t v1, v2; + + // Classify vertices + for (a = 0 ; a < vertexCount ; a++) + { + bool neg = ((DotProduct(plane->normal, vertex[a]) - plane->dist) < 0.0); + negative[a] = neg; + negativeCount += neg; + } + + // Discard this polygon if it's completely culled + if (negativeCount == vertexCount) + return 0; + + count = 0; + for (b = 0 ; b < vertexCount ; b++) + { + // c is the index of the previous vertex + c = (b != 0) ? b - 1 : vertexCount - 1; + + if (negative[b]) + { + if (!negative[c]) + { + // Current vertex is on negative side of plane, but previous vertex is on positive side. + VectorCopy (vertex[c], v1); + VectorCopy (vertex[b], v2); + + t = (DotProduct(plane->normal, v1) - plane->dist) / + (plane->normal[0] * (v1[0] - v2[0]) + + plane->normal[1] * (v1[1] - v2[1]) + + plane->normal[2] * (v1[2] - v2[2])); + + VectorScale (v1, (1.0 - t), newVertex[count]); + VectorMA (newVertex[count], t, v2, newVertex[count]); + + count++; + } + } + else + { + if (negative[c]) + { + // Current vertex is on positive side of plane, but previous vertex is on negative side. + VectorCopy (vertex[b], v1); + VectorCopy (vertex[c], v2); + + t = (DotProduct(plane->normal, v1) - plane->dist) / + (plane->normal[0] * (v1[0] - v2[0]) + + plane->normal[1] * (v1[1] - v2[1]) + + plane->normal[2] * (v1[2] - v2[2])); + + VectorScale (v1, (1.0 - t), newVertex[count]); + VectorMA (newVertex[count], t, v2, newVertex[count]); + + count++; + } + + // Include current vertex + VectorCopy (vertex[b], newVertex[count]); + count++; + } + } + + // Return number of vertices in clipped polygon + return count; +} + +/* +=============== +R_DrawDecals +=============== +*/ +void R_DrawDecals (void) +{ + int i; + float dcolor; + vec3_t decaldist; + decal_t *p, *kill; + float *point_tex, *point_xyz; + + if (!qmb_initialized) + return; + + + sceGuEnable (GU_BLEND); + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + sceGuDepthMask (GU_TRUE); + sceGuShadeModel (GU_SMOOTH); + sceGuDepthOffset(-256); + + + for ( ; ; ) + { + kill = active_decals; + + if (kill && (kill->die < cl.time) && (!kill->bspdecal)) + { + active_decals = kill->next; + kill->next = free_decals; + free_decals = kill; + continue; + } + break; + } + + for (p = active_decals ; p ; p = p->next) + { + + for ( ; ; ) + { + kill = p->next; + + if (kill && (kill->die < cl.time) && (!kill->bspdecal)) + { + p->next = kill->next; + kill->next = free_decals; + free_decals = kill; + continue; + } + break; + } + VectorSubtract (r_refdef.vieworg, p->origin, decaldist); + + if (VectorLength(decaldist) > r_decal_viewdistance.value) + continue; + + if (p->texture == decal_q3blood) + sceGuBlendFunc (GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + else + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + + + GL_Bind (p->texture); + + dcolor = 1; + if (((p->die - cl.time) < 0.5) && (!p->bspdecal)) + { + float scale = 2 * (p->die - cl.time); + sceGuColor(GU_COLOR(dcolor * scale, dcolor * scale, dcolor * scale, scale)); + } + else + { + dcolor = (1 - (VectorLength(decaldist) / r_decal_viewdistance.value)); + sceGuColor(GU_COLOR(dcolor, dcolor, dcolor, dcolor)); + } + + for (i = 0 ; i < p->triangleCount ; i++) + { + // Allocate memory for this polygon. + const int unclipped_vertex_count = 3; + glvert_t* const unclipped_vertices = static_cast(sceGuGetMemory(sizeof(glvert_t) * unclipped_vertex_count)); + for(int v = 0; v < unclipped_vertex_count ; v++) + { + point_tex = &p->texcoordArray[p->triangleArray[i][v]][0]; + point_xyz = &p->vertexArray [p->triangleArray[i][v]][0]; + unclipped_vertices[v].st[0] = point_tex[0]; + unclipped_vertices[v].st[1] = point_tex[1]; + unclipped_vertices[v].xyz[0] = point_xyz[0]; + unclipped_vertices[v].xyz[1] = point_xyz[1]; + unclipped_vertices[v].xyz[2] = point_xyz[2]; + } + + if (clipping::is_clipping_required( + unclipped_vertices, + unclipped_vertex_count)) + { + // Clip the polygon. + const glvert_t* clipped_vertices; + std::size_t clipped_vertex_count; + clipping::clip( + unclipped_vertices, + unclipped_vertex_count, + &clipped_vertices, + &clipped_vertex_count); + + // Did we have any vertices left? + if (clipped_vertex_count) + { + // Copy the vertices to the display list. + const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t); + glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size)); + memcpy(display_list_vertices, clipped_vertices, buffer_size); + + // Draw the clipped vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + clipped_vertex_count, 0, display_list_vertices); + } + } + else + { + // Draw the poly directly. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + unclipped_vertex_count, 0, unclipped_vertices); + } + } + } + + sceGuDepthOffset(0); + + sceGuDisable (GU_BLEND); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuDepthMask (GU_FALSE); + + sceGuColor(GU_COLOR(1,1,1,1)); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuShadeModel (GU_FLAT); +} + diff --git a/source/psp/video_hardware_draw.cpp b/source/psp/video_hardware_draw.cpp new file mode 100644 index 0000000..e0c7a1b --- /dev/null +++ b/source/psp/video_hardware_draw.cpp @@ -0,0 +1,3472 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. +Copyright (C) 2008-2009 Crow_bar. + +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 + +#include +#include +#include +#include +#include +#include +#include + +extern "C" +{ +#include "../quakedef.h" +} + +#include "video_hardware_dxtn.h" +#include "video_hardware_resample.h" + +#include "vram.hpp" +#include + +byte *draw_chars; // 8*8 graphic characters +qpic_t *sniper_scope; + +int translate_texture; +int char_texture; +int detail_texture; +int underwater_texture; +//int zombie_skins[3][8]; +int zombie_skins[2][2]; +int ref_texture; +int nonetexture; +typedef byte texel; + +bool tex_scale_down = true; + +//Loading Fill by Crow_bar +float loading_cur_step; +char loading_name[32]; +float loading_num_step; + +#define CLUT4 + +typedef struct +{ + // Source. + char identifier[64]; + int original_width; + int original_height; + bool stretch_to_power_of_two; + + // Texture description. + int format; + int filter; + int width; + int height; + int mipmaps; + int bpp; + int swizzle; + qboolean islmp; +#ifdef CLUT4 + unsigned char *palette; + ScePspRGBA8888 *palette_2; + #else + ScePspRGBA8888 *palette; +#endif + qboolean palette_active; + + // Buffers. + texel* ram; + texel* vram; +} gltexture_t; + +int loadtextureimage (char* filename, int matchwidth, int matchheight, qboolean complain, int filter); +void VID_SetPalette4(unsigned char* clut4pal); +int vid_palmode; + +#define MAX_GLTEXTURES 1024 +gltexture_t gltextures[MAX_GLTEXTURES]; +bool gltextures_used[MAX_GLTEXTURES]; +int numgltextures; + +typedef struct +{ + int index; // index into gltextures[]. +} glpic_t; + +//cvar_t png_compression_level = {"png_compression_level", "1"}; +//cvar_t jpeg_compression_level = {"jpeg_compression_level", "75"}; + +void GL_Bind4Part(int texture_index) +{ + const gltexture_t& texture = gltextures[texture_index]; + VID_SetPalette4(texture.palette); + vid_palmode = GU_PSM_T4; + + sceGuTexMode(texture.format, 0, 0, GU_FALSE); + sceGuTexFilter(texture.filter, texture.filter); + + const void* const texture_memory = texture.vram ? texture.vram : texture.ram; + sceGuTexImage(0, texture.width, texture.height, texture.width, texture_memory); +} + +void GL_InitTextureUsage () +{ + for (int i=0; i 0 && r_mipmaps.value > 0) + { + //Con_Printf("asd\n"); + float slope = 0.4f; + sceGuTexSlope(slope); // the near from 0 slope is the lower (=best detailed) mipmap it uses + sceGuTexFilter(GU_LINEAR_MIPMAP_LINEAR, GU_LINEAR_MIPMAP_LINEAR); + sceGuTexLevelMode(int(r_mipmaps_func.value), r_mipmaps_bias.value); + } + else { + // dr_mabuse1981: "retro filter" start. + if (r_retro.value) + sceGuTexFilter(GU_NEAREST, GU_NEAREST); + else + sceGuTexFilter(texture.filter, texture.filter); + // dr_mabuse1981: "retro filter" end. + } + + // Set the texture image. + const void* const texture_memory = texture.vram ? texture.vram : texture.ram; + sceGuTexImage(0, texture.width, texture.height, texture.width, texture_memory); + + if (texture.mipmaps > 0 && r_mipmaps.value > 0) { + int size = (texture.width * texture.height); + int offset = size; + int div = 2; + + for (int i = 1; i <= texture.mipmaps; i++) { + void* const texture_memory2 = ((byte*)texture_memory) + offset; + sceGuTexImage(i, texture.width / div, texture.height / div, texture.width / div, texture_memory2); + offset += size / (div*div); + div *= 2; + } + } + } + + +} + +void GL_BindLM (int texture_index) +{ + // Binding the currently bound texture? + if (currenttexture == texture_index) + { + // Don't bother. + return; + } + + // Remember the current texture. + currenttexture = texture_index; + + // Which texture is it? + const gltexture_t& texture = gltextures[texture_index]; + + // Set the texture mode. + sceGuTexMode(texture.format, 0, 0, texture.swizzle); + sceGuTexFilter(texture.filter, texture.filter); + + // Set the texture image. + const void* const texture_memory = texture.vram ? texture.vram : texture.ram; + sceGuTexImage(0, texture.width, texture.height, texture.width, texture_memory); +} + +void GL_BindDET (int texture_index) +{ + int tex_m = (int)r_detail_mipmaps_func.value; + // Binding the currently bound texture? + if (currenttexture == texture_index) + { + // Don't bother. + return; + } + + // Remember the current texture. + currenttexture = texture_index; + + // Which texture is it? + const gltexture_t& texture = gltextures[texture_index]; + + // Set the texture mode. + sceGuTexMode(texture.format, texture.mipmaps , 0, texture.swizzle); + + if (texture.mipmaps > 0 && r_detail_mipmaps.value > 0) + { + float slope = 0.4f; + sceGuTexSlope(slope); // the near from 0 slope is the lower (=best detailed) mipmap it uses + sceGuTexFilter(GU_LINEAR_MIPMAP_LINEAR, GU_LINEAR_MIPMAP_LINEAR); + sceGuTexLevelMode(tex_m, r_detail_mipmaps_bias.value); // manual slope setting + } + else + sceGuTexFilter(texture.filter, texture.filter); + + + //sceGuTexLevelMode(1, 1.0f); // manual slope setting + //sceGuTexSlope(0.2f); // the near from 0 slope is the lower (=best detailed) mipmap it uses + + // Set the texture image. + const void* const texture_memory = texture.vram ? texture.vram : texture.ram; + sceGuTexImage(0, texture.width, texture.height, texture.width, texture_memory); + + + if (texture.mipmaps > 0 && r_detail_mipmaps.value > 0) + { + int size = (texture.width * texture.height * texture.bpp); + int offset = size; + int div = 2; + + for (int i = 1; i <= texture.mipmaps; i++) + { + void* const texture_memory2 = ((byte*) texture_memory)+offset; + sceGuTexImage(i, texture.width/div, texture.height/div, texture.width/div, texture_memory2); + offset += size/(div*div); + div *=2; + } + } + +} + +//============================================================================= +/* Support Routines */ + +typedef struct cachepic_s +{ + char name[MAX_QPATH]; + qpic_t pic; + byte padding[32]; // for appended glpic +} cachepic_t; + +#define MAX_CACHED_PICS 128 +cachepic_t menu_cachepics[MAX_CACHED_PICS]; +int menu_numcachepics; + +byte menuplyr_pixels[4096]; + +static int GL_LoadPicTexture (qpic_t *pic) +{ + return GL_LoadTexture ("", pic->width, pic->height, pic->data, qfalse, GU_NEAREST, 0); +} + +qpic_t *Draw_PicFromWad (char *name) +{ + qpic_t *p; + glpic_t *gl; + + p = static_cast(W_GetLumpName (name)); + gl = (glpic_t *)p->data; + gl->index = GL_LoadPicTexture (p); + + return p; +} + + +/* +================ +Draw_CachePic +================ +*/ +qpic_t *Draw_CachePic (char *path) +{ + cachepic_t *pic; + int i; + qpic_t *dat; + glpic_t *gl; + char str[128]; + + strcpy (str, path); + 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, str); + +// +// load the pic from disk +// + + int index = loadtextureimage (str, 0, 0, qfalse, GU_LINEAR); + if(index) + { + pic->pic.width = gltextures[index].original_width; + pic->pic.height = gltextures[index].original_height; + + gltextures[index].islmp = qfalse; + gl = (glpic_t *)pic->pic.data; + gl->index = index; + + return &pic->pic; + } + + dat = (qpic_t *)COM_LoadTempFile (str); + if (!dat) + { + strcat (str, ".lmp"); + dat = (qpic_t *)COM_LoadTempFile (str); + if (!dat) + { + Con_Printf ("Draw_CachePic: failed to load file %s\n", str); + return NULL; + } + } + SwapPic (dat); + + + pic->pic.width = dat->width; + pic->pic.height = dat->height; + + gl = (glpic_t *)pic->pic.data; + gl->index = GL_LoadPicTexture (dat); + + gltextures[gl->index].islmp = qtrue; + return &pic->pic; +} + +/* +================ +Draw_CachePic +================ +*/ +qpic_t *Draw_CacheImg (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 +// + + int index = loadtextureimage (path, 0, 0, qfalse, GU_LINEAR); + if(index != -1) + { + pic->pic.width = gltextures[index].original_width; + pic->pic.height = gltextures[index].original_height; + + gl = (glpic_t *)pic->pic.data; + gl->index = index; + + return &pic->pic; + } + + dat = (qpic_t *)COM_LoadTempFile (path); + if (!dat) + dat = (qpic_t *)COM_LoadTempFile ("gfx/error.lmp"); + 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; + gl->index = GL_LoadPicTexture (dat); + + return &pic->pic; +} + + +byte nontexdt[8][8] = +{ + {0x43,0x43,0x43,0x43, 0x53,0x53,0x53,0x53}, + {0x43,0x43,0x43,0x43, 0x53,0x53,0x53,0x53}, + {0x43,0x43,0x43,0x43, 0x53,0x53,0x53,0x53}, + {0x43,0x43,0x43,0x43, 0x53,0x53,0x53,0x53}, + + {0x53,0x53,0x53,0x53, 0x43,0x43,0x43,0x43}, + {0x53,0x53,0x53,0x53, 0x43,0x43,0x43,0x43}, + {0x53,0x53,0x53,0x53, 0x43,0x43,0x43,0x43}, + {0x53,0x53,0x53,0x53, 0x43,0x43,0x43,0x43}, + +}; + +/* +================ +R_CreateDlightImage +from Quake3 +================ +*/ +#define DLIGHT_SIZE 16 +static void R_CreateDlightImage( void ) +{ + int x,y; + byte data[DLIGHT_SIZE][DLIGHT_SIZE]; + int b; + + // make a centered inverse-square falloff blob for dynamic lighting + for (x=0 ; x 255) + { + + b = 255; + } + else if ( b < 75 ) + { + b = 0; + } + + data[y][x] = b; + } + } + ref_texture = GL_LoadTexture ("reftexture", DLIGHT_SIZE, DLIGHT_SIZE, (byte*)data, qfalse, GU_LINEAR, 0); +} + +/* +=============== +Draw_Init +=============== +*/ +void Draw_Init (void) +{ + byte *detail, *uw; + int i; + + // 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 + + nonetexture = GL_LoadTexture ("nonetexture", 8, 8, (byte*)nontexdt, qfalse, GU_NEAREST, 0); + R_CreateDlightImage(); + + // now turn them into textures + char_texture = loadtextureimage ("gfx/charset", 0, 0, qfalse, GU_NEAREST); + if (char_texture == 0)// did not find a matching TGA... + Sys_Error ("Could not load charset, make sure you have every folder and file installed properly\nDouble check that all of your files are in their correct places\nAnd that you have installed the game properly.\nRefer to the readme.txt file for help\n"); + + detail_texture = loadtextureimage ("gfx/detail", 0, 0, qfalse, GU_LINEAR); + + if (detail_texture == 0)// did not find a matching TGA... + { + detail = static_cast(COM_LoadTempFile ("gfx/detail.lmp")); + if (!detail) + { + //Con_Printf ("Couldn't load gfx/detail \n"); FIXME no point? + detail_texture = nonetexture; + } + else + detail_texture = GL_LoadTexture ("Detail", 256, 256, detail, qfalse, GU_LINEAR, 3); + } + + underwater_texture = loadtextureimage ("gfx/uwater", 0, 0, qfalse, GU_LINEAR); + if (underwater_texture == 0)// did not find a matching TGA... + { + + uw = static_cast(COM_LoadTempFile ("gfx/uwater.lmp")); + if (!uw) + { + Con_Printf ("Couldn't load gfx/uwater \n"); + underwater_texture = nonetexture; + } + else + underwater_texture = GL_LoadTexture ("Underwater", 256, 256, uw, qfalse, GU_LINEAR, 0); + } + + sniper_scope = Draw_CachePic ("gfx/hud/scope"); + + zombie_skins[0][0] = loadtextureimage("/textures/ai/z0",0,0,qfalse,GU_LINEAR); + zombie_skins[0][1] = loadtextureimage("/textures/ai/z0",0,0,qfalse,GU_LINEAR); + zombie_skins[1][0] = loadtextureimage("/textures/ai/z0",0,0,qfalse,GU_LINEAR); + zombie_skins[1][1] = loadtextureimage("/textures/ai/z0",0,0,qfalse,GU_LINEAR); + + Clear_LoadingFill (); +} + + + +/* +================ +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; + + if (num == 32) + return; // space + + num &= 255; + + if (y <= -8) + return; // totally off screen + + row = num>>4; + col = num&15; + + GL_Bind (char_texture); + + struct vertex + { + short u, v; + short x, y, z; + }; + + vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2)); + + vertices[0].u = col * 8; + vertices[0].v = row * 8; + vertices[0].x = x; + vertices[0].y = y; + vertices[0].z = 0; + + vertices[1].u = (col + 1) * 8; + vertices[1].v = (row + 1) * 8; + vertices[1].x = x + 8; + vertices[1].y = y + 8; + vertices[1].z = 0; + + sceGuDrawArray(GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices); +} + + +/* +================ +Draw_CharacterRGBA + +This is the same as Draw_Character, but with RGBA color codes. +- MotoLegacy +================ +*/ +extern cvar_t scr_coloredtext; +void Draw_CharacterRGBA(int x, int y, int num, float r, float g, float b, float a) +{ + int row, col; + + if (num == 32) + return; // space + + num &= 255; + + if (y <= -8) + return; // totally off screen + + row = num>>4; + col = num&15; + + GL_Bind (char_texture); + + if (scr_coloredtext.value) + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); + + struct vertex + { + short u, v; + short x, y, z; + }; + + vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2)); + + vertices[0].u = col * 8; + vertices[0].v = row * 8; + vertices[0].x = x; + vertices[0].y = y; + vertices[0].z = 0; + + vertices[1].u = (col + 1) * 8; + vertices[1].v = (row + 1) * 8; + vertices[1].x = x + 8; + vertices[1].y = y + 8; + vertices[1].z = 0; + + sceGuColor(GU_COLOR(r/255, g/255, b/255, a/255)); + sceGuDrawArray(GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices); + + if (scr_coloredtext.value) + sceGuTexFunc(GU_TFX_REPLACE , GU_TCC_RGBA); +} + +/* +================ +Draw_String +================ +*/ +void Draw_String (int x, int y, char *str) +{ + while (*str) + { + Draw_Character (x, y, *str); + str++; + x += 8; + } +} + +/* +============= +Draw_AlphaPic +============= +*/ +void Draw_AlphaPic (int x, int y, qpic_t *pic, float alpha) +{ + if (alpha != 1.0f) + { + sceGuTexFunc(GU_TFX_DECAL, GU_TCC_RGBA); + } + + glpic_t *gl; + + gl = (glpic_t *)pic->data; + if (!gl->index) + GL_Bind (nonetexture); + else + GL_Bind (gl->index); + + struct vertex + { + short u, v; + short x, y, z; + }; + + vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2)); + + vertices[0].u = 0; + vertices[0].v = 0; + vertices[0].x = x; + vertices[0].y = y; + vertices[0].z = 0; + + const gltexture_t& glt = gltextures[gl->index]; + if (gltextures[gl->index].islmp) + { + vertices[1].u = glt.original_width; + vertices[1].v = glt.original_height; + } + else + { + vertices[1].u = glt.width; + vertices[1].v = glt.height; + } + vertices[1].x = x + pic->width; + vertices[1].y = y + pic->height; + vertices[1].z = 0; + + sceGuColor(GU_RGBA(0xff, 0xff, 0xff, static_cast(alpha * 255.0f))); + + sceGuDrawArray( + GU_SPRITES, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + 2, 0, vertices); + + sceGuColor(0xffffffff); + + if (alpha != 1.0f) + { + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + } +} + + +/* +============= +Draw_ColorPic +============= +*/ +void Draw_ColorPic (int x, int y, qpic_t *pic, float r, float g , float b, float a) +{ + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + + glpic_t *gl; + + gl = (glpic_t *)pic->data; + if (!gl->index) + GL_Bind (nonetexture); + else + GL_Bind (gl->index); + + struct vertex + { + short u, v; + short x, y, z; + }; + + vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2)); + + vertices[0].u = 0; + vertices[0].v = 0; + + vertices[0].x = x; + vertices[0].y = y; + vertices[0].z = 0; + + const gltexture_t& glt = gltextures[gl->index]; + if (gltextures[gl->index].islmp) + { + vertices[1].u = glt.original_width; + vertices[1].v = glt.original_height; + } + else + { + vertices[1].u = glt.width; + vertices[1].v = glt.height; + } + + vertices[1].x = x + pic->width; + vertices[1].y = y + pic->height; + vertices[1].z = 0; + + sceGuColor(GU_RGBA( + static_cast(r), + static_cast(g), + static_cast(b), + static_cast(a))); + + sceGuDrawArray( + GU_SPRITES, + GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, + 2, 0, vertices); + + sceGuColor(0xffffffff); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); +} + +/* +============= +Draw_Pic +============= +*/ +void Draw_Pic (int x, int y, qpic_t *pic) +{ + Draw_AlphaPic(x, y, pic, 1.0f); +} + +/* +============= +Draw_StretchPic +============= +*/ +void Draw_StretchPic (int x, int y, qpic_t *pic, int x_value, int y_value) +{ + + glpic_t *gl; + + gl = (glpic_t *)pic->data; + if (!gl->index) + GL_Bind (nonetexture); + else + GL_Bind (gl->index); + + struct vertex + { + unsigned short u, v; + short x, y, z; + }; + + + vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2)); + const gltexture_t& glt = gltextures[gl->index]; + vertices[0].u = 0; + vertices[0].v = 0; + vertices[0].x = x; + vertices[0].y = y; + vertices[0].z = 0; + + if (gltextures[gl->index].islmp) + { + vertices[1].u = glt.original_width; + vertices[1].v = glt.original_height; + } + else + { + vertices[1].u = glt.width; + vertices[1].v = glt.height; + } + vertices[1].x = x + x_value; + vertices[1].y = y + y_value; + vertices[1].z = 0; + + sceGuDrawArray(GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices); + +} +/* +============= +Draw_TransPic +============= +*/ +void Draw_TransPic (int x, int y, qpic_t *pic) +{ + + 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, c; + unsigned trans[64*64]; + int p; + + c = pic->width * pic->height; + + for(v = 0; v < c ; v++) + { + p = menuplyr_pixels[v]; + if (p == 255) + trans[v] = p; + else + trans[v] = translation[p]; + } + int translate_texture = GL_LoadTexture ("Player_Trl", pic->width, pic->height, (const byte*)trans, qtrue, GU_LINEAR, 0); + + GL_Bind (translate_texture); + + struct vertex + { + short u, v; + short x, y, z; + }; + + vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2)); + + vertices[0].u = 0; + vertices[0].v = 0; + vertices[0].x = x; + vertices[0].y = y; + vertices[0].z = 0; + + vertices[1].u = 64; + vertices[1].v = 64; + vertices[1].x = x + pic->width; + vertices[1].y = y + pic->height; + vertices[1].z = 0; + + sceGuDrawArray(GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices); + + GL_UnloadTexture(translate_texture); +} + + +/* +================ +Draw_ConsoleBackground + +================ +*/ +void Draw_ConsoleBackground (int lines) +{ + //int y = (vid.height * 3) >> 2; + + if (con_forcedup) + Draw_Fill(0, 0, vid.width, lines, 0); + else + Draw_Fill(0, 0, vid.width, lines, 0); + + + +} +/* +================ +Draw_LoadingFill +By Crow_bar +================ +*/ +void Draw_LoadingFill(void) +{ + if(!loading_num_step) + return; + + int size = 8; + int max_step = 350; + int x = (vid.width / 2) - (max_step / 2); + int y = vid.height - (size/ 2) - 10; + int l; + char str[64]; + + + if(loading_cur_step > loading_num_step) + loading_cur_step = loading_num_step; + + float loadsize = loading_cur_step * (max_step / loading_num_step); + Draw_FillByColor (x - 2, y - 2, max_step + 4, size + 4, GU_RGBA(255, 255, 255, 200)); + Draw_FillByColor (x, y, loadsize, size, GU_RGBA(0, 0, 0, 200)); + + strcpy (str, "Loading: "); + strcat (str, loading_name); + l = strlen (str); + Draw_String((vid.width - l*8)/2, y, str); +} + +void Clear_LoadingFill (void) +{ + //it is end loading + loading_cur_step = 0; + loading_num_step = 0; + memset(loading_name, 0, sizeof(loading_name)); +} + +/* +============= +Draw_FillByColor + +Fills a box of pixels with a single color +============= +*/ +void Draw_FillByColor (int x, int y, int w, int h, unsigned int c) +{ + struct vertex + { + short x, y, z; + }; + + vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2)); + + vertices[0].x = x; + vertices[0].y = y; + vertices[0].z = 0; + + vertices[1].x = x + w; + vertices[1].y = y + h; + vertices[1].z = 0; + + sceGuDisable(GU_TEXTURE_2D); + sceGuColor(c); + sceGuDrawArray(GU_SPRITES, GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices); + sceGuColor(0xffffffff); + sceGuEnable(GU_TEXTURE_2D); +} + +void Draw_Fill (int x, int y, int w, int h, int c) +{ + struct vertex + { + short x, y, z; + }; + + vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2)); + + vertices[0].x = x; + vertices[0].y = y; + vertices[0].z = 0; + + vertices[1].x = x + w; + vertices[1].y = y + h; + vertices[1].z = 0; + + sceGuDisable(GU_TEXTURE_2D); + sceGuColor(GU_RGBA(host_basepal[c*3], host_basepal[c*3+1], host_basepal[c*3+2], 0xff)); + sceGuDrawArray(GU_SPRITES, GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices); + sceGuColor(0xffffffff); + sceGuEnable(GU_TEXTURE_2D); +} + +byte *StringToRGB (char *s) +{ + byte *col; + static byte rgb[4]; + + Cmd_TokenizeString (s); + if (Cmd_Argc() == 3) + { + rgb[0] = (byte)Q_atoi(Cmd_Argv(0)); + rgb[1] = (byte)Q_atoi(Cmd_Argv(1)); + rgb[2] = (byte)Q_atoi(Cmd_Argv(2)); + } + else + { + col = (byte *)&d_8to24table[(byte)Q_atoi(s)]; + rgb[0] = col[0]; + rgb[1] = col[1]; + rgb[2] = col[2]; + } + rgb[3] = 255; + + return rgb; +} + +extern "C" cvar_t crosshair; +extern cvar_t crosshair; +extern qboolean croshhairmoving; +//extern cvar_t cl_zoom; +extern qpic_t *hitmark; +double Hitmark_Time, crosshair_spread_time; +float cur_spread; +float crosshair_offset_step; + +int CrossHairWeapon (void) +{ + int i; + switch(cl.stats[STAT_ACTIVEWEAPON]) + { + case W_COLT: + case W_BIATCH: + i = 15; + break; + case W_KAR: + case W_ARMAGEDDON: + i = 50; + break; + case W_THOMPSON: + case W_GIBS: + i = 10; + break; + case W_357: + case W_KILLU: + i = 10; + break; + case W_BAR: + case W_WIDOW: + i = 10; + break; + case W_BROWNING: + case W_ACCELERATOR: + i = 20; + break; + case W_DB: + case W_BORE: + i = 25; + break; + case W_FG: + case W_IMPELLER: + i = 10; + break; + case W_GEWEHR: + case W_COMPRESSOR: + i = 10; + break; + case W_KAR_SCOPE: + case W_HEADCRACKER: + i = 50; + break; + case W_M1: + case W_M1000: + i = 10; + break; + case W_M1A1: + case W_WIDDER: + i = 10; + break; + case W_MP40: + case W_AFTERBURNER: + i = 10; + break; + case W_MG: + case W_BARRACUDA: + i = 20; + break; + case W_PANZER: + case W_LONGINUS: + i = 0; + break; + case W_PPSH: + case W_REAPER: + i = 10; + break; + case W_PTRS: + case W_PENETRATOR: + i = 50; + break; + case W_RAY: + case W_PORTER: + i = 10; + break; + case W_SAWNOFF: + case W_SNUFF: + i = 30; + break; + case W_STG: + case W_SPATZ: + i = 10; + break; + case W_TRENCH: + case W_GUT: + i = 25; + break; + case W_TYPE: + case W_SAMURAI: + i = 10; + break; + case W_MP5: + i = 10; + break; + case W_TESLA: + i = 0; + break; + default: + i = 0; + break; + } + + if (cl.perks & 64) + i *= 0.65; + + return i; +} +int CrossHairMaxSpread (void) +{ + int i; + switch(cl.stats[STAT_ACTIVEWEAPON]) + { + case W_COLT: + case W_BIATCH: + i = 30; + break; + case W_KAR: + case W_ARMAGEDDON: + i = 75; + break; + case W_THOMPSON: + case W_GIBS: + i = 25; + break; + case W_357: + case W_KILLU: + i = 20; + break; + case W_BAR: + case W_WIDOW: + i = 35; + break; + case W_BROWNING: + case W_ACCELERATOR: + i = 50; + break; + case W_DB: + case W_BORE: + i = 25; + break; + case W_FG: + case W_IMPELLER: + i = 40; + break; + case W_GEWEHR: + case W_COMPRESSOR: + i = 35; + break; + case W_KAR_SCOPE: + case W_HEADCRACKER: + i = 75; + break; + case W_M1: + case W_M1000: + i = 35; + break; + case W_M1A1: + case W_WIDDER: + i = 35; + break; + case W_MP40: + case W_AFTERBURNER: + i = 25; + break; + case W_MG: + case W_BARRACUDA: + i = 50; + break; + case W_PANZER: + case W_LONGINUS: + i = 0; + break; + case W_PPSH: + case W_REAPER: + i = 25; + break; + case W_PTRS: + case W_PENETRATOR: + i = 75; + break; + case W_RAY: + case W_PORTER: + i = 20; + break; + case W_SAWNOFF: + case W_SNUFF: + i = 30; + break; + case W_STG: + case W_SPATZ: + i = 35; + break; + case W_TRENCH: + case W_GUT: + i = 25; + break; + case W_TYPE: + case W_SAMURAI: + i = 25; + break; + case W_MP5: + i = 25; + break; + case W_TESLA: + i = 0; + break; + default: + i = 0; + break; + } + + if (cl.perks & 64) + i *= 0.65; + + return i; +} + +/* +================ +Draw_Crosshair +================ +*/ + +extern float crosshair_opacity; +void Draw_Crosshair (void) +{ + if (cl.stats[STAT_HEALTH] < 20) + return; + + if (!crosshair_opacity) + crosshair_opacity = 255; + + float col; + + if (sv_player->v.facingenemy == 1) { + col = 0; + } else { + col = 255; + } + + // crosshair moving + if (crosshair_spread_time > sv.time && crosshair_spread_time) + { + cur_spread = cur_spread + 10; + crosshair_opacity = 128; + + if (cur_spread >= CrossHairMaxSpread()) + cur_spread = CrossHairMaxSpread(); + } + // crosshair not moving + else if (crosshair_spread_time < sv.time && crosshair_spread_time) + { + cur_spread = cur_spread - 4; + crosshair_opacity = 255; + + if (cur_spread <= 0) { + cur_spread = 0; + crosshair_spread_time = 0; + } + } + + if (cl.stats[STAT_ACTIVEWEAPON] == W_M2 || cl.stats[STAT_ACTIVEWEAPON] == W_TESLA || cl.stats[STAT_ACTIVEWEAPON] == W_DG3) + { + Draw_CharacterRGBA((vid.width)/2-4, (vid.height)/2, 'O', 255, col, col, crosshair_opacity); + } + else if (crosshair.value == 1 && cl.stats[STAT_ZOOM] != 1 && cl.stats[STAT_ZOOM] != 2 && cl.stats[STAT_ACTIVEWEAPON] != W_PANZER) + { + int x_value, y_value; + int crosshair_offset = CrossHairWeapon() + cur_spread; + if (CrossHairMaxSpread() < crosshair_offset || croshhairmoving) + crosshair_offset = CrossHairMaxSpread(); + + if (sv_player->v.view_ofs[2] == 8) { + crosshair_offset *= 0.80; + } else if (sv_player->v.view_ofs[2] == -10) { + crosshair_offset *= 0.65; + } + + crosshair_offset_step += (crosshair_offset - crosshair_offset_step) * 0.5; + + x_value = (vid.width - 8)/2 - crosshair_offset_step; + y_value = (vid.height - 8)/2; + Draw_CharacterRGBA(x_value, y_value, 158, 255, col, col, crosshair_opacity); + + x_value = (vid.width - 8)/2 + crosshair_offset_step; + y_value = (vid.height - 8)/2; + Draw_CharacterRGBA(x_value, y_value, 158, 255, col, col, crosshair_opacity); + + x_value = (vid.width - 8)/2; + y_value = (vid.height - 8)/2 - crosshair_offset_step; + Draw_CharacterRGBA(x_value, y_value, 157, 255, col, col, crosshair_opacity); + + x_value = (vid.width - 8)/2; + y_value = (vid.height - 8)/2 + crosshair_offset_step; + Draw_CharacterRGBA(x_value, y_value, 157, 255, col, col, crosshair_opacity); + } + else if (crosshair.value && cl.stats[STAT_ZOOM] != 1 && cl.stats[STAT_ZOOM] != 2) + Draw_CharacterRGBA((vid.width - 8)/2, (vid.height - 8)/2, '.', 255, col, col, crosshair_opacity); + if (cl.stats[STAT_ZOOM] == 2) + Draw_Pic (0, 0, sniper_scope); + if (Hitmark_Time > sv.time) + Draw_Pic ((vid.width - hitmark->width)/2,(vid.height - hitmark->height)/2, hitmark); +} + +/* +================ +Draw_ColoredString +================ +*/ + +static int HexToInt(char c) +{ + if (isdigit(c)) + return c - '0'; + else if (c >= 'a' && c <= 'f') + return 10 + c - 'a'; + else if (c >= 'A' && c <= 'F') + return 10 + c - 'A'; + else + return -1; +} + +// Creds to UP Team for scale code - Moto +void Draw_ColoredString(int x, int y, char *text, float r, float g, float b, float a, int scale) +{ + int num; + qboolean white = qtrue; + + if (y <= -8) + return; // totally off screen + + if (!*text) + return; + + GL_Bind (char_texture); + + + + if (scr_coloredtext.value) + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); + + for ( ; *text; text++) + { + + // MotoLegacy - Added 0-255 RGBA support (5/26/2020) + sceGuColor(GU_COLOR(r/255, g/255, b/255, a/255)); + + num = *text & 255; + + if (num != 32 && num != (32 | 128)) + { + float frow, fcol; + + frow = num>>4; + fcol = num&15; + + struct vertex + { + short u, v; + short x, y, z; + }; + + vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2)); + + vertices[0].u = fcol * 8; + vertices[0].v = frow * 8; + vertices[0].x = x; + vertices[0].y = y; + vertices[0].z = 0; + + vertices[1].u = (fcol + 1) * 8; + vertices[1].v = (frow + 1) * 8; + vertices[1].x = x + (8*scale); + vertices[1].y = y + (8*scale); + vertices[1].z = 0; + + sceGuDrawArray(GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices); + } + x += 8*scale; + } + + if (!white) + sceGuColor(GU_COLOR(1,1,1,1)); + + if (scr_coloredtext.value) + sceGuTexFunc(GU_TFX_REPLACE , GU_TCC_RGBA); +} + +//============================================================================= +#ifdef NAA +//font.c : font.raw +// bin2c font.raw font.c font +//font.c +extern"C" +{ + #include "font.c" +} +,.,., +static int fontwidthtab[128] = +{ + 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, + 10, 10, 10, 10, + + 10, 6, 8, 10, // ! " # + 10, 10, 10, 6, // $ % & ' + 10, 10, 10, 10, // ( ) * + + 6, 10, 6, 10, // , - . / + + 10, 10, 10, 10, // 0 1 2 3 + 10, 10, 10, 10, // 6 5 8 7 + 10, 10, 6, 6, // 10 9 : ; + 10, 10, 10, 10, // < = > ? + + 16, 10, 10, 10, // @ A B C + 10, 10, 10, 10, // D E F G + 10, 6, 8, 10, // H I J K + 8, 10, 10, 10, // L M N O + + 10, 10, 10, 10, // P Q R S + 10, 10, 10, 12, // T U V W + 10, 10, 10, 10, // X Y Z [ + 10, 10, 8, 10, // \ ] ^ _ + + 6, 8, 8, 8, // ` a b c + 8, 8, 6, 8, // d e f g + 8, 6, 6, 8, // h i j k + 6, 10, 8, 8, // l m n o + + 8, 8, 8, 8, // p q r s + 8, 8, 8, 12, // t u v w + 8, 8, 8, 10, // x y z { + 8, 10, 8, 12 // | } ~ +}; + +void Draw_FrontText(const char* text, int x, int y, unsigned int color, int fw) //Crow_bar +{ + int len = (int)strlen(text); + + if(!len) + { + return; + } + + // Set the texture mode. + sceGuTexMode(GU_PSM_8888, 0, 0, 0); + sceGuTexImage(0, 256, 128, 256, font); + sceGuTexFilter(GU_NEAREST, GU_NEAREST); + + sceGuShadeModel(GU_SMOOTH); + + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + sceGuDepthMask(GU_TRUE); + + typedef struct + { + float s, t; + unsigned int c; + float x, y, z; + } vertex; + + vertex* const v = static_cast(sceGuGetMemory(sizeof(vertex) * 2 * len)); + + int i; + for(i = 0; i < len; i++) + { + unsigned char c = (unsigned char)text[i]; + if(c < 32) + { + c = 0; + } + else if(c >= 128) + { + c = 0; + } + + int tx = (c & 0x0F) << 4; + int ty = (c & 0xF0); + + vertex* v0 = &v[i*2+0]; + vertex* v1 = &v[i*2+1]; + + v0->s = (float)(tx + (fw ? ((16 - fw) >> 1) : ((16 - fontwidthtab[c]) >> 1))); + v0->t = (float)(ty); + v0->c = color; + v0->x = (float)(x); + v0->y = (float)(y); + v0->z = 0.0f; + + v1->s = (float)(tx + 16 - (fw ? ((16 - fw) >> 1) : ((16 - fontwidthtab[c]) >> 1))); + v1->t = (float)(ty + 16); + v1->c = color; + v1->x = (float)(x + (fw ? fw : fontwidthtab[c])); + v1->y = (float)(y + 16); + v1->z = 0.0f; + + x += (fw ? fw : fontwidthtab[c]); + } + sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_2D, len * 2, 0, v); + + sceGuShadeModel(GU_FLAT); + + sceGuDepthMask(GU_FALSE); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); +} + +void Draw_FrontTest (void) +{ + Draw_FrontText("red", 0, 0, 0xFF0000FF, 0); + Draw_FrontText("green", 0, 16, 0xFF00FF00, 0); + Draw_FrontText("blue", 0, 32, 0xFFFF0000, 0); + + Draw_FrontText("free char width", 0, 64, 0xFFFFFFFF, 0); + Draw_FrontText("block char width 10", 0, 80, 0xFFFFFFFF, 10); + Draw_FrontText("block char width 16", 0, 96, 0xFFFFFFFF, 16); + + Draw_FrontText("opacity 100%", 0, 128, 0xFFFFFFFF, 0); + Draw_FrontText("opacity 50%", 0, 144, 0x7FFFFFFF, 0); + Draw_FrontText("opacity 10%", 0, 160, 0x18FFFFFF, 0); + + Draw_FrontText("I'm Crow_bar", 2, 194, 0x40FFFFFF, 0); + Draw_FrontText("I'm Crow_bar", 0, 192, 0xFFFFFFFF, 0); + + static float t = 0.0f; + t += 0.1f; + + unsigned int c = 0xFF000000 | + (unsigned int)((sinf(t * 0.393f + 0.086f) / 2.0f + 0.5f) * 255.0f) << 16 | + (unsigned int)((sinf(t * 0.444f + 0.854f) / 2.0f + 0.5f) * 255.0f) << 8 | + (unsigned int)((sinf(t * 0.117f + 1.337f) / 2.0f + 0.5f) * 255.0f) << 0; + + Draw_FrontText("Hello World from pspdev", 0, 224, c, 0); +} +#endif +//============================================================================= + +/* +================ +Draw_FadeScreen + +================ +*/ +void Draw_FadeScreen (void) +{ + struct vertex + { + short x, y, z; + }; + + vertex* const vertices = static_cast(sceGuGetMemory(sizeof(vertex) * 2)); + + vertices[0].x = 0; + vertices[0].y = 0; + vertices[0].z = 0; + vertices[1].x = vid.width; + vertices[1].y = vid.height; + vertices[1].z = 0; + + sceGuDisable(GU_TEXTURE_2D); + + sceGuColor(GU_RGBA(0, 0, 0, 0x80)); + sceGuDrawArray( + GU_SPRITES, + GU_VERTEX_16BIT | GU_TRANSFORM_2D, + 2, 0, vertices); + + sceGuEnable(GU_TEXTURE_2D); +} + +//============================================================================= + +/* +================ +GL_Set2D + +Setup as if the screen was 320*200 +================ +*/ +void GL_Set2D (void) +{ + sceGuViewport (glx, gly, glwidth, glheight); + sceGuScissor(0, 0, glwidth, glheight); + + sceGuEnable(GU_BLEND); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); +} + + +/* +================ +swizzle_slow +================ +*/ +void swizzle(u8* out, const u8* in, unsigned int width, unsigned int height) +{ + unsigned int i,j; + unsigned int rowblocks = (width / 16); + + for (j = 0; j < height; ++j) + { + for (i = 0; i < width; ++i) + { + unsigned int blockx = i / 16; + unsigned int blocky = j / 8; + + unsigned int x = (i - blockx*16); + unsigned int y = (j - blocky*8); + unsigned int block_index = blockx + ((blocky) * rowblocks); + unsigned int block_address = block_index * 16 * 8; + + out[block_address + x + y * 16] = in[i+j*width]; + } + } +} + +/* +================ +swizzle_fast +================ +*/ +static void swizzle_fast(u8* out, const u8* in, unsigned int width, unsigned int height) +{ + unsigned int blockx, blocky; + unsigned int j; + + unsigned int width_blocks = (width / 16); + unsigned int height_blocks = (height / 8); + + unsigned int src_pitch = (width-16)/4; + unsigned int src_row = width * 8; + + const u8* ysrc = in; + u32* dst = (u32*)out; + + for (blocky = 0; blocky < height_blocks; ++blocky) + { + const u8* xsrc = ysrc; + for (blockx = 0; blockx < width_blocks; ++blockx) + { + const u32* src = (u32*)xsrc; + for (j = 0; j < 8; ++j) + { + *(dst++) = *(src++); + *(dst++) = *(src++); + *(dst++) = *(src++); + *(dst++) = *(src++); + src += src_pitch; + } + xsrc += 16; + } + ysrc += src_row; + } +} + + + +/* +================ +GL_ResampleTexture for 8 bit +================ +*/ +void GL_ResampleTexture(const byte *in, int inwidth, int inheight, unsigned char *out, int outwidth, int outheight) +{ + const unsigned int fracstep = inwidth * 0x10000 / outwidth; + for (int i = 0; i < outheight ; ++i, out += outwidth) + { + const byte* inrow = in + inwidth * (i * inheight / outheight); + unsigned int frac = fracstep >> 1; + for (int j = 0; j < outwidth; ++j, frac += fracstep) + { + out[j] = inrow[frac >> 16]; + } + } +} + +/* +================ +GL_Upload8 +================ +*/ +void GL_Upload8(int texture_index, const byte *data, int width, int height) +{ + if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false) + { + Sys_Error("Invalid texture index %d", texture_index); + } + + const gltexture_t& texture = gltextures[texture_index]; + + // Check that the texture matches. + if ((width != texture.original_width) != (height != texture.original_height)) + { + Sys_Error("Attempting to upload a texture which doesn't match the destination"); + } + + // Create a temporary buffer to use as a source for swizzling. + std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0); + std::vector unswizzled(buffer_size); + + + if (texture.mipmaps > 0) { + int size_incr = buffer_size/4; + for (int i= 1;i <= texture.mipmaps;i++) { + buffer_size += size_incr; + size_incr = size_incr/4; + } + } + + // Do we need to resize? + if (texture.stretch_to_power_of_two) + { + // Resize. + GL_ResampleTexture(data, width, height, &unswizzled[0], texture.width, texture.height); + } + else + { + // Straight copy. + for (int y = 0; y < height; ++y) + { + const byte* const src = data + (y * width); + byte* const dst = &unswizzled[y * texture.width]; + memcpy(dst, src, width); + } + } + + // Swizzle to system RAM. + swizzle_fast(texture.ram, &unswizzled[0], texture.width, texture.height); + + if (texture.mipmaps > 0) + { + int size = (texture.width * texture.height); + int offset = size; + int div = 2; + + for (int i = 1; i <= texture.mipmaps;i++) + { + GL_ResampleTexture(data, width, height, &unswizzled[0], texture.width/div, texture.height/div); + swizzle_fast(texture.ram+offset, &unswizzled[0], texture.width/div, texture.height/div); + offset += size/(div*div); + div *=2; + } + } + + unswizzled.clear(); + + // Copy to VRAM? + if (texture.vram) + { + // Copy. + memcpy(texture.vram, texture.ram, buffer_size); + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.vram, buffer_size); + } + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.ram, buffer_size); +} + +/* +================ +GL_Upload8_A +================ +*/ +void GL_Upload8_A(int texture_index, const byte *data, int width, int height) +{ + if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false) + { + Sys_Error("Invalid texture index %d", texture_index); + } + + const gltexture_t& texture = gltextures[texture_index]; + + // Check that the texture matches. + if ((width != texture.original_width) != (height != texture.original_height)) + { + Sys_Error("Attempting to upload a texture which doesn't match the destination"); + } + + // Create a temporary buffer to use as a source for swizzling. + const std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0); + memcpy((void *) texture.ram, (void *) data, buffer_size); + + + // Copy to VRAM? + if (texture.vram) + { + // Copy. + memcpy(texture.vram, texture.ram, buffer_size); + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.vram, buffer_size); + } + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.ram, buffer_size); +} +/* +================ +GL_Upload16_A +================ +*/ +void GL_Upload16_A(int texture_index, const byte *data, int width, int height) +{ + if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false) + { + Sys_Error("Invalid texture index %d", texture_index); + } + + const gltexture_t& texture = gltextures[texture_index]; + + // Check that the texture matches. + if ((width != texture.original_width) != (height != texture.original_height)) + { + Sys_Error("Attempting to upload a texture which doesn't match the destination"); + } + + // Create a temporary buffer to use as a source for swizzling. + const std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0); + memcpy((void *) texture.ram, (void *) data, buffer_size); + + // Copy to VRAM? + if (texture.vram) + { + // Copy. + memcpy(texture.vram, texture.ram, buffer_size); + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.vram, buffer_size); + } + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.ram, buffer_size); +} + +/* +======================================= +TextureConvector +======================================= +From DE2 by Christoph Arnold "charnold" +modify by Crow_bar +======================================= +*/ +void TextureConvector(unsigned char *in32, unsigned char *out16, int w, int h, int format) +{ + int texel; + + int size = w * h; + + for (texel = 0; texel < size; texel++) + { + if (format == GU_PSM_4444) + { + *(out16) = (*in32>>4) & 0x0f; in32++; // r + *(out16++) |= (*in32) & 0xf0; in32++; // g + *(out16) = (*in32>>4) & 0x0f; in32++; // b + *(out16++) |= (*in32) & 0xf0; in32++; // a + } + else if (format == GU_PSM_5650) + { + unsigned char r,g,b; + + r = (*in32>>3) & 0x1f; in32++; // r = 5 bit + g = (*in32>>2) & 0x3f; in32++; // g = 6 bit + b = (*in32>>3) & 0x1f; in32++; // b = 5 bit + in32++; // a = 0 bit + + *(out16) = r; // alle 5 bits von r auf lower 5 bits von out16 + *(out16++) |= (g<<5) & 0xe0; // lower 3 bits von g auf higher 3 bits von out16 + *(out16) = (g>>3) & 0x07; // higher 3 bits von g auf lower 3 bits von out16 + *(out16++) |= (b<<3) & 0xf8; // alle 5 bits von b auf higher 5 bits von out16 + + } + else if (format == GU_PSM_5551) + { + unsigned char r,g,b,a; + + r = (*in32>>3) & 0x1f; in32++; // r = 5 bit + g = (*in32>>3) & 0x1f; in32++; // g = 5 bit + b = (*in32>>3) & 0x1f; in32++; // b = 5 bit + a = (*in32>>7) & 0x01; in32++; // a = 1 bit + + *(out16) = r; // alle 5 bits von r auf lower 5 bits von out16 + *(out16++) |= (g<<5) & 0xe0; // lower 3 bits von g auf higher 3 bits von out16 + *(out16) = (g>>3) & 0x03; // higher 2 bits von g auf lower 2 bits von out16 + *(out16) |= (b<<2) & 0x7c; // alle 5 bits von b auf bits 3-7 von out16 + *(out16++) |= (a<<7) & 0x80; // 1 bit von a auf bit 8 von out16 + } + } +} + +/* +================ +GL_Upload16_B +================ +*/ +void GL_Upload32_16(int texture_index, const byte *data, int width, int height) +{ + if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false) + { + Sys_Error("Invalid texture index %d", texture_index); + } + + const gltexture_t& texture = gltextures[texture_index]; + + // Check that the texture matches. + if ((width != texture.original_width) != (height != texture.original_height)) + { + Sys_Error("Attempting to upload a texture which doesn't match the destination"); + } + + // Create a temporary buffer to use as a source for swizzling. + std::size_t buffer_sizesrc = GL_GetTexSize(-1, texture.width, texture.height, 4); + std::vector unswizzled(buffer_sizesrc); + + std::size_t buffer_sizedst = GL_GetTexSize(texture.format, texture.width, texture.height, 0); + std::vector bpp(buffer_sizesrc); + + + if (texture.stretch_to_power_of_two) + { + // Resize. + Image_Resample ((void*)data, width, height, &unswizzled[0], texture.width, texture.height, texture.bpp, int(r_restexf.value)); + } + else + { + // Straight copy. + for (int y = 0; y < height; ++y) + { + const byte* const src = data + (y * width * texture.bpp); + byte* const dst = &unswizzled[y * texture.width * texture.bpp]; + memcpy(dst, src, width * texture.bpp); + } + } + + TextureConvector((unsigned char*)&unswizzled[0], (unsigned char*)&bpp[0], texture.width, texture.height, texture.format); + unswizzled.clear(); + + // Swizzle to system RAM. + swizzle_fast(texture.ram, &bpp[0], texture.width * 2, texture.height); + bpp.clear(); + + + // Copy to VRAM? + if (texture.vram) + { + // Copy. + memcpy(texture.vram, texture.ram, buffer_sizedst); + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.vram, buffer_sizedst); + } + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.ram, buffer_sizedst); +} + + +/* +================ +GL_Upload16 +================ +*/ +void GL_Upload16(int texture_index, const byte *data, int width, int height) +{ + if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false) + { + Sys_Error("Invalid texture index %d", texture_index); + } + + const gltexture_t& texture = gltextures[texture_index]; + + // Check that the texture matches. + if ((width != texture.original_width) != (height != texture.original_height)) + { + Sys_Error("Attempting to upload a texture which doesn't match the destination"); + } + + // Create a temporary buffer to use as a source for swizzling. + const std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0); + std::vector unswizzled(buffer_size); + + + // Straight copy. + for (int y = 0; y < height; ++y) + { + const byte* const src = data + (y * width * texture.bpp); + byte* const dst = &unswizzled[y * texture.width * texture.bpp]; + memcpy(dst, src, width * texture.bpp); + } + + // Swizzle to system RAM. + swizzle_fast(texture.ram, &unswizzled[0], texture.width * texture.bpp, texture.height); + unswizzled.clear(); + + + // Copy to VRAM? + if (texture.vram) + { + // Copy. + memcpy(texture.vram, texture.ram, buffer_size); + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.vram, buffer_size); + } + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.ram, buffer_size); +} + +/* +=============== +GL_Upload24_A +=============== +*/ +void GL_Upload24_A(int texture_index, const byte *data, int width, int height) +{ + if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false) + { + Sys_Error("Invalid texture index %d", texture_index); + } + + const gltexture_t& texture = gltextures[texture_index]; + + // Check that the texture matches. + if ((width != texture.original_width) != (height != texture.original_height)) + { + Sys_Error("Attempting to upload a texture which doesn't match the destination"); + } + + // Create a temporary buffer to use as a source for swizzling. + const std::size_t buffer_size = GL_GetTexSize(-1, texture.width, texture.height, texture.bpp); + + //resize Crow_bar PSP port 24BIT RESAMPLER + if (texture.stretch_to_power_of_two) + { + Image_Resample ((void*)data, width, height, (void *) texture.ram, texture.width, texture.height, texture.bpp, int(r_restexf.value)); + } + else + { + memcpy((void *) texture.ram, (void *) data, buffer_size); + } + + + // Copy to VRAM? + if (texture.vram) + { + // Copy. + memcpy(texture.vram, texture.ram, buffer_size); + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.vram, buffer_size); + } + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.ram, buffer_size); +} + +/* +================ +GL_Upload24 +================ +*/ +void GL_Upload24(int texture_index, const byte *data, int width, int height) +{ + if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false) + { + Sys_Error("Invalid texture index %d", texture_index); + } + + const gltexture_t& texture = gltextures[texture_index]; + + // Check that the texture matches. + if ((width != texture.original_width) != (height != texture.original_height)) + { + Sys_Error("Attempting to upload a texture which doesn't match the destination"); + } + + // Create a temporary buffer to use as a source for swizzling. + std::size_t buffer_size = GL_GetTexSize(-1, texture.width, texture.height, texture.bpp); + std::vector unswizzled(buffer_size); + + if (texture.mipmaps > 0) + { + int size_incr = buffer_size/4; + + for (int i= 1;i <= texture.mipmaps;i++) + { + buffer_size += size_incr; + size_incr = size_incr/4; + } + } + + //32BIT resize and swizzler by Crow_bar PSP port + // Do we need to resize? + if (texture.stretch_to_power_of_two) + { + // Resize. + Image_Resample ((void*)data, width, height, &unswizzled[0], texture.width, texture.height, texture.bpp, int(r_restexf.value)); + } + else + { + // Straight copy. + for (int y = 0; y < height; ++y) + { + const byte* const src = data + (y * width * texture.bpp); + byte* const dst = &unswizzled[y * texture.width * texture.bpp]; + memcpy(dst, src, width * texture.bpp); + } + } + + // Swizzle to system RAM. + swizzle_fast(texture.ram, &unswizzled[0], texture.width * texture.bpp, texture.height); + + if (texture.mipmaps > 0) + { + int size = GL_GetTexSize(-1, texture.width, texture.height, texture.bpp); + int offset = size; + int div = 2; + + for (int i = 1; i <= texture.mipmaps;i++) + { + Image_Resample((void*)data, width, height, &unswizzled[0], + texture.width/div, texture.height/div, texture.bpp, int(r_restexf.value)); + swizzle_fast(texture.ram+offset, &unswizzled[0], (texture.width/div) * texture.bpp, texture.height/div); + offset += size/(div*div); + div *=2; + } + } + + unswizzled.clear(); + + // Copy to VRAM? + if (texture.vram) + { + // Copy. + memcpy(texture.vram, texture.ram, buffer_size); + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.vram, buffer_size); + } + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.ram, buffer_size); +} + +/* +=============== +GL_Upload32_A +=============== +*/ +void GL_Upload32_A(int texture_index, const byte *data, int width, int height) +{ + if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false) + { + Sys_Error("Invalid texture index %d", texture_index); + } + + gltexture_t& texture = gltextures[texture_index]; + + // Check that the texture matches. + if ((width != texture.original_width) != (height != texture.original_height)) + { + Sys_Error("Attempting to upload a texture which doesn't match the destination"); + } + + // Create a temporary buffer to use as a source for swizzling. + std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0); + + //resize Crow_bar PSP port 32BIT RESAMPLER + if (texture.stretch_to_power_of_two) + { + Image_Resample ((void*)data, width, height, (void *) texture.ram, texture.width, texture.height, texture.bpp, int(r_restexf.value)); + } + else + { + memcpy((void *) texture.ram, (void *) data, buffer_size); + } + + // Copy to VRAM? + if (texture.vram) + { + // Copy. + memcpy(texture.vram, texture.ram, buffer_size); + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.vram, buffer_size); + } + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.ram, buffer_size); +} + +/* +================ +GL_Upload32 +================ +*/ +void GL_Upload32(int texture_index, const byte *data, int width, int height) +{ + if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false) + { + Sys_Error("Invalid texture index %d", texture_index); + } + + //const + gltexture_t& texture = gltextures[texture_index]; + + // Check that the texture matches. + if ((width != texture.original_width) != (height != texture.original_height)) + { + Sys_Error("Attempting to upload a texture which doesn't match the destination"); + } + + // Create a temporary buffer to use as a source for swizzling. + std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0); + std::vector unswizzled(buffer_size); + + if (texture.mipmaps > 0) + { + int size_incr = buffer_size/4; + + for (int i= 1;i <= texture.mipmaps;i++) + { + buffer_size += size_incr; + size_incr = size_incr/4; + } + } + + //32BIT resize and swizzler by Crow_bar PSP port + // Do we need to resize? + if (texture.stretch_to_power_of_two) + { + // Resize. + Image_Resample ((void*)data, width, height, &unswizzled[0], texture.width, texture.height, texture.bpp, int(r_restexf.value)); + } + else + { + // Straight copy. + for (int y = 0; y < height; ++y) + { + const byte* const src = data + (y * width * texture.bpp); + byte* const dst = &unswizzled[y * texture.width * texture.bpp]; + memcpy(dst, src, width * texture.bpp); + } + } + + // Swizzle to system RAM. + swizzle_fast(texture.ram, &unswizzled[0], texture.width * texture.bpp, texture.height); + + + if (texture.mipmaps > 0) + { + int size = GL_GetTexSize(texture.format, texture.width, texture.height, 0); + int offset = size; + int div = 2; + + for (int i = 1; i <= texture.mipmaps;i++) + { + Image_Resample((void*)data, width, height, &unswizzled[0], + texture.width/div, texture.height/div, texture.bpp, int(r_restexf.value)); + swizzle_fast(texture.ram+offset, &unswizzled[0], (texture.width/div) * texture.bpp, texture.height/div); + offset += size/(div*div); + div *=2; + } + } + + unswizzled.clear(); + + // Copy to VRAM? + if (texture.vram) + { + // Copy. + memcpy(texture.vram, texture.ram, buffer_size); + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.vram, buffer_size); + } + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.ram, buffer_size); +} + +/* +================ +GL_UploadDXT +================ +*/ +void GL_UploadDXT(int texture_index, const byte *data, int width, int height) +{ + if ((texture_index < 0) || (texture_index >= MAX_GLTEXTURES) || gltextures_used[texture_index] == false) + { + Sys_Error("Invalid texture index %d", texture_index); + } + + const gltexture_t& texture = gltextures[texture_index]; + + // Check that the texture matches. + if ((width != texture.original_width) != (height != texture.original_height)) + { + Sys_Error("Attempting to upload a texture which doesn't match the destination"); + } + + // Create a temporary buffer to use as a source for swizzling. + std::size_t buffer_sizesrc = GL_GetTexSize(-1, texture.width, texture.height, texture.bpp); + std::vector unswizzled(buffer_sizesrc); + + // New compressed texture + std::size_t buffer_sizedst = GL_GetTexSize(texture.format, texture.width, texture.height, 0); + + if (texture.stretch_to_power_of_two) + { + // Resize. + Image_Resample ((void*)data, width, height, &unswizzled[0], texture.width, texture.height, texture.bpp, int(r_restexf.value)); + } + else + { + // Straight copy. + for (int y = 0; y < height; ++y) + { + const byte* const src = data + (y * width * texture.bpp); + byte* const dst = &unswizzled[y * texture.width * texture.bpp]; + memcpy(dst, src, width * texture.bpp); + } + } + + tx_compress_dxtn(texture.bpp, texture.width, texture.height,(const unsigned char *)&unswizzled[0], texture.format, (unsigned char *)texture.ram); + unswizzled.clear(); + // Copy to VRAM? + if (texture.vram) + { + // Copy. + memcpy(texture.vram, texture.ram, buffer_sizedst); + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.vram, buffer_sizedst); + } + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.ram, buffer_sizedst); +} + +static std::size_t round_up(std::size_t size) +{ + static const float denom = 1.0f / logf(2.0f); + const float logged = logf(size) * denom; + const float ceiling = ceilf(logged); + return 1 << static_cast(ceiling); +} + + +static std::size_t round_down(std::size_t size) +{ + static const float denom = 1.0f / logf(2.0f); + const float logged = logf(size) * denom; + const float floor = floorf(logged); + return 1 << static_cast(floor); +} + +/* +================ +GL_UnloadTexture +================ +*/ +void GL_UnloadTexture(int texture_index) +{ + + if (gltextures_used[texture_index] == true) + { + gltexture_t& texture = gltextures[texture_index]; + + Con_DPrintf("Unloading: %s,%d\n",texture.identifier, texture.bpp); + // Source. + strcpy(texture.identifier,""); + texture.original_width = 0; + texture.original_height = 0; + texture.stretch_to_power_of_two = qfalse; + + // Fill in the texture description. + texture.format = GU_PSM_T8; + texture.bpp = 0; + texture.filter = GU_LINEAR; + texture.width = 0; + texture.height = 0; + texture.mipmaps = 0; + texture.swizzle = 0; +#ifdef STATIC_PAL + memset(texture.palette, 0, sizeof(texture.palette)); +#else + if (texture.palette != NULL) + { + free(texture.palette); + texture.palette = NULL; + } + if (texture.palette_2 != NULL) + { + free(texture.palette_2); + texture.palette_2 = NULL; + } +#endif + texture.palette_active = qfalse; + if (texture.palette != NULL) + { + free(texture.palette); + texture.palette = NULL; + } + if (texture.palette_2 != NULL) + { + free(texture.palette_2); + texture.palette_2 = NULL; + } + + // Buffers. + if (texture.ram != NULL) + { + free(texture.ram); + texture.ram = NULL; + } + /*if (texture.vram != NULL) + { + vfree(texture.vram); + texture.vram = NULL; + } */ + + } + + gltextures_used[texture_index] = false; + numgltextures--; + +} + +/* +================ +GL_LoadTexture +================ +*/ +int GL_LoadTexture (const char *identifier, int width, int height, const byte *data, qboolean stretch_to_power_of_two, int filter, int mipmap_level) +{ + int texture_index = -1; + + tex_scale_down = r_tex_scale_down.value == qtrue; + // See if the texture is already present. + if (identifier[0]) + { + for (int i = 0; i < MAX_GLTEXTURES; ++i) + { + if (gltextures_used[i] == true) + { + const gltexture_t& texture = gltextures[i]; + if (!strcmp (identifier, texture.identifier)) + { + return i; + } + } + } + } + + // Out of textures? + if (numgltextures == MAX_GLTEXTURES) + { + Sys_Error("Out of OpenGL textures"); + } + + // Use the next available texture. + numgltextures++; + texture_index = numgltextures; + + for (int i = 0; i < MAX_GLTEXTURES; ++i) + { + if (gltextures_used[i] == false) { + texture_index = i; + break; + } + } + gltexture_t& texture = gltextures[texture_index]; + gltextures_used[texture_index] = true; + + // Fill in the source data. + strcpy(texture.identifier, identifier); + texture.original_width = width; + texture.original_height = height; + texture.stretch_to_power_of_two = stretch_to_power_of_two != qfalse; + + // Fill in the texture description. + texture.format = GU_PSM_T8; + texture.filter = filter; + texture.mipmaps = mipmap_level; + texture.swizzle = GU_TRUE; + texture.bpp = 1; +#ifdef STATIC_PAL + memset(texture.palette, 0, sizeof(texture.palette)); +#endif + texture.palette_active = qfalse; + + if (tex_scale_down == true && texture.stretch_to_power_of_two == true) + { + texture.width = std::max(round_down(width), 32U); + texture.height = std::max(round_down(height),32U); + } + else + { + texture.width = std::max(round_up(width), 32U); + texture.height = std::max(round_up(height),32U); + } + + for (int i=0; i <= mipmap_level;i++) + { + int div = (int) powf(2,i); + if ((texture.width / div) > 16 && (texture.height / div) > 16 ) { + texture.mipmaps = i; + } + } + + // Do we really need to resize the texture? + if (texture.stretch_to_power_of_two) + { + // Not if the size hasn't changed. + texture.stretch_to_power_of_two = (texture.width != width) || (texture.height != height); + } + + Con_DPrintf("Loading: %s [%dx%d](%0.2f KB)\n",texture.identifier,texture.width,texture.height, (float) (texture.width*texture.height)/1024); + + // Allocate the RAM. + std::size_t buffer_size = texture.width * texture.height; + + if (texture.mipmaps > 0) { + int size_incr = buffer_size/4; + for (int i= 1;i <= texture.mipmaps;i++) { + buffer_size += size_incr; + size_incr = size_incr/4; + } + } + + texture.ram = static_cast(memalign(16, buffer_size)); + + if (!texture.ram) + { + Sys_Error("Out of RAM for textures."); + } + + // Allocate the VRAM. + texture.vram = static_cast(valloc(buffer_size)); + + // Upload the texture. + GL_Upload8(texture_index, data, width, height); + + if (texture.vram && texture.ram) + { + free(texture.ram); + texture.ram = NULL; + } + // Done. + return texture_index; +} + +/* +================ +GL_LoadPalTex +================ +*/ +int GL_LoadPalTex (const char *identifier, int width, int height, const byte *data, qboolean stretch_to_power_of_two, int filter, int mipmap_level, byte *palette, int paltype) +{ + + int texture_index = -1; + + tex_scale_down = r_tex_scale_down.value == qtrue; + // See if the texture is already present. + if (identifier[0]) + { + for (int i = 0; i < MAX_GLTEXTURES; ++i) + { + if (gltextures_used[i] == true) + { + const gltexture_t& texture = gltextures[i]; + if (!strcmp (identifier, texture.identifier)) + { + return i; + } + } + } + } + + // Out of textures? + if (numgltextures == MAX_GLTEXTURES) + { + Sys_Error("Out of OpenGL textures"); + } + + // Use the next available texture. + numgltextures++; + texture_index = numgltextures; + + for (int i = 0; i < MAX_GLTEXTURES; ++i) + { + if (gltextures_used[i] == false) + { + texture_index = i; + break; + } + } + gltexture_t& texture = gltextures[texture_index]; + gltextures_used[texture_index] = true; + + // Fill in the source data. + strcpy(texture.identifier, identifier); + texture.original_width = width; + texture.original_height = height; + texture.stretch_to_power_of_two = stretch_to_power_of_two != qfalse; + + // Fill in the texture description. + texture.format = GU_PSM_T8; + texture.filter = filter; + texture.mipmaps = mipmap_level; + texture.swizzle = GU_TRUE; + texture.bpp = 1; + + // Upload the Palette + if((paltype == PAL_RGB && palette) || + (paltype == PAL_RGBA && palette) || + (paltype == PAL_Q2 && palette == NULL) || + (paltype == PAL_H2 && palette == NULL) ) + { +#ifndef STATIC_PAL + if(paltype == PAL_Q2) + { + texture.palette_2 = d_8to24tableQ2; //hard coded palette + } + else if(paltype == PAL_H2) + { + texture.palette_2 = d_8to24tableH2; //hard coded palette + } + else + { + texture.palette_2 = static_cast(memalign(16, sizeof(ScePspRGBA8888) * 256)); + if(!texture.palette_2) + { + Sys_Error("Out of RAM for palettes."); + } +#endif + if(paltype == PAL_RGBA) + { + // Convert the palette to PSP format. + for (ScePspRGBA8888* color = &texture.palette_2[0]; color < &texture.palette_2[256]; ++color) + { + const unsigned int r = gammatable[*palette++]; + const unsigned int g = gammatable[*palette++]; + const unsigned int b = gammatable[*palette++]; + const unsigned int a = gammatable[*palette++]; + *color = GU_RGBA(r, g, b, a); + } + } + else if(paltype == PAL_RGB) + { + // Convert the palette to PSP format. + for (ScePspRGBA8888* color = &texture.palette_2[0]; color < &texture.palette_2[256]; ++color) + { + const unsigned int r = gammatable[*palette++]; + const unsigned int g = gammatable[*palette++]; + const unsigned int b = gammatable[*palette++]; + *color = GU_RGBA(r, g, b, 0xff); + } + } +#ifndef STATIC_PAL + } +#endif + texture.palette_2[255] = 0; //alpha color + texture.palette_active = qtrue; + } + else + { + Sys_Error("GL_LoadPalTex: Unknow palette type"); + } + + + if (tex_scale_down == true && texture.stretch_to_power_of_two == true) + { + texture.width = std::max(round_down(width), 32U); + texture.height = std::max(round_down(height),32U); + } + else + { + texture.width = std::max(round_up(width), 32U); + texture.height = std::max(round_up(height),32U); + } + + for (int i=0; i <= mipmap_level;i++) + { + int div = (int) powf(2,i); + if ((texture.width / div) > 16 && (texture.height / div) > 16 ) + { + texture.mipmaps = i; + } + } + + // Do we really need to resize the texture? + if (texture.stretch_to_power_of_two) + { + // Not if the size hasn't changed. + texture.stretch_to_power_of_two = (texture.width != width) || (texture.height != height); + } + + Con_DPrintf("Loading TEX_PAL: %s [%dx%d](%0.2f KB)\n",texture.identifier,texture.width,texture.height, (float) ((texture.width*texture.height)/1024) + 256); + + // Allocate the RAM. + std::size_t buffer_size = texture.width * texture.height; + + if (texture.mipmaps > 0) + { + int size_incr = buffer_size/4; + for (int i= 1;i <= texture.mipmaps;i++) + { + buffer_size += size_incr; + size_incr = size_incr/4; + } + } + + texture.ram = static_cast(memalign(16, buffer_size)); + + if (!texture.ram) + { + Sys_Error("Out of RAM for textures."); + } + + // Allocate the VRAM. + texture.vram = static_cast(valloc(buffer_size)); + + // Upload the texture. + GL_Upload8(texture_index, data, width, height); + + if (texture.vram && texture.ram) + { + free(texture.ram); + texture.ram = NULL; + } + // Done. + return texture_index; +} + + +/* +================ +GL_LoadTextureLM +================ +*/ +int GL_LoadTextureLM (const char *identifier, int width, int height, const byte *data, int bpp, int filter, qboolean update, int forcopy) +{ + tex_scale_down = r_tex_scale_down.value == qtrue; + int texture_index = -1; + // See if the texture is already present. + if (identifier[0]) + { + for (int i = 0; i < MAX_GLTEXTURES; ++i) + { + if (gltextures_used[i] == true) + { + const gltexture_t& texture = gltextures[i]; + if (!strcmp (identifier, texture.identifier)) + { + if (update == qfalse) + { + return i; + } + else + { + texture_index = i; + break; + } + } + } + } + } + + if (update == qfalse || texture_index == -1) + { + // Out of textures? + if (numgltextures == MAX_GLTEXTURES) + { + Sys_Error("Out of OpenGL textures"); + } + + // Use the next available texture. + numgltextures++; + texture_index = numgltextures; + + for (int i = 0; i < MAX_GLTEXTURES; ++i) + { + if (gltextures_used[i] == false) + { + texture_index = i; + break; + } + } + gltexture_t& texture = gltextures[texture_index]; + gltextures_used[texture_index] = true; + + // Fill in the source data. + strcpy(texture.identifier, identifier); + texture.original_width = width; + texture.original_height = height; + texture.stretch_to_power_of_two = false; + texture.bpp = bpp; + +#ifdef STATIC_PAL + memset(texture.palette, 0, sizeof(texture.palette)); +#endif + texture.palette_active = qfalse; + + // Fill in the texture description. + switch(texture.bpp) + { + case 1: + texture.format = GU_PSM_T8; + break; + case 2: + texture.format = GU_PSM_4444; + break; + case 3: + texture.format = GU_PSM_8888; + break; + case 4: + texture.format = GU_PSM_8888; + break; + default: + Sys_Error("Failed bpp!"); + } + + texture.filter = filter; + texture.mipmaps = 0; + + if(forcopy) + { + texture.swizzle = GU_FALSE; + } + else + { + texture.swizzle = GU_TRUE; + } + + if (tex_scale_down == true && texture.stretch_to_power_of_two == true) + { + texture.width = std::max(round_down(width), 16U); + texture.height = std::max(round_down(height), 16U); + } + else + { + texture.width = std::max(round_up(width), 16U); + texture.height = std::max(round_up(height), 16U); + } + + // Allocate the RAM. + const std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0); + texture.ram = static_cast(memalign(16, buffer_size)); + if (!texture.ram) + { + Sys_Error("Out of RAM for lightmap textures."); + } + + // Allocate the VRAM. + texture.vram = static_cast(valloc(buffer_size)); + + // Upload the texture. + if(!texture.swizzle) + { + switch(texture.bpp) + { + case 1: + GL_Upload8_A(texture_index, data, width, height); + break; + case 2: + GL_Upload16_A(texture_index, data, width, height); + break; + case 3: + GL_Upload24_A(texture_index, data, width, height); + break; + case 4: + GL_Upload32_A(texture_index, data, width, height); + break; + default: + Sys_Error("Failed bpp!"); + } + } + else + { + switch(texture.bpp) + { + case 1: + GL_Upload8(texture_index, data, width, height); + break; + case 2: + GL_Upload16(texture_index, data, width, height); + break; + case 3: + GL_Upload24(texture_index, data, width, height); + break; + case 4: + GL_Upload32(texture_index, data, width, height); + break; + default: + Sys_Error("Failed bpp!"); + } + } + + if (texture.vram && texture.ram) + { + free(texture.ram); + texture.ram = NULL; + } + } + else + { + gltexture_t& texture = gltextures[texture_index]; + + if ((width == texture.original_width) && (height == texture.original_height)) + { + + // Upload the texture. + if(!texture.swizzle) + { + switch(texture.bpp) + { + case 1: + GL_Upload8_A(texture_index, data, width, height); + break; + case 2: + GL_Upload16_A(texture_index, data, width, height); + break; + case 3: + GL_Upload24_A(texture_index, data, width, height); + break; + case 4: + GL_Upload32_A(texture_index, data, width, height); + break; + default: + Sys_Error("Failed bpp!"); + } + } + else + { + switch(texture.bpp) + { + case 1: + GL_Upload8(texture_index, data, width, height); + break; + case 2: + GL_Upload16(texture_index, data, width, height); + break; + case 3: + GL_Upload24(texture_index, data, width, height); + break; + case 4: + GL_Upload32(texture_index, data, width, height); + break; + default: + Sys_Error("Failed bpp!"); + } + } + } + if (texture.vram && texture.ram) + { + free(texture.ram); + texture.ram = NULL; + } + } + // Done. + return texture_index; +} + +/* +================ +GL_LoadImages +================ +*/ +int GL_LoadImages (const char *identifier, int width, int height, const byte *data, qboolean stretch_to_power_of_two, int filter, int mipmap_level, int bpp) +{ + int texture_index = -1; + + tex_scale_down = r_tex_scale_down.value == qtrue; + + // See if the texture is already present. + if (identifier[0]) + { + for (int i = 0; i < MAX_GLTEXTURES; ++i) + { + if (gltextures_used[i] == true) + { + const gltexture_t& texture = gltextures[i]; + if (!strcmp (identifier, texture.identifier)) + { + return i; + } + } + } + } + + // Out of textures? + if (numgltextures == MAX_GLTEXTURES) + { Sys_Error("Out of OpenGL textures"); + } + + // Use the next available texture. + numgltextures++; + texture_index = numgltextures; + + for (int i = 0; i < MAX_GLTEXTURES; ++i) + { + if (gltextures_used[i] == false) + { + texture_index = i; + break; + } + } + + gltexture_t& texture = gltextures[texture_index]; + gltextures_used[texture_index] = true; + + // Fill in the source data. + strcpy(texture.identifier, identifier); + texture.original_width = width; + texture.original_height = height; + texture.stretch_to_power_of_two = stretch_to_power_of_two != qfalse; + texture.bpp = bpp; +#ifdef STATIC_PAL + memset(texture.palette, 0, sizeof(texture.palette)); +#endif + texture.palette_active = qfalse; + + // Fill in the texture description. + switch(texture.bpp) + { + case 1: + texture.format = GU_PSM_T8; + break; + case 2: + texture.format = GU_PSM_4444; //5650, 5551, T16(pal) + break; + case 4: + switch((int)r_texcompr.value) + { + // case 32: + case 0: + texture.format = GU_PSM_8888; //T32(pal) + break; + case 1: + texture.format = GU_PSM_DXT1; + break; + case 3: + texture.format = GU_PSM_DXT3; + break; + case 16: + texture.format = GU_PSM_4444; + break; + case 5: + default: + texture.format = GU_PSM_DXT5; + break; + } + break; + } + + texture.filter = filter; + if(texture.format < GU_PSM_DXT1) + { + texture.mipmaps = mipmap_level; + } + else + { + texture.mipmaps = 0; + } + + switch(texture.format) + { + case GU_PSM_T8: + case GU_PSM_4444: + case GU_PSM_8888: + texture.swizzle = GU_TRUE; + break; + case GU_PSM_DXT1: + case GU_PSM_DXT3: + case GU_PSM_DXT5: + texture.swizzle = GU_FALSE; + break; + } + + if (tex_scale_down == true && texture.stretch_to_power_of_two == true) + { + texture.width = std::max(round_down(width), 32U); + texture.height = std::max(round_down(height),32U); + } + else + { + texture.width = std::max(round_up(width), 32U); + texture.height = std::max(round_up(height),32U); + } + + if(texture.format < GU_PSM_DXT1) + { + for (int i=0; i <= mipmap_level;i++) + { + int div = (int) powf(2,i); + if((texture.width / div) > 16 && (texture.height / div) > 16 ) + { + texture.mipmaps = i; + } + } + } + + // Do we really need to resize the texture? + if (texture.stretch_to_power_of_two) + { + // Not if the size hasn't changed. + texture.stretch_to_power_of_two = (texture.width != width) || (texture.height != height); + } + + // Allocate the RAM. + std::size_t buffer_size = GL_GetTexSize(texture.format, texture.width, texture.height, 0); + + Con_DPrintf("Loading: %s [%dx%d](%0.2f KB)\n",texture.identifier,texture.width,texture.height, (float) buffer_size/1024); + + //Con_Printf("Loading: %s [%dx%d](%d Bytes) (Format: %d, BPP: %d)\n",texture.identifier,texture.width,texture.height, buffer_size,texture.format,bpp); + + if (texture.mipmaps > 0 && texture.format < GU_PSM_DXT1) + { + int size_incr = buffer_size/4; + for (int i= 1;i <= texture.mipmaps;i++) + { + buffer_size += size_incr; + size_incr = size_incr/4; + } + } + + texture.ram = static_cast(memalign(16, buffer_size)); + + if (!texture.ram) + { + Sys_Error("Out of RAM for images."); + } + + // Allocate the VRAM. + texture.vram = static_cast(valloc(buffer_size)); + // Upload the texture. + switch(texture.format) + { + case GU_PSM_T8: + GL_Upload8(texture_index, data, width, height); + break; + case GU_PSM_4444: + GL_Upload16(texture_index, data, width, height); + break; + case GU_PSM_8888: + GL_Upload32(texture_index, data, width, height); + break; + case GU_PSM_DXT1: + case GU_PSM_DXT3: + case GU_PSM_DXT5: + GL_UploadDXT(texture_index, data, width, height); + break; + } + if (texture.vram && texture.ram) + { + free(texture.ram); + texture.ram = NULL; + } + // Done. + return texture_index; +} + +int GL_LoadTexture8Pal32 (char *identifier, int width, int height, byte *data, byte *pal) +{ + int i, s; + s = width*height; + + if (s&3) + Sys_Error ("GL_Upload8: s&3"); + + byte *trans = (byte*)memalign(16, s*4); + for (i=0 ; i= MAX_GLTEXTURES) || gltextures_used[texture_index] == false) + { + Sys_Error("Invalid texture index %d", texture_index); + } + + const gltexture_t& texture = gltextures[texture_index]; + + // Check that the texture matches. + if ((width != texture.original_width) != (height != texture.original_height)) + { + Sys_Error("Attempting to upload a texture which doesn't match the destination"); + } + + // Create a temporary buffer to use as a source for swizzling. + std::size_t buffer_size = (width * height) / 2; + + memcpy(texture.ram, data, buffer_size); + memcpy(texture.palette, data + buffer_size, 16 * 4); + int i; + + // Copy to VRAM? + /*if (texture.vram) + { + // Copy. + memcpy(texture.vram, texture.ram, buffer_size); + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.vram, buffer_size); + }*/ + + // Flush the data cache. + sceKernelDcacheWritebackRange(texture.ram, buffer_size); +} + +int GL_LoadTexture4(const char *identifier, unsigned int width, unsigned int height, const byte *data, int filter) +{ + int texture_index = -1; + + tex_scale_down = r_tex_scale_down.value == qtrue; + + if (identifier[0]) + { + for (int i = 0; i < MAX_GLTEXTURES; ++i) + { + if (gltextures_used[i] == true) + { + const gltexture_t& texture = gltextures[i]; + if (!strcmp(identifier, texture.identifier)) + { + return i; + } + } + } + } + + // Out of textures? + if (numgltextures == MAX_GLTEXTURES) + { + Sys_Error("Out of OpenGL textures"); + } + + // Use the next available texture. + numgltextures++; + texture_index = numgltextures; + + for (int i = 0; i < MAX_GLTEXTURES; ++i) + { + if (gltextures_used[i] == false) { + texture_index = i; + break; + } + } + gltexture_t& texture = gltextures[texture_index]; + gltextures_used[texture_index] = true; + + // Fill in the source data. + strcpy(texture.identifier, identifier); + texture.original_width = texture.width = width; + texture.original_height = texture.height = height; + texture.stretch_to_power_of_two = qfalse; + + // Fill in the texture description. + texture.format = GU_PSM_T4; + texture.filter = filter; + texture.mipmaps = 0; // mipmap_level; + + + Con_DPrintf("Loading: %s [%dx%d](%0.2f KB)\n", texture.identifier, texture.width, texture.height, (float)(texture.width*texture.height) / 1024); + + // Allocate the RAM. + std::size_t buffer_size = (texture.width * texture.height) / 2; // 4bpp + + texture.ram = static_cast(memalign(16, buffer_size)); + texture.palette = static_cast(memalign(16, 16 * 4)); + + if (!texture.ram || !texture.palette) + { + Sys_Error("Out of RAM for textures."); + } + + // Allocate the VRAM. + //texture.vram = static_cast(quake::vram::allocate(buffer_size)); + + // Upload the texture. + GL_Upload4(texture_index, data, width, height); + + if (texture.vram && texture.ram) { + free(texture.ram); + texture.ram = NULL; + } + // Done. + return texture_index; +} diff --git a/source/psp/video_hardware_dxtn.cpp b/source/psp/video_hardware_dxtn.cpp new file mode 100644 index 0000000..bc3bfb6 --- /dev/null +++ b/source/psp/video_hardware_dxtn.cpp @@ -0,0 +1,842 @@ +/* + * libtxc_dxtn + * Version: 0.1b + * + * Fixed some bugs with dxt1 compression + * + * Copyright (C) 2004 Roland Scheidegger + * All Rights Reserved. + * Copyright (C) 2006-2008 Franck Charlet + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and / or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include + +#include + +#include "video_hardware_dxtn.h" + + +/* weights used for error function, basically weights (unsquared 2/4/1) according to rgb->luminance conversion + not sure if this really reflects visual perception */ +#define REDWEIGHT 4 +#define GREENWEIGHT 16 +#define BLUEWEIGHT 1 + +#define ALPHACUT 127 + +// ---------------------------------------------------- +// Convert the DXTx buffers for the PSP + +static void Convert_DXT5(unsigned char *data, unsigned int size) +{ + unsigned short *src = (unsigned short *) data; + int i; + int j; + unsigned short converted[8]; + + for(j = 0; size >= 16; size -= 16, j++) { + converted[4] = src[1]; + converted[5] = src[2]; + converted[6] = src[3]; + converted[7] = src[0]; + + converted[0] = src[6]; + converted[1] = src[7]; + converted[2] = src[4]; + converted[3] = src[5]; + for(i = 0; i < 8; i++) src[i] = converted[i]; + src += 8; + } +} + +static void Convert_DXT3(unsigned char *data, unsigned int size) +{ + unsigned short *src = (unsigned short *) data; + int i; + int j; + unsigned short converted[8]; + + for(j = 0; size >= 16; size -= 16, j++) { + converted[4] = src[0]; + converted[5] = src[1]; + converted[6] = src[2]; + converted[7] = src[3]; + + converted[0] = src[6]; + converted[1] = src[7]; + converted[2] = src[4]; + converted[3] = src[5]; + for(i = 0; i < 8; i++) src[i] = converted[i]; + src += 8; + } +} + +static void Convert_DXT1(unsigned char *data, unsigned int size) +{ + unsigned short *src = (unsigned short *) data; + int i; + int j; + unsigned short converted[4]; + + for(j = 0; size >= 8; size -= 8, j++) { + converted[0] = src[2]; + converted[1] = src[3]; + converted[2] = src[0]; + converted[3] = src[1]; + for(i = 0; i < 4; i++) src[i] = converted[i]; + src += 4; + } +} + +// ---------------------------------------------------- + +static void fancybasecolorsearch( unsigned char *blkaddr, unsigned char srccolors[4][4][4], unsigned char *bestcolor[2], int numxpixels, int numypixels, int type, int haveAlpha) { + /* use same luminance-weighted distance metric to determine encoding as for finding the base colors */ + + int i, j, colors, z; + unsigned int pixerror, pixerrorred, pixerrorgreen, pixerrorblue, pixerrorbest; + int colordist, blockerrlin[2][3]; + unsigned char nrcolor[2]; + int pixerrorcolorbest[3]; + unsigned char enc = 0; + unsigned char cv[4][4]; + unsigned char testcolor[2][3]; + + if(((bestcolor[0][0] & 0xf8) << 8 | (bestcolor[0][1] & 0xfc) << 3 | bestcolor[0][2] >> 3) < + ((bestcolor[1][0] & 0xf8) << 8 | (bestcolor[1][1] & 0xfc) << 3 | bestcolor[1][2] >> 3)) { + testcolor[0][0] = bestcolor[0][0]; + testcolor[0][1] = bestcolor[0][1]; + testcolor[0][2] = bestcolor[0][2]; + testcolor[1][0] = bestcolor[1][0]; + testcolor[1][1] = bestcolor[1][1]; + testcolor[1][2] = bestcolor[1][2]; + } + else { + testcolor[1][0] = bestcolor[0][0]; + testcolor[1][1] = bestcolor[0][1]; + testcolor[1][2] = bestcolor[0][2]; + testcolor[0][0] = bestcolor[1][0]; + testcolor[0][1] = bestcolor[1][1]; + testcolor[0][2] = bestcolor[1][2]; + } + + for(i = 0; i < 3; i ++) { + cv[0][i] = testcolor[0][i]; + cv[1][i] = testcolor[1][i]; + cv[2][i] = (testcolor[0][i] * 2 + testcolor[1][i]) / 3; + cv[3][i] = (testcolor[0][i] + testcolor[1][i] * 2) / 3; + } + + blockerrlin[0][0] = 0; + blockerrlin[0][1] = 0; + blockerrlin[0][2] = 0; + blockerrlin[1][0] = 0; + blockerrlin[1][1] = 0; + blockerrlin[1][2] = 0; + + nrcolor[0] = 0; + nrcolor[1] = 0; + + for(j = 0; j < numypixels; j++) { + for(i = 0; i < numxpixels; i++) { + pixerrorbest = 0xffffffff; + for(colors = 0; colors < 4; colors++) { + colordist = srccolors[j][i][0] - (cv[colors][0]); + pixerror = colordist * colordist * REDWEIGHT; + pixerrorred = colordist; + colordist = srccolors[j][i][1] - (cv[colors][1]); + pixerror += colordist * colordist * GREENWEIGHT; + pixerrorgreen = colordist; + colordist = srccolors[j][i][2] - (cv[colors][2]); + pixerror += colordist * colordist * BLUEWEIGHT; + pixerrorblue = colordist; + if(pixerror < pixerrorbest) { + enc = colors; + pixerrorbest = pixerror; + pixerrorcolorbest[0] = pixerrorred; + pixerrorcolorbest[1] = pixerrorgreen; + pixerrorcolorbest[2] = pixerrorblue; + } + } + if(enc == 0) { + for(z = 0; z < 3; z++) { + blockerrlin[0][z] += 3 * pixerrorcolorbest[z]; + } + nrcolor[0] += 3; + } + else if(enc == 2) { + for(z = 0; z < 3; z++) { + blockerrlin[0][z] += 2 * pixerrorcolorbest[z]; + } + nrcolor[0] += 2; + for(z = 0; z < 3; z++) { + blockerrlin[1][z] += 1 * pixerrorcolorbest[z]; + } + nrcolor[1] += 1; + } + else if(enc == 3) { + for(z = 0; z < 3; z++) { + blockerrlin[0][z] += 1 * pixerrorcolorbest[z]; + } + nrcolor[0] += 1; + for(z = 0; z < 3; z++) { + blockerrlin[1][z] += 2 * pixerrorcolorbest[z]; + } + nrcolor[1] += 2; + } + else if(enc == 1) { + for(z = 0; z < 3; z++) { + blockerrlin[1][z] += 3 * pixerrorcolorbest[z]; + } + nrcolor[1] += 3; + } + } + } + if(nrcolor[0] == 0) nrcolor[0] = 1; + if(nrcolor[1] == 0) nrcolor[1] = 1; + for(j = 0; j < 2; j++) { + for(i = 0; i < 3; i++) { + int newvalue = testcolor[j][i] + blockerrlin[j][i] / nrcolor[j]; + if(newvalue <= 0) testcolor[j][i] = 0; + else if(newvalue >= 255) testcolor[j][i] = 255; + else testcolor[j][i] = newvalue; + } + } + + if ((abs(testcolor[0][0] - testcolor[1][0]) < 8) && + (abs(testcolor[0][1] - testcolor[1][1]) < 4) && + (abs(testcolor[0][2] - testcolor[1][2]) < 8)) { + /* both colors are so close they might get encoded as the same 16bit values */ + unsigned char coldiffred, coldiffgreen, coldiffblue, coldiffmax, factor, ind0, ind1; + + coldiffred = abs(testcolor[0][0] - testcolor[1][0]); + coldiffgreen = 2 * abs(testcolor[0][1] - testcolor[1][1]); + coldiffblue = abs(testcolor[0][2] - testcolor[1][2]); + coldiffmax = coldiffred; + if(coldiffmax < coldiffgreen) coldiffmax = coldiffgreen; + if(coldiffmax < coldiffblue) coldiffmax = coldiffblue; + if(coldiffmax > 0) { + if(coldiffmax > 4) factor = 2; + else if(coldiffmax > 2) factor = 3; + else factor = 4; + /* Won't do much if the color value is near 255... */ + /* argh so many ifs */ + if(testcolor[1][1] >= testcolor[0][1]) { + ind1 = 1; ind0 = 0; + } + else { + ind1 = 0; ind0 = 1; + } + if((testcolor[ind1][1] + factor * coldiffgreen) <= 255) + testcolor[ind1][1] += factor * coldiffgreen; + else testcolor[ind1][1] = 255; + if((testcolor[ind1][0] - testcolor[ind0][1]) > 0) { + if((testcolor[ind1][0] + factor * coldiffred) <= 255) + testcolor[ind1][0] += factor * coldiffred; + else testcolor[ind1][0] = 255; + } + else { + if((testcolor[ind0][0] + factor * coldiffred) <= 255) + testcolor[ind0][0] += factor * coldiffred; + else testcolor[ind0][0] = 255; + } + if((testcolor[ind1][2] - testcolor[ind0][2]) > 0) { + if((testcolor[ind1][2] + factor * coldiffblue) <= 255) + testcolor[ind1][2] += factor * coldiffblue; + else testcolor[ind1][2] = 255; + } + else { + if((testcolor[ind0][2] + factor * coldiffblue) <= 255) + testcolor[ind0][2] += factor * coldiffblue; + else testcolor[ind0][2] = 255; + } + } + } + + if(((testcolor[0][0] & 0xf8) << 8 | (testcolor[0][1] & 0xfc) << 3 | testcolor[0][2] >> 3) < + ((testcolor[1][0] & 0xf8) << 8 | (testcolor[1][1] & 0xfc) << 3 | testcolor[1][2]) >> 3) { + for(i = 0; i < 3; i++) { + bestcolor[0][i] = testcolor[0][i]; + bestcolor[1][i] = testcolor[1][i]; + } + } + else { + for(i = 0; i < 3; i++) { + bestcolor[0][i] = testcolor[1][i]; + bestcolor[1][i] = testcolor[0][i]; + } + } +} + +static void storedxtencodedblock( unsigned char *blkaddr, unsigned char srccolors[4][4][4], unsigned char *bestcolor[2], int numxpixels, int numypixels, int type, int haveAlpha) { + /* use same luminance-weighted distance metric to determine encoding as for finding the base colors */ + + int i, j, colors; + unsigned int testerror, testerror2, pixerror, pixerrorbest; + int colordist; + unsigned short color0, color1, tempcolor; + unsigned int bits = 0, bits2 = 0; + unsigned char *colorptr; + unsigned char enc = 0; + unsigned char cv[4][4]; + + bestcolor[0][0] = bestcolor[0][0] & 0xf8; + bestcolor[0][1] = bestcolor[0][1] & 0xfc; + bestcolor[0][2] = bestcolor[0][2] & 0xf8; + bestcolor[1][0] = bestcolor[1][0] & 0xf8; + bestcolor[1][1] = bestcolor[1][1] & 0xfc; + bestcolor[1][2] = bestcolor[1][2] & 0xf8; + + color0 = bestcolor[0][0] << 8 | bestcolor[0][1] << 3 | bestcolor[0][2] >> 3; + color1 = bestcolor[1][0] << 8 | bestcolor[1][1] << 3 | bestcolor[1][2] >> 3; + if(color0 < color1) { + tempcolor = color0; color0 = color1; color1 = tempcolor; + colorptr = bestcolor[0]; bestcolor[0] = bestcolor[1]; bestcolor[1] = colorptr; + } + + for(i = 0; i < 3; i ++) { + cv[0][i] = bestcolor[0][i]; + cv[1][i] = bestcolor[1][i]; + cv[2][i] = (bestcolor[0][i] * 2 + bestcolor[1][i]) / 3; + cv[3][i] = (bestcolor[0][i] + bestcolor[1][i] * 2) / 3; + } + + testerror = 0; + for(j = 0; j < numypixels; j++) { + for(i = 0; i < numxpixels; i++) { + pixerrorbest = 0xffffffff; + for(colors = 0; colors < 4; colors++) { + colordist = srccolors[j][i][0] - cv[colors][0]; + pixerror = colordist * colordist * REDWEIGHT; + colordist = srccolors[j][i][1] - cv[colors][1]; + pixerror += colordist * colordist * GREENWEIGHT; + colordist = srccolors[j][i][2] - cv[colors][2]; + pixerror += colordist * colordist * BLUEWEIGHT; + if(pixerror < pixerrorbest) { + pixerrorbest = pixerror; + enc = colors; + } + } + testerror += pixerrorbest; + bits |= enc << (2 * (j * 4 + i)); + } + } + for(i = 0; i < 3; i ++) { + cv[2][i] = (bestcolor[0][i] + bestcolor[1][i]) / 2; + /* this isn't used. Looks like the black color constant can only be used + with RGB_DXT1 if I read the spec correctly (note though that the radeon gpu disagrees, + it will decode 3 to black even with DXT3/5), and due to how the color searching works + it won't get used even then */ + cv[3][i] = 0; + } + testerror2 = 0; + for(j = 0; j < numypixels; j++) { + for(i = 0; i < numxpixels; i++) { + pixerrorbest = 0xffffffff; + if((type == GU_PSM_DXT1) && (srccolors[j][i][3] <= ALPHACUT)) { + enc = 3; + pixerrorbest = 0; // don't calculate error + } else { + // we're calculating the same what we have done already for colors 0-1 above... + for(colors = 0; colors < 3; colors++) { + colordist = srccolors[j][i][0] - cv[colors][0]; + pixerror = colordist * colordist * REDWEIGHT; + colordist = srccolors[j][i][1] - cv[colors][1]; + pixerror += colordist * colordist * GREENWEIGHT; + colordist = srccolors[j][i][2] - cv[colors][2]; + pixerror += colordist * colordist * BLUEWEIGHT; + if(pixerror < pixerrorbest) { + pixerrorbest = pixerror; + // need to exchange colors later + if(colors > 1) enc = colors; + else enc = colors ^ 1; + } + } + } + testerror2 += pixerrorbest; + bits2 |= enc << (2 * (j * 4 + i)); + } + } + + /* finally we're finished, write back colors and bits */ + if((testerror > testerror2) || (haveAlpha)) { + *blkaddr++ = color1 & 0xff; + *blkaddr++ = color1 >> 8; + *blkaddr++ = color0 & 0xff; + *blkaddr++ = color0 >> 8; + *blkaddr++ = (unsigned char) (bits2 & 0xff); + *blkaddr++ = (unsigned char) (( bits2 >> 8) & 0xff); + *blkaddr++ = (unsigned char) (( bits2 >> 16) & 0xff); + *blkaddr = (unsigned char) (bits2 >> 24); + } else { + *blkaddr++ = color0 & 0xff; + *blkaddr++ = color0 >> 8; + *blkaddr++ = color1 & 0xff; + *blkaddr++ = color1 >> 8; + *blkaddr++ = (unsigned char) (bits & 0xff); + *blkaddr++ = (unsigned char) (( bits >> 8) & 0xff); + *blkaddr++ = (unsigned char) (( bits >> 16) & 0xff); + *blkaddr = (unsigned char) (bits >> 24); + } +} + +static void encodedxtcolorblockfaster( unsigned char *blkaddr, unsigned char srccolors[4][4][4], int numxpixels, int numypixels, unsigned int type ) { +/* simplistic approach. We need two base colors, simply use the "highest" and the "lowest" color + present in the picture as base colors */ + + /* define lowest and highest color as shortest and longest vector to 0/0/0, though the + vectors are weighted similar to their importance in rgb-luminance conversion + doesn't work too well though... + This seems to be a rather difficult problem */ + + unsigned char *bestcolor[2]; + unsigned char basecolors[2][3]; + unsigned char i, j; + unsigned int lowcv, highcv, testcv; + + lowcv = highcv = srccolors[0][0][0] * srccolors[0][0][0] * REDWEIGHT + + srccolors[0][0][1] * srccolors[0][0][1] * GREENWEIGHT + + srccolors[0][0][2] * srccolors[0][0][2] * BLUEWEIGHT; + bestcolor[0] = bestcolor[1] = srccolors[0][0]; + for(j = 0; j < numypixels; j++) { + for(i = 0; i < numxpixels; i++) { + /* don't use this as a base color if the pixel will get black/transparent anyway */ + testcv = srccolors[j][i][0] * srccolors[j][i][0] * REDWEIGHT + + srccolors[j][i][1] * srccolors[j][i][1] * GREENWEIGHT + + srccolors[j][i][2] * srccolors[j][i][2] * BLUEWEIGHT; + if(testcv > highcv) { + highcv = testcv; + bestcolor[1] = srccolors[j][i]; + } + else if(testcv < lowcv) { + lowcv = testcv; + bestcolor[0] = srccolors[j][i]; + } + } + } + /* make sure the original color values won't get touched... */ + for(j = 0; j < 2; j++) { + for(i = 0; i < 3; i++) { + basecolors[j][i] = bestcolor[j][i]; + } + } + bestcolor[0] = basecolors[0]; + bestcolor[1] = basecolors[1]; + /* try to find better base colors */ + fancybasecolorsearch(blkaddr, srccolors, bestcolor, numxpixels, numypixels, type, TRUE); + /* find the best encoding for these colors, and store the result */ + storedxtencodedblock(blkaddr, srccolors, bestcolor, numxpixels, numypixels, type, TRUE); +} + +static void writedxt5encodedalphablock( unsigned char *blkaddr, unsigned char alphabase1, unsigned char alphabase2, unsigned char alphaenc[16]) { + *blkaddr++ = alphabase1; + *blkaddr++ = alphabase2; + *blkaddr++ = alphaenc[0] | (alphaenc[1] << 3) | ((alphaenc[2] & 3) << 6); + *blkaddr++ = (alphaenc[2] >> 2) | (alphaenc[3] << 1) | (alphaenc[4] << 4) | ((alphaenc[5] & 1) << 7); + *blkaddr++ = (alphaenc[5] >> 1) | (alphaenc[6] << 2) | (alphaenc[7] << 5); + *blkaddr++ = alphaenc[8] | (alphaenc[9] << 3) | ((alphaenc[10] & 3) << 6); + *blkaddr++ = (alphaenc[10] >> 2) | (alphaenc[11] << 1) | (alphaenc[12] << 4) | ((alphaenc[13] & 1) << 7); + *blkaddr++ = (alphaenc[13] >> 1) | (alphaenc[14] << 2) | (alphaenc[15] << 5); +} + +static void encodedxt5alpha(unsigned char *blkaddr, unsigned char srccolors[4][4][4], int numxpixels, int numypixels) { + unsigned char alphabase[2], alphause[2]; + short alphatest[2]; + unsigned int alphablockerror1, alphablockerror2, alphablockerror3; + unsigned char i, j, aindex, acutValues[7]; + unsigned char alphaenc1[16], alphaenc2[16], alphaenc3[16]; + int alphaabsmin = FALSE; + int alphaabsmax = FALSE; + short alphadist; + + alphatest[0] = 0; + alphatest[1] = 0; + /* find lowest and highest alpha value in block, alphabase[0] lowest, alphabase[1] highest */ + alphabase[0] = 0xff; alphabase[1] = 0x0; + for(j = 0; j < numypixels; j++) { + for(i = 0; i < numxpixels; i++) { + if(srccolors[j][i][3] == 0) + alphaabsmin = TRUE; + else if(srccolors[j][i][3] == 255) + alphaabsmax = TRUE; + else { + if(srccolors[j][i][3] > alphabase[1]) alphabase[1] = srccolors[j][i][3]; + if(srccolors[j][i][3] < alphabase[0]) alphabase[0] = srccolors[j][i][3]; + } + } + } + + if((alphabase[0] > alphabase[1]) && !(alphaabsmin && alphaabsmax)) { /* one color, either max or min */ + /* shortcut here since it is a very common case (and also avoids later problems) */ + /* || (alphabase[0] == alphabase[1] && !alphaabsmin && !alphaabsmax) */ + /* could also thest for alpha0 == alpha1 (and not min/max), but probably not common, so don't bother */ + + *blkaddr++ = srccolors[0][0][3]; + blkaddr += 1; + *blkaddr++ = 0; + *blkaddr++ = 0; + *blkaddr++ = 0; + *blkaddr++ = 0; + *blkaddr++ = 0; + *blkaddr++ = 0; + return; + } + + /* find best encoding for alpha0 > alpha1 */ + /* it's possible this encoding is better even if both alphaabsmin and alphaabsmax are true */ + alphablockerror1 = 0x0; + alphablockerror2 = 0xffffffff; + alphablockerror3 = 0xffffffff; + if(alphaabsmin) alphause[0] = 0; + else alphause[0] = alphabase[0]; + if(alphaabsmax) alphause[1] = 255; + else alphause[1] = alphabase[1]; + /* calculate the 7 cut values, just the middle between 2 of the computed alpha values */ + for(aindex = 0; aindex < 7; aindex++) { + /* don't forget here is always rounded down */ + acutValues[aindex] = (alphause[0] * (2*aindex + 1) + alphause[1] * (14 - (2*aindex + 1))) / 14; + } + + for(j = 0; j < numypixels; j++) { + for(i = 0; i < numxpixels; i++) { + /* maybe it's overkill to have the most complicated calculation just for the error + calculation which we only need to figure out if encoding1 or encoding2 is better... */ + if(srccolors[j][i][3] > acutValues[0]) { + alphaenc1[4*j + i] = 0; + alphadist = srccolors[j][i][3] - alphause[1]; + } + else if(srccolors[j][i][3] > acutValues[1]) { + alphaenc1[4*j + i] = 2; + alphadist = srccolors[j][i][3] - (alphause[1] * 6 + alphause[0] * 1) / 7; + } + else if(srccolors[j][i][3] > acutValues[2]) { + alphaenc1[4*j + i] = 3; + alphadist = srccolors[j][i][3] - (alphause[1] * 5 + alphause[0] * 2) / 7; + } + else if(srccolors[j][i][3] > acutValues[3]) { + alphaenc1[4*j + i] = 4; + alphadist = srccolors[j][i][3] - (alphause[1] * 4 + alphause[0] * 3) / 7; + } + else if(srccolors[j][i][3] > acutValues[4]) { + alphaenc1[4*j + i] = 5; + alphadist = srccolors[j][i][3] - (alphause[1] * 3 + alphause[0] * 4) / 7; + } + else if(srccolors[j][i][3] > acutValues[5]) { + alphaenc1[4*j + i] = 6; + alphadist = srccolors[j][i][3] - (alphause[1] * 2 + alphause[0] * 5) / 7; + } + else if(srccolors[j][i][3] > acutValues[6]) { + alphaenc1[4*j + i] = 7; + alphadist = srccolors[j][i][3] - (alphause[1] * 1 + alphause[0] * 6) / 7; + } else { + alphaenc1[4*j + i] = 1; + alphadist = srccolors[j][i][3] - alphause[0]; + } + alphablockerror1 += alphadist * alphadist; + } + } + + /* it's not very likely this encoding is better if both alphaabsmin and alphaabsmax + are false but try it anyway */ + if(alphablockerror1 >= 32) { + /* don't bother if encoding is already very good, this condition should also imply + we have valid alphabase colors which we absolutely need (alphabase[0] <= alphabase[1]) */ + alphablockerror2 = 0; + for(aindex = 0; aindex < 5; aindex++) { + /* don't forget here is always rounded down */ + acutValues[aindex] = (alphabase[0] * (10 - (2*aindex + 1)) + alphabase[1] * (2*aindex + 1)) / 10; + } + for(j = 0; j < numypixels; j++) { + for(i = 0; i < numxpixels; i++) { + /* maybe it's overkill to have the most complicated calculation just for the error + calculation which we only need to figure out if encoding1 or encoding2 is better... */ + if(srccolors[j][i][3] == 0) { + alphaenc2[4*j + i] = 6; + alphadist = 0; + } + else if(srccolors[j][i][3] == 255) { + alphaenc2[4*j + i] = 7; + alphadist = 0; + } + else if(srccolors[j][i][3] <= acutValues[0]) { + alphaenc2[4*j + i] = 0; + alphadist = srccolors[j][i][3] - alphabase[0]; + } + else if(srccolors[j][i][3] <= acutValues[1]) { + alphaenc2[4*j + i] = 2; + alphadist = srccolors[j][i][3] - (alphabase[0] * 4 + alphabase[1] * 1) / 5; + } + else if(srccolors[j][i][3] <= acutValues[2]) { + alphaenc2[4*j + i] = 3; + alphadist = srccolors[j][i][3] - (alphabase[0] * 3 + alphabase[1] * 2) / 5; + } + else if(srccolors[j][i][3] <= acutValues[3]) { + alphaenc2[4*j + i] = 4; + alphadist = srccolors[j][i][3] - (alphabase[0] * 2 + alphabase[1] * 3) / 5; + } + else if(srccolors[j][i][3] <= acutValues[4]) { + alphaenc2[4*j + i] = 5; + alphadist = srccolors[j][i][3] - (alphabase[0] * 1 + alphabase[1] * 4) / 5; + } else { + alphaenc2[4*j + i] = 1; + alphadist = srccolors[j][i][3] - alphabase[1]; + } + alphablockerror2 += alphadist * alphadist; + } + } + + /* skip this if the error is already very small + this encoding is MUCH better on average than #2 though, but expensive! */ + if((alphablockerror2 > 96) && (alphablockerror1 > 96)) { + short blockerrlin1 = 0; + short blockerrlin2 = 0; + unsigned char nralphainrangelow = 0; + unsigned char nralphainrangehigh = 0; + alphatest[0] = 0xff; + alphatest[1] = 0x0; + /* if we have large range it's likely there are values close to 0/255, try to map them to 0/255 */ + for(j = 0; j < numypixels; j++) { + for(i = 0; i < numxpixels; i++) { + if((srccolors[j][i][3] > alphatest[1]) && (srccolors[j][i][3] < (255 -(alphabase[1] - alphabase[0]) / 28))) + alphatest[1] = srccolors[j][i][3]; + if((srccolors[j][i][3] < alphatest[0]) && (srccolors[j][i][3] > (alphabase[1] - alphabase[0]) / 28)) + alphatest[0] = srccolors[j][i][3]; + } + } + /* shouldn't happen too often, don't really care about those degenerated cases */ + if(alphatest[1] <= alphatest[0]) { + alphatest[0] = 1; + alphatest[1] = 254; + } + for(aindex = 0; aindex < 5; aindex++) { + /* don't forget here is always rounded down */ + acutValues[aindex] = (alphatest[0] * (10 - (2*aindex + 1)) + alphatest[1] * (2*aindex + 1)) / 10; + } + + /* find the "average" difference between the alpha values and the next encoded value. + This is then used to calculate new base values. + Should there be some weighting, i.e. those values closer to alphatest[x] have more weight, + since they will see more improvement, and also because the values in the middle are somewhat + likely to get no improvement at all (because the base values might move in different directions)? + OTOH it would mean the values in the middle are even less likely to get an improvement + */ + for(j = 0; j < numypixels; j++) { + for(i = 0; i < numxpixels; i++) { + if(srccolors[j][i][3] <= alphatest[0] / 2) { + } + else if (srccolors[j][i][3] > ((255 + alphatest[1]) / 2)) { + } + else if (srccolors[j][i][3] <= acutValues[0]) { + blockerrlin1 += (srccolors[j][i][3] - alphatest[0]); + nralphainrangelow += 1; + } + else if (srccolors[j][i][3] <= acutValues[1]) { + blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 4 + alphatest[1] * 1) / 5); + blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 4 + alphatest[1] * 1) / 5); + nralphainrangelow += 1; + nralphainrangehigh += 1; + } + else if (srccolors[j][i][3] <= acutValues[2]) { + blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 3 + alphatest[1] * 2) / 5); + blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 3 + alphatest[1] * 2) / 5); + nralphainrangelow += 1; + nralphainrangehigh += 1; + } + else if (srccolors[j][i][3] <= acutValues[3]) { + blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 2 + alphatest[1] * 3) / 5); + blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 2 + alphatest[1] * 3) / 5); + nralphainrangelow += 1; + nralphainrangehigh += 1; + } + else if (srccolors[j][i][3] <= acutValues[4]) { + blockerrlin1 += (srccolors[j][i][3] - (alphatest[0] * 1 + alphatest[1] * 4) / 5); + blockerrlin2 += (srccolors[j][i][3] - (alphatest[0] * 1 + alphatest[1] * 4) / 5); + nralphainrangelow += 1; + nralphainrangehigh += 1; + } + else { + blockerrlin2 += (srccolors[j][i][3] - alphatest[1]); + nralphainrangehigh += 1; + } + } + } + /* shouldn't happen often, needed to avoid div by zero */ + if(nralphainrangelow == 0) nralphainrangelow = 1; + if(nralphainrangehigh == 0) nralphainrangehigh = 1; + alphatest[0] = alphatest[0] + (blockerrlin1 / nralphainrangelow); + /* again shouldn't really happen often... */ + if(alphatest[0] < 0) alphatest[0] = 0; + + alphatest[1] = alphatest[1] + (blockerrlin2 / nralphainrangehigh); + if(alphatest[1] > 255) alphatest[1] = 255; + + alphablockerror3 = 0; + for(aindex = 0; aindex < 5; aindex++) { + /* don't forget here is always rounded down */ + acutValues[aindex] = (alphatest[0] * (10 - (2*aindex + 1)) + alphatest[1] * (2*aindex + 1)) / 10; + } + for(j = 0; j < numypixels; j++) { + for(i = 0; i < numxpixels; i++) { + /* maybe it's overkill to have the most complicated calculation just for the error + calculation which we only need to figure out if encoding1 or encoding2 is better... */ + if (srccolors[j][i][3] <= alphatest[0] / 2) { + alphaenc3[4*j + i] = 6; + alphadist = srccolors[j][i][3]; + } + else if (srccolors[j][i][3] > ((255 + alphatest[1]) / 2)) { + alphaenc3[4*j + i] = 7; + alphadist = 255 - srccolors[j][i][3]; + } + else if (srccolors[j][i][3] <= acutValues[0]) { + alphaenc3[4*j + i] = 0; + alphadist = srccolors[j][i][3] - alphatest[0]; + } + else if (srccolors[j][i][3] <= acutValues[1]) { + alphaenc3[4*j + i] = 2; + alphadist = srccolors[j][i][3] - (alphatest[0] * 4 + alphatest[1] * 1) / 5; + } + else if (srccolors[j][i][3] <= acutValues[2]) { + alphaenc3[4*j + i] = 3; + alphadist = srccolors[j][i][3] - (alphatest[0] * 3 + alphatest[1] * 2) / 5; + } + else if (srccolors[j][i][3] <= acutValues[3]) { + alphaenc3[4*j + i] = 4; + alphadist = srccolors[j][i][3] - (alphatest[0] * 2 + alphatest[1] * 3) / 5; + } + else if (srccolors[j][i][3] <= acutValues[4]) { + alphaenc3[4*j + i] = 5; + alphadist = srccolors[j][i][3] - (alphatest[0] * 1 + alphatest[1] * 4) / 5; + } + else { + alphaenc3[4*j + i] = 1; + alphadist = srccolors[j][i][3] - alphatest[1]; + } + alphablockerror3 += alphadist * alphadist; + } + } + } + } + + /* write the alpha values and encoding back. */ + if((alphablockerror1 <= alphablockerror2) && (alphablockerror1 <= alphablockerror3)) { + writedxt5encodedalphablock( blkaddr, alphause[1], alphause[0], alphaenc1 ); + } else if(alphablockerror2 <= alphablockerror3) { + writedxt5encodedalphablock( blkaddr, alphabase[0], alphabase[1], alphaenc2 ); + } else { + writedxt5encodedalphablock( blkaddr, (unsigned char) alphatest[0], (unsigned char) alphatest[1], alphaenc3 ); + } +} + +static void extractsrccolors( unsigned char srcpixels[4][4][4], const GLchan *srcaddr, int srcRowStride, int numxpixels, int numypixels, int comps) { + unsigned char i, j, c; + const GLchan *curaddr; + + for(j = 0; j < numypixels; j++) { + curaddr = srcaddr + j * srcRowStride * comps; + for(i = 0; i < numxpixels; i++) { + for(c = 0; c < comps; c++) { + srcpixels[j][i][c] = *curaddr++ / (CHAN_MAX / 255); + } + } + } +} + +int tx_compress_dxtn(int srccomps, int width, int height, const unsigned char *srcPixData, unsigned int destFormat, unsigned char *dest) +{ + unsigned char *blkaddr = dest; + unsigned char srcpixels[4][4][4]; + const GLchan *srcaddr = srcPixData; + int numxpixels, numypixels;//blubswillrule: hehe, "nummy pixels" + int i, j; + + switch (destFormat) + { + case GU_PSM_DXT1: + for (j = 0; j < height; j += 4) + { + if (height > j + 3) numypixels = 4; + else numypixels = height - j; + srcaddr = srcPixData + j * width * srccomps; + for (i = 0; i < width; i += 4) + { + if (width > i + 3) numxpixels = 4; + else numxpixels = width - i; + extractsrccolors(srcpixels, srcaddr, width, numxpixels, numypixels, srccomps); + encodedxtcolorblockfaster(blkaddr, srcpixels, numxpixels, numypixels, destFormat); + srcaddr += srccomps * numxpixels; + blkaddr += 8; + } + } + Convert_DXT1((unsigned char *) dest, blkaddr - dest); + break; + + case GU_PSM_DXT3: + for(j = 0; j < height; j += 4) + { + if(height > j + 3) numypixels = 4; + else numypixels = height - j; + srcaddr = srcPixData + j * width * srccomps; + for(i = 0; i < width; i += 4) + { + if(width > i + 3) numxpixels = 4; + else numxpixels = width - i; + extractsrccolors(srcpixels, srcaddr, width, numxpixels, numypixels, srccomps); + *blkaddr++ = (srcpixels[0][0][3] >> 4) | (srcpixels[0][1][3] & 0xf0); + *blkaddr++ = (srcpixels[0][2][3] >> 4) | (srcpixels[0][3][3] & 0xf0); + *blkaddr++ = (srcpixels[1][0][3] >> 4) | (srcpixels[1][1][3] & 0xf0); + *blkaddr++ = (srcpixels[1][2][3] >> 4) | (srcpixels[1][3][3] & 0xf0); + *blkaddr++ = (srcpixels[2][0][3] >> 4) | (srcpixels[2][1][3] & 0xf0); + *blkaddr++ = (srcpixels[2][2][3] >> 4) | (srcpixels[2][3][3] & 0xf0); + *blkaddr++ = (srcpixels[3][0][3] >> 4) | (srcpixels[3][1][3] & 0xf0); + *blkaddr++ = (srcpixels[3][2][3] >> 4) | (srcpixels[3][3][3] & 0xf0); + encodedxtcolorblockfaster(blkaddr, srcpixels, numxpixels, numypixels, destFormat); + srcaddr += srccomps * numxpixels; + blkaddr += 8; + } + } + Convert_DXT3((unsigned char *) dest, blkaddr - dest); + break; + case GU_PSM_DXT5: + for(j = 0; j < height; j += 4) + { + if(height > j + 3) numypixels = 4; + else numypixels = height - j; + srcaddr = srcPixData + j * width * srccomps; + for(i = 0; i < width; i += 4) + { + if(width > i + 3) numxpixels = 4; + else numxpixels = width - i; + extractsrccolors(srcpixels, srcaddr, width, numxpixels, numypixels, srccomps); + encodedxt5alpha(blkaddr, srcpixels, numxpixels, numypixels); + encodedxtcolorblockfaster(blkaddr + 8, srcpixels, numxpixels, numypixels, destFormat); + srcaddr += srccomps * numxpixels; + blkaddr += 16; + } + } + Convert_DXT5((unsigned char *) dest, blkaddr - dest); + break; + default: + return(0); + } + return(blkaddr - dest); +} diff --git a/source/psp/video_hardware_dxtn.h b/source/psp/video_hardware_dxtn.h new file mode 100644 index 0000000..9f1e2bf --- /dev/null +++ b/source/psp/video_hardware_dxtn.h @@ -0,0 +1,56 @@ +/* + * libtxc_dxtn + * Version: 0.1b + * + * Fixed some bugs with dxt1 compression + * + * Copyright (C) 2004 Roland Scheidegger + * All Rights Reserved. + * Copyright (C) 2006-2008 Franck Charlet + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _TXC_DXTN_H_ +#define _TXC_DXTN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned char GLchan; + +#define FALSE 0 +#define TRUE 1 + +#define UBYTE_TO_CHAN(b) (b) +#define CHAN_MAX 255 +#define RCOMP 0 +#define GCOMP 1 +#define BCOMP 2 +#define ACOMP 3 + +extern int tx_compress_dxtn(int srccomps, int width, int height, const unsigned char *srcPixData, unsigned int destformat, unsigned char *dest); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/psp/video_hardware_entity_fragment.cpp b/source/psp/video_hardware_entity_fragment.cpp new file mode 100644 index 0000000..9f743a8 --- /dev/null +++ b/source/psp/video_hardware_entity_fragment.cpp @@ -0,0 +1,240 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 + +extern "C" +{ +#include "../quakedef.h" +} + +mnode_t *r_pefragtopnode; + + +//=========================================================================== + +/* +=============================================================================== + + 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) + { + Con_Printf ("Too many efrags!\n"); + return; // no free fragments... + } + 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_md3: + case mod_halflife: + 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); + } + } +} + + diff --git a/source/psp/video_hardware_fog.cpp b/source/psp/video_hardware_fog.cpp new file mode 100644 index 0000000..3cb356a --- /dev/null +++ b/source/psp/video_hardware_fog.cpp @@ -0,0 +1,475 @@ +/* + +Fogging system based on FitzQuake's implementation + +*/ + +extern "C" +{ +#include "../quakedef.h" +} + +#include + +//============================================================================== +// +// GLOBAL FOG +// +//============================================================================== + +extern refdef_t r_refdef; + +float old_start; +float old_end; +float old_red; +float old_green; +float old_blue; + +float fade_time; //duration of fade +float fade_done; //time when fade will be done + +/* +============= +Fog_Update + +update internal variables +============= +*/ +void Fog_Update (float start, float end, float red, float green, float blue, float time) +{ + //save previous settings for fade + if (time > 0) + { + //check for a fade in progress + if (fade_done > cl.time) + { + float f; + + f = (fade_done - cl.time) / fade_time; + old_start = f * old_start + (1.0 - f) * r_refdef.fog_start; + old_end = f * old_end + (1.0 - f) * r_refdef.fog_end; + old_red = f * old_red + (1.0 - f) * r_refdef.fog_red; + old_green = f * old_green + (1.0 - f) * r_refdef.fog_green; + old_blue = f * old_blue + (1.0 - f) * r_refdef.fog_blue; + } + else + { + old_start = r_refdef.fog_start; + old_end = r_refdef.fog_end; + old_red = r_refdef.fog_red; + old_green = r_refdef.fog_green; + old_blue = r_refdef.fog_blue; + } + } + + r_refdef.fog_start = start; + r_refdef.fog_end = end; + r_refdef.fog_red = red; + r_refdef.fog_green = green; + r_refdef.fog_blue = blue; + fade_time = time; + fade_done = cl.time + time; +} + +/* +============= +Fog_ParseServerMessage + +handle an SVC_FOG message from server +============= +*/ +void Fog_ParseServerMessage (void) +{ + float start, end, red, green, blue, time; + + start = MSG_ReadByte() / 255.0; + end = MSG_ReadByte() / 255.0; + red = MSG_ReadByte() / 255.0; + green = MSG_ReadByte() / 255.0; + blue = MSG_ReadByte() / 255.0; + time = MSG_ReadShort() / 100.0; + + Fog_Update (start, end, red, green, blue, time); +} + +/* +============= +Fog_FogCommand_f + +handle the 'fog' console command +============= +*/ +void Fog_FogCommand_f (void) +{ + switch (Cmd_Argc()) + { + default: + case 1: + Con_Printf("usage:\n"); + Con_Printf(" fog \n"); + Con_Printf(" fog \n"); + Con_Printf(" fog \n"); + Con_Printf(" fog \n"); + Con_Printf(" fog \n"); + Con_Printf(" fog \n"); + Con_Printf("current values:\n"); + Con_Printf(" \"start\" is \"%f\"\n", r_refdef.fog_start); + Con_Printf(" \"end\" is \"%f\"\n", r_refdef.fog_end); + Con_Printf(" \"red\" is \"%f\"\n", r_refdef.fog_red); + Con_Printf(" \"green\" is \"%f\"\n", r_refdef.fog_green); + Con_Printf(" \"blue\" is \"%f\"\n", r_refdef.fog_blue); + Con_Printf(" \"fade\" is \"%f\"\n", fade_time); + break; + case 2: //TEST + Fog_Update(r_refdef.fog_start, + r_refdef.fog_end, + r_refdef.fog_red, + r_refdef.fog_green, + r_refdef.fog_blue, + atof(Cmd_Argv(1))); + break; + case 3: + Fog_Update(atof(Cmd_Argv(1)), + atof(Cmd_Argv(2)), + r_refdef.fog_red, + r_refdef.fog_green, + r_refdef.fog_blue, + 0.0); + break; + case 4: + Fog_Update(r_refdef.fog_start, + r_refdef.fog_end, + CLAMP(0.0, atof(Cmd_Argv(1)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(2)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(3)), 100.0), + 0.0); + break; + case 5: //TEST + Fog_Update(r_refdef.fog_start, + r_refdef.fog_end, + CLAMP(0.0, atof(Cmd_Argv(1)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(2)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(3)), 100.0), + atof(Cmd_Argv(4))); + break; + case 6: + Fog_Update(atof(Cmd_Argv(1)), + atof(Cmd_Argv(2)), + CLAMP(0.0, atof(Cmd_Argv(3)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(4)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(5)), 100.0), + 0.0); + break; + case 7: + Fog_Update(atof(Cmd_Argv(1)), + atof(Cmd_Argv(2)), + CLAMP(0.0, atof(Cmd_Argv(3)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(4)), 100.0), + CLAMP(0.0, atof(Cmd_Argv(5)), 100.0), + atof(Cmd_Argv(6))); + break; + } +} + +/* +============= +Fog_ParseWorldspawn + +called at map load +============= +*/ +void Fog_ParseWorldspawn (void) +{ + char key[128], value[4096]; + char *data; + + //initially no fog + r_refdef.fog_start = 0; + old_start = 0; + + r_refdef.fog_end = -1; + old_end = -1; + + r_refdef.fog_red = 0.0; + old_red = 0.0; + + r_refdef.fog_green = 0.0; + old_green = 0.0; + + r_refdef.fog_blue = 0.0; + old_blue = 0.0; + + fade_time = 0.0; + fade_done = 0.0; + + data = COM_Parse(cl.worldmodel->entities); + if (!data) + return; // error + if (com_token[0] != '{') + return; // error + while (1) + { + data = COM_Parse(data); + if (!data) + return; // error + if (com_token[0] == '}') + break; // end of worldspawn + if (com_token[0] == '_') + strcpy(key, com_token + 1); + else + strcpy(key, com_token); + while (key[strlen(key)-1] == ' ') // remove trailing spaces + key[strlen(key)-1] = 0; + data = COM_Parse(data); + if (!data) + return; // error + strcpy(value, com_token); + + if (!strcmp("fog", key)) + { + sscanf(value, "%f %f %f %f %f", &r_refdef.fog_start, &r_refdef.fog_end, &r_refdef.fog_red, &r_refdef.fog_green, &r_refdef.fog_blue); + } + } +} + +/* +============= +Fog_SetupFrame + +called at the beginning of each frame +============= +*/ +void Fog_SetupFrame (void) +{ + float c[4]; + float f, s, e; + + if (fade_done > cl.time) + { + f = (fade_done - cl.time) / fade_time; + s = f * old_start + (1.0 - f) * r_refdef.fog_start; + e = f * old_end + (1.0 - f) * r_refdef.fog_end; + c[0] = f * old_red + (1.0 - f) * r_refdef.fog_red; + c[1] = f * old_green + (1.0 - f) * r_refdef.fog_green; + c[2] = f * old_blue + (1.0 - f) * r_refdef.fog_blue; + c[3] = r_skyfog.value; + } + else + { + s = r_refdef.fog_start; + e = r_refdef.fog_end; + c[0] = r_refdef.fog_red; + c[1] = r_refdef.fog_green; + c[2] = r_refdef.fog_blue; + c[3] = 1.0; + c[3] = r_skyfog.value; + } + + if(e == 0) + e = -1; + + sceGuFog ( s, e, GU_COLOR( c[0]* 0.01f, c[1]* 0.01f, c[2]* 0.01f, c[3] ) ); + + if(s == 0 || e < 0) + sceGuDisable(GU_FOG); +} + +/* +============= +Fog_SetColorForSkyS +Start called before drawing flat-colored sky +Crow_bar* +============= +*/ +void Fog_SetColorForSkyS (void) +{ + if (r_refdef.fog_end > 0.0f && r_skyfog.value) + { + float a = r_refdef.fog_end * 0.00025f; + float r = r_refdef.fog_red * 0.01f + (a * 0.25f); + float g = r_refdef.fog_green * 0.01f + (a * 0.25f); + float b = r_refdef.fog_blue * 0.01f + (a * 0.25f); + + if (a > 1.0f) + a = 1.0f; + if (r > 1.0f) + r = 1.0f; + if (g > 1.0f) + g = 1.0f; + if (b > 1.0f) + b = 1.0f; + + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuColor(GU_COLOR(r,g,b,a)); + + //sceGuEnable(GU_BLEND); + //sceGuBlendFunc(GU_ADD, GU_FIX, GU_FIX, GU_COLOR(r,g,b,a), GU_COLOR(r,g,b,a)); + } +} + +/* +============= +Fog_SetColorForSky +End called before drawing flat-colored sky +Crow_bar* +============= +*/ +void Fog_SetColorForSkyE (void) +{ + if (r_refdef.fog_end > 0.0f && r_skyfog.value) + { + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuColor(0xffffffff); + //sceGuDisable(GU_BLEND); + //sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + } +} + +/* +============= +Fog_GetStart + +returns current start of fog +============= +*/ +float Fog_GetStart (void) +{ + float f; + + if (fade_done > cl.time) + { + f = (fade_done - cl.time) / fade_time; + return f * old_start + (1.0 - f) * r_refdef.fog_start; + } + else + return r_refdef.fog_start; +} + +/* +============= +Fog_GetEnd + +returns current end of fog +============= +*/ +float Fog_GetEnd (void) +{ + float f; + + if (fade_done > cl.time) + { + f = (fade_done - cl.time) / fade_time; + return f * old_start + (1.0 - f) * r_refdef.fog_end; + } + else + return r_refdef.fog_end; +} + +/* +============= +Fog_EnableGFog + +called before drawing stuff that should be fogged +============= +*/ +void Fog_EnableGFog (void) +{ + if (!Fog_GetStart() == 0 || !Fog_GetEnd() <= 0) + sceGuEnable(GU_FOG); +} + +/* +============= +Fog_DisableGFog + +called after drawing stuff that should be fogged +============= +*/ +void Fog_DisableGFog (void) +{ + if (!Fog_GetStart() == 0 || !Fog_GetEnd() <= 0) + sceGuDisable(GU_FOG); +} + +/* +============= +Fog_SetColorForSky + +called before drawing flat-colored sky +============= +*/ +/* +void Fog_SetColorForSky (void) +{ + float c[3]; + float f, d; + + if (fade_done > cl.time) + { + f = (fade_done - cl.time) / fade_time; + d = f * old_density + (1.0 - f) * fog_density; + c[0] = f * old_red + (1.0 - f) * fog_red; + c[1] = f * old_green + (1.0 - f) * fog_green; + c[2] = f * old_blue + (1.0 - f) * fog_blue; + } + else + { + d = fog_density; + c[0] = fog_red; + c[1] = fog_green; + c[2] = fog_blue; + } + + if (d > 0) + glColor3fv (c); +} +*/ +//============================================================================== +// +// VOLUMETRIC FOG +// +//============================================================================== +/* + +void Fog_DrawVFog (void){} +void Fog_MarkModels (void){} +*/ +//============================================================================== +// +// INIT +// +//============================================================================== + +/* +============= +Fog_NewMap + +called whenever a map is loaded +============= +*/ + +void Fog_NewMap (void) +{ + Fog_ParseWorldspawn (); //for global fog +// Fog_MarkModels (); //for volumetric fog +} + +/* +============= +Fog_Init + +called when quake initializes +============= +*/ +void Fog_Init (void) +{ + Cmd_AddCommand ("fog",Fog_FogCommand_f); + + //set up global fog + r_refdef.fog_start = 0; + r_refdef.fog_end = -1; + r_refdef.fog_red = 0.5; + r_refdef.fog_green = 0.5; + r_refdef.fog_blue = 0.5; + fade_time = 1; +} diff --git a/source/psp/video_hardware_fullbright.cpp b/source/psp/video_hardware_fullbright.cpp new file mode 100644 index 0000000..ce100d6 --- /dev/null +++ b/source/psp/video_hardware_fullbright.cpp @@ -0,0 +1,63 @@ +//video_hardware_fullbright.c + +extern "C" +{ +#include "../quakedef.h" +} +#include + +void DrawGLPoly_ex (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) +{ + // gl_texsort 1 version + int i; + msurface_t *fa; + texture_t *t; + + if (r_fullbright.value) + return; + + 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) + { + sceGuDisable(GU_FOG); //Disable fog for fullbright textures + sceGuEnable(GU_BLEND); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + GL_Bind (t->fullbright); + DrawGLPoly_ex (fa->polys); + sceGuDisable (GU_BLEND); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuEnable(GU_FOG); + } + + fa->draw_this_frame = 0; + } +} + + diff --git a/source/psp/video_hardware_fullbright.h b/source/psp/video_hardware_fullbright.h new file mode 100644 index 0000000..822aac0 --- /dev/null +++ b/source/psp/video_hardware_fullbright.h @@ -0,0 +1,9 @@ +//video_hardware_fullbright.h +#include "video_hardware_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); + + diff --git a/source/psp/video_hardware_hlmdl.cpp b/source/psp/video_hardware_hlmdl.cpp new file mode 100644 index 0000000..e321c73 --- /dev/null +++ b/source/psp/video_hardware_hlmdl.cpp @@ -0,0 +1,909 @@ + +extern"C" +{ +#include "../quakedef.h" +} + +/* + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + Half-Life Model Renderer (Experimental) Copyright (C) 2001 James 'Ender' Brown [ender@quakesrc.org] 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. fromquake.h - + + render.c - apart from calculations (mostly range checking or value conversion code is a mix of standard Quake 1 + meshing, and vertex deforms. The rendering loop uses standard Quake 1 drawing, after SetupBones deforms the vertex. + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + Also, please note that it won't do all hl models.... + Nor will it work 100% +++++++++++++++++++++++++++ + modify by Crow_bar 2009 + 10.08.09 +++++++++++++++++++++++++++ + */ +#include "video_hardware_hlmdl.h" + +#include +#include +#include +using namespace std; +extern list mapTextureNameList; + +extern model_t *loadmodel; + +extern vec3_t lightcolor; // LordHavoc: .lit support +extern entity_t *currententity; +extern float *shadedots; +extern vec3_t shadevector; +extern float shadelight, ambientlight; +#define NUMVERTEXNORMALS 162 +extern float r_avertexnormals[NUMVERTEXNORMALS][3]; +#define SHADEDOT_QUANT 16 +extern float r_avertexnormal_dots[SHADEDOT_QUANT][256]; + +void QuaternionGLMatrix(float x, float y, float z, float w, vec4_t *GLM) +{ + GLM[0][0] = 1 - 2 * y * y - 2 * z * z; + GLM[1][0] = 2 * x * y + 2 * w * z; + GLM[2][0] = 2 * x * z - 2 * w * y; + GLM[0][1] = 2 * x * y - 2 * w * z; + GLM[1][1] = 1 - 2 * x * x - 2 * z * z; + GLM[2][1] = 2 * y * z + 2 * w * x; + GLM[0][2] = 2 * x * z + 2 * w * y; + GLM[1][2] = 2 * y * z - 2 * w * x; + GLM[2][2] = 1 - 2 * x * x - 2 * y * y; +} + +/* + ======================================================================================================================= + QuaternionGLAngle - Convert a GL angle to a quaternion matrix + ======================================================================================================================= + */ +void QuaternionGLAngle(const vec3_t angles, vec4_t quaternion) +{ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + float yaw = angles[2] * 0.5; + float pitch = angles[1] * 0.5; + float roll = angles[0] * 0.5; + float siny = sin(yaw); + float cosy = cos(yaw); + float sinp = sin(pitch); + float cosp = cos(pitch); + float sinr = sin(roll); + float cosr = cos(roll); + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + quaternion[0] = sinr * cosp * cosy - cosr * sinp * siny; + quaternion[1] = cosr * sinp * cosy + sinr * cosp * siny; + quaternion[2] = cosr * cosp * siny - sinr * sinp * cosy; + quaternion[3] = cosr * cosp * cosy + sinr * sinp * siny; +} + + + + + +matrix3x4 transform_matrix[128]; /* Vertex transformation matrix */ + +int g_chrome[MAXSTUDIOVERTS][2]; // texture coords for surface normals +int g_chromeage[MAXSTUDIOBONES]; // last time chrome vectors were updated +vec3_t g_chromeup[MAXSTUDIOBONES]; // chrome vector "up" in bone reference frames +vec3_t g_chromeright[MAXSTUDIOBONES]; // chrome vector "right" in bone reference frames +vec3_t g_vright = { 50, 50, 0 }; +vec3_t *g_pvlightvalues; +vec3_t g_blightvec[MAXSTUDIOBONES]; // light vectors in bone reference frames +vec3_t g_lightvalues[MAXSTUDIOVERTS]; // light surface normals +vec3_t g_lightvec; // light vector in model reference frame +vec3_t g_lightcolor; +int g_smodels_total; // cookie +vec3_t m_angles; + +void GL_Draw_HL_AliasFrame(short *order, vec3_t *transformed, float tex_w, float tex_h, float *lv); +void GL_Draw_HL_AliasChrome(short *order, vec3_t *transformed, float tex_w, float tex_h, float *lv); + +/* +======================================================================================================================= + Mod_LoadHLModel - read in the model's constituent parts +======================================================================================================================= +*/ + +extern char loadname[]; +extern int nonetexture; +qboolean Mod_LoadHLModel (model_t *mod, void *buffer) +{ + /*~~*/ + int i; + + hlmodelcache_t *model; + hlmdl_header_t *header; + hlmdl_tex_t *tex; + hlmdl_bone_t *bones; + hlmdl_bonecontroller_t *bonectls; + int start, end, total; + /*~~*/ + start = Hunk_LowMark (); + //load the model into hunk + model = static_cast(Hunk_Alloc(sizeof(hlmodelcache_t))); + + header = static_cast(Hunk_Alloc(com_filesize)); + memcpy(header, buffer, com_filesize); + + if (header->version != 10) + { + Con_Printf("Cannot load model %s - unknown version %i\n", mod->name, header->version); + Hunk_FreeToLowMark(start); + return qfalse; + } + + tex = (hlmdl_tex_t *) ((byte *) header + header->textures); + bones = (hlmdl_bone_t *) ((byte *) header + header->boneindex); + bonectls = (hlmdl_bonecontroller_t *) ((byte *) header + header->controllerindex); + + model->header = (char *)header - (char *)model; + Con_Printf("HLMDL Load: header"); + + model->textures = (char *)tex - (char *)model; + Con_Printf(", tex"); + + model->bones = (char *)bones - (char *)model; + Con_Printf(", bones"); + + model->bonectls = (char *)bonectls - (char *)model; + Con_Printf(", bonectls\n"); + + for(i = 0; i < header->numtextures; i++) + { + + tex[i].i = GL_LoadPalTex (tex[i].name, tex[i].w, tex[i].h, (byte *) header + tex[i].i, qtrue, GU_LINEAR, 0, (byte *) header + tex[i].w * tex[i].h + tex[i].i, PAL_RGB); + mapTextureNameList.push_back(tex[i].i); // for unload textures + } +// +// move the complete, relocatable alias model to the cache +// + end = Hunk_LowMark (); + + total = end - start; + + mod->type = mod_halflife; + + Cache_Alloc (&mod->cache, total, loadname); + + if (!mod->cache.data) + return qfalse; + + memcpy (mod->cache.data, model, total); + + Hunk_FreeToLowMark (start); + return qtrue; +} + +/* +====================================================================================================================== + HL_CurSequence - return the current sequence + + +====================================================================================================================== +*/ +int HL_CurSequence(hlmodel_t model) +{ + return model.sequence; +} + +/* +======================================================================================================================= + HL_NewSequence - animation control (just some range checking really) +======================================================================================================================= +*/ +int HL_NewSequence(hlmodel_t *model, int _inew) +{ + if(_inew < 0) + _inew = model->header->numseq - 1; + else if(_inew >= model->header->numseq) + _inew = 0; + + model->sequence = _inew; + model->frame = 0; + { + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + hlmdl_sequencelist_t *pseqdesc; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + if(_inew == 0) + { + pseqdesc = (hlmdl_sequencelist_t *) ((byte *) model->header + model->header->seqindex) + model->sequence; + } + else + { + pseqdesc = (hlmdl_sequencelist_t *) ((byte *) model->header + model->header->seqindex) + model->sequence; + } + + Sys_Printf("Current Sequence: %s\n", pseqdesc->name); + } + + return model->sequence; +} + +/* +======================================================================================================================= + HL_SetController - control where the model is facing (upper body usually) +======================================================================================================================= + */ +void HL_SetController(hlmodel_t *model, int num, float value) +{ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + int real, limit; + hlmdl_bonecontroller_t *control = (hlmdl_bonecontroller_t *) + ((byte *) model->header + model->header->controllerindex); + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + if(num >= model->header->numcontrollers) return; + + if(num == 4) + { + limit = 64; + } + else + { + limit = 255; + } + + if(control->type & (0x0008 | 0x0010 | 0x0020)) + { + if(control->end < control->start) value = -value; + + if(control->start + 359.0 >= control->end) + { + if(value > ((control->start + control->end) / 2.0) + 180) value = value - 360; + if(value < ((control->start + control->end) / 2.0) - 180) value = value + 360; + } + else + { + if(value > 360) + value = value - (int) (value / 360.0) * 360.0; + else if(value < 0) + value = value + (int) ((value / -360.0) + 1) * 360.0; + } + } + + real = limit * (value - control[num].start) / (control[num].end - control[num].start); + if(real < 0) real = 0; + if(real > limit) real = limit; + model->controller[num] = real; +} + +/* +======================================================================================================================= + HL_CalculateBones - calculate bone positions - quaternion+vector in one function +======================================================================================================================= + */ +void HL_CalculateBones(int offset, int frame, vec4_t adjust, hlmdl_bone_t *bone, hlmdl_anim_t *animation, float *destination) +{ + /*~~~~~~~~~~*/ + int i; + vec3_t angle; + /*~~~~~~~~~~*/ + + /* For each vector */ + for(i = 0; i < 3; i++) + { + /*~~~~~~~~~~~~~~~*/ + int o = i + offset; /* Take the value offset - allows quaternion & vector in one function */ + /*~~~~~~~~~~~~~~~*/ + + angle[i] = bone->value[o]; /* Take the bone value */ + + if(animation->offset[o] != 0) + { + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + int tempframe = frame; + hlmdl_animvalue_t *animvalue = (hlmdl_animvalue_t *) ((byte *) animation + animation->offset[o]); + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + /* find values including the required frame */ + while(animvalue->num.total <= tempframe) + { + tempframe -= animvalue->num.total; + animvalue += animvalue->num.valid + 1; + } + if(animvalue->num.valid > tempframe) + { + if(animvalue->num.valid > (tempframe + 1)) + angle[i] += animvalue[tempframe + 1].value * 1; // + 0 * animvalue[tempframe + 2].value * bone->scale[o]; + else + angle[i] = animvalue[animvalue->num.valid].value; + angle[i] = bone->value[o] + angle[i] * bone->scale[o]; + } + else + { + if(animvalue->num.total <= tempframe + 1) + { + angle[i] += + (animvalue[animvalue->num.valid].value * 1 + + 0 * animvalue[animvalue->num.valid + 2].value) * + bone->scale[o]; + } + else + { + angle[i] += animvalue[animvalue->num.valid].value * bone->scale[o]; + } + } + } + + if(bone->bonecontroller[o] != -1) { /* Add the programmable offset. */ + angle[i] += adjust[bone->bonecontroller[o]]; + } + } + + if(offset < 3) + { + VectorCopy(angle, destination); /* Just a standard vector */ + } + else + { + QuaternionGLAngle(angle, destination); /* A quaternion */ + } +} + +/* +======================================================================================================================= + HL_CalcBoneAdj - Calculate the adjustment values for the programmable controllers +======================================================================================================================= + */ +void HL_CalcBoneAdj(hlmodel_t *model) +{ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + int i; + float value; + hlmdl_bonecontroller_t *control = (hlmdl_bonecontroller_t *) + ((byte *) model->header + model->header->controllerindex); + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + for(i = 0; i < model->header->numcontrollers; i++) + { + /*~~~~~~~~~~~~~~~~~~~~~*/ + int j = control[i].index; + /*~~~~~~~~~~~~~~~~~~~~~*/ +/* + if(control[i].type & 0x8000) + { + value = model->controller[j] + control[i].start; + } + else + { + value = model->controller[j]; + if(value < 0) + value = 0; + else if(value > 1.0) + value = 1.0; + value = (1.0 - value) * control[i].start + value * control[i].end; + } +*/ + + if (j <= 3) + { + // check for 360% wrapping + if (control[i].type & STUDIO_RLOOP) + { + value = model->controller[j] * (360.0/256.0) + control[i].start; + } + else + { + value = model->controller[j] / 255.0; + if (value < 0) + value = 0; + if (value > 1.0) + value = 1.0; + value = (1.0 - value) * control[i].start + value * control[i].end; + } + // Con_DPrintf( "%d %d %f : %f\n", m_controller[i], m_prevcontroller[i], value, dadt ); + } + else + { + value = /*model->mouth*/1 / 64.0; + if (value > 1.0) + value = 1.0; + value = (1.0 - value) * control[i].start + value * control[i].end; + // Con_DPrintf("%d %f\n", mouthopen, value ); + } + + /* Rotational controllers need their values converted */ + switch(control[i].type & STUDIO_TYPES) + { + case STUDIO_XR: + case STUDIO_YR: + case STUDIO_ZR: + model->adjust[i] = value * (M_PI / 180.0); + break; + case STUDIO_X: + case STUDIO_Y: + case STUDIO_Z: + model->adjust[i] = value; + break; + } + } +} + +/* +======================================================================================================================= + HL_SetupBones - determine where vertex should be using bone movements +======================================================================================================================= + */ +void HL_SetupBones(hlmodel_t *model) +{ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + int i; + float matrix[3][4]; + static vec3_t positions[128]; + static vec4_t quaternions[128]; + hlmdl_sequencelist_t *sequence = (hlmdl_sequencelist_t *) ((byte *) model->header + model->header->seqindex) + + model->sequence; + hlmdl_sequencedata_t *sequencedata = (hlmdl_sequencedata_t *) + ((byte *) model->header + model->header->seqgroups) + + sequence->seqindex; + hlmdl_anim_t *animation = (hlmdl_anim_t *) + ((byte *) model->header + sequencedata->data + sequence->index); + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + HL_CalcBoneAdj(model); /* Deal with programmable controllers */ + + if(sequence->motiontype & STUDIO_X) + positions[sequence->motionbone][0] = 0.0; + if(sequence->motiontype & STUDIO_Y) + positions[sequence->motionbone][1] = 0.0; + if(sequence->motiontype & STUDIO_Z) + positions[sequence->motionbone][2] = 0.0; + + /* Sys_Printf("Frame: %i\n", model->frame); */ + for(i = 0; i < model->header->numbones; i++) + { + /* + * There are two vector offsets in the structure. The first seems to be the + * positions of the bones, the second the quats of the bone matrix itself. We + * convert it inside the routine - Inconsistant, but hey.. so's the whole model + * format. + */ + HL_CalculateBones(0, model->frame, model->adjust, model->bones + i, animation + i, positions[i]); + HL_CalculateBones(3, model->frame, model->adjust, model->bones + i, animation + i, quaternions[i]); + + /* FIXME: Blend the bones and make them cry :) */ + QuaternionGLMatrix(quaternions[i][0], quaternions[i][1], quaternions[i][2], quaternions[i][3], matrix); + matrix[0][3] = positions[i][0]; + matrix[1][3] = positions[i][1]; + matrix[2][3] = positions[i][2]; + + /* If we have a parent, take the addition. Otherwise just copy the values */ + if(model->bones[i].parent>=0) + { + R_ConcatTransforms(transform_matrix[model->bones[i].parent], matrix, transform_matrix[i]); + } + else + { + memcpy(transform_matrix[i], matrix, 12 * sizeof(float)); + } + } +} + +/* +======================================================================================================================= + Chrome +======================================================================================================================= +*/ +/* +================ +TransformFinalVert +================ +*/ +void Lighting (float *lv, int bone, int flags, vec3_t normal) +{ + float illum; + float lightcos; + + illum = ambientlight; + + if (flags & STUDIO_NF_FLATSHADE) + { + illum += shadelight * 0.8; + } + else + { + float r; + lightcos = DotProduct (normal, g_blightvec[bone]); // -1 colinear, 1 opposite + + if (lightcos > 1.0) + lightcos = 1; + + illum += shadelight; + + //r = g_lambert; + //if (r <= 1.0) + r = 1.0; + + lightcos = (lightcos + (r - 1.0)) / r; // do modified hemispherical lighting + if (lightcos > 0.0) + { + illum -= shadelight * lightcos; + } + if (illum <= 0) + illum = 0; + } + + if (illum > 255) + illum = 255; + *lv = illum / 255.0; // Light from 0 to 1.0 +} + +void VectorIRotate (const vec3_t in1, const float in2[3][4], vec3_t out) +{ + out[0] = in1[0]*in2[0][0] + in1[1]*in2[1][0] + in1[2]*in2[2][0]; + out[1] = in1[0]*in2[0][1] + in1[1]*in2[1][1] + in1[2]*in2[2][1]; + out[2] = in1[0]*in2[0][2] + in1[1]*in2[1][2] + in1[2]*in2[2][2]; +} + +void SetupLighting ( hlmodel_t *model ) +{ // dr_mabuse1981: edited abit to make halflife model NOT look ughly and for Shpulds model brightness cvar + int i; + if(r_model_brightness.value) + ambientlight = 256; + else + ambientlight = 128; + shadelight = 33; + + g_lightvec[0] = shadevector[0]; + g_lightvec[1] = shadevector[1]; + g_lightvec[2] = shadevector[2]; + + g_lightcolor[0] = lightcolor[0]; + g_lightcolor[1] = lightcolor[1]; + g_lightcolor[2] = lightcolor[2]; + + // TODO: only do it for bones that actually have textures + for (i = 0; i < model->header->numbones; i++) + { + VectorIRotate( g_lightvec, transform_matrix[i], g_blightvec[i] ); + } +} + +void Chrome (int *pchrome, int bone, vec3_t normal) +{ + float n; + + if (g_chromeage[bone] != g_smodels_total) + { + // calculate vectors from the viewer to the bone. This roughly adjusts for position + vec3_t chromeupvec; // g_chrome t vector in world reference frame + vec3_t chromerightvec; // g_chrome s vector in world reference frame + vec3_t tmp; // vector pointing at bone in world reference frame + VectorScale( m_angles, -1, tmp ); + tmp[0] += transform_matrix[bone][0][3]; + tmp[1] += transform_matrix[bone][1][3]; + tmp[2] += transform_matrix[bone][2][3]; + VectorNormalize( tmp ); + CrossProduct( tmp, g_vright, chromeupvec ); + VectorNormalize( chromeupvec ); + CrossProduct( tmp, chromeupvec, chromerightvec ); + VectorNormalize( chromerightvec ); + VectorIRotate( chromeupvec, transform_matrix[bone], g_chromeup[bone] ); + VectorIRotate( chromerightvec, transform_matrix[bone], g_chromeright[bone] ); + g_chromeage[bone] = g_smodels_total; + } + // calc s coord + n = DotProduct( normal, g_chromeright[bone] ); + pchrome[0] = (n + 1.0) * 32; // FIX: make this a float + // calc t coord + n = DotProduct( normal, g_chromeup[bone] ); + pchrome[1] = (n + 1.0) * 32; // FIX: make this a float +} + +/* +======================================================================================================================= + R_Draw_HL_AliasModel - main drawing function +======================================================================================================================= +*/ +void R_DrawHLModel(entity_t *curent) +{ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + hlmodelcache_t *modelc = static_cast(Mod_Extradata(curent->model)); + hlmodel_t model; + int b, m, v; + short *skins; + hlmdl_sequencelist_t *sequence; + float *lv; + float lv_tmp; + int lnum; + vec3_t dist; + float add; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + //general model + model.header = (hlmdl_header_t *) ((char *)modelc + modelc->header); + model.textures = (hlmdl_tex_t *) ((char *)modelc + modelc->textures); + model.bones = (hlmdl_bone_t *) ((char *)modelc + modelc->bones); + model.bonectls = (hlmdl_bonecontroller_t *) ((char *)modelc + modelc->bonectls); + + //specific to entity + model.sequence = curent->frame; + model.frame = 0; + model.frametime = 0; + + HL_NewSequence(&model, curent->frame); + + skins = (short *) ((byte *) model.header + model.header->skins); + sequence = (hlmdl_sequencelist_t *) ((byte *) model.header + model.header->seqindex) + + model.sequence; + + model.controller[0] = curent->bonecontrols[0]; + model.controller[1] = curent->bonecontrols[1]; + model.controller[2] = curent->bonecontrols[2]; + model.controller[3] = curent->bonecontrols[3]; + + model.frametime += (cl.time /* - cl.lerpents[curent->keynum].framechange*/)*sequence->timing; + + if (model.frametime>=1) + { + model.frame += (int) model.frametime; + model.frametime -= (int)model.frametime; + } + + if (!sequence->numframes) + return; +/* + if(model.frame >= sequence->numframes) + model.frame %= sequence->numframes; +*/ + model.frame = curent->frame; // dr_mabuse1981: This makes your Halflife model frame based (only for sequence 0 atm but better than the old shit.) + if (sequence->motiontype) + model.frame = sequence->numframes-1; + + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + sceGuShadeModel(GU_SMOOTH); + + Con_Printf("%s %i\n", sequence->name, sequence->unknown1[0]); + + sceGumPushMatrix(); + + for(int i = 0; i < 3; i++) m_angles[i] = currententity->angles[i]; + + R_LightPoint(curent->origin); // LordHavoc: lightcolor is all that matters from this + + 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 + } + } + + VectorScale(lightcolor, 1.0f / 200.0f, lightcolor); + + float an; + an = curent->angles[1]/180*M_PI; + shadevector[0] = cosf(-an); + shadevector[1] = sinf(-an); + shadevector[2] = 1; + VectorNormalize (shadevector); + + R_BlendedRotateForEntity(curent, 0); + + HL_SetupBones(&model); /* Setup the bones */ + SetupLighting(&model); /* Setup the light */ + + g_smodels_total++; // render data cache cookie + + g_pvlightvalues = &g_lightvalues[0]; + + /* Manipulate each mesh directly */ + for(b = 0; b < model.header->numbodyparts; b++) + { + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + hlmdl_bodypart_t *bodypart = (hlmdl_bodypart_t *) ((byte *) model.header + model.header->bodypartindex) + + b; + int bodyindex = (0 / bodypart->base) % bodypart->nummodels; + hlmdl_model_t *amodel = (hlmdl_model_t *) ((byte *) model.header + bodypart->modelindex) + bodyindex; + byte *bone = ((byte *) model.header + amodel->vertinfoindex); + byte *nbone = ((byte *) model.header + amodel->norminfoindex); + vec3_t *verts = (vec3_t *) ((byte *) model.header + amodel->vertindex); + vec3_t *norms = (vec3_t *) ((byte *) model.header + amodel->normindex); + vec3_t transformed[2048]; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + for(v = 0; v < amodel->numverts; v++) // Transform per the matrix + { + VectorTransform(verts[v], transform_matrix[bone[v]], transformed[v]); + } + + lv = (float *)g_pvlightvalues; + for(m = 0; m < amodel->nummesh; m++) + { + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + hlmdl_mesh_t *mesh = (hlmdl_mesh_t *) ((byte *) model.header + amodel->meshindex) + m; + float tex_w = 1.0f / model.textures[skins[mesh->skinindex]].w; + float tex_h = 1.0f / model.textures[skins[mesh->skinindex]].h; + int flags = model.textures[skins[mesh->skinindex]].flags; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + for (int c = 0; c < mesh->numnorms; c++, lv += 3, norms++, nbone++) + { + Lighting (&lv_tmp, *nbone, flags, (float *)norms); + // FIX: move this check out of the inner loop + if (flags & STUDIO_NF_CHROME) + Chrome(g_chrome[(float (*)[3])lv - g_pvlightvalues], *nbone, (float *)norms ); + + lv[0] = lv_tmp * g_lightcolor[0]; + lv[1] = lv_tmp * g_lightcolor[1]; + lv[2] = lv_tmp * g_lightcolor[2]; + } + + if (model.textures[skins[mesh->skinindex]].flags & STUDIO_NF_CHROME) + { + GL_Bind(model.textures[skins[mesh->skinindex]].i); + GL_Draw_HL_AliasChrome((short *) ((byte *) model.header + mesh->index), transformed, tex_w, tex_h, lv); + } + else + { + GL_Bind(model.textures[skins[mesh->skinindex]].i); + GL_Draw_HL_AliasFrame((short *) ((byte *) model.header + mesh->index), transformed, tex_w, tex_h, lv); + } + } + } + sceGuShadeModel(GU_FLAT); + sceGumPopMatrix(); + sceGumUpdateMatrix(); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); +} + +/* + ======================================================================================================================= + GL_Draw_HL_AliasFrame - clip and draw all triangles + ======================================================================================================================= + */ +void GL_Draw_HL_AliasFrame(short *order, vec3_t *transformed, float tex_w, float tex_h, float *lv) +{ + int count = 0; + while (1) + { + count = *order++; + + if (!count) + break; // done + + int prim; + + if(count < 0) + { + count = -count; + prim = GU_TRIANGLE_FAN; + } + else + { + prim = GU_TRIANGLE_STRIP; + } + + // Allocate the vertices. + struct vertex + { + float u, v; + unsigned int color; + float x, y, z; + }; + + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * count)); + + for (int vertex_index = 0; vertex_index < count; ++vertex_index) + { + float *verts = transformed[order[0]]; + + out[vertex_index].u = order[2] * tex_w; + out[vertex_index].v = order[3] * tex_h; + order += 4; + + lv = g_pvlightvalues[order[1]]; + //color clamp + for(int i = 0; i < 3; i++) + { + if(lv[i] > 1) + lv[i] = 1; + + if(lv[i] < 0) + lv[i] = 0; + } + out[vertex_index].x = verts[0]; + out[vertex_index].y = verts[1]; + out[vertex_index].z = verts[2]; + out[vertex_index].color = GU_COLOR(lv[0], lv[1], lv[2], 1.0f); + } + if(r_showtris.value) + { + sceGuDisable(GU_TEXTURE_2D); + } + sceGuDrawArray(r_showtris.value ? GU_LINE_STRIP : prim, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_COLOR_8888, count, 0, out); + if(r_showtris.value) + { + sceGuEnable(GU_TEXTURE_2D); + } + } +} + +void GL_Draw_HL_AliasChrome(short *order, vec3_t *transformed, float tex_w, float tex_h, float *lv) +{ + int count = 0; + while (1) + { + count = *order++; + + if (!count) + break; // done + + int prim; + + if(count < 0) + { + count = -count; + prim = GU_TRIANGLE_FAN; + } + else + { + prim = GU_TRIANGLE_STRIP; + } + + // Allocate the vertices. + struct vertex + { + float u, v; + unsigned int color; + float x, y, z; + }; + + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * count)); + + for (int vertex_index = 0; vertex_index < count; ++vertex_index) + { + float *verts = transformed[order[0]]; + + out[vertex_index].u = g_chrome[order[1]][0] * tex_w; + out[vertex_index].v = g_chrome[order[1]][1] * tex_h; + order += 4; + + lv = g_pvlightvalues[order[1]]; + //color clamp + for(int i = 0; i < 3; i++) + { + if(lv[i] > 1) + lv[i] = 1; + + if(lv[i] < 0) + lv[i] = 0; + } + out[vertex_index].x = verts[0]; + out[vertex_index].y = verts[1]; + out[vertex_index].z = verts[2]; + out[vertex_index].color = GU_COLOR(lv[0], lv[1], lv[2], 1.0f); + } + if(r_showtris.value) + { + sceGuDisable(GU_TEXTURE_2D); + } + sceGuDrawArray(r_showtris.value ? GU_LINE_STRIP : prim, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_COLOR_8888, count, 0, out); + if(r_showtris.value) + { + sceGuEnable(GU_TEXTURE_2D); + } + } +} + diff --git a/source/psp/video_hardware_hlmdl.h b/source/psp/video_hardware_hlmdl.h new file mode 100644 index 0000000..4a80c57 --- /dev/null +++ b/source/psp/video_hardware_hlmdl.h @@ -0,0 +1,356 @@ +/* + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + Half-Life Model Renderer (Experimental) Copyright (C) 2001 James 'Ender' Brown [ender@quakesrc.org] 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. fromquake.h - + + model_hl.h - halflife model structure + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ +#define HLPOLYHEADER (('T' << 24) + ('S' << 16) + ('D' << 8) + 'I') /* little-endian "IDST" */ +#define HLMDLHEADER "IDST" + +#define MAXSTUDIOTRIANGLES 20000 // TODO: tune this +#define MAXSTUDIOVERTS 2048 // TODO: tune this +#define MAXSTUDIOSEQUENCES 256 // total animation sequences +#define MAXSTUDIOSKINS 100 // total textures +#define MAXSTUDIOSRCBONES 512 // bones allowed at source movement +#define MAXSTUDIOBONES 128 // total bones actually used +#define MAXSTUDIOMODELS 32 // sub-models per model +#define MAXSTUDIOBODYPARTS 32 +#define MAXSTUDIOGROUPS 4 +#define MAXSTUDIOANIMATIONS 512 // per sequence +#define MAXSTUDIOMESHES 256 +#define MAXSTUDIOEVENTS 1024 +#define MAXSTUDIOPIVOTS 256 +#define MAXSTUDIOCONTROLLERS 8 + +/* + ----------------------------------------------------------------------------------------------------------------------- + main model header + ----------------------------------------------------------------------------------------------------------------------- + */ +typedef struct +{ + int filetypeid; //IDSP + int version; //10 + char name[64]; + int filesize; + vec3_t unknown3[5]; + + int flags; + int numbones; + int boneindex; + int numcontrollers; + int controllerindex; + int unknown5[2]; + int numseq; + int seqindex; + + int numseqgroups; // demand loaded sequences + int seqgroups; + + int numtextures; + int textures; + int texturedataindex; + int numskinref; // replaceable textures + int numskinfamilies; + + + int skins; + int numbodyparts; + int bodypartindex; + + int unknown9[8]; +} hlmdl_header_t; + +typedef struct +{ + int id; + int version; + char name[64]; + int length; + vec3_t eyeposition; // ideal eye position + vec3_t min; // ideal movement hull size + vec3_t max; + vec3_t bbmin; // clipping bounding box + vec3_t bbmax; + int flags; + int numbones; // bones + int boneindex; + int numbonecontrollers; // bone controllers + int bonecontrollerindex; + int numhitboxes; // complex bounding boxes + int hitboxindex; + int numseq; // animation sequences + int seqindex; + int numseqgroups; // demand loaded sequences + int seqgroupindex; + int numtextures; // raw textures + int textureindex; + + int texturedataindex; + int numskinref; // replaceable textures + int numskinfamilies; + + int skinindex; + int numbodyparts; + int bodypartindex; + int numattachments; // queryable attachable points + int attachmentindex; + int soundtable; + int soundindex; + int soundgroups; + int soundgroupindex; + int numtransitions; // animation node to animation node transition graph + int transitionindex; +} studiohdr_t; + +/* + ----------------------------------------------------------------------------------------------------------------------- + skin info + ----------------------------------------------------------------------------------------------------------------------- +*/ + +// lighting options +#define STUDIO_NF_FLATSHADE 0x0001 +#define STUDIO_NF_CHROME 0x0002 +#define STUDIO_NF_FULLBRIGHT 0x0004 + +typedef struct +{ + char name[64]; + int flags; + int w; /* width */ + int h; /* height */ + int i; /* index */ +} hlmdl_tex_t; + + +/* + ----------------------------------------------------------------------------------------------------------------------- + body part index + ----------------------------------------------------------------------------------------------------------------------- + */ +typedef struct +{ + char name[64]; + int nummodels; + int base; + int modelindex; +} hlmdl_bodypart_t; + +/* + ----------------------------------------------------------------------------------------------------------------------- + meshes + ----------------------------------------------------------------------------------------------------------------------- + */ +typedef struct +{ + int numtris; + int index; + int skinindex; + int numnorms; // per mesh normals + int normindex; // normal vec3_t +} hlmdl_mesh_t; + +/* + ----------------------------------------------------------------------------------------------------------------------- + bones + ----------------------------------------------------------------------------------------------------------------------- + */ +typedef struct +{ + char name[32]; + int parent; + int unknown1; + int bonecontroller[6]; + float value[6]; + float scale[6]; +} hlmdl_bone_t; + +/* + ----------------------------------------------------------------------------------------------------------------------- + bone controllers + ----------------------------------------------------------------------------------------------------------------------- + */ +typedef struct +{ + int name; + int type; + float start; + float end; + int unknown1; + int index; +} hlmdl_bonecontroller_t; + +/* + ----------------------------------------------------------------------------------------------------------------------- + halflife model descriptor + ----------------------------------------------------------------------------------------------------------------------- + */ +typedef struct +{ + char name[64]; + + int type; + + float boundingradius; + + int nummesh; + int meshindex; + + int numverts; + int vertinfoindex; + int vertindex; + + int numnorms; + int norminfoindex; + int normindex; + + int numgroups; + int groupindex; +} hlmdl_model_t; + +/* + ----------------------------------------------------------------------------------------------------------------------- + animation + ----------------------------------------------------------------------------------------------------------------------- + */ +typedef struct +{ + unsigned short offset[6]; +} hlmdl_anim_t; + +/* + ----------------------------------------------------------------------------------------------------------------------- + animation frames + ----------------------------------------------------------------------------------------------------------------------- + */ +typedef union +{ + struct + { + byte valid; + byte total; + } num; + short value; +} hlmdl_animvalue_t; + +/* + ----------------------------------------------------------------------------------------------------------------------- + sequence descriptions + ----------------------------------------------------------------------------------------------------------------------- + */ +typedef struct +{ + char name[32]; + float timing; + int unknown1[5]; + int numframes; + int unknown2[2]; + int motiontype; + int motionbone; + vec3_t unknown3; + int unknown4[2]; + vec3_t unknown5[2]; + int unknown6; + int index; + int unknown7[2]; + float unknown[4]; + int unknown8; + int seqindex; + int unknown9[4]; +} hlmdl_sequencelist_t; + +/* + ----------------------------------------------------------------------------------------------------------------------- + sequence groups + ----------------------------------------------------------------------------------------------------------------------- + */ +typedef struct +{ + char name[96]; /* should be split label[32] and name[64] */ + void * cache; + int data; +} hlmdl_sequencedata_t; + +/* + ----------------------------------------------------------------------------------------------------------------------- + halflife model internal structure + ----------------------------------------------------------------------------------------------------------------------- + */ +typedef struct +{ + int sequence; + int frame; /* Current animation sequence and frame */ + float frametime; /* Time of last frame drawn */ + float controller[4]; /* Position of bone controllers */ + float adjust[4]; + + /* Static pointers */ + hlmdl_header_t *header; + hlmdl_tex_t *textures; + hlmdl_bone_t *bones; + hlmdl_bonecontroller_t *bonectls; +} hlmodel_t; + +typedef struct //this is stored as the cache. an hlmodel_t is generated when drawing +{ + int header; + int textures; + int bones; + int bonectls; +} hlmodelcache_t; + +// lighting options +#define STUDIO_NF_FLATSHADE 0x0001 +#define STUDIO_NF_CHROME 0x0002 +#define STUDIO_NF_FULLBRIGHT 0x0004 + +// motion flags +#define STUDIO_X 0x0001 +#define STUDIO_Y 0x0002 +#define STUDIO_Z 0x0004 +#define STUDIO_XR 0x0008 +#define STUDIO_YR 0x0010 +#define STUDIO_ZR 0x0020 +#define STUDIO_LX 0x0040 +#define STUDIO_LY 0x0080 +#define STUDIO_LZ 0x0100 +#define STUDIO_AX 0x0200 +#define STUDIO_AY 0x0400 +#define STUDIO_AZ 0x0800 +#define STUDIO_AXR 0x1000 +#define STUDIO_AYR 0x2000 +#define STUDIO_AZR 0x4000 +#define STUDIO_TYPES 0x7FFF +#define STUDIO_RLOOP 0x8000 // controller that wraps shortest distance + +// sequence flags +#define STUDIO_LOOPING 0x0001 + +// bone flags +#define STUDIO_HAS_NORMALS 0x0001 +#define STUDIO_HAS_VERTICES 0x0002 +#define STUDIO_HAS_BBOX 0x0004 +#define STUDIO_HAS_CHROME 0x0008 // if any of the textures have chrome on them + +#define RAD_TO_STUDIO (32768.0/M_PI) +#define STUDIO_TO_RAD (M_PI/32768.0) + +/* HL mathlib prototypes: */ +void QuaternionGLAngle(const vec3_t angles, vec4_t quaternion); +void QuaternionGLMatrix(float x, float y, float z, float w, vec4_t *GLM); +//void UploadTexture(hlmdl_tex_t *ptexture, qbyte *data, qbyte *pal); + +/* HL drawing */ +qboolean Mod_LoadHLModel (model_t *mod, void *buffer); +int HL_CurSequence(hlmodel_t model); +int HL_NewSequence(hlmodel_t * model, int _inew); +void HL_SetController(hlmodel_t *model, int num, float value); +void R_DrawHLModel(entity_t *curent); diff --git a/source/psp/video_hardware_images.cpp b/source/psp/video_hardware_images.cpp new file mode 100644 index 0000000..c453194 --- /dev/null +++ b/source/psp/video_hardware_images.cpp @@ -0,0 +1,1075 @@ +/* +Copyright (C) 2008-2009 Crow_bar. + +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 + +extern "C" +{ +#include +#include "../quakedef.h" +} + +cvar_t jpeg_compression_level = {"jpeg_compression_level", "75"}; + +int image_width; +int image_height; +static int image_palette_type = 0; +static byte image_palette[1024]; + + +#define IMAGE_MAX_DIMENSIONS 4096 + +/* +============= +LoadJPG +============= +*/ +byte *LoadJPG (FILE *fin, int matchwidth, int matchheight) +{ + int i, row_stride; + byte *data, *scanline, *p; + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + + cinfo.err = jpeg_std_error (&jerr); + jpeg_create_decompress (&cinfo); + jpeg_stdio_src (&cinfo, fin); + jpeg_read_header (&cinfo, true); + jpeg_start_decompress (&cinfo); + + if (cinfo.image_width > IMAGE_MAX_DIMENSIONS || cinfo.image_height > IMAGE_MAX_DIMENSIONS) + { + return NULL; + } + + if ((matchwidth && cinfo.image_width != matchwidth) || (matchheight && cinfo.image_height != matchheight)) + { + jpeg_finish_decompress (&cinfo); + jpeg_destroy_decompress (&cinfo); + return NULL; + } + + Con_Printf("JPG: Allocating data\n"); + + data = static_cast(malloc (cinfo.image_width * cinfo.image_height * 4)); + row_stride = cinfo.output_width * cinfo.output_components; + scanline = static_cast(malloc (row_stride)); + + Con_Printf("JPG: done allocating data\n"); + + p = data; + while (cinfo.output_scanline < cinfo.output_height) + { + jpeg_read_scanlines (&cinfo, &scanline, 1); + + // convert the image to RGBA + switch (cinfo.output_components) + { + // RGB images + case 3: + for (i=0 ; imanufacturer != 0x0a + || pcx->version != 5 + || pcx->encoding != 1 + || pcx->bits_per_pixel != 8 + || pcx->xmax >= 320 + || pcx->ymax >= 256) + { + Con_Printf ("Bad pcx file\n"); + return NULL; + } + + if (matchwidth && (pcx->xmax+1) != matchwidth) + { + fclose (f); + return NULL; + } + + if (matchheight && (pcx->ymax+1) != matchheight) + { + fclose (f); + return NULL; + } + + // seek to palette + fseek (f, -768, SEEK_END); + fread (palette, 1, 768, f); + + fseek (f, sizeof(pcxbuf) - 4, SEEK_SET); + + count = (pcx->xmax+1) * (pcx->ymax+1); + + image_rgba = static_cast(malloc( count )); + + for (y=0 ; y<=pcx->ymax ; y++) + { + pix = image_rgba + y * (pcx->xmax+1); + for (x=0 ; x<=pcx->xmax ; ) // muff - fixed - was referencing ymax + { + dataByte = fgetc(f); + + if((dataByte & 0xC0) == 0xC0) + { + runLength = dataByte & 0x3F; + dataByte = fgetc(f); + } + else + runLength = 1; + + while(runLength-- > 0) + pix[x++] = dataByte; + } + } + image_width = pcx->xmax+1; + image_height = pcx->ymax+1; + + memcpy(image_palette, palette, sizeof(palette)); + image_palette_type = PAL_RGB; + + fclose (f); + + return image_rgba; +} + +/* +================ +LoadWal +================ +*/ + +typedef struct miptexq2_s +{ + char name[32]; + unsigned width, height; + unsigned offsets[4]; // four mip maps stored + char animname[32]; // next frame in animation chain + int flags; + int contents; + int value; +} miptexq2_t; + +byte *LoadWAL (char *name) +{ + miptexq2_t *mt; + int width, height, ofs, size; + byte *data; + + mt = (miptexq2_t*)COM_LoadFile (name, 0); + if (!mt) + { + return NULL; + } + + width = LittleLong (mt->width); + height = LittleLong (mt->height); + ofs = LittleLong (mt->offsets[0]); + + size = width * height; + + data = static_cast(malloc(size)); + memcpy(data, (byte *)mt + ofs, size); + + image_palette_type = PAL_Q2; + + Z_Free(mt); + + return data; +} + +/* +========================================================= + + Targa + +========================================================= +*/ + +#define TGA_MAXCOLORS 16384 + +/* Definitions for image types. */ +#define TGA_Null 0 /* no image data */ +#define TGA_Map 1 /* Uncompressed, color-mapped images. */ +#define TGA_RGB 2 /* Uncompressed, RGB images. */ +#define TGA_Mono 3 /* Uncompressed, black and white images. */ +#define TGA_RLEMap 9 /* Runlength encoded color-mapped images. */ +#define TGA_RLERGB 10 /* Runlength encoded RGB images. */ +#define TGA_RLEMono 11 /* Compressed, black and white images. */ +#define TGA_CompMap 32 /* Compressed color-mapped data, using Huffman, Delta, and runlength encoding. */ +#define TGA_CompMap4 33 /* Compressed color-mapped data, using Huffman, Delta, and runlength encoding. 4-pass quadtree-type process. */ + +/* Definitions for interleave flag. */ +#define TGA_IL_None 0 /* non-interleaved. */ +#define TGA_IL_Two 1 /* two-way (even/odd) interleaving */ +#define TGA_IL_Four 2 /* four way interleaving */ +#define TGA_IL_Reserved 3 /* reserved */ + +/* Definitions for origin flag */ +#define TGA_O_UPPER 0 /* Origin in lower left-hand corner. */ +#define TGA_O_LOWER 1 /* Origin in upper left-hand corner. */ + +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; + +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); +} + +/* +============= +LoadTGA +============= +*/ +byte *LoadTGA (FILE *fin, int matchwidth, int matchheight) +{ + int w, h, x, y, realrow, truerow, baserow, i, temp1, temp2, pixel_size, map_idx; + int RLE_count, RLE_flag, size, interleave, origin; + bool mapped, rlencoded; + byte *data, *dst, r, g, b, a, j, k, l, *ColorMap; + TargaHeader header; + + header.id_length = fgetc (fin); + header.colormap_type = fgetc (fin); + header.image_type = fgetc (fin); + header.colormap_index = fgetLittleShort (fin); + header.colormap_length = fgetLittleShort (fin); + header.colormap_size = fgetc (fin); + header.x_origin = fgetLittleShort (fin); + header.y_origin = fgetLittleShort (fin); + header.width = fgetLittleShort (fin); + header.height = fgetLittleShort (fin); + header.pixel_size = fgetc (fin); + header.attributes = fgetc (fin); + + if (header.width > IMAGE_MAX_DIMENSIONS || header.height > IMAGE_MAX_DIMENSIONS) + { + Con_DPrintf ("TGA image %s exceeds maximum supported dimensions\n"); + fclose (fin); + return NULL; + } + + if ((matchwidth && header.width != matchwidth) || (matchheight && header.height != matchheight)) + { + fclose (fin); + return NULL; + } + + if (header.id_length != 0) + fseek (fin, header.id_length, SEEK_CUR); + + /* validate TGA type */ + switch (header.image_type) + { + case TGA_Map: + case TGA_RGB: + case TGA_Mono: + case TGA_RLEMap: + case TGA_RLERGB: + case TGA_RLEMono: + break; + + default: + Con_DPrintf ("Unsupported TGA image %s: Only type 1 (map), 2 (RGB), 3 (mono), 9 (RLEmap), 10 (RLERGB), 11 (RLEmono) TGA images supported\n"); + fclose (fin); + return NULL; + } + + /* validate color depth */ + switch (header.pixel_size) + { + case 8: + case 15: + case 16: + case 24: + case 32: + break; + + default: + Con_DPrintf ("Unsupported TGA image %s: Only 8, 15, 16, 24 or 32 bit images (with colormaps) supported\n"); + fclose (fin); + return NULL; + } + + r = g = b = a = l = 0; + + /* if required, read the color map information. */ + ColorMap = NULL; + mapped = (header.image_type == TGA_Map || header.image_type == TGA_RLEMap) && header.colormap_type == 1; + if (mapped) + { + /* validate colormap size */ + switch (header.colormap_size) + { + case 8: + case 15: + case 16: + case 32: + case 24: + break; + + default: + Con_DPrintf ("Unsupported TGA image %s: Only 8, 15, 16, 24 or 32 bit colormaps supported\n"); + fclose (fin); + return NULL; + } + + temp1 = header.colormap_index; + temp2 = header.colormap_length; + if ((temp1 + temp2 + 1) >= TGA_MAXCOLORS) + { + fclose (fin); + return NULL; + } + ColorMap = static_cast(malloc (TGA_MAXCOLORS * 4)); + map_idx = 0; + for (i = temp1 ; i < temp1 + temp2 ; ++i, map_idx += 4) + { + /* read appropriate number of bytes, break into rgb & put in map. */ + switch (header.colormap_size) + { + case 8: /* grey scale, read and triplicate. */ + r = g = b = getc (fin); + a = 255; + break; + + case 15: /* 5 bits each of red green and blue. */ + /* watch byte order. */ + j = getc (fin); + k = getc (fin); + l = ((unsigned int)k << 8) + j; + r = (byte)(((k & 0x7C) >> 2) << 3); + g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3); + b = (byte)((j & 0x1F) << 3); + a = 255; + break; + + case 16: /* 5 bits each of red green and blue, 1 alpha bit. */ + /* watch byte order. */ + j = getc (fin); + k = getc (fin); + l = ((unsigned int)k << 8) + j; + r = (byte)(((k & 0x7C) >> 2) << 3); + g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3); + b = (byte)((j & 0x1F) << 3); + a = (k & 0x80) ? 255 : 0; + break; + + case 24: /* 8 bits each of blue, green and red. */ + b = getc (fin); + g = getc (fin); + r = getc (fin); + a = 255; + l = 0; + break; + + case 32: /* 8 bits each of blue, green, red and alpha. */ + b = getc (fin); + g = getc (fin); + r = getc (fin); + a = getc (fin); + l = 0; + break; + } + ColorMap[map_idx+0] = r; + ColorMap[map_idx+1] = g; + ColorMap[map_idx+2] = b; + ColorMap[map_idx+3] = a; + } + } + + /* check run-length encoding. */ + rlencoded = (header.image_type == TGA_RLEMap || header.image_type == TGA_RLERGB || header.image_type == TGA_RLEMono); + RLE_count = RLE_flag = 0; + + image_width = w = header.width; + image_height = h = header.height; + + size = w * h * 4; + data = static_cast(calloc (size, 1)); + + /* read the Targa file body and convert to portable format. */ + pixel_size = header.pixel_size; + origin = (header.attributes & 0x20) >> 5; + interleave = (header.attributes & 0xC0) >> 6; + truerow = baserow = 0; + for (y=0 ; y> 2) << 3); + g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3); + b = (byte)((j & 0x1F) << 3); + a = 255; + break; + + case 16: /* 5 bits each of red green and blue, 1 alpha bit. */ + /* watch byte order. */ + j = getc (fin); + k = getc (fin); + l = ((unsigned int)k << 8) + j; + r = (byte)(((k & 0x7C) >> 2) << 3); + g = (byte)((((k & 0x03) << 3) + ((j & 0xE0) >> 5)) << 3); + b = (byte)((j & 0x1F) << 3); + a = (k & 0x80) ? 255 : 0; + break; + + case 24: /* 8 bits each of blue, green and red. */ + b = getc (fin); + g = getc (fin); + r = getc (fin); + a = 255; + l = 0; + break; + + case 32: /* 8 bits each of blue, green, red and alpha. */ + b = getc (fin); + g = getc (fin); + r = getc (fin); + a = getc (fin); + l = 0; + break; + + default: + Con_DPrintf ("Malformed TGA image: Illegal pixel_size '%d'\n", pixel_size); + fclose (fin); + free (data); + if (mapped) + free (ColorMap); + return NULL; + } + +PixEncode: + if (mapped) + { + map_idx = l * 4; + *dst++ = ColorMap[map_idx+0]; + *dst++ = ColorMap[map_idx+1]; + *dst++ = ColorMap[map_idx+2]; + *dst++ = ColorMap[map_idx+3]; + } + else + { + *dst++ = r; + *dst++ = g; + *dst++ = b; + *dst++ = a; + } + } + + if (interleave == TGA_IL_Four) + truerow += 4; + else if (interleave == TGA_IL_Two) + truerow += 2; + else + truerow++; + if (truerow >= h) + truerow = ++baserow; + } + + if (mapped) + free (ColorMap); + + fclose (fin); + + return data; +} + +/* +========================================================= + + PNG + +========================================================= +*/ + +static void PNG_IO_user_read_data (png_structp png_ptr, png_bytep data, png_size_t length) +{ + FILE *f = (FILE *)png_get_io_ptr(png_ptr); + + fread (data, 1, length, f); +} + +/* +static void PNG_IO_user_write_data (png_structp png_ptr, png_bytep data, png_size_t length) +{ + FILE *f = (FILE *)png_get_io_ptr(png_ptr); + + fwrite (data, 1, length, f); +} + +static void PNG_IO_user_flush_data (png_structp png_ptr) +{ + FILE *f = (FILE *)png_get_io_ptr(png_ptr); + + fflush (f); +} +*/ + +/* +============= +LoadPNG +============= +*/ +byte *LoadPNG (FILE *fin, int matchwidth, int matchheight) +{ + int y, width, height, bitdepth, colortype; + int interlace, compression, filter, bytesperpixel; + byte header[8], **rowpointers, *data; + png_structp png_ptr; + png_infop pnginfo; + unsigned long rowbytes; + + fread (header, 1, 8, fin); + + if (png_sig_cmp(header, 0, 8)) + { + Con_DPrintf ("Invalid PNG image\n"); + fclose (fin); + return NULL; + } + + if (!(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL))) + { + fclose (fin); + return NULL; + } + + if (!(pnginfo = png_create_info_struct(png_ptr))) + { + png_destroy_read_struct (&png_ptr, &pnginfo, NULL); + fclose (fin); + return NULL; + } + + /* naievil -- invalid use of incomplete type + if (setjmp(png_ptr->jmpbuf)) + { + png_destroy_read_struct (&png_ptr, &pnginfo, NULL); + fclose (fin); + return NULL; + } + */ + + png_set_read_fn (png_ptr, fin, PNG_IO_user_read_data); + png_set_sig_bytes (png_ptr, 8); + png_read_info (png_ptr, pnginfo); + png_get_IHDR (png_ptr, pnginfo, (png_uint_32 *)&width, (png_uint_32 *)&height, &bitdepth, &colortype, &interlace, &compression, &filter); + + if (width > IMAGE_MAX_DIMENSIONS || height > IMAGE_MAX_DIMENSIONS) + { + Con_DPrintf ("PNG image exceeds maximum supported dimensions\n"); + png_destroy_read_struct (&png_ptr, &pnginfo, NULL); + fclose (fin); + return NULL; + } + + if ((matchwidth && width != matchwidth) || (matchheight && height != matchheight)) + { + png_destroy_read_struct (&png_ptr, &pnginfo, NULL); + fclose (fin); + return NULL; + } + + if (colortype == PNG_COLOR_TYPE_PALETTE) + { + png_set_palette_to_rgb (png_ptr); + png_set_filler (png_ptr, 255, PNG_FILLER_AFTER); + } + + if (colortype == PNG_COLOR_TYPE_GRAY && bitdepth < 8) + png_set_expand_gray_1_2_4_to_8 (png_ptr); + + if (png_get_valid(png_ptr, pnginfo, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha (png_ptr); + + if (colortype == PNG_COLOR_TYPE_GRAY || colortype == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb (png_ptr); + + if (colortype != PNG_COLOR_TYPE_RGBA) + png_set_filler (png_ptr, 255, PNG_FILLER_AFTER); + + if (bitdepth < 8) + png_set_expand (png_ptr); + else if (bitdepth == 16) + png_set_strip_16 (png_ptr); + + png_read_update_info (png_ptr, pnginfo); + rowbytes = png_get_rowbytes (png_ptr, pnginfo); + bytesperpixel = png_get_channels (png_ptr, pnginfo); + bitdepth = png_get_bit_depth (png_ptr, pnginfo); + + if (bitdepth != 8 || bytesperpixel != 4) + { + Con_DPrintf ("Unsupported PNG image: Bad color depth and/or bpp\n"); + png_destroy_read_struct (&png_ptr, &pnginfo, NULL); + fclose (fin); + return NULL; + } + + data = static_cast(malloc (height * rowbytes)); + rowpointers = static_cast(malloc (height * sizeof(*rowpointers))); + + for (y=0 ; y(malloc(numPixels * 4)); + + for ( row = rows-1; row >= 0; row-- ) + { + pixbuf = bmpRGBA + row*columns*4; + + for ( column = 0; column < columns; column++ ) + { + unsigned char red, green, blue, alpha; + int palIndex; + unsigned short shortPixel; + + switch ( bmpHeader.bitsPerPixel ) + { + case 8: + palIndex = getc(fin); + *pixbuf++ = bmpHeader.palette[palIndex][2]; + *pixbuf++ = bmpHeader.palette[palIndex][1]; + *pixbuf++ = bmpHeader.palette[palIndex][0]; + *pixbuf++ = 0xff; + break; + case 16: + shortPixel = * ( unsigned short * ) pixbuf; + pixbuf += 2; + *pixbuf++ = ( shortPixel & ( 31 << 10 ) ) >> 7; + *pixbuf++ = ( shortPixel & ( 31 << 5 ) ) >> 2; + *pixbuf++ = ( shortPixel & ( 31 ) ) << 3; + *pixbuf++ = 0xff; + break; + + case 24: + blue = getc (fin); + green = getc (fin); + red = getc (fin); + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = 255; + break; + case 32: + blue = getc (fin); + green = getc (fin); + red = getc (fin); + alpha = getc (fin); + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = alpha; + break; + default: + Sys_Error("LoadBMP: illegal pixel_size '%d' in file\n", bmpHeader.bitsPerPixel); + break; + } + } + } + + fclose(fin); + + return bmpRGBA; +} + +/* +============= +loadimagepixels +============= +*/ +byte* loadimagepixels (char* filename, qboolean complain, int matchwidth, int matchheight) +{ + FILE *f; + char basename[128], name[128]; + byte *c; + COM_StripExtension(filename, basename); // strip the extension to allow TGA and PCX + c = (byte*)(basename); + + while (*c) + { + if (*c == '*') + *c = '+'; + c++; + } + + com_netpath[0] = 0; +/* + sprintf (name, "%s.wal", basename); + if (data = LoadWAL(name)) + return data; +*/ + + sprintf (name, "%s.tga", basename); + FS_FOpenFile (name, &f); + if (f) + return LoadTGA (f, matchwidth, matchheight); + + sprintf (name, "%s.pcx", basename); + FS_FOpenFile (name, &f); + if (f) + return LoadPCX (f, matchwidth, matchheight); + + sprintf (name, "%s.jpg", basename); + FS_FOpenFile (name, &f); + if (f) + { + return LoadJPG (f, matchwidth, matchheight); + } + sprintf (name, "%s.png", basename); + FS_FOpenFile (name, &f); + if (f) + return LoadPNG (f, matchwidth, matchheight); + + sprintf (name, "%s.bmp", basename); + FS_FOpenFile (name, &f); + if (f) + return LoadBMP (f, matchwidth, matchheight); + if (complain) + Con_Printf ("Couldn't load %s .tga .jpg .bmp .png \n", filename); + + return NULL; +} + +/* +============= +loadtextureimage +============= +*/ + +int loadtextureimage (char* filename, int matchwidth, int matchheight, qboolean complain, int filter) +{ + int texture_index; + byte *data; + data = loadimagepixels (filename, complain, matchwidth, matchheight); + if(!data) + { + return 0; + } + + if(image_palette_type) + { + texture_index = GL_LoadPalTex (filename, image_width, image_height, data, qtrue, filter, 0, (byte *)image_palette, image_palette_type); + memset(image_palette, 0, sizeof(image_palette)); // clear temp pal + image_palette_type = 0; + } + else + { + texture_index = GL_LoadImages (filename, image_width, image_height, data, qtrue, filter, 0, 4); + } + free(data); + return texture_index; +} diff --git a/source/psp/video_hardware_images.h b/source/psp/video_hardware_images.h new file mode 100644 index 0000000..367d253 --- /dev/null +++ b/source/psp/video_hardware_images.h @@ -0,0 +1,31 @@ +/* +Copyright (C) 1996-2003 A Nourai, 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. + +*/ +// image.h + +extern int image_width, image_height; + +extern cvar_t png_compression_level, jpeg_compression_level; + +int Image_WriteTGA (char *filename, byte *pixels, int width, int height); +int Image_WritePNG (char *filename, int compression, byte *pixels, int width, int height); +int Image_WritePNGPLTE (char *filename, int compression, byte *pixels, int width, int height, byte *palette); +int Image_WriteJPEG (char *filename, int compression, byte *pixels, int width, int height); +int Image_WritePCX (char *filename, byte *data, int width, int height, byte *palette); + diff --git a/source/psp/video_hardware_light.cpp b/source/psp/video_hardware_light.cpp new file mode 100644 index 0000000..1091359 --- /dev/null +++ b/source/psp/video_hardware_light.cpp @@ -0,0 +1,357 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 + +extern "C" +{ +#include "../quakedef.h" +} +#include + +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 ; jdie < cl.time || !l->radius) + continue; + R_RenderDlight (l); + } + + glColor3f (1,1,1); + glDisable (GL_BLEND); + glEnable (GL_TEXTURE_2D); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask (1);*/ +/* + sceGuEnable(GU_LIGHTING); + sceGuEnable(GU_LIGHT0); + sceGuEnable(GU_LIGHT1); + sceGuEnable(GU_LIGHT2); + sceGuEnable(GU_LIGHT3); + + l = cl_dlights; + for (i=0 ; idie < cl.time || !l->radius || light_num > 3) + continue; + + ScePspFVector3 pos = { l->origin[0]*3.0, l->origin[1]*3.0, l->origin[2]*3.0}; + sceGuLight(i, GU_POINTLIGHT, GU_DIFFUSE_AND_SPECULAR, &pos); + sceGuLightColor(i, GU_DIFFUSE, GU_COLOR(l->color[0], l->color[1], l->color[2], 1)); + sceGuLightAtt(i,1.0f,0.0f,0.0f); + light_num++; + } + + sceGuSpecular(12.0f); + sceGuAmbient(0x00222222); +*/ +} + + +/* +============================================================================= + +DYNAMIC LIGHTS + +============================================================================= +*/ +void R_MarkLights (dlight_t *light, int bit, mnode_t *node) +{ + mplane_t *splitplane; + float dist, l, maxdist; + msurface_t *surf; + int i, j, s, t, sidebit; + vec3_t impact; + +loc0: + if (node->contents < 0) + return; + + splitplane = node->plane; +// dist = PlaneDiff(light->origin, splitplane); + + if (splitplane->type < 3) + dist = light->origin[splitplane->type] - splitplane->dist; + else + dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist; + + if (dist > light->radius) + { + node = node->children[0]; + goto loc0; + } + if (dist < -light->radius) + { + node = node->children[1]; + goto loc0; + } + + maxdist = light->radius * light->radius; +// mark the polygons + surf = cl.worldmodel->surfaces + node->firstsurface; + for (i=0 ; inumsurfaces ; i++, surf++) + { + dist = DotProduct (light->origin, surf->plane->normal) - surf->plane->dist; // JT030305 - fix light bleed through + if (dist >= 0) + sidebit = 0; + else + sidebit = SURF_PLANEBACK; + + if ( (surf->flags & SURF_PLANEBACK) != sidebit ) //Discoloda + continue; //Discoloda + + 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; + s = bound(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; + t = bound(0, 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; + } + } + } + if (node->children[0]->contents >= 0) + R_MarkLights (light, bit, node->children[0]); + if (node->children[1]->contents >= 0) + R_MarkLights (light, bit, node->children[1]); +} + +/* +============= +R_PushDlights +============= +*/ +void R_PushDlights (void) +{ + int i; + dlight_t *l; + + 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; + +// 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 (r_fullbright.value || !cl.worldmodel->lightdata) + { + lightcolor[0] = lightcolor[1] = lightcolor[2] = 255; + return 255; + } + + end[0] = p[0]; + end[1] = p[1]; + end[2] = p[2] - 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 diff --git a/source/psp/video_hardware_main.cpp b/source/psp/video_hardware_main.cpp new file mode 100644 index 0000000..9dc7366 --- /dev/null +++ b/source/psp/video_hardware_main.cpp @@ -0,0 +1,4297 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 + +extern "C" +{ +#include "../quakedef.h" +#include "iridlibs/perflib.h" +float TraceLine (vec3_t start, vec3_t end, vec3_t impact, vec3_t normal); +} + +//includes +#include "video_hardware_hlmdl.h" +#include +#include + +#include "clipping.hpp" + +using namespace quake; + +//prototypes +extern "C" void V_CalcBlend (void); + +void Fog_SetupFrame (void); +void Fog_EnableGFog (void); +void Fog_DisableGFog (void); +void R_DrawDecals (void); +void R_RenderDecals (void); +void R_MarkLeaves (void); +void QMB_LetItRain(void); +void QMB_LaserSight (void); +void VID_SetPaletteH2(); +void VID_SetPaletteTX(); + +// globals +const float piconst = GU_PI / 180.0f; + +entity_t r_worldentity; +entity_t *currententity; + +qboolean r_cache_thrash; // compatability +qboolean envmap; +qboolean mirror; + +// view origin +vec3_t vup; +vec3_t vpn; +vec3_t vright; +vec3_t r_origin; + +vec3_t modelorg, r_entorigin; + + +int r_visframecount; // bumped when going to a new PVS +int r_framecount; // used for dlight push checking +int c_brush_polys, c_alias_polys, c_md3_polys; +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 +int game_fps; // should probably move this somewhere less.. lame + +mplane_t *mirror_plane; +mplane_t frustum[4]; + +// screen size info +refdef_t r_refdef; +mleaf_t *r_viewleaf; +mleaf_t *r_oldviewleaf; +texture_t *r_notexture_mip; + +bool fixlight; +bool alphafunc2; +bool alphafunc; + +ScePspFMatrix4 r_world_matrix; +ScePspFMatrix4 r_base_world_matrix; +ScePspFMatrix4 md3mult; + +int d_lightstylevalue[256]; // 8.8 fraction of base light value + +cvar_t r_partalpha = {"r_partalpha", "0.8",qtrue}; +cvar_t r_norefresh = {"r_norefresh", "0"}; +cvar_t r_drawentities = {"r_drawentities", "1"}; +cvar_t r_drawviewmodel = {"r_drawviewmodel", "1"}; +cvar_t r_speeds = {"r_speeds", "0"}; +cvar_t r_fullbright = {"r_fullbright", "0"}; +cvar_t r_lightmap = {"r_lightmap", "0"}; +cvar_t r_shadows = {"r_shadows", "0"}; +cvar_t r_mirroralpha = {"r_mirroralpha", "1",qtrue}; +cvar_t r_wateralpha = {"r_wateralpha", "0.6",qtrue}; +cvar_t r_vsync = {"r_vsync", "0",qtrue}; +cvar_t r_farclip = {"r_farclip", "4096"}; //far cliping for q3 models +cvar_t r_loadq3models = {"r_loadq3models", "0",qtrue}; //replace player model to q3 player +cvar_t cl_loadmapcfg = {"cl_loadmapcfg", "0",qtrue}; //Load individual cfg for map +cvar_t r_restexf = {"r_restexf", "0",qtrue}; //texture resampler setup +cvar_t r_texcompr = {"r_texcompr", "5",qtrue}; //texture compression setup (default DXT5) warning DXT1 conflicted with switches palettes +cvar_t r_maxrange = {"r_maxrange", "4096"}; //render distance +cvar_t r_skydis = {"r_skydis", "2560",qtrue}; +cvar_t r_skyfog = {"r_skyfog", "1",qtrue}; +cvar_t r_caustics = {"r_caustics", "1",qtrue}; +cvar_t r_detail = {"r_detail", "1",qtrue}; +cvar_t r_detail_mipmaps = {"r_detail_mipmaps", "1",qtrue}; +cvar_t r_detail_mipmaps_func = {"r_detail_mipmaps_func", "2",qtrue}; +cvar_t r_detail_mipmaps_bias = {"r_detail_mipmaps_bias", "-6",qtrue}; +cvar_t r_asynch = {"r_asynch", "0"}; +cvar_t r_ipolations = {"r_ipolations", "0"}; +cvar_t r_i_model_animation = {"r_i_model_animation", "1",qtrue}; // Toggle smooth model animation +cvar_t r_i_model_transform = {"r_i_model_transform", "1",qtrue}; // Toggle smooth model movement +cvar_t r_mipmaps = {"r_mipmaps", "1",qtrue}; +cvar_t r_mipmaps_func = {"r_mipmaps_func", "2",qtrue}; // Adjust mip map calculations +cvar_t r_mipmaps_bias = {"r_mipmaps_bias", "-7",qtrue}; // Adjust mip map bias +cvar_t r_retro = {"r_retro", "0",qtrue}; // dr_mabuse1981: "retro filter". +cvar_t r_dynamic = {"r_dynamic", "1"}; +cvar_t r_novis = {"r_novis", "0"}; +cvar_t r_tex_scale_down = {"r_tex_scale_down", "1",qtrue}; +cvar_t r_particles_simple = {"r_particles_simple", "0",qtrue}; +cvar_t gl_keeptjunctions = {"gl_keeptjunctions", "0"}; +cvar_t r_waterripple = {"r_waterripple", "2",qtrue}; +cvar_t r_waterwarp = {"r_waterwarp", "1",qtrue}; +cvar_t r_fastsky = {"r_fastsky", "1",qtrue}; +cvar_t r_skycolor = {"r_skycolor", "64 64 70",qtrue}; +cvar_t r_showbboxes = {"r_showbboxes", "0"}; +cvar_t r_showbboxes_full = {"r_showbboxes_full", "0",qtrue}; +cvar_t r_showtris = {"r_showtris", "0"}; +cvar_t r_showtris_full = {"r_showtris_full", "0",qtrue}; +cvar_t r_polyblend = {"r_polyblend", "1",qtrue}; + +//QMB +cvar_t r_explosiontype = {"r_explosiontype", "0",qtrue}; +cvar_t r_laserpoint = {"r_laserpoint", "0",qtrue}; +cvar_t r_part_explosions = {"r_part_explosions", "1",qtrue}; +cvar_t r_part_trails = {"r_part_trails", "1",qtrue}; +cvar_t r_part_sparks = {"r_part_sparks", "1",qtrue}; +cvar_t r_part_spikes = {"r_part_spikes", "1",qtrue}; +cvar_t r_part_gunshots = {"r_part_gunshots", "1",qtrue}; +cvar_t r_part_blood = {"r_part_blood", "1",qtrue}; +cvar_t r_part_telesplash = {"r_part_telesplash", "1",qtrue}; +cvar_t r_part_blobs = {"r_part_blobs", "1",qtrue}; +cvar_t r_part_lavasplash = {"r_part_lavasplash", "1",qtrue}; +cvar_t r_part_flames = {"r_part_flames", "1",qtrue}; +cvar_t r_part_lightning = {"r_part_lightning", "1",qtrue}; +cvar_t r_part_flies = {"r_part_flies", "1",qtrue}; +cvar_t r_part_muzzleflash = {"r_part_muzzleflash", "1",qtrue}; +cvar_t r_flametype = {"r_flametype", "2",qtrue}; +//Shpuld +cvar_t r_model_brightness = { "r_model_brightness", "1", qtrue}; // Toggle high brightness model lighting + +//MotoLegacy +cvar_t nzp_decals = {"nzp_decals", "1", qtrue}; +cvar_t nzp_particles = {"nzp_particles", "1", qtrue}; + +extern cvar_t cl_maxfps; + + + + + +bool modelIsArm (char *m_name) +{ + if (!strcmp (m_name, "progs/ai/zal.mdl") || + !strcmp (m_name, "progs/ai/zar.mdl") || + !strcmp (m_name, "progs/ai/bzal.mdl") || + !strcmp (m_name, "progs/ai/bzar.mdl") || + !strcmp (m_name, "progs/ai/zalc.mdl") || + !strcmp (m_name, "progs/ai/zarc.mdl")) + return true; + + return false; + +} +bool modelIsHead (char *m_name) +{ + if (!strcmp (m_name, "progs/ai/zh.mdl") || + !strcmp (m_name, "progs/ai/bzh.mdl") || + !strcmp (m_name, "progs/ai/zhc.mdl")) + return true; + + return false; + +} +bool modelIsBody (char *m_name) +{ + if (!strcmp (m_name, "progs/ai/zb.mdl") || + !strcmp (m_name, "progs/ai/bzb.mdl") || + !strcmp (m_name, "progs/ai/zbc.mdl")) + return true; + + return false; + +} +/* +================ +ConvertMatrix +By Crow_bar for MD3 +================ +*/ +void ConvertMatrix(float *a, float *b) +{ + for (int i = 0; i < 16; i++) + a[i] = b[i]; +} + +/* +============= +R_RotateForTagEntity +============= +*/ +void R_RotateForTagEntity (tagentity_t *tagent, md3tag_t *tag, float *m) +{ + int i; + float lerpfrac, timepassed; + + // positional interpolation + timepassed = cl.time - tagent->tag_translate_start_time; + + if (tagent->tag_translate_start_time == 0 || timepassed > 1) + { + tagent->tag_translate_start_time = cl.time; + VectorCopy (tag->pos, tagent->tag_pos1); + VectorCopy (tag->pos, tagent->tag_pos2); + } + + if (!VectorCompare(tag->pos, tagent->tag_pos2)) + { + tagent->tag_translate_start_time = cl.time; + VectorCopy (tagent->tag_pos2, tagent->tag_pos1); + VectorCopy (tag->pos, tagent->tag_pos2); + lerpfrac = 0; + } + else + { + lerpfrac = timepassed / 0.1; + if (cl.paused || lerpfrac > 1) + lerpfrac = 1; + } + + VectorInterpolate (tagent->tag_pos1, lerpfrac, tagent->tag_pos2, m + 12); + m[15] = 1; + + for (i=0 ; i<3 ; i++) + { + // orientation interpolation (Euler angles, yuck!) + timepassed = cl.time - tagent->tag_rotate_start_time[i]; + + if (tagent->tag_rotate_start_time[i] == 0 || timepassed > 1) + { + tagent->tag_rotate_start_time[i] = cl.time; + VectorCopy (tag->rot[i], tagent->tag_rot1[i]); + VectorCopy (tag->rot[i], tagent->tag_rot2[i]); + } + + if (!VectorCompare(tag->rot[i], tagent->tag_rot2[i])) + { + tagent->tag_rotate_start_time[i] = cl.time; + VectorCopy (tagent->tag_rot2[i], tagent->tag_rot1[i]); + VectorCopy (tag->rot[i], tagent->tag_rot2[i]); + lerpfrac = 0; + } + else + { + lerpfrac = timepassed / 0.1; + if (cl.paused || lerpfrac > 1) + lerpfrac = 1; + } + + VectorInterpolate (tagent->tag_rot1[i], lerpfrac, tagent->tag_rot2[i], m + i*4); + m[i*4+3] = 0; + } +} + +/* +============= +R_RotateForViewEntity +============= +*/ +void R_RotateForViewEntity (entity_t *ent) +{ + // Translate. + const ScePspFVector3 translation = + { + ent->origin[0], ent->origin[1], ent->origin[2] + }; + sceGumTranslate(&translation); + + // Rotate. + const ScePspFVector3 rotation = + { + ent->angles[ROLL] * piconst, + -ent->angles[PITCH] * piconst, + ent->angles[YAW] * piconst + }; + sceGumRotateZYX(&rotation); +} + +/* +================= +R_CullBox -- replaced with new function from lordhavoc + +Returns true if the box is completely outside the frustum +================= +*/ +qboolean R_CullBox (vec3_t emins, vec3_t emaxs) +{ + int i; + mplane_t *p; + + + + for (i = 0;i < 4;i++) + { + p = frustum + i; + switch(p->signbits) + { + default: + case 0: + if (p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2] < p->dist) + return qtrue; + break; + case 1: + if (p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2] < p->dist) + return qtrue; + break; + case 2: + if (p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2] < p->dist) + return qtrue; + break; + case 3: + if (p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2] < p->dist) + return qtrue; + break; + case 4: + if (p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2] < p->dist) + return qtrue; + break; + case 5: + if (p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2] < p->dist) + return qtrue; + break; + case 6: + if (p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2] < p->dist) + return qtrue; + break; + case 7: + if (p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2] < p->dist) + return qtrue; + break; + } + } + return qfalse; +} + +/* +================= +R_CullSphere + +Returns true if the sphere is completely outside the frustum +================= +*/ +qboolean R_CullSphere (vec3_t centre, float radius) +{ + int i; + mplane_t *p; + + for (i=0, p=frustum ; i<4 ; i++, p++) + { + if (PlaneDiff(centre, p) <= -radius) + return qtrue; + } + + return qfalse; +} + +/* +============= +R_RotateForEntity +============= +*/ +void R_RotateForEntity (entity_t *e, int shadow) +{ + // Translate. + const ScePspFVector3 translation = + { + e->origin[0], e->origin[1], e->origin[2] + }; + sceGumTranslate(&translation); +/* + // Scale. + const ScePspFVector3 scale = + { + e->scale, e->scale, e->scale + }; + sceGumScale(&scale); +*/ + + // Rotate. + sceGumRotateZ(e->angles[YAW] * (GU_PI / 180.0f)); + if (shadow == 0) + { + sceGumRotateY (-e->angles[PITCH] * (GU_PI / 180.0f)); + sceGumRotateX (e->angles[ROLL] * (GU_PI / 180.0f)); + } + + sceGumUpdateMatrix(); +} + +/* +============= +R_InterpolateEntity + +was R_BlendedRotateForEntity +fenix@io.com: model transform interpolation + +modified by blubswillrule +//fixme (come back and fix this once we can test on psp and view the true issue with interpolation) +============= +*/ + +void R_InterpolateEntity(entity_t *e, int shadow) // Tomaz - New Shadow +{ + float timepassed; + float blend; + vec3_t deltaVec; + int i; + + // positional interpolation + + timepassed = realtime - e->translate_start_time; + + //notes to self (blubs) + //-Added this method, and commented out the check for r_i_model_transforms.value + //tried the snapping interpolation, though it worked, it was still a bit jittery... + //problem with linear interpolation is we don't know the exact time it should take to move from origin1 to origin2... + //looks like the rotation interpolation doesn't work all that great either, rotation could benefit from the snapping interpolation that I use + //if I get this method to work well, make sure we go back and check for r_i_model_transforms again, (because vmodel and other models that don't use interpolation) + //probably go back and edit animations too as I redo the last 2 textures.. + + + /*if (e->translate_start_time == 0 || timepassed > 1) + { + e->translate_start_time = realtime; + VectorCopy (e->origin, e->origin1); + VectorCopy (e->origin, e->origin2); + } + + //our origin has been updated + 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 = 1; + if (cl.paused) + blend = 0; + + e->origin1[0] += (blend * 0.25 * (e->origin2[0] - e->origin1[0])); + e->origin1[1] += (blend * 0.25 * (e->origin2[1] - e->origin1[1])); + e->origin1[2] += (blend * 0.25 * (e->origin2[2] - e->origin1[2])); + } + + //VectorSubtract (e->origin2, e->origin1, deltaVec); + + // Translate. + const ScePspFVector3 translation = + {*/ + /*e->origin[0] + (blend * deltaVec[0]), + e->origin[1] + (blend * deltaVec[1]), + e->origin[2] + (blend * deltaVec[2])*/ + /* + e->origin1[0], e->origin1[1], e->origin1[2] + }; + */ + + if (e->translate_start_time == 0 || timepassed > 1) + { + e->translate_start_time = realtime; + VectorCopy (e->origin, e->origin1); + VectorCopy (e->origin, e->origin2); + } + + //our origin has been updated + 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.4;//0.1 not sure what this value should be... + //technically this value should be the total amount of time that we take from 1 position to the next, it's practically how long it should take us to go from one location to the next... + if (cl.paused || blend > 1) + blend = 0; + } + + VectorSubtract (e->origin2, e->origin1, deltaVec); + + // Translate. + const ScePspFVector3 translation = + { + e->origin[0] + (blend * deltaVec[0]), + e->origin[1] + (blend * deltaVec[1]), + e->origin[2] + (blend * deltaVec[2]) + }; + sceGumTranslate(&translation); + + + // 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, deltaVec); + + // always interpolate along the shortest path + for (i = 0; i < 3; i++) + { + if (deltaVec[i] > 180) + { + deltaVec[i] -= 360; + } + else if (deltaVec[i] < -180) + { + deltaVec[i] += 360; + } + } + + // Rotate. + sceGumRotateZ((e->angles1[YAW] + ( blend * deltaVec[YAW])) * (GU_PI / 180.0f)); + if (shadow == 0) + { + sceGumRotateY ((-e->angles1[PITCH] + (-blend * deltaVec[PITCH])) * (GU_PI / 180.0f)); + sceGumRotateX ((e->angles1[ROLL] + ( blend * deltaVec[ROLL])) * (GU_PI / 180.0f)); + } + + sceGumUpdateMatrix(); +} + + + +/* +============= +R_BlendedRotateForEntity + +fenix@io.com: model transform interpolation +============= +*/ +void R_BlendedRotateForEntity (entity_t *e, int shadow) // Tomaz - New Shadow +{ + 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 = 0; + } + + VectorSubtract (e->origin2, e->origin1, d); + + // Translate. + const ScePspFVector3 translation = { + e->origin[0] + (blend * d[0]), + e->origin[1] + (blend * d[1]), + e->origin[2] + (blend * d[2]) + }; + sceGumTranslate(&translation); +/* + // Scale. + const ScePspFVector3 scale = { + e->scale + (blend * d[0]), + e->scale + (blend * d[1]), + e->scale + (blend * d[2] + }; + sceGumScale(&scale); +*/ + // 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; + } + } + + // Rotate. + sceGumRotateZ((e->angles1[YAW] + ( blend * d[YAW])) * (GU_PI / 180.0f)); + if (shadow == 0) + { + sceGumRotateY ((-e->angles1[PITCH] + (-blend * d[PITCH])) * (GU_PI / 180.0f)); + sceGumRotateX ((e->angles1[ROLL] + ( blend * d[ROLL])) * (GU_PI / 180.0f)); + } + + sceGumUpdateMatrix(); +} + +/* +============================================================= + + SPRITE MODELS + +============================================================= +*/ + +extern vec3_t lightcolor; // LordHavoc: .lit support + +/* +================ +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 = static_cast(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 + +================= +*/ +void R_DrawSpriteModel (entity_t *e) +{ + vec3_t point, v_forward, v_right, v_up; + msprite_t *psprite; + mspriteframe_t *frame; + float *s_up, *s_right; + float angle, sr, cr; + bool additive = false; + bool filter = false; + + // don't even bother culling, because it's just a single polygon without a surface cache + frame = R_GetSpriteFrame (e); + psprite = static_cast(currententity->model->cache.data); + + switch(psprite->type) + { + case SPR_VP_PARALLEL_UPRIGHT: //faces view plane, up is towards the heavens + v_up[0] = 0; + v_up[1] = 0; + v_up[2] = 1; + s_up = v_up; + s_right = vright; + break; + case SPR_FACING_UPRIGHT: //faces camera origin, up is towards the heavens + VectorSubtract(currententity->origin, r_origin, v_forward); + v_forward[2] = 0; + VectorNormalizeFast(v_forward); + v_right[0] = v_forward[1]; + v_right[1] = -v_forward[0]; + v_right[2] = 0; + v_up[0] = 0; + v_up[1] = 0; + v_up[2] = 1; + s_up = v_up; + s_right = v_right; + break; + case SPR_VP_PARALLEL: //faces view plane, up is towards the top of the screen + s_up = vup; + s_right = vright; + break; + case SPR_ORIENTED: //pitch yaw roll are independent of camera + AngleVectors (currententity->angles, v_forward, v_right, v_up); + s_up = v_up; + s_right = v_right; + break; + case SPR_VP_PARALLEL_ORIENTED: //faces view plane, but obeys roll value + angle = currententity->angles[ROLL] * M_PI_DIV_180; + #ifdef PSP_VFPU + sr = vfpu_sinf(angle); + cr = vfpu_cosf(angle); + #else + sr = sin(angle); + cr = cos(angle); + #endif + v_right[0] = vright[0] * cr + vup[0] * sr; + v_up[0] = vright[0] * -sr + vup[0] * cr; + v_right[1] = vright[1] * cr + vup[1] * sr; + v_up[1] = vright[1] * -sr + vup[1] * cr; + v_right[2] = vright[2] * cr + vup[2] * sr; + v_up[2] = vright[2] * -sr + vup[2] * cr; + s_up = v_up; + s_right = v_right; + break; + default: + return; + } + + if (psprite->beamlength == 10) // we use the beam length of sprites, since they are unused by quake anyway. + additive = true; + + if (psprite->beamlength == 20) + filter = true; + + // Bind the texture. + GL_Bind(frame->gl_texturenum); + + sceGuEnable(GU_BLEND); + sceGuDepthMask(GU_TRUE); + + Fog_DisableGFog (); + + if (additive) + { + sceGuDepthMask(GU_TRUE); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0xFFFFFFFF); + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGB); + } + else if (filter) + { + sceGuDepthMask(GU_TRUE); + sceGuBlendFunc(GU_ADD, GU_FIX, GU_SRC_COLOR, 0, 0); + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGB); + } + else + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + + // Allocate memory for this polygon. + glvert_t* const vertices = + static_cast(sceGuGetMemory(sizeof(glvert_t) * 4)); + + VectorMA (e->origin, frame->down, s_up, point); + VectorMA (point, frame->left, s_right, point); + + vertices[0].st[0] = 0.0f; + vertices[0].st[1] = 1.0f; + vertices[0].xyz[0] = point[0]; + vertices[0].xyz[1] = point[1]; + vertices[0].xyz[2] = point[2]; + + VectorMA (e->origin, frame->up, s_up, point); + VectorMA (point, frame->left, s_right, point); + + vertices[1].st[0] = 0.0f; + vertices[1].st[1] = 0.0f; + vertices[1].xyz[0] = point[0]; + vertices[1].xyz[1] = point[1]; + vertices[1].xyz[2] = point[2]; + + VectorMA (e->origin, frame->up, s_up, point); + VectorMA (point, frame->right, s_right, point); + + vertices[2].st[0] = 1.0f; + vertices[2].st[1] = 0.0f; + vertices[2].xyz[0] = point[0]; + vertices[2].xyz[1] = point[1]; + vertices[2].xyz[2] = point[2]; + + VectorMA (e->origin, frame->down, s_up, point); + VectorMA (point, frame->right, s_right, point); + + vertices[3].st[0] = 1.0f; + vertices[3].st[1] = 1.0f; + vertices[3].xyz[0] = point[0]; + vertices[3].xyz[1] = point[1]; + vertices[3].xyz[2] = point[2]; + + // Draw the clipped vertices. + sceGuDrawArray( GU_TRIANGLE_FAN,GU_TEXTURE_32BITF | GU_VERTEX_32BITF,4, 0, vertices); + + sceGuDepthMask(GU_FALSE); + sceGuDisable(GU_BLEND); + + Fog_EnableGFog (); + + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); +} + +/* +============================================================= + + ALIAS MODELS + +============================================================= +*/ + + +#define NUMVERTEXNORMALS 162 + +#define INTERP_WEAP_MAXNUM 24 +#define INTERP_WEAP_MINDIST 5000 +#define INTERP_WEAP_MAXDIST 95000 +#define INTERP_MINDIST 70 +#define INTERP_MAXDIST 300 + +extern "C" float r_avertexnormals[NUMVERTEXNORMALS][3]; +float r_avertexnormals[NUMVERTEXNORMALS][3] = { +#include "../anorms.h" +}; + +vec3_t shadevector; +float shadelight, ambientlight; + +// precalculated dot products for quantized angles +#define SHADEDOT_QUANT 16 +float r_avertexnormal_dots[SHADEDOT_QUANT][256] = +#include "../anorm_dots.h" +; + +float *shadedots = r_avertexnormal_dots[0]; + +// fenix@io.com: model animation interpolation +int lastposenum0; +// + +int lastposenum; + +// fenix@io.com: model transform interpolation +float old_i_model_transform; +// + +/* +============= +GL_DrawAliasFrame +============= +*/ +void GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum, float apitch, float ayaw) +{ + float l,r,g,b; + trivertx_t *verts; + int *order; + int count; + int prim; + prim = GU_TRIANGLE_FAN; + if(r_showtris.value) + { + sceGuDisable(GU_TEXTURE_2D); + prim = GU_LINE_STRIP; + } + + lastposenum = posenum; + verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); + verts += posenum * paliashdr->poseverts; + order = (int *)((byte *)paliashdr + paliashdr->commands); + + struct vertex + { + float u, v; + unsigned int color; + float x, y, z; + }; + + //for blubs's alternate BuildTris: 1) Disable while(1) loop 2) Disable the break; 3) replace GU_TRIANGLE_STRIP with GU_TRIANGLES + while (1) + { + // get the vertex count and primitive type + count = *order++; + if (!count) + break; // done + + if(prim != GU_LINE_STRIP) + { + if (count < 0) + { + prim = GU_TRIANGLE_FAN; + count = -count; + } + else + { + prim = GU_TRIANGLE_STRIP; + //prim = GU_TRIANGLES; //used for blubs' alternate BuildTris with one continual triangle list + } + } + + // Allocate the vertices. + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * count)); + //================================================================== fps: 50 =============================================== + for (int vertex_index = 0; vertex_index < count; ++vertex_index) + { + // texture coordinates come from the draw list + out[vertex_index].u = ((float *)order)[0]; + out[vertex_index].v = ((float *)order)[1]; + order += 2; + + l = shadedots[verts->lightnormalindex]; + r = l * lightcolor[0]; + g = l * lightcolor[1]; + b = l * lightcolor[2]; + + if(r > 1) + r = 1; + if(g > 1) + g = 1; + if(b > 1) + b = 1; + + out[vertex_index].x = verts->v[0]; + out[vertex_index].y = verts->v[1]; + out[vertex_index].z = verts->v[2]; + out[vertex_index].color = GU_COLOR(r, g, b, 1.0f); + + ++verts; + } + sceGuDrawArray(prim, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_COLOR_8888, count, 0, out); + + //================================================================== fps: 50 =============================================== + } + if(r_showtris.value) + { + sceGuEnable(GU_TEXTURE_2D); + } + sceGuColor(0xffffffff); +} + +/* +============= +GL_DrawAliasBlendedFrame + +fenix@io.com: model animation interpolation +============= +*/ +void GL_DrawAliasBlendedFrame (aliashdr_t *paliashdr, int pose1, int pose2, float blend, float apitch, float ayaw) +{ + float l,r,g,b; + trivertx_t* verts1; + trivertx_t* verts2; + int* order; + int count; + vec3_t d; + vec3_t point; + int prim; + prim = GU_TRIANGLE_FAN; + if(r_showtris.value) + { + sceGuDisable(GU_TEXTURE_2D); + prim = GU_LINE_STRIP; + } + + 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); + + struct vertex + { + float u, v; + unsigned int color; + float x, y, z; + }; + + //for blubs's alternate BuildTris: 1) Disable while(1) loop 2) Disable the break; 3) replace GU_TRIANGLE_STRIP with GU_TRIANGLES + while (1) + { + // get the vertex count and primitive type + count = *order++; + + if (!count) break; + + if(prim != GU_LINE_STRIP) + { + if (count < 0) + { + prim = GU_TRIANGLE_FAN; + count = -count; + } + else + { + prim = GU_TRIANGLE_STRIP; + //prim = GU_TRIANGLES; //used for blubs' alternate BuildTris with one continual triangle list + } + } + + // Allocate the vertices. + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * count)); + + for (int vertex_index = 0; vertex_index < count; ++vertex_index) + { + + // texture coordinates come from the draw list + out[vertex_index].u = ((float *)order)[0]; + out[vertex_index].v = ((float *)order)[1]; + + order += 2; + d[0] = shadedots[verts2->lightnormalindex] - shadedots[verts1->lightnormalindex]; + l = shadedots[verts1->lightnormalindex] + (blend * d[0]); + + r = l * lightcolor[0]; + g = l * lightcolor[1]; + b = l * lightcolor[2]; + + if(r > 1) + r = 1; + if(g > 1) + g = 1; + if(b > 1) + b = 1; + + VectorSubtract(verts2->v, verts1->v, d); + + // blend the vertex positions from each frame together + point[0] = verts1->v[0] + (blend * d[0]); + point[1] = verts1->v[1] + (blend * d[1]); + point[2] = verts1->v[2] + (blend * d[2]); + + out[vertex_index].x = point[0]; + out[vertex_index].y = point[1]; + out[vertex_index].z = point[2]; + out[vertex_index].color = GU_COLOR(r, g, b, 1.0f); + + ++verts1; + ++verts2; + } + sceGuDrawArray(prim, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_COLOR_8888, count, 0, out); + } + if(r_showtris.value) + { + sceGuEnable(GU_TEXTURE_2D); + } + sceGuColor(0xffffffff); +} + +/* +============= +GL_DrawAliasInterpolatedFrame +============= +*/ +void GL_DrawAliasInterpolatedFrame (aliashdr_t *paliashdr, int posenum, int oldposenum, int interp) +{ + float l,r,g,b; + float interpolations; + trivertx_t *verts, *oldverts; + int *order; + int count; + + int prim; + prim = GU_TRIANGLE_FAN; + if(r_showtris.value) + { + sceGuDisable(GU_TEXTURE_2D); + prim = GU_LINE_STRIP; + } + + lastposenum = posenum; + interpolations = interp/r_ipolations.value; + verts = (trivertx_t *)((byte *)paliashdr + paliashdr->posedata); + oldverts = verts; + verts += posenum * paliashdr->poseverts; + if (oldposenum >= 0) + oldverts += oldposenum * paliashdr->poseverts; + else + oldverts += posenum * paliashdr->poseverts; + + order = (int *)((byte *)paliashdr + paliashdr->commands); + + + struct vertex + { + float u, v; + unsigned int color; + float x, y, z; + }; + + while (1) + { + // get the vertex count and primitive type + count = *order++; + + if (!count) + break; + + if(prim != GU_LINE_STRIP) + { + if (count < 0) + { + prim = GU_TRIANGLE_FAN; + count = -count; + } + else + { + prim = GU_TRIANGLE_STRIP; + //prim = GU_TRIANGLES; + } + } + + // Allocate the vertices. + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * count)); + + for (int vertex_index = 0; vertex_index < count; ++vertex_index) + { + // texture coordinates come from the draw list + out[vertex_index].u = ((float *)order)[0]; + out[vertex_index].v = ((float *)order)[1]; + order += 2; + // normals and vertexes come from the frame list + l = shadedots[verts->lightnormalindex]; + + r = l * lightcolor[0]; + g = l * lightcolor[1]; + b = l * lightcolor[2]; + if(r > 1) + r = 1; + if(g > 1) + g = 1; + if(b > 1) + b = 1; + + out[vertex_index].x = oldverts->v[0] + ((verts->v[0] - oldverts->v[0])*interpolations); + out[vertex_index].y = oldverts->v[1] + ((verts->v[1] - oldverts->v[1])*interpolations); + out[vertex_index].z = oldverts->v[2] + ((verts->v[2] - oldverts->v[2])*interpolations); + + out[vertex_index].color = GU_COLOR(r, g, b, 1.0f); + + ++verts; + ++oldverts; + } + sceGuDrawArray(prim, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_COLOR_8888, count, 0, out); + } + if(r_showtris.value) + { + sceGuEnable(GU_TEXTURE_2D); + } + sceGuColor(0xffffffff); +} + + +/* +============= +GL_DrawAliasShadow +============= +*/ +extern vec3_t lightspot; +void GL_DrawAliasShadow (aliashdr_t *paliashdr, int posenum) +{ + trivertx_t *verts; + int *order; + vec3_t point; + 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; + + struct vertex + { + float x, y, z; + }; + + while (1) + { + // get the vertex count and primitive type + int prim; + count = *order++; + + if (!count) + break; // done + + if (count < 0) + { + count = -count; + prim = GU_TRIANGLE_FAN; + } + else + prim = GU_TRIANGLE_STRIP; + + // Allocate the vertices. + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * count)); + + for (int vertex_index = 0; vertex_index < count; ++vertex_index) + { + // texture coordinates come from the draw list + // (skipped for shadows) glTexCoord2fv ((float *)order); + order += 2; + + // normals and vertexes come from the frame list + point[0] = verts->v[0] * paliashdr->scale[0] + paliashdr->scale_origin[0]; + point[1] = verts->v[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; + point[2] = verts->v[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; + + point[0] -= shadevector[0]*(point[2]+lheight); + point[1] -= shadevector[1]*(point[2]+lheight); + point[2] = height; + + out[vertex_index].x = point[0]; + out[vertex_index].y = point[1]; + out[vertex_index].z = point[2]; + ++verts; + } + if(r_showtris.value) + { + sceGuDisable(GU_TEXTURE_2D); + } + + sceGuDrawArray(r_showtris.value ? GU_LINE_STRIP : prim, GU_VERTEX_32BITF, count, 0, out); + + if(r_showtris.value) + { + sceGuEnable(GU_TEXTURE_2D); + } + } +} + +/* +============= +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; + + // Tomaz - New Shadow Begin + trace_t downtrace; + vec3_t downmove; + float s1,c1; + // Tomaz - New Shadow End + + blend = (realtime - e->frame_start_time) / e->frame_interval; + + if (blend > 1) blend = 1; + + lheight = e->origin[2] - lightspot[2]; + height = -lheight; // Tomaz - New Shadow + + // Tomaz - New Shadow Begin + VectorCopy (e->origin, downmove); + downmove[2] = downmove[2] - 4096; + memset (&downtrace, 0, sizeof(downtrace)); + SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, e->origin, downmove, &downtrace); + + #ifdef PSP_VFPU + s1 = vfpu_sinf( e->angles[1]/180*M_PI); + c1 = vfpu_cosf( e->angles[1]/180*M_PI); + #else + s1 = sin( e->angles[1]/180*M_PI); + c1 = cos( e->angles[1]/180*M_PI); + #endif + // Tomaz - New Shadow End + + 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 prim; + if (count < 0) + { + count = -count; + prim = GU_TRIANGLE_FAN; + } + else + { + prim = GU_TRIANGLE_STRIP; + } + + // Allocate the vertices. + struct vertex + { + float x, y, z; + }; + + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * count)); + + for (int vertex_index = 0; vertex_index < count; ++vertex_index) + { + 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); + + // Tomaz - New shadow Begin + point1[0] = point1[0] + (blend * d[0]); + point1[1] = point1[1] + (blend * d[1]); + point1[2] = point1[2] + (blend * d[2]); + + point1[2] = - (e->origin[2] - downtrace.endpos[2]); + + point1[2] += ((point1[1] * (s1 * downtrace.plane.normal[0])) - + (point1[0] * (c1 * downtrace.plane.normal[0])) - + (point1[0] * (s1 * downtrace.plane.normal[1])) - + (point1[1] * (c1 * downtrace.plane.normal[1]))) + + ((1.0 - downtrace.plane.normal[2])*20) + 0.2 ; + + out[vertex_index].x = point1[0]; + out[vertex_index].y = point1[1] ; + out[vertex_index].z = point1[2]; + // Tomaz - New shadow Begin + + verts1++; + verts2++; + } + if(r_showtris.value) + { + sceGuDisable(GU_TEXTURE_2D); + } + sceGuDrawArray(r_showtris.value ? GU_LINE_STRIP : prim,GU_VERTEX_32BITF, count, 0, out); + if(r_showtris.value) + { + sceGuEnable(GU_TEXTURE_2D); + } + } +} + + + +/* +================= +R_SetupAliasFrame + +================= +*/ +void R_SetupAliasFrame (int frame, aliashdr_t *paliashdr, float apitch, float ayaw) +{ + 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, apitch, ayaw); +} + +/* +================= +R_SetupAliasBlendedFrame + +fenix@io.com: model animation interpolation +================= +*/ +//double t1, t2, t3; + +void R_SetupAliasBlendedFrame (int frame, aliashdr_t *paliashdr, entity_t* e, float apitch, float ayaw) +{ + 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) + { + //Con_Printf("numposes (%i) > 1: %s, %i\n", numposes, e->model->name, frame); FIXME-Jukkiwashere (not sure if this was something you were working on blubs) + //if (e->iframetime) + // e->frame_interval = e->iframetime; + //else + 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. */ + //Jukki, except that this is not good for us. We need to control it better + //if (e->iframetime) + // e->frame_interval = e->iframetime; + //else + 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; + + if (blend == 1) + GL_DrawAliasFrame (paliashdr, pose, apitch, ayaw); + else + GL_DrawAliasBlendedFrame (paliashdr, e->pose1, e->pose2, blend, apitch, ayaw); +} + +/* +================= +R_SetupAliasInterpolatedFrame +================= +*/ +void R_SetupAliasInterpolatedFrame (int frame, int lastframe, float interp, aliashdr_t *paliashdr) +{ + int pose, numposes, oldpose; + float interval; + + if ((frame >= paliashdr->numframes) || (frame < 0)) + { + Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", frame); + frame = 0; + } + + if ((lastframe >= paliashdr->numframes) || (lastframe < 0)) + { + Con_DPrintf ("R_AliasSetupFrame: no such last frame %d\n", lastframe); + lastframe = 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; + } + + oldpose = paliashdr->frames[lastframe].firstpose; + numposes = paliashdr->frames[lastframe].numposes; + + if (numposes > 1) + { + interval = paliashdr->frames[lastframe].interval; + oldpose += (int)(cl.time / interval) % numposes; + } + + GL_DrawAliasInterpolatedFrame (paliashdr, pose, oldpose, interp); +} + +/* +============= +GL_DrawQ2AliasFrame +============= +*/ +void GL_DrawQ2AliasFrame (entity_t *e, md2_t *pheader, int lastpose, int pose, float lerp) +{ + float ilerp, l; + int *order, count; + md2trivertx_t *verts1, *verts2; + vec3_t scale1, translate1, scale2, translate2; + md2frame_t *frame1, *frame2; + + sceGuShadeModel(GU_SMOOTH); + + ilerp = 1.0f - lerp; + + //new version by muff - fixes bug, easier to read, faster (well slightly) + frame1 = (md2frame_t *)((int) pheader + pheader->ofs_frames + (pheader->framesize * lastpose)); + frame2 = (md2frame_t *)((int) pheader + pheader->ofs_frames + (pheader->framesize * pose)); + + VectorCopy(frame1->scale, scale1); + VectorCopy(frame1->translate, translate1); + VectorCopy(frame2->scale, scale2); + VectorCopy(frame2->translate, translate2); + verts1 = &frame1->verts[0]; + verts2 = &frame2->verts[0]; + order = (int *)((int)pheader + pheader->ofs_glcmds); + + while (1) + { + // get the vertex count and primitive type + count = *order++; + if (!count) + break; // done + int prim; + if (count < 0) + { + count = -count; + prim = GU_TRIANGLE_FAN; + } + else + { + prim = GU_TRIANGLE_STRIP; + } + + // Allocate the vertices. + struct vertex + { + float u, v; + unsigned int color; + float x, y, z; + }; + + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * count)); + + for (int vertex_index = 0; vertex_index < count; ++vertex_index) + { + // texture coordinates come from the draw list + out[vertex_index].u = ((float *)order)[0]; + out[vertex_index].v = ((float *)order)[1]; + l = shadedots[verts1->lightnormalindex]; + //l = shadedots[verts2->lightnormalindex] - shadedots[verts1->lightnormalindex]; + + float r,g,b; + + r = l * lightcolor[0]; + g = l * lightcolor[1]; + b = l * lightcolor[2]; + + if(r > 1) + r = 1; + if(g > 1) + g = 1; + if(b > 1) + b = 1; + + out[vertex_index].x = + (verts1[order[2]].v[0]*scale1[0]+translate1[0])*ilerp+ + (verts2[order[2]].v[0]*scale2[0]+translate2[0])*lerp; + + + out[vertex_index].y = + (verts1[order[2]].v[1]*scale1[1]+translate1[1])*ilerp+ + (verts2[order[2]].v[1]*scale2[1]+translate2[1])*lerp; + + out[vertex_index].z = + (verts1[order[2]].v[2]*scale1[2]+translate1[2])*ilerp+ + (verts2[order[2]].v[2]*scale2[2]+translate2[2])*lerp; + + out[vertex_index].color = + GU_COLOR(r, g, b, 1.0f); + + order+=3; + } + + if(r_showtris.value) + { + sceGuDisable(GU_TEXTURE_2D); + } + sceGuDrawArray(r_showtris.value ? GU_LINE_STRIP : prim, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_COLOR_8888, count, 0, out); + if(r_showtris.value) + { + sceGuEnable(GU_TEXTURE_2D); + } + } + sceGuColor(0xffffffff); +} + +/* +============= +GL_DrawQ2AliasShadow +============= +*/ +void GL_DrawQ2AliasShadow (entity_t *e, md2_t *pheader, int lastpose, int pose, float lerp) +{ + float ilerp, height, lheight; + int *order, count; + md2trivertx_t *verts1, *verts2; + vec3_t scale1, translate1, scale2, translate2, point; + md2frame_t *frame1, *frame2; + + // Tomaz - New Shadow Begin + trace_t downtrace; + vec3_t downmove; + float s1,c1; + // Tomaz - New Shadow End + + lheight = currententity->origin[2] - lightspot[2]; + + height = 0; + + ilerp = 1.0 - lerp; + + // Tomaz - New Shadow Begin + VectorCopy (e->origin, downmove); + downmove[2] = downmove[2] - 4096; + memset (&downtrace, 0, sizeof(downtrace)); + SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, e->origin, downmove, &downtrace); + + #ifdef PSP_VFPU + s1 = vfpu_sinf( e->angles[1]/180*M_PI); + c1 = vfpu_cosf( e->angles[1]/180*M_PI); + #else + s1 = sin( e->angles[1]/180*M_PI); + c1 = cos( e->angles[1]/180*M_PI); + #endif + // Tomaz - New Shadow End + + //new version by muff - fixes bug, easier to read, faster (well slightly) + frame1 = (md2frame_t *)((int) pheader + pheader->ofs_frames + (pheader->framesize * lastpose)); + frame2 = (md2frame_t *)((int) pheader + pheader->ofs_frames + (pheader->framesize * pose)); + + VectorCopy(frame1->scale, scale1); + VectorCopy(frame1->translate, translate1); + VectorCopy(frame2->scale, scale2); + VectorCopy(frame2->translate, translate2); + verts1 = &frame1->verts[0]; + verts2 = &frame2->verts[0]; + order = (int *)((int) pheader + pheader->ofs_glcmds); + + height = -lheight + 1.0; + + while (1) + { + // get the vertex count and primitive type + count = *order++; + if (!count) + break; // done + int prim; + if (count < 0) + { + count = -count; + prim = GU_TRIANGLE_FAN; + } + else + { + prim = GU_TRIANGLE_STRIP; + } + + // Allocate the vertices. + struct vertex + { + float x, y, z; + }; + + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * count)); + + for (int vertex_index = 0; vertex_index < count; ++vertex_index) + { + point[0] = + (verts1[order[2]].v[0]*scale1[0]+translate1[0])*ilerp+(verts2[order[2]].v[0]*scale2[0]+translate2[0])*lerp; + + point[1] = + (verts1[order[2]].v[1]*scale1[1]+translate1[1])*ilerp+(verts2[order[2]].v[1]*scale2[1]+translate2[1])*lerp; + + point[2] = + (verts1[order[2]].v[2]*scale1[2]+translate1[2])*ilerp+(verts2[order[2]].v[2]*scale2[2]+translate2[2])*lerp; + + // Tomaz - New shadow Begin + point[2] = - (e->origin[2] - downtrace.endpos[2]) ; + + point[2] += ((point[1] * (s1 * downtrace.plane.normal[0])) - + (point[0] * (c1 * downtrace.plane.normal[0])) - + (point[0] * (s1 * downtrace.plane.normal[1])) - + (point[1] * (c1 * downtrace.plane.normal[1]))) + + ((1.0 - downtrace.plane.normal[2])*20) + 0.2 ; + + out[vertex_index].x = point[0]; + out[vertex_index].y = point[1]; + out[vertex_index].z = point[2]; + // Tomaz - New shadow Begin + + order+=3; + } + if(r_showtris.value) + { + sceGuDisable(GU_TEXTURE_2D); + } + sceGuDrawArray(r_showtris.value ? GU_LINE_STRIP : prim,GU_VERTEX_32BITF, count, 0, out); + if(r_showtris.value) + { + sceGuEnable(GU_TEXTURE_2D); + } + } +} + +/* +================= +R_SetupQ2AliasFrame + +================= +*/ +void R_SetupQ2AliasFrame (entity_t *e, md2_t *pheader) +{ + int frame; + float lerp; + + frame = e->frame; + + sceGumPushMatrix (); + R_RotateForEntity (e, 0); + + if ((frame >= pheader->num_frames) || (frame < 0)) + { + Con_DPrintf ("R_SetupQ2AliasFrame: no such frame %d\n", frame); + frame = 0; + } + + if (e->draw_lastmodel == e->model) + { + if (frame != e->draw_pose) + { + e->draw_lastpose = e->draw_pose; + e->draw_pose = frame; + e->draw_lerpstart = cl.time; + lerp = 0; + } + else + lerp = (cl.time - e->draw_lerpstart) * 10.0; + } + else // uninitialized + { + e->draw_lastmodel = e->model; + e->draw_lastpose = e->draw_pose = frame; + e->draw_lerpstart = cl.time; + lerp = 0; + } + if (lerp > 1) lerp = 1; + + GL_DrawQ2AliasFrame (e, pheader, e->draw_lastpose, frame, lerp); + + if (r_shadows.value) + { + trace_t downtrace; + vec3_t downmove; + VectorCopy (e->origin, downmove); + + downmove[2] = downmove[2] - 4096; + memset (&downtrace, 0, sizeof(downtrace)); + SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, e->origin, downmove, &downtrace); + + sceGuDisable (GU_TEXTURE_2D); + sceGuEnable (GU_BLEND); + sceGuDepthMask(GU_TRUE); // disable zbuffer updates + sceGuColor(GU_COLOR(0,0,0,(1 - ((e->origin[2] + e->model->mins[2]-downtrace.endpos[2])/60)))); + + //stencil shadows + sceGuEnable(GU_STENCIL_TEST); + sceGuStencilFunc(GU_EQUAL,1,2); + sceGuStencilOp(GU_KEEP,GU_KEEP,GU_INCR); + + GL_DrawQ2AliasShadow (e, pheader, e->draw_lastpose, frame, lerp); + + sceGuDisable(GU_STENCIL_TEST); + + sceGuDepthMask(GU_FALSE); // enable zbuffer updates + sceGuEnable (GU_TEXTURE_2D); + sceGuDisable (GU_BLEND); + sceGuColor(0xffffffff); + } + + sceGumPopMatrix(); + sceGumUpdateMatrix(); +} + + +void IgnoreInterpolatioFrame (entity_t *e, aliashdr_t *paliashdr) +{ + if (strcmp(e->old_model, e->model->name) && e->model != NULL) + { + strcpy(e->old_model, e->model->name); + // fenix@io.com: model transform interpolation + e->frame_start_time = 0; + e->translate_start_time = 0; + e->rotate_start_time = 0; + e->pose1 = 0; + e->pose2 = paliashdr->frames[e->frame].firstpose; + } +} + +/* +===================== +R_DrawZombieLimb + +===================== +*/ +//Blubs Z hacks: need this declaration. +model_t *Mod_FindName (char *name); + + +void R_DrawZombieLimb (entity_t *e,int which) +{ + //entity_t *e; + model_t *clmodel; + aliashdr_t *paliashdr; + entity_t *limb_ent; + + //e = &cl_entities[ent]; + //clmodel = e->model; + + if(which == 1) + limb_ent = &cl_entities[e->z_head]; + else if(which == 2) + limb_ent = &cl_entities[e->z_larm]; + else if(which == 3) + limb_ent = &cl_entities[e->z_rarm]; + else + return; + + clmodel = limb_ent->model; + if(clmodel == NULL) + return; + + VectorCopy (e->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + // locate the proper data + paliashdr = (aliashdr_t *)Mod_Extradata (clmodel);//e->model + c_alias_polys += paliashdr->numtris; + + sceGumPushMatrix(); + + + //movement interpolation by blubs + R_InterpolateEntity(e,0); + + //blubs disabled + /*if (r_i_model_transform.value) + R_BlendedRotateForEntity (e, 0); + else + R_RotateForEntity (e, 0);*/ + + const ScePspFVector3 translation = + { + paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] + }; + sceGumTranslate(&translation); + + const ScePspFVector3 scaling = + { + paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2] + }; + sceGumScale(&scaling); + + sceGumUpdateMatrix(); + + IgnoreInterpolatioFrame(e, paliashdr); + + + if (r_i_model_animation.value) + { + R_SetupAliasBlendedFrame (e->frame, paliashdr, e, e->angles[0], e->angles[1]); + } + else + { + if (r_ipolations.value)//blubs: seems like we don't even use InterpolatedFrames. + { + if (r_asynch.value) + { + if (e->interpolation >= r_ipolations.value) + { + e->last_frame = e->current_frame; + e->current_frame = e->frame; + e->interpolation = 1; + } + } + else + { + if (e->frame != e->current_frame) + { + e->last_frame = e->current_frame; + e->current_frame = e->frame; + e->interpolation = 1; + } + } + R_SetupAliasInterpolatedFrame (e->current_frame,e->last_frame,e->interpolation,paliashdr); + } + else + R_SetupAliasFrame (e->frame, paliashdr, e->angles[0], e->angles[1]); + } + //t3 += Sys_FloatTime(); + sceGumPopMatrix(); + sceGumUpdateMatrix(); +} +/* +================= +R_DrawTransparentAliasModel +blubs: used for semitransparent fullbright models (like their sprite counterparts) +================= +*/ + +void R_DrawTransparentAliasModel (entity_t *e) +{ + model_t *clmodel; + vec3_t mins, maxs; + aliashdr_t *paliashdr; + float an; + int anim; + + clmodel = e->model; + + VectorAdd (e->origin, clmodel->mins, mins); + VectorAdd (e->origin, clmodel->maxs, maxs); + + if (R_CullBox(mins, maxs)) + return; + + VectorCopy (e->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + // + // get lighting information + // LordHavoc: .lit support begin + //ambientlight = shadelight = R_LightPoint (e->origin); // LordHavoc: original code, removed shadelight and ambientlight + R_LightPoint(e->origin); // LordHavoc: lightcolor is all that matters from this + // LordHavoc: .lit support end + lightcolor[0] = lightcolor[1] = lightcolor[2] = 256; + shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; + // LordHavoc: .lit support begin + //shadelight = shadelight / 200.0; // LordHavoc: original code + VectorScale(lightcolor, 1.0f / 200.0f, lightcolor); + // LordHavoc: .lit support end + an = e->angles[1]/180*M_PI; + shadevector[0] = cosf(-an); + shadevector[1] = sinf(-an); + shadevector[2] = 1; + VectorNormalize (shadevector); + // locate the proper data// + paliashdr = (aliashdr_t *)Mod_Extradata (e->model); + c_alias_polys += paliashdr->numtris; + // draw all the triangles// + sceGumPushMatrix(); + R_InterpolateEntity(e,0); + const ScePspFVector3 translation = + { + paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] + }; + sceGumTranslate(&translation); + const ScePspFVector3 scaling = + { + paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2] + }; + sceGumScale(&scaling); + + //for models(pink transparency) + sceGuEnable(GU_BLEND); + sceGuEnable(GU_ALPHA_TEST); + sceGuAlphaFunc(GU_GREATER, 0, 0xff); + + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + + //st1x:now quake transparency is working + //force_fullbright + //sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + + sceGuShadeModel(GU_SMOOTH); + sceGumUpdateMatrix(); + IgnoreInterpolatioFrame(e, paliashdr); + anim = (int)(cl.time*10) & 3; + GL_Bind(paliashdr->gl_texturenum[e->skinnum][anim]); + //Rendering block + if (r_i_model_animation.value) + { + R_SetupAliasBlendedFrame (e->frame, paliashdr, e, e->angles[0], e->angles[1]); + } + else + { + if (r_ipolations.value)//blubs: seems like we don't even use InterpolatedFrames. + { + if (r_asynch.value) + { + if (e->interpolation >= r_ipolations.value) + { + e->last_frame = e->current_frame; + e->current_frame = e->frame; + e->interpolation = 1; + } + } + else + { + if (e->frame != e->current_frame) + { + e->last_frame = e->current_frame; + e->current_frame = e->frame; + e->interpolation = 1; + } + } + R_SetupAliasInterpolatedFrame (e->current_frame,e->last_frame,e->interpolation,paliashdr); + } + else + R_SetupAliasFrame (e->frame, paliashdr, e->angles[0], e->angles[1]); + } + sceGumPopMatrix(); + sceGumUpdateMatrix(); + + //st1x:now quake transparency is working + sceGuAlphaFunc(GU_GREATER, 0, 0xff); + sceGuDisable(GU_ALPHA_TEST); + sceGuTexFunc(GU_TFX_REPLACE , GU_TCC_RGBA); + sceGuShadeModel(GU_FLAT); + +// else if(ISGLOW(e)) + { + sceGuDepthMask(GU_FALSE); + //sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + //sceGuDisable (GU_BLEND); + } + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + + sceGuDisable(GU_BLEND); +} + + +/* +================= +R_DrawAliasModel + +================= +*/ + +int doZHack; + +void R_DrawAliasModel (entity_t *e) +{ + char specChar; + model_t *clmodel; + vec3_t mins, maxs; + aliashdr_t *paliashdr; + float an; + int anim; + bool force_fullbright, additive; + + clmodel = e->model; + + VectorAdd (e->origin, clmodel->mins, mins); + VectorAdd (e->origin, clmodel->maxs, maxs); + + if (R_CullBox(mins, maxs)) + return; + + //=============================================================================================== 97% at this point + if(ISADDITIVE(e)) + { + float deg = e->renderamt; + float alpha_val = deg; + float alpha_val2 = 1 - deg; + + if(deg <= 0.7) + sceGuDepthMask(GU_TRUE); + + sceGuEnable (GU_BLEND); + sceGuBlendFunc(GU_ADD, GU_FIX, GU_FIX, + GU_COLOR(alpha_val,alpha_val,alpha_val,alpha_val), + GU_COLOR(alpha_val2,alpha_val2,alpha_val2,alpha_val2)); + } + else if(ISGLOW(e)) + { + sceGuDepthMask(GU_TRUE); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0xFFFFFFFF); + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); + } + else if (ISSOLID(e)) + { + sceGuEnable(GU_ALPHA_TEST); + float c = (e->renderamt) * 255.0f; + sceGuAlphaFunc(GU_GREATER, 0x88, c); + } + force_fullbright = false; + additive = false; + + VectorCopy (e->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + // + // get lighting information + // + + // LordHavoc: .lit support begin + //ambientlight = shadelight = R_LightPoint (e->origin); // LordHavoc: original code, removed shadelight and ambientlight + R_LightPoint(e->origin); // LordHavoc: lightcolor is all that matters from this + + // LordHavoc: .lit support end + + //blubswillrule: disabled dynamic lights + /*for (lnum=0 ; lnum= cl.time) + { + VectorSubtract (e->origin,cl_dlights[lnum].origin,dist); + add = cl_dlights[lnum].radius - Length(dist); + + 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 + } + }*/ + + //Shpuld + if(r_model_brightness.value) + { + lightcolor[0] += 32; + lightcolor[1] += 32; + lightcolor[2] += 32; + } + + + for(int g = 0; g < 3; g++) + { + if(lightcolor[g] < 8) + lightcolor[g] = 8; + if(lightcolor[g] > 125) + lightcolor[g] = 125; + } + + specChar = clmodel->name[strlen(clmodel->name) - 5]; + if(specChar == '!' || e->effects & EF_FULLBRIGHT) + { + lightcolor[0] = lightcolor[1] = lightcolor[2] = 256; + force_fullbright = true; + } + if(specChar == '@') + { + alphafunc = true; + } + if(specChar == '&') + { + lightcolor[0] = lightcolor[1] = lightcolor[2] = 256; + force_fullbright = true; + alphafunc = true; + } + //t3 += Sys_FloatTime(); + shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; + + // LordHavoc: .lit support begin + //shadelight = shadelight / 200.0; // LordHavoc: original code + VectorScale(lightcolor, 1.0f / 200.0f, lightcolor); + // LordHavoc: .lit support end + + an = e->angles[1]/180*M_PI; + shadevector[0] = cosf(-an); + shadevector[1] = sinf(-an); + shadevector[2] = 1; + VectorNormalize (shadevector); + + // + // locate the proper data + // + if(doZHack && specChar == '#') + { + if(clmodel->name[strlen(clmodel->name) - 6] == 'c') + paliashdr = (aliashdr_t *) Mod_Extradata(Mod_FindName("progs/ai/zcfull.mdl")); + else + paliashdr = (aliashdr_t *) Mod_Extradata(Mod_FindName("progs/ai/zfull.mdl")); + } + else + paliashdr = (aliashdr_t *)Mod_Extradata (e->model); + + c_alias_polys += paliashdr->numtris; + + // + // draw all the triangles + // + sceGumPushMatrix(); + + R_InterpolateEntity(e,0); + + //blubs disabled this + /*if (r_i_model_transform.value) + R_BlendedRotateForEntity (e, 0); + else + R_RotateForEntity (e, 0); + */ + + const ScePspFVector3 translation = + { + paliashdr->scale_origin[0], paliashdr->scale_origin[1], paliashdr->scale_origin[2] + }; + sceGumTranslate(&translation); + + const ScePspFVector3 scaling = + { + paliashdr->scale[0], paliashdr->scale[1], paliashdr->scale[2] + }; + sceGumScale(&scaling); + + //============================================================================================================================= 83% at this point + + // we can't dynamically colormap textures, so they are cached + // seperately for the players. Heads are just uncolored. + //if (e->colormap != vid.colormap && 0 /* && !gl_nocolors.value*/) + //{ + // i = e - cl_entities; + // if (i >= 1 && i<=cl.maxclients /*&& !strcmp (e->model->name, "models/player.mdl")*/) + // { + // GL_Bind(playertextures - 1 + i); + // } + //} + + //for models(pink transparency) + if (alphafunc) + { + sceGuEnable(GU_ALPHA_TEST); + sceGuAlphaFunc(GU_GREATER, 0, 0xff); + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + } + //st1x:now quake transparency is working + if (force_fullbright) + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + else + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + //for models (blue transparency) + if (alphafunc2 || alphafunc) + { + sceGuEnable(GU_ALPHA_TEST); + sceGuAlphaFunc(GU_GREATER, 0xaa, 0xff); + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + } + sceGuShadeModel(GU_SMOOTH); + + sceGumUpdateMatrix(); + + IgnoreInterpolatioFrame(e, paliashdr); + + if (specChar == '#')//Zombie body + { + switch(e->skinnum) + { + case 0: + GL_Bind(zombie_skins[0][0]); + break; + case 1: + GL_Bind(zombie_skins[0][1]); + break; + case 2: + GL_Bind(zombie_skins[1][0]); + break; + case 3: + GL_Bind(zombie_skins[1][1]); + break; + default: //out of bounds? assuming 0 + Con_Printf("Zombie tex out of bounds: Tex[%i]\n",e->skinnum); + GL_Bind(zombie_skins[0][0]); + break; + } + } + else + { + anim = (int)(cl.time*10) & 3; + GL_Bind(paliashdr->gl_texturenum[e->skinnum][anim]); + } + + //===================================================================================================== 80% at this point + //Rendering block + if (r_i_model_animation.value) + { + R_SetupAliasBlendedFrame (e->frame, paliashdr, e, e->angles[0], e->angles[1]); + } + else + { + if (r_ipolations.value)//blubs: seems like we don't even use InterpolatedFrames. + { + if (r_asynch.value) + { + if (e->interpolation >= r_ipolations.value) + { + e->last_frame = e->current_frame; + e->current_frame = e->frame; + e->interpolation = 1; + } + } + else + { + if (e->frame != e->current_frame) + { + e->last_frame = e->current_frame; + e->current_frame = e->frame; + e->interpolation = 1; + } + } + R_SetupAliasInterpolatedFrame (e->current_frame,e->last_frame,e->interpolation,paliashdr); + } + else + R_SetupAliasFrame (e->frame, paliashdr, e->angles[0], e->angles[1]); + } + sceGumPopMatrix(); + sceGumUpdateMatrix(); + + if (doZHack == 0 && specChar == '#')//if we're drawing zombie, also draw its limbs in one call + { + if(e->z_head) + R_DrawZombieLimb(e,1); + if(e->z_larm) + R_DrawZombieLimb(e,2); + if(e->z_rarm) + R_DrawZombieLimb(e,3); + } + //st1x:now quake transparency is working + sceGuAlphaFunc(GU_GREATER, 0, 0xff); + sceGuDisable(GU_ALPHA_TEST); + sceGuTexFunc(GU_TFX_REPLACE , GU_TCC_RGBA); + sceGuShadeModel(GU_FLAT); + + //Blubswillrule: disabled the next two calls, they look like duplicates + //sceGuShadeModel(GU_FLAT); + //sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + if (ISADDITIVE(e)) + { + float deg = e->renderamt; + if(deg <= 0.7) + sceGuDepthMask(GU_FALSE); + //sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + //sceGuDisable (GU_BLEND); + } + else if(ISSOLID(e)) + { + sceGuAlphaFunc(GU_GREATER, 0, 0xff); + sceGuDisable(GU_ALPHA_TEST); + } + else if(ISGLOW(e)) + { + sceGuDepthMask(GU_FALSE); + //sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + //sceGuDisable (GU_BLEND); + } + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuDisable(GU_BLEND); +} + +void R_DrawMD2Model (entity_t *e) +{ + int i; + int lnum; + vec3_t dist; + float add; + model_t *clmodel; + vec3_t mins, maxs; + float an; + bool force_fullbright, additive; + + md2_t *pheader; // LH / muff + + if(ISADDITIVE(e)) + { + float deg = e->renderamt; + float alpha_val = deg; + float alpha_val2 = 1 - deg; + + if(deg <= 0.7) + sceGuDepthMask(GU_TRUE); + + sceGuEnable (GU_BLEND); + sceGuBlendFunc(GU_ADD, GU_FIX, GU_FIX, + GU_COLOR(alpha_val,alpha_val,alpha_val,alpha_val), + GU_COLOR(alpha_val2,alpha_val2,alpha_val2,alpha_val2)); + } + else if(ISGLOW(e)) + { + sceGuDepthMask(GU_TRUE); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0xFFFFFFFF); + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); + } + else if (ISSOLID(e)) + { + sceGuEnable(GU_ALPHA_TEST); + float c = (e->renderamt) * 255.0f; + sceGuAlphaFunc(GU_GREATER, 0x88, c); + } + + force_fullbright = false; + additive = false; + clmodel = e->model; + + VectorAdd (e->origin, clmodel->mins, mins); + VectorAdd (e->origin, clmodel->maxs, maxs); + + //if (e->angles[0] || e->angles[1] || e->angles[2]) + //{ + // if (R_CullSphere(e->origin, clmodel->radius)) + // return; + //} + //else + //{ + if (R_CullBox(mins, maxs)) + return; + //} + + + VectorCopy (e->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + // + // get lighting information + // + + // LordHavoc: .lit support begin + //ambientlight = shadelight = R_LightPoint (e->origin); // LordHavoc: original code, removed shadelight and ambientlight + R_LightPoint(e->origin); // LordHavoc: lightcolor is all that matters from this + // LordHavoc: .lit support end + + // always give the gun some light + // LordHavoc: .lit support begin + //if (e == &cl.viewent && ambientlight < 24) // LordHavoc: original code + // ambientlight = shadelight = 24; // LordHavoc: original code +/* + 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 (e->origin, + cl_dlights[lnum].origin, + dist); + add = cl_dlights[lnum].radius - Length(dist); + + // LordHavoc: .lit support begin + /* LordHavoc: original code + if (add > 0) { + ambientlight += add; + //ZOID models should be affected by dlights as well + shadelight += add; + } + */ + 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 + } + } + + // clamp lighting so it doesn't overbright as much +/* + if (shadelight > 65) + shadelight = 65; + if (ambientlight > 196) + { + ambientlight = 196; + force_fullbright = true; + } + else + force_fullbright = false; +*/ + + // ZOID: never allow players to go totally black + // i = e - cl_entities; + // if (i >= 1 && i<=cl.maxclients /*&& !strcmp (e->model->name, "models/player.mdl") */) + // LordHavoc: .lit support begin + // if (ambientlight < 8) // LordHavoc: original code + // ambientlight = shadelight = 8; // LordHavoc: original code + + //Shpuld + if(r_model_brightness.value) + { + lightcolor[0] += 32; + lightcolor[1] += 32; + lightcolor[2] += 32; + } + + + for(int g = 0; g < 3; g++) + { + if(lightcolor[g] < 8) + lightcolor[g] = 8; + + if(lightcolor[g] > 125) + lightcolor[g] = 125; + } + // HACK HACK HACK -- no fullbright colors, so make torches and projectiles full light + if (!strcmp (clmodel->name, "progs/flame2.mdl") || + !strcmp (clmodel->name, "progs/flame.mdl") || + !strcmp (clmodel->name, "progs/lavaball.mdl") || + !strcmp (clmodel->name, "progs/bolt.mdl") || + !strcmp (clmodel->name, "progs/bolt2.mdl") || + !strcmp (clmodel->name, "progs/bolt3.mdl") || + !strcmp (clmodel->name, "progs/eyes.mdl") || + !strcmp (clmodel->name, "progs/k_spike.mdl") || + !strcmp (clmodel->name, "progs/s_spike.mdl") || + !strcmp (clmodel->name, "progs/spike.mdl") || + !strcmp (clmodel->name, "progs/Misc/chalk.mdl") || + !strcmp (clmodel->name, "progs/Misc/x2.mdl") || + !strcmp (clmodel->name, "progs/Misc/nuke.mdl") || + !strcmp (clmodel->name, "progs/Misc/instakill.mdl") || + !strcmp (clmodel->name, "progs/Misc/perkbottle.mdl") || + !strcmp (clmodel->name, "progs/Misc/carpenter.mdl") || + !strcmp (clmodel->name, "progs/Misc/maxammo.mdl") || + !strcmp (clmodel->name, "progs/Misc/lamp_ndu.mdl") || + !strcmp (clmodel->name, "progs/laser.mdl")) + { + lightcolor[0] = lightcolor[1] = lightcolor[2] = 256; + force_fullbright = true; + } + + if (e->effects & EF_FULLBRIGHT) + { + lightcolor[0] = lightcolor[1] = lightcolor[2] = 256; + force_fullbright = true; + } + + if (!strcmp (clmodel->name, "progs/v_rpg.mdl") || + !strcmp (clmodel->name, "progs/stalker.mdl") || + !strcmp (clmodel->name, "progs/VModels/scope.mdl")) + { + alphafunc = true; + } + shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; + + // LordHavoc: .lit support begin + //shadelight = shadelight / 200.0; // LordHavoc: original code + VectorScale(lightcolor, 1.0f / 200.0f, lightcolor); + // LordHavoc: .lit support end + + an = e->angles[1]/180*M_PI; + shadevector[0] = cosf(-an); + shadevector[1] = sinf(-an); + shadevector[2] = 1; + VectorNormalize (shadevector); + + // + // locate the proper data + // + pheader = (md2_t *)Mod_Extradata (e->model); + c_alias_polys += pheader->num_tris; + + // + // draw all the triangles + // + GL_Bind(pheader->gl_texturenum[e->skinnum]); + + // we can't dynamically colormap textures, so they are cached + // seperately for the players. Heads are just uncolored. + if (e->colormap != vid.colormap && 0 /* && !gl_nocolors.value*/) + { + i = e - cl_entities; + if (i >= 1 && i<=cl.maxclients /*&& !strcmp (e->model->name, "models/player.mdl")*/) + { + GL_Bind(playertextures - 1 + i); + } + } + //for models(pink transparency) + if (alphafunc) + { + sceGuEnable(GU_ALPHA_TEST); + sceGuAlphaFunc(GU_GREATER, 0, 0xff); + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + } +//st1x:now quake transparency is working + if (force_fullbright) + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + else + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + //for models (blue transparency) + if (alphafunc2) + { + sceGuEnable(GU_ALPHA_TEST); + sceGuAlphaFunc(GU_GREATER, 0xaa, 0xff); + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + } + + sceGuShadeModel(GU_SMOOTH); + + + + R_SetupQ2AliasFrame (e, pheader); + + //st1x:now quake transparency is working + sceGuAlphaFunc(GU_GREATER, 0, 0xff); + sceGuDisable(GU_ALPHA_TEST); + sceGuTexFunc(GU_TFX_REPLACE , GU_TCC_RGBA); + sceGuShadeModel(GU_FLAT); + + + if (ISADDITIVE(e)) + { + float deg = e->renderamt; + + if(deg <= 0.7) + sceGuDepthMask(GU_FALSE); + + //sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + //sceGuDisable (GU_BLEND); + } + else if(ISSOLID(e)) + { + sceGuAlphaFunc(GU_GREATER, 0, 0xff); + sceGuDisable(GU_ALPHA_TEST); + } + else if(ISGLOW(e)) + { + sceGuDepthMask(GU_FALSE); + //sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + //sceGuDisable (GU_BLEND); + } + + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuDisable(GU_BLEND); +} + +//================================================================================== +//=================================Q3 Models======================================== +//================================================================================== + +int bodyframe = 0, legsframe = 0; +animtype_t bodyanim, legsanim; + +//ScePspFMatrix4 matrix; + +void R_ReplaceQ3Frame (int frame) +{ + animdata_t *currbodyanim, *currlegsanim; + static animtype_t oldbodyanim, oldlegsanim; + static float bodyanimtime, legsanimtime; + static qboolean deathanim = qfalse; + + if (deathanim) + { + bodyanim = oldbodyanim; + legsanim = oldlegsanim; + } + + if (frame < 41 || frame > 102) + deathanim = qfalse; + + if (frame >= 0 && frame <= 5) // axrun + { + bodyanim = torso_stand2; + legsanim = legs_run; + } + else if (frame >= 6 && frame <= 11) // rockrun + { + bodyanim = torso_stand; + legsanim = legs_run; + } + else if ((frame >= 12 && frame <= 16) || (frame >= 35 && frame <= 40)) // stand, pain + { + bodyanim = torso_stand; + legsanim = legs_idle; + } + else if ((frame >= 17 && frame <= 28) || (frame >= 29 && frame <= 34)) // axstand, axpain + { + bodyanim = torso_stand2; + legsanim = legs_idle; + } + else if (frame >= 41 && frame <= 102 && !deathanim) // axdeath, deatha, b, c, d, e + { + bodyanim = legsanim = both_death1; + deathanim = qtrue; + } + else if (frame > 103 && frame <= 118) // gun attacks + { + bodyanim = torso_attack; + } + else if (frame >= 119) // axe attacks + { + bodyanim = torso_attack2; + } + + currbodyanim = &anims[bodyanim]; + currlegsanim = &anims[legsanim]; + + if (bodyanim == oldbodyanim) + { + if (cl.time >= bodyanimtime + currbodyanim->interval) + { + if (currbodyanim->loop_frames && bodyframe + 1 >= currbodyanim->offset + currbodyanim->loop_frames) + bodyframe = currbodyanim->offset; + else if (bodyframe + 1 < currbodyanim->offset + currbodyanim->num_frames) + bodyframe++; + bodyanimtime = cl.time; + } + } + else + { + bodyframe = currbodyanim->offset; + bodyanimtime = cl.time; + } + + if (legsanim == oldlegsanim) + { + if (cl.time >= legsanimtime + currlegsanim->interval) + { + if (currlegsanim->loop_frames && legsframe + 1 >= currlegsanim->offset + currlegsanim->loop_frames) + legsframe = currlegsanim->offset; + else if (legsframe + 1 < currlegsanim->offset + currlegsanim->num_frames) + legsframe++; + legsanimtime = cl.time; + } + } + else + { + legsframe = currlegsanim->offset; + legsanimtime = cl.time; + } + + oldbodyanim = bodyanim; + oldlegsanim = legsanim; +} + +int multimodel_level; +bool surface_transparent; + +/* +================= +R_DrawQ3Frame +================= +*/ +void R_DrawQ3Frame (int frame, md3header_t *pmd3hdr, md3surface_t *pmd3surf, entity_t *ent, int distance) +{ + int i, j, numtris, pose, pose1, pose2; + float l, lerpfrac; + vec3_t lightvec, interpolated_verts; + unsigned int *tris; + md3tc_t *tc; + md3vert_mem_t *verts, *v1, *v2; + model_t *clmodel = ent->model; + + if ((frame >= pmd3hdr->numframes) || (frame < 0)) + { + Con_DPrintf ("R_DrawQ3Frame: no such frame %d\n", frame); + frame = 0; + } + + if (ent->pose1 >= pmd3hdr->numframes) + ent->pose1 = 0; + + pose = frame; + + if (!strcmp(clmodel->name, "models/player/lower.md3")) + ent->frame_interval = anims[legsanim].interval; + else if (!strcmp(clmodel->name, "models/player/upper.md3")) + ent->frame_interval = anims[bodyanim].interval; + else + ent->frame_interval = 0.1; + + if (ent->pose2 != pose) + { + ent->frame_start_time = cl.time; + ent->pose1 = ent->pose2; + ent->pose2 = pose; + ent->framelerp = 0; + } + else + { + ent->framelerp = (cl.time - ent->frame_start_time) / ent->frame_interval; + } + + // weird things start happening if blend passes 1 + if (cl.paused || ent->framelerp > 1) + ent->framelerp = 1; + + verts = (md3vert_mem_t *)((byte *)pmd3hdr + pmd3surf->ofsverts); + tc = (md3tc_t *)((byte *)pmd3surf + pmd3surf->ofstc); + tris = (unsigned int *)((byte *)pmd3surf + pmd3surf->ofstris); + numtris = pmd3surf->numtris * 3; + pose1 = ent->pose1 * pmd3surf->numverts; + pose2 = ent->pose2 * pmd3surf->numverts; + + if (surface_transparent) + { + sceGuEnable (GU_BLEND); + //sceGuBlendFunc (GL_ONE, GL_ONE); + sceGuDepthMask (GU_TRUE); + sceGuDisable (GU_CULL_FACE); + } + else + if (ISADDITIVE(ent)) + sceGuEnable (GU_BLEND); + + // Allocate the vertices. + struct vertex + { + float u, v; + unsigned int color; + float x, y, z; + }; + + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * numtris)); + + for (i=0 ; ivec, v2->vec, distance) ? ent->framelerp : 1; + + + l = FloatInterpolate (shadedots[v1->oldnormal>>8], lerpfrac, shadedots[v2->oldnormal>>8]); + l = l / 256; + l = fmin(l, 1); + + VectorInterpolate (v1->vec, lerpfrac, v2->vec, interpolated_verts); + + out[i].x = interpolated_verts[0]; + out[i].y = interpolated_verts[1]; + out[i].z = interpolated_verts[2]; + + for (j = 0 ; j < 3 ; j++) + lightvec[j] = lightcolor[j] /1.0f + l; + + out[i].color = GU_COLOR(lightvec[0], lightvec[1], lightvec[2], 1.0f); + *tris++; + } + + if(r_showtris.value) + { + sceGuDisable(GU_TEXTURE_2D); + } + + sceGuDrawArray(r_showtris.value ? GU_LINE_STRIP : GU_TRIANGLES, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_COLOR_8888, numtris, 0, out); + + if(r_showtris.value) + { + sceGuEnable(GU_TEXTURE_2D); + } + + if (surface_transparent) + { + sceGuDisable (GU_BLEND); + sceGuBlendFunc (GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuDepthMask (GU_FALSE); + sceGuEnable (GU_CULL_FACE); + } + else + if (ISADDITIVE(ent)) + sceGuDisable (GU_BLEND); +} + +/* +================= +R_DrawQ3Shadow +================= +*/ +void R_DrawQ3Shadow (entity_t *ent, float lheight, float s1, float c1, trace_t downtrace) +{ + int i, j, numtris, pose1, pose2; + vec3_t point1, point2, interpolated; + md3header_t *pmd3hdr; + md3surface_t *pmd3surf; + unsigned int *tris; + md3vert_mem_t *verts; + model_t *clmodel = ent->model; +#if 0 + float m[16]; + md3tag_t *tag; + tagentity_t *tagent; +#endif + + pmd3hdr = (md3header_t *)Mod_Extradata (clmodel); + + pmd3surf = (md3surface_t *)((byte *)pmd3hdr + pmd3hdr->ofssurfs); + for (i=0 ; inumsurfs ; i++) + { + verts = (md3vert_mem_t *)((byte *)pmd3hdr + pmd3surf->ofsverts); + tris = (unsigned int *)((byte *)pmd3surf + pmd3surf->ofstris); + numtris = pmd3surf->numtris * 3; + pose1 = ent->pose1 * pmd3surf->numverts; + pose2 = ent->pose2 * pmd3surf->numverts; + + // Allocate the vertices. + struct vertex + { + float x, y, z; + }; + + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * numtris)); + + for (j=0 ; jframelerp, point2, interpolated); + + interpolated[2] = -(ent->origin[2] - downtrace.endpos[2]); + + interpolated[2] += ((interpolated[1] * (s1 * downtrace.plane.normal[0])) - + (interpolated[0] * (c1 * downtrace.plane.normal[0])) - + (interpolated[0] * (s1 * downtrace.plane.normal[1])) - + (interpolated[1] * (c1 * downtrace.plane.normal[1]))) + + ((1 - downtrace.plane.normal[2]) * 20) + 0.2; + + out[j].x = interpolated[0]; + out[j].y = interpolated[1]; + out[j].z = interpolated[2]; + *tris++; + } + if(r_showtris.value) + { + sceGuDisable(GU_TEXTURE_2D); + } + sceGuDrawArray(r_showtris.value ? GU_LINE_STRIP : GU_TRIANGLES,GU_VERTEX_32BITF, numtris, 0, out); + if(r_showtris.value) + { + sceGuEnable(GU_TEXTURE_2D); + } + + pmd3surf = (md3surface_t *)((byte *)pmd3surf + pmd3surf->ofsend); + } + + if (!pmd3hdr->numtags) // single model, done + return; + +// no multimodel shadow support yet +#if 0 + tag = (md3tag_t *)((byte *)pmd3hdr + pmd3hdr->ofstags); + tag += ent->pose2 * pmd3hdr->numtags; + for (i=0 ; inumtags ; i++, tag++) + { + if (multimodel_level == 0 && !strcmp(tag->name, "tag_torso")) + { + tagent = &q3player_body; + ent = &q3player_body.ent; + multimodel_level++; + } + else if (multimodel_level == 1 && !strcmp(tag->name, "tag_head")) + { + tagent = &q3player_head; + ent = &q3player_head.ent; + multimodel_level++; + } + else + { + continue; + } + + glPushMatrix (); + R_RotateForTagEntity (tagent, tag, m); + glMultMatrixf (m); + R_DrawQ3Shadow (ent, lheight, s1, c1, downtrace); + glPopMatrix (); + } +#endif +} + +/* +================= +R_SetupQ3Frame +================= +*/ +void R_SetupQ3Frame (entity_t *ent) +{ + int i, j, frame, shadernum, texture; + float m[16]; + md3header_t *pmd3hdr; + md3surface_t *pmd3surf; + md3tag_t *tag; + model_t *clmodel = ent->model; + tagentity_t *tagent; + + if (!strcmp(clmodel->name, "models/player/lower.md3")) + frame = legsframe; + else if (!strcmp(clmodel->name, "models/player/upper.md3")) + frame = bodyframe; + else + frame = ent->frame; + + // locate the proper data + pmd3hdr = (md3header_t *)Mod_Extradata (clmodel); + + // draw all the triangles + + // draw non-transparent surfaces first, then the transparent ones + for (i=0 ; i<2 ; i++) + { + pmd3surf = (md3surface_t *)((byte *)pmd3hdr + pmd3hdr->ofssurfs); + for (j=0 ; jnumsurfs ; j++) + { + md3shader_mem_t *shader; + + surface_transparent = (strstr(pmd3surf->name, "energy") || + strstr(pmd3surf->name, "f_") || + strstr(pmd3surf->name, "flare") || + strstr(pmd3surf->name, "flash") || + strstr(pmd3surf->name, "Sphere") || + strstr(pmd3surf->name, "telep")); + + if ((!i && surface_transparent) || (i && !surface_transparent)) + { + pmd3surf = (md3surface_t *)((byte *)pmd3surf + pmd3surf->ofsend); + continue; + } + + c_md3_polys += pmd3surf->numtris; + + shadernum = ent->skinnum; + if ((shadernum >= pmd3surf->numshaders) || (shadernum < 0)) + { + Con_DPrintf ("R_SetupQ3Frame: no such skin # %d\n", shadernum); + shadernum = 0; + } + + shader = (md3shader_mem_t *)((byte *)pmd3hdr + pmd3surf->ofsshaders); + + texture = shader[shadernum].gl_texnum; + + //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + GL_Bind (texture); + + R_DrawQ3Frame (frame, pmd3hdr, pmd3surf, ent, INTERP_MAXDIST); + + pmd3surf = (md3surface_t *)((byte *)pmd3surf + pmd3surf->ofsend); + } + } + + if (!pmd3hdr->numtags) // single model, done + return; + + tag = (md3tag_t *)((byte *)pmd3hdr + pmd3hdr->ofstags); + tag += frame * pmd3hdr->numtags; + for (i=0 ; inumtags ; i++, tag++) + { + if (multimodel_level == 0 && !strcmp(tag->name, "tag_torso")) + { + tagent = &q3player_body; + ent = &q3player_body.ent; + multimodel_level++; + } + else if (multimodel_level == 1 && !strcmp(tag->name, "tag_head")) + { + tagent = &q3player_head; + ent = &q3player_head.ent; + multimodel_level++; + } + else + { + continue; + } + + sceGumPushMatrix (); + R_RotateForTagEntity (tagent, tag, m); + ConvertMatrix((float*)&md3mult, m); + sceGumMultMatrix(&md3mult); + sceGumUpdateMatrix (); + R_SetupQ3Frame (ent); + sceGumPopMatrix (); + } +} + +extern cvar_t scr_fov; + +/* +================= +R_DrawQ3Model +================= +*/ +void R_DrawQ3Model (entity_t *ent) +{ + vec3_t mins, maxs, md3_scale_origin = {0, 0, 0}; + model_t *clmodel = ent->model; + float scale; + int lnum; + vec3_t dist; + float add, an; + + VectorAdd (ent->origin, clmodel->mins, mins); + VectorAdd (ent->origin, clmodel->maxs, maxs); + if(ISADDITIVE(ent)) + { + float deg = ent->renderamt; + float alpha_val = deg; + float alpha_val2 = 1 - deg; + if(deg <= 0.7) + sceGuDepthMask(GU_TRUE); + + sceGuEnable (GU_BLEND); + sceGuBlendFunc(GU_ADD, GU_FIX, GU_FIX, + GU_COLOR(alpha_val,alpha_val,alpha_val,alpha_val), + GU_COLOR(alpha_val2,alpha_val2,alpha_val2,alpha_val2)); + } + else if(ISGLOW(ent)) + { + sceGuDepthMask(GU_TRUE); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0xFFFFFFFF); + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGB); + } + else if (ISSOLID(ent)) + { + sceGuEnable(GU_ALPHA_TEST); + float c = (ent->renderamt) * 255.0f; + sceGuAlphaFunc(GU_GREATER, 0x88, c); + } + if (ent->angles[0] || ent->angles[1] || ent->angles[2]) + { + if (R_CullSphere(ent->origin, clmodel->radius)) + return; + } + else + { + if (R_CullBox(mins, maxs)) + return; + } + + //========================================================================== + + VectorCopy (ent->origin, r_entorigin); + VectorSubtract (r_origin, r_entorigin, modelorg); + + // + // get lighting information + // + R_LightPoint(currententity->origin); // LordHavoc: lightcolor is all that matters from this + + for (lnum=0 ; lnum= cl.time) + { + VectorSubtract (currententity->origin, + cl_dlights[lnum].origin, + dist); + add = cl_dlights[lnum].radius - Length(dist); + 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]; + } + } + } + + // clamp lighting so it doesn't overbright as much + //ColorClamp(lightcolor[0], lightcolor[1], lightcolor[2], 0, 125, 8); + + for(int g = 0; g < 3; g++) + { + if(lightcolor[g] < 8) + lightcolor[g] = 8; + + if(lightcolor[g] > 125) + lightcolor[g] = 125; + } + + shadedots = r_avertexnormal_dots[((int)(ent->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; + + VectorScale(lightcolor, 1.0f / 200.0f, lightcolor); + + + an = ent->angles[1]/180*M_PI; + shadevector[0] = cosf(-an); + shadevector[1] = sinf(-an); + shadevector[2] = 1; + VectorNormalize (shadevector); + + + //========================================================================== + + sceGumPushMatrix (); + + + if (ent == &cl.viewent) + R_RotateForViewEntity (ent); + else + R_RotateForEntity(ent, 0); + + if ((ent == &cl.viewent) && (scr_fov.value != 0)) + { + if (scr_fov.value <= 90) + scale = 1.0f; + else + #ifdef PSP_VFPU + scale = 1.0f / vfpu_tanf( DEG2RAD(scr_fov.value/2)); + #else + scale = 1.0f / tan( DEG2RAD(scr_fov.value/2)); + #endif + + const ScePspFVector3 translation = + { + md3_scale_origin[0]*scale, md3_scale_origin[1], md3_scale_origin[2] + }; + sceGumTranslate(&translation); + + const ScePspFVector3 GUscale = + { + scale, 1, 1 + }; + sceGumScale(&GUscale); + } + else + { + const ScePspFVector3 translation = + { + md3_scale_origin[0], md3_scale_origin[1], md3_scale_origin[2] + }; + sceGumTranslate(&translation); + } + + + sceGuShadeModel (GU_SMOOTH); + + sceGumUpdateMatrix (); + + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + + //========================================================================== + + if ((!strcmp(ent->model->name, "models/player/lower.md3"))||(!strcmp(ent->model->name, "models/player/upper.md3"))) + { + //q3player_body.ent.renderamt = q3player_head.ent.renderamt = cl_entities[cl.viewentity].renderamt; + R_ReplaceQ3Frame (ent->frame); + ent->noshadow = qtrue; + } + + multimodel_level = 0; + R_SetupQ3Frame (ent); + + sceGuShadeModel (GU_FLAT); + + sceGuTexFunc(GU_TFX_REPLACE , GU_TCC_RGBA); + + sceGumPopMatrix (); + sceGumUpdateMatrix (); + + if (r_shadows.value && !ent->noshadow) + { + int farclip; + float theta, lheight, s1, c1; + vec3_t downmove; + trace_t downtrace; + static float shadescale = 0; + + farclip = fmax((int)r_farclip.value, 4096); + + if (!shadescale) + shadescale = 1 / sqrt(2); + theta = -ent->angles[1] / 180 * M_PI; + + #ifdef PSP_VFPU + VectorSet (shadevector, cos(theta) * shadescale, vfpu_sinf(theta) * shadescale, shadescale); + #else + VectorSet (shadevector, cos(theta) * shadescale, sin(theta) * shadescale, shadescale); + #endif + + sceGumPushMatrix (); + + R_RotateForEntity (ent, 0); + + VectorCopy (ent->origin, downmove); + downmove[2] -= farclip; + memset (&downtrace, 0, sizeof(downtrace)); + SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, ent->origin, downmove, &downtrace); + + lheight = ent->origin[2] - lightspot[2]; + + #ifdef PSP_VFPU + s1 = vfpu_sinf(ent->angles[1] / 180 * M_PI); + c1 = vfpu_cosf(ent->angles[1] / 180 * M_PI); + #else + s1 = sin(ent->angles[1] / 180 * M_PI); + c1 = cos(ent->angles[1] / 180 * M_PI); + #endif + + sceGuDepthMask (GU_TRUE); + sceGuDisable (GU_TEXTURE_2D); + sceGuEnable (GU_BLEND); + + sceGuColor(GU_RGBA( + static_cast(0.0f * 255.0f), + static_cast(0.0f * 255.0f), + static_cast(0.0f * 255.0f), + static_cast(((ambientlight - (mins[2] - downtrace.endpos[2]))*r_shadows.value)*0.0066 * 255.0f))); + + multimodel_level = 0; + + sceGuEnable(GU_STENCIL_TEST); + sceGuStencilFunc(GU_EQUAL,1,2); + sceGuStencilOp(GU_KEEP,GU_KEEP,GU_INCR); + + R_DrawQ3Shadow (ent, lheight, s1, c1, downtrace); + + sceGuDisable(GU_STENCIL_TEST); + + sceGuDepthMask (GU_FALSE); + sceGuEnable (GU_TEXTURE_2D); + sceGuDisable (GU_BLEND); + sceGuColor(GU_RGBA(0xff,0xff,0xff,0xff)); //return to normal color + + sceGumPopMatrix (); + sceGumUpdateMatrix (); + } + if (ISADDITIVE(ent)) + { + float deg = ent->renderamt; + + if(deg <= 0.7) + sceGuDepthMask(GU_FALSE); + + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuDisable (GU_BLEND); + } + else if(ISSOLID(ent)) + { + sceGuAlphaFunc(GU_GREATER, 0, 0xff); + sceGuDisable(GU_ALPHA_TEST); + } + else if(ISGLOW(ent)) + { + sceGuDepthMask(GU_FALSE); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuDisable (GU_BLEND); + } +} + +/* +============= +R_DrawNullModel +From pspq2 +============= +*/ +void R_DrawNullModel(void) +{ + R_LightPoint(currententity->origin); + sceGumPushMatrix(); + sceGuDisable(GU_TEXTURE_2D); + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + sceGuShadeModel (GU_SMOOTH); + R_RotateForEntity(currententity, 0); + typedef struct VERT_t + { + float x, y, z; + } VERT; + VERT* v; + sceGuColor(0x0099FF); + v = (VERT*)sceGuGetMemory(sizeof(VERT) * 6); + v[0].x = 0.0f; v[0].y = 0.0f; v[0].z = 9.0f; + v[1].x = 9.0f; v[1].y = 0.0f; v[1].z = 0.0f; + v[2].x = 0.0f; v[2].y = -9.0f; v[2].z = 0.0f; + v[3].x = -9.0f; v[3].y = 0.0f; v[3].z = 0.0f; + v[4].x = 0.0f; v[4].y = 9.0f; v[4].z = 0.0f; + v[5].x = 9.0f; v[5].y = 0.0f; v[5].z = 0.0f; + sceGumDrawArray(r_showtris.value ? GU_LINE_STRIP : GU_TRIANGLE_FAN, GU_VERTEX_32BITF | GU_TRANSFORM_3D, 6, 0, v); + sceGuColor(0x0000FF); + v = (VERT*)sceGuGetMemory(sizeof(VERT) * 6); + v[0].x = 0.0f; v[0].y = 0.0f; v[0].z = -9.0f; + v[1].x = 9.0f; v[1].y = 0.0f; v[1].z = 0.0f; + v[2].x = 0.0f; v[2].y = 9.0f; v[2].z = 0.0f; + v[3].x = -9.0f; v[3].y = 0.0f; v[3].z = 0.0f; + v[4].x = 0.0f; v[4].y = -9.0f; v[4].z = 0.0f; + v[5].x = 9.0f; v[5].y = 0.0f; v[5].z = 0.0f; + sceGumDrawArray(r_showtris.value ? GU_LINE_STRIP : GU_TRIANGLE_FAN, GU_VERTEX_32BITF | GU_TRANSFORM_3D, 6, 0, v); + sceGuTexFunc(GU_TFX_REPLACE , GU_TCC_RGBA); + sceGuColor(0xFFFFFF); + sceGuEnable(GU_TEXTURE_2D); + sceGumPopMatrix(); +} +/* +================ +R_EmitWireBox -- johnfitz -- draws one axis aligned bounding box +================ +*/ +void R_EmitWireBox (vec3_t mins, vec3_t maxs,qboolean line_strip) +{ + // Allocate the vertices. + struct vertex + { + float x, y, z; + }; + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * 10)); + out[0].x = mins[0]; out[0].y = mins[1]; out[0].z = mins[2]; + out[1].x = mins[0]; out[1].y = mins[1]; out[1].z = maxs[2]; + out[2].x = maxs[0]; out[2].y = mins[1]; out[2].z = mins[2]; + out[3].x = maxs[0]; out[3].y = mins[1]; out[3].z = maxs[2]; + out[4].x = maxs[0]; out[4].y = maxs[1]; out[4].z = mins[2]; + out[5].x = maxs[0]; out[5].y = maxs[1]; out[5].z = maxs[2]; + + out[6].x = mins[0]; out[6].y = maxs[1]; out[6].z = mins[2]; + out[7].x = mins[0]; out[7].y = maxs[1]; out[7].z = maxs[2]; + out[8].x = mins[0]; out[8].y = mins[1]; out[8].z = mins[2]; + out[9].x = mins[0]; out[9].y = mins[1]; out[9].z = maxs[2]; + sceGuDrawArray(line_strip ? GU_LINE_STRIP : GU_TRIANGLE_STRIP,GU_VERTEX_32BITF, 10, 0, out); +} + +void R_DrawLine(vec3_t start,vec3_t end, vec3_t rgb) +{ + + //Do Before! + sceGuDisable (GU_TEXTURE_2D); + sceGuDisable (GU_CULL_FACE); + sceGuColor(GU_COLOR(rgb[0],rgb[1],rgb[2],1)); + + // Allocate the vertices. + struct vertex + { + float x, y, z; + }; + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * 2));//10 + + out[0].x = start[0]; out[0].y = start[1]; out[0].z = start[2]; + out[1].x = end[0]; out[1].y = end[1]; out[1].z = end[2]; + + sceGuDrawArray(GU_LINE_STRIP,GU_VERTEX_32BITF, 2, 0, out); + + //Do After! + sceGuColor(GU_COLOR(1,1,1,1)); + sceGuEnable (GU_TEXTURE_2D); + sceGuEnable (GU_CULL_FACE); + +} + +//================================================================================== +int SetFlameModelState (void) +{ + if (!r_part_flames.value && !strcmp(currententity->model->name, "progs/flame0.mdl")) + { + currententity->model = cl.model_precache[cl_modelindex[mi_flame1]]; + } + else if (r_part_flames.value) + { + vec3_t liteorg; + + VectorCopy (currententity->origin, liteorg); + if (currententity->baseline.modelindex == cl_modelindex[mi_flame0]) + { + if (r_part_flames.value == 2) + { + liteorg[2] += 14; + QMB_Q3TorchFlame (liteorg, 15); + } + else + { + liteorg[2] += 5.5; + + if(r_flametype.value == 2) + QMB_FlameGt (liteorg, 7, 0.8); + else + QMB_TorchFlame(liteorg); + } + } + else if (currententity->baseline.modelindex == cl_modelindex[mi_flame1]) + { + if (r_part_flames.value == 2) + { + liteorg[2] += 14; + QMB_Q3TorchFlame (liteorg, 15); + } + else + { + liteorg[2] += 5.5; + + if(r_flametype.value > 1) + QMB_FlameGt (liteorg, 7, 0.8); + else + QMB_TorchFlame(liteorg); + + } + currententity->model = cl.model_precache[cl_modelindex[mi_flame0]]; + } + else if (currententity->baseline.modelindex == cl_modelindex[mi_flame2]) + { + if (r_part_flames.value == 2) + { + liteorg[2] += 14; + QMB_Q3TorchFlame (liteorg, 32); + } + else + { + liteorg[2] -= 1; + + if(r_flametype.value > 1) + QMB_FlameGt (liteorg, 12, 1); + else + QMB_BigTorchFlame(liteorg); + } + return -1; //continue; + } + else if (!strcmp(currententity->model->name, "progs/wyvflame.mdl")) + { + liteorg[2] -= 1; + + if(r_flametype.value > 1) + QMB_FlameGt (liteorg, 12, 1); + else + QMB_BigTorchFlame(liteorg); + + return -1; //continue; + } + } + + return 0; +} + +/* +============= +R_DrawViewModel +============= +*/ +void R_DrawViewModel (void) +{ + /* + float ambient[4], diffuse[4]; + int j; + int lnum; + vec3_t dist; + float add; + dlight_t *dl; + int ambientlight, shadelight; +*/ + // fenix@io.com: model transform interpolation + float old_i_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; + + // Tomaz - QC Alpha Scale Begin + currententity->renderamt = cl_entities[cl.viewentity].renderamt; + currententity->rendermode = cl_entities[cl.viewentity].rendermode; + currententity->rendercolor[0] = cl_entities[cl.viewentity].rendercolor[0]; + currententity->rendercolor[1] = cl_entities[cl.viewentity].rendercolor[1]; + currententity->rendercolor[2] = cl_entities[cl.viewentity].rendercolor[2]; + // Tomaz - QC Alpha Scale End + + +/* + j = R_LightPoint (currententity->origin); + + if (j < 24) + j = 24; // allways give some light on gun + ambientlight = j; + shadelight = j; + +// add dynamic lights + for (lnum=0 ; lnumradius) + continue; + if (!dl->radius) + continue; + if (dl->die < cl.time) + continue; + + VectorSubtract (currententity->origin, dl->origin, dist); + add = dl->radius - Length(dist); + if (add > 0) + ambientlight += add; + } +*/ +/* + ambient[0] = ambient[1] = ambient[2] = ambient[3] = (float)ambientlight / 128; + diffuse[0] = diffuse[1] = diffuse[2] = diffuse[3] = (float)shadelight / 128; +*/ + + // hack the depth range to prevent view model from poking into walls + sceGuDepthRange(0, 19660); + + //Blubs' tests for vmodel clipping + //sceGuDisable(GU_CLIP_PLANES); + //sceGuDisable(GU_DEPTH_TEST); + //sceGuDisable(GU_CULL_FACE); + //sceGuDisable(GU_SCISSOR_TEST); + switch (currententity->model->type) + { + case mod_alias: + // fenix@io.com: model transform interpolation + old_i_model_transform = r_i_model_transform.value; + r_i_model_transform.value = false; + if (currententity->model->aliastype == ALIASTYPE_MD2) + R_DrawMD2Model (currententity); + else + R_DrawAliasModel (currententity); + r_i_model_transform.value = old_i_model_transform; + break; + case mod_md3: + R_DrawQ3Model (currententity); + break; + case mod_halflife: + R_DrawHLModel (currententity); + break; + default: + Con_Printf("Not drawing view model of type %i\n", currententity->model->type); + break; + } + //sceGuEnable(GU_SCISSOR_TEST); + //sceGuEnable(GU_DEPTH_TEST); + //sceGuEnable(GU_CLIP_PLANES); + //sceGuEnable(GU_CULL_FACE); + sceGuDepthRange(0, 65535); + + + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuDisable(GU_BLEND); +} + +/* +============= +R_DrawView2Model +============= +*/ +void R_DrawView2Model (void) +{ + float old_i_model_transform; + + + if (!r_drawviewmodel.value) + return; + + if (chase_active.value) + return; + + if (envmap) + return; + + if (!r_drawentities.value) + return; + + if (cl.stats[STAT_HEALTH] < 0) + return; + + currententity = &cl.viewent2; + + if (!currententity->model) + return; + + // Tomaz - QC Alpha Scale Begin + currententity->renderamt = cl_entities[cl.viewentity].renderamt; + currententity->rendermode = cl_entities[cl.viewentity].rendermode; + currententity->rendercolor[0] = cl_entities[cl.viewentity].rendercolor[0]; + currententity->rendercolor[1] = cl_entities[cl.viewentity].rendercolor[1]; + currententity->rendercolor[2] = cl_entities[cl.viewentity].rendercolor[2]; + + // hack the depth range to prevent view model from poking into walls + sceGuDepthRange(0, 19660); + switch (currententity->model->type) + { + case mod_alias: + // fenix@io.com: model transform interpolation + old_i_model_transform = r_i_model_transform.value; + r_i_model_transform.value = false; + if (currententity->model->aliastype == ALIASTYPE_MD2) + R_DrawMD2Model (currententity); + else + R_DrawAliasModel (currententity); + r_i_model_transform.value = old_i_model_transform; + break; + case mod_md3: + R_DrawQ3Model (currententity); + break; + case mod_halflife: + R_DrawHLModel (currententity); + break; + default: + Con_Printf("Not drawing view model of type %i\n", currententity->model->type); + break; + } + sceGuDepthRange(0, 65535); + + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuDisable(GU_BLEND); +} + + +/* +============ +R_PolyBlend +============ +*/ +void R_PolyBlend (void) +{ + if (!r_polyblend.value) + return; + + if (!v_blend[3]) + return; + + sceGuEnable (GU_BLEND); + sceGuDisable (GU_TEXTURE_2D); + sceGuTexFunc (GU_TFX_MODULATE, GU_TCC_RGBA); + + sceGumLoadIdentity(); + + sceGuColor(GU_COLOR(v_blend[0], v_blend[1], v_blend[2], v_blend[3])); + struct vertex + { + short x, y, z; + }; + vertex* const out = static_cast(sceGuGetMemory(sizeof(vertex) * 2)); + + out[0].x = 0; + out[0].y = 0; + out[0].z = 0; + + out[1].x = 480; + out[1].y = 272; + out[1].z = 0; + + sceGuDrawArray(GU_SPRITES, GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, out); + sceGuColor(0xffffffff); + + sceGuDisable (GU_BLEND); + sceGuEnable (GU_TEXTURE_2D); + sceGuTexFunc (GU_TFX_REPLACE, GU_TCC_RGBA); +} + + +static 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"); + + //xaa - opt + int w_ratio = glwidth/vid.width; + int h_ratio = glheight/vid.height; + vrect_t* renderrect = &r_refdef.vrect; + + //setupframe + Fog_SetupFrame(); + R_AnimateLight(); + ++r_framecount; + + 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 = qfalse; + + c_brush_polys = 0; + c_alias_polys = 0; + c_md3_polys = 0; + + //setfrustum + // disabled as well + if (r_refdef.fov_x == 90) { + // front side is visible + VectorAdd(vpn, vright, frustum[0].normal); + VectorSubtract(vpn, vright, frustum[1].normal); + + VectorAdd(vpn, vright, frustum[1].normal); + VectorSubtract(vpn, vup, frustum[3].normal); + } else { + RotatePointAroundVector( frustum[0].normal, vup, vpn, -( vecx_point_transform ) ); + RotatePointAroundVector( frustum[1].normal, vup, vpn, ( vecx_point_transform ) ); + RotatePointAroundVector( frustum[2].normal, vright, vpn, ( vecy_point_transform ) ); + RotatePointAroundVector( frustum[3].normal, vright, vpn, -( vecy_point_transform ) ); + } + + for (i=0 ; i<4 ; i++) { + frustum[i].type = PLANE_ANYZ; + frustum[i].dist = DotProduct (r_origin, frustum[i].normal); + frustum[i].signbits = SignbitsForPlane (&frustum[i]); + } + +//setupgl + // set up viewpoint + sceGumMatrixMode(GU_PROJECTION); + sceGumLoadIdentity(); + + x = renderrect->x * w_ratio; + x2 = (renderrect->x + renderrect->width) * w_ratio; + y = (vid.height-renderrect->y) * h_ratio; + y2 = (vid.height - (renderrect->y + renderrect->height)) * h_ratio; + + // fudge around because of frac screen scale + if (x > 0) x--; + if (x2 < glwidth) x2++; + if (y2 < 0) y2--; + if (y < glheight) y++; + + w = x2 - x; + h = y - y2; + + if (envmap) + { + x = y2 = 0; + w = h = 256; + } + + sceGuViewport( + glx, + gly + (glheight >> 1) - y2 - (h >> 1), //xaa - try to skip some divides (/2) here + w, + h); + sceGuScissor(x, glheight - y2 - h, x + w, glheight - y2); + + screenaspect = (float)renderrect->width/renderrect->height; + + //johnfitz -- warp view for underwater + fovx = screenaspect; + fovy = r_refdef.fov_y; + if (r_waterwarp.value) + { + contents = Mod_PointInLeaf (r_origin, cl.worldmodel)->contents; + if (contents == CONTENTS_WATER || + contents == CONTENTS_SLIME || + contents == CONTENTS_LAVA) + { + //variance should be a percentage of width, where width = 2 * tan(fov / 2) + //otherwise the effect is too dramatic at high FOV and too subtle at low FOV + //what a mess! + //fovx = atan(tan(DEG2RAD(r_refdef.fov_x) / 2) * (0.97 + sin(cl.time * 1) * 0.04)) * 2 / M_PI_DIV_180; + #ifdef PSP_VFPU + fovy = vfpu_atanf(vfpu_tanf(DEG2RAD(r_refdef.fov_y) / 2) * (1.03 - vfpu_sinf(cl.time * 2) * 0.04)) * 2 / M_PI_DIV_180; + #else + fovy = atan(tan(DEG2RAD(r_refdef.fov_y) / 2) * (1.03 - sin(cl.time * 2) * 0.04)) * 2 / M_PI_DIV_180; + #endif + } + } + + sceGumPerspective(fovy, fovx, 4, r_maxrange.value); + + if (mirror) + { + if (mirror_plane->normal[2]) + { + const ScePspFVector3 scaling = {1, -1, 1}; + sceGumScale(&scaling); + } + else + { + const ScePspFVector3 scaling = {-1, 1, 1}; + sceGumScale(&scaling); + } + } + + sceGumUpdateMatrix(); + sceGumMatrixMode(GU_VIEW); + sceGumLoadIdentity(); + + sceGumRotateX(-90 * piconst); + sceGumRotateZ(90 * piconst); + sceGumRotateX(-r_refdef.viewangles[2] * piconst); + sceGumRotateY(-r_refdef.viewangles[0] * piconst); + sceGumRotateZ(-r_refdef.viewangles[1] * piconst); + + /*glTranslatef (-r_refdef.vieworg[0], -r_refdef.vieworg[1], -r_refdef.vieworg[2]);*/ + const ScePspFVector3 translation = { + -r_refdef.vieworg[0], + -r_refdef.vieworg[1], + -r_refdef.vieworg[2] + }; + sceGumTranslate(&translation); + + /*glGetFloatv (GL_MODELVIEW_MATRIX, r_world_matrix);*/ + sceGumStoreMatrix(&r_world_matrix); + sceGumUpdateMatrix(); + + sceGumMatrixMode(GU_MODEL); + + clipping::begin_frame(); + + // set drawing parms + sceGuDisable(GU_BLEND); + sceGuDisable(GU_ALPHA_TEST); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + + R_MarkLeaves (); // done here so we know if we're in water + Fog_EnableGFog (); //johnfitz + R_DrawWorld (); // adds static entities to the list + S_ExtraUpdate (); // don't let sound get messed up if going slow + + // drawentitiesonlist + if (r_drawentities.value) { + + // draw sprites seperately, because of alpha blending + for (i=0 ; iangles[0] *= 0.3; + + //currentmodel = currententity->model; + if(!(currententity->model)) + { + R_DrawNullModel(); + continue; + } + + specChar = currententity->model->name[strlen(currententity->model->name)-5]; + + switch (currententity->model->type) { + case mod_alias: + if (qmb_initialized && SetFlameModelState() == -1) continue; + + if (currententity->model->aliastype == ALIASTYPE_MD2) + R_DrawMD2Model(currententity); + else { + if (specChar == '$') + R_DrawTransparentAliasModel(currententity); + else + R_DrawAliasModel(currententity); + } + + break; + case mod_md3: + R_DrawQ3Model (currententity); + break; + + case mod_halflife: + R_DrawHLModel (currententity); + break; + + case mod_brush: + R_DrawBrushModel (currententity); + break; + case mod_sprite: + R_DrawSpriteModel(currententity); + break; + default: + break; + } + } + } + + R_RenderDlights (); //pointless jump? -xaa + + R_DrawDecals(); + R_DrawParticles (); + Fog_DisableGFog (); //johnfitz +} + + +/* +============= +R_Clear +============= +*/ +void R_Clear (void) +{ + if(r_refdef.fog_end > 0 && r_skyfog.value) + { + sceGuClear(GU_COLOR_BUFFER_BIT | GU_DEPTH_BUFFER_BIT); + sceGuClearColor(GU_COLOR(r_refdef.fog_red * 0.01f,r_refdef.fog_green * 0.01f,r_refdef.fog_blue * 0.01f,1.0f)); + } + else sceGuClear(GU_DEPTH_BUFFER_BIT); + + if(r_shadows.value) + { + sceGuClearStencil(GU_TRUE); + sceGuClear(GU_STENCIL_BUFFER_BIT); + } +} + +/* +============= +R_Mirror +============= +*/ +void R_Mirror (void) +{ + float d; + msurface_t *s; + entity_t *ent; + + if (!mirror) + return; + + r_base_world_matrix = r_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] = -asinf(vpn[2])/M_PI*180; + r_refdef.viewangles[1] = atan2f(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++; + } + + R_RenderScene (); + R_DrawWaterSurfaces (); + + // blend on top + sceGuEnable (GU_BLEND); + + sceGumMatrixMode(GU_PROJECTION); + sceGumLoadIdentity(); + + if (mirror_plane->normal[2]) + { + const ScePspFVector3 scaling = {1, -1, 1}; + sceGumScale(&scaling); + } + else + { + const ScePspFVector3 scaling = {-1, 1, 1}; + sceGumScale(&scaling); + } + + //sceGuFrontFace(GU_CW); + + sceGumMatrixMode(GU_VIEW); + sceGumLoadIdentity(); + + sceGumMatrixMode(GU_MODEL); + + sceGumStoreMatrix(&r_base_world_matrix); + sceGumUpdateMatrix(); + + sceGuColor(GU_COLOR(1,1,1,r_mirroralpha.value)); + + s = cl.worldmodel->textures[mirrortexturenum]->texturechain; + for ( ; s ; s=s->texturechain) + R_RenderBrushPoly (s); + cl.worldmodel->textures[mirrortexturenum]->texturechain = NULL; + + sceGuDisable (GU_BLEND); + sceGuColor (0xffffffff); +} + +/* +================ +R_RenderView + +r_refdef must be set before the first call +================ +*/ +void R_RenderView (void) +{ + c_brush_polys = 0; + c_alias_polys = 0; + c_md3_polys = 0; + + mirror = qfalse; + + R_Clear (); + + // render normal view + R_RenderScene (); + + R_DrawViewModel (); + R_DrawView2Model (); + R_DrawWaterSurfaces (); + + // render mirror view + R_Mirror (); + R_PolyBlend (); + + //Crow_bar fixed + if (r_speeds.value) + { + Con_Printf ("%4i world poly\n", c_brush_polys); + Con_Printf ("%4i entity poly\n", c_alias_polys); + Con_Printf ("%4i CPU Percentage\n", (int)PFL_GetCPUPercentage()/(1000/game_fps)); + Con_Printf ("%4i GPU Percentage\n", (int)PFL_GetGPUPercentage()/(1000/game_fps)); + Con_Printf ("%4i CPU Time (ms)\n", (int)PFL_GetCPUTime()); + Con_Printf ("%4i GPU Time (ms)\n", (int)PFL_GetGPUTime()); + } +} diff --git a/source/psp/video_hardware_mesh.cpp b/source/psp/video_hardware_mesh.cpp new file mode 100644 index 0000000..4e444f2 --- /dev/null +++ b/source/psp/video_hardware_mesh.cpp @@ -0,0 +1,542 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 + +extern "C" +{ +#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; +} + + +/* +================ +BuildTrisSingleTriGroup +by blubswillrule +because though I do appreciate the ram saving abilities of generating tristrips and fans, I do not enjoy the increased amount of gpu rendering calls due to having to pass each tristrip or trifan inividually +we're going to instead build one continuous array of triangles such that we can pass the entire model in one single draw call. +After testing: +rendering fps gains were negative (due to having more vertices), so let's not use this after all, hehe... +================ +*/ +/* +static void BuildTris (void) +{ + int j, k; + int startv; + float s, t; + int len; + int bestverts[3072];//1024 + int besttris[1024]; + + // + // build tristrips + // + + numorder = 0; + numcommands = 0; + memset (used, 0, sizeof(used)); + + startv = 0; + + mtriangle_t *check; + check = &triangles[0]; + for(j = 0; j < pheader -> numtris; j++,check++) + { + besttris[j] = j; + for(k = 0; k < 3; k++) + { + //stripverts[stripcount+2] = check->vertindex[ (k+2)%3 ]; + bestverts[(j * 3) + k] = check->vertindex[k]; + } + } + + len = pheader->numtris * 3; + commands[numcommands++] = len; + for (j=0 ; j< len; j++) + { + k = bestverts[j]; + vertexorder[numorder++] = k; + // emit s/t coords into the commands stream + + s = stverts[k].s; + t = stverts[k].t; + //if (!triangles[besttris[0]].facesfront && stverts[k].onseam) + // s += pheader->skinwidth / 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 + allverts += len; + alltris += pheader->numtris; +} +*/ + + +/* +================ +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 + // + numorder = 0; + numcommands = 0; + besttype = 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; +} + +#if MS2WRITING +/* +================ +GL_MakeAliasModelDisplayLists +================ +*/ +void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr) +{ + aliasmodel = m; + paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m); + + // + // look for a cached version + // + char cache[MAX_QPATH]; + sprintf(cache, "%s/glquake/", com_gamedir); + COM_StripExtension (m->name+strlen("progs/"), cache+strlen(cache)); + strcat (cache, ".ms2"); + + int f; + Sys_FileOpenRead(cache, &f); + if (f >= 0) + { + Sys_FileRead(f, &numcommands, 4); + Sys_FileRead(f, &numorder, 4); + Sys_FileRead(f, &commands, numcommands * sizeof(commands[0])); + Sys_FileRead(f, &vertexorder, numorder * sizeof(vertexorder[0])); + Sys_FileClose(f); + } + else + { + char dirName[MAX_OSPATH]; + + // + // build it from scratch + // + BuildTris (); // trifans or lists + + // Create a dir to put the cache file in. + memset(dirName, 0, MAX_OSPATH); + sprintf(dirName, "%s/glquake", com_gamedir); + Sys_mkdir(dirName); + + // + // save out the cached version + // + f = Sys_FileOpenWrite(cache); + if (f >= 0) + { + Sys_FileWrite(f, &numcommands, 4); + Sys_FileWrite(f, &numorder, 4); + Sys_FileWrite(f, &commands, numcommands * sizeof(commands[0])); + Sys_FileWrite(f, &vertexorder, numorder * sizeof(vertexorder[0])); + Sys_FileClose(f); + } + else + { + Sys_Error("Couldn't open %s for writing", cache); + } + } + + + // save the data out + + paliashdr->poseverts = numorder; + + int* cmds = static_cast(Hunk_Alloc (numcommands * 4)); + paliashdr->commands = (byte *)cmds - (byte *)paliashdr; + memcpy (cmds, commands, numcommands * 4); + + trivertx_t* verts = static_cast(Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts + * sizeof(trivertx_t) )); + paliashdr->posedata = (byte *)verts - (byte *)paliashdr; + for (int i=0 ; inumposes ; i++) + for (int j=0 ; jscale[0] + paliashdr->scale_origin[0]; + scaled[1] = (float)original[1] * paliashdr->scale[1] + paliashdr->scale_origin[1]; + scaled[2] = (float)original[2] * paliashdr->scale[2] + paliashdr->scale_origin[2]; +} + +/* +================ +GL_MakeAliasModelDisplayLists +================ +*/ +void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr) +{ + int i, j; + int *cmds; + trivertx_t *verts; + + aliasmodel = m; + paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m); + + + // Tonik: don't cache anything, because it seems just as fast + // (if not faster) to rebuild the tris instead of loading them from disk + + BuildTris(); // trifans or lists + + // save the data out + + paliashdr->poseverts = numorder; + + cmds = static_cast(Hunk_Alloc (numcommands * 4)); + paliashdr->commands = (byte *)cmds - (byte *)paliashdr; + memcpy (cmds, commands, numcommands * 4); + + verts = static_cast(Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts + * sizeof(trivertx_t))); + paliashdr->posedata = (byte *)verts - (byte *)paliashdr; + for (i=0 ; inumposes ; i++) + for (j=0 ; jmodhint == MOD_WEAPON && qmb_initialized && r_part_muzzleflash.value) + { + vec3_t scaledf0, scaledf1; // scaled versions of the verts (need to prescale for comparison) + float vdiff; // difference in front to back movement + qboolean *nodraw; // true if the vert is a muzzleflash + + // get pointers to the verts + trivertx_t *vertsf0 = (trivertx_t *)((byte *)hdr + hdr->posedata); + trivertx_t *vertsfi = (trivertx_t *)((byte *)hdr + hdr->posedata); + + // set up the nodraw buffer + nodraw = static_cast(malloc (numorder * sizeof(qboolean))); + + // setthese pointers to the 0th and 1st frames + vertsf0 += hdr->frames[0].firstpose * hdr->poseverts; + vertsfi += hdr->frames[1].firstpose * hdr->poseverts; + + // now go through them and compare. we expect that (a) the animation is sensible and there's no major + // difference between the 2 frames to be expected, and (b) any verts that do exhibit a major difference + // can be assumed to belong to the muzzleflash + for (j = 0; j < numorder; j++) + { + ScaleVerts (vertsf0->v, scaledf0); + ScaleVerts (vertsfi->v, scaledf1); + + // get difference in front to back movement + vdiff = scaledf1[0] - scaledf0[0]; + + // if it's above a certain treshold, assume a muzzleflash and mark for nodraw + // 10 is the approx lowest range of visible front to back in a view model, so that seems reasonable to work with + if (vdiff > 10) + nodraw[j] = qtrue; + else nodraw[j] = qfalse; + + // next set of verts + vertsf0++; + vertsfi++; + } + + // copy the relevant verts from the first frame to every other frame + for (i = 1; i < paliashdr->numframes; i++) + { + // get pointers to the verts again + vertsf0 = (trivertx_t *)((byte *) hdr + hdr->posedata); + vertsfi = (trivertx_t *)((byte *) hdr + hdr->posedata); + + // set these pointers to the 0th and i'th frames + vertsf0 += hdr->frames[0].firstpose * hdr->poseverts; + vertsfi += hdr->frames[i].firstpose * hdr->poseverts; + + for (j = 0; j < numorder; j++) + { + // copy the verts from frame 0 + if (nodraw[j]) + { + vertsfi->v[0] = vertsf0->v[0]; + vertsfi->v[1] = vertsf0->v[1]; + vertsfi->v[2] = vertsf0->v[2]; + } + + // next set of verts + vertsf0++; + vertsfi++; + } + } + + // release the nodraw buffer + free (nodraw); + }*/ +} +#endif diff --git a/source/psp/video_hardware_mhex2.cpp b/source/psp/video_hardware_mhex2.cpp new file mode 100644 index 0000000..a3b3b14 --- /dev/null +++ b/source/psp/video_hardware_mhex2.cpp @@ -0,0 +1,432 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. +Copyright (C) 2008 Crow_bar PSPHEXENII. + +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 + +extern "C" +{ +#include "../quakedef.h" +} + +/* +================================================================= + +ALIAS MODEL DISPLAY LIST GENERATION + +================================================================= +*/ + +static model_t *aliasmodel; +static aliashdr_t *paliashdr; + +static int used[8192]; + +// the command list holds counts and s/t values that are valid for +// every frame +static int commands[8192]; +static int numcommands; + +// all frames will have their vertexes rearranged and expanded +// so they are in the order expected by the command list +static int vertexorder[8192]; +static int numorder; + +static int allverts, alltris; + +static int stripverts[128]; +static int striptris[128]; +static int stripstverts[128]; +static int stripcount; + +/* +================ +StripLength +================ +*/ +int StripLengthH2 (int starttri, int startv) +{ + int m1, m2; + int st1, st2; + int j; + mh2triangle_t *last, *check; + int k; + + used[starttri] = 2; + + last = &h2triangles[starttri]; + + stripverts[0] = last->vertindex[(startv)%3]; + stripstverts[0] = last->stindex[(startv)%3]; + + stripverts[1] = last->vertindex[(startv+1)%3]; + stripstverts[1] = last->stindex[(startv+1)%3]; + + stripverts[2] = last->vertindex[(startv+2)%3]; + stripstverts[2] = last->stindex[(startv+2)%3]; + + striptris[0] = starttri; + stripcount = 1; + + m1 = last->vertindex[(startv+2)%3]; + st1 = last->stindex[(startv+2)%3]; + m2 = last->vertindex[(startv+1)%3]; + st2 = last->stindex[(startv+1)%3]; + + // look for a matching triangle +nexttri: + for (j=starttri+1, check=&h2triangles[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->stindex[k] != st1) + continue; + if (check->vertindex[ (k+1)%3 ] != m2) + continue; + if (check->stindex[ (k+1)%3 ] != st2) + 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 ]; + st2 = check->stindex[ (k+2)%3 ]; + } + else + { + m1 = check->vertindex[ (k+2)%3 ]; + st1 = check->stindex[ (k+2)%3 ]; + } + + stripverts[stripcount+2] = check->vertindex[ (k+2)%3 ]; + stripstverts[stripcount+2] = check->stindex[ (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 +=========== +*/ +int FanLengthH2 (int starttri, int startv) +{ + int m1, m2; + int st1, st2; + int j; + mh2triangle_t *last, *check; + int k; + + used[starttri] = 2; + + last = &h2triangles[starttri]; + + stripverts[0] = last->vertindex[(startv)%3]; + stripstverts[0] = last->stindex[(startv)%3]; + + stripverts[1] = last->vertindex[(startv+1)%3]; + stripstverts[1] = last->stindex[(startv+1)%3]; + + stripverts[2] = last->vertindex[(startv+2)%3]; + stripstverts[2] = last->stindex[(startv+2)%3]; + + striptris[0] = starttri; + stripcount = 1; + + m1 = last->vertindex[(startv+0)%3]; + st1 = last->stindex[(startv+2)%3]; + m2 = last->vertindex[(startv+2)%3]; + st2 = last->stindex[(startv+1)%3]; + + + // look for a matching triangle +nexttri: + for (j=starttri+1, check=&h2triangles[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->stindex[k] != st1) + continue; + if (check->vertindex[ (k+1)%3 ] != m2) + continue; + if (check->stindex[ (k+1)%3 ] != st2) + 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 ]; + st2 = check->stindex[ (k+2)%3 ]; + + stripverts[stripcount+2] = m2; + stripstverts[stripcount+2] = st2; + 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 +================ +*/ +void BuildTrisH2 (void) +{ + int i, j, k; + int startv; + float s, t; + int len, bestlen, besttype; + int bestverts[1024]; + int besttris[1024]; + int beststverts[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 = StripLengthH2 (i, startv); + else + len = FanLengthH2 (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_MakeAliasModelDisplayListsH2 +================ +*/ +#ifndef MS2WRITING +void GL_MakeAliasModelDisplayListsH2 (model_t *m, aliashdr_t *hdr) +{ + aliasmodel = m; + paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m); + + BuildTrisH2 (); // trifans or lists + + // save the data out + + paliashdr->poseverts = numorder; + + int* cmds = static_cast(Hunk_Alloc (numcommands * 4)); + paliashdr->commands = (byte *)cmds - (byte *)paliashdr; + memcpy (cmds, commands, numcommands * 4); + + trivertx_t* verts = static_cast(Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts + * sizeof(trivertx_t) )); + paliashdr->posedata = (byte *)verts - (byte *)paliashdr; + for (int i=0 ; inumposes ; i++) + for (int j=0 ; jname+strlen("models/"), cache+strlen(cache)); + strcat (cache, ".ms2"); + + COM_FOpenFile (cache, &f, qtrue); + if (f) + { + fread (&numcommands, 4, 1, f); + fread (&numorder, 4, 1, f); + fread (&commands, numcommands * sizeof(commands[0]), 1, f); + fread (&vertexorder, numorder * sizeof(vertexorder[0]), 1, f); + fclose (f); + } + else + { + char dirName[MAX_OSPATH]; + // + // build it from scratch + // + Con_Printf ("meshing %s...\n",m->name); + + BuildTrisH2 (); // trifans or lists + + // Create a dir to put the cache file in. + memset(dirName, 0, MAX_OSPATH); + //sprintf(dirName, "%s/glhexen/", com_gamedir); + Sys_mkdir(dirName); + + // + // save out the cached version + // + sprintf (fullpath, "%s/%s", com_gamedir, cache); + f = fopen (fullpath, "wb"); + if (f) + { + fwrite (&numcommands, 4, 1, f); + fwrite (&numorder, 4, 1, f); + fwrite (&commands, numcommands * sizeof(commands[0]), 1, f); + fwrite (&vertexorder, numorder * sizeof(vertexorder[0]), 1, f); + fclose (f); + } + } + + + // save the data out + + paliashdr->poseverts = numorder; + + cmds = static_cast(Hunk_Alloc (numcommands * 4)); + paliashdr->commands = (byte *)cmds - (byte *)paliashdr; + memcpy (cmds, commands, numcommands * 4); + + verts = static_cast(Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts + * sizeof(trivertx_t) )); + paliashdr->posedata = (byte *)verts - (byte *)paliashdr; + for (i=0 ; inumposes ; i++) + for (j=0 ; j +#include + +extern "C" +{ +#include "../quakedef.h" +void CL_CopyPlayerInfo (entity_t *ent, entity_t *player); +} + +void GL_InitTextureUsage (); + +qboolean r_loadq3player = qfalse; + +int decal_blood1, decal_blood2, decal_blood3, decal_q3blood, decal_burn, decal_mark, decal_glow; + +/* +================== +R_InitOtherTextures +================== +*/ +void R_InitOtherTextures (void) +{ + //static decals + decal_blood1 = loadtextureimage ("textures/decals/blood_splat01", 0, 0, qfalse, GU_LINEAR); + decal_blood2 = loadtextureimage ("textures/decals/blood_splat02", 0, 0, qfalse, GU_LINEAR); + decal_blood3 = loadtextureimage ("textures/decals/blood_splat03", 0, 0, qfalse, GU_LINEAR); + decal_q3blood = loadtextureimage ("textures/decals/blood_stain", 0, 0, qfalse, GU_LINEAR); + decal_burn = loadtextureimage ("textures/decals/explo_burn01", 0, 0, qfalse, GU_LINEAR); + decal_mark = loadtextureimage ("textures/decals/particle_burn01", 0, 0, qfalse, GU_LINEAR); + decal_glow = loadtextureimage ("textures/decals/glow2", 0, 0, qfalse, GU_LINEAR); +} + +/* +================== +R_InitTextures +================== +*/ +void R_InitTextures (void) +{ + int x,y, m; + byte *dest; + + GL_InitTextureUsage (); + +// create a simple checkerboard texture for the default + r_notexture_mip = static_cast(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}, +}; + +byte dottexture2[16][16] = +{ + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + {0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0}, + {0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0}, + {0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0}, + {0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0}, + {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0}, + {0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0}, + {0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0}, + {0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0}, + {0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0}, + {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0}, + {0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0}, + {0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0}, + {0,0,0,1,1,1,0,0,0,0,1,1,1,0,0,0}, + {0,0,0,0,0,1,0,0,0,0,1,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[16][16][2]; + + // + // particle texture + // + + for (x=0 ; x<16 ; x++) + { + for (y=0 ; y<16 ; y++) + { + data[y][x][0] = (dottexture2[x][y])*0xF0 | (dottexture2[x][y])*0x0F; + data[y][x][1] = (dottexture2[x][y])*0xF0 | (dottexture2[x][y])*0x0F; + } + } + //particletexture = GL_LoadImages ("_particle_", 16, 16, &data[0][0][0], qtrue, GU_LINEAR, 0, 2); +} + +//extern qboolean config_lock; + +void LoadMapConfig (void) +{ +/* + char *mapname = ""; + + if (DF_FindFile("config.cfg")) + { + Cbuf_AddText ("exec config.cfg\n"); + Cbuf_Execute (); + } + + if (DF_FindFile("autoexec.cfg")) + { + Cbuf_AddText ("exec autoexec.cfg\n"); + Cbuf_Execute (); + } + + config_lock = qfalse; + + //mapname = cl_map.string; + Con_Printf("loading cfg for %s\n", mapname); + + if (DF_FindFile(va("%s.cfg",mapname))) + { + //config_lock = true; + Cbuf_AddText (va("exec %s.cfg\n", mapname)); + Cbuf_Execute (); + } +*/ +} + +// joe: added from FuhQuake +void R_PreMapLoad (char *mapname) +{ + Cvar_Set ("map", mapname); +} + +/* +=============== +R_Envmap_f + +Grab six views for environment mapping tests +=============== +*/ +#define ENVMAP_SIZE 256*256*4 +void R_Envmap_f (void) +{ + byte *buffer; + + buffer = static_cast(malloc(ENVMAP_SIZE)); + if(!buffer) + { + Con_Printf("ENV MAP FAILED, buffer not created\n"); + return; + } + + envmap = qtrue; + + r_refdef.vrect.x = 0; + r_refdef.vrect.y = 0; + r_refdef.vrect.width = 256; + r_refdef.vrect.height = 256; + + r_refdef.viewangles[0] = 0; + r_refdef.viewangles[1] = 0; + r_refdef.viewangles[2] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + GL_GetPixelsRGBA(buffer, 256, 256, 0); + COM_WriteFile ("env0.rgb", buffer, ENVMAP_SIZE); + + r_refdef.viewangles[1] = 90; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + GL_GetPixelsRGBA(buffer, 256, 256, 0); + COM_WriteFile ("env1.rgb", buffer, ENVMAP_SIZE); + + r_refdef.viewangles[1] = 180; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + GL_GetPixelsRGBA(buffer, 256, 256, 0); + COM_WriteFile ("env2.rgb", buffer, ENVMAP_SIZE); + + r_refdef.viewangles[1] = 270; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + GL_GetPixelsRGBA(buffer, 256, 256, 0); + COM_WriteFile ("env3.rgb", buffer, ENVMAP_SIZE); + + r_refdef.viewangles[0] = -90; + r_refdef.viewangles[1] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + GL_GetPixelsRGBA(buffer, 256, 256, 0); + COM_WriteFile ("env4.rgb", buffer, ENVMAP_SIZE); + + r_refdef.viewangles[0] = 90; + r_refdef.viewangles[1] = 0; + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + R_RenderView (); + GL_GetPixelsRGBA(buffer, 256, 256, 0); + COM_WriteFile ("env5.rgb", buffer, ENVMAP_SIZE); + + envmap = qfalse; + + GL_EndRendering (); + + if(buffer) + { + free(buffer); + buffer = NULL; + } +} + +/* +=============== +R_Init +=============== +*/ +void R_InitDecals (void); +void R_ToggleDecals_f (void); +void R_ToggleParticles_f (void); + +void R_Init (void) +{ + Cmd_AddCommand ("timerefresh", R_TimeRefresh_f); + Cmd_AddCommand ("envmap", R_Envmap_f); + Cmd_AddCommand ("pointfile", R_ReadPointFile_f); + Cmd_AddCommand ("toggleparticles", R_ToggleParticles_f); + Cmd_AddCommand ("toggledecals", R_ToggleDecals_f); + + Cvar_RegisterVariable (&r_hlbsponly); + Cvar_RegisterVariable (&r_partalpha); + Cvar_RegisterVariable (&r_restexf); + Cvar_RegisterVariable (&r_texcompr); + Cvar_RegisterVariable (&r_skyfog); + Cvar_RegisterVariable (&r_skydis); + Cvar_RegisterVariable (&r_caustics); + Cvar_RegisterVariable (&r_detail); + Cvar_RegisterVariable (&r_detail_mipmaps); + Cvar_RegisterVariable (&r_detail_mipmaps_func); + Cvar_RegisterVariable (&r_detail_mipmaps_bias); + + Cvar_RegisterVariable (&r_waterripple); + 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); + Cvar_RegisterVariable (&r_tex_scale_down); + Cvar_RegisterVariable (&r_particles_simple); + Cvar_RegisterVariable (&r_vsync); + Cvar_RegisterVariable (&r_mipmaps); + Cvar_RegisterVariable (&r_mipmaps_func); + Cvar_RegisterVariable (&r_mipmaps_bias); + Cvar_RegisterVariable (&r_retro); // dr_mabuse1981: "retro filter". + Cvar_RegisterVariable (&r_maxrange); + Cvar_RegisterVariable (&r_i_model_animation); + Cvar_RegisterVariable (&r_i_model_transform); + + Cvar_RegisterVariable (&r_fastsky); + Cvar_RegisterVariable (&r_skycolor); + + Cvar_RegisterVariable (&r_loadq3models); + Cvar_RegisterVariable (&r_farclip); + + Cvar_RegisterVariable (&r_asynch); + Cvar_RegisterVariable (&r_ipolations); + Cvar_RegisterVariable (&gl_keeptjunctions); + Cvar_RegisterVariable (&r_waterwarp); + + Cvar_RegisterVariable (&r_showbboxes); + Cvar_RegisterVariable (&r_showbboxes_full); + + Cvar_RegisterVariable (&r_polyblend); + + Cvar_RegisterVariable (&r_showtris); + Cvar_RegisterVariable (&r_showtris_full); + + Cvar_RegisterVariable (&cl_loadmapcfg); + Cvar_RegisterVariable (&r_laserpoint); + Cvar_RegisterVariable (&r_part_explosions); + Cvar_RegisterVariable (&r_part_trails); + Cvar_RegisterVariable (&r_part_sparks); + Cvar_RegisterVariable (&r_part_spikes); + Cvar_RegisterVariable (&r_part_gunshots); + Cvar_RegisterVariable (&r_part_blood); + Cvar_RegisterVariable (&r_part_telesplash); + Cvar_RegisterVariable (&r_part_blobs); + Cvar_RegisterVariable (&r_part_lavasplash); + Cvar_RegisterVariable (&r_part_flames); + Cvar_RegisterVariable (&r_part_lightning); + Cvar_RegisterVariable (&r_part_flies); + Cvar_RegisterVariable (&r_explosiontype); + Cvar_RegisterVariable (&r_part_muzzleflash); + + //Shpuld + Cvar_RegisterVariable (&r_model_brightness); + + R_InitParticles (); + R_InitParticleTexture (); + R_InitOtherTextures (); + R_InitDecals (); + Sky_Init (); //johnfitz + Fog_Init (); //johnfitz + + /* + playertextures = texture_extension_number; + texture_extension_number += 16; + */ +} + +void R_ClearDecals(void); +/* +=============== +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 (); + R_ClearDecals(); + + GL_BuildLightmaps (); + + Sky_NewMap (); //johnfitz -- skybox in worldspawn + Fog_NewMap (); //johnfitz -- global fog in worldspawn + + // identify sky texture + skytexturenum = -1; + mirrortexturenum = -1; + for (i=0 ; inumtextures ; i++) + { + if (!cl.worldmodel->textures[i]) + continue; + if (!Q_strncmp(cl.worldmodel->textures[i]->name,"sky",3) ) + skytexturenum = i; + if (!Q_strncmp(cl.worldmodel->textures[i]->name,"window02_1",10) )//reminder + //if (!Q_strncmp(cl.worldmodel->textures[i]->name,"3tiles_grey_64",10) )//reminder "window02_1" + mirrortexturenum = i; + cl.worldmodel->textures[i]->texturechain = NULL; + } + + if (cl_loadmapcfg.value) + { + LoadMapConfig(); + } + + // HACK HACK HACK - create two extra entities if drawing the player's multimodel + if (r_loadq3player) + { + memset (&q3player_body, 0, sizeof(tagentity_t)); + CL_CopyPlayerInfo (&q3player_body.ent, &cl_entities[cl.viewentity]); + memset (&q3player_head, 0, sizeof(tagentity_t)); + CL_CopyPlayerInfo (&q3player_head.ent, &cl_entities[cl.viewentity]); + } +} + + +/* +==================== +R_TimeRefresh_f + +For program optimization +==================== +*/ +void R_TimeRefresh_f (void) +{ + int i; + double start, stop, time; + + + start = Sys_FloatTime (); + for (i=0 ; i<128 ; i++) + { + r_refdef.viewangles[1] = i/128.0*360.0; + R_RenderView (); + } + + stop = Sys_FloatTime (); + time = stop-start; + if (time > 0) + Con_Printf ("%f seconds (%f fps)\n", time, 128/time); + + GL_EndRendering (); +} + +void D_FlushCaches (void) +{ +} + + diff --git a/source/psp/video_hardware_model.cpp b/source/psp/video_hardware_model.cpp new file mode 100644 index 0000000..175cc7a --- /dev/null +++ b/source/psp/video_hardware_model.cpp @@ -0,0 +1,3769 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. +Copyright (C) 2008-2009 Crow_bar. + +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. + +*/ +// models.c -- model loading and caching + +// models are the only shared resource between a client and server running +// on the same machine. + +extern "C" +{ +#include "../quakedef.h" +} +#include +#include + + +#include "video_hardware_fullbright.h" +#include "video_hardware_hlmdl.h" + +#include +using namespace std; + +list mapTextureNameList; + +int LIGHTMAP_BYTES; + +model_t *loadmodel; +char loadname[32]; // for hunk tags + +//void UnloadWads (void); //wad unload by Crow_bar +void VID_InitPaleteH2(unsigned char* palette); + +void Mod_LoadSpriteModel (model_t *mod, void *buffer); +void Mod_LoadBrushModel (model_t *mod, void *buffer); +void Mod_LoadAliasModel (model_t *mod, void *buffer); +void Mod_LoadH2AliasModel (model_t *mod, void *buffer); +void Mod_LoadQ2AliasModel (model_t *mod, void *buffer); +qboolean Mod_LoadQ2SpriteModel (model_t *mod, void *buffer); +//void Mod_LoadQ2BrushModel (model_t *mod, void *buffer); +void Mod_LoadQ3AliasModel (model_t *mod, void *buffer); +model_t *Mod_LoadModel (model_t *mod, qboolean crash); + +byte mod_novis[MAX_MAP_LEAFS/8]; + +#define MAX_MOD_KNOWN 512 +model_t mod_known[MAX_MOD_KNOWN]; +int mod_numknown; + +//model_t mod_inline[MAX_MOD_KNOWN]; + +cvar_t gl_subdivide_size = {"gl_subdivide_size", "128", qtrue}; + +extern int solidskytexture; +extern int alphaskytexture; + +/* +=============== +Mod_Init +=============== +*/ +void Mod_Init (void) +{ + Cvar_RegisterVariable (&gl_subdivide_size); + memset (mod_novis, 0xff, sizeof(mod_novis)); +} + +/* +=============== +Mod_Init + +Caches the data if needed +=============== +*/ +void *Mod_Extradata (model_t *mod) +{ + void *r; + + r = Cache_Check (&mod->cache); + if (r) + return r; + + Mod_LoadModel (mod, qtrue); + + 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 0 + memcpy (out, in, row); +#else + 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); +#endif + + 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,texture_index; + model_t *mod; + + for (i=0 , mod=mod_known ; itype != mod_alias && mod->type != mod_md3 && mod->type != mod_halflife) + { + mod->needload = qtrue; + } + + //Models & Sprite Unloading code By Crow_bar + if (mod->type == mod_alias || mod->type == mod_md3 || mod->type == mod_halflife) + { + if (Cache_Check (&mod->cache)) + Cache_Free (&mod->cache); + } + else if (mod->type == mod_sprite) + { + mod->cache.data = NULL; + } + + } + + UnloadWads(); //Crow_bar + + ent_file = NULL; //~~~~ + + // maybe we should check if it is new map or not + // (so we dont unload textures unnecessary) + while (mapTextureNameList.size() > 0) + { + texture_index = mapTextureNameList.front(); + mapTextureNameList.pop_front(); + GL_UnloadTexture(texture_index); + } + solidskytexture = -1; + alphaskytexture = -1; + + //purge old sky textures + for (i=0; i<6; i++) + { + if (skyimage[i] && skyimage[i] != solidskytexture) + GL_UnloadTexture(skyimage[i]); + skyimage[i] = NULL; + } + + //purge old lightmaps + for (i=0; iname, name) ) + break; + + return mod; + } +*/ +// +// search the currently loaded models +// + 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 = qtrue; + mod_numknown++; + } + + return mod; +} + +/* +================== +Mod_TouchModel + +================== +*/ +void Mod_TouchModel (char *name) +{ + model_t *mod; + + mod = Mod_FindName (name); + + if (!mod->needload && (mod->type == mod_alias || mod->type == mod_md3 || mod->type == mod_halflife)) + Cache_Check (&mod->cache); + +} + +/* +================== +Mod_LoadModel + +Loads a model into the cache +================== +*/ +model_t *Mod_LoadModel (model_t *mod, qboolean crash) +{ + void *d; + unsigned *buf; + byte stackbuf[1024]; // avoid dirtying the cache heap + char strip[128]; + char md3name[128]; + + if (!mod->needload) + { + if (mod->type == mod_alias || mod->type == mod_md3 || mod->type == mod_halflife) + { + 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 +// + if (r_loadq3models.value) + { + COM_StripExtension(mod->name, &strip[0]); + sprintf (&md3name[0], "%s.md3", &strip[0]); + + buf = (unsigned *)COM_LoadStackFile (md3name, stackbuf, sizeof(stackbuf)); + if (!buf) + { + buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); + if (!buf) + { + if (crash) + Sys_Error ("Mod_NumForName: %s not found", mod->name); + return NULL; + } + } + } + else + { + buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); + if (!buf && crash) + { + // Reload with another .mdl + buf = (unsigned *)COM_LoadStackFile("progs/missing_model.mdl", stackbuf, sizeof(stackbuf)); + if (buf) + { + Con_Printf ("Missing model %s substituted\n", mod->name); + } + } + 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 = qfalse; + + switch (LittleLong(*(unsigned *)buf)) + { + case IDPOLYHEADER: //Quake .mdl support + Mod_LoadAliasModel (mod, buf); + break; + case RAPOLYHEADER: //Hexen II .mdl support + Mod_LoadH2AliasModel (mod, buf); + break; + case MD2IDALIASHEADER: //Quake II .md2 support + Mod_LoadQ2AliasModel (mod, buf); + break; + case MD3IDHEADER: //Quake III .md3 support + Mod_LoadQ3AliasModel (mod, buf); + break; + case HLPOLYHEADER: //Half-Life .mdl support + Mod_LoadHLModel (mod, buf); + break; + case IDSPRITEHEADER: //Quake .spr .spr32 and HL sprite support + Mod_LoadSpriteModel (mod, buf); + break; + case IDSPRITE2HEADER: //Quake II .sp2 support + Mod_LoadQ2SpriteModel (mod, buf); + break; +/* + case IDBSPHEADER: //q2 bsp support + Mod_LoadQ2BrushModel (mod, buf); + break; +*/ + default: //.bsp ver 29,30 support + Mod_LoadBrushModel (mod, buf); + break; + } + + return mod; +} + +/* +================== +Mod_ForName + +Loads in a model for the given name +================== +*/ +model_t *Mod_ForName (char *name, qboolean crash) +{ + model_t *mod; + mod = Mod_FindName (name); + + return Mod_LoadModel (mod, crash); +} + + +/* +=============================================================================== + + BRUSHMODEL LOADING + +=============================================================================== +*/ + +byte *mod_base; +int GL_LoadTexturePixels (byte *data, char *identifier, int width, int height, int mode); + +#define ISSKYTEX(name) ((name)[0] == 's' && (name)[1] == 'k' && (name)[2] == 'y') + +#define ISTURBTEX(name) ((loadmodel->bspversion == BSPVERSION && (name)[0] == '*') || \ + (loadmodel->bspversion == HL_BSPVERSION && (name)[0] == '!') || \ + (loadmodel->bspversion == NZP_BSPVERSION && (name)[0] == '!')) + +extern int detail_texture; +extern int nonetexture; +/* +================= +Mod_LoadTextures +================= +*/ +void Mod_LoadTextures (lump_t *l) +{ + int i, j, pixels, num, max, altmax; + miptex_t *mt; + texture_t *tx, *tx2; + texture_t *anims[10]; + texture_t *altanims[10]; + dmiptexlump_t *m; + char texname[64]; + + loadmodel->textures = NULL; + + char fbr_mask_name[64]; + + if (!l->filelen) + { + return; + } + m = (dmiptexlump_t *)(mod_base + l->fileofs); + + m->nummiptex = LittleLong (m->nummiptex); + + loadmodel->numtextures = m->nummiptex; + loadmodel->textures = static_cast(Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname)); + + loading_num_step = loading_num_step + m->nummiptex; + + 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 = static_cast(Hunk_AllocName (sizeof(texture_t) , loadname )); + + const std::size_t buffer_size = pixels; + + byte* tx_pixels = static_cast(memalign(16, buffer_size)); + + if (!tx_pixels) + { + Sys_Error("BrushTex: Out of RAM for loading textures\n"); + } + 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); + // the pixels immediately follow the structures + memcpy ( tx_pixels, mt+1, pixels); + + int level = 0; + if (r_mipmaps.value > 0) + level = 3; + + //if (loadmodel->isworldmodel && loadmodel->bspversion != HL_BSPVERSION && ISSKYTEX(tx->name)) + if (loadmodel->bspversion != HL_BSPVERSION && loadmodel->bspversion != NZP_BSPVERSION && ISSKYTEX(tx->name)) + { + R_InitSky (tx_pixels); + mapTextureNameList.push_back(solidskytexture); + mapTextureNameList.push_back(alphaskytexture); + } + else + { + if (loadmodel->bspversion == HL_BSPVERSION || loadmodel->bspversion == NZP_BSPVERSION) + { + + char filename[64]; // Filename to check r4w file + byte *f; + sprintf(filename, "textures/maps/%s/%s.r4w", sv.name, mt->name); // search in textures/maps/MAPNAME/TEXNAME + + f = static_cast(COM_LoadHunkFile(filename)); + + if (!f) { + sprintf(filename, "textures/%s.r4w", mt->name); // search in textures/TEXNAME + f = static_cast(COM_LoadHunkFile(filename)); + } + + if (!f) { + Con_Printf("Loading texture %s as WAD3\n", mt->name); // didn't find the texture in the folder + + // naievil -- try to push wad3 loading + int index = WAD3_LoadTexture(mt); + if(index) + { + com_netpath[0] = 0; + tx->gl_texturenum = index; + tx->fullbright = -1; + mapTextureNameList.push_back(tx->gl_texturenum); + tx->dt_texturenum = 0; + + // if(tx_pixels = WAD3_LoadTexture(mt)) + // { + // com_netpath[0] = 0; + // tx->gl_texturenum = GL_LoadPalletedTexture (tx_pixels, tx->name, tx->width, tx->height, 0); + // tx->fullbright = -1; + // mapTextureNameList.push_back(tx->gl_texturenum); + // tx->dt_texturenum = 0; + + } + else + { + Con_Printf("Texture %s not found\n", mt->name); // didn't find the texture in the folder + com_netpath[0] = 0; + tx->gl_texturenum = nonetexture; + } + + } else { + + int w, h; + + unsigned int magic = *((unsigned int*)(f)); + if (magic == 0x65663463) // what the fuck? + { + w = *((int*)(f + 4)); + h = *((int*)(f + 8)); + + tx->gl_texturenum = GL_LoadTexture4(mt->name, w, h, (byte*)(f + 16), GU_LINEAR); + mapTextureNameList.push_back(tx->gl_texturenum); + } + + } + } + else + { + sprintf (texname, "textures/%s", mt->name); + tx->gl_texturenum = loadtextureimage (texname, 0, 0, qfalse, GU_LINEAR); + if(tx->gl_texturenum == 0) + { + tx->gl_texturenum = GL_LoadTexture (mt->name, tx->width, tx->height, (byte *)(tx_pixels), qtrue, GU_LINEAR, level); + } + mapTextureNameList.push_back(tx->gl_texturenum); +/* + //Crow_bar mult detail textures + sprintf (detname, "gfx/detail/%s", mt->name); + tx->dt_texturenum = loadtextureimage (detname, 0, 0, qfalse, GU_LINEAR); + mapTextureNameList.push_back(tx->dt_texturenum); +*/ + tx->dt_texturenum = 0; + // check for fullbright pixels in the texture - only if it ain't liquid, etc also + if ((tx->name[0] != '*') && (FindFullbrightTexture ((byte *)(tx_pixels), pixels))) + { + // convert any non fullbright pixel to fully transparent + ConvertPixels ((byte *)(tx_pixels), 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 *)(tx_pixels), qtrue, GU_LINEAR, level); + mapTextureNameList.push_back(tx->fullbright); + } + else + tx->fullbright = -1; // because 0 is a potentially valid texture number + } + } + strcpy(loading_name, mt->name); + free (tx_pixels); + loading_cur_step++; + SCR_UpdateScreen(); + } + +// +// 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]; + } + } +} + +/* +================= +Mod_LoadLighting +================= +*/ +void Mod_LoadLighting (lump_t *l) +{ + if (COM_CheckParm ("-lm_1")) + LIGHTMAP_BYTES = 1; + else if (COM_CheckParm ("-lm_2")) + LIGHTMAP_BYTES = 2; + else if (COM_CheckParm ("-lm_3")) + LIGHTMAP_BYTES = 3; + else + LIGHTMAP_BYTES = 4; + + loadmodel->lightdata = NULL; + + if (loadmodel->bspversion == HL_BSPVERSION || loadmodel->bspversion == NZP_BSPVERSION) + { + if (!l->filelen) + { + return; + } + loadmodel->lightdata = static_cast(Hunk_AllocName ( l->filelen, loadname)); + memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); + return; + } + + 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); + + if (data) + { + 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"); + } + // LordHavoc: no .lit found, expand the white lighting data to color + + if (!l->filelen) + { + return; + } + loadmodel->lightdata = static_cast(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 + +} + +// added by dr_mabuse1981 + +/* +================= +Mod_HL_LoadLighting +================= +*/ +void Mod_HL_LoadLighting (lump_t *l) +{ + if (COM_CheckParm ("-lm_1")) + LIGHTMAP_BYTES = 1; + else if (COM_CheckParm ("-lm_2")) + LIGHTMAP_BYTES = 2; + else if (COM_CheckParm ("-lm_3")) + LIGHTMAP_BYTES = 3; + else + LIGHTMAP_BYTES = 4; + + if (!l->filelen) + { + loadmodel->lightdata = NULL; + return; + } + + loadmodel->lightdata = static_cast(Hunk_AllocName ( l->filelen, loadname)); + memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); +} + +/* +================= +Mod_LoadVisibility +================= +*/ +void Mod_LoadVisibility (lump_t *l) +{ + loadmodel->visdata = NULL; + + if (!l->filelen) + { + return; + } + + loadmodel->visdata = static_cast(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 + + Q_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 + + Q_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 +.ent file support by Crow_bar +================= +*/ +void Mod_LoadEntities (lump_t *l) +{ + char entfilename[128]; + + loadmodel->entities = NULL; + + strcpy(entfilename, loadmodel->name); + COM_StripExtension(entfilename, entfilename); + strcat(entfilename, ".ent"); + ent_file = (byte*) COM_LoadHunkFile (entfilename); + + if (ent_file) + { + 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 = static_cast(Hunk_AllocName ( l->filelen, entfilename)); + memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); + + if (loadmodel->bspversion == HL_BSPVERSION || loadmodel->bspversion == NZP_BSPVERSION) + Mod_ParseWadsFromEntityLump(loadmodel->entities); +} + + +/* +================= +Mod_LoadVertexes +================= +*/ +void Mod_LoadVertexes (lump_t *l) +{ + dvertex_t *in; + mvertex_t *out; + int i, count; + + in = reinterpret_cast(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = static_cast(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 = reinterpret_cast(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = static_cast(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) +{ + dedge_t *in; + medge_t *out; + int i, count; + + in = reinterpret_cast(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = static_cast(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 = reinterpret_cast(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = static_cast(Hunk_AllocName ( count*sizeof(*out), loadname)); + + loadmodel->texinfo = out; + loadmodel->numtexinfo = count; + + for ( i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][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; +#if 0 + if (len1 + len2 < 0.001) + out->mipadjust = 1; // don't crash + else + out->mipadjust = 1 / floorf( (len1+len2)/2 + 0.1 ); +#endif + + 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) +{ + dface_t *in; + msurface_t *out; + int i, count, surfnum; + int planenum, side; + + in = reinterpret_cast(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = static_cast(Hunk_AllocName ( count*sizeof(*out), loadname)); + + loadmodel->surfaces = out; + loadmodel->numsurfaces = count; + + for ( surfnum=0 ; surfnumfirstedge = LittleLong(in->firstedge); + out->numedges = LittleShort(in->numedges); + out->flags = 0; + + planenum = LittleShort(in->planenum); + side = LittleShort(in->side); + if (side) + out->flags |= SURF_PLANEBACK; + + out->plane = loadmodel->planes + planenum; + + out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo); + + CalcSurfaceExtents (out); + + // lighting info + + for (i=0 ; istyles[i] = in->styles[i]; + + if (loadmodel->bspversion == HL_BSPVERSION || loadmodel->bspversion == NZP_BSPVERSION) + i = LittleLong(in->lightofs); + else + i = LittleLong(in->lightofs * 3); + + if (i == -1) + out->samples = NULL; + else + out->samples = loadmodel->lightdata + (i); + + if (!Q_strncmp(out->texinfo->texture->name,"sky",3)) + { + out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); + + //if(kurok) + //GL_SubdivideSurface (out); // cut up polygon for warps + //else + GL_Surface (out); // Don't cut up polygon for warps + continue; + } + + if (!Q_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_Surface (out); // Don't cut up polygon for warps + //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 (lump_t *l) +{ + int i, j, count, p; + dnode_t *in; + mnode_t *out; + + in = reinterpret_cast(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = static_cast(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++) + { + p = LittleShort (in->children[j]); + if (p >= 0) + out->children[j] = loadmodel->nodes + p; + else + out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); + } + } + + Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs +} + +/* +================= +Mod_LoadLeafs +================= +*/ +void Mod_LoadLeafs (lump_t *l) +{ + dleaf_t *in; + mleaf_t *out; + int i, j, count, p; + + in = reinterpret_cast(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = static_cast(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 + + LittleShort(in->firstmarksurface); + out->nummarksurfaces = LittleShort(in->nummarksurfaces); + + 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]; + + // gl underwater warp + if (out->contents != CONTENTS_EMPTY) + { + for (j=0 ; jnummarksurfaces ; j++) + out->firstmarksurface[j]->flags |= SURF_UNDERWATER; + } + } +} + +/* +================= +Mod_LoadClipnodes +================= +*/ +void Mod_LoadClipnodes (lump_t *l) +{ + dclipnode_t *in, *out; + int i, count; + hull_t *hull; + + in = reinterpret_cast(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = static_cast(Hunk_AllocName ( count*sizeof(*out), loadname)); + + loadmodel->clipnodes = out; + loadmodel->numclipnodes = count; + + if (loadmodel->bspversion == HL_BSPVERSION || loadmodel->bspversion == NZP_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; + } + + for (i=0 ; iplanenum = LittleLong(in->planenum); + out->children[0] = LittleShort(in->children[0]); + out->children[1] = LittleShort(in->children[1]); + } +} + +/* +================= +Mod_MakeHull0 + +Deplicate the drawing hull structure as a clipping hull +================= +*/ +void Mod_MakeHull0 (void) +{ + mnode_t *in, *child; + dclipnode_t *out; + int i, j, count; + hull_t *hull; + + hull = &loadmodel->hulls[0]; + + in = loadmodel->nodes; + count = loadmodel->numnodes; + out = static_cast(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 i, j, count; + short *in; + msurface_t **out; + + in = reinterpret_cast(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Con_Printf ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = static_cast(Hunk_AllocName ( count*sizeof(*out), loadname)); + + loadmodel->marksurfaces = out; + loadmodel->nummarksurfaces = count; + + for ( i=0 ; i= loadmodel->numsurfaces) + Sys_Error ("Mod_ParseMarksurfaces: bad surface number"); + out[i] = loadmodel->surfaces + j; + } +} + +/* +================= +Mod_LoadSurfedges +================= +*/ +void Mod_LoadSurfedges (lump_t *l) +{ + int i, count; + int *in, *out; + + in = reinterpret_cast(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Con_Printf("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = static_cast(Hunk_AllocName ( count*sizeof(*out), loadname)); + + loadmodel->surfedges = out; + loadmodel->numsurfedges = count; + + for ( i=0 ; i(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Con_Printf("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = static_cast(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++) + { + #ifdef PSP_VFPU + corner[i] = vfpu_fabsf(mins[i]) > vfpu_fabsf(maxs[i]) ? vfpu_fabsf(mins[i]) : vfpu_fabsf(maxs[i]); + #else + corner[i] = fabsf(mins[i]) > fabsf(maxs[i]) ? fabsf(mins[i]) : fabsf(maxs[i]); + #endif + } + + return Length (corner); +} + +/* +================= +Mod_LoadBrushModel +================= +*/ + +void Mod_LoadBrushModel (model_t *mod, void *buffer) +{ + int i, j; + dheader_t *header; + dmodel_t *bm; + loadmodel->type = mod_brush; + + header = (dheader_t *)buffer; + + i = LittleLong (header->version); + + mod->bspversion = LittleLong (header->version); + + if (mod->bspversion != BSPVERSION && mod->bspversion != HL_BSPVERSION && mod->bspversion != NZP_BSPVERSION) + Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or %i (HalfLife))", mod->name, mod->bspversion, BSPVERSION, HL_BSPVERSION); + + //loadmodel->isworldmodel = !strcmp(loadmodel->name, va("maps/%s.bsp", mapname.string)); + + if (mod->bspversion == BSPVERSION && r_hlbsponly.value && !developer.value) + Host_Error ("Mod_LoadBrushModel: Normal quake maps are disabled. Please use half life bsp. "); + + +// swap all the lumps + mod_base = (byte *)header; + + for (i=0 ; ilumps[LUMP_VERTEXES]); + + loading_cur_step++; + strcpy(loading_name, "Edges"); + SCR_UpdateScreen (); + + Mod_LoadEdges (&header->lumps[LUMP_EDGES]); + + loading_cur_step++; + strcpy(loading_name, "Surfedges"); + SCR_UpdateScreen (); + + Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); + + loading_cur_step++; + strcpy(loading_name, "Entities"); + SCR_UpdateScreen (); + + Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); + + loading_cur_step++; + strcpy(loading_name, "Textures"); + SCR_UpdateScreen (); + + Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); + + + if(mod->bspversion == HL_BSPVERSION || mod->bspversion == NZP_BSPVERSION) // dr_mabuse1981 + { + Mod_HL_LoadLighting (&header->lumps[LUMP_LIGHTING]); + } + else + { + Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]); + } + + loading_cur_step++; + SCR_UpdateScreen (); + + Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); + + loading_cur_step++; + strcpy(loading_name, "Texinfo"); + SCR_UpdateScreen (); + + Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); + + loading_cur_step++; + strcpy(loading_name, "Faces"); + SCR_UpdateScreen (); + + Mod_LoadFaces (&header->lumps[LUMP_FACES]); + + loading_cur_step++; + strcpy(loading_name, "Marksurfaces"); + SCR_UpdateScreen (); + + Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]); + + loading_cur_step++; + strcpy(loading_name, "Visibility"); + SCR_UpdateScreen (); + + Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); + + loading_cur_step++; + strcpy(loading_name, "Leafs"); + SCR_UpdateScreen (); + + Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); + + loading_cur_step++; + strcpy(loading_name, "Nodes"); + SCR_UpdateScreen (); + + Mod_LoadNodes (&header->lumps[LUMP_NODES]); + + loading_cur_step++; + strcpy(loading_name, "Clipnodes"); + SCR_UpdateScreen (); + + Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); + + loading_cur_step++; + strcpy(loading_name, "Submodels"); + SCR_UpdateScreen (); + + Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); + + loading_cur_step++; + strcpy(loading_name, "Hull"); + SCR_UpdateScreen (); + + Mod_MakeHull0 (); + + strcpy(loading_name, "Screen"); + loading_cur_step++; + SCR_UpdateScreen (); + + 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]; +mh2triangle_t h2triangles[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; + trivertx_t *pinframe; + int i; + //int 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->bboxmax.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->bboxmax.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; + } +} + +/* +=============== +Mod_LoadAllSkins +=============== +*/ +static qboolean mod_h2; + +void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype) +{ + int i, j, k; + char name[64], model[64], model2[64]; + int s; + //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; +/* + if ( (pheader->skinwidth & 15) || (pheader->skinheight & 15) ) + Sys_Error ("Texture %s is not 16 aligned", name); +*/ + for (i=0 ; itype == ALIAS_SKIN_SINGLE) + { + Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight ); + COM_StripExtension(loadmodel->name, model); + sprintf (model2, "%s_%i", model, i); + pheader->gl_texturenum[i][0] = + pheader->gl_texturenum[i][1] = + pheader->gl_texturenum[i][2] = + pheader->gl_texturenum[i][3] = loadtextureimage (model2, 0, 0, qfalse, GU_LINEAR); + + if (pheader->gl_texturenum[i][0] == 0)// did not find a matching TGA... + { + sprintf (name, "%s_%i", loadmodel->name, i); + if(mod_h2) + { + pheader->gl_texturenum[i][0] = + pheader->gl_texturenum[i][1] = + pheader->gl_texturenum[i][2] = + pheader->gl_texturenum[i][3] = + GL_LoadPalTex (name, pheader->skinwidth, pheader->skinheight, (byte *)(pskintype + 1) , qtrue, GU_LINEAR, 0, NULL, PAL_H2); + } + else + { + pheader->gl_texturenum[i][0] = + pheader->gl_texturenum[i][1] = + pheader->gl_texturenum[i][2] = + pheader->gl_texturenum[i][3] = + GL_LoadTexture (name, pheader->skinwidth, pheader->skinheight, (byte *)(pskintype + 1) , qtrue, GU_LINEAR, 0); + } + } + pskintype = (daliasskintype_t *)((byte *)(pskintype+1) + s); + /* Crow_bar Memory Leak Fixed One Work not used */ + mapTextureNameList.push_back(pheader->gl_texturenum[i][i]); + /* Crow_bar Memory Leak Fixed */ + } + else + { + // animating skin group. yuck. + pskintype++; + pinskingroup = (daliasskingroup_t *)pskintype; + groupskins = LittleLong (pinskingroup->numskins); + pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); + + pskintype = reinterpret_cast(pinskinintervals + groupskins); + + for (j=0 ; jskinwidth, pheader->skinheight ); + COM_StripExtension(loadmodel->name, model); + sprintf (model2, "%s_%i_%i", model, i, j); + pheader->gl_texturenum[i][j&3] = + loadtextureimage (model2, 0, 0, qfalse, GU_LINEAR); + + if (pheader->gl_texturenum[i][j&3] == 0)// did not find a matching TGA... + { + sprintf (name, "%s_%i_%i", loadmodel->name, i, j); + if(mod_h2) + { + pheader->gl_texturenum[i][j&3] = + GL_LoadPalTex (name, pheader->skinwidth,pheader->skinheight, (byte *)(pskintype), qtrue, GU_LINEAR, 0, NULL, PAL_H2); + } + else + { + pheader->gl_texturenum[i][j&3] = + GL_LoadTexture (name, pheader->skinwidth,pheader->skinheight, (byte *)(pskintype), qtrue, GU_LINEAR, 0); + } + } + pskintype = (daliasskintype_t *)((byte *)(pskintype) + s); + /* Crow_bar Memory Leak Fixed*/ + mapTextureNameList.push_back(pheader->gl_texturenum[i][j&3]); + /* Crow_bar Memory Leak Fixed*/ + } + 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; + +// some models are special + // NOTE: comparing not only with player.mdl, but with all models + // begin with "player" coz we need to support DME models as well! + if (!strncmp(mod->name, "progs/player", 12)) + mod->modhint = MOD_PLAYER; + else if (!strcmp(mod->name, "progs/eyes.mdl")) + mod->modhint = MOD_EYES; + else if (!strcmp(mod->name, "progs/flame0.mdl") || + !strcmp(mod->name, "progs/flame.mdl") || + !strcmp(mod->name, "progs/flame2.mdl")) + mod->modhint = MOD_FLAME; + else if (!strcmp(mod->name, "progs/bolt.mdl") || + !strcmp(mod->name, "progs/bolt2.mdl") || + !strcmp(mod->name, "progs/bolt3.mdl")) + mod->modhint = MOD_THUNDERBOLT; + else if (!strcmp(mod->name, "progs/VModels/v_Colt.mdl") || //JUKKI Add nzp weapons here please plox + !strcmp(mod->name, "progs/VModels/v_kar.mdl") || + !strcmp(mod->name, "progs/VModels/v_thomp.mdl")) + mod->modhint = MOD_WEAPON; + else if (!strcmp(mod->name, "progs/lavaball.mdl")) + mod->modhint = MOD_LAVABALL; + else if (!strcmp(mod->name, "progs/spike.mdl") || + !strcmp(mod->name, "progs/s_spike.mdl")) + mod->modhint = MOD_SPIKE; + else if (!strcmp(mod->name, "progs/shambler.mdl")) + mod->modhint = MOD_SHAMBLER; + else + mod->modhint = MOD_NORMAL; + + 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 = static_cast(Hunk_AllocName (size, loadname)); + + mod->flags = LittleLong (pinmodel->flags); + +// +// 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", mod->name); + + pheader->numtris = LittleLong (pinmodel->numtris); + + if (pheader->numtris <= 0) + Sys_Error ("model %s has no triangles", mod->name); + + 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 = static_cast(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 = static_cast(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]; + + //maliasframedesc_t *frame0; + + for (i=0 ; i(LittleLong (pframetype->type)); + + /*if(i == 0)//see this right here? blubs can type, but something is a-miss with the precalculated mdl min and max values, so let's not use this. + { + frame0 = &pheader->frames[0]; + + mod->mins[0] = frame0->bboxmin.v[0]; + mod->mins[1] = frame0->bboxmin.v[1]; + mod->mins[2] = frame0->bboxmin.v[2]; + mod->maxs[0] = frame0->bboxmax.v[0]; + mod->maxs[1] = frame0->bboxmax.v[1]; + mod->maxs[2] = frame0->bboxmax.v[2]; + }*/ + 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] = -32; + mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 32; + + // + // 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_LoadAliasH2Model +reads extra field for num ST verts, and extra index list of them +Hexen II model format +================= +*/ +void GL_MakeAliasModelDisplayListsH2 (model_t *m, aliashdr_t *hdr); +void Mod_LoadH2AliasModel (model_t *mod, void *buffer) +{ + int i, j; + newmdl_t *pinmodel; + stvert_t *pinstverts; + dnewtriangle_t *pintriangles; + int version, numframes;//, numskins; + int size; + daliasframetype_t *pframetype; + daliasskintype_t *pskintype; + int start, end, total; + + start = Hunk_LowMark (); + + pinmodel = (newmdl_t *)buffer; + + version = LittleLong (pinmodel->version); + + if (version != ALIAS_NEWVERSION) + Sys_Error ("%s has wrong version number (%i should be %i)", + mod->name, version, ALIAS_NEWVERSION); + + +// +// 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 = static_cast(Hunk_AllocName (size, loadname)); + + mod->flags = LittleLong (pinmodel->flags); + +// +// 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); + pheader->version = LittleLong (pinmodel->num_st_verts); //hide num_st in version + + 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", mod->name); + + pheader->numtris = LittleLong (pinmodel->numtris); + + if (pheader->numtris <= 0) + Sys_Error ("model %s has no triangles", mod->name); + + 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 = static_cast(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 = static_cast(Mod_LoadAllSkins (pheader->numskins, pskintype, mod->flags)); +*/ + mod_h2 = qtrue; + pskintype = (daliasskintype_t *)&pinmodel[1]; + pskintype = static_cast(Mod_LoadAllSkins (pheader->numskins, pskintype)); + mod_h2 = qfalse; +// +// load base s and t vertices +// + pinstverts = (stvert_t *)pskintype; + + for (i=0 ; iversion ; i++) //version holds num_st_verts + { + 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 = (dnewtriangle_t *)&pinstverts[pheader->version]; + + for (i=0 ; inumtris ; i++) + { + triangles[i].facesfront = LittleLong (pintriangles[i].facesfront); + + for (j=0 ; j<3 ; j++) + { + h2triangles[i].vertindex[j] = LittleShort (pintriangles[i].vertindex[j]); + h2triangles[i].stindex[j] = LittleShort (pintriangles[i].stindex[j]); + } + } + +// +// load the frames +// + posenum = 0; + pframetype = (daliasframetype_t *)&pintriangles[pheader->numtris]; + + for (i=0 ; i(LittleLong (pframetype->type)); + + 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_MakeAliasModelDisplayListsH2 (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_LoadQ2AliasModel +================= +*/ +int loadtextureimage (char* filename, int matchwidth, int matchheight, qboolean complain, int filter); + +void Mod_LoadQ2AliasModel (model_t *mod, void *buffer) +{ + //int numskins; + int i, j, version, numframes, size, *pinglcmd, *poutglcmd, start, end, total; + md2_t *pinmodel, *pheader; + md2triangle_t *pintriangles, *pouttriangles; + md2frame_t *pinframe, *poutframe; + char *pinskins; + //char skinname[256], *skinnamebase; + + start = Hunk_LowMark (); + + pinmodel = (md2_t *)buffer; + + version = LittleLong (pinmodel->version); + if (version != MD2ALIAS_VERSION) + Sys_Error ("%s has wrong version number (%i should be %i)", + mod->name, version, MD2ALIAS_VERSION); + + mod->type = mod_alias; + mod->aliastype = ALIASTYPE_MD2; + +// LordHavoc: see pheader ofs adjustment code below for why this is bigger + size = LittleLong(pinmodel->ofs_end) + sizeof(md2_t); + + if (size <= 0 || size >= MD2MAX_SIZE) + Sys_Error ("%s is not a valid model", mod->name); + pheader = static_cast(Hunk_AllocName (size, loadname)); + + mod->flags = 0; // there are no MD2 flags + +// endian-adjust and copy the data, starting with the alias model header + for (i = 0;i < 17;i++) // LordHavoc: err... FIXME or something... + ((int*)pheader)[i] = LittleLong(((int *)pinmodel)[i]); + mod->numframes = numframes = pheader->num_frames; + + mod->synctype = ST_RAND; + + if (pheader->ofs_skins <= 0 || pheader->ofs_skins >= pheader->ofs_end) + Sys_Error ("%s is not a valid model", mod->name); + if (pheader->ofs_st <= 0 || pheader->ofs_st >= pheader->ofs_end) + Sys_Error ("%s is not a valid model", mod->name); + if (pheader->ofs_tris <= 0 || pheader->ofs_tris >= pheader->ofs_end) + Sys_Error ("%s is not a valid model", mod->name); + if (pheader->ofs_frames <= 0 || pheader->ofs_frames >= pheader->ofs_end) + Sys_Error ("%s is not a valid model", mod->name); + if (pheader->ofs_glcmds <= 0 || pheader->ofs_glcmds >= pheader->ofs_end) + Sys_Error ("%s is not a valid model", mod->name); + + if (pheader->num_tris < 1 || pheader->num_tris > MD2MAX_TRIANGLES) + Sys_Error ("%s has invalid number of triangles: %i", mod->name, pheader->num_tris); + if (pheader->num_xyz < 1 || pheader->num_xyz > MD2MAX_VERTS) + Sys_Error ("%s has invalid number of vertices: %i", mod->name, pheader->num_xyz); + if (pheader->num_frames < 1 || pheader->num_frames > 256) //MD2MAX_FRAMES) + Sys_Error ("%s has invalid number of frames: %i", mod->name, pheader->num_frames); + if (pheader->num_skins < 0 || pheader->num_skins > MD2MAX_SKINS) + Sys_Error ("%s has invalid number of skins: %i", mod->name, pheader->num_skins); + +// LordHavoc: adjust offsets in new model to give us some room for the bigger header +// cheap offsetting trick, just offset it all by the pheader size...mildly wasteful + for (i = 0;i < 7;i++) + ((int*)&pheader->ofs_skins)[i] += sizeof(pheader); + +// load the skins + if (pheader->num_skins) + { + pinskins = static_cast((void*)((int) pinmodel + LittleLong(pinmodel->ofs_skins))); + for (i = 0;i < pheader->num_skins;i++) + { + pheader->gl_texturenum[i] = loadtextureimage (pinskins, 0, 0, qtrue, GU_LINEAR); + pinskins += MD2MAX_SKINNAME; + /* Crow_bar Memory Leak Fixed */ + mapTextureNameList.push_back(pheader->gl_texturenum[i]); + /* Crow_bar Memory Leak Fixed */ + } + } + +// load triangles + pintriangles = static_cast((void*)((int) pinmodel + LittleLong(pinmodel->ofs_tris))); + pouttriangles = static_cast((void*)((int) pheader + pheader->ofs_tris)); + // swap the triangle list + for (i=0 ; i < pheader->num_tris ; i++) + { + for (j=0 ; j<3 ; j++) + { + pouttriangles->index_xyz[j] = LittleShort (pintriangles->index_xyz[j]); + pouttriangles->index_st[j] = LittleShort (pintriangles->index_st[j]); + if (pouttriangles->index_xyz[j] >= pheader->num_xyz) + Sys_Error ("%s has invalid vertex indices", mod->name); + if (pouttriangles->index_st[j] >= pheader->num_st) + Sys_Error ("%s has invalid vertex indices", mod->name); + } + pintriangles++; + pouttriangles++; + } + +// +// load the frames +// + pinframe = static_cast((void*) ((int) pinmodel + LittleLong(pinmodel->ofs_frames))); + poutframe = static_cast((void*) ((int) pheader + pheader->ofs_frames)); + for (i=0 ; i < numframes ; i++) + { + for (j = 0;j < 3;j++) + { + poutframe->scale[j] = LittleFloat(pinframe->scale[j]); + poutframe->translate[j] = LittleFloat(pinframe->translate[j]); + } + + for (j = 0;j < 16;j++) + poutframe->name[j] = pinframe->name[j]; + + for (j = 0;j < pheader->num_xyz;j++) + { + poutframe->verts[j].v[0] = pinframe->verts[j].v[0]; + poutframe->verts[j].v[1] = pinframe->verts[j].v[1]; + poutframe->verts[j].v[2] = pinframe->verts[j].v[2]; + poutframe->verts[j].lightnormalindex = pinframe->verts[j].lightnormalindex; + } + + + pinframe = static_cast((void*) &pinframe->verts[j].v[0]); + poutframe = static_cast((void*) &poutframe->verts[j].v[0]); + } + + // LordHavoc: I may fix this at some point + mod->mins[0] = mod->mins[1] = mod->mins[2] = -64; + mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 64; + + // load the draw list + pinglcmd = static_cast((void*) ((int) pinmodel + LittleLong(pinmodel->ofs_glcmds))); + poutglcmd = static_cast((void*) ((int) pheader + pheader->ofs_glcmds)); + for (i = 0;i < pheader->num_glcmds;i++) + *poutglcmd++ = LittleLong(*pinglcmd++); + +// 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); +} + + +//============================================================================= +//========================Q3 Models============================================ +//============================================================================= +//additional skin loading +char **skinfilelist; +int skinfilecount; + +static qboolean Mod_TryAddSkin(char *skinname, ...) +{ + va_list argptr; + char string[MAX_QPATH]; + + //make sure we don't add it twice + int i; + + + va_start (argptr, skinname); + vsnprintf (string,sizeof(string)-1, skinname,argptr); + va_end (argptr); + string[MAX_QPATH-1] = '\0'; + + for (i = 0; i < skinfilecount; i++) + { + if (!strcmp(skinfilelist[i], string)) + return qtrue; //already added + } + + //if (!COM_FCheckExists(string)) + // return false; + + if (!Sys_FileTime(string)) + return qfalse; + + skinfilelist = static_cast(realloc(skinfilelist, sizeof(*skinfilelist)*(skinfilecount+1))); + skinfilelist[skinfilecount] = static_cast(malloc(strlen(string)+1)); + strcpy(skinfilelist[skinfilecount], string); + skinfilecount++; + + return qtrue; +} + +int Mod_BuildSkinFileList(char *modelname) +{ + int i; + char skinfilename[MAX_QPATH]; + + //flush the old list + for (i = 0; i < skinfilecount; i++) + { + Z_Free(skinfilelist[i]); + skinfilelist[i] = NULL; + } + skinfilecount=0; + + COM_StripExtension(modelname, skinfilename); + + //try and add numbered skins, and then try fixed names. + for (i = 0; ; i++) + { + if (!Mod_TryAddSkin("%s_%i.skin", modelname, i)) + { + if (i == 0) + { + if (!Mod_TryAddSkin("%s_default.skin", skinfilename, i)) + break; + } + else if (i == 1) + { + if (!Mod_TryAddSkin("%s_blue.skin", skinfilename, i)) + break; + } + else if (i == 2) + { + if (!Mod_TryAddSkin("%s_red.skin", skinfilename, i)) + break; + } + else if (i == 3) + { + if (!Mod_TryAddSkin("%s_green.skin", skinfilename, i)) + break; + } + else if (i == 4) + { + if (!Mod_TryAddSkin("%s_yellow.skin", skinfilename, i)) + break; + } + else + break; + } + } + return skinfilecount; +} + +animdata_t anims[NUM_ANIMTYPES]; +vec3_t md3bboxmins, md3bboxmaxs; +void Mod_GetQ3AnimData (char *buf, char *animtype, animdata_t *adata) +{ + int i, j, data[4]; + char *token, num[4]; + + if ((token = strstr(buf, animtype))) + { + while (*token != '\n') + token--; + token++; // so we jump back to the first char + for (i=0 ; i<4 ; i++) + { + memset (num, 0, sizeof(num)); + for (j = 0 ; *token != '\t' ; j++) + num[j] = *token++; + data[i] = Q_atoi(num); + token++; + } + adata->offset = data[0]; + adata->num_frames = data[1]; + adata->loop_frames = data[2]; + adata->interval = 1.0 / (float)data[3]; + } +} + +void Mod_LoadQ3Animation (void) +{ + int ofs_legs; + char *animdata; + animdata_t tmp1, tmp2; + + tmp1.offset = 0; + tmp2.offset = 0; + if (!(animdata = (char *)COM_LoadFile("progs/player/animation.cfg", 0))) + { + Con_Printf ("ERROR: Couldn't open animation file\n"); + return; + } + + memset (anims, 0, sizeof(anims)); + + Mod_GetQ3AnimData (animdata, "BOTH_DEATH1", &anims[both_death1]); + Mod_GetQ3AnimData (animdata, "BOTH_DEATH2", &anims[both_death2]); + Mod_GetQ3AnimData (animdata, "BOTH_DEATH3", &anims[both_death3]); + Mod_GetQ3AnimData (animdata, "BOTH_DEAD1", &anims[both_dead1]); + Mod_GetQ3AnimData (animdata, "BOTH_DEAD2", &anims[both_dead2]); + Mod_GetQ3AnimData (animdata, "BOTH_DEAD3", &anims[both_dead3]); + + Mod_GetQ3AnimData (animdata, "TORSO_ATTACK", &anims[torso_attack]); + Mod_GetQ3AnimData (animdata, "TORSO_ATTACK2", &anims[torso_attack2]); + Mod_GetQ3AnimData (animdata, "TORSO_STAND", &anims[torso_stand]); + Mod_GetQ3AnimData (animdata, "TORSO_STAND2", &anims[torso_stand2]); + + Mod_GetQ3AnimData (animdata, "TORSO_GESTURE", &tmp1); + Mod_GetQ3AnimData (animdata, "LEGS_WALKCR", &tmp2); +// we need to subtract the torso-only frames to get the correct indices + ofs_legs = tmp2.offset - tmp1.offset; + + Mod_GetQ3AnimData (animdata, "LEGS_WALK", &anims[legs_walk]);//R00k + Mod_GetQ3AnimData (animdata, "LEGS_RUN", &anims[legs_run]); + Mod_GetQ3AnimData (animdata, "LEGS_IDLE", &anims[legs_idle]); + anims[legs_walk].offset -= ofs_legs; + anims[legs_run].offset -= ofs_legs; + anims[legs_idle].offset -= ofs_legs; + + Z_Free (animdata); +} + +/* +================= +Mod_LoadQ3ModelTexture +================= +*/ +void Mod_LoadQ3ModelTexture (char *identifier, int flags, int *gl_texnum) +{ + char loadpath[64]; + + Q_snprintfz (loadpath, sizeof(loadpath), "textures/q3models/%s", identifier); + *gl_texnum = loadtextureimage (loadpath, 0, 0, qtrue, GU_LINEAR); + + if (!*gl_texnum) + { + Q_snprintfz (loadpath, sizeof(loadpath), "textures/%s", identifier); + *gl_texnum = loadtextureimage (loadpath, 0, 0, qtrue, GU_LINEAR); + } + if (!*gl_texnum) + { + Q_snprintfz (loadpath, sizeof(loadpath), "progs/%s", identifier); + *gl_texnum = loadtextureimage (loadpath, 0, 0, qtrue, GU_LINEAR); + } + if (!*gl_texnum) + { + Q_snprintfz (loadpath, sizeof(loadpath), "maps/%s", identifier); + *gl_texnum = loadtextureimage (loadpath, 0, 0, qtrue, GU_LINEAR); + } + + mapTextureNameList.push_back(*gl_texnum); +} + +//============================================================================== +#define CLASSIC_MAX_QPATH 64 + +typedef struct +{ + char name[CLASSIC_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 +{ + char filename[MAX_OSPATH]; + pack_t *pack; // only one of filename / pack will be used + struct searchpath_s *next; +} searchpath_t; + +extern char loadname[32]; // for hunk tags + + +typedef struct searchpath_s searchpath_t; +extern searchpath_t* com_searchpaths; +//============================================================================== + +/* +================= +Mod_LoadAllQ3Skins + +supporting only the default skin yet +================= +*/ +void Mod_LoadAllQ3Skins (char *modelname, md3header_t *header) +{ +#if 0 + int i, j, defaultskin, numskinsfound; + char skinname[MAX_QPATH], **skinsfound; + md3surface_t *surf; + md3shader_t *shader; + searchpath_t *search; + + i = strrchr (modelname, '/') - modelname; + Q_strncpyz (skinname, modelname, i+1); + +/* + //EraseDirEntries (); + for (search = com_searchpaths ; search ; search = search->next) + { + if (!search->pack) + { + RDFlags |= RD_NOERASE; + ReadDir (va("%s/%s", search->filename, skinname), va("%s*.skin", loadname)); + } + } + + numskinsfound = num_files; + skinsfound = (char **)malloc (numskinsfound * sizeof(char *)); + for (i=0 ; i(malloc (MAX_QPATH)); + Q_snprintfz (skinsfound[i], MAX_QPATH, "%s/%s", skinname, i); + } +*/ + SceUID dir = sceIoDopen(va("%s/models/%s", com_gamedir, skinname)); + if(dir < 0) + { + return; + } + + SceIoDirent dirent; + + memset(&dirent, 0, sizeof(SceIoDirent)); + + while(sceIoDread(dir, &dirent) > 0) + { + if(dirent.d_name[0] == '.') + { + continue; + } + + if(!strcmp(COM_FileExtension(dirent.d_name),".skin")|| + !strcmp(COM_FileExtension(dirent.d_name),".SKIN")) + { + + } + } + // It only works this lame way coz if I snprintf to skinname from skinname + // then linux's vsnprintf clears out skinname first and it's gonna lost + + Q_strncpyz (skinname, va("%s/%s", skinname, loadname), MAX_QPATH); + + defaultskin = -1; + + for (i=0 ; iofssurfs); + for (j = 0 ; j < header->numsurfs && strcmp(surf->name, token) ; j++) + surf = (md3surface_t *)((byte *)surf + surf->ofsend); + + token = &skindata[pos]; + while (skindata[pos] != '\n' && skindata[pos]) + pos++; + skindata[pos++-1] = '\0'; // becoz of \r\n + + if (token[0] && j < header->numsurfs) + { + shader = (md3shader_t *)((byte *)surf + surf->ofsshaders); + for (j = 0 ; j < surf->numshaders ; j++, shader++) + Q_strncpyz (shader->name, token, MAX_QPATH); + } + } + + Z_Free (skindata); + } + + for (i=0 ; iname, &md3name[0]); + + if (!strcmp (md3name, "progs/g_shot") || + !strcmp (md3name, "progs/g_nail") || + !strcmp (md3name, "progs/g_nail2") || + !strcmp (md3name, "progs/g_rock") || + !strcmp (md3name, "progs/g_rock2") || + !strcmp (md3name, "progs/g_light") || + !strcmp (md3name, "progs/armor") || + !strcmp (md3name, "progs/backpack") || + !strcmp (md3name, "progs/w_g_key") || + !strcmp (md3name, "progs/w_s_key") || + !strcmp (md3name, "progs/m_g_key") || + !strcmp (md3name, "progs/m_s_key") || + !strcmp (md3name, "progs/b_g_key") || + !strcmp (md3name, "progs/b_s_key") || + !strcmp (md3name, "progs/quaddama") || + !strcmp (md3name, "progs/invisibl") || + !strcmp (md3name, "progs/invulner") || + !strcmp (md3name, "progs/jetpack") || + !strcmp (md3name, "progs/cube") || + !strcmp (md3name, "progs/suit") || + !strcmp (md3name, "progs/boots") || + !strcmp (md3name, "progs/end1") || + !strcmp (md3name, "progs/end2") || + !strcmp (md3name, "progs/end3") || + !strcmp (md3name, "progs/end4")) { + mod->flags |= EF_ROTATE; + } + else if (!strcmp (md3name, "progs/missile")) + { + mod->flags |= EF_ROCKET; + } + else if (!strcmp (md3name, "progs/gib1") || //EF_GIB + !strcmp (md3name, "progs/gib2") || + !strcmp (md3name, "progs/gib3") || + !strcmp (md3name, "progs/h_player") || + !strcmp (md3name, "progs/h_dog") || + !strcmp (md3name, "progs/h_mega") || + !strcmp (md3name, "progs/h_guard") || + !strcmp (md3name, "progs/h_wizard") || + !strcmp (md3name, "progs/h_knight") || + !strcmp (md3name, "progs/h_hellkn") || + !strcmp (md3name, "progs/h_zombie") || + !strcmp (md3name, "progs/h_shams") || + !strcmp (md3name, "progs/h_shal") || + !strcmp (md3name, "progs/h_ogre") || + !strcmp (md3name, "progs/armor") || + !strcmp (md3name, "progs/h_demon")) { + mod->flags |= EF_GIB; + } + else if (!strcmp (md3name, "progs/grenade")) + { + mod->flags |= EF_GRENADE; + } + else if (!strcmp (md3name, "progs/w_spike")) + { + mod->flags |= EF_TRACER; + } + else if (!strcmp (md3name, "progs/k_spike")) + { + mod->flags |= EF_TRACER2; + } + else if (!strcmp (md3name, "progs/v_spike")) + { + mod->flags |= EF_TRACER3; + } + else if (!strcmp (md3name, "progs/zom_gib")) + { + mod->flags |= EF_ZOMGIB; + } + else if (!strcmp(md3name, "progs/VModel/v_colt") || + !strcmp(md3name, "progs/VModel/v_kar") || + !strcmp(md3name, "progs/VModel/v_thomp")) + { + mod->modhint = MOD_WEAPON; + } + + else if (!strcmp (md3name, "progs/lavaball")) + { + mod->modhint = MOD_LAVABALL; + } + + header = (md3header_t *)buffer; + + version = LittleLong (header->version); + if (version != MD3_VERSION) + Sys_Error ("Mod_LoadQ3Model: %s has wrong version number (%i should be %i)", md3name, version, MD3_VERSION); + +// endian-adjust all data + header->numframes = LittleLong (header->numframes); + + if (header->numframes < 1) + Sys_Error ("Mod_LoadQ3Model: model %s has no frames", md3name); + else if (header->numframes > MAXMD3FRAMES) + Sys_Error ("Mod_LoadQ3Model: model %s has too many frames", md3name); + + header->numtags = LittleLong (header->numtags); + if (header->numtags > MAXMD3TAGS) + Sys_Error ("Mod_LoadQ3Model: model %s has too many tags", md3name); + + header->numsurfs = LittleLong (header->numsurfs); + if (header->numsurfs < 1) + Sys_Error ("Mod_LoadQ3Model: model %s has no surfaces", md3name); + else if (header->numsurfs > MAXMD3SURFS) + Sys_Error ("Mod_LoadQ3Model: model %s has too many surfaces", md3name); + + header->numskins = LittleLong (header->numskins); + header->ofsframes = LittleLong (header->ofsframes); + header->ofstags = LittleLong (header->ofstags); + header->ofssurfs = LittleLong (header->ofssurfs); + header->ofsend = LittleLong (header->ofsend); + + // swap all the frames + frame = (md3frame_t *)((byte *)header + header->ofsframes); + for (i=0 ; inumframes ; i++) + { + frame[i].radius = LittleFloat (frame->radius); + for (j=0 ; j<3 ; j++) + { + frame[i].mins[j] = LittleFloat (frame[i].mins[j]); + frame[i].maxs[j] = LittleFloat (frame[i].maxs[j]); + frame[i].pos[j] = LittleFloat (frame[i].pos[j]); + } + } + + // swap all the tags + tag = (md3tag_t *)((byte *)header + header->ofstags); + for (i=0 ; inumtags ; i++) + { + for (j=0 ; j<3 ; j++) + { + tag[i].pos[j] = LittleFloat (tag[i].pos[j]); + tag[i].rot[0][j] = LittleFloat (tag[i].rot[0][j]); + tag[i].rot[1][j] = LittleFloat (tag[i].rot[1][j]); + tag[i].rot[2][j] = LittleFloat (tag[i].rot[2][j]); + } + } + + // swap all the surfaces + surf = (md3surface_t *)((byte *)header + header->ofssurfs); + for (i=0 ; inumsurfs ; i++) + { + surf->ident = LittleLong (surf->ident); + surf->flags = LittleLong (surf->flags); + surf->numframes = LittleLong (surf->numframes); + if (surf->numframes != header->numframes) + Sys_Error ("Mod_LoadQ3Model: number of frames don't match in %s", md3name); + + surf->numshaders = LittleLong (surf->numshaders); + if (surf->numshaders <= 0) + Sys_Error ("Mod_LoadQ3Model: model %s has no shaders", md3name); + else if (surf->numshaders > MAXMD3SHADERS) + Sys_Error ("Mod_LoadQ3Model: model %s has too many shaders", md3name); + + surf->numverts = LittleLong (surf->numverts); + if (surf->numverts <= 0) + Sys_Error ("Mod_LoadQ3Model: model %s has no vertices", md3name); + else if (surf->numverts > MAXMD3VERTS) + Sys_Error ("Mod_LoadQ3Model: model %s has too many vertices", md3name); + + surf->numtris = LittleLong (surf->numtris); + if (surf->numtris <= 0) + Sys_Error ("Mod_LoadQ3Model: model %s has no triangles", md3name); + else if (surf->numtris > MAXMD3TRIS) + Sys_Error ("Mod_LoadQ3Model: model %s has too many triangles", md3name); + + surf->ofstris = LittleLong (surf->ofstris); + surf->ofsshaders = LittleLong (surf->ofsshaders); + surf->ofstc = LittleLong (surf->ofstc); + surf->ofsverts = LittleLong (surf->ofsverts); + surf->ofsend = LittleLong (surf->ofsend); + + // swap all the shaders + shader = (md3shader_t *)((byte *)surf + surf->ofsshaders); + for (j=0 ; jnumshaders ; j++) + shader[j].index = LittleLong (shader[j].index); + + // swap all the triangles + tris = (md3triangle_t *)((byte *)surf + surf->ofstris); + for (j=0 ; jnumtris ; j++) + { + tris[j].indexes[0] = LittleLong (tris[j].indexes[0]); + tris[j].indexes[1] = LittleLong (tris[j].indexes[1]); + tris[j].indexes[2] = LittleLong (tris[j].indexes[2]); + } + + // swap all the texture coords + tc = (md3tc_t *)((byte *)surf + surf->ofstc); + for (j=0 ; jnumverts ; j++) + { + tc[j].s = LittleFloat (tc[j].s); + tc[j].t = LittleFloat (tc[j].t); + } + + // swap all the vertices + vert = (md3vert_t *)((byte *)surf + surf->ofsverts); + for (j=0 ; j < surf->numverts * surf->numframes ; j++) + { + vert[j].vec[0] = LittleShort (vert[j].vec[0]); + vert[j].vec[1] = LittleShort (vert[j].vec[1]); + vert[j].vec[2] = LittleShort (vert[j].vec[2]); + vert[j].normal = LittleShort (vert[j].normal); + } + + // find the next surface + surf = (md3surface_t *)((byte *)surf + surf->ofsend); + } + +// allocate extra size for structures different in memory + surf = (md3surface_t *)((byte *)header + header->ofssurfs); + for (size = 0, i = 0 ; i < header->numsurfs ; i++) + { + size += surf->numshaders * sizeof(md3shader_mem_t); // shader containing texnum + size += surf->numverts * surf->numframes * sizeof(md3vert_mem_t); // floating point vertices + surf = (md3surface_t *)((byte *)surf + surf->ofsend); + } + + header = static_cast(Cache_Alloc (&mod->cache, com_filesize + size, loadname)); + if (!mod->cache.data) + return; + + memcpy (header, buffer, com_filesize); + base = com_filesize; + + mod->type = mod_md3; + mod->numframes = header->numframes; + + md3bboxmins[0] = md3bboxmins[1] = md3bboxmins[2] = 99999; + md3bboxmaxs[0] = md3bboxmaxs[1] = md3bboxmaxs[2] = -99999; + radiusmax = 0; + + frame = (md3frame_t *)((byte *)header + header->ofsframes); + for (i=0 ; inumframes ; i++) + { + for (j=0 ; j<3 ; j++) + { + md3bboxmins[j] = min(md3bboxmins[j], frame[i].mins[j]); + md3bboxmaxs[j] = max(md3bboxmaxs[j], frame[i].maxs[j]); + } + radiusmax = max(radiusmax, frame[i].radius); + } + VectorCopy (md3bboxmins, mod->mins); + VectorCopy (md3bboxmaxs, mod->maxs); + mod->radius = radiusmax; + +// load the skins + Mod_LoadAllQ3Skins (mod->name, header); + +// load the animation frames if loading the player model + if (!strcmp(mod->name, "progs/player/lower.md3")) + Mod_LoadQ3Animation (); + + surf = (md3surface_t *)((byte *)header + header->ofssurfs); + + for (i=0 ; inumsurfs; i++) + { + shader = (md3shader_t *)((byte *)surf + surf->ofsshaders); + surf->ofsshaders = base; + size = surf->numshaders; + for (j=0 ; jofsshaders); + + Q_strncpyz (memshader[j].name, shader->name, sizeof(memshader[j].name)); + memshader[j].index = shader->index; + + COM_StripExtension (COM_SkipPath(shader->name), basename); + + gl_texnum = 0; + texture_flag = 0; + Mod_LoadQ3ModelTexture (basename, texture_flag, &gl_texnum); + + memshader[j].gl_texnum = gl_texnum; + + shader++; + } + base += size * sizeof(md3shader_mem_t); + + vert = (md3vert_t *)((byte *)surf + surf->ofsverts); + surf->ofsverts = base; + size = surf->numverts * surf->numframes; + for (j=0 ; jofsverts); + + vertexes[j].oldnormal = vert->normal; + + vertexes[j].vec[0] = (float)vert->vec[0] * MD3_XYZ_SCALE; + vertexes[j].vec[1] = (float)vert->vec[1] * MD3_XYZ_SCALE; + vertexes[j].vec[2] = (float)vert->vec[2] * MD3_XYZ_SCALE; + + lat = ((vert->normal >> 8) & 0xff) * M_PI / 128.0f; + lng = (vert->normal & 0xff) * M_PI / 128.0f; + #ifdef PSP_VFPU + vertexes[j].normal[0] = vfpu_cosf(lat) * vfpu_sinf(lng); + vertexes[j].normal[1] = vfpu_sinf(lat) * vfpu_sinf(lng); + vertexes[j].normal[2] = vfpu_cosf(lng); + #else + vertexes[j].normal[0] = cos(lat) * sin(lng); + vertexes[j].normal[1] = sin(lat) * sin(lng); + vertexes[j].normal[2] = cos(lng); + #endif + + vectoangles (vertexes[j].normal, ang); + vertexes[j].anorm_pitch = ang[0] * 256 / 360; + vertexes[j].anorm_yaw = ang[1] * 256 / 360; + + vert++; + } + base += size * sizeof(md3vert_mem_t); + + surf = (md3surface_t *)((byte *)surf + surf->ofsend); + } +} +//============================================================================== + +/* +================= +Mod_LoadSpriteFrame +================= +*/ +void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum, int version, byte *palette) +{ + dspriteframe_t *pinframe; + mspriteframe_t *pspriteframe; + int width, height, size, origin[2]; + char name[64], sprite[64], sprite2[64]; + + pinframe = (dspriteframe_t *)pin; + + width = LittleLong (pinframe->width); + height = LittleLong (pinframe->height); + size = width * height; + + pspriteframe = static_cast(Hunk_AllocName (sizeof (mspriteframe_t),loadname)); + + Q_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); + + if (version == SPRITE_VERSION) + { + COM_StripExtension(loadmodel->name, sprite); + sprintf (sprite2, "%s_%i", sprite, framenum); + pspriteframe->gl_texturenum = loadtextureimage (sprite2, 0, 0, qfalse, GU_LINEAR); + + if (pspriteframe->gl_texturenum == 0)// did not find a matching TGA... + { + pspriteframe->gl_texturenum = GL_LoadTexture (name, width, height, (byte *)(pinframe + 1), qtrue, GU_LINEAR, 0); + } + } + else if (version == SPRITE32_VERSION) + { + size *= 4; + pspriteframe->gl_texturenum = GL_LoadImages (name, width, height, (byte *)(pinframe + 1), qtrue, GU_LINEAR, 0, 4); + } + else if (version == SPRITEHL_VERSION) + { + pspriteframe->gl_texturenum = + GL_LoadPalTex (name, width, height, (byte *)(pinframe + 1), qtrue, GU_LINEAR, 0, palette, PAL_RGBA); + Con_Printf("HL Sprite TEX - OK\n"); + } + else + { + Sys_Error("Mod_LoadSpriteFrame: Non sprite type"); + } + + /* Crow_bar Memory Leak Fixed */ + mapTextureNameList.push_back(pspriteframe->gl_texturenum); + /* Crow_bar Memory Leak Fixed */ + + return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size); +} + + +/* +================= +Mod_LoadSpriteGroup +================= +*/ +void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum, int version, byte *palette) +{ + 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 = static_cast(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 = static_cast(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, version, palette); + } + + 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; + byte palette[256*4]; + int sptype; + + const unsigned char *datain; + datain = (unsigned char *)buffer; + + version = LittleLong (((dsprite_t *)buffer)->version); + + switch (version) + { + case SPRITE_VERSION: + case SPRITEHL_VERSION: + case SPRITE32_VERSION: + break; + default: + Sys_Error ("%s has wrong version number (%i should be %i(q1),%i(hl),%i(dp))", mod->name, version, SPRITE_VERSION, SPRITEHL_VERSION, SPRITE32_VERSION); + } + + if (version == SPRITEHL_VERSION) //Not work + { + Con_Printf("Half_Life sprite begin loading\n"); + int i, rendermode; + const unsigned char *in; + dspritehl_t *pinhlsprite; + + pinhlsprite = (dspritehl_t *)datain; + datain += sizeof(dspritehl_t); + + numframes = LittleLong (pinhlsprite->numframes); + + size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); + + psprite = static_cast(Hunk_AllocName (size, loadname)); + + mod->cache.data = psprite; + + psprite->numframes = LittleLong (pinhlsprite->numframes); + psprite->type = LittleLong (pinhlsprite->type); + mod->synctype = (synctype_t)LittleLong (pinhlsprite->synctype); + rendermode = LittleLong (pinhlsprite->texFormat); + + in = datain; + datain += 2; + i = in[0] + in[1] * 256; + if (i != 256) + Host_Error ("Mod_LoadSpriteModel: unexpected number of palette colors %i (should be 256)", i); + in = datain; + datain += 768; + + switch(rendermode) + { + case SPR_NORMAL: + for (i = 0;i < 256;i++) + { + palette[i*4+2] = in[i*3+0]; + palette[i*4+1] = in[i*3+1]; + palette[i*4+0] = in[i*3+2]; + palette[i*4+3] = 255; + } + break; + case SPR_ADDITIVE: + for (i = 0;i < 256;i++) + { + palette[i*4+2] = in[i*3+0]; + palette[i*4+1] = in[i*3+1]; + palette[i*4+0] = in[i*3+2]; + palette[i*4+3] = 255; + } + // also passes additive == true to Mod_Sprite_SharedSetup + break; + case SPR_INDEXALPHA: + for (i = 0;i < 256;i++) + { + palette[i*4+2] = in[765]; + palette[i*4+1] = in[766]; + palette[i*4+0] = in[767]; + palette[i*4+3] = i; + in += 3; + } + break; + case SPR_ALPHATEST: + for (i = 0;i < 256;i++) + { + palette[i*4+2] = in[i*3+0]; + palette[i*4+1] = in[i*3+1]; + palette[i*4+0] = in[i*3+2]; + palette[i*4+3] = 255; + } + palette[255*4+0] = palette[255*4+1] = palette[255*4+2] = palette[255*4+3] = 0; + // should this use alpha test or alpha blend? (currently blend) + break; + default: + Host_Error("Mod_LoadSpriteModel: unknown texFormat (%i, should be 0, 1, 2, or 3)", i); + return; + } + + psprite->maxwidth = LittleLong (pinhlsprite->width); + psprite->maxheight = LittleLong (pinhlsprite->height); + psprite->beamlength = LittleFloat (pinhlsprite->beamlength); + + pframetype = (dspriteframetype_t *)(pinhlsprite + 1); + Con_Printf("Half_Life sprite end loading\n"); + } + else + { + dsprite_t *pinqsprite; + pinqsprite = (dsprite_t *)datain; + datain += sizeof(dsprite_t); + + sptype = LittleLong (pinqsprite->type); + + numframes = LittleLong (pinqsprite->numframes); + + size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); + + psprite = static_cast(Hunk_AllocName (size, loadname)); + + mod->cache.data = psprite; + psprite->type = sptype; + + mod->synctype = static_cast(LittleLong (pinqsprite->synctype)); + + psprite->numframes = numframes; + psprite->maxwidth = LittleLong (pinqsprite->width); + psprite->maxheight = LittleLong (pinqsprite->height); + psprite->beamlength = LittleFloat (pinqsprite->beamlength); + + pframetype = (dspriteframetype_t *)(pinqsprite + 1); + } + + 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) + { + Con_Printf ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes); + } + + mod->numframes = numframes; + + for (i=0 ; i(LittleFloat(pframetype->type)); + psprite->frames[i].type = frametype; + + if (frametype == SPR_SINGLE) + { + pframetype = (dspriteframetype_t *) + Mod_LoadSpriteFrame (pframetype + 1, + &psprite->frames[i].frameptr, i, version, palette); + } + else + { + pframetype = (dspriteframetype_t *) + Mod_LoadSpriteGroup (pframetype + 1, + &psprite->frames[i].frameptr, i, version, palette); + } + } + + mod->type = mod_sprite; +} + +/* +================= +Mod_LoadQ2SpriteModel +By Crow_bar +================= +*/ +qboolean Mod_LoadQ2SpriteModel (model_t *mod, void *buffer) +{ + int i; + int version; + dmd2sprite_t *pin; + msprite_t *psprite; + int numframes; + int size; + dmd2sprframe_t *pframetype; + mspriteframe_t *frame; + float origin[2]; + int hunkstart; + + hunkstart = Hunk_LowMark(); + + pin = (dmd2sprite_t *)buffer; + + version = LittleLong (pin->version); + if (version != SPRITE2_VERSION) + { + Sys_Error ("%s has wrong version number " + "(%i should be %i)", mod->name, version, SPRITE2_VERSION); + return qfalse; + } + + numframes = LittleLong (pin->numframes); + + size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); + + psprite = static_cast(Hunk_AllocName (size, loadname)); + + mod->cache.data = psprite; + + psprite->type = SPR_VP_PARALLEL; + psprite->maxwidth = 1; + psprite->maxheight = 1; + psprite->beamlength = 1; + mod->synctype = static_cast(1); + 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); + Hunk_FreeToLowMark(hunkstart); + return qfalse; + } + + mod->numframes = numframes; + + pframetype = pin->frames; + + for (i=0 ; iframes[i].type = frametype; + + frame = psprite->frames[i].frameptr = static_cast(Hunk_AllocName(sizeof(mspriteframe_t), loadname)); + + frame->gl_texturenum = loadtextureimage (pframetype->name, 0, 0, qtrue, GU_LINEAR); + mapTextureNameList.push_back(frame->gl_texturenum); + + frame->width = LittleLong(pframetype->width); + frame->height = LittleLong(pframetype->height); + origin[0] = LittleLong (pframetype->origin_x); + origin[1] = LittleLong (pframetype->origin_y); +#if 0 //quake 2 negative translate + frame->up = -origin[1]; + frame->down = frame->height - origin[1]; + frame->left = -origin[0]; + frame->right = frame->width - origin[0]; +#else + frame->up = origin[1]; + frame->down = origin[1] - frame->height; + frame->left = -origin[0]; + frame->right = -origin[0] + frame->width; +#endif + + pframetype++; + } + + mod->type = mod_sprite; + + return qtrue; +} + +//============================================================================= + +/* +================ +Mod_Print +================ +*/ +extern "C" 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); + } +} + + diff --git a/source/psp/video_hardware_model.h b/source/psp/video_hardware_model.h new file mode 100644 index 0000000..399b760 --- /dev/null +++ b/source/psp/video_hardware_model.h @@ -0,0 +1,794 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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" + +#ifdef PSP_VFPU +#include +#endif + + +/* + +d*_t structures are on-disk representations +m*_t structures are in-memory + +*/ + +// entity effects + +#define EF_BLUELIGHT 1 +#define EF_MUZZLEFLASH 2 +#define EF_BRIGHTLIGHT 4 +#define EF_REDLIGHT 8 +#define EF_ORANGELIGHT 16 +#define EF_GREENLIGHT 32 +#define EF_LIGHT 64 +#define EF_NODRAW 128 +#define EF_BRIGHTFIELD 256 +#define EF_FULLBRIGHT 512 +#define EF_DARKLIGHT 1024 +#define EF_DARKFIELD 2048 +#define EF_PURPLELIGHT 4096 +#define EF_RAYRED 8196 // red trail for porter x2 +#define EF_RAYGREEN 16384 // gree trail for ray gun + +/* +============================================================================== + +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; + int dt_texturenum; +#ifdef FULLBRIGHT + int fullbright; +#endif + + 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 +} 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 short v[2]; + unsigned int cachededgeoffset; +} medge_t; + +typedef struct +{ + float vecs[2][4]; + float mipadjust; + texture_t *texture; + int flags; +} mtexinfo_t; + +//typedef vec_t vec2_t[2]; + +typedef struct glvert_s +{ + vec2_t st; + vec3_t xyz; +} glvert_t; + +typedef struct glpoly_s +{ + struct glpoly_s *next; + struct glpoly_s *chain; + int numverts; + int flags; // for SURF_UNDERWATER + struct glpoly_s *detail_chain; // next detail poly in chain + struct glpoly_s *caustics_chain; // next caustic poly in chain + vec3_t midpoint;//MHQuake + float fxofs; //MHQuake + // This is a variable sized array, and hence must be the last element in + // this structure. + // + // The array is (numverts * 2) in size. The first half are regular + // vertices, and the second half have copies of the first half's XYZs but + // keep the light map texture coordinates. This makes the vertices easier + // to render on the PSP. + // naievil -- changed from verts[1] to verts[2] in order to not have possible out of bounds error during compilation + // and create some sort of condition that causes the verts to not be assigned! + glvert_t verts[2]; +} 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; + int draw_this_frame; + +// lighting info + int dlightframe; + int dlightbits; + + int lightmaptexturenum; + byte styles[MAXLIGHTMAPS]; + int cached_light[MAXLIGHTMAPS]; // values currently used in lightmap + qboolean cached_dlight; // true if dynamic light in cache + 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 + + float 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 + + 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; + +// !!! 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; + 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? no! and it! + void *cachespot; // remove? no! and it! + 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; + +typedef struct mh2triangle_s +{ + int facesfront; + unsigned short vertindex[3]; + unsigned short stindex[3]; +} mh2triangle_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; + +// MOTO - ADQuake has MAXALIASVERTS set to 5120.. why? +#define MAXALIASVERTS 2048 +#define MAXALIASFRAMES 256 +#define MAXALIASTRIS 2048 +extern aliashdr_t *pheader; +extern stvert_t stverts[MAXALIASVERTS]; +extern mtriangle_t triangles[MAXALIASTRIS]; +extern mh2triangle_t h2triangles[MAXALIASTRIS]; +extern trivertx_t *poseverts[MAXALIASFRAMES]; + +//=================================================================== + +// +// Whole model +// + +typedef enum +{ +mod_brush, +mod_sprite, +mod_alias, +mod_halflife, +mod_md3 +} +modtype_t; + +#define EF_ROCKET 1 // leave a trail +#define EF_GRENADE 2 // leave a trail +#define EF_GIB 4 // leave a trail +#define EF_ROTATE 8 // rotate (bonus items) +#define EF_TRACER 16 // green split trail +#define EF_ZOMGIB 32 // small blood trail +#define EF_TRACER2 64 // orange split trail + rotate +#define EF_TRACER3 128 // purple trail +#define EF_Q3TRANS 256 // Q3 model containing transparent surface(s) + +/* +======================================================================== +.MD2 triangle model file format +======================================================================== +*/ + +// LordHavoc: grabbed this from the Q2 utility source, +// renamed a things to avoid conflicts + +#define MD2IDALIASHEADER (('2'<<24)+('P'<<16)+('D'<<8)+'I') +#define MD2ALIAS_VERSION 8 + +#define MD2MAX_TRIANGLES 4096 +#define MD2MAX_VERTS 2048 +#define MD2MAX_FRAMES 512 +#define MD2MAX_SKINS 32 +#define MD2MAX_SKINNAME 64 +// sanity checking size +#define MD2MAX_SIZE (1024*4200) + +typedef struct +{ + short s; + short t; +} md2stvert_t; + +typedef struct +{ + short index_xyz[3]; + short index_st[3]; +} md2triangle_t; + +typedef struct +{ + byte v[3]; // scaled byte to fit in frame mins/maxs + byte lightnormalindex; +} md2trivertx_t; + +#define MD2TRIVERTX_V0 0 +#define MD2TRIVERTX_V1 1 +#define MD2TRIVERTX_V2 2 +#define MD2TRIVERTX_LNI 3 +#define MD2TRIVERTX_SIZE 4 + +typedef struct +{ + float scale[3]; // multiply byte verts by this + float translate[3]; // then add this + char name[16]; // frame name from grabbing + md2trivertx_t verts[1]; // variable sized +} md2frame_t; + + +// the glcmd format: +// a positive integer starts a tristrip command, followed by that many +// vertex structures. +// a negative integer starts a trifan command, followed by -x vertexes +// a zero indicates the end of the command list. +// a vertex consists of a floating point s, a floating point t, +// and an integer vertex index. + + +typedef struct +{ + int ident; + int version; + + int skinwidth; + int skinheight; + int framesize; // byte size of each frame + + int num_skins; + int num_xyz; + int num_st; // greater than num_xyz for seams + int num_tris; + int num_glcmds; // dwords in strip/fan command list + int num_frames; + + int ofs_skins; // each skin is a MAX_SKINNAME string + int ofs_st; // byte offset from start for stverts + int ofs_tris; // offset for dtriangles + int ofs_frames; // offset for first frame + int ofs_glcmds; + int ofs_end; // end of file + + int gl_texturenum[MAX_SKINS]; +} md2_t; + +#define ALIASTYPE_MDL 1 +#define ALIASTYPE_MD2 2 + +//============================================================================== +//===================================Q3 Models================================== +//============================================================================== + +#define MD3IDHEADER (('3'<<24)+('P'<<16)+('D'<<8)+'I') +#define MD3_VERSION 15 + +typedef struct +{ + int ident; + int version; + char name[MAX_QPATH]; + int flags; + int numframes; + int numtags; + int numsurfs; + int numskins; + int ofsframes; + int ofstags; + int ofssurfs; + int ofsend; +} md3header_t; + +typedef struct +{ + vec3_t mins, maxs; + vec3_t pos; + float radius; + char name[16]; +} md3frame_t; + +typedef struct +{ + char name[MAX_QPATH]; + vec3_t pos; + vec3_t rot[3]; +} md3tag_t; + +typedef struct +{ + int ident; + char name[MAX_QPATH]; + int flags; + int numframes; + int numshaders; + int numverts; + int numtris; + int ofstris; + int ofsshaders; + int ofstc; + int ofsverts; + int ofsend; +} md3surface_t; + +typedef struct +{ + char name[MAX_QPATH]; + int index; +} md3shader_t; + +typedef struct +{ + char name[MAX_QPATH]; + int index; + int gl_texnum, fb_texnum; +} md3shader_mem_t; + +typedef struct +{ + int indexes[3]; +} md3triangle_t; + +typedef struct +{ + float s, t; +} md3tc_t; + +typedef struct +{ + short vec[3]; + unsigned short normal; +} md3vert_t; + +typedef struct +{ + vec3_t vec; + vec3_t normal; + byte anorm_pitch, anorm_yaw; + unsigned short oldnormal; // needed for normal lighting +} md3vert_mem_t; + +#define MD3_XYZ_SCALE (1.0 / 64) + +#define MAXMD3FRAMES 1024 +#define MAXMD3TAGS 16 +#define MAXMD3SURFS 32 +#define MAXMD3SHADERS 256 +#define MAXMD3VERTS 4096 +#define MAXMD3TRIS 8192 + +typedef struct animdata_s +{ + int offset; + int num_frames; + int loop_frames; + float interval; +} animdata_t; + +typedef enum animtype_s +{ + both_death1, both_death2, both_death3, both_dead1, both_dead2, both_dead3, + torso_attack, torso_attack2, torso_stand, torso_stand2, + legs_walk,legs_run, legs_idle, + NUM_ANIMTYPES +} animtype_t; + +extern animdata_t anims[NUM_ANIMTYPES]; + +//============================================================================== + +// some models are special +typedef enum +{ + MOD_NORMAL, + MOD_PLAYER, + MOD_EYES, + MOD_FLAME, + MOD_THUNDERBOLT, + MOD_WEAPON, + MOD_LAVABALL, + MOD_SPIKE, + MOD_SHAMBLER, + MOD_SPR, + MOD_SPR32, +// MOD_GKEY, +// MOD_SKEY, +} modhint_t; + + + +typedef struct model_s +{ + char name[MAX_QPATH]; + qboolean needload; // bmodels and sprites don't cache normally + + modhint_t modhint; + + modtype_t type; + int aliastype; // LordHavoc: Q2 model support + int numframes; + synctype_t synctype; + + int flags; + +// +// volume occupied by the model graphics +// + vec3_t mins, maxs; + float radius; + +// +// solid volume for clipping +// + qboolean 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; + dclipnode_t *clipnodes; + + int nummarksurfaces; + msurface_t **marksurfaces; + + hull_t hulls[MAX_MAP_HULLS]; + + int numtextures; + texture_t **textures; + + byte *visdata; + byte *lightdata; + char *entities; + int bspversion; + qboolean isworldmodel; +// +// additional model data +// + cache_user_t cache; // only access through Mod_Extradata + +} model_t; + +/* +============================================================================== + + .BSP Q2 file format + +============================================================================== +*/ + +#define kIDBSPHEADER (('P'<<24)+('S'<<16)+('B'<<8)+'I') + // little-endian "IBSP" +#define Q2_BSPVERSION 38 + + +// upper design bounds +// leaffaces, leafbrushes, planes, and verts are still bounded by +// 16 bit short limits +#define Q2_MAX_MAP_MODELS 1024 +#define Q2_MAX_MAP_BRUSHES 8192 +#define Q2_MAX_MAP_ENTITIES 2048 +#define Q2_MAX_MAP_ENTSTRING 0x40000 +#define Q2_MAX_MAP_TEXINFO 8192 + +#define Q2_MAX_MAP_AREAS 256 +#define Q2_MAX_MAP_AREAPORTALS 1024 +#define Q2_MAX_MAP_PLANES 65536 +#define Q2_MAX_MAP_NODES 65536 +#define Q2_MAX_MAP_BRUSHSIDES 65536 +#define Q2_MAX_MAP_LEAFS 65536 +#define Q2_MAX_MAP_VERTS 65536 +#define Q2_MAX_MAP_FACES 65536 +#define Q2_MAX_MAP_LEAFFACES 65536 +#define Q2_MAX_MAP_LEAFBRUSHES 65536 +#define Q2_MAX_MAP_PORTALS 65536 +#define Q2_MAX_MAP_EDGES 128000 +#define Q2_MAX_MAP_SURFEDGES 256000 +#define Q2_MAX_MAP_LIGHTING 0x200000 +#define Q2_MAX_MAP_VISIBILITY 0x100000 + +// key / value pair sizes + +#define Q2_MAX_KEY 32 +#define Q2_MAX_VALUE 1024 + +#define Q2_LUMP_ENTITIES 0 +#define Q2_LUMP_PLANES 1 +#define Q2_LUMP_VERTEXES 2 +#define Q2_LUMP_VISIBILITY 3 +#define Q2_LUMP_NODES 4 +#define Q2_LUMP_TEXINFO 5 +#define Q2_LUMP_FACES 6 +#define Q2_LUMP_LIGHTING 7 +#define Q2_LUMP_LEAFS 8 +#define Q2_LUMP_LEAFFACES 9 +#define Q2_LUMP_LEAFBRUSHES 10 +#define Q2_LUMP_EDGES 11 +#define Q2_LUMP_SURFEDGES 12 +#define Q2_LUMP_MODELS 13 +#define Q2_LUMP_BRUSHES 14 +#define Q2_LUMP_BRUSHSIDES 15 +#define Q2_LUMP_POP 16 +#define Q2_LUMP_AREAS 17 +#define Q2_LUMP_AREAPORTALS 18 +#define Q2_HEADER_LUMPS 19 + +//============================================================================= + +//============================================================================ + + +void Mod_Init (void); +void Mod_ClearAll (void); +model_t *Mod_ForName (char *name, qboolean 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); + +void CL_AddDecal (vec3_t n, vec3_t pt, int type); +#endif // __MODEL__ diff --git a/source/psp/video_hardware_part.cpp b/source/psp/video_hardware_part.cpp new file mode 100644 index 0000000..f8f69ac --- /dev/null +++ b/source/psp/video_hardware_part.cpp @@ -0,0 +1,898 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007-2008 Crow_bar. + +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. + +*/ + +extern "C" +{ +#include "../quakedef.h" +} + +#define MAX_PARTICLES 2048 // default max # of particles at one + // time +#define ABSOLUTE_MIN_PARTICLES 512 // no fewer than this no matter what's + + // on the command line +int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61}; +int ramp2[8] = {0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x68, 0x66}; +int ramp3[8] = {0x6d, 0x6b, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01}; + +particle2_t *active_particles, *free_particles; + +particle2_t *particles; +int r_numparticles; + +vec3_t r_pright, r_pup, r_ppn; + +/* +=============== +R_InitParticles +=============== +*/ +void R_Init_Classic_Particles (void) +{ + int i; + + i = COM_CheckParm ("-particles"); + + if (i) + { + r_numparticles = (int)(Q_atoi(com_argv[i+1])); + if (r_numparticles < ABSOLUTE_MIN_PARTICLES) + r_numparticles = ABSOLUTE_MIN_PARTICLES; + } + else + { + r_numparticles = MAX_PARTICLES; + } + + particles = (particle2_t *) + Hunk_AllocName (r_numparticles * sizeof(particle2_t), "particles"); + +} + +void R_InitParticles (void) +{ + R_Init_Classic_Particles (); + QMB_InitParticles (); +} + +void R_DarkFieldParticles (entity_t *ent) +{ + int i, j, k; + particle2_t *p; + float vel; + vec3_t dir; + vec3_t org; + + org[0] = ent->origin[0]; + org[1] = ent->origin[1]; + org[2] = ent->origin[2]; + for (i=-16 ; i<16 ; i+=8) + for (j=-16 ; j<16 ; j+=8) + for (k=0 ; k<32 ; k+=8) + { + 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 = 150 + rand()%6; + 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_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_Entity_Classic_Particles (entity_t *ent) +{ + int count; + int i; + particle2_t *p; + float angle; + float sr, sp, sy, cr, cp, cy; + vec3_t forward; + float dist; + + dist = 64; + count = 50; + +if (!avelocities[0][0]) +{ +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; + } +} + +void R_EntityParticles (entity_t *ent) +{ + + if (qmb_initialized && r_part_trails.value) + QMB_EntityParticles (ent); + else + R_Entity_Classic_Particles (ent); +} + +/* +=============== +R_Clear_Classic_Particles +=============== +*/ +void R_Clear_Classic_Particles (void) +{ + int i; + + free_particles = &particles[0]; + active_particles = NULL; + + for (i=0 ;i= (sizeof(line) - 2)) + { + Sys_Error("Line buffer overflow when reading point file"); + } + + if (!Sys_FileRead(f, &line[chars++], 1) != 1) + { + break; + } + } + while (line[chars - 1] != '\n'); + line[chars] = '\0'; + + r = sscanf (line, "%f %f %f\n", &org[0], &org[1], &org[2]); + if (r != 3) + break; + c++; + + if (!free_particles) + { + Con_Printf ("Not enough free particles\n"); + break; + } + p = free_particles; + free_particles = p->next; + 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); + } + + Sys_FileClose(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_Classic_ParticleExplosion (vec3_t org) +{ + int i, j; + particle2_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 + 5; + p->color = ramp1[0]; + p->ramp = rand()&3; + if (i & 1) + { + p->type = pt_explode; + for (j=0 ; j<3 ; j++) + { + p->org[j] = org[j] + ((rand()%32)-16); + p->vel[j] = (rand()%512)-256; + } + } + else + { + p->type = pt_explode2; + for (j=0 ; j<3 ; j++) + { + p->org[j] = org[j] + ((rand()%32)-16); + p->vel[j] = (rand()%512)-256; + } + } + } +} + +void R_ParticleExplosion (vec3_t org) +{ + if (!qmb_initialized) + R_Classic_ParticleExplosion (org); + else + QMB_ParticleExplosion (org); +} + +/* +=============== +R_ParticleExplosion2 + +=============== +*/ +void R_ParticleExplosion2 (vec3_t org, int colorStart, int colorLength) +{ + int i, j; + particle2_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_Classic_BlobExplosion (vec3_t org) +{ + int i, j; + particle2_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; + } + } + } +} + +void R_BlobExplosion (vec3_t org) +{ + if (!qmb_initialized) + R_Classic_BlobExplosion (org); + else + QMB_BlobExplosion (org); +} + +/* +=============== +Run_Classic_ParticleEffect + +=============== +*/ +void Run_Classic_ParticleEffect (vec3_t org, vec3_t dir, int color, int count) +{ + int i, j; + particle2_t *p; + + for (i=0 ; inext; + p->next = active_particles; + active_particles = p; + + if (count == 1024) + { // rocket explosion + p->die = cl.time + 5; + p->color = ramp1[0]; + p->ramp = rand()&3; + if (i & 1) + { + p->type = pt_explode; + for (j=0 ; j<3 ; j++) + { + p->org[j] = org[j] + ((rand()%32)-16); + p->vel[j] = (rand()%512)-256; + } + } + else + { + p->type = pt_explode2; + 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; + } + } + } +} + +#define RunParticleEffect(var, org, dir, color, count) \ + if (qmb_initialized && r_part_##var.value) \ + QMB_RunParticleEffect (org, dir, color, count); \ + else \ + Run_Classic_ParticleEffect (org, dir, color, count); + + +void R_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count) +{ + if (color == 73 || color == 225) + { + RunParticleEffect(blood, org, dir, color, count); + return; + } + + switch (count) + { + case 10: + case 20: + case 30: + RunParticleEffect(sparks, org, dir, color, count); + break; + default: + RunParticleEffect(gunshots, org, dir, color, count); + } +} + +/* +=============== +R_LavaSplash + +=============== +*/ +void R_Classic_LavaSplash (vec3_t org) +{ + int i, j, k; + particle2_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); + } +} + +void R_LavaSplash (vec3_t org) +{ + if (!qmb_initialized) + R_Classic_LavaSplash (org); + else + QMB_LavaSplash(org); +} + +/* +=============== +R_TeleportSplash + +=============== +*/ +void R_Classic_TeleportSplash (vec3_t org) +{ + int i, j, k; + particle2_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); + } +} + +void R_TeleportSplash (vec3_t org) +{ + if (!qmb_initialized) + R_Classic_TeleportSplash (org); + else + QMB_TeleportSplash(org); + +} + +void R_Rocket_Classic_Trail (vec3_t start, vec3_t end, int type) +{ + vec3_t vec; + float len; + int j; + particle2_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); + 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 smoke + p->ramp = (rand()&3) + 2; + 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); + } +} + +void R_RocketTrail (vec3_t start, vec3_t end, int type) +{ + + if (qmb_initialized && r_part_trails.value) + QMB_RocketTrail (start, end, (trail_type_t)type); + else + R_Rocket_Classic_Trail (start, end, type); +} + +/* +=============== +R_DrawParticles +=============== +*/ +extern cvar_t sv_gravity; + +#define PART_BUFFER_SIZE 128 +void R_Classic_DrawParticles (void) +{ + particle2_t *p, *kill; + float grav; + int i; + float time2, time3; + float time1; + float dvel; + float frametime; + vec3_t up, right; + float scale; + + int part_buffer_size = PART_BUFFER_SIZE; + psp_particle* part_buffer = NULL; + part_buffer = D_CreateBuffer(part_buffer_size); + + VectorScale (vup, 1.25, up); + VectorScale (vright, 1.25, right); + + D_StartParticles (); +/* + VectorScale (vright, xscaleshrink, r_pright); + VectorScale (vup, yscaleshrink, r_pup); + VectorCopy (vright, r_pright); + VectorCopy (vup, r_pup); +*/ + VectorCopy (vpn, r_ppn); + + frametime = cl.time - cl.oldtime; + time3 = frametime * 15; + time2 = frametime * 10; // 15; + 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; + } + + // 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; + else + scale = 1 + scale * 0.004; + + // D_DrawParticle (p, up, right, scale); + int rv = D_DrawParticleBuffered(part_buffer,p, up, right, scale); + if (rv == -1) + part_buffer = D_CreateBuffer(part_buffer_size); + + 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 = -1; + else + p->color = ramp3[(int)p->ramp]; + p->vel[2] += grav; + break; + + case pt_explode: + p->ramp += time2; + if (p->ramp >=8) + p->die = -1; + 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 = -1; + 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; + } + } + + D_DeleteBuffer(part_buffer); + D_EndParticles (); +} + +void R_DrawParticles (void) +{ +R_Classic_DrawParticles (); +QMB_DrawParticles (); +} + diff --git a/source/psp/video_hardware_resample.cpp b/source/psp/video_hardware_resample.cpp new file mode 100644 index 0000000..bfe869b --- /dev/null +++ b/source/psp/video_hardware_resample.cpp @@ -0,0 +1,463 @@ +#include +#include + +extern "C" +{ +#include "../quakedef.h" +} + +#define LERPBYTE(i) r = row1[i]; out[i] = (byte) ((((row2[i] - r) * lerp) >> 16) + r) +#define NOLERPBYTE(i) *out++ = inrow[f + i] + +/* +================ +Image_Resample32LerpLine +================ +*/ +static void Image_Resample32LerpLine (byte *in, byte *out, int inwidth, int outwidth) +{ + int j, xi, oldx = 0, f, fstep, endx, lerp; + + fstep = (int) (inwidth * 65536.0f / outwidth); + endx = (inwidth - 1); + for (j = 0, f = 0; j < outwidth; j++, f += fstep) { + xi = (int) f >> 16; + if (xi != oldx) { + in += (xi - oldx) * 4; + oldx = xi; + } + if (xi < endx) { + lerp = f & 0xFFFF; + *out++ = (byte) ((((in[4] - in[0]) * lerp) >> 16) + in[0]); + *out++ = (byte) ((((in[5] - in[1]) * lerp) >> 16) + in[1]); + *out++ = (byte) ((((in[6] - in[2]) * lerp) >> 16) + in[2]); + *out++ = (byte) ((((in[7] - in[3]) * lerp) >> 16) + in[3]); + } else { + *out++ = in[0]; + *out++ = in[1]; + *out++ = in[2]; + *out++ = in[3]; + } + } +} + +/* +================ +Image_Resample32 +================ +*/ +static void Image_Resample32 (void *indata, int inwidth, int inheight,void *outdata, int outwidth, int outheight, int quality) +{ + if (quality) + { + int i, j, r, yi, oldy, f, fstep, endy = (inheight - 1), lerp; + int inwidth4 = inwidth * 4, outwidth4 = outwidth * 4; + byte *inrow, *out, *row1, *row2, *memalloc; + + out = static_cast(outdata); + fstep = (int) (inheight * 65536.0f / outheight); + + memalloc = static_cast(malloc(2 * outwidth4)); + row1 = memalloc; + row2 = memalloc + outwidth4; + inrow = (byte *) indata; + oldy = 0; + Image_Resample32LerpLine (inrow, row1, inwidth, outwidth); + Image_Resample32LerpLine (inrow + inwidth4, row2, inwidth, outwidth); + for (i = 0, f = 0; i < outheight; i++, f += fstep) + { + yi = f >> 16; + if (yi < endy) + { + lerp = f & 0xFFFF; + if (yi != oldy) + { + inrow = (byte *) indata + inwidth4 * yi; + if (yi == oldy + 1) + memcpy(row1, row2, outwidth4); + else + Image_Resample32LerpLine (inrow, row1, inwidth, outwidth); + Image_Resample32LerpLine (inrow + inwidth4, row2, inwidth, outwidth); + oldy = yi; + } + j = outwidth - 4; + while(j >= 0) + { + LERPBYTE(0); LERPBYTE(1); LERPBYTE(2); LERPBYTE(3); + LERPBYTE(4); LERPBYTE(5); LERPBYTE(6); LERPBYTE(7); + LERPBYTE(8); LERPBYTE(9); LERPBYTE(10); LERPBYTE(11); + LERPBYTE(12); LERPBYTE(13); LERPBYTE(14); LERPBYTE(15); + out += 16; + row1 += 16; + row2 += 16; + j -= 4; + } + if (j & 2) + { + LERPBYTE(0); LERPBYTE(1); LERPBYTE(2); LERPBYTE(3); + LERPBYTE(4); LERPBYTE(5); LERPBYTE(6); LERPBYTE(7); + out += 8; + row1 += 8; + row2 += 8; + } + if (j & 1) + { + LERPBYTE(0); LERPBYTE(1); LERPBYTE(2); LERPBYTE(3); + out += 4; + row1 += 4; + row2 += 4; + } + row1 -= outwidth4; + row2 -= outwidth4; + } + else + { + if (yi != oldy) + { + inrow = (byte *) indata + inwidth4 * yi; + if (yi == oldy+1) + memcpy(row1, row2, outwidth4); + else + Image_Resample32LerpLine (inrow, row1, inwidth, outwidth); + oldy = yi; + } + memcpy(out, row1, outwidth4); + } + } + free(memalloc); + } + else + { + int i, j; + unsigned int frac, fracstep, *inrow, *out; + + out = static_cast(outdata); + + fracstep = inwidth * 0x10000 / outwidth; + for (i = 0; i < outheight; i++) { + inrow = (unsigned int*)((int *)indata + inwidth * (i * inheight / outheight)); + frac = fracstep >> 1; + j = outwidth - 4; + while (j >= 0) + { + out[0] = inrow[frac >> 16]; frac += fracstep; + out[1] = inrow[frac >> 16]; frac += fracstep; + out[2] = inrow[frac >> 16]; frac += fracstep; + out[3] = inrow[frac >> 16]; frac += fracstep; + out += 4; + j -= 4; + } + if (j & 2) + { + out[0] = inrow[frac >> 16]; frac += fracstep; + out[1] = inrow[frac >> 16]; frac += fracstep; + out += 2; + } + if (j & 1) + { + out[0] = inrow[frac >> 16]; frac += fracstep; + out += 1; + } + } + } +} + + +/* +================ +Image_Resample24LerpLine +================ +*/ +static void Image_Resample24LerpLine (byte *in, byte *out, int inwidth, int outwidth) { + int j, xi, oldx = 0, f, fstep, endx, lerp; + + fstep = (int) (inwidth * 65536.0f / outwidth); + endx = (inwidth - 1); + for (j = 0, f = 0; j < outwidth; j++, f += fstep) { + xi = (int) f >> 16; + if (xi != oldx) { + in += (xi - oldx) * 3; + oldx = xi; + } + if (xi < endx) { + lerp = f & 0xFFFF; + *out++ = (byte) ((((in[3] - in[0]) * lerp) >> 16) + in[0]); + *out++ = (byte) ((((in[4] - in[1]) * lerp) >> 16) + in[1]); + *out++ = (byte) ((((in[5] - in[2]) * lerp) >> 16) + in[2]); + } else { + *out++ = in[0]; + *out++ = in[1]; + *out++ = in[2]; + } + } +} + +/* +================ +Image_Resample24 +================ +*/ +static void Image_Resample24 (void *indata, int inwidth, int inheight, + void *outdata, int outwidth, int outheight, int quality) { + if (quality) { + int i, j, r, yi, oldy, f, fstep, endy = (inheight - 1), lerp; + int inwidth3 = inwidth * 3, outwidth3 = outwidth * 3; + byte *inrow, *out, *row1, *row2, *memalloc; + + out = static_cast(outdata); + fstep = (int) (inheight * 65536.0f / outheight); + + memalloc = static_cast(malloc(2 * outwidth3)); + row1 = memalloc; + row2 = memalloc + outwidth3; + inrow = (byte *) indata; + oldy = 0; + Image_Resample24LerpLine (inrow, row1, inwidth, outwidth); + Image_Resample24LerpLine (inrow + inwidth3, row2, inwidth, outwidth); + for (i = 0, f = 0; i < outheight; i++, f += fstep) { + yi = f >> 16; + if (yi < endy) { + lerp = f & 0xFFFF; + if (yi != oldy) { + inrow = (byte *) indata + inwidth3 * yi; + if (yi == oldy + 1) + memcpy(row1, row2, outwidth3); + else + Image_Resample24LerpLine (inrow, row1, inwidth, outwidth); + Image_Resample24LerpLine (inrow + inwidth3, row2, inwidth, outwidth); + oldy = yi; + } + j = outwidth - 4; + while(j >= 0) { + LERPBYTE(0); LERPBYTE(1); LERPBYTE(2); + LERPBYTE(3); LERPBYTE(4); LERPBYTE(5); + LERPBYTE(6); LERPBYTE(7); LERPBYTE(8); + LERPBYTE(9); LERPBYTE(10); LERPBYTE(11); + out += 12; + row1 += 12; + row2 += 12; + j -= 4; + } + if (j & 2) { + LERPBYTE(0); LERPBYTE(1); LERPBYTE(2); + LERPBYTE(3); LERPBYTE(4); LERPBYTE(5); + out += 6; + row1 += 6; + row2 += 6; + } + if (j & 1) { + LERPBYTE(0); LERPBYTE(1); LERPBYTE(2); + out += 3; + row1 += 3; + row2 += 3; + } + row1 -= outwidth3; + row2 -= outwidth3; + } + else + { + if (yi != oldy) { + inrow = (byte *) indata + inwidth3 * yi; + if (yi == oldy+1) + memcpy(row1, row2, outwidth3); + else + Image_Resample24LerpLine (inrow, row1, inwidth, outwidth); + oldy = yi; + } + memcpy(out, row1, outwidth3); + } + } + free(memalloc); + } + else + { + int i, j, f; + unsigned int frac, fracstep, inwidth3 = inwidth * 3; + byte *inrow, *out; + + out = static_cast(outdata); + + fracstep = inwidth * 0x10000 / outwidth; + for (i = 0; i < outheight; i++) { + inrow = (byte *) indata + inwidth3 * (i * inheight / outheight); + frac = fracstep >> 1; + j = outwidth - 4; + while (j >= 0) + { + f = (frac >> 16) * 3; NOLERPBYTE(0); NOLERPBYTE(1); NOLERPBYTE(2); frac += fracstep; + f = (frac >> 16) * 3; NOLERPBYTE(0); NOLERPBYTE(1); NOLERPBYTE(2); frac += fracstep; + f = (frac >> 16) * 3; NOLERPBYTE(0); NOLERPBYTE(1); NOLERPBYTE(2); frac += fracstep; + f = (frac >> 16) * 3; NOLERPBYTE(0); NOLERPBYTE(1); NOLERPBYTE(2); frac += fracstep; + j -= 4; + } + if (j & 2) + { + f = (frac >> 16) * 3; NOLERPBYTE(0); NOLERPBYTE(1); NOLERPBYTE(2); frac += fracstep; + f = (frac >> 16) * 3; NOLERPBYTE(0); NOLERPBYTE(1); NOLERPBYTE(2); frac += fracstep; + out += 2; + } + if (j & 1) + { + f = (frac >> 16) * 3; NOLERPBYTE(0); NOLERPBYTE(1); NOLERPBYTE(2); frac += fracstep; + out += 1; + + } + } + } +} + + +/* +================ +Image_Resample +================ +*/ +void Image_Resample (void *indata, int inwidth, int inheight,void *outdata, int outwidth, int outheight, int bpp, int quality) +{ + if (bpp == 4) + Image_Resample32(indata, inwidth, inheight, outdata, outwidth, outheight, quality); + else if (bpp == 3) + Image_Resample24(indata, inwidth, inheight, outdata, outwidth, outheight, quality); + else + Sys_Error("Image_Resample: unsupported bpp (%d)", bpp); +} + +/* +================ +Image_MipReduce +================ +*/ +void Image_MipReduce (byte *in, byte *out, int *width, int *height, int bpp) +{ + int x, y, nextrow; + + nextrow = *width * bpp; + + if (*width > 1) + { + *width >>= 1; + if (*height > 1) + { + + *height >>= 1; + if (bpp == 4) + { + for (y = 0; y < *height; y++) + { + for (x = 0; x < *width; x++) + { + out[0] = (byte) ((in[0] + in[4] + in[nextrow] + in[nextrow + 4]) >> 2); + out[1] = (byte) ((in[1] + in[5] + in[nextrow + 1] + in[nextrow + 5]) >> 2); + out[2] = (byte) ((in[2] + in[6] + in[nextrow + 2] + in[nextrow + 6]) >> 2); + out[3] = (byte) ((in[3] + in[7] + in[nextrow + 3] + in[nextrow + 7]) >> 2); + out += 4; + in += 8; + } + in += nextrow; + } + } + else if (bpp == 3) + { + for (y = 0; y < *height; y++) + { + for (x = 0; x < *width; x++) + { + out[0] = (byte) ((in[0] + in[3] + in[nextrow] + in[nextrow + 3]) >> 2); + out[1] = (byte) ((in[1] + in[4] + in[nextrow + 1] + in[nextrow + 4]) >> 2); + out[2] = (byte) ((in[2] + in[5] + in[nextrow + 2] + in[nextrow + 5]) >> 2); + out += 3; + in += 6; + } + in += nextrow; + } + } + else + { + Sys_Error("Image_MipReduce: unsupported bpp (%d)", bpp); + } + } + else + { + + if (bpp == 4) + { + for (y = 0; y < *height; y++) + { + for (x = 0; x < *width; x++) + { + out[0] = (byte) ((in[0] + in[4]) >> 1); + out[1] = (byte) ((in[1] + in[5]) >> 1); + out[2] = (byte) ((in[2] + in[6]) >> 1); + out[3] = (byte) ((in[3] + in[7]) >> 1); + out += 4; + in += 8; + } + } + } + else if (bpp == 3) + { + for (y = 0; y < *height; y++) + { + for (x = 0; x < *width; x++) + { + out[0] = (byte) ((in[0] + in[3]) >> 1); + out[1] = (byte) ((in[1] + in[4]) >> 1); + out[2] = (byte) ((in[2] + in[5]) >> 1); + out += 3; + in += 6; + } + } + } + else + { + Sys_Error("Image_MipReduce: unsupported bpp (%d)", bpp); + } + } + } + else if (*height > 1) + { + + *height >>= 1; + if (bpp == 4) + { + for (y = 0; y < *height; y++) + { + for (x = 0; x < *width; x++) + { + out[0] = (byte) ((in[0] + in[nextrow]) >> 1); + out[1] = (byte) ((in[1] + in[nextrow + 1]) >> 1); + out[2] = (byte) ((in[2] + in[nextrow + 2]) >> 1); + out[3] = (byte) ((in[3] + in[nextrow + 3]) >> 1); + out += 4; + in += 4; + } + in += nextrow; + } + } + else if (bpp == 3) + { + for (y = 0; y < *height; y++) + { + for (x = 0; x < *width; x++) + { + out[0] = (byte) ((in[0] + in[nextrow]) >> 1); + out[1] = (byte) ((in[1] + in[nextrow + 1]) >> 1); + out[2] = (byte) ((in[2] + in[nextrow + 2]) >> 1); + out += 3; + in += 3; + } + in += nextrow; + } + } + else + { + Sys_Error("Image_MipReduce: unsupported bpp (%d)", bpp); + } + } + else + { + Sys_Error("Image_MipReduce: Input texture has dimensions %dx%d", width, height); + } +} + + diff --git a/source/psp/video_hardware_resample.h b/source/psp/video_hardware_resample.h new file mode 100644 index 0000000..7b5a35e --- /dev/null +++ b/source/psp/video_hardware_resample.h @@ -0,0 +1,3 @@ +void Image_Resample (void *indata, int inwidth, int inheight,void *outdata, int outwidth, int outheight, int bpp, int quality); //resample from fuh quake +void ResampleTexture (unsigned *indata, int inwidth, int inheight, unsigned *outdata, int outwidth, int outheight, qboolean quality); //resample from joe quake +void Image_MipReduce (byte *in, byte *out, int *width, int *height, int bpp); //from fuh quake diff --git a/source/psp/video_hardware_screen.cpp b/source/psp/video_hardware_screen.cpp new file mode 100644 index 0000000..a3a2335 --- /dev/null +++ b/source/psp/video_hardware_screen.cpp @@ -0,0 +1,1458 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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.c -- master for refresh, status bar, console, chat, notify, etc + +extern "C" +{ +#include "../quakedef.h" +} + +/* + +background clear +rendering +turtle/net/ram icons +hud +centerprint / slow centerprint +notify lines +intermission / finale overlay +loading plaque +console +menu + +required background clears +required update regions + + +syncronous draw mode or async +One off screen buffer, with updates either copied or xblited +Need to double buffer? + + +async draw will require the refresh area to be cleared, because it will be +xblited, but sync draw can just ignore it. + +sync +draw + +CenterPrint () +SlowPrint () +Screen_Update (); +Con_Printf (); + +net +turn off messages option + +the refresh is allways rendered, unless the console is full screen + + +console is: + notify lines + half + full + + +*/ + + +int glx, gly, glwidth, glheight; + +// only the refresh window will be updated unless these variables are flagged +int scr_copytop; +int scr_copyeverything; + +float scr_con_current; +float scr_conlines; // lines of console to display + +float oldscreensize, oldfov; +cvar_t scr_coloredtext = {"scr_coloredtext","1", qtrue}; +cvar_t scr_fov = {"fov","80", qtrue}; // 10 - 170 +cvar_t scr_conspeed = {"scr_conspeed","300"}; +cvar_t scr_centertime = {"scr_centertime","2"}; +cvar_t scr_showram = {"showram","1"}; +cvar_t scr_showpause = {"showpause","1"}; +cvar_t scr_printspeed = {"scr_printspeed","8"}; +cvar_t scr_conheight = {"scr_conheight", "0.5"}; +cvar_t scr_loadscreen = {"scr_loadscreen","1", qtrue}; + + +cvar_t r_dithering = {"r_dithering","1",qtrue}; + +extern "C" cvar_t crosshair; + +qboolean scr_initialized; // ready to draw + +qpic_t *hitmark; +qpic_t *ls_ndu; +qpic_t *ls_warehouse; +qpic_t *ls_xmas; + +int scr_fullupdate; + +int loadingScreen; +int ShowBlslogo; + +int clearconsole; +int clearnotify; + + +viddef_t vid; // global video state + +extern "C" vrect_t scr_vrect; +vrect_t scr_vrect = {0}; + +qboolean scr_disabled_for_loading; +qboolean scr_drawloading; +float scr_disabled_time; + +qboolean block_drawing; + +extern int game_fps; + +void SCR_ScreenShot_f (void); + +/* +=============================================================================== + +CENTER PRINTING + +=============================================================================== +*/ + +char scr_centerstring[1024]; +float scr_centertime_start; // for slow victory printing +extern "C" float scr_centertime_off; +float scr_centertime_off = 0.0f; +int scr_center_lines; +int scr_erase_lines; +int scr_erase_center; + +/* +============== +SCR_CenterPrint + +Called for important messages that should stay in the center of the screen +for a few moments +============== +*/ +void SCR_CenterPrint (char *str) +{ + strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1); + scr_centertime_off = scr_centertime.value; + scr_centertime_start = cl.time; + +// count the number of lines for centering + scr_center_lines = 1; + while (*str) + { + if (*str == '\n') + scr_center_lines++; + str++; + } +} + + +void SCR_DrawCenterString (void) +{ + char *start; + int l; + int j; + int x, y; + int remaining; + + if (cl.stats[STAT_HEALTH] < 0) + return; +// the finale prints the characters one at a time + if (cl.intermission) + remaining = scr_printspeed.value * (cl.time - scr_centertime_start); + else + remaining = 9999; + + scr_erase_center = 0; + start = scr_centerstring; + + if (scr_center_lines <= 4) + y = vid.height*0.35; + else + y = 48; + + do + { + // scan the width of the line + for (l=0 ; l<40 ; l++) + if (start[l] == '\n' || !start[l]) + break; + x = (vid.width - l*8)/2; + for (j=0 ; j 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; + if (cl.stats[STAT_HEALTH] <= 0) + return; + + SCR_DrawCenterString (); +} +/* +=============================================================================== + +Press somthing printing + +=============================================================================== +*/ + +char scr_usestring[1024]; +float scr_usetime_off = 0.0f; +int button_pic_x; +extern qpic_t *b_circle; +extern qpic_t *b_square; +extern qpic_t *b_cross; +extern qpic_t *b_triangle; +extern qpic_t *b_left; +extern qpic_t *b_right; +extern qpic_t *b_up; +extern qpic_t *b_down; +extern qpic_t *b_lt; +extern qpic_t *b_rt; +extern qpic_t *b_start; +extern qpic_t *b_select; +extern qpic_t *b_home; + +/* +============== +SCR_UsePrint + +Similiar to above, but will also print the current button for the action. +============== +*/ + +qpic_t *GetButtonIcon (char *buttonname) +{ + int j; + int l; + char *b; + l = strlen(buttonname); + + for (j=0 ; j<256 ; j++) + { + b = keybindings[j]; + if (!b) + continue; + if (!strncmp (b, buttonname, l) ) + { + if (!strcmp(Key_KeynumToString(j), "UPARROW")) + return b_up; + else if (!strcmp(Key_KeynumToString(j), "DOWNARROW")) + return b_down; + else if (!strcmp(Key_KeynumToString(j), "LEFTARROW")) + return b_left; + else if (!strcmp(Key_KeynumToString(j), "RIGHTARROW")) + return b_right; + else if (!strcmp(Key_KeynumToString(j), "SELECT")) + return b_select; + else if (!strcmp(Key_KeynumToString(j), "HOME")) + return b_home; + else if (!strcmp(Key_KeynumToString(j), "TRIANGLE")) + return b_triangle; + else if (!strcmp(Key_KeynumToString(j), "CIRCLE")) + return b_circle; + else if (!strcmp(Key_KeynumToString(j), "CROSS")) + return b_cross; + else if (!strcmp(Key_KeynumToString(j), "SQUARE")) + return b_square; + else if (!strcmp(Key_KeynumToString(j), "LTRIGGER")) + return b_lt; + else if (!strcmp(Key_KeynumToString(j), "RTRIGGER")) + return b_rt; + } + } + return b_cross; +} + +char *GetUseButtonL () +{ + int j; + int l; + char *b; + l = strlen("+use"); + + for (j=0 ; j<256 ; j++) + { + b = keybindings[j]; + if (!b) + continue; + if (!strncmp (b, "+use", l) ) + { + if (!strcmp(Key_KeynumToString(j), "SELECT") || + !strcmp(Key_KeynumToString(j), "LTRIGGER") || + !strcmp(Key_KeynumToString(j), "RTRIGGER") || + !strcmp(Key_KeynumToString(j), "HOME")) + return " "; + else + return " "; + } + } + return " "; +} + +char *GetPerkName (int perk) +{ + switch (perk) + { + case 1: + return "Quick Revive"; + case 2: + return "Juggernog"; + case 3: + return "Speed Cola"; + case 4: + return "Double Tap"; + case 5: + return "Stamin-Up"; + case 6: + return "PhD Flopper"; + case 7: + return "Deadshot Daiquiri"; + case 8: + return "Mule Kick"; + default: + return "NULL"; + } +} + +void SCR_UsePrint (int type, int cost, int weapon) +{ + char s[128]; + + switch (type) + { + case 0://clear + strcpy(s, ""); + break; + case 1://door + strcpy(s, va("Hold %s to open Door [Cost:%i]\n", GetUseButtonL(), cost)); + button_pic_x = 5; + break; + case 2://debris + strcpy(s, va("Hold %s to remove Debris [Cost:%i]\n", GetUseButtonL(), cost)); + button_pic_x = 5; + break; + case 3://ammo + strcpy(s, va("Hold %s to buy Ammo for %s [Cost:%i]\n", GetUseButtonL(), pr_strings+sv_player->v.Weapon_Name_Touch, cost)); + button_pic_x = 5; + break; + case 4://weapon + strcpy(s, va("Hold %s to buy %s [Cost:%i]\n", GetUseButtonL(), pr_strings+sv_player->v.Weapon_Name_Touch, cost)); + button_pic_x = 5; + break; + case 5://window + strcpy(s, va("Hold %s to Rebuild Barrier\n", GetUseButtonL())); + button_pic_x = 5; + break; + case 6://box + strcpy(s, va("Hold %s to buy a Random Weapon [cost:%i]\n", GetUseButtonL(), cost)); + button_pic_x = 5; + break; + case 7://box take + strcpy(s, va("Press %s to take Weapon\n", GetUseButtonL())); + button_pic_x = 6; + break; + case 8://power + strcpy(s, "The Power must be Activated first\n"); + button_pic_x = 100; + break; + case 9://perk + strcpy(s, va("Hold %s to buy %s [Cost:%i]\n", GetUseButtonL(), GetPerkName(weapon), cost)); + button_pic_x = 5; + break; + case 10://turn on power + strcpy(s, va("Hold %s to Turn On the Power\n", GetUseButtonL())); + button_pic_x = 5; + break; + case 11://turn on trap + strcpy(s, va("Hold %s to Activate the Trap [Cost:%i]\n", GetUseButtonL(), cost)); + button_pic_x = 5; + break; + case 12://PAP + strcpy(s, va("Hold %s to Pack-a-Punch [Cost:%i]\n", GetUseButtonL(), cost)); + button_pic_x = 5; + break; + case 13://revive + strcpy(s, va("Hold %s to Fix your Code.. :)\n", GetUseButtonL())); + button_pic_x = 5; + break; + case 14://use teleporter (free) + strcpy(s, va("Hold %s to use Teleporter\n", GetUseButtonL())); + button_pic_x = 5; + break; + case 15://use teleporter (cost) + strcpy(s, va("Hold %s to use Teleporter [Cost:%i]\n", GetUseButtonL(), cost)); + button_pic_x = 5; + break; + case 16://tp cooldown + strcpy(s, "Teleporter is cooling down\n"); + button_pic_x = 100; + break; + case 17://link + strcpy(s, va("Hold %s to initiate link to pad\n", GetUseButtonL())); + button_pic_x = 5; + break; + case 18://no link + strcpy(s, "Link not active\n"); + button_pic_x = 100; + break; + case 19://finish link + strcpy(s, va("Hold %s to link pad with core\n", GetUseButtonL())); + button_pic_x = 5; + break; + case 20://buyable ending + strcpy(s, va("Hold %s to End the Game [Cost:%i]\n", GetUseButtonL(), cost)); + button_pic_x = 5; + break; + default: + Con_Printf ("No type defined in engine for useprint\n"); + break; + } + + strncpy (scr_usestring, va(s), sizeof(scr_usestring)-1); + scr_usetime_off = 0.1; +} + + +void SCR_DrawUseString (void) +{ + int l; + int x, y; + + if (cl.stats[STAT_HEALTH] < 0) + return; +// the finale prints the characters one at a time + + y = vid.height*0.70; + l = strlen (scr_usestring); + x = (vid.width - l*8)/2; + + Draw_String (x, y, scr_usestring); + Draw_Pic (x + button_pic_x*8, y, GetButtonIcon("+use")); +} + +void SCR_CheckDrawUseString (void) +{ + scr_copytop = 1; + + scr_usetime_off -= host_frametime; + + if (scr_usetime_off <= 0 && !cl.intermission) + return; + if (key_dest != key_game) + return; + if (cl.stats[STAT_HEALTH] <= 0) + return; + + SCR_DrawUseString (); +} + +//============================================================================= + +/* +==================== +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/tanf(fov_x/360*M_PI); + + a = atanf(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) +{ + float size; + int h; + qboolean full = qfalse; + + + scr_fullupdate = 0; // force a background redraw + vid.recalc_refdef = 0; + +//======================================== + + +// bound field of view + if (scr_fov.value < 10) + Cvar_Set ("fov","10"); + if (scr_fov.value > 170) + Cvar_Set ("fov","170"); + +// intermission is always full screen + full = qtrue; + size = 1.0; + + h = vid.height; + + r_refdef.vrect.width = int(vid.width * size); + if (r_refdef.vrect.width < 96) + { + size = 96.0 / r_refdef.vrect.width; + r_refdef.vrect.width = 96; // min for icons + } + + r_refdef.vrect.height = int(vid.height * size); + if (r_refdef.vrect.height > vid.height) + r_refdef.vrect.height = vid.height; + if (r_refdef.vrect.height > vid.height) + r_refdef.vrect.height = vid.height; + r_refdef.vrect.x = (vid.width - r_refdef.vrect.width)/2; + + if (full) + r_refdef.vrect.y = 0; + else + r_refdef.vrect.y = (h - r_refdef.vrect.height)/2; + + r_refdef.fov_x = scr_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_Init +================== +*/ +void SCR_Init (void) +{ + + Cvar_RegisterVariable (&scr_fov); + Cvar_RegisterVariable (&scr_conspeed); + Cvar_RegisterVariable (&scr_showram); + Cvar_RegisterVariable (&scr_showpause); + Cvar_RegisterVariable (&scr_centertime); + Cvar_RegisterVariable (&scr_loadscreen); + Cvar_RegisterVariable (&scr_printspeed); + Cvar_RegisterVariable (&scr_conheight); + Cvar_RegisterVariable (&r_dithering); + Cvar_RegisterVariable (&scr_coloredtext); + +// +// register our commands +// + Cmd_AddCommand ("screenshot",SCR_ScreenShot_f); + + hitmark = Draw_CachePic("gfx/hud/hit_marker"); + //ls_wahnsinn = Draw_CachePic("gfx/lscreen/wahnsinn.lmp"); + //ls_xmas = Draw_CachePic("gfx/lscreen/christmas_special.lmp"); + //ls_warehouse = Draw_CacheImg("gfx/lscreen/warehouse"); + //ls_ndu = Draw_CacheImg("gfx/lscreen/ndu"); + + scr_initialized = qtrue; +} + + +/* +============== +DrawPause +============== +*/ +void SCR_DrawPause (void) +{ + qpic_t *pic; + + if (!scr_showpause.value) // turn off for screenshots + return; + + if (!cl.paused) + return; + + pic = Draw_CachePic ("gfx/pause.lmp"); + Draw_Pic ( (vid.width - pic->width)/2, + (vid.height - 48 - pic->height)/2, pic); +} + +/* +//muff - hacked out of SourceForge implementation + modified +============== +SCR_DrawFPS +============== +*/ + + +void Draw_FrontText(const char* text, int x, int y, unsigned int color, int fw); +void SCR_DrawFPS (void) +{ + extern cvar_t show_fps; + static double lastframetime; + static double startT = 0; + static double Tframe = 0; + static int lframeCount = 0; + double t; + extern int fps_count; + static int lastfps; + int x, y; + char st[80],st1[80], st2[80]; + static int averge_fps; + + if(lframeCount != host_framecount)//incrementing our local frame counter + { + ++Tframe; + lframeCount = host_framecount; + } + + if(startT == 0) + { + startT = Sys_FloatTime(); + } + + if (!show_fps.value) + return; + + t = Sys_FloatTime (); + + if ((t - lastframetime) >= 1.0) + { + lastfps = fps_count; + fps_count = 0; + lastframetime = t; + + if(t > startT) + averge_fps = round(Tframe/(t - startT)); + } + sprintf(st1,"%3d FPS %3d AVG", lastfps, averge_fps); + //Draw_FrontText(st, 0, 0, 0xFF0000FF, 0); + + if(lastfps < 20) + sprintf(st,"%3d FPS", lastfps); + else if(lastfps > 20 && lastfps < 40) + sprintf(st,"%3d FPS", lastfps); + else + sprintf(st,"%3d FPS", lastfps); + + if(averge_fps < 20) + sprintf(st2," %3d AVG", averge_fps); + else if(averge_fps > 20 && averge_fps < 40) + sprintf(st2," %3d AVG", averge_fps); + else + sprintf(st2," %3d AVG", averge_fps); + + strcat(st,st2); + + x = vid.width - strlen(st1) * 8; + y = 0 ; + + Draw_ColoredString(x, y, st, 255, 255, 255, 255, 1); + + game_fps = lastfps; +} + +#include +/* +============== +SCR_DrawBAT +Crow_bar +============== +*/ +void SCR_DrawBAT (void) +{ + extern cvar_t show_bat; + int x, y; + char stA[80],stB[80]; + + if (!show_bat.value) + return; + + if (!scePowerIsBatteryExist()) + { + // Don't report anything. + return; + } + + const int level = scePowerGetBatteryLifePercent(); + const bool charging = scePowerGetBatteryChargingStatus(); + + // Is the level not sensible? + if ((level < 0) || (level > 100)) + { + // Hopefully it will be sensible soon. + return; + } + + + sprintf(stA, "Battery %d%%\n",level); + sprintf(stB, "Battery %d%% (charging)\n",level); + + if(!charging) + x = vid.width - strlen(stA) * 16 - 70; + else + x = vid.width - strlen(stB) * 16 - 70; + + y = 2 ; + + if(show_bat.value == 2) + { + if(!charging) + Draw_String(x, y, stA); + else + Draw_String(x, y, stB); + } + else + { + if(charging) + { + Draw_Fill (240, y, level, 5, 12+((int)(realtime*8)&120)); + } + else + Draw_Fill (240, y, level, 5, level); + } + +} + +/* +============== +SCR_DrawLoading +============== +*/ +void SCR_DrawLoading (void) +{ + qpic_t *pic; + + if (!scr_drawloading) + return; + + pic = Draw_CachePic ("gfx/loading.lmp"); + Draw_Pic ( (vid.width - pic->width)/2, + (vid.height - 48 - pic->height)/2, pic); +} + +int Random_Int (int max_int) +{ + float f; + f = (rand ()&0x7fff) / ((float)0x7fff) * max_int; + if (f > 0) + return (int)(f + 0.5) + 1; + else + return (int)(f - 0.5) + 1; +} +/* +============== +SCR_DrawLoadScreen +============== +*/ + +/* + Creds to the following people from the 2020 + Loading Screen Hint Submission/Contest: + + * BCDeshiG + * Derped_Crusader + * Aidan + * yasen +*/ +double loadingtimechange; +int loadingdot; +char *lodinglinetext; +qpic_t *awoo; +char *ReturnLoadingtex (void) +{ + int StringNum = Random_Int(48); + switch(StringNum) + { + case 1: + return "The original Quake came out in 1996 (24 years ago as of 2020!)"; + break; + case 2: + return "Use the Kar98-k to be the hero we want you to be!"; + break; + case 3: + return "There is a huge number of modern engines based on Quake!"; + break; + case 4: + return "Development for NZ:P officially started on September 27, 2009"; + break; + case 5: + return "NZ:P was first released on December 25, 2010"; + break; + case 6: + return "The 1.1 release of NZ:P has over 90,000 downloads!"; + break; + case 7: + return "NZ:P has been downloaded over 400,00 times!"; + break; + case 8: + return "The original NZP was made mainly by 3 guys around the world."; + break; + case 9: + return "Blubswillrule: known as \"blubs\", is from the USA."; + break; + case 10: + return "Jukki is from Finland."; + break; + case 11: + return "Ju[s]tice, or \"tom\" is from Lithuania."; + break; + case 12: + return "This game is the reason that we have bad sleeping habits!"; + break; + case 13: + return "We had a lot of fun making this game."; + break; + case 14: + return "Did you know you can make your own Custom Map?"; + break; + case 15: + return "Try Retro Mode, it's in the Graphics Settings!"; + break; + case 16: + return "Tired of Nacht der Untoten? Make your own map!"; + break; + case 17: + return "Slay zombies & be grateful."; + break; + case 18: + return "Custom maps, CUSTOM MAPS!"; + break; + case 19: + return "Go outside & build a snowman!"; + break; + case 20: + return "Please surround yourself with zombies!"; + break; + case 21: + return "Don't play for too long, or zombies will eat you."; + break; + case 22: + return "That was epic... EPIC FOR THE WIIIN!"; //why + break; + case 23: + return "PPSSPP is an awesome PSP emulator!"; + break; + case 24: + return "You dead yet?"; + break; + case 25: + return "Now 21% cooler!"; + break; + case 26: + return "your lg is nothink on the lan"; //what + break; + case 27: + return "I'm not your chaotic on dm6!"; + break; + case 28: + return "Shoot zombies to kill them. Or knife them. You choose."; + break; + case 29: + return "How many people forgot to Compile today?"; + break; + case 30: + return "ggnore"; + break; + case 31: + return "Have you tried NZ:P on PC or NX?"; + break; + case 32: + return "Submerge your device in water for godmode!"; + break; + case 33: + return "10/10/10 was a good day."; + break; + case 34: + return "Also check out \"No Bugs Allowed\" for the PSP!"; + break; + case 35: + return "MotoLegacy, or \"Ian\", is from the USA."; + break; + case 36: + return "Zombies don't like bullets."; + break; + case 37: + return "Thanks for being an awesome fan!"; + break; + case 38: + return "Removed Herobrine"; + break; + case 39: + return "Pack-a-Punch the Kar98k to get to round infinity."; + break; + case 40: + return "I feel like I'm being gaslit."; + break; + case 41: + return "Heads up! You will die if you are killed!"; + break; + case 42: + return "Zombies legally can't kill you if you say no!"; + break; + case 43: + return "Please help me find the meaning of . Thanks."; + break; + case 44: + return "Loading..."; // "ripperoni" made me think the game crashed while loading + break; + case 45: + return "Get rid of the 21% cooler tip, it's an MLP reference"; + break; + case 46: + return "You're playing on a PSP!"; + break; + case 47: + return "Don't leak the beta!"; + break; + } + return "wut wut"; +} +void SCR_DrawLoadScreen (void) +{ + + if (developer.value) + return; + if (!con_forcedup) + return; +/* + static int count; + + if (host_frametime < 0.01) + { + count = 0; + return; + } + + count++; + if (count < 3) + return; +*/ + + /*if(cl.worldmodel) + { + loadingScreen = 0; + ShowBlslogo = 1; + return; + }*/ + + + /*if (!ShowBlslogo) + { + pic = Draw_CachePic ("gfx/lscreen/blstrans"); + Draw_Pic (scr_vrect.x, scr_vrect.y, pic); + }*/ + /*if (loadingScreen == 1) + Draw_Pic (scr_vrect.x, scr_vrect.y, ls_wahnsinn);*/ + //else if (loadingScreen == 2) + /*Draw_Pic (scr_vrect.x, scr_vrect.y, ls_anstieg);*//* + else if (loadingScreen == 3) + { + pic = Draw_CachePic ("gfx/lscreen/psp_ch"); + Draw_Pic (scr_vrect.x, scr_vrect.y, pic); + } + else if (loadingScreen == 4) + { + pic = Draw_CachePic ("gfx/lscreen/psp_warehouse"); + Draw_Pic (scr_vrect.x, scr_vrect.y, pic); + }*/ + + // loading screens + switch(loadingScreen) { + //case 1: Draw_Pic(scr_vrect.x, scr_vrect.y, ls_ndu); break; + //case 2: Draw_Pic(scr_vrect.x, scr_vrect.y, ls_warehouse); break; + //case 3: Draw_Pic(scr_vrect.x, scr_vrect.y, ls_xmas); break; + } + + if (loadingtimechange < Sys_FloatTime ()) + { + lodinglinetext = ReturnLoadingtex(); + loadingtimechange = Sys_FloatTime () + 5; + } + + if (key_dest == key_game) { + Draw_String (0, 0, lodinglinetext); + + if (lodinglinetext == "Please help me find the meaning of . Thanks.") { + awoo = Draw_CacheImg("gfx/menu/awoo"); + Draw_Pic(284, 0, awoo); + } + } + +} + + +//============================================================================= + + +/* +================== +SCR_SetUpToDrawConsole +================== +*/ +void SCR_SetUpToDrawConsole (void) +{ + Con_CheckResize (); + + if (scr_drawloading) + return; // never a console with loading plaque + +// decide on the height of the console + if (!cl.worldmodel || cls.signon != SIGNONS)//blubs here, undid it actually + { + con_forcedup = qtrue; + } + else + { + con_forcedup = qfalse; + } + + 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 + + if (scr_conlines < scr_con_current) + { + scr_con_current -= scr_conspeed.value*host_frametime; + 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; + if (scr_conlines < scr_con_current) + scr_con_current = scr_conlines; + } + + 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, qtrue); + clearconsole = 0; + } + else + { + if (key_dest == key_game || key_dest == key_message) + Con_DrawNotify (); // only draw notify in game + } +} + + +//============================================================================= + + +/* +=============== +SCR_BeginLoadingPlaque + +================ +*/ +void SCR_BeginLoadingPlaque (void) +{ + CDAudio_Pause(); + S_StopAllSounds (qtrue); + + if (cls.state != ca_connected) + return; + if (cls.signon != SIGNONS)//blubs editted this + return; + +// redraw with no console and the loading plaque + Con_ClearNotify (); + scr_centertime_off = 0; + scr_con_current = 0; + + scr_drawloading = qtrue; + scr_fullupdate = 0; + SCR_UpdateScreen (); + scr_drawloading = qfalse; + + scr_disabled_for_loading = qtrue; + scr_disabled_time = realtime; + scr_fullupdate = 0; +} + +/* +=============== +SCR_EndLoadingPlaque + +================ +*/ +void SCR_EndLoadingPlaque (void) +{ + scr_disabled_for_loading = qfalse; + scr_fullupdate = 0; + Con_ClearNotify (); + CDAudio_Resume(); +} + +//============================================================================= + +char *scr_notifystring; +qboolean scr_drawdialog; + +void SCR_DrawNotifyString (void) +{ + char *start; + int l; + int j; + int x, y; + + start = scr_notifystring; + + y = vid.height*0.35f; + + do + { + // scan the width of the line + for (l=0 ; l<40 ; l++) + if (start[l] == '\n' || !start[l]) + break; + x = (vid.width - l*8)/2; + for (j=0 ; j 60) + { + scr_disabled_for_loading = qfalse; + Con_Printf ("load failed.\n"); + } + else + return; + } + + if (!scr_initialized || !con_initialized) + return; // not initialized yet + + //if(cls.state == ca_connected && cls.signon == SIGNONS) + //{ + // Con_Printf("Attempting to update screen! \n"); + //} + + GL_BeginRendering (&glx, &gly, &glwidth, &glheight); + // + // determine size of refresh window + // + if (cl.stats[STAT_ZOOM] == 1) + { + /*if (zoomin_time == 0 && !original_fov) + { + original_fov = scr_fov.value; + zoomin_time = Sys_FloatTime () + 0.01; + Cvar_SetValue ("fov", scr_fov.value - 2.5); + + } + else if (zoomin_time < Sys_FloatTime ()) + { + int tempfloat = original_fov - GetWeaponZoomAmmount(); + if (scr_fov.value > tempfloat) + { + zoomin_time = Sys_FloatTime () + 0.01; + Cvar_SetValue ("fov", scr_fov.value - 2.5); + } + else + { + Cvar_SetValue ("fov", tempfloat); + zoomin_time = 0; + } + }*/ + if(!original_fov) + original_fov = scr_fov.value; + if(scr_fov.value > (GetWeaponZoomAmmount() + 1))//+1 for accounting for floating point inaccurraces + { + scr_fov.value += ((original_fov - GetWeaponZoomAmmount()) - scr_fov.value) * 0.25; + Cvar_SetValue("fov",scr_fov.value); + } + } + else if (cl.stats[STAT_ZOOM] == 2) + { + Cvar_SetValue ("fov", 30); + zoomin_time = 0; + } + else if (cl.stats[STAT_ZOOM] == 3) + { + if(!original_fov) + original_fov = scr_fov.value; + //original_fov = scr_fov.value; + scr_fov.value += (original_fov - 10 - scr_fov.value) * 0.3; + Cvar_SetValue("fov",scr_fov.value); + } + else if (cl.stats[STAT_ZOOM] == 0 && original_fov != 0) + { + /*zoomin_time = Sys_FloatTime () + 0.01; + Cvar_SetValue ("fov", scr_fov.value + 2.5); + if (scr_fov.value == original_fov) + { + zoomin_time = 0; + original_fov = 0; + }*/ + if(scr_fov.value < (original_fov + 1))//+1 for accounting for floating point inaccuracies + { + scr_fov.value += (original_fov - scr_fov.value) * 0.25; + Cvar_SetValue("fov",scr_fov.value); + } + else + { + original_fov = 0; + } + } + + + if (oldfov != scr_fov.value) + { + oldfov = scr_fov.value; + vid.recalc_refdef = qtrue; + } + + if (oldscreensize != 120) + { + oldscreensize = 120; + vid.recalc_refdef = qtrue; + } + + if (vid.recalc_refdef) + SCR_CalcRefdef (); + +// +// do 3D refresh drawing, and then update the screen +// + SCR_SetUpToDrawConsole (); + + V_RenderView (); + + GL_Set2D (); + + Draw_Crosshair (); + + //muff - to show FPS on screen + SCR_DrawFPS (); + SCR_DrawBAT (); + SCR_DrawPause (); + SCR_CheckDrawCenterString (); + SCR_CheckDrawUseString (); + HUD_Draw (); + SCR_DrawConsole (); + M_Draw (); + + if(scr_loadscreen.value) + SCR_DrawLoadScreen(); + + Draw_LoadingFill(); + + V_UpdatePalette (); + + GL_EndRendering (); +} + diff --git a/source/psp/video_hardware_surface.cpp b/source/psp/video_hardware_surface.cpp new file mode 100644 index 0000000..97b1209 --- /dev/null +++ b/source/psp/video_hardware_surface.cpp @@ -0,0 +1,1911 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 +#include + +extern "C" +{ +#include "../quakedef.h" +} + +#ifdef PSP_VFPU +#include +#endif + +extern int LIGHTMAP_BYTES; + +#include "clipping.hpp" +#include "video_hardware_fullbright.h" + +using namespace quake; + +int skytexturenum; + +#define BLOCK_WIDTH 128 +#define BLOCK_HEIGHT 128 + +int lightmap_textures; +unsigned blocklights[BLOCK_WIDTH*BLOCK_HEIGHT*3]; // LordHavoc: .lit support (*3 for RGB) + + +int active_lightmaps; + + +typedef struct glRect_s +{ + unsigned char l,t,w,h; +} glRect_t; + +////////////////////////////////////////////////////////////////////////////// +//For none .lit maps.///////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +glpoly_t *lightmap_polys[MAX_LIGHTMAPS]; +qboolean 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[1*MAX_LIGHTMAPS*BLOCK_WIDTH*BLOCK_HEIGHT]; +int lightmap_index[MAX_LIGHTMAPS]; + +// For gl_texsort 0 +msurface_t *skychain = NULL; +msurface_t *waterchain = NULL; +glpoly_t *caustics_polys = NULL; +glpoly_t *detail_polys = NULL; + +void R_RenderDynamicLightmaps (msurface_t *fa); + +void VID_SetPaletteLM(); +// switch palette for lightmaps + +void VID_SetPaletteTX(); +// switch palette for textures + +/* +=============== +R_AddDynamicLights +=============== +*/ +void R_AddDynamicLights (msurface_t *surf) +{ + int lnum; + int sd, td; + float dist, rad, minlight; + vec3_t impact, local; + int s, t; + int i; + int smax, tmax; + mtexinfo_t *tex; + + // LordHavoc: .lit support begin + float cred, cgreen, cblue, brightness; + unsigned *bl; + // LordHavoc: .lit support end + + 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; + #ifdef PSP_VFPU + rad -= vfpu_fabsf(dist); + #else + rad -= fabsf(dist); + #endif + 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 + // blocklights[t*smax + s] += (rad - dist)*256; // LordHavoc: original code + { + + brightness = rad - dist; + if (!cl_dlights[lnum].dark) + { + bl[0] += (int) (brightness * cred); + bl[1] += (int) (brightness * cgreen); + bl[2] += (int) (brightness * cblue); + } + else + { + if(bl[0] > (int) (brightness * cred)) + bl[0] -= (int) (brightness * cred); + else + bl[0] = 0; + + if(bl[1] > (int) (brightness * cgreen)) + bl[1] -= (int) (brightness * cgreen); + else + bl[1] = 0; + + if(bl[2] > (int) (brightness * cblue)) + bl[2] -= (int) (brightness * cblue); + else + bl[2] = 0; + } + /* + brightness = rad - dist; + bl[0] += (int) (brightness * cred); + bl[1] += (int) (brightness * cgreen); + bl[2] += (int) (brightness * cblue); + */ + } + bl += 3; + // LordHavoc: .lit support end + } + } + } +} + + +/* +=============== +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 smax, tmax; + int t; + int i, j, size; + byte *lightmap; + unsigned scale; + int maps; + unsigned *bl; + + //unsigned *blcr, *blcg, *blcb; + + surf->cached_dlight = (surf->dlightframe == r_framecount) ? qtrue : qfalse; + + 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 = 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: + switch (LIGHTMAP_BYTES) + { + case 4: + stride -= (smax<<2); + bl = blocklights; + for (i=0 ; i> 7;if (t > 255) t = 255;*dest++ = t; + t = *bl++ >> 7;if (t > 255) t = 255;*dest++ = t; + t = *bl++ >> 7;if (t > 255) t = 255;*dest++ = t; + *dest++ = 255; + // LordHavoc: .lit support end + } + } + break; + case 3: + stride -= (smax<<2); + bl = blocklights; + for (i=0 ; i> 7;if (t > 255) t = 255;*dest++ = t; + t = *bl++ >> 7;if (t > 255) t = 255;*dest++ = t; + t = *bl++ >> 7;if (t > 255) t = 255;*dest++ = t; + *dest++ = 255; + // LordHavoc: .lit support end + } + } + break; + case 2: + bl = blocklights; + for (i=0 ; i> 15; // LordHavoc: basically / 3, but faster and combined with >> 7 shift down, note: actual number would be 85.3333... + bl += 3; + // LordHavoc: .lit support end + if (t > 255) + t = 255; + dest[j] = t; + } + } + break; + case 1: + bl = blocklights; + for (i=0 ; i> 15; // LordHavoc: basically / 3, but faster and combined with >> 7 shift down, note: actual number would be 85.3333... + bl += 3; + // LordHavoc: .lit support end + if (t > 255) + t = 255; + dest[j] = t; + } + } + break; + default: + Sys_Error ("Bad lightmap format"); + } +} + + +/* +=============== +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 + +static inline void DrawGLPolyLM (glpoly_t *p) +{ + // Does this poly need clipped? + + + const int unclipped_vertex_count = p->numverts; + const glvert_t* const unclipped_vertices = &(p->verts[p->numverts]); + + if (clipping::is_clipping_required( + unclipped_vertices, + unclipped_vertex_count)) + { + // Clip the polygon. + const glvert_t* clipped_vertices; + std::size_t clipped_vertex_count; + clipping::clip( + unclipped_vertices, + unclipped_vertex_count, + &clipped_vertices, + &clipped_vertex_count); + + // Did we have any vertices left? + if (clipped_vertex_count) + { + // Copy the vertices to the display list. + const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t); + glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size)); + memcpy(display_list_vertices, clipped_vertices, buffer_size); + + // Draw the clipped vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF , + clipped_vertex_count, 0, display_list_vertices); + } + } + else + { + // Draw the poly directly. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF , + unclipped_vertex_count, 0, unclipped_vertices); + } +} + +static inline void DrawGLPoly (glpoly_t *p) +{ + // Does this poly need clipped? + const int unclipped_vertex_count = p->numverts; + const glvert_t* const unclipped_vertices = p->verts; + if (clipping::is_clipping_required( + unclipped_vertices, + unclipped_vertex_count)) + { + // Clip the polygon. + const glvert_t* clipped_vertices; + std::size_t clipped_vertex_count; + clipping::clip( + unclipped_vertices, + unclipped_vertex_count, + &clipped_vertices, + &clipped_vertex_count); + + // Did we have any vertices left? + if (clipped_vertex_count) + { + // Copy the vertices to the display list. + const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t); + glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size)); + memcpy(display_list_vertices, clipped_vertices, buffer_size); + + // Draw the clipped vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + clipped_vertex_count, 0, display_list_vertices); + } + } + else + { + // Draw the poly directly. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + unclipped_vertex_count, 0, unclipped_vertices); + } +} + +static inline void DrawTrisPoly (glpoly_t *p) //Crow_bar +{ + sceGuDisable(GU_TEXTURE_2D); + // Does this poly need clipped? + const int unclipped_vertex_count = p->numverts; + const glvert_t* const unclipped_vertices = p->verts; + if (clipping::is_clipping_required( + unclipped_vertices, + unclipped_vertex_count)) + { + // Clip the polygon. + const glvert_t* clipped_vertices; + std::size_t clipped_vertex_count; + clipping::clip( + unclipped_vertices, + unclipped_vertex_count, + &clipped_vertices, + &clipped_vertex_count); + + // Did we have any vertices left? + if (clipped_vertex_count) + { + // Copy the vertices to the display list. + const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t); + glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size)); + memcpy(display_list_vertices, clipped_vertices, buffer_size); + + // Draw the clipped vertices. + sceGuDrawArray( + GU_LINE_STRIP, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + clipped_vertex_count, 0, display_list_vertices); + } + } + else + { + // Draw the poly directly. + sceGuDrawArray( + GU_LINE_STRIP, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + unclipped_vertex_count, 0, unclipped_vertices); + } + sceGuEnable(GU_TEXTURE_2D); +} + +void DrawGLPoly_ex (glpoly_t *p) +{ + DrawGLPoly(p); +} + +// speed up sin calculations - Ed +extern float turbsin[]; +static inline void DrawGLWaterPolyLM (glpoly_t *p) +{ +/* + // Does this poly need clipped? + + const float real_time = static_cast(realtime); + const float scale = (1.0f / 64); + const float turbscale = (256.0f / (2.0f * static_cast(M_PI))); + + const int unclipped_vertex_count = p->numverts; + //glvert_t* const unclipped_vertices = &(p->verts[p->numverts]); + + glvert_t* const unclipped_vertices = + static_cast(sceGuGetMemory(sizeof(glvert_t) * unclipped_vertex_count)); + + // Generate each vertex. + const glvert_t* src = p->verts; + const glvert_t* last_vertex = src + unclipped_vertex_count; + glvert_t* dst = unclipped_vertices; + + while (src != last_vertex) + { + // Get the input UVs. + const float os = src->st[0]; + const float ot = src->st[1]; + + // Fill in the vertex data. + dst->st[0] = os; + dst->st[1] = ot; + + //dst->xyz[0] = src->xyz[0] + 8*sinf(src->xyz[1]*0.05+realtime)*sinf(src->xyz[2]*0.05+realtime); + //dst->xyz[1] = src->xyz[1] + 8*sinf(src->xyz[0]*0.05+realtime)*sinf(src->xyz[2]*0.05+realtime); + //dst->xyz[2] = src->xyz[2]; + + dst->xyz[0] = dst->xyz[0] + 8*sinf(dst->xyz[1]*0.05+realtime)*sinf(dst->xyz[2]*0.05+realtime); + dst->xyz[1] = dst->xyz[1] + 8*sinf(dst->xyz[0]*0.05+realtime)*sinf(dst->xyz[2]*0.05+realtime); + dst->xyz[2] = dst->xyz[2]; + + // Next vertex. + ++src; + ++dst; + } + + if (clipping::is_clipping_required( + unclipped_vertices, + unclipped_vertex_count)) + { + // Clip the polygon. + const glvert_t* clipped_vertices; + std::size_t clipped_vertex_count; + clipping::clip( + unclipped_vertices, + unclipped_vertex_count, + &clipped_vertices, + &clipped_vertex_count); + + // Did we have any vertices left? + if (clipped_vertex_count) + { + // Copy the vertices to the display list. + const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t); + glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size)); + memcpy(display_list_vertices, clipped_vertices, buffer_size); + + // Draw the clipped vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF , + clipped_vertex_count, 0, display_list_vertices); + } + } + else + { + + // Draw the poly directly. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF , + unclipped_vertex_count, 0, unclipped_vertices); + } +*/ +DrawGLPolyLM (p); +} + +static inline void DrawGLWaterPoly (glpoly_t *p) +{ +/* + // Does this poly need clipped? + + const float real_time = static_cast(realtime); + const float scale = (1.0f / 64); + const float turbscale = (256.0f / (2.0f * static_cast(M_PI))); + + const int unclipped_vertex_count = p->numverts; +// glvert_t* const unclipped_vertices = p->verts; + + glvert_t* const unclipped_vertices = + static_cast(sceGuGetMemory(sizeof(glvert_t) * unclipped_vertex_count)); + + // Generate each vertex. + const glvert_t* src = p->verts; + const glvert_t* last_vertex = src + unclipped_vertex_count; + glvert_t* dst = unclipped_vertices; + + while (src != last_vertex) + { + // Get the input UVs. + const float os = src->st[0]; + const float ot = src->st[1]; + + // Fill in the vertex data. + dst->st[0] = os; + dst->st[1] = ot; + + dst->xyz[0] = src->xyz[0] + 8*sinf(src->xyz[1]*0.05+realtime)*sinf(src->xyz[2]*0.05+realtime); + dst->xyz[1] = src->xyz[1] + 8*sinf(src->xyz[0]*0.05+realtime)*sinf(src->xyz[2]*0.05+realtime); + dst->xyz[2] = src->xyz[2]; + + //dst->xyz[0] = dst->xyz[0] + 8*sinf(dst->xyz[1]*0.05+realtime)*sinf(dst->xyz[2]*0.05+realtime); + //dst->xyz[1] = dst->xyz[1] + 8*sinf(dst->xyz[0]*0.05+realtime)*sinf(dst->xyz[2]*0.05+realtime); + //dst->xyz[2] = dst->xyz[2]; + // Next vertex. + ++src; + ++dst; + } + + if (clipping::is_clipping_required( + unclipped_vertices, + unclipped_vertex_count)) + { + // Clip the polygon. + const glvert_t* clipped_vertices; + std::size_t clipped_vertex_count; + clipping::clip( + unclipped_vertices, + unclipped_vertex_count, + &clipped_vertices, + &clipped_vertex_count); + + // Did we have any vertices left? + if (clipped_vertex_count) + { + // Copy the vertices to the display list. + const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t); + glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size)); + memcpy(display_list_vertices, clipped_vertices, buffer_size); + + // Draw the clipped vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF , + clipped_vertex_count, 0, display_list_vertices); + } + } + else + { + + // Draw the poly directly. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF , + unclipped_vertex_count, 0, unclipped_vertices); + } +*/ +DrawGLPoly (p); +} + +/* +============= +EmitDetailPolys +============= +void GL_BindDET (int texture_index); +void EmitDetailPolys (void) +{ + texture_t *tex; + + if (!detail_polys) + return; + + if (tex->dt_texturenum == 0) + return; + + // For each polygon... + //Crow_bar multi detail texture + GL_BindDET(tex->dt_texturenum); + + sceGuBlendFunc (GU_ADD, GU_DST_COLOR, GU_SRC_COLOR, 0, 0); + sceGuEnable(GU_BLEND); + sceGuTexFunc(GU_TFX_DECAL, GU_TCC_RGBA); + + for (const glpoly_t* p = detail_polys ; p ; p = p->detail_chain) + { + // Allocate memory for this polygon. + const int unclipped_vertex_count = p->numverts; + glvert_t* const unclipped_vertices = + static_cast(sceGuGetMemory(sizeof(glvert_t) * unclipped_vertex_count)); + + // Generate each vertex. + const glvert_t* src = p->verts; + const glvert_t* last_vertex = src + unclipped_vertex_count; + glvert_t* dst = unclipped_vertices; + + while (src != last_vertex) + { + // Fill in the vertex data. + dst->st[0] = src->st[0]; + dst->st[1] = src->st[1]; + + dst->xyz[0] = src->xyz[0]; + dst->xyz[1] = src->xyz[1]; + dst->xyz[2] = src->xyz[2]; + + // Next vertex. + ++src; + ++dst; + } + + // Do these vertices need clipped? + if (clipping::is_clipping_required(unclipped_vertices, unclipped_vertex_count)) + { + // Clip the polygon. + const glvert_t* clipped_vertices; + std::size_t clipped_vertex_count; + clipping::clip( + unclipped_vertices, + unclipped_vertex_count, + &clipped_vertices, + &clipped_vertex_count); + + // Any vertices left? + if (clipped_vertex_count) + { + // Copy the vertices to the display list. + const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t); + glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size)); + memcpy(display_list_vertices, clipped_vertices, buffer_size); + + // Draw the clipped vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + clipped_vertex_count, 0, display_list_vertices); + } + } + else + { + // Draw the vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + unclipped_vertex_count, 0, unclipped_vertices); + } + } + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuDisable (GU_BLEND); + + detail_polys = NULL; +} +*/ + + +/* +================ +R_BlendLightmaps +================ +*/ +static void R_BlendLightmaps (void) +{ + int i; + glpoly_t *p; + + if (r_fullbright.value) + return; + + sceGuDepthMask(GU_TRUE); + sceGuEnable(GU_BLEND); + sceGuBlendFunc(GU_ADD, GU_DST_COLOR, GU_SRC_COLOR, 0, 0); + + if(LIGHTMAP_BYTES == 1) + VID_SetPaletteLM(); + + if (r_lightmap.value) + sceGuDisable(GU_BLEND); + + for (i=0 ; ichain) + { + if (p->flags & SURF_UNDERWATER) + { + DrawGLPolyLM(p); + //DrawGLWaterPolyLM(p); + } + else + DrawGLPolyLM(p); + + } + } + + if(LIGHTMAP_BYTES == 1) + VID_SetPaletteTX(); + + sceGuDisable(GU_BLEND); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuDepthMask (GU_FALSE); + sceGuEnable(GU_DEPTH_TEST); // dr_mabuse1981: fix +} + +/* +================ +R_RenderBrushPoly +================ +dr_mabuse1981: There was a random bug with rendering brushes, it is now fixed. +*/ +void R_RenderBrushPoly (msurface_t *fa) +{ + texture_t *t; + byte *base; + int maps; + glRect_t *theRect; + int smax, tmax; + + c_brush_polys++; + + if(r_showtris.value) //Crow_bar + { + if(r_showtris_full.value) + sceGuDisable(GU_DEPTH_TEST); + else + sceGuDepthMask (GU_TRUE); + + DrawTrisPoly (fa->polys); + + if(r_showtris_full.value) + sceGuEnable(GU_DEPTH_TEST); + else + sceGuDepthMask (GU_FALSE); + } + + 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 (!Q_strncmp(fa->texinfo->texture->name,"{",1)) // Halflife Alpha + { + sceGuEnable(GU_ALPHA_TEST); + sceGuAlphaFunc(GU_GREATER, 0xaa, 0xff); + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + } + + if (!Q_strncmp(fa->texinfo->texture->name,"light",5)) // Lights + { + DrawGLPoly (fa->polys); + return; + } + + if (!Q_strncmp(fa->texinfo->texture->name,"env",3)) // ENV + { + EmitReflectivePolys (fa); + } + + if (!Q_strncmp(fa->texinfo->texture->name,"nodraw",6)) // Nodraw + { + return; + } + + if (fa->flags & SURF_UNDERWATER) + DrawGLWaterPoly (fa->polys); + else + DrawGLPoly (fa->polys); + + if (!Q_strncmp(fa->texinfo->texture->name,"glass",5)) // Glass + { + EmitReflectivePolys (fa); + } + + // add the poly to the proper lightmap chain + fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; + lightmap_polys[fa->lightmaptexturenum] = fa->polys; + + + // 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 || fa->cached_dlight)// dynamic previously + { +dynamic: + if (r_dynamic.value) + { + lightmap_modified[fa->lightmaptexturenum] = qtrue; + 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); + + } + } + sceGuAlphaFunc(GU_GREATER, 0, 0xff); + sceGuDisable(GU_ALPHA_TEST); +} + +/* +================ +R_RenderDynamicLightmaps +Multitexture +================ +*/ +void R_RenderDynamicLightmaps (msurface_t *fa) +{ + byte *base; + int maps; + glRect_t *theRect; + int smax, tmax; + + c_brush_polys++; + + if (fa->flags & ( SURF_DRAWSKY | SURF_DRAWTURB) ) + return; + + fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; + lightmap_polys[fa->lightmaptexturenum] = fa->polys; + + // 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] = qtrue; + 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 = qtrue; + mirror_plane = s->plane; +} + + +/* +================ +R_DrawWaterSurfaces +================ +*/ +void R_DrawWaterSurfaces (void) +{ + int i; + msurface_t *s; + texture_t *t; + + if (r_wateralpha.value == 1.0 /*&& gl_texsort.value*/) + return; + + float alpha1 = r_wateralpha.value; + float alpha2 = 1 - r_wateralpha.value; + + // + // go back to the world matrix + // + + /*glLoadMatrixf (r_world_matrix);*/ + sceGumMatrixMode(GU_VIEW); + sceGumLoadMatrix(&r_world_matrix); + sceGumUpdateMatrix(); + + sceGumMatrixMode(GU_MODEL); + + if (r_wateralpha.value < 1.0) + { + sceGuEnable (GU_BLEND); + sceGuTexFunc(GU_TFX_REPLACE , GU_TCC_RGBA); + sceGuBlendFunc(GU_ADD, GU_FIX, GU_FIX, GU_COLOR(alpha1,alpha1,alpha1,alpha1), GU_COLOR(alpha2,alpha2,alpha2,alpha2)); + } + + + /*if (!gl_texsort.value) { + if (!waterchain) + return; + + for ( s = waterchain ; s ; s=s->texturechain) { + GL_Bind (s->texinfo->texture->gl_texturenum); + EmitWaterPolys (s); + } + + waterchain = NULL; + } else*/ + { + + 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) + { + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuColor (GU_RGBA(0xff, 0xff, 0xff, 0xff)); + sceGuDisable (GU_BLEND); + } + +} + +/* +================ +DrawTextureChains +================ +*/ +extern int underwater_texture; + +static 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) + { + 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); + + //Crow_bar //begin blubsremoved because quartal + //if ((s->flags & SURF_UNDERWATER) && r_caustics.value && underwater_texture) + //{ + // s->polys->caustics_chain = caustics_polys; + // caustics_polys = s->polys; + //} + // end blubsremoved quartal +/* + if (!(s->flags & SURF_UNDERWATER) && r_detail.value && t->dt_texturenum) + { + s->polys->detail_chain = detail_polys; + detail_polys = s->polys; + } +*/ + } + } + + t->texturechain = NULL; + } + + //EmitUnderWaterPolys (); //blubsremoved quartal + //EmitDetailPolys (); + +} + +void R_GlowSetupBegin(entity_t *e) +{ + //(matrix transform)& alpha value by distance +} + +void R_GlowSetupEnd(entity_t *e) +{ + //Restore matrix +} + +/* +================= +R_DrawBrushModel +================= +*/ +void R_DrawBrushModel (entity_t *e) +{ + int k; + vec3_t mins, maxs; + int i; + msurface_t *psurf; + float dot; + mplane_t *pplane; + model_t *clmodel; + qboolean rotated; + qboolean dlight;// + + dlight = qtrue;// + + + currententity = e; + currenttexture = -1; + + clmodel = e->model; + + if (e->angles[0] || e->angles[1] || e->angles[2]) + { + rotated = qtrue; + if (R_CullSphere(e->origin, clmodel->radius)) + return; + } + else + { + rotated = qfalse; + VectorAdd (e->origin, clmodel->mins, mins); + VectorAdd (e->origin, clmodel->maxs, maxs); + + if (R_CullBox (mins, maxs)) + return; + } + + + 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); + } + } + + sceGumPushMatrix(); + + //Crow_bar half_life render. + if (ISADDITIVE(e)) + { + //Con_DPrintf("ISADDITIVE:brush\n"); + float deg = e->renderamt; + float alpha1 = deg; + float alpha2 = 1 - deg; + if(deg <= 0.7) + sceGuDepthMask(GU_TRUE); + + sceGuEnable (GU_BLEND); + sceGuBlendFunc(GU_ADD, GU_FIX, GU_FIX, + GU_COLOR(alpha1,alpha1,alpha1,alpha1), + GU_COLOR(alpha2,alpha2,alpha2,alpha2)); + dlight = qfalse; + } + else if (ISSOLID(e)) + { + sceGuEnable(GU_ALPHA_TEST); + int c = (int)(e->renderamt * 255.0f); + sceGuAlphaFunc(GU_GREATER, c, 0xff); + dlight = qfalse; + } + else if (ISGLOW(e)) + { + sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); + sceGuDepthMask(GU_TRUE); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0xFFFFFFFF); + R_GlowSetupBegin(e); + } + else if (ISTEXTURE(e)) + { + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + sceGuColor(GU_RGBA(255, 255, 255, (int)(e->renderamt * 255.0f))); + dlight = qfalse; + } + else if (ISCOLOR(e)) + { + sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); + sceGuColor(GU_RGBA((int)(e->rendercolor[0] * 255.0f), + (int)(e->rendercolor[1] * 255.0f), + (int)(e->rendercolor[2] * 255.0f), 255)); + } + //Con_DPrintf("\n"); + //Con_DPrintf("render mode is: %i \n", (int)e->rendermode); + //Con_DPrintf("render mask is: %i \n", (int)(e->renderamt * 255.0f)); + //Con_DPrintf("render color is: %i %i %i \n", (int)(e->rendercolor[0] * 255.0f), + // (int)(e->rendercolor[1] * 255.0f), + // (int)(e->rendercolor[2] * 255.0f)); + + + e->angles[0] = -e->angles[0]; // stupid quake bug + R_BlendedRotateForEntity (e, 0); //blend transform + clipping::begin_brush_model(); + 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(dlight) + R_BlendLightmaps (); + + if (ISADDITIVE(e)) + { + float deg = e->renderamt; + if(deg <= 0.7) + sceGuDepthMask(GU_FALSE); + + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuDisable (GU_BLEND); + } + else if(ISSOLID(e)) + { + sceGuAlphaFunc(GU_GREATER, 0, 0xff); + sceGuDisable(GU_ALPHA_TEST); + } + else if(ISGLOW(e)) + { + R_GlowSetupEnd(e); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuDepthMask(GU_FALSE); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuDisable (GU_BLEND); + } + else if(ISCOLOR(e)) + { + sceGuColor(0xffffffff); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + } + else if(ISTEXTURE(e)) + { + sceGuColor(0xffffffff); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + } + //dr_mabuse1981: commented out, this was the one who caused the epic lag + //DrawFullBrightTextures (clmodel->surfaces, clmodel->numsurfaces); + //dr_mabuse1981: commented out, this was the one who caused the epic lag + + clipping::end_brush_model(); + sceGumPopMatrix(); + sceGumUpdateMatrix(); +} + +/* +============================================================= + + WORLD MODEL + +============================================================= +*/ + +/* +================ +R_RecursiveWorldNode +================ +*/ +void R_RecursiveWorldNode (mnode_t *node) +{ + int c, side; + mplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + float dot; + + 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 + + // if sorting by texture, just store it out + /*if (gl_texsort.value)*/ + { + if (!mirror + || surf->texinfo->texture != cl.worldmodel->textures[mirrortexturenum]) + { + surf->texturechain = surf->texinfo->texture->texturechain; + surf->texinfo->texture->texturechain = surf; + } + }/* else if (surf->flags & SURF_DRAWSKY) { + surf->texturechain = skychain; + skychain = surf; + } else if (surf->flags & SURF_DRAWTURB) { + surf->texturechain = waterchain; + waterchain = surf; + } else + R_DrawSequentialPoly (surf);*/ + + } + } + + } + +// recurse down the back side + R_RecursiveWorldNode (node->children[!side]); +} + +extern char skybox_name[32]; +/* +============= +R_DrawWorld +============= +*/ +void R_DrawWorld (void) +{ + entity_t ent; + + memset (&ent, 0, sizeof(ent)); + ent.model = cl.worldmodel; + + VectorCopy (r_refdef.vieworg, modelorg); + + currententity = &ent; + currenttexture = -1; + + /*glColor3f (1,1,1);*/ + memset (lightmap_polys, 0, sizeof(lightmap_polys)); + + R_ClearSkyBox (); + + + R_RecursiveWorldNode (cl.worldmodel->nodes); + + DrawTextureChains (); + + R_BlendLightmaps (); + + //dr_mabuse1981: commented out, this was the one who caused the epic lag + //DrawFullBrightTextures (cl.worldmodel->surfaces, cl.worldmodel->numsurfaces); + //dr_mabuse1981: commented out, this was the one who caused the epic lag + if (strcmp(skybox_name, "") != 0) + R_DrawSkyBox(); +} + + +/* +=============== +R_MarkLeaves +=============== +*/ +void R_MarkLeaves (void) +{ + byte *vis; + mnode_t *node; + int i; + + if (r_oldviewleaf == r_viewleaf || mirror) + return; + + ++r_visframecount; + r_oldviewleaf = r_viewleaf; + + vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); + + for (i = 0; i < cl.worldmodel->numleafs; ++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 +static int AllocBlock (int w, int h, int *x, int *y) +{ + int i, j; + int best, best2; + 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) + { + //Con_Printf("best + h > BLOCK_HEIGHT\n"); + continue; + } + for (i=0 ; iedges; + lnumverts = fa->numedges; +// vertpage = 0; + + // + // draw texture + // + poly = static_cast(Hunk_Alloc (sizeof(glpoly_t) + (lnumverts * 2 - 1) * sizeof(glvert_t))); + 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].xyz); + poly->verts[i].st[0] = s; + poly->verts[i].st[1] = 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; + + VectorCopy(vec, poly->verts[i + lnumverts].xyz); + poly->verts[i + lnumverts].st[0] = s; + poly->verts[i + lnumverts].st[1] = t; + } + + // + // remove co-linear points - Ed + // + + // Colinear point removal-start + + lnumverts = poly->numverts; + + if (!gl_keeptjunctions.value && !(fa->flags & SURF_UNDERWATER) ) + { + int numRemoved = 0; + int j; + + for (i = 0 ; i < lnumverts ; ++i) + { + vec3_t v1, v2; + const glvert_t *prev, *this_, *next; +// float f; + + prev = &poly->verts[(i + lnumverts - 1) % lnumverts]; + this_ = &poly->verts[i]; + next = &poly->verts[(i + 1) % lnumverts]; + + VectorSubtract( this_->xyz, prev->xyz, v1 ); + VectorNormalize( v1 ); + VectorSubtract( next->xyz, prev->xyz, v2 ); + VectorNormalize( v2 ); + + // skip co-linear points + #define COLINEAR_EPSILON 0.001 + #if PSP_VFPU + if ((vfpu_fabsf( v1[0] - v2[0] ) <= COLINEAR_EPSILON) && + (vfpu_fabsf( v1[1] - v2[1] ) <= COLINEAR_EPSILON) && + (vfpu_fabsf( v1[2] - v2[2] ) <= COLINEAR_EPSILON)) + #else + if ((fabsf( v1[0] - v2[0] ) <= COLINEAR_EPSILON) && + (fabsf( v1[1] - v2[1] ) <= COLINEAR_EPSILON) && + (fabsf( v1[2] - v2[2] ) <= COLINEAR_EPSILON)) + #endif + { + for (j = i + 1; j < lnumverts; j = j + 1) + { + poly->verts[j - 1] = poly->verts[j]; + poly->verts[poly->numverts + j - 1] = poly->verts[poly->numverts+j]; + } + + --lnumverts; + ++nColinElim; + numRemoved++; + // retry next vertex next time, which is now current vertex + --i; + } + } + + if (numRemoved > 0) { + for (j = poly->numverts; j < poly->numverts + lnumverts; j++) { + poly->verts[j - numRemoved] = poly->verts[j]; + } + } + } + + // Colinear point removal-end + poly->numverts = lnumverts; +} + +/* +======================== +GL_CreateSurfaceLightmap +======================== +*/ +static 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; + + //Con_Printf ("Lightmap surfaces = %i\n", MAX_LIGHTMAPS); + //Con_Printf ("Lightmap bytes = %i\n", LIGHTMAP_BYTES); + + memset (allocated, 0, sizeof(allocated)); + + r_framecount = 1; // no dlightcache + + if (!lightmap_textures) + { + lightmap_textures = 0; + } + + 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 + // + char lm_name[16]; + for (i=0 ; i + +extern "C" +{ +#include "../quakedef.h" +} + +#include "clipping.hpp" + +using namespace quake; + +extern model_t *loadmodel; + +/*int skytexturenum;*/ + +int solidskytexture = -1; +int alphaskytexture = -1; + +float speedscale; // for top sky and bottom sky + +int skytexorder[6] = {0,2,1,3,4,5}; +int skyimage[6]; // Where sky images are stored +char skybox_name[32] = ""; //name of current skybox, or "" if no skybox +char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; + +msurface_t *warpface; + +extern cvar_t gl_subdivide_size; + +static 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; + } + } +} + +static 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, subdivide_size;; + + if (numverts > 60) + Sys_Error ("numverts = %i", numverts); + + subdivide_size = fmax(1, gl_subdivide_size.value); + BoundPoly (numverts, verts, mins, maxs); + + for (i=0 ; i<3 ; i++) + { + m = (mins[i] + maxs[i]) * 0.5; + m = subdivide_size * floorf (m / subdivide_size + 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 = static_cast(Hunk_Alloc (sizeof(glpoly_t) + (numverts - 1) * sizeof(glvert_t))); + poly->next = warpface->polys; + warpface->polys = poly; + poly->numverts = numverts; + for (i=0 ; iverts[i].xyz); + s = DotProduct (verts, warpface->texinfo->vecs[0]); + t = DotProduct (verts, warpface->texinfo->vecs[1]); + poly->verts[i].st[0] = s; + poly->verts[i].st[1] = 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; + + 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]); +} + +//========================================================= + + +#define TURBSINSIZE 128 +#define TURBSCALE ((float)TURBSINSIZE / (2 * M_PI)) + +byte turbsin[TURBSINSIZE] = +{ + 127, 133, 139, 146, 152, 158, 164, 170, 176, 182, 187, 193, 198, 203, 208, 213, + 217, 221, 226, 229, 233, 236, 239, 242, 245, 247, 249, 251, 252, 253, 254, 254, + 255, 254, 254, 253, 252, 251, 249, 247, 245, 242, 239, 236, 233, 229, 226, 221, + 217, 213, 208, 203, 198, 193, 187, 182, 176, 170, 164, 158, 152, 146, 139, 133, + 127, 121, 115, 108, 102, 96, 90, 84, 78, 72, 67, 61, 56, 51, 46, 41, + 37, 33, 28, 25, 21, 18, 15, 12, 9, 7, 5, 3, 2, 1, 0, 0, + 0, 0, 0, 1, 2, 3, 5, 7, 9, 12, 15, 18, 21, 25, 28, 33, + 37, 41, 46, 51, 56, 61, 67, 72, 78, 84, 90, 96, 102, 108, 115, 121, +}; + +__inline static float SINTABLE_APPROX (float time) +{ + float sinlerpf, lerptime, lerp; + int sinlerp1, sinlerp2; + + sinlerpf = time * TURBSCALE; + sinlerp1 = floor(sinlerpf); + sinlerp2 = sinlerp1 + 1; + lerptime = sinlerpf - sinlerp1; + + lerp = turbsin[sinlerp1 & (TURBSINSIZE - 1)] * (1 - lerptime) + turbsin[sinlerp2 & (TURBSINSIZE - 1)] * lerptime; + return -8 + 16 * lerp / 255.0; +} + +/* +================ +GL_Surface +================ +*/ +void GL_Surface (msurface_t *fa) +{ + vec3_t verts[64]; + int numverts; + int i; + int lindex; + float *vec; + glpoly_t *poly; + //float texscale; + float s, t; + + //texscale = (1.0/32.0); + + // + // 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++; + } + + //create the poly + poly = static_cast(Hunk_Alloc (sizeof(glpoly_t) + (numverts - 1) * sizeof(glvert_t))); + poly->next = NULL; + fa->polys = poly; + poly->numverts = numverts; + for (i=0, vec=(float *)verts; iverts[i].xyz); + s = DotProduct(vec, fa->texinfo->vecs[0]);// * texscale; + t = DotProduct(vec, fa->texinfo->vecs[1]);// * texscale; + poly->verts[i].st[0] = s; + poly->verts[i].st[1] = t; + } +} + +/* +============= +EmitFlatPoly +============= +*/ +void EmitFlatPoly (msurface_t *fa) +{ + // For each polygon... + for (const glpoly_t* p = fa->polys; p; p = p->next) + { + // Allocate memory for this polygon. + const int unclipped_vertex_count = p->numverts; + glvert_t* const unclipped_vertices = + static_cast(sceGuGetMemory(sizeof(glvert_t) * unclipped_vertex_count)); + + // Generate each vertex. + const glvert_t* src = p->verts; + const glvert_t* last_vertex = src + unclipped_vertex_count; + glvert_t* dst = unclipped_vertices; + + while (src != last_vertex) + { + // Fill in the vertex data. + dst->st[0] = src->st[0]; //Tex + dst->st[1] = src->st[1]; + + dst->xyz[0] = src->xyz[0]; //Verts + dst->xyz[1] = src->xyz[1]; + dst->xyz[2] = src->xyz[2]; + + // Next vertex. + ++src; + ++dst; + } + + // Do these vertices need clipped? + if (clipping::is_clipping_required(unclipped_vertices, unclipped_vertex_count)) + { + // Clip the polygon. + const glvert_t* clipped_vertices; + std::size_t clipped_vertex_count; + clipping::clip( + unclipped_vertices, + unclipped_vertex_count, + &clipped_vertices, + &clipped_vertex_count); + + // Any vertices left? + if (clipped_vertex_count) + { + // Copy the vertices to the display list. + const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t); + glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size)); + memcpy(display_list_vertices, clipped_vertices, buffer_size); + + // Draw the clipped vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + clipped_vertex_count, 0, display_list_vertices); + } + } + else + { + // Draw the vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + unclipped_vertex_count, 0, unclipped_vertices); + } + } +} + +/* +============= +EmitWaterPolys + +Does a water warp on the pre-fragmented glpoly_t chain +============= +*/ +void EmitWaterPolys (msurface_t *fa) +{ + //const float real_time = static_cast(realtime); + const float scale = (1.0f / 64); +/* + //jkrige - clamp waterripple values + if(r_waterripple.value>10) + r_waterripple.value=10; + + if(r_waterripple.value<0) + r_waterripple.value=0; + //jkrige - clamp waterripple values +*/ + + // For each polygon... + for (const glpoly_t* p = fa->polys; p; p = p->next) + { + // Allocate memory for this polygon. + const int unclipped_vertex_count = p->numverts; + glvert_t* const unclipped_vertices = + static_cast(sceGuGetMemory(sizeof(glvert_t) * unclipped_vertex_count)); + + // Generate each vertex. + const glvert_t* src = p->verts; + const glvert_t* last_vertex = src + unclipped_vertex_count; + glvert_t* dst = unclipped_vertices; + + while (src != last_vertex) + { + // Get the input UVs. + const float os = src->st[0]; + const float ot = src->st[1]; + + // Fill in the vertex data. + dst->st[0] = (os + SINTABLE_APPROX(ot * 0.125 + cl.time)) * scale; + dst->st[1] = (ot + SINTABLE_APPROX(os * 0.125 + cl.time)) * scale; + + dst->xyz[0] = src->xyz[0]; + dst->xyz[1] = src->xyz[1]; + dst->xyz[2] = src->xyz[2]; + //dst->xyz[2] = src->xyz[2] + r_waterripple.value * sin(src->xyz[0]*0.05+realtime)*sin(src->xyz[2]*0.05+realtime); + // Next vertex. + ++src; + ++dst; + } + + // Do these vertices need clipped? + if (clipping::is_clipping_required(unclipped_vertices, unclipped_vertex_count)) + { + // Clip the polygon. + const glvert_t* clipped_vertices; + std::size_t clipped_vertex_count; + clipping::clip( + unclipped_vertices, + unclipped_vertex_count, + &clipped_vertices, + &clipped_vertex_count); + + // Any vertices left? + if (clipped_vertex_count) + { + // Copy the vertices to the display list. + const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t); + glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size)); + memcpy(display_list_vertices, clipped_vertices, buffer_size); + + // Draw the clipped vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + clipped_vertex_count, 0, display_list_vertices); + } + } + else + { + // Draw the vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + unclipped_vertex_count, 0, unclipped_vertices); + } + } +} + +/* +============= +EmitUnderWaterPolys +based on water polys! +By Crow_bar. +============= +*/ +extern int underwater_texture; +void EmitUnderWaterPolys (void) +{ + const float scale = (-3 * (0.5 / 64)); + extern glpoly_t *caustics_polys; + + sceGuEnable(GU_BLEND); + sceGuBlendFunc (GU_ADD, GU_DST_COLOR, GU_SRC_COLOR, 0, 0); + sceGuTexFunc(GU_TFX_DECAL, GU_TCC_RGBA); + + GL_Bind(underwater_texture); + + + // For each polygon... + for (const glpoly_t* p = caustics_polys ; p ; p = p->caustics_chain) + { + // Allocate memory for this polygon. + const int unclipped_vertex_count = p->numverts; + glvert_t* const unclipped_vertices = + static_cast(sceGuGetMemory(sizeof(glvert_t) * unclipped_vertex_count)); + + // Generate each vertex. + const glvert_t* src = p->verts; + const glvert_t* last_vertex = src + unclipped_vertex_count; + glvert_t* dst = unclipped_vertices; + + while (src != last_vertex) + { + // Get the input UVs. + const float os = src->st[0]; + const float ot = src->st[1]; + + // Fill in the vertex data. + dst->st[0] = (os + SINTABLE_APPROX(0.465 * (cl.time + ot))) * scale; + dst->st[1] = (ot + SINTABLE_APPROX(0.465 * (cl.time + os))) * scale; + + dst->xyz[0] = src->xyz[0]; + dst->xyz[1] = src->xyz[1]; + dst->xyz[2] = src->xyz[2]; + + // Next vertex. + ++src; + ++dst; + } + + // Do these vertices need clipped? + if (clipping::is_clipping_required(unclipped_vertices, unclipped_vertex_count)) + { + // Clip the polygon. + const glvert_t* clipped_vertices; + std::size_t clipped_vertex_count; + clipping::clip( + unclipped_vertices, + unclipped_vertex_count, + &clipped_vertices, + &clipped_vertex_count); + + // Any vertices left? + if (clipped_vertex_count) + { + // Copy the vertices to the display list. + const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t); + glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size)); + memcpy(display_list_vertices, clipped_vertices, buffer_size); + + // Draw the clipped vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + clipped_vertex_count, 0, display_list_vertices); + } + } + else + { + // Draw the vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + unclipped_vertex_count, 0, unclipped_vertices); + } + } + + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); + sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuDisable (GU_BLEND); + + caustics_polys = NULL; +} + +/* +============= +EmitSkyPolys +============= +*/ +void EmitSkyPolys (msurface_t *fa) +{ + for (const glpoly_t* p = fa->polys; p; p = p->next) + { + // Allocate memory for this polygon. + const int unclipped_vertex_count = p->numverts; + glvert_t* const unclipped_vertices = static_cast(sceGuGetMemory(sizeof(glvert_t) * unclipped_vertex_count)); + + vec3_t dir; + // Generate each vertex. + const glvert_t* src = p->verts; + const glvert_t* last_vertex = src + unclipped_vertex_count; + glvert_t* dst = unclipped_vertices; + while (src != last_vertex) + { + + VectorSubtract(src->xyz, r_origin, dir); + dir[2] *= 3; // flatten the sphere + + const float length = 6 * 63 / sqrtf(DotProduct(dir, dir)); + + dir[0] *= length; + dir[1] *= length; + + dst->st[0] = (speedscale + dir[0]) * (1.0f / 128.0f); + dst->st[1] = (speedscale + dir[1]) * (1.0f / 128.0f); + dst->xyz[0] = src->xyz[0]; + dst->xyz[1] = src->xyz[1]; + dst->xyz[2] = src->xyz[2]; + + // Next vertex. + ++src; + ++dst; + } + + // Do these vertices need clipped? + if (clipping::is_clipping_required(unclipped_vertices, unclipped_vertex_count)) + { + // Clip the polygon. + const glvert_t* clipped_vertices; + std::size_t clipped_vertex_count; + clipping::clip( + unclipped_vertices, + unclipped_vertex_count, + &clipped_vertices, + &clipped_vertex_count); + + // Any vertices left? + if (clipped_vertex_count) + { + // Copy the vertices to the display list. + const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t); + glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size)); + memcpy(display_list_vertices, clipped_vertices, buffer_size); + + // Draw the clipped vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + clipped_vertex_count, 0, display_list_vertices); + } + } + else + { + // Draw the vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + unclipped_vertex_count, 0, unclipped_vertices); + } + } + +} + + +/* +============= +EmitScrollPolys + +Does a scroll on the pre-fragmented glpoly_t chain +============= +*/ +void EmitScrollPolys (msurface_t *fa) +{ + const float real_time = static_cast(realtime); + const float scroll = (-64 * ((real_time*0.5) - (int)(real_time*0.5))); + + // For each polygon... + for (const glpoly_t* p = fa->polys; p; p = p->next) + { + // Allocate memory for this polygon. + const int unclipped_vertex_count = p->numverts; + glvert_t* const unclipped_vertices = + static_cast(sceGuGetMemory(sizeof(glvert_t) * unclipped_vertex_count)); + + // Generate each vertex. + const glvert_t* src = p->verts; + const glvert_t* last_vertex = src + unclipped_vertex_count; + glvert_t* dst = unclipped_vertices; + + while (src != last_vertex) + { + // Fill in the vertex data. + dst->st[0] = src->st[0] + scroll; + dst->st[1] = src->st[1]; + dst->xyz[0] = src->xyz[0]; + dst->xyz[1] = src->xyz[1]; + dst->xyz[2] = src->xyz[2]; + + // Next vertex. + ++src; + ++dst; + } + + // Do these vertices need clipped? + if (clipping::is_clipping_required(unclipped_vertices, unclipped_vertex_count)) + { + // Clip the polygon. + const glvert_t* clipped_vertices; + std::size_t clipped_vertex_count; + clipping::clip( + unclipped_vertices, + unclipped_vertex_count, + &clipped_vertices, + &clipped_vertex_count); + + // Any vertices left? + if (clipped_vertex_count) + { + // Copy the vertices to the display list. + const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t); + glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size)); + memcpy(display_list_vertices, clipped_vertices, buffer_size); + + // Draw the clipped vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + clipped_vertex_count, 0, display_list_vertices); + } + } + else + { + // Draw the vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + unclipped_vertex_count, 0, unclipped_vertices); + } + } +} + + +extern int ref_texture; + +/* +============= +EmitReflectivePolys + +Does a reflective warp on the pre-fragmented glpoly_t chain +============= +*/ +void EmitReflectivePolys (msurface_t *fa) +{ + // For each polygon... + for (const glpoly_t* p = fa->polys; p; p = p->next) + { + // Allocate memory for this polygon. + const int unclipped_vertex_count = p->numverts; + glvert_t* const unclipped_vertices = + static_cast(sceGuGetMemory(sizeof(glvert_t) * unclipped_vertex_count)); + + // Generate each vertex. + const glvert_t* src = p->verts; + const glvert_t* last_vertex = src + unclipped_vertex_count; + glvert_t* dst = unclipped_vertices; + + while (src != last_vertex) + { + vec3_t dir; + VectorSubtract(src->xyz, r_origin, dir); + dir[2] *= 3; // flatten the sphere + + const float length = 6 * 63 / sqrtf(DotProduct(dir, dir)); + + dir[0] *= length; + dir[1] *= length; + + dst->st[0] = (dir[0]) * (1.0f / 256.0f); + dst->st[1] = (dir[1]) * (1.0f / 256.0f); + dst->xyz[0] = src->xyz[0]; + dst->xyz[1] = src->xyz[1]; + dst->xyz[2] = src->xyz[2]; + + // Next vertex. + ++src; + ++dst; + } + + // Do these vertices need clipped? + if (clipping::is_clipping_required(unclipped_vertices, unclipped_vertex_count)) + { + // Clip the polygon. + const glvert_t* clipped_vertices; + std::size_t clipped_vertex_count; + clipping::clip( + unclipped_vertices, + unclipped_vertex_count, + &clipped_vertices, + &clipped_vertex_count); + + // Any vertices left? + if (clipped_vertex_count) + { + // Copy the vertices to the display list. + const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t); + glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size)); + memcpy(display_list_vertices, clipped_vertices, buffer_size); + + // Draw the clipped vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + clipped_vertex_count, 0, display_list_vertices); + } + } + else + { + // Draw the vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + unclipped_vertex_count, 0, unclipped_vertices); + } + } +} + +/* +=============== +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) +{ + GL_Bind (solidskytexture); + + speedscale = realtime*8; + speedscale -= (int)speedscale & ~127 ; + + EmitSkyPolys (fa); + + sceGuEnable(GU_BLEND); + + GL_Bind (alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale & ~127 ; + + EmitSkyPolys (fa); + + sceGuDisable(GU_BLEND); +} + +/* +=============== +R_DrawScroll_SkyChain +=============== +*/ +void R_DrawScroll_SkyChain (msurface_t *s) +{ + msurface_t *fa; + + GL_Bind(solidskytexture); + + speedscale = realtime*8; + speedscale -= (int)speedscale & ~127 ; + + for (fa=s ; fa ; fa=fa->texturechain) + EmitSkyPolys (fa); + + sceGuEnable(GU_BLEND); + + GL_Bind (alphaskytexture); + + speedscale = realtime*16; + speedscale -= (int)speedscale & ~127 ; + + for (fa=s ; fa ; fa=fa->texturechain) + EmitSkyPolys (fa); + + sceGuDisable(GU_BLEND); +} + +/* +=============== +R_DrawFlat_SkyChain +=============== +*/ +void R_DrawFlat_SkyChain (msurface_t *s) +{ + msurface_t *fa; + + sceGuDisable (GU_TEXTURE_2D); + + byte *sky_color = StringToRGB (r_skycolor.string); //Get color + sceGuColor(GU_RGBA(sky_color[0], sky_color[1], sky_color[2], 255)); + + for (fa = s ; fa ; fa = fa->texturechain) + EmitFlatPoly (fa); + + sceGuColor(0xffffffff); + sceGuEnable (GU_TEXTURE_2D); +} + +/* +================================================================= + + Quake 2 environment sky + +================================================================= +*/ +void UnloadSkyTexture (void) +{ + for (int i = 0; i < 6; i++) + { + if (skyimage[i]) + GL_UnloadTexture(skyimage[i]); + skyimage[i] = NULL; + } +} + +/* +================== +R_LoadSkys +================== +*/ +extern int nonetexture; +void Sky_LoadSkyBox (char *name) +{ + if (strcmp(skybox_name, name) == 0) + return; //no change + + //purge old sky textures + UnloadSkyTexture (); + + //turn off skybox if sky is set to "" + if (name[0] == '0') + { + skybox_name[0] = 0; + + //if map don't have sky + if (solidskytexture == -1) + solidskytexture = nonetexture; + if (alphaskytexture == -1) + alphaskytexture = nonetexture; + + return; + } + + for (int i = 0; i < 6; i++) + { + int mark = Hunk_LowMark (); + if(!(skyimage[i] = loadtextureimage (va("gfx/env/%s%s", name, suf[i]), 0, 0, qfalse, GU_LINEAR)) && + !(skyimage[i] = loadtextureimage (va("gfx/env/%s_%s", name, suf[i]), 0, 0, qfalse, GU_LINEAR))) + { + Con_Printf("Sky: %s[%s] not found, used std\n", name, suf[i]); + if(!(skyimage[i] = loadtextureimage (va("gfx/env/skybox%s", suf[i]), 0, 0, qfalse, GU_LINEAR))) + { + Sys_Error("STD SKY NOT FOUND!"); + } + + } + Hunk_FreeToLowMark (mark); + } + strcpy(skybox_name, name); +} + +/* +================= +Sky_NewMap +================= +*/ +void Sky_NewMap (void) +{ + char key[128], value[4096]; + char *data; + + //purge old sky textures + UnloadSkyTexture (); + + // + // initially no sky + // + Sky_LoadSkyBox (""); //not used + + // + // read worldspawn (this is so ugly, and shouldn't it be done on the server?) + // + data = cl.worldmodel->entities; + if (!data) + return; //FIXME: how could this possibly ever happen? -- if there's no + // worldspawn then the sever wouldn't send the loadmap message to the client + + data = COM_Parse(data); + + if (!data) //should never happen + return; // error + + if (com_token[0] != '{') //should never happen + return; // error + + while (1) + { + data = COM_Parse(data); + + if (!data) + return; // error + + if (com_token[0] == '}') + break; // end of worldspawn + + if (com_token[0] == '_') + strcpy(key, com_token + 1); + else + strcpy(key, com_token); + while (key[strlen(key)-1] == ' ') // remove trailing spaces + key[strlen(key)-1] = 0; + + data = COM_Parse(data); + if (!data) + return; // error + + strcpy(value, com_token); + + if (!strcmp("sky", key)) + Sky_LoadSkyBox(value); + else if (!strcmp("skyname", key)) //half-life + Sky_LoadSkyBox(value); + else if (!strcmp("qlsky", key)) //quake lives + Sky_LoadSkyBox(value); + } +} + +/* +================= +Sky_SkyCommand_f +================= +*/ +void Sky_SkyCommand_f (void) +{ + switch (Cmd_Argc()) + { + case 1: + Con_Printf("\"sky\" is \"%s\"\n", skybox_name); + break; + case 2: + Sky_LoadSkyBox(Cmd_Argv(1)); + break; + default: + Con_Printf("usage: sky \n"); + } +} + +/* +============= +Sky_Init +============= +*/ +void Sky_Init (void) +{ + int i; + + Cmd_AddCommand ("sky",Sky_SkyCommand_f); + + for (i=0; i<6; i++) + skyimage[i] = NULL; +} + +static vec3_t skyclip[6] = { + {1,1,0}, + {1,-1,0}, + {0,-1,1}, + {0,1,1}, + {1,0,1}, + {-1,0,1} +}; +int c_sky; + +// 1 = s, 2 = t, 3 = 2048 +static int st_to_vec[6][3] = +{ + {3,-1,2}, + {-3,1,2}, + + {1,3,2}, + {-1,-3,2}, + + {-2,-1,3}, // 0 degrees yaw, look straight up + {2,-1,-3} // look straight down + +// {-1,2,3}, +// {1,2,-3} +}; + +// s = [0]/[2], t = [1]/[2] +static int vec_to_st[6][3] = +{ + {-2,3,1}, + {2,3,-1}, + + {1,3,2}, + {-1,3,-2}, + + {-2,-1,3}, + {-2,1,-3} + +// {-1,2,3}, +// {1,2,-3} +}; + +static float skymins[2][6], skymaxs[2][6]; + +static void DrawSkyPolygon (int nump, vec3_t vecs) +{ + int i,j,axis; + float s,t,dv,*vp; + vec3_t v, av; + + c_sky++; + + // decide which face it maps to + VectorCopy (vec3_origin, v); + for (i=0, vp=vecs ; i av[1] && av[0] > av[2]) + axis = (v[0] < 0) ? 1 : 0; + else if (av[1] > av[2] && av[1] > av[0]) + axis = (v[1] < 0) ? 3 : 2; + else + axis = (v[2] < 0) ? 5 : 4; + + // project new texture coords + for (i=0 ; i 0) ? vecs[j - 1] : -vecs[-j - 1]; + + j = vec_to_st[axis][0]; + s = (j < 0) ? -vecs[-j -1] / dv : vecs[j-1] / dv; + + j = vec_to_st[axis][1]; + t = (j < 0) ? -vecs[-j -1] / dv : vecs[j-1] / dv; + + if (s < skymins[0][axis]) + skymins[0][axis] = s; + if (t < skymins[1][axis]) + skymins[1][axis] = t; + if (s > skymaxs[0][axis]) + skymaxs[0][axis] = s; + if (t > skymaxs[1][axis]) + skymaxs[1][axis] = t; + } +} + +#define MAX_CLIP_VERTS 64 +void ClipSkyPolygon (int nump, vec3_t vecs, int stage) +{ + float *norm; + float *v; + qboolean front, back; + float d, e; + float dists[MAX_CLIP_VERTS]; + int sides[MAX_CLIP_VERTS]; + vec3_t newv[2][MAX_CLIP_VERTS]; + int newc[2]; + int i, j; + + if (nump > MAX_CLIP_VERTS-2) + Sys_Error ("ClipSkyPolygon: MAX_CLIP_VERTS"); + if (stage == 6) + { // fully clipped, so draw it + DrawSkyPolygon (nump, vecs); + return; + } + + front = back = qfalse; + norm = skyclip[stage]; + for (i=0, v = vecs ; i ON_EPSILON) + { + front = qtrue; + sides[i] = SIDE_FRONT; + } + else if (d < ON_EPSILON) + { + back = qtrue; + sides[i] = SIDE_BACK; + } + else + sides[i] = SIDE_ON; + dists[i] = d; + } + + if (!front || !back) + { // not clipped + ClipSkyPolygon (nump, vecs, stage+1); + return; + } + + // clip it + sides[i] = sides[0]; + dists[i] = dists[0]; + VectorCopy (vecs, (vecs+(i*3)) ); + newc[0] = newc[1] = 0; + + for (i=0, v = vecs ; i 511.0f/512.0f) + s = 511.0f/512.0f; + + if (t < 1.0f/512.0f) + t = 1.0f/512.0f; + else if (t > 511.0f/512.0f) + t = 511.0f/512.0f; + + t = 1.0f - t; + + s_axis = s; + t_axis = t; +} + +void Fog_EnableGFog (void); +void Fog_DisableGFog (void); +void Fog_SetColorForSkyS (void); +void Fog_SetColorForSkyE (void); +/* +============== +R_DrawSkyBox +============== +*/ +void R_DrawSkyBox (void) +{ + int i; + + Fog_DisableGFog(); //setup for Sky + Fog_SetColorForSkyS(); //setup for Sky + + //sceGuDepthRange(32767, 65535); //not used + + for (i=0 ; i<6 ; i++) + { + // Allocate memory for this polygon. + const int unclipped_vertex_count = 4; + glvert_t* const unclipped_vertices = + static_cast(sceGuGetMemory(sizeof(glvert_t) * unclipped_vertex_count)); + + if (skymins[0][i] >= skymaxs[0][i] + || skymins[1][i] >= skymaxs[1][i]) + continue; + + GL_Bind (skyimage[skytexorder[i]]); + + MakeSkyVec (skymins[0][i], skymins[1][i], i); + + unclipped_vertices[0].st[0] = s_axis; + unclipped_vertices[0].st[1] = t_axis; + unclipped_vertices[0].xyz[0] = v_axis[0]; + unclipped_vertices[0].xyz[1] = v_axis[1]; + unclipped_vertices[0].xyz[2] = v_axis[2]; + + MakeSkyVec (skymins[0][i], skymaxs[1][i], i); + + unclipped_vertices[1].st[0] = s_axis; + unclipped_vertices[1].st[1] = t_axis; + unclipped_vertices[1].xyz[0] = v_axis[0]; + unclipped_vertices[1].xyz[1] = v_axis[1]; + unclipped_vertices[1].xyz[2] = v_axis[2]; + + MakeSkyVec (skymaxs[0][i], skymaxs[1][i], i); + + unclipped_vertices[2].st[0] = s_axis; + unclipped_vertices[2].st[1] = t_axis; + unclipped_vertices[2].xyz[0] = v_axis[0]; + unclipped_vertices[2].xyz[1] = v_axis[1]; + unclipped_vertices[2].xyz[2] = v_axis[2]; + + MakeSkyVec (skymaxs[0][i], skymins[1][i], i); + + unclipped_vertices[3].st[0] = s_axis; + unclipped_vertices[3].st[1] = t_axis; + unclipped_vertices[3].xyz[0] = v_axis[0]; + unclipped_vertices[3].xyz[1] = v_axis[1]; + unclipped_vertices[3].xyz[2] = v_axis[2]; + + if (clipping::is_clipping_required( + unclipped_vertices, + unclipped_vertex_count)) + { + // Clip the polygon. + const glvert_t* clipped_vertices; + std::size_t clipped_vertex_count; + clipping::clip( + unclipped_vertices, + unclipped_vertex_count, + &clipped_vertices, + &clipped_vertex_count); + + // Did we have any vertices left? + if (clipped_vertex_count) + { + // Copy the vertices to the display list. + const std::size_t buffer_size = clipped_vertex_count * sizeof(glvert_t); + glvert_t* const display_list_vertices = static_cast(sceGuGetMemory(buffer_size)); + memcpy(display_list_vertices, clipped_vertices, buffer_size); + + // Draw the clipped vertices. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + clipped_vertex_count, 0, display_list_vertices); + } + } + else + { + // Draw the poly directly. + sceGuDrawArray( + GU_TRIANGLE_FAN, + GU_TEXTURE_32BITF | GU_VERTEX_32BITF, + unclipped_vertex_count, 0, unclipped_vertices); + } + } + //sceGuDepthRange(0, 65535); + Fog_SetColorForSkyE(); //setup for Sky + Fog_EnableGFog(); //setup for Sky + +} + +//=============================================================== + +/* +================= +R_DrawSkyChain +================= +*/ +void R_DrawSkyChain (msurface_t *s) +{ + msurface_t *fa; + int i; + vec3_t verts[MAX_CLIP_VERTS]; + glpoly_t *p; + + if (r_fastsky.value || !skybox_name[0]) + { + R_DrawFlat_SkyChain (s); + } + else + { + if (skybox_name[0]) // if the skybox has a name, draw the skybox + { + c_sky = 0; + + // calculate vertex values for sky box + for (fa=s ; fa ; fa=fa->texturechain) + { + for (p=fa->polys ; p ; p=p->next) + { + for (i=0 ; inumverts ; i++) + { + VectorSubtract (p->verts[i].xyz, r_origin, verts[i]); + } + ClipSkyPolygon (p->numverts, verts[0], 0); + } + + } + } + else // otherwise, draw the normal quake sky + { + R_DrawScroll_SkyChain (s); + } + } +} + +//=============================================================== + +/* +============= +R_InitSky + +A sky texture is 256*128, with the right side being a masked overlay +============== +*/ +void R_InitSky (byte *mt) +{ + byte trans[128*128]; + + const byte* const src = (byte *)mt; + + for (int i=0 ; i<128 ; i++) + { + for (int j=0 ; j<128 ; j++) + { + const byte p = src[i*256 + j + 128]; + trans[(i*128) + j] = p; + } + } + + if (solidskytexture == -1) + solidskytexture = GL_LoadTexture("solidskytexture", 128, 128, trans, qfalse, GU_LINEAR, 0); + + for (int i=0 ; i<128 ; i++) + { + for (int j=0 ; j<128 ; j++) + { + const byte p = src[i*256 + j]; + if (p == 0) + trans[(i*128) + j] = 255; + else + trans[(i*128) + j] = p; + } + } + + if (alphaskytexture == -1) + alphaskytexture = GL_LoadTexture("alphaskytexture", 128, 128, trans, qfalse, GU_LINEAR, 0); +} + diff --git a/source/psp/vram.cpp b/source/psp/vram.cpp new file mode 100644 index 0000000..4f12d52 --- /dev/null +++ b/source/psp/vram.cpp @@ -0,0 +1,162 @@ +/* +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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. + +*/ + +#define VRAM_DEBUGGING 0 + +#include "vram.hpp" + +#include +#include + +extern "C" +{ +#include "../quakedef.h" +} + +namespace quake +{ + namespace vram + { + typedef std::vector allocated_list; + + static char* const base = static_cast(sceGeEdramGetAddr()); + static const std::size_t block_size = 32 * 32; + static const std::size_t block_count = sceGeEdramGetSize() / block_size; + static allocated_list allocated(block_count, 0); + static std::size_t bytes_required = 0; + static std::size_t bytes_allocated = 0; + + void* allocate(std::size_t size) + { + // How many blocks to allocate? + bytes_required += size; + std::size_t blocks_required = (size + block_size - 1) / block_size; + +#if VRAM_DEBUGGING + Con_Printf("vram::allocate %u bytes (%u blocks)\n", size, blocks_required); + Con_Printf("\tblock_size = %u\n", block_size); + Con_Printf("\tblock_count = %u\n", block_count); +#endif + + // Find a sequential area this big. + for (std::size_t start = 0; start < (block_count - blocks_required);) + { + // Is this block allocated? + const std::size_t allocated_blocks = allocated.at(start); + if (allocated_blocks) + { + // Skip the allocated block. + start += allocated_blocks; + +#if VRAM_DEBUGGING + //Con_Printf("\tskipping from block %u to %u\n", start - allocated_blocks, start); +#endif + } + else + { + // Where would the allocated block end? + const std::size_t end = start + blocks_required; + +#if VRAM_DEBUGGING + //Con_Printf("\ttrying blocks %u to %u\n", start, end - 1); +#endif + + // Check for allocated blocks in the area we want. + std::size_t free_blocks_here = 1; + for (std::size_t b = start + 1; b < end; ++b) + { + if (allocated.at(b)) + { + break; + } + else + { + ++free_blocks_here; + } + } + + // Is the block big enough? + if (free_blocks_here >= blocks_required) + { + // Mark it as allocated. +#if VRAM_DEBUGGING + Con_Printf("\tmarking blocks %u to %u as allocated\n", start, end - 1); +#endif + bytes_allocated += (blocks_required * block_size); + for (std::size_t b = start; b < end; ++b) + { + allocated.at(b) = blocks_required--; + } + + // Done. +#if VRAM_DEBUGGING + Con_Printf("\tdone (%u allocated, %u required)\n", bytes_allocated, bytes_required); +#endif + return base + (block_size * start); + } + else + { + // Move on. + start += free_blocks_here; +#if VRAM_DEBUGGING + Con_Printf("\tskipping from block %u to %u, because there wasn't a big enough run of free blocks\n", + start - free_blocks_here, start); +#endif + } + } + } + + // Failed. +#if VRAM_DEBUGGING + Con_Printf("\tfailed, no free blocks big enough (%u allocated, %u required)\n", + bytes_allocated, bytes_required); +#endif + return 0; + } + + void free(void* memory) + { + // Which block is this? + const std::size_t relative_address = static_cast(memory) - base; + const std::size_t block_index = relative_address / block_size; + +#if VRAM_DEBUGGING + Con_Printf("vram::free freeing blocks starting with %u\n", block_index); +#endif + + // Mark the blocks as deallocated. + const std::size_t blocks_to_free = allocated.at(block_index); +#if VRAM_DEBUGGING + Con_Printf("\tfreeing to %u\n", block_index + blocks_to_free - 1); +#endif + for (std::size_t block = 0; block < blocks_to_free; ++block) + { + allocated.at(block_index + block) = 0; + } + + const std::size_t bytes_to_free = blocks_to_free * block_size; + bytes_allocated -= bytes_to_free; + bytes_required -= bytes_to_free; +#if VRAM_DEBUGGING + Con_Printf("\tnow %u allocated, %u required\n", bytes_allocated, bytes_required); +#endif + } + } +} diff --git a/source/psp/vram.hpp b/source/psp/vram.hpp new file mode 100644 index 0000000..12f67d3 --- /dev/null +++ b/source/psp/vram.hpp @@ -0,0 +1,39 @@ +/* +Copyright (C) 2007 Peter Mackay and Chris Swindle. + +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 QUAKE_VRAM_HPP +#define QUAKE_VRAM_HPP + +#include + +namespace quake +{ + namespace vram + { + // Allocates a block of VRAM. + void* allocate(std::size_t size); + + // Frees a block of VRAM. + void free(void* const memory); + } +} + +#endif + diff --git a/source/psp/wad3.cpp b/source/psp/wad3.cpp new file mode 100644 index 0000000..1eef404 --- /dev/null +++ b/source/psp/wad3.cpp @@ -0,0 +1,499 @@ +/* +Copyright (C) 2009 Crow_bar. + +Used code from "Fuhquake" modify by Crow_bar + +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. + +*/ + +extern "C" +{ +#include "../quakedef.h" +} +#include + +#include + +using namespace std; + +list UnloadFileList; + +#define TEXWAD_MAXIMAGES 16384 +typedef struct +{ + char name[16]; + FILE *file; + int position; + int size; +} texwadlump_t; + +texwadlump_t texwadlump[TEXWAD_MAXIMAGES]; +int numwadtextures; + +//By Crow_bar +void UnloadWads (void) +{ + FILE *files; + + while (UnloadFileList.size() > 0) + { + files = UnloadFileList.front(); + UnloadFileList.pop_front(); + fclose(files); + } + numwadtextures = 0; +} + +void WAD3_LoadTextureWadFile (char *filename) +{ + lumpinfo_t *lumps, *lump_p; + wadinfo_t header; + int i, j, infotableofs, numlumps, lowmark; + FILE *file; + + if (FS_FOpenFile (va("textures/wad3/%s", filename), &file) != -1) + goto loaded; + if (FS_FOpenFile (va("textures/halflife/%s", filename), &file) != -1) + goto loaded; + if (FS_FOpenFile (va("textures/%s", filename), &file) != -1) + goto loaded; + if (FS_FOpenFile (filename, &file) != -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"); + fclose(file); + return; + } + + if (memcmp(header.identification, "WAD3", 4)) + { + Con_Printf ("WAD3_LoadTextureWadFile: Wad file %s doesn't have WAD3 id\n",filename); + fclose(file); + return; + } + + numlumps = LittleLong(header.numlumps); + + if (numlumps < 1 || numlumps > TEXWAD_MAXIMAGES) + { + Con_Printf ("WAD3_LoadTextureWadFile: invalid number of lumps (%i)\n", numlumps); + fclose(file); + return; + } + + infotableofs = LittleLong(header.infotableofs); + + if (fseek(file, infotableofs, SEEK_SET)) + { + Con_Printf ("WAD3_LoadTextureWadFile: unable to seek to lump table"); + fclose(file); + return; + } + + lowmark = Hunk_LowMark(); + + if (!(lumps = static_cast(Hunk_Alloc(sizeof(lumpinfo_t) * numlumps)))) + { + Con_Printf ("WAD3_LoadTextureWadFile: unable to allocate temporary memory for lump table"); + fclose(file); + return; + } + + if (fread(lumps, 1, sizeof(lumpinfo_t) * numlumps, file) != sizeof(lumpinfo_t) * numlumps) + { + Con_Printf ("WAD3_LoadTextureWadFile: unable to read lump table"); + fclose(file); + Hunk_FreeToLowMark(lowmark); + return; + } + + UnloadFileList.push_back(file); //Crow_bar. UnloadWads code + + for (i = 0, lump_p = lumps; i < numlumps; i++,lump_p++) + { + W_CleanupName (lump_p->name, lump_p->name); + for (j = 0;j < numwadtextures;j++) + { + if (!strcmp(lump_p->name, texwadlump[j].name)) // name match, replace old one + break; + } + if (j >= TEXWAD_MAXIMAGES) + break; // abort loading + if (j == numwadtextures) + { + W_CleanupName (lump_p->name, texwadlump[j].name); + texwadlump[j].file = file; + texwadlump[j].position = LittleLong(lump_p->filepos); + texwadlump[j].size = LittleLong(lump_p->disksize); + numwadtextures++; + } + } + + Hunk_FreeToLowMark(lowmark); + //leaves the file open +} + +//converts paletted to rgba +int 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]); + pal = in + ((image_size * 85) >> 6) + 2; + + + data = (byte*)malloc(image_size); + for (i = 0; i < image_size; i++) + { + p = *in++; +/* + if (tex->name[0] == '{' && p == 255) + data[i] = 0; + else +*/ + data[i] = p; + } + + + + int level = 0; + if (r_mipmaps.value > 0) + level = 3; + + int index = GL_LoadPalTex (tex->name, tex->width, tex->height, (const byte*)data, qtrue, GU_LINEAR, level, (byte *)pal, PAL_RGB); + + free(data); + + return index; + +} + +int WAD3_LoadTexture(miptex_t *mt) +{ + char texname[MAX_QPATH]; + int i, j, lowmark = 0; + FILE *file; + miptex_t *tex; + int index; + + if (mt->offsets[0]) + return ConvertWad3ToRGBA(mt); + + texname[sizeof(texname) - 1] = 0; + W_CleanupName (mt->name, texname); + + for (i = 0;i < numwadtextures;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)) + { + fclose(file); + Con_Printf("WAD3_LoadTexture: corrupt WAD3 file"); + return 0; + } + + lowmark = Hunk_LowMark(); + tex = static_cast(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 0; + } + + tex->width = LittleLong(tex->width); + tex->height = LittleLong(tex->height); + + if (tex->width != mt->width || tex->height != mt->height) + { + Hunk_FreeToLowMark(lowmark); + return 0; + } + + for (j = 0;j < MIPLEVELS;j++) + tex->offsets[j] = LittleLong(tex->offsets[j]); + + index = ConvertWad3ToRGBA(tex); + + Hunk_FreeToLowMark(lowmark); + + return index; + } + return 0; +} + +int WAD3_LoadTextureName(char *name) +{ + char texname[MAX_QPATH]; + int i, j, lowmark = 0; + FILE *file; + miptex_t *tex; + int index; + + texname[sizeof(texname) - 1] = 0; + W_CleanupName (name, texname); + + for (i = 0;i < numwadtextures;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)) + { + fclose(file); + Con_Printf("WAD3_LoadTexture: corrupt WAD3 file"); + return 0; + } + + lowmark = Hunk_LowMark(); + tex = static_cast(Hunk_Alloc(texwadlump[i].size)); + + if (fread(tex, 1, texwadlump[i].size, file) < texwadlump[i].size) + { + Con_Printf("WAD3_LoadTexture: corrupt WAD3 file"); + fclose(file); + Hunk_FreeToLowMark(lowmark); + return 0; + } + + tex->width = LittleLong(tex->width); + tex->height = LittleLong(tex->height); +#if 0 + if (tex->width != mt->width || tex->height != mt->height) + { + fclose(file); + Hunk_FreeToLowMark(lowmark); + return 0; + } +#endif + for (j = 0;j < MIPLEVELS;j++) + tex->offsets[j] = LittleLong(tex->offsets[j]); + + index = ConvertWad3ToRGBA(tex); + + UnloadFileList.push_back(file); //Crow_bar. UnloadWads code +/* + fclose(file); +*/ + Hunk_FreeToLowMark(lowmark); + + return index; + } + return 0; +} + +//Other wad3 loaders +void W_LoadTextureWadFileHL (char *filename, int complain) +{ + lumpinfo_t *lumps, *lump_p; + wadinfo_t header; + unsigned i, j; + int infotableofs; + FILE *file; + int numlumps; + + FS_FOpenFile (filename, &file); + if (!file) + { + if (complain) + Con_Printf ("W_LoadTextureWadFile: couldn't find %s\n", filename); + return; + } + + if (fread(&header, sizeof(wadinfo_t), 1, file) != 1) + {Con_Printf ("W_LoadTextureWadFile: unable to read wad header");return;} + + if(header.identification[0] != 'W' + || header.identification[1] != 'A' + || header.identification[2] != 'D' + || header.identification[3] != '3') + { + fclose(file); + Con_Printf ("W_LoadTextureWadFile: Wad file %s doesn't have WAD3 id\n",filename); + return; + } + + numlumps = LittleLong(header.numlumps); + + if (numlumps < 1 || numlumps > TEXWAD_MAXIMAGES) + { + fclose(file); + Con_Printf ("W_LoadTextureWadFile: invalid number of lumps (%i)\n", numlumps); + return; + } + infotableofs = LittleLong(header.infotableofs); + + if (fseek(file, infotableofs, SEEK_SET)) + { + fclose(file); + Con_Printf ("W_LoadTextureWadFile: unable to seek to lump table"); + return; + } + + if (!(lumps = static_cast(malloc(sizeof(lumpinfo_t)*numlumps)))) + { + fclose(file); + Con_Printf ("W_LoadTextureWadFile: unable to allocate temporary memory for lump table"); + return; + } + + if (fread(lumps, sizeof(lumpinfo_t), numlumps, file) != (unsigned)numlumps) + { + fclose(file); + Con_Printf ("W_LoadTextureWadFile: unable to read lump table"); + return; + } + + for (i=0, lump_p = lumps ; i<(unsigned)numlumps ; i++,lump_p++) + { + W_CleanupName (lump_p->name, lump_p->name); + for (j = 0;j < TEXWAD_MAXIMAGES;j++) + { + if (texwadlump[j].name[0]) // occupied slot, check the name + { + if (!strcmp(lump_p->name, texwadlump[j].name)) // name match, replace old one + break; + } + else // empty slot + break; + } + if (j >= TEXWAD_MAXIMAGES) + break; // abort loading + + W_CleanupName (lump_p->name, texwadlump[j].name); + texwadlump[j].file = file; + texwadlump[j].position = LittleLong(lump_p->filepos); + texwadlump[j].size = LittleLong(lump_p->disksize); + } + free(lumps); + //fclose(file); + // leaves the file open +} + +byte *W_ConvertWAD3TextureHL(miptex_t *tex) +{ + byte *in, *data, *out, *pal; + int d, p, image_size; + + in = (byte *)((int) tex + tex->offsets[0]); + data = out = static_cast(malloc(tex->width * tex->height * 4)); + + if (!data) + return NULL; + + image_size = tex->width * tex->height; + + pal = in + (((image_size) * 85) >> 6); + pal += 2; + + for (d = 0;d < image_size;d++) + { + p = *in++; + if (tex->name[0] == '{' && p == 255) + out[0] = out[1] = out[2] = out[3] = 0; + else + { + p *= 3; + out[0] = pal[p]; + out[1] = pal[p+1]; + out[2] = pal[p+2]; + out[3] = 255; + } + out += 4; + } + return data; +} + +byte *W_GetTextureHL(char *name) +{ + char texname[17]; + int i, j; + FILE *file; + miptex_t *tex; + byte *data; + + texname[16] = 0; + + W_CleanupName (name, texname); + + for (i = 0;i < TEXWAD_MAXIMAGES;i++) + { + if (texwadlump[i].name[0]) + { + if (!strcmp(texname, texwadlump[i].name)) // found it + { + file = texwadlump[i].file; + if (fseek(file, texwadlump[i].position, SEEK_SET)) + { + Con_Printf("W_GetTexture: corrupt WAD3 file"); + return NULL; + } + + tex = static_cast(malloc(texwadlump[i].size)); + + if (!tex) + return NULL; + + if (fread(tex, 1, texwadlump[i].size, file) < (unsigned)texwadlump[i].size) + { + Con_Printf("W_GetTexture: corrupt WAD3 file"); + return NULL; + } + + tex->width = LittleLong(tex->width); + tex->height = LittleLong(tex->height); + for (j = 0;j < MIPLEVELS;j++) + tex->offsets[j] = LittleLong(tex->offsets[j]); + + data = W_ConvertWAD3TextureHL(tex); + + free(tex); + fclose(file); + return data; + } + } + else + break; + } + + return NULL; +} diff --git a/source/quakedef.h b/source/quakedef.h new file mode 100644 index 0000000..c0a4bb9 --- /dev/null +++ b/source/quakedef.h @@ -0,0 +1,363 @@ +/* +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 QUAKE_GAME // as opposed to utilities + +#define VERSION 0.2 //0.11/0.44/0.88/1.0/ +#define D3DQUAKE_VERSION 0.01 +#define WINQUAKE_VERSION 0.996 +#define LINUX_VERSION 1.30 +#define X11_VERSION 1.10 + + +#define GAMENAME "nzp" + +#include +#include +#include +#include +#include +#include +//#include // For QMB assert + + + +#define VID_LockBuffer() +#define VID_UnlockBuffer() + + +#if defined __i386__ +#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 UNUSED(x) (x = x) // for pesky compiler / lint warnings + +#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 +*/ + +#define MAX_MSGLEN 64000 // max length of a reliable message Crow_Bar. UP for PSP +#define MAX_DATAGRAM 8000 // max length of unreliable message Crow_Bar. UP for PSP + +// +// per-level limits +// +#define MAX_EDICTS 600 // FIXME: ouch! ouch! ouch! +#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 + +// +// stats are integers communicated to the client by the server +// +#define MAX_CL_STATS 32 +#define STAT_HEALTH 0 +#define STAT_points 1 +#define STAT_WEAPON 2 +#define STAT_AMMO 3 +#define STAT_WEAPONFRAME 5 +#define STAT_CURRENTMAG 6 +#define STAT_ZOOM 7 +#define STAT_WEAPONSKIN 8 +#define STAT_ACTIVEWEAPON 10 +#define STAT_ROUNDS 11 +#define STAT_ROUNDCHANGE 12 +#define STAT_X2 13 +#define STAT_INSTA 14 +#define STAT_PRIGRENADES 15 +#define STAT_SECGRENADES 4 +#define STAT_GRENADES 9 +#define STAT_WEAPON2 17 +#define STAT_WEAPON2SKIN 18 +#define STAT_WEAPON2FRAME 19 +#define STAT_CURRENTMAG2 20 + +// stock defines + +#define W_COLT 1 +#define W_KAR 2 +#define W_THOMPSON 3 +#define W_357 4 +#define W_BAR 5 +#define W_BK 6 +#define W_BROWNING 7 +#define W_DB 8 +#define W_FG 9 +#define W_GEWEHR 10 +#define W_KAR_SCOPE 11 +#define W_M1 12 +#define W_M1A1 13 +#define W_M2 14 +#define W_MP40 15 +#define W_MG 16 +#define W_PANZER 17 +#define W_PPSH 18 +#define W_PTRS 19 +#define W_RAY 20 +#define W_SAWNOFF 21 +#define W_STG 22 +#define W_TRENCH 23 +#define W_TYPE 24 + +#define W_BIATCH 28 +#define W_KILLU 29 //357 +#define W_COMPRESSOR 30 // Gewehr +#define W_M1000 31 //garand +//#define W_KOLLIDER 32 +#define W_PORTER 33 // Ray +#define W_WIDDER 34 // M1A1 +#define W_FIW 35 //upgraded flamethrower +#define W_ARMAGEDDON 36 //Kar +//#define W_WUNDER 37 +#define W_GIBS 38 // thompson +#define W_SAMURAI 39 //Type +#define W_AFTERBURNER 40 //mp40 +#define W_SPATZ 41 // stg +#define W_SNUFF 42 // sawn off +#define W_BORE 43 // double barrel +#define W_IMPELLER 44 //fg +#define W_BARRACUDA 45 //mg42 +#define W_ACCELERATOR 46 //M1919 browning +#define W_GUT 47 //trench +#define W_REAPER 48 //ppsh +#define W_HEADCRACKER 49 //scoped kar +#define W_LONGINUS 50 //panzer +#define W_PENETRATOR 51 //ptrs +#define W_WIDOW 52 //bar +//#define W_KRAUS 53 //ballistic +#define W_MP5 54 +#define W_M14 55 + +#define W_TESLA 56 +#define W_DG3 57 //tesla + +#define W_NOWEP 420 + +//=========================================== + +#define MAX_SCOREBOARD 16 +#define MAX_SCOREBOARDNAME 32 + +#define SOUND_CHANNELS 8 + + +#include "common.h" +#include "bspfile.h" +#include "vid.h" +#include "sys.h" +#include "zone.h" +#include "mathlib.h" + +typedef struct +{ + vec3_t origin; + vec3_t angles; + int modelindex; + int frame; + int colormap; + int skin; + int effects; + // dr_mabuse1981: HalfLife rendermodes fixed START + unsigned short renderamt; + unsigned short rendermode; + unsigned short rendercolor; + // dr_mabuse1981: HalfLife rendermodes fixed END +} entity_state_t; + +#include "wad.h" +#include "draw.h" +#include "cvar.h" +#include "screen.h" +#include "net.h" +#include "protocol.h" +#include "cmd.h" +#include "cl_hud.h" +#include "sound.h" +#include "render.h" +#include "client.h" +#include "progs.h" +#include "server.h" + + +#include "psp/video_hardware_model.h" + +#include "input.h" +#include "world.h" +#include "keys.h" +#include "console.h" +#include "view.h" +#include "menu.h" +#include "crc.h" +#include "cdaudio.h" + +#include "psp/video_hardware.h" + +//============================================================================= + +// the host system specifies the base of the directory tree, the +// command line parms passed to the program, and the amount of memory +// available for the program to use + +typedef struct +{ + char *basedir; + char *cachedir; // for development over ISDN lines + int argc; + char **argv; + void *membase; + int memsize; +} quakeparms_t; + + +//============================================================================= + + + +extern qboolean noclip_anglehack; + + +// +// host +// +extern quakeparms_t host_parms; + +extern cvar_t sys_ticrate; +extern cvar_t sys_nostdout; +extern cvar_t developer; + +extern qboolean host_initialized; // true if into command execution +extern double host_frametime; +extern byte *host_basepal; +extern byte *host_colormap; +extern byte *host_q2pal; +extern byte *host_h2pal; +extern int host_framecount; // incremented every frame, never reset +extern double realtime; // not bounded in any way, changed at + // start of every frame, never reset + +void Host_ClearMemory (void); +void Host_ServerFrame (void); +void Host_InitCommands (void); +void Host_Init (quakeparms_t *parms); +void Host_Shutdown(void); +void Host_Error (char *error, ...); +void Host_EndGame (char *message, ...); +void Host_Frame (float time); +void Host_Quit_f (void); +void Host_ClientCommands (char *fmt, ...); +void Host_ShutdownServer (qboolean crash); + +extern qboolean msg_suppress_1; // suppresses resolution and cache size console output + // an fullscreen DIB focus gain/loss +extern int current_skill; // skill level for currently loaded level (in case + // the user changes the cvar while the level is + // running, this reflects the level actually in use) + +extern qboolean isDedicated; + +extern int minimum_memory; + +extern func_t EndFrame; + +extern vec3_t NULLVEC; + +#define ISUNDERWATER(x) ((x) == CONTENTS_WATER || (x) == CONTENTS_SLIME || (x) == CONTENTS_LAVA) +#define TruePointContents(p) SV_HullPointContents(&cl.worldmodel->hulls[0], 0, p) + +// +// chase +// +extern cvar_t chase_active; + +void Chase_Init (void); +void Chase_Reset (void); +void Chase_Update (void); + +//ZOMBIE AI STUFF +#define MAX_WAYPOINTS 256 //max waypoints +typedef struct +{ + int pathlist [MAX_WAYPOINTS]; + int zombienum; +} zombie_ai; + +typedef struct +{ + vec3_t origin; + int id; + float g_score, f_score; + int open; // Determine if the waypoint is "open" a.k.a avaible + int target_id [8]; // Targets array number + char special[64]; //special tag is required for the closed waypoints + int target [8]; //Each waypoint can have up to 8 targets + float dist [8]; // Distance to the next waypoints + int came_from; // Used for pathfinding store where we got here to this + qboolean used; //if the waypoint is in use +} waypoint_ai; + +extern waypoint_ai waypoints[MAX_WAYPOINTS]; + + +// thread structs +typedef struct +{ + vec3_t origin; + vec3_t forward; + vec3_t right; + vec3_t up; + qboolean ready; +} soundstruct_t; \ No newline at end of file diff --git a/source/render.h b/source/render.h new file mode 100644 index 0000000..6fb1e10 --- /dev/null +++ b/source/render.h @@ -0,0 +1,241 @@ +/* +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 +{ + qboolean 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; + + // LordHavoc: added support for Q2 interpolation + int draw_lastpose, draw_pose; // for interpolation + float draw_lerpstart; // for interpolation + struct model_s *draw_lastmodel; // for interpolation + // LordHavoc: added support for Q2 interpolation + + // Tomaz - QC Alpha Scale Glow Begin + float renderamt; + float rendermode; + float rendercolor[3]; + //Crow_bar + + struct model_s *model; // NULL = no model + char old_model[128]; // NULL = no model + struct efrag_s *efrag; // linked list of efrags + int frame; + float syncbase; // for client-side animations + byte *colormap; + int examp; // for Alias models + int effects; // light, particals, etc + int skinnum; // for Alias models + int iframetime; // for Alias models + int visframe; // last frame this entity was + + // 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; // found in an active leaf + + int dlightframe; // dynamic lighting + int dlightbits; + // light lerping - pox@planetquake.com + float last_shadelight; + + int last_frame; + int current_frame; + float interpolation; + + qboolean noshadow; + + int keynum; + + float bonecontrols[4]; + + int oldframe; + float framelerp; + + // FIXME: could turn these into a union + int trivial_accept; + struct mnode_s *topnode; // for bmodels, first world node + + int modelindex; + vec3_t trail_origin; + qboolean traildrawn; + + float lastShadeLight; + + float frame_start_time; + float frame_interval; + int pose1; + int pose2; + + int z_head; + int z_larm; + int z_rarm; + + // fenix@io.com: model transform interpolation + // that splits bmodel, or NULL if + // not split +} entity_t; + +// md3 related +typedef struct tagentity_s +{ + entity_t ent; + + float tag_translate_start_time; + vec3_t tag_pos1, tag_pos2; + + float tag_rotate_start_time[3]; + vec3_t tag_rot1[3], tag_rot2[3]; +} tagentity_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 allways 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; + + float fog_start; + float fog_end; + float fog_red; + float fog_green; + float fog_blue; + //float fog_alpha; + +} 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_DrawLine(vec3_t start,vec3_t end, vec3_t rgb);//blubs added this + +void R_ViewChanged (vrect_t *pvrect, int lineadj, float aspect); +void R_InitSky (byte *mt); // called at level load + +void R_AddEfrags (entity_t *ent); +void R_RemoveEfrags (entity_t *ent); + +void R_NewMap (void); + +// particles + +typedef enum trail_type_s +{ + ROCKET_TRAIL, GRENADE_TRAIL, BLOOD_TRAIL, TRACER1_TRAIL, SLIGHT_BLOOD_TRAIL,NAIL_TRAIL, + TRACER2_TRAIL, VOOR_TRAIL, ALT_ROCKET_TRAIL, LAVA_TRAIL, BUBBLE_TRAIL, NEHAHRA_SMOKE, + RAYGREEN_TRAIL, RAYRED_TRAIL +} trail_type_t; + +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); + + +void R_DarkFieldParticles (entity_t *ent); +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 qboolean 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); + diff --git a/source/screen.h b/source/screen.h new file mode 100644 index 0000000..ef21ccf --- /dev/null +++ b/source/screen.h @@ -0,0 +1,54 @@ +/* +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_SizeUp (void); +void SCR_SizeDown (void); +void SCR_BringDownConsole (void); +void SCR_CenterPrint (char *str); +void SCR_UsePrint (int type, int cost, int weapon); +qpic_t *GetButtonIcon (char *buttonname); + +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 clearnotify; // set to 0 whenever notify text is drawn +extern qboolean scr_disabled_for_loading; +extern qboolean scr_skipupdate; + +// only the refresh window will be updated unless these variables are flagged +extern int scr_copytop; +extern int scr_copyeverything; + +extern qboolean block_drawing; + +void SCR_UpdateWholeScreen (void); diff --git a/source/server.h b/source/server.h new file mode 100644 index 0000000..264c16a --- /dev/null +++ b/source/server.h @@ -0,0 +1,248 @@ +/* +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 + qboolean changelevel_issued; // cleared when at SV_SpawnServer +} server_static_t; + +//============================================================================= + +typedef enum {ss_loading, ss_active} server_state_t; + +typedef struct +{ + qboolean active; // false if only a net client + + qboolean paused; + qboolean loadgame; // handle connections specially + + double time; + + int lastcheck; // used by PF_checkclient + double lastchecktime; + + char name[64]; // map name + 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 +{ + qboolean active; // false = client is free + qboolean spawned; // false = don't send datagrams + qboolean dropasap; // has been told to go to another level + qboolean privileged; // can execute any host command + qboolean 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 + + 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_points; + int old_kills; +// joe, from ProQuake: allow clients to connect if they don't have the map + qboolean nomap; +} 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 +#define MOVETYPE_BOUNCEMISSILE 11 // bounce w/o gravity +#define MOVETYPE_FOLLOW 12 // track movement of aiment +#define MOVETYPE_HEAD 13 // track movement of head +#define MOVETYPE_LARM 14 // track movement of larm +#define MOVETYPE_RARM 15 // track movement of rarm + + +// edict->solid values +#define SOLID_NOT 0 // no interaction with other objects +#define SOLID_TRIGGER 1 // touch on edge, but not blocking +#define SOLID_BBOX 2 // touch on edge, block +#define SOLID_SLIDEBOX 3 // touch on edge, but not an onground +#define SOLID_BSP 4 // bsp clip, touch on edge, block +#define SOLID_CORPSE 5 // traceable and walkthrought + +// 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 + +// entity effects + +#define EF_BLUELIGHT 1 +#define EF_MUZZLEFLASH 2 +#define EF_BRIGHTLIGHT 4 +#define EF_REDLIGHT 8 +#define EF_ORANGELIGHT 16 +#define EF_GREENLIGHT 32 +#define EF_LIGHT 64 +#define EF_NODRAW 128 +#define EF_BRIGHTFIELD 256 +#define EF_FULLBRIGHT 512 +#define EF_DARKLIGHT 1024 +#define EF_DARKFIELD 2048 + +#define SPAWNFLAG_NOT_EASY 256 +#define SPAWNFLAG_NOT_MEDIUM 512 +#define SPAWNFLAG_NOT_HARD 1024 +#define SPAWNFLAG_NOT_DEATHMATCH 2048 + +#define MD_ALPHA 4096 +#define TX_ALPHA 8192 +#define TX_SCROLL 16384 +#define TX_REF 32768 + +//============================================================================ + +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; + +//=========================================================== + +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 (qboolean 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 (char *fmt, ...); +void SV_BroadcastPrintf (char *fmt, ...); + +void SV_Physics (void); + +qboolean SV_CheckBottom (edict_t *ent); +qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink); + +void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg); + +void SV_MoveToGoal (void); +void SV_MoveToOrigin (void); + +void SV_CheckForNewClients (void); +void SV_RunClients (void); +void SV_SaveSpawnparms (); +void SV_SpawnServer (char *server); diff --git a/source/snd_dma.c b/source/snd_dma.c new file mode 100644 index 0000000..e3d6fd3 --- /dev/null +++ b/source/snd_dma.c @@ -0,0 +1,953 @@ +/* +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" +#include "thread.h" + +void S_Play(void); +void S_PlayVol(void); +void S_SoundList(void); +void S_Update_(); +void S_StopAllSounds(qboolean clear); +void S_StopAllSoundsC(void); + +// ======================================================================= +// Internal sound data & structures +// ======================================================================= + +channel_t channels[MAX_CHANNELS]; +int total_channels; + +int snd_blocked = 0; +static qboolean snd_ambient = 1; +qboolean 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; + +cvar_t bgmvolume = {"bgmvolume", "1", true}; +cvar_t bgmtype = {"bgmtype", "cd", true}; // cd or none + +cvar_t volume = {"volume", "0.7", true}; + +cvar_t nosound = {"nosound", "0"}; +cvar_t precache = {"precache", "1"}; +cvar_t loadas8bit = {"loadas8bit", "0"}; +cvar_t bgmbuffer = {"bgmbuffer", "4096"}; +cvar_t ambient_level = {"ambient_level", "0.3"}; +cvar_t ambient_fade = {"ambient_fade", "100"}; +cvar_t snd_noextraupdate = {"snd_noextraupdate", "0"}; +cvar_t snd_show = {"snd_show", "0"}; +cvar_t _snd_mixahead = {"_snd_mixahead", "0.1", true}; + + +// ==================================================================== +// 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. +// + +qboolean 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) + { + Con_Printf("S_Startup: SNDDMA_Init failed.\n"); + 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(&bgmtype); + 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 ("sounds/ambience/water1.wav"); + ambient_sfx[AMBIENT_SKY] = S_PrecacheSound ("sounds/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 (Q_strlen(name) >= MAX_QPATH) + Sys_Error ("Sound name too long: %s", name); + +// see if already loaded + for (i=0 ; i < num_sfx ; i++) + if (!Q_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) ) + { // allways 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 dist; + vec_t lscale, rscale, scale; + vec3_t source_vec; + sfx_t *snd; + +// anything coming from the view entity will allways 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) + return; + + if (shm->samplebits == 8) + clear = 0x80; + else + clear = 0; + + + { + Q_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. + 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; +} + +void S_ExtraUpdate (void) +{ + + + + 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; + + 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) +{ +} + diff --git a/source/snd_mem.c b/source/snd_mem.c new file mode 100644 index 0000000..45b5a95 --- /dev/null +++ b/source/snd_mem.c @@ -0,0 +1,341 @@ +/* +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 + Q_strcpy(namebuffer, ""); + Q_strcat(namebuffer, s->name); + +// Con_Printf ("loading %s\n",namebuffer); + + data = COM_LoadStackFile(namebuffer, stackbuf, sizeof(stackbuf)); + + 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 (!Q_strncmp((char *)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 && !Q_strncmp((char *)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 ((char *)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; +} + diff --git a/source/snd_mix.c b/source/snd_mix.c new file mode 100644 index 0000000..e5f74fa --- /dev/null +++ b/source/snd_mix.c @@ -0,0 +1,307 @@ +/* +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; + + snd_vol = volume.value*256; + + snd_p = (int *) paintbuffer; + lpaintedtime = paintedtime; + + { + 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); + } +} + +void S_TransferPaintBuffer(int endtime) +{ + int out_idx; + int count; + int out_mask; + int *p; + int step; + int val; + int snd_vol; + DWORD *pbuf; + + 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; + + + { + 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; + } + } +} + + +/* +=============================================================================== + +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 + Q_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 = (unsigned 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; +} + diff --git a/source/sound.h b/source/sound.h new file mode 100644 index 0000000..76e3576 --- /dev/null +++ b/source/sound.h @@ -0,0 +1,178 @@ +/* +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 +{ + qboolean gamealive; + qboolean soundalive; + qboolean 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(qboolean 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 +qboolean 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 qboolean 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 bgmtype; +extern cvar_t volume; + +extern qboolean 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 diff --git a/source/spritegn.h b/source/spritegn.h new file mode 100644 index 0000000..ce83789 --- /dev/null +++ b/source/spritegn.h @@ -0,0 +1,152 @@ +/* +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 +#define SPRITEHL_VERSION 2 +#define SPRITE32_VERSION 32 + +// 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; + +typedef struct +{ + int ident; + int version; + int type; + int texFormat; + float boundingradius; + int width; + int height; + int numframes; + float beamlength; + synctype_t synctype; +} dspritehl_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 +#define SPR_LABEL 5 +#define SPR_LABEL_SCALE 6 + +#define SPR_NORMAL 0 +#define SPR_ADDITIVE 1 +#define SPR_INDEXALPHA 2 +#define SPR_ALPHATEST 3 + +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 { + spriteframetype_t type; +} dspriteframetype_t; + +#define IDSPRITEHEADER (('P'<<24)+('S'<<16)+('D'<<8)+'I') + // little-endian "IDSP" + +#define MAX_SKINNAME 64 +#define IDSPRITE2HEADER (('2'<<24)+('S'<<16)+('D'<<8)+'I') + // little-endian "IDS2" +#define SPRITE2_VERSION 2 + +typedef struct +{ + int width, height; + int origin_x, origin_y; // raster coordinates inside pic + char name[MAX_SKINNAME]; // name of pcx file +} dmd2sprframe_t; + +typedef struct { + int ident; + int version; + int numframes; + dmd2sprframe_t frames[1]; // variable sized +} dmd2sprite_t; + diff --git a/source/sv_main.c b/source/sv_main.c new file mode 100644 index 0000000..c099b3c --- /dev/null +++ b/source/sv_main.c @@ -0,0 +1,1521 @@ +/* +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; + +char localmodels[MAX_MODELS][5]; // inline model names for precache + +//============================================================================ +cvar_t r_hlbsponly = {"r_hlbsponly","0",true}; + +/* +=============== +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); + + 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 +allready 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[2048]; + + MSG_WriteByte (&client->message, svc_print); + sprintf (message, "%c\nVERSION %4.2f SERVER (%i CRC)", 2, VERSION, pr_crc); + MSG_WriteString (&client->message,message); + + MSG_WriteByte (&client->message, svc_serverinfo); + MSG_WriteLong (&client->message, PROTOCOL_VERSION); + 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)); + + MSG_WriteByte (&client->message, svc_signonnum); + MSG_WriteByte (&client->message, 1); + + client->sendsignon = true; + client->spawned = false; // need prespawn, spawn, etc +} + +/* +================ +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; + + //Con_DPrintf ("Client %s connected\n", client->netconnection->address); + Con_Printf ("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 + + client->privileged = false; + + 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; + Q_memset (fatpvs, 0, fatbytes); + SV_AddToFatPVS (org, sv.worldmodel->nodes); + return fatpvs; +} + +//============================================================================= + + +/* +============= +SV_WriteEntitiesToClient + +============= +*/ +void SV_WriteEntitiesToClient (edict_t *clent, sizebuf_t *msg, qboolean nomap) +{ + int e, i; + int bits; + byte *pvs; + vec3_t org; + float miss; + edict_t *ent; + + // Tomaz - QC Alpha Scale Glow Begin + eval_t *val; + float renderamt = 0; + float rendermode = 0; + + float rendercolor[3]; + memset(rendercolor, 0, sizeof(rendercolor)); + // Tomaz - QC Alpha Scale Glow End + + // find the client's PVS + VectorAdd (clent->v.origin, clent->v.view_ofs, org); + pvs = SV_FatPVS (org); + +// 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 ALLWAYS sent + { +// ignore ents without visible models + if (!ent->v.modelindex || !pr_strings[ent->v.model]) + 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 + // joe, from ProQuake: don't send updates if the client doesn't have the map + if (nomap) + continue; + } + if (msg->maxsize - msg->cursize < 16) + { + Con_Printf ("SV_WriteEntitiesToClient: packet overflow->big_value\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) JUkki removed to test interploation + // 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->v.iframetime != 0.1) + bits |= U_FRAMETIME; + + if (ent->baseline.frame != ent->v.frame) + bits |= U_FRAME; + + if (ent->baseline.effects != ent->v.effects) + bits |= U_EFFECTS; + + if (ent->baseline.modelindex != ent->v.modelindex) + bits |= U_MODEL; + // Tomaz - QC Alpha Scale Glow Begin + + { + //Smal Size + if ((val = GETEDICTFIELDVALUE(ent, eval_renderamt)) && val->_float != 255.0) // HalfLife support + renderamt = val->_float / 255.0f; + + if ((val = GETEDICTFIELDVALUE(ent, eval_rendermode)) && val->_float != 0) // HalfLife support + rendermode = val->_float; + + if ((val = GETEDICTFIELDVALUE(ent, eval_rendercolor))) // HalfLife support + { + rendercolor[0] = val->vector[0] / 255.0f; + rendercolor[1] = val->vector[1] / 255.0f; + rendercolor[2] = val->vector[2] / 255.0f; + } + + if (renderamt > 0) + bits |= U_RENDERAMT; + + if (rendermode > 0) + bits |= U_RENDERMODE; + + if (rendercolor[0] > 0) + { + bits |= U_RENDERCOLOR1; + } + + if (rendercolor[1] > 0 ) + { + bits |= U_RENDERCOLOR2; + } + + if (rendercolor[2] > 0 ) + { + bits |= U_RENDERCOLOR3; + } + } + + // Tomaz - QC Alpha Scale Glow End + if (e >= 256)//We have more than 256 entities + bits |= U_LONGENTITY; + + if (bits >= 256)//this is we've exceded some old 8-bit message + bits |= U_MOREBITS; + + // Tomaz - QC Control Begin + if (bits >= 65536)//this is if we've excited the original 16-bit message + bits |= U_EXTEND1; + if (bits >= 16777216) + bits |= U_EXTEND2; + // Tomaz - QC Control End + + // + // write the message + // + MSG_WriteByte (msg,bits | U_SIGNAL); + + if (bits & U_MOREBITS) + MSG_WriteByte (msg, bits>>8); + + // Tomaz - QC Control Begin + if (bits & U_EXTEND1) + MSG_WriteByte (msg, bits>>16); + if (bits & U_EXTEND2) + MSG_WriteByte (msg, bits>>24); + // Tomaz - QC Control End + + 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_WriteShort (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_FRAMETIME) + MSG_WriteFloat (msg, ent->v.iframetime); + if (bits & U_EFFECTS) + MSG_WriteShort (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]); + // Tomaz - QC Alpha Scale Glow Begin + if (bits & U_RENDERAMT) + MSG_WriteFloat(msg, renderamt); + + if (bits & U_RENDERMODE) + MSG_WriteFloat(msg, rendermode); + + if (bits & U_RENDERCOLOR1) + MSG_WriteFloat(msg, rendercolor[0]); + + if (bits & U_RENDERCOLOR2) + MSG_WriteFloat(msg, rendercolor[1]); + + if (bits & U_RENDERCOLOR3) + MSG_WriteFloat(msg, rendercolor[2]); + // Tomaz - QC Alpha Scale Glow End + } +} + +/* +============= +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; + +// +// 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; + + if (ent->v.perks) + bits |= SU_PERKS; + + 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.weaponskin) + bits |= SU_WEAPONSKIN; + + if (ent->v.weapon) + bits |= SU_WEAPON; + + //if (ent->v.perks) + // bits |= SU_PERKS; + + if (ent->v.primary_grenades) + bits |= SU_PRIGRENADES; + + //Think this is out of range of a short + //if (ent->v.secondary_grenades) + // bits |= SU_SECGRENADES; + + if (ent->v.grenades) + bits |= SU_GRENADES; + +// 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); + + if (bits & SU_PERKS) + MSG_WriteLong (msg, ent->v.perks); + + for (i=0 ; i<3 ; i++) + { + if (bits & (SU_PUNCH1<v.punchangle[i]); + if (bits & (SU_VELOCITY1<v.velocity[i]/16); + } + + + if (bits & SU_WEAPONFRAME) + MSG_WriteByte (msg, ent->v.weaponframe); + if (bits & SU_WEAPONSKIN) + MSG_WriteByte (msg, ent->v.weaponskin); + if (bits & SU_WEAPON) + MSG_WriteByte (msg, SV_ModelIndex(pr_strings+ent->v.weaponmodel)); + + if (bits & SU_GRENADES) + MSG_WriteLong (msg, ent->v.grenades); + + MSG_WriteShort (msg, ent->v.primary_grenades); + MSG_WriteShort (msg, ent->v.secondary_grenades); + MSG_WriteShort (msg, ent->v.health); + MSG_WriteShort (msg, ent->v.currentammo); + MSG_WriteByte (msg, ent->v.currentmag); + MSG_WriteByte (msg, ent->v.zoom); + + MSG_WriteByte (msg, ent->v.weapon); + MSG_WriteByte (msg, pr_global_struct->rounds); + MSG_WriteByte (msg, pr_global_struct->rounds_change); + MSG_WriteByte (msg, ent->v.x2_icon); + MSG_WriteByte (msg, ent->v.insta_icon); + MSG_WriteByte (msg, ent->v.progress_bar); + MSG_WriteByte (msg, SV_ModelIndex(pr_strings+ent->v.weapon2model)); + MSG_WriteByte (msg, ent->v.weapon2skin); + MSG_WriteByte (msg, ent->v.weapon2frame); + MSG_WriteByte (msg, ent->v.currentmag2); +} + +/* +======================= +SV_SendClientDatagram +======================= +*/ +qboolean SV_SendClientDatagram (client_t *client) +{ + byte buf[MAX_DATAGRAM]; + sizebuf_t msg; + + msg.data = buf; + msg.maxsize = sizeof(buf); + msg.cursize = 0; + + MSG_WriteByte (&msg, svc_time); + MSG_WriteFloat (&msg, sv.time); + +// add the client specific data to the datagram + SV_WriteClientdataToMessage (client->edict, &msg);//This should be good now + + SV_WriteEntitiesToClient (client->edict, &msg, client->nomap); + +// 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_points != host_client->edict->v.points) + { + for (j=0, client = svs.clients ; jactive) + continue; + MSG_WriteByte (&client->message, svc_updatepoints); + MSG_WriteByte (&client->message, i); + MSG_WriteLong (&client->message, host_client->edict->v.points); + } + + host_client->old_points = host_client->edict->v.points; + } + } + + for (i=0, host_client = svs.clients ; iold_kills != host_client->edict->v.kills) + { + for (j=0, client = svs.clients ; jactive) + continue; + MSG_WriteByte (&client->message, svc_updatekills); + MSG_WriteByte (&client->message, i); + MSG_WriteShort (&client->message, host_client->edict->v.kills); + } + + host_client->old_points = host_client->edict->v.points; + } + } + + 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 points, 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 + } + } + + // 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("models/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 = (byte*)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) + Cmd_ExecuteString ("reconnect\n", src_command); +} + + +/* +================ +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; +void Load_Waypoint (); + +void SV_SpawnServer (char *server) +{ + 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); + +// load progs to get entity field count + PR_LoadProgs (); + +// 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; + +// 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); + + Load_Waypoint (); + Con_DPrintf ("Server spawned.\n"); +} + + + +//ZOMBIE AI THINGS BELOVE THIS!!! +#define W_MAX_TEMPSTRING 2048 +char *w_string_temp; +int W_fopen (void) +{ + int h = 0; + + Sys_FileOpenRead (va("%s/maps/%s.way",com_gamedir, sv.name), &h); + return h; +} + +void W_fclose (int h) +{ + Sys_FileClose(h); +} + +char *W_fgets (int h) +{ + // reads one line (up to a \n) into a string + int i; + int count; + char buffer; + + count = Sys_FileRead(h, &buffer, 1); + if (count && buffer == '\r') // carriage return + { + count = Sys_FileRead(h, &buffer, 1); // skip + } + if (!count) // EndOfFile + { + return ""; + } + + i = 0; + while (count && buffer != '\n') + { + if (i < 128-1) // no place for character in temp string + { + w_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 + } + }; + w_string_temp[i] = 0; + + return (w_string_temp); +} + +char *W_substring (char *p, int offset, int length) +{ + int maxoffset; // 2001-10-25 Enhanced temp string handling by Maddes + + // 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 >= maxoffset) + length = maxoffset-1; +// 2001-10-25 Enhanced temp string handling by Maddes end + if (length < 0) + length = 0; + + p += offset; + strncpy(w_string_temp, p, length); + w_string_temp[length]=0; + + return w_string_temp; +} + +void W_stov (char *v, vec3_t out) +{ + int i; + vec3_t d; + + 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, out); +} + + +waypoint_ai waypoints[MAX_WAYPOINTS]; +void Load_Waypoint () +{ + char temp[64]; + int i, p, s; + vec3_t d; + int h = 0; + + h = W_fopen(); + + w_string_temp = Z_Malloc(128); + if (h == -1) + { + Con_DPrintf("No waypoint file (%s/maps/%s.way) found\n", com_gamedir, sv.name); + return; + } + for (i = 1; i < MAX_WAYPOINTS; i++) + { + waypoints[i].used = 0; + } + + i = 1; + Con_DPrintf("Loading waypoints\n"); + while (1) + { + if (strncmp(W_fgets (h), "Waypoint", 8)) + { + Con_DPrintf("Last waypoint\n"); + break; + } + else + { + if (i == MAX_WAYPOINTS) + Sys_Error ("Maximum waypoints loaded {%i)\n", MAX_WAYPOINTS); + W_fgets (h); + + W_stov (W_substring (W_fgets (h), 9, 20), d); + + strcpy(temp, W_substring (W_fgets (h), 5, 20)); + + i = atoi (temp); + waypoints[i].id = atoi (temp); + VectorCopy (d, waypoints[i].origin); + + strcpy(waypoints[i].special, W_substring (W_fgets (h), 10, 20)); + + if (waypoints[i].special[0]) + waypoints[i].open = 0; + else + waypoints[i].open = 1; + + strcpy(temp, W_substring (W_fgets (h), 9, 20)); + waypoints[i].target[0] = atoi (temp); + + strcpy(temp, W_substring (W_fgets (h), 10, 20)); + waypoints[i].target[1] = atoi (temp); + + + strcpy(temp, W_substring (W_fgets (h), 10, 20)); + waypoints[i].target[2] = atoi (temp); + + + strcpy(temp, W_substring (W_fgets (h), 10, 20)); + waypoints[i].target[3] = atoi (temp); + + + strcpy(temp, W_substring (W_fgets (h), 10, 20)); + waypoints[i].target[4] = atoi (temp); + + + strcpy(temp, W_substring (W_fgets (h), 10, 20)); + waypoints[i].target[5] = atoi (temp); + + + strcpy(temp, W_substring (W_fgets (h), 10, 20)); + waypoints[i].target[6] = atoi (temp); + + + strcpy(temp, W_substring (W_fgets (h), 10, 20)); + waypoints[i].target[7] = atoi (temp); + + W_fgets (h); + W_fgets (h); + waypoints[i].used = 1; + + + Con_DPrintf("Waypoint (%i) id: %i, tag: %s, open: %i, target: %i, target2: %i, target3: %i, target4: %i, target5: %i, target6: %i, target7: %i, target8: %i\n", + i, + waypoints[i].id, + waypoints[i].special, + waypoints[i].open, + waypoints[i].target[0], + waypoints[i].target[1], + waypoints[i].target[2], + waypoints[i].target[3], + waypoints[i].target[4], + waypoints[i].target[5], + waypoints[i].target[6], + waypoints[i].target[7]); + } + } + Con_DPrintf("Total waypoints: %i\n", i); + for (i = 1;i < MAX_WAYPOINTS; i++) //for sake of saving time later we are now going to save each targets array position and distace to each waypoint + { + for (p = 0;waypoints[i].target[p]; p++) + { + for (s = 1; s < MAX_WAYPOINTS; s++) + { + if (s == MAX_WAYPOINTS) + Sys_Error ("Waypoint (%i) without a target!\n", s); + if (waypoints[i].target[p] == waypoints[s].id) + { + waypoints[i].dist[p] = VecLength2(waypoints[s].origin, waypoints[i].origin); + waypoints[i].target_id[p] = s; + break; + } + } + } + Con_DPrintf("Waypoint (%i)target: %i (%i, %f), target2: %i (%i, %f), target3: %i (%i, %f), target4: %i (%i, %f), target5: %i (%i, %f), target6: %i (%i, %f), target7: %i (%i, %f), target8: %i (%i, %f)\n", + waypoints[i].id, + waypoints[i].target[0], + waypoints[i].target_id[0], + waypoints[i].dist[0], + waypoints[i].target[1], + waypoints[i].target_id[1], + waypoints[i].dist[1], + waypoints[i].target[2], + waypoints[i].target_id[2], + waypoints[i].dist[2], + waypoints[i].target[3], + waypoints[i].target_id[3], + waypoints[i].dist[3], + waypoints[i].target[4], + waypoints[i].target_id[4], + waypoints[i].dist[4], + waypoints[i].target[5], + waypoints[i].target_id[5], + waypoints[i].dist[5], + waypoints[i].target[6], + waypoints[i].target_id[6], + waypoints[i].dist[6], + waypoints[i].target[7], + waypoints[i].target_id[7], + waypoints[i].dist[7]); + } + W_fclose(h); + //Z_Free (w_string_temp); +} diff --git a/source/sv_move.c b/source/sv_move.c new file mode 100644 index 0000000..ae4a642 --- /dev/null +++ b/source/sv_move.c @@ -0,0 +1,560 @@ +/* +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; + +qboolean 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 +============= +*/ + +qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)//Blubs edited this to make zombies walk through zombies +{ + 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,MOVE_NOMONSTERS, ent);//blubs edit, was 'false' + + 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,MOVE_NOMONSTERS , ent);//blubsmove, 'false' + + if (trace.allsolid) + { + return false; + } + + if (trace.startsolid) + { + neworg[2] -= STEPSIZE; + trace = SV_Move (neworg, ent->v.mins, ent->v.maxs, end,MOVE_NOMONSTERS , ent);//blubs edit, 'false' + 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; + 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); +qboolean 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_NewChaseDirO + +================ +*/ +void SV_NewChaseDirO (edict_t *actor, vec3_t goal, 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 = goal[0] - actor->v.origin[0]; + deltay = goal[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 + +====================== +*/ +qboolean 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_CloseEnough + +====================== +*/ +qboolean SV_CloseEnoughO (edict_t *ent, vec3_t goal, float dist) +{ + int i; + + for (i=0 ; i<3 ; i++) + { + if (goal[i] > ent->v.absmax[i] + dist) + return false; + if (goal[i] < ent->v.absmin[i] - dist) + return false; + } + return true; +} + +/* +====================== +SV_MoveToGoal + +====================== +*/ +void SV_MoveToGoal (void) +{ + edict_t *ent; + float dist; + float *goal; + + ent = PROG_TO_EDICT(pr_global_struct->self); + + dist = G_FLOAT(OFS_PARM0); + goal = G_VECTOR(OFS_PARM1); + + 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 + if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts && SV_CloseEnoughO (ent, goal, dist) ) + return; + +// bump around... + if ((rand()&3)==1 || !SV_StepDirection (ent, ent->v.ideal_yaw, dist)) + SV_NewChaseDirO (ent, goal, dist); + +} + +/* +====================== +SV_MoveToOrigin + +====================== +*/ +void SV_MoveToOrigin (void) +{ + edict_t *ent, *goal; + float dist; + + 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 + if ( PROG_TO_EDICT(ent->v.enemy) != sv.edicts && SV_CloseEnough (ent, goal, dist) ) + return; + +// bump around... + if ((rand()&3)==1 || !SV_StepDirection (ent, ent->v.ideal_yaw, dist)) + SV_NewChaseDir (ent, goal, dist); + +} + diff --git a/source/sv_phys.c b/source/sv_phys.c new file mode 100644 index 0000000..2dc009e --- /dev/null +++ b/source/sv_phys.c @@ -0,0 +1,1736 @@ +/* +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" + +/* + + +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. + +*/ + +cvar_t sv_friction = {"sv_friction","4",false,true}; +cvar_t sv_stopspeed = {"sv_stopspeed","100"}; +cvar_t sv_gravity = {"sv_gravity","800",false,true}; +cvar_t sv_maxvelocity = {"sv_maxvelocity","2000"}; +cvar_t sv_nostep = {"sv_nostep","0"}; + +#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 + || check->v.movetype == MOVETYPE_FOLLOW + || check->v.movetype == MOVETYPE_NOCLIP + || check->v.movetype == MOVETYPE_LARM + || check->v.movetype == MOVETYPE_RARM + || check->v.movetype == MOVETYPE_HEAD) + 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 (IS_NAN(ent->v.velocity[i])) + { + Con_Printf ("Got a NaN velocity on %s\n", pr_strings + ent->v.classname); + ent->v.velocity[i] = 0; + } + if (IS_NAN(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. +============= +*/ +qboolean 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, MOVE_NOMONSTERS, ent);//Editted by blubs, we do want to ignore monsters in the trace + + 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; + + eval_t *val; + + val = GETEDICTFIELDVALUE(ent, eval_gravity); + if (val && val->_float) + ent_gravity = val->_float; + else + ent_gravity = 1.0; + 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 if (ent->v.solid == SOLID_CORPSE) + // 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 + || check->v.movetype == MOVETYPE_FOLLOW + || check->v.movetype == MOVETYPE_NOCLIP + || check->v.movetype == MOVETYPE_LARM + || check->v.movetype == MOVETYPE_RARM + || check->v.movetype == MOVETYPE_HEAD) + 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; + } + } + + +} + + +/* +============ +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 + || check->v.movetype == MOVETYPE_LARM + || check->v.movetype == MOVETYPE_RARM + || check->v.movetype == MOVETYPE_HEAD) + 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); + } + } + + +} + + +/* +================ +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 (ent->v.avelocity[0] || ent->v.avelocity[1] || ent->v.avelocity[2]) + SV_PushRotate (ent, host_frametime); + + if (movetime) + 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 +============= +*/ +qboolean SV_CheckWater (edict_t *ent) +{ + vec3_t point; + int cont; + + 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) + { + 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; + } + } + + 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 ( fabsf(oldorg[1] - ent->v.origin[1]) > 4 + || fabsf(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 ( fabsf(oldorg[1] - ent->v.origin[1]) < 0.03125 + && fabsf(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_Walk + +Blubswillrule + +A physics/velocity based walking method for monsters (zombies) + + +The following functions are duplicates from player modified for use by monsters +======================================================================================================================================================== +*/ + +/* +============ +SV_PushMonsterEntity + +duplicate of SV_PushEntity: used for monster velocities. + +Does not change the entities velocity at all +============ +*/ +trace_t SV_PushMonsterEntity (edict_t *ent, vec3_t push) +{ + trace_t trace; + vec3_t end; + + VectorAdd (ent->v.origin, push, end); + + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent); + + VectorCopy (trace.endpos, ent->v.origin); + //SV_LinkEdict (ent, true); we don't need to link it here + + if (trace.ent) + SV_Impact (ent, trace.ent); + + return trace; +} + +/* +===================== +SV_MonsterWalkMove + +duplicate SV_WalkMove: Only used by monsters who move by velocity +====================== +*/ +void SV_MonsterWalkMove (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 + + 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_PushMonsterEntity (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 ( fabsf(oldorg[1] - ent->v.origin[1]) < 0.03125 && fabsf(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_PushMonsterEntity (ent, downmove); // FIXME: don't link? + + if (downtrace.plane.normal[2] > 0.7) + { + if (downtrace.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); + } +} + +//Creating a duplicate SV_TestEntityPosition, one that does not check for monsters. +//============= +edict_t *SV_TestEntityPosition_NOMONSTERS(edict_t *ent) +{ + trace_t trace; + + trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NOMONSTERS, ent); + + if (trace.startsolid) + return sv.edicts; + + return NULL; +} +//============= +//Creating a duplicate checkstuck that uses the above modified SV_TestEntityPosition_NOMONSTERS + +//Also adds checking of the entities v.zoom value +//1: used setorigin, so don't adjust for a collision +//0: not in collision +//============= + +void SV_CheckStuck_IgnoreMonsters (edict_t *ent) +{ + int i, j; + int z; + vec3_t org; + + if (!SV_TestEntityPosition_NOMONSTERS(ent)) + { + VectorCopy (ent->v.origin, ent->v.oldorigin); + ent->v.zoom = 0; + return; + } + + if(ent->v.zoom == 1)//if used setorigin + { + VectorCopy (ent->v.origin, ent->v.oldorigin); + return;//we don't care to adjust for stuckness, whoever used setorigin in qc better handle that + } + VectorCopy (ent->v.origin, org); + VectorCopy (ent->v.oldorigin, ent->v.origin); + if (!SV_TestEntityPosition_NOMONSTERS(ent)) + { + Con_DPrintf ("zombie unstuck from bsp hull or non-monster entity.\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_NOMONSTERS(ent)) + { + Con_DPrintf ("zombie unstuck from bsp hull or non-monster entity.\n"); + SV_LinkEdict (ent, true); + return; + } + } + + VectorCopy (org, ent->v.origin); + Con_DPrintf ("zombie is stuck in bsp hull or non-monster entity.\n"); +} +//============================= +//PushAwayZombies +//blubswillrule +//Makes sure zombies are not inside of each other +//glorified version of PF_FindRadius +//============================= +void SV_PushAwayZombies(edict_t *ent) +{ + edict_t *other_ent; + float rad = 23;//approx. length of bbox corner + float *org = ent->v.origin; + vec3_t eorg; + int i, j; + + other_ent = NEXT_EDICT(sv.edicts); + for (i=1 ; ifree) + continue; + //if (ent->v.solid == SOLID_NOT) + // continue; + if( other_ent->v.solid != SOLID_SLIDEBOX) + continue; + if( other_ent->v.movetype != MOVETYPE_WALK) + continue; + for (j=0 ; j<3 ; j++) + eorg[j] = org[j] - (other_ent->v.origin[j] + (other_ent->v.mins[j] + other_ent->v.maxs[j])*0.5); + if (Length(eorg) > rad) + continue; + + //Process nearby zombie + for(j = 0; j < 2; j++)//only x & y + other_ent->v.velocity[j] += (other_ent->v.origin[j] - ent->v.origin[j]) * 0.001;//push away other zombie + //ent->v.velocity[j] += (ent->v.origin[j] - other_ent->v.origin[j]) * 0.01;//push away self + } +} +//============================= + +void SV_Physics_Walk(edict_t *ent) +{ + //if (!SV_CheckWater (ent) && ! ((int)ent->v.flags & FL_WATERJUMP) ) + // SV_AddGravity (ent); + //Slight modification of AddGravity below + + //Zombie bbox is actually smaller, but the movement needs to pretend it is bigger + vec3_t old_mins; + vec3_t old_maxs; + + if(!SV_RunThink(ent)) + return; + + VectorCopy(ent->v.mins,old_mins); + VectorCopy(ent->v.maxs,old_maxs); + + //'-16,-16,-32', '16,16,40' + ent->v.mins[0] = -16; ent->v.mins[1] = -16; ent->v.mins[2] = -32; + ent->v.maxs[0] = 16; ent->v.maxs[1] = 16; ent->v.maxs[2] = 40; + + //if (!((int)ent->v.flags & (FL_ONGROUND))) + //{ + ent->v.velocity[2] -= 1.0 * sv_gravity.value * host_frametime; + SV_CheckVelocity (ent); + //} + + //SV_CheckStuck_IgnoreMonsters(ent); + //PushAwayZombies causes too high of a drop in framerate + //SV_PushAwayZombies(ent); + SV_MonsterWalkMove(ent); + + + //checking for ground beneath us + trace_t downtrace; + vec3_t groundlocation; + VectorCopy(ent->v.origin,groundlocation); + groundlocation[2] = -STEPSIZE + ent->v.velocity[2]*host_frametime; + + downtrace = SV_Move(ent->v.origin,ent->v.mins,ent->v.maxs,groundlocation, MOVE_NOMONSTERS,ent); + + if(!downtrace.allsolid && !downtrace.startsolid) + { + VectorCopy (downtrace.endpos, ent->v.origin); + + ent->v.flags = (int) ent->v.flags & ~FL_ONGROUND; + if (downtrace.plane.normal[2] > 0.7) + { + if(downtrace.ent) + { + if (downtrace.ent->v.solid == SOLID_BSP) + { + ent->v.flags = (int) ent->v.flags | FL_ONGROUND; + ent->v.groundentity = EDICT_TO_PROG(downtrace.ent); + ent->v.velocity[2] = 0; + } + } + } + } + + //restoring the bounding boxes + VectorCopy(old_mins,ent->v.mins); + VectorCopy(old_maxs,ent->v.maxs); + + SV_LinkEdict(ent,true); +} + + +/* +================ +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); + SV_WalkMove (ent); + + 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); +} + + +/* +============= +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); +} +/* +============= +SV_Physics_Limbs + +Entities that are "stuck" to another entity +============= +*/ +void SV_Physics_Limbs (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); +} + + +/* +============= +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; + cont = SV_PointContents (ent->v.origin); + 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; + + // regular thinking + if (!SV_RunThink (ent)) + return; + +// 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_BOUNCEMISSILE + && ent->v.movetype != MOVETYPE_FLYMISSILE) + SV_AddGravity (ent); + +// move angles + VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles); + +// move origin + VectorScale (ent->v.velocity, host_frametime, move); + trace = SV_PushEntity (ent, move); + if (trace.fraction == 1) + return; + if (ent->free) + return; + + if (ent->v.movetype == MOVETYPE_BOUNCE) + backoff = 1.5; + else if (ent->v.movetype == MOVETYPE_BOUNCEMISSILE) + backoff = 2.0; + 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) + { + if (ent->v.velocity[2] < 60 || (ent->v.movetype != MOVETYPE_BOUNCE && ent->v.movetype != MOVETYPE_BOUNCEMISSILE)) + { + 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. +============= +*/ + +void SV_Physics_Step (edict_t *ent) +{ + qboolean 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); +} + +//============================================================================ + +/* +================ +SV_Physics + +================ +*/ +void SV_Physics (void) +{ + int i; + edict_t *ent; + +// 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); + else if (ent->v.movetype == MOVETYPE_FOLLOW) + SV_Physics_Follow (ent); + else if(ent->v.movetype == MOVETYPE_WALK) + SV_Physics_Walk(ent); + else if (ent->v.movetype == MOVETYPE_HEAD || ent->v.movetype == MOVETYPE_RARM || ent->v.movetype == MOVETYPE_LARM) + SV_Physics_Limbs (ent); + 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 + || ent->v.movetype == MOVETYPE_BOUNCEMISSILE + || 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 (EndFrame) + { + // let the progs know that the frame has ended + 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 (EndFrame); + + } + + if (pr_global_struct->force_retouch) + pr_global_struct->force_retouch--; + + sv.time += host_frametime; +} + +trace_t SV_Trace_Toss (edict_t *ent, edict_t *ignore) +{ + double save_frametime; + vec3_t move, end; + edict_t tempent, *tent; + trace_t trace; + + 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); + + if (trace.ent) + if (trace.ent != ignore) + break; + } + host_frametime = save_frametime; + + return trace; +} diff --git a/source/sv_user.c b/source/sv_user.c new file mode 100644 index 0000000..29e882b --- /dev/null +++ b/source/sv_user.c @@ -0,0 +1,645 @@ +/* +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; + +extern cvar_t sv_friction; +cvar_t sv_edgefriction = {"edgefriction", "2"}; +extern cvar_t sv_stopspeed; + +static vec3_t forward, right, up; + +vec3_t wishdir; +float wishspeed; + +// world +float *angles; +float *origin; +float *velocity; + +qboolean onground; + +usercmd_t cmd; + +cvar_t sv_idealpitchscale = {"sv_idealpitchscale","0.8"}; + + +/* +=============== +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 = sqrtf(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 +============== +*/ +cvar_t sv_maxspeed = {"sv_maxspeed", "320", false, true}; +cvar_t sv_accelerate = {"sv_accelerate", "10"}; +#if 0 +void SV_Accelerate (vec3_t wishvel) +{ + int i; + float addspeed, accelspeed; + vec3_t pushvec; + + if (wishspeed == 0) + return; + + VectorSubtract (wishvel, velocity, pushvec); + addspeed = VectorNormalize (pushvec); + + accelspeed = sv_accelerate.value*host_frametime*addspeed; + if (accelspeed > addspeed) + accelspeed = addspeed; + + for (i=0 ; i<3 ; i++) + velocity[i] += accelspeed*pushvec[i]; +} +#endif +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 -= 20*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, newmove; + + AngleVectors (sv_player->v.angles, forward, right, up); + + fmove = cmd.forwardmove; + smove = cmd.sidemove; + + if (fmove != 0 && smove != 0) + { + newmove = sqrt(pow(fmove, 2)/2); + + if (fmove < 0) + fmove = newmove*-1; + else + fmove = newmove; + + if (smove < 0) + smove = newmove*-1; + else + smove = newmove; + } +// 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 ( 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 + for (i=0 ; i<3 ; i++) + angle[i] = MSG_ReadFloat (); + + 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_ReadLong (); + host_client->edict->v.button0 = bits & 1; + host_client->edict->v.button2 = (bits & 2)>>1; + host_client->edict->v.button1 = (bits & 4)>>1; + host_client->edict->v.button3 = (bits & 8)>>1; + host_client->edict->v.button4 = (bits & 16)>>1; + host_client->edict->v.button5 = (bits & 32)>>1; + host_client->edict->v.button6 = (bits & 64)>>1; + host_client->edict->v.button7 = (bits & 128)>>1; + host_client->edict->v.button8 = (bits & 256)>>1; + + i = MSG_ReadByte (); + if (i) + host_client->edict->v.impulse = i; +} + +/* +=================== +SV_ReadClientMessage + +Returns false if the client should be killed +=================== +*/ +qboolean 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 (Q_strncasecmp(s, "status", 6) == 0) + ret = 1; + else if (Q_strncasecmp(s, "god", 3) == 0) + ret = 1; + else if (Q_strncasecmp(s, "notarget", 8) == 0) + ret = 1; + else if (Q_strncasecmp(s, "fly", 3) == 0) + ret = 1; + else if (Q_strncasecmp(s, "name", 4) == 0) + ret = 1; + else if (Q_strncasecmp(s, "noclip", 6) == 0) + ret = 1; + else if (Q_strncasecmp(s, "say", 3) == 0) + ret = 1; + else if (Q_strncasecmp(s, "say_team", 8) == 0) + ret = 1; + else if (Q_strncasecmp(s, "tell", 4) == 0) + ret = 1; + else if (Q_strncasecmp(s, "color", 5) == 0) + ret = 1; + else if (Q_strncasecmp(s, "kill", 4) == 0) + ret = 1; + else if (Q_strncasecmp(s, "pause", 5) == 0) + ret = 1; + else if (Q_strncasecmp(s, "spawn", 5) == 0) + ret = 1; + else if (Q_strncasecmp(s, "begin", 5) == 0) + ret = 1; + else if (Q_strncasecmp(s, "prespawn", 8) == 0) + ret = 1; + else if (Q_strncasecmp(s, "kick", 4) == 0) + ret = 1; + else if (Q_strncasecmp(s, "ping", 4) == 0) + ret = 1; + else if (Q_strncasecmp(s, "give", 4) == 0) + ret = 1; + else if (Q_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 (); + } +} + diff --git a/source/sys.h b/source/sys.h new file mode 100644 index 0000000..07f227a --- /dev/null +++ b/source/sys.h @@ -0,0 +1,75 @@ +/* +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 (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); + +void M_Exit_f (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); + +// returns psp model as char* +char* Sys_GetPSPModel(void); \ No newline at end of file diff --git a/source/thread.c b/source/thread.c new file mode 100644 index 0000000..4496fcb --- /dev/null +++ b/source/thread.c @@ -0,0 +1,59 @@ +#include + +#include "quakedef.h" +#include "thread.h" + +SceUID sound_thread; +qboolean threads_should_quit; +soundstruct_t snd_thread_struct[10]; + +void Thread_UpdateSound(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) +{ + int index; + index = -1; + for (int i = 0; i < 10; i++) { + if (snd_thread_struct[i].ready == false) { + index = i; + break; + } + } + + snd_thread_struct[index].origin[0] = origin[0]; + snd_thread_struct[index].origin[1] = origin[1]; + snd_thread_struct[index].origin[2] = origin[2]; + snd_thread_struct[index].forward[0] = forward[0]; + snd_thread_struct[index].forward[1] = forward[1]; + snd_thread_struct[index].forward[2] = forward[2]; + snd_thread_struct[index].right[0] = right[0]; + snd_thread_struct[index].right[1] = right[1]; + snd_thread_struct[index].right[2] = right[2]; + snd_thread_struct[index].up[0] = up[0]; + snd_thread_struct[index].up[1] = up[1]; + snd_thread_struct[index].up[2] = up[2]; + snd_thread_struct[index].ready = true; +} + +int Thread_Sound(SceSize args, void *argp) +{ + while (!threads_should_quit) { + // loop and see sound processes in the queue + for (int i = 0; i < 10; i++) { + if (snd_thread_struct[i].ready == true) { + snd_thread_struct[i].ready = false; + S_Update(snd_thread_struct[i].origin, snd_thread_struct[i].forward, snd_thread_struct[i].right, snd_thread_struct[i].up); + } + } + + // sleep for 10ms + sceKernelDelayThread(10); + } + + return 0; +} + +void Sys_InitThreads(void) +{ + threads_should_quit = false; + sound_thread = sceKernelCreateThread("sound_thread", Thread_Sound, 0x18, 0x10000, PSP_THREAD_ATTR_USER | PSP_THREAD_ATTR_VFPU, NULL); + sceKernelStartThread(sound_thread, 0, NULL); +} \ No newline at end of file diff --git a/source/thread.h b/source/thread.h new file mode 100644 index 0000000..d9defe8 --- /dev/null +++ b/source/thread.h @@ -0,0 +1,12 @@ +#ifndef _THREAD_H_ +#define _THREAD_H_ + +#include + +extern SceUID sound_thread; +extern qboolean threads_should_quit; + +extern void Sys_InitThreads(void); +extern void Thread_UpdateSound(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up); + +#endif \ No newline at end of file diff --git a/source/vid.h b/source/vid.h new file mode 100644 index 0000000..e70b916 --- /dev/null +++ b/source/vid.h @@ -0,0 +1,85 @@ +/* +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 unsigned d_8to24tableQ2[256]; +extern unsigned d_8to24tableH2[256]; + +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 (qboolean pause); +// called only on Win32, when pause happens, so the mouse can be released + diff --git a/source/view.c b/source/view.c new file mode 100644 index 0000000..53303ea --- /dev/null +++ b/source/view.c @@ -0,0 +1,1726 @@ +/* +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 + +#ifdef PSP_VFPU +#include +#endif + +sfx_t *cl_sfx_step[4]; + +/* + +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_t lcd_x = {"lcd_x","0"}; +cvar_t lcd_yaw = {"lcd_yaw","0"}; + +cvar_t scr_ofsx = {"scr_ofsx","0", false}; +cvar_t scr_ofsy = {"scr_ofsy","0", false}; +cvar_t scr_ofsz = {"scr_ofsz","0", false}; + +cvar_t cl_rollspeed = {"cl_rollspeed", "200"}; +cvar_t cl_rollangle = {"cl_rollangle", "2.0"}; + +cvar_t cl_bob = {"cl_bob","0.02", false}; +cvar_t cl_bobcycle = {"cl_bobcycle","0.06", false}; +cvar_t cl_bobup = {"cl_bobup","0.02", false};//BLUB changed to 0.02 + +cvar_t cl_sidebobbing = {"cl_sidebobbing","1"}; +cvar_t cl_bobside = {"cl_bobside","0.02"}; +cvar_t cl_bobsidecycle = {"cl_bobsidecycle","0.9"}; +cvar_t cl_bobsideup = {"cl_bobsideup","0.5"}; + +cvar_t v_kicktime = {"v_kicktime", "0.5", false}; +cvar_t v_kickroll = {"v_kickroll", "0.6", false}; +cvar_t v_kickpitch = {"v_kickpitch", "0.6", false}; + +cvar_t v_iyaw_cycle = {"v_iyaw_cycle", "2", false}; +cvar_t v_iroll_cycle = {"v_iroll_cycle", "0.5", false}; +cvar_t v_ipitch_cycle = {"v_ipitch_cycle", "1", false}; +cvar_t v_iyaw_level = {"v_iyaw_level", "0.3", false}; +cvar_t v_iroll_level = {"v_iroll_level", "0.1", false}; +cvar_t v_ipitch_level = {"v_ipitch_level", "0.3", false}; + +cvar_t v_idlescale = {"v_idlescale", "0", false}; + +cvar_t crosshair = {"crosshair", "0", true}; + +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 = fabsf(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 + +=============== +*/ + +// Blub's new V_CalcBob code, both side and pitch are in one, dictated by the (which) parameter +float V_CalcBob (float speed,float which)//0 = regular, 1 = side bobbing +{ + float bob = 0; + float sprint = 1; + + if(cl.stats[STAT_ZOOM] == 3) + sprint = 1.8; + + if(cl.stats[STAT_ZOOM] == 2) + return 0; + + //12.048 -> 4.3 = 100 -> 36ish, so replace 100 with 36 + #ifdef PSP_VFPU + if(which == 0) + bob = cl_bobup.value * 36 * speed * (sprint * sprint) * vfpu_sinf((cl.time * 12.5 * sprint));//Pitch Bobbing 10 + else if(which == 1) + bob = cl_bobside.value * 36 * speed * (sprint * sprint * sprint) * vfpu_sinf((cl.time * 6.25 * sprint) - (M_PI * 0.25));//Yaw Bobbing 5 + #else + if(which == 0) + bob = cl_bobup.value * 36 * speed * (sprint * sprint) * sin((cl.time * 12.5 * sprint));//Pitch Bobbing 10 + else if(which == 1) + bob = cl_bobside.value * 36 * speed * (sprint * sprint * sprint) * sin((cl.time * 6.25 * sprint) - (M_PI * 0.25));//Yaw Bobbing 5 + #endif + + return bob; +} + +//===================================================== View Bobbing ===================================================== +static int lastSound; +float PlayStepSound(void) +{ + float num; + int sound = 0; + while(1) + { + num = (rand ()&0x7fff) / ((float)0x7fff); + sound = (int)(num * 4); + sound++; + if(sound != lastSound) + break; + } + + if(sound == 1) + S_StartSound (cl.viewentity, 4, cl_sfx_step[0], vec3_origin, 1, 1); + else if(sound == 2) + S_StartSound (cl.viewentity, 4, cl_sfx_step[1], vec3_origin, 1, 1); + else if(sound == 3) + S_StartSound (cl.viewentity, 4, cl_sfx_step[2], vec3_origin, 1, 1); + else if(sound == 4) + S_StartSound (cl.viewentity, 4, cl_sfx_step[3], vec3_origin, 1, 1); + + lastSound = sound; + return sound; +} +static int canStep; + +float V_CalcVBob(float speed, float which) +{ + float bob = 0; + //float speedMulti = (0.2 + sqrt((cl.velocity[0] * cl.velocity[0]) + (cl.velocity[1] * cl.velocity[1])))/97; We're moving this to parent function to save calculations... + //was going to multiply by speed in the sine function to step faster when you're moving faster... but it skipped around on points of the sine curve too much + //It's be much more efficient to just have a constant step speed, though it would look really weird, it's the only way to have a constant step + + float sprint = 1; + + if(cl.stats[STAT_ZOOM] == 3) + sprint = 1.8; + + if(cl.stats[STAT_ZOOM] == 2) + return 0; + + if(sprint == 1) + { + #ifdef PSP_VFPU + if(which == 0) + bob = speed * 8.6 * (1/sprint) * vfpu_sinf((cl.time * 12.5 * sprint));//10 + else if(which == 1) + bob = speed * 8.6 * (1/sprint) * vfpu_sinf((cl.time * 6.25 * sprint) - (M_PI * 0.25));//5 + else if(which == 2) + bob = speed * 8.6 * (1/sprint) * vfpu_sinf((cl.time * 6.25 * sprint) - (M_PI * 0.25));//5 + #else + if(which == 0) + bob = speed * 8.6 * (1/sprint) * sin((cl.time * 12.5 * sprint));//10 + else if(which == 1) + bob = speed * 8.6 * (1/sprint) * sin((cl.time * 6.25 * sprint) - (M_PI * 0.25));//5 + else if(which == 2) + bob = speed * 8.6 * (1/sprint) * sin((cl.time * 6.25 * sprint) - (M_PI * 0.25));//5 + #endif + } + else + { + #ifdef PSP_VFPU + if(which == 0) + bob = speed * 8.6 * (1/sprint) * vfpu_cosf((cl.time * 6.25 * sprint)); + else if(which == 1) + bob = speed * 8.6 * (1/sprint) * vfpu_cosf((cl.time * 12.5 * sprint)); + else if(which == 2) + bob = speed * 8.6 * (1/sprint) * vfpu_cosf((cl.time * 6.25 * sprint)); + #else + if(which == 0) + bob = speed * 8.6 * (1/sprint) * cos((cl.time * 6.25 * sprint)); + else if(which == 1) + bob = speed * 8.6 * (1/sprint) * cos((cl.time * 12.5 * sprint)); + else if(which == 2) + bob = speed * 8.6 * (1/sprint) * cos((cl.time * 6.25 * sprint)); + #endif + } + + + if(speed > 0.1 && which == 0) + { + #ifdef PSP_VFPU + if(canStep && vfpu_sinf(cl.time * 12.5 * sprint) < -0.8) + #else + if(canStep && sin(cl.time * 12.5 * sprint) < -0.8) + #endif + { + PlayStepSound(); + canStep = 0; + } + #ifdef PSP_VFPU + if(vfpu_sinf(cl.time * 12.5 * sprint) > 0.9) + #else + if(sin(cl.time * 12.5 * sprint) > 0.9) + #endif + { + canStep = 1; + } + } + return bob; +} + +//============================================================================= + + +cvar_t v_centermove = {"v_centermove", "0.15", false}; +cvar_t v_centerspeed = {"v_centerspeed","500"}; + + +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) + { + #ifdef PSP_VFPU + if ( vfpu_fabsf(cl.cmd.forwardmove) < cl_forwardspeed) + #else + if ( fabsf(cl.cmd.forwardmove) < cl_forwardspeed) + #endif + 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; + } +} + + + + + +/* +============================================================================== + + PALETTE FLASHES + +============================================================================== +*/ + + +cshift_t cshift_empty = { {130,80,50}, 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_t v_gamma = {"gamma", "1", true}; + +byte gammatable[256]; // palette is sent through this + +byte ramps[3][256]; +float v_blend[4]; // rgba 0.0 - 1.0 + +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 * powf ( (i+0.5f)/255.5f , g ) + 0.5; + if (inf < 0) + inf = 0; + if (inf > 255) + inf = 255; + gammatable[i] = inf; + } +} + +/* +================= +V_CheckGamma +================= +*/ +qboolean 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_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) +{ + //int s, e, r, g, b, a; + switch (contents) + { + case CONTENTS_EMPTY: + case CONTENTS_SOLID: + 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; + break; + } +/* + s = 0; + e = 400; + r = 130; + g = 80; + b = 50; + + a = r_wateralpha.value * 255.0f; + if (contents!=CONTENTS_EMPTY||contents!=CONTENTS_SOLID) + { + sceGuEnable(GU_FOG); + sceGuFog (s, e, GU_COLOR( r * 0.01f, g * 0.01f, b * 0.01f, a * 0.01f)); + } +*/ +} + +/* +============= +V_HealthCshift +============= +*/ +void V_HealthCshift (void) +{ + int pulse_value, pulseadd; + float tempi1, tempi2, tempi3, tempi4; + if (cl.stats[STAT_HEALTH] < 100 && cl.stats[STAT_HEALTH]) + { + cl.cshifts[CSHIFT_DAMAGE].destcolor[0] = 255; + cl.cshifts[CSHIFT_DAMAGE].destcolor[1] = 0; + cl.cshifts[CSHIFT_DAMAGE].destcolor[2] = 0; + + if (cl.stats[STAT_HEALTH] < 50) + { + pulse_value = abs(((int)(realtime*100)&100) - 50); + pulseadd = 50; + } + else + { + pulse_value = abs(((int)(realtime*50)&20) - 10); + pulseadd = 10; + } + tempi1 = cl.stats[STAT_HEALTH] + pulse_value; + tempi2 = 100 + pulseadd; + tempi3 = tempi1/tempi2; + tempi4 = 200 - (tempi3*255); + if (tempi4 < 0) + tempi4 = 0; + cl.cshifts[CSHIFT_DAMAGE].percent = (int)tempi4; + } + else + cl.cshifts[CSHIFT_DAMAGE].percent = 0; +} + +/* +============= +V_CalcBlend +============= +*/ +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; +} + +/* +============= +V_UpdatePalette +============= +*/ +void V_UpdatePalette (void) +{ + int i, j; + qboolean new; + byte *basepal, *newpal; + byte pal[768]; + float r,g,b,a; + int ir, ig, ib; + qboolean force; + + //V_HealthCshift (); + + new = false; + + for (i=0 ; i 255) + ir = 255; + if (ig > 255) + ig = 255; + if (ib > 255) + ib = 255; + + ramps[0][i] = gammatable[ir]; + ramps[1][i] = gammatable[ig]; + ramps[2][i] = gammatable[ib]; + } + + basepal = host_basepal; + newpal = pal; + + for (i=0 ; i<256 ; i++) + { + ir = basepal[0]; + ig = basepal[1]; + ib = basepal[2]; + basepal += 3; + + newpal[0] = ramps[0][ir]; + newpal[1] = ramps[1][ig]; + newpal[2] = ramps[2][ib]; + newpal += 3; + } + + VID_ShiftPalette (pal); +} + + +/* +============================================================================== + + VIEW RENDERING + +============================================================================== +*/ + +float angledelta (float a) +{ + a = anglemod(a); + if (a > 180) + a -= 360; + return a; +} + +/* +================== +CalcGunAngle +================== +*/ + +static float OldYawTheta; +static float OldPitchTheta; + + +static vec2_t cADSOfs; + +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; + + + //=========Strafe-Roll========= + //Creating backup + CWeaponRot[PITCH] = cl.viewent.angles[PITCH] * -1; + CWeaponRot[YAW] = cl.viewent.angles[YAW] * -1; + CWeaponRot[ROLL] = cl.viewent.angles[ROLL] * -1; + + float side; + side = V_CalcRoll (cl_entities[cl.viewentity].angles, cl.velocity); + cl.viewent.angles[ROLL] = angledelta(cl.viewent.angles[ROLL] - ((cl.viewent.angles[ROLL] - (side * 5)) * 0.5)); + + //^^^ Model swaying + if(cl.stats[STAT_ZOOM] == 1) + { + cl.viewent.angles[YAW] = (r_refdef.viewangles[YAW] + yaw) - (angledelta((r_refdef.viewangles[YAW] + yaw) - OldYawTheta ) * 0.3);//0.6 + } + else + { + cl.viewent.angles[YAW] = (r_refdef.viewangles[YAW] + yaw) - (angledelta((r_refdef.viewangles[YAW] + yaw) - OldYawTheta ) * 0.6);//0.6 + } + + cl.viewent.angles[PITCH] = -1 * ((r_refdef.viewangles[PITCH] + pitch) - (angledelta((r_refdef.viewangles[PITCH] + pitch) + OldPitchTheta ) * 0.2)); + + //cl.viewent.angles[PITCH] = - (r_refdef.viewangles[PITCH] + pitch); + + //BLUBS STOPS HERE + OldYawTheta = cl.viewent.angles[YAW]; + OldPitchTheta = cl.viewent.angles[PITCH]; + //readd this + cl.viewent2.angles[ROLL] = cl.viewent.angles[ROLL] -= v_idlescale.value * sinf(cl.time*v_iroll_cycle.value * 2) * v_iroll_level.value; + cl.viewent2.angles[PITCH] = cl.viewent.angles[PITCH] -= v_idlescale.value * sinf(cl.time*v_ipitch_cycle.value * 2) * v_ipitch_level.value; + cl.viewent2.angles[YAW] = cl.viewent.angles[YAW] -= v_idlescale.value * sinf(cl.time*v_iyaw_cycle.value * 2) * v_iyaw_level.value; + + //Evaluating total offset + CWeaponRot[PITCH] -= cl.viewent.angles[PITCH]; + CWeaponRot[YAW] += cl.viewent.angles[YAW]; + CWeaponRot[ROLL] += cl.viewent.angles[ROLL]; + +} + +/* +============== +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] + 32) + r_refdef.vieworg[2] = ent->origin[2] + 32; +} + +/* +============== +V_AddIdle + +Idle swaying +============== +*/ +void V_AddIdle (void) +{ + r_refdef.viewangles[ROLL] += v_idlescale.value * sinf(cl.time*v_iroll_cycle.value) * v_iroll_level.value; + r_refdef.viewangles[PITCH] += v_idlescale.value * sinf(cl.time*v_ipitch_cycle.value) * v_ipitch_level.value; + r_refdef.viewangles[YAW] += v_idlescale.value * sinf(cl.time*v_iyaw_cycle.value) * 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; + } + + +} + + +/* +================== +V_CalcIntermissionRefdef + +================== +*/ +void V_CalcIntermissionRefdef (void) +{ + entity_t *ent, *view, *view2; + 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; + view2 = &cl.viewent2; + + VectorCopy (ent->origin, r_refdef.vieworg); + VectorCopy (ent->angles, r_refdef.viewangles); + view->model = NULL; + view2->model = NULL; + +// allways idle in intermission + old = v_idlescale.value; + v_idlescale.value = 1; + V_AddIdle (); + v_idlescale.value = old; +} + +float ApproxEqual(float A, float B) +{ + if((A-B) < -0.001) + return 0; + if(A-B > 0.001) + return 0; + + //Con_Printf ("%f",sinf(M_PI)); + + return 1; +}; + +/* +================== +Weapon ADS Declarations +================== +*/ +void GetWeaponADSOfs(vec2_t out) +{ + switch(cl.stats[STAT_ACTIVEWEAPON]) + { + case W_COLT: + { + //out[0] = -15.281; + //out[1] = 5.0677; + out[0] = -5.4792; + out[1] = 1.6500; + return; + } + case W_KAR: + { + //out[0] = -15.4472; + //out[1] = 8.75790; + out[0] = -5.4959; + out[1] = 3.1869; + return; + } + case W_KAR_SCOPE: + { + //out[0] = -15.4472; + //out[1] = 1.8985; + out[0] = -5.2860; + out[1] = 0.7061; + return; + } + case W_THOMPSON: + { + //out[0] = -14.0936; + //out[1] = 6.7265; + out[0] = -6.0693; + out[1] = 3.0076; + return; + } + case W_TRENCH: + { + //out[0] = -20.5952; + //out[1] = 10.1903; + out[0] = -5.5271; + out[1] = 2.8803; + return; + } + case W_357: + { + //out[0] = -15.3425; + //out[1] = 3.7888; + out[0] = -8.3065; + out[1] = 0.8792; + return; + } + case W_MG: + { + out[0] = -6.6437; + out[1] = 3.5092; + return; + } + case W_DB: + { + out[0] = -5.8017; + out[1] = 2.9121; + return; + } + case W_SAWNOFF: + { + out[0] = -5.8017; + out[1] = 2.9121; + return; + } + case W_M1A1: + { + out[0] = -5.3878; + out[1] = 3.6719; + return; + } + case W_BAR: + { + out[0] = -3.8833; + //out[1] = 2.3745; + out[1] = 2.6745; + return; + } + default: + { + //Large values > 20ish cause weapon to flicker, scale model down if we encounter! + //Scale better be 4.3, or else viewbobbing is going to be inaccurate. + out[0] = -5.4792; + out[1] = 1.6500; + return; + } + } +}; +/* +================== +V_CalcRefdef + +================== +*/ + + +static float lastUpVelocity; +static float VerticalOffset; +static float cVerticalOffset; +vec3_t CWeaponOffset;//blubs declared this +vec3_t CWeaponRot; + +extern double crosshair_spread_time; +void DropRecoilKick (void) +{ + float len; + + if (crosshair_spread_time > sv.time) + return; + len = VectorNormalize (cl.gun_kick); + + //Con_Printf ("len = %f\n",len); + len = len - 5*host_frametime; + if (len < 0) + len = 0; + VectorScale (cl.gun_kick, len, cl.gun_kick); + //Con_Printf ("len final = %f\n",len); +} +vec3_t lastPunchAngle; +void V_CalcRefdef (void) +{ + entity_t *ent, *view, *view2; + int i; + vec3_t forward, right, up; + vec3_t angles; + + static float oldz = 0; + + V_DriftPitch (); + DropRecoilKick(); + +// 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; + view2 = &cl.viewent2; + +// 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 + + //Blubs Bobbing calculations were here, moved them down to be right above bob code block + +// refresh position + VectorCopy (ent->origin, r_refdef.vieworg); + r_refdef.vieworg[2] += cl.viewheight;//blubs removed "+ bob", it's added again below actually... + +// 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 backwards + 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); + //cl.oldviewangles = cl.viewangles; + + CalcGunAngle (); + + view->angles[PITCH] = view->angles[PITCH] - cl.gun_kick[PITCH]; + view->angles[YAW] = view->angles[YAW] + cl.gun_kick[YAW]; + VectorCopy (ent->origin, view->origin); + view->origin[2] += cl.viewheight; + + //Storing base location, later to calculate total offset + CWeaponOffset[0]= view->origin[0] * -1; + CWeaponOffset[1]= view->origin[1] * -1; + CWeaponOffset[2]= view->origin[2] * -1; + + + //Angle Vectors used by landing and iron sights, so do it here + vec3_t temp_up,temp_right,temp_forward; + AngleVectors (r_refdef.viewangles,temp_forward, temp_right, temp_up); + //============================================================ Fall Landing Buffering ============================================================ + if(lastUpVelocity < cl.velocity[2] - 5)//We've had a dramatic change in velocity + { + VerticalOffset = (lastUpVelocity - cl.velocity[2])/25; + if(VerticalOffset < -15) + { + VerticalOffset = -15; + } + } + + cVerticalOffset += (VerticalOffset - cVerticalOffset) * 0.3; + + temp_up[0] *= cVerticalOffset; + temp_up[1] *= cVerticalOffset; + temp_up[2] *= cVerticalOffset; + + view->origin[0] +=(temp_up[0]); + view->origin[1] +=(temp_up[1]); + view->origin[2] +=(temp_up[2]); + + if(cVerticalOffset > VerticalOffset - 2 && cVerticalOffset < VerticalOffset + 2)//Close enough to goal + { + VerticalOffset = 0; + } + lastUpVelocity = cl.velocity[2]; + + //============================================================ Engine-Side Iron Sights ============================================================ + AngleVectors (r_refdef.viewangles, temp_forward, temp_right, temp_up); + + vec2_t ADSOffset; + if(cl.stats[STAT_ZOOM] == 1 || cl.stats[STAT_ZOOM] == 2) + { + ADSOffset[0] = sv_player->v.ADS_Offset[0]; + ADSOffset[1] = sv_player->v.ADS_Offset[1]; + ADSOffset[2] = sv_player->v.ADS_Offset[2]; + + ADSOffset[0] = ADSOffset[0]/1000; + ADSOffset[1] = ADSOffset[1]/1000; + ADSOffset[2] = ADSOffset[2]/1000; + } + else + { + ADSOffset[0] = 0; + ADSOffset[1] = 0; + } + //Side offset + cADSOfs [0] += (ADSOffset[0] - cADSOfs[0]) * 0.25; + cADSOfs [1] += (ADSOffset[1] - cADSOfs[1]) * 0.25; + + temp_right[0] *= cADSOfs[0]; + temp_right[1] *= cADSOfs[0]; + temp_right[2] *= cADSOfs[0]; + + temp_up[0] *= cADSOfs[1]; + temp_up[1] *= cADSOfs[1]; + temp_up[2] *= cADSOfs[1]; + + view->origin[0] +=(temp_right[0] + temp_up[0]); + view->origin[1] +=(temp_right[1] + temp_up[1]); + view->origin[2] +=(temp_right[2] + temp_up[2]); + + float speed = (0.2 + sqrt((cl.velocity[0] * cl.velocity[0]) + (cl.velocity[1] * cl.velocity[1]))); + speed = speed/190; + + float bob, bobside = 0; + + if (cl_sidebobbing.value) + bobside = V_CalcBob(speed,1); + bob = V_CalcBob (speed,0); + + //============================ Weapon Bobbing Code Block================================= + for (i=0 ; i<3 ; i++) + { + if (cl_sidebobbing.value) + { + view->origin[i] += right[i]*bobside*0.4; + view->origin[i] += up[i]*bob*0.5; +// view2->origin[i] += right[i]*bobside*0.2; +// view2->origin[i] += up[i]*bob*0.2; +// mz->origin[i] += right[i]*bobside*0.2; +// mz->origin[i] += up[i]*bob*0.2; + } + else + { + view->origin[i] += forward[i]*bob*0.4; +// view2->origin[i] += forward[i]*bob*0.4; +// mz->origin[i] += forward[i]*bob*0.4; + } + } + + //view->origin[2] += bob * 2;//Removed because it added bobbing 2 times, we need to have more control than that, removed all multipliers but + +// view2->origin[2] += bob; +// mz->origin[2] += bob; + + //=============================== Added View Bobbing Code Block (Blubs wuz here)======================= + vec3_t vbob; + vbob[0] = V_CalcVBob(speed,0) * cl_bob.value * 50;//cl_bob * 50 undo each other, but we want to give some control to people to limit view bobbing + vbob[1] = V_CalcVBob(speed,1) * cl_bob.value * 50; + vbob[2] = V_CalcVBob(speed,2) * cl_bob.value * 50; + + r_refdef.viewangles[YAW] = angledelta(r_refdef.viewangles[YAW] + (vbob[0] * 0.1)); + r_refdef.viewangles[PITCH] = angledelta(r_refdef.viewangles[PITCH] + (vbob[1] * 0.1)); + r_refdef.viewangles[ROLL] = anglemod(r_refdef.viewangles[ROLL] + (vbob[2] * 0.05)); + + + + //Here we finally set CWeaponOffset by the total weapon model offset, used for mzfl which uses CWeaponOffset variable. + CWeaponOffset[0] += view->origin[0]; + CWeaponOffset[1] += view->origin[1]; + CWeaponOffset[2] += view->origin[2]; +//I don't know what the comments below this are, but blubs didn't add them... + +// fudge position around to keep amount of weapon visible +// roughly equal with different FOV + + view->model = cl.model_precache[cl.stats[STAT_WEAPON]]; + view->frame = cl.stats[STAT_WEAPONFRAME]; + view->skinnum = cl.stats[STAT_WEAPONSKIN]; + view->colormap = vid.colormap; + + view2->model = cl.model_precache[cl.stats[STAT_WEAPON2]]; + view2->frame = cl.stats[STAT_WEAPON2FRAME]; + view2->skinnum = cl.stats[STAT_WEAPON2SKIN]; + view2->colormap = vid.colormap; +// set up the refresh position + + //blubs's punchangle interpolation + lastPunchAngle[0] += (cl.punchangle[0] - lastPunchAngle[0]) * 0.5; + lastPunchAngle[1] += (cl.punchangle[1] - lastPunchAngle[1]) * 0.5; + lastPunchAngle[2] += (cl.punchangle[2] - lastPunchAngle[2]) * 0.5; + //VectorCopy(cl.punchangle,lastPunchAngle); + + VectorAdd (r_refdef.viewangles, lastPunchAngle, r_refdef.viewangles); + //VectorCopy(cl.punchangle,lastPunchAngle); + + VectorAdd (r_refdef.viewangles, cl.gun_kick, 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 (); + + view2->origin[0] = view->origin[0]; + view2->origin[1] = view->origin[1]; + view2->origin[2] = view->origin[2]; + + view2->angles[0] = view->angles[0]; + view2->angles[1] = view->angles[1]; + view2->angles[2] = view->angles[2]; + +} +//================ +// Blub's magical debug tracemove, begin debug functions here +//================ +/* +int EN_Find(int num,char *string); + +void drawbbox (vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end,int color) +{ + vec3_t colorVec; + if(color == 1) + { + colorVec[0] = 0; colorVec[1] = 1; colorVec[2] = 1; + } + if(color == 0) + { + colorVec[0] = 1; colorVec[1] = 0; colorVec[2] = 0; + } + if(color == -1) + { + colorVec[0] = 1; colorVec[1] = 0.5; colorVec[2] = 0.5; + } + + //Goal Tracebox + if(color >= 6 && color < 7) + { + colorVec[0] = 1; colorVec[1] = 1; colorVec[2] = 0; + } + if(color == 7) + { + colorVec[0] = 0; colorVec[1] = 1; colorVec[2] = 0; + } + + //Down Tracebox + if(color == 2) + { + colorVec[0] = 0.25; colorVec[1] = 0.2; colorVec[2] = 0.2; + } + if(color > 2 && color <= 3) + { + colorVec[0] = 1; colorVec[1] = 0.5; colorVec[2] = 0.25; + } + + //Up Tracebox + if(color >= 4 && color < 5) + { + colorVec[0] = 1; colorVec[1] = 0; colorVec[2] = 1; + } + if(color == 5) + { + colorVec[0] = 0; colorVec[1] = 0; colorVec[2] = 1; + } + + + vec3_t boxmins,boxmaxs; + int i; + + if(start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) + { + for(i = 0; i < 3; i++) + { + boxmins[i] = start[i] + mins[i]; + boxmaxs[i] = start[i] + maxs[i]; + } + } + else + { + 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; + } + } + } + vec3_t start1,end1; + + //Drawing resultant box + //Bottom Square + start1[0] = boxmins[0]; start1[1] = boxmins[1]; start1[2] = boxmins[2]; + end1[0] = boxmins[0]; end1[1] = boxmaxs[1]; end1[2] = boxmins[2]; + R_DrawLine(start1,end1,colorVec); + + start1[0] = boxmins[0]; start1[1] = boxmaxs[1]; start1[2] = boxmins[2]; + end1[0] = boxmaxs[0]; end1[1] = boxmaxs[1]; end1[2] = boxmins[2]; + R_DrawLine(start1,end1,colorVec); + + start1[0] = boxmaxs[0]; start1[1] = boxmaxs[1]; start1[2] = boxmins[2]; + end1[0] = boxmaxs[0]; end1[1] = boxmins[1]; end1[2] = boxmins[2]; + R_DrawLine(start1,end1,colorVec); + + start1[0] = boxmaxs[0]; start1[1] = boxmins[1]; start1[2] = boxmins[2]; + end1[0] = boxmins[0]; end1[1] = boxmins[1]; end1[2] = boxmins[2]; + R_DrawLine(start1,end1,colorVec); + + + //Top square + start1[0] = boxmins[0]; start1[1] = boxmins[1]; start1[2] = boxmaxs[2]; + end1[0] = boxmins[0]; end1[1] = boxmaxs[1]; end1[2] = boxmaxs[2]; + R_DrawLine(start1,end1,colorVec); + + start1[0] = boxmins[0]; start1[1] = boxmaxs[1]; start1[2] = boxmaxs[2]; + end1[0] = boxmaxs[0]; end1[1] = boxmaxs[1]; end1[2] = boxmaxs[2]; + R_DrawLine(start1,end1,colorVec); + + start1[0] = boxmaxs[0]; start1[1] = boxmaxs[1]; start1[2] = boxmaxs[2]; + end1[0] = boxmaxs[0]; end1[1] = boxmins[1]; end1[2] = boxmaxs[2]; + R_DrawLine(start1,end1,colorVec); + + start1[0] = boxmaxs[0]; start1[1] = boxmins[1]; start1[2] = boxmaxs[2]; + end1[0] = boxmins[0]; end1[1] = boxmins[1]; end1[2] = boxmaxs[2]; + R_DrawLine(start1,end1,colorVec); + + //Side Lines + start1[0] = boxmins[0]; start1[1] = boxmins[1]; start1[2] = boxmins[2]; + end1[0] = boxmins[0]; end1[1] = boxmins[1]; end1[2] = boxmaxs[2]; + R_DrawLine(start1,end1,colorVec); + + start1[0] = boxmins[0]; start1[1] = boxmaxs[1]; start1[2] = boxmins[2]; + end1[0] = boxmins[0]; end1[1] = boxmaxs[1]; end1[2] = boxmaxs[2]; + R_DrawLine(start1,end1,colorVec); + + start1[0] = boxmaxs[0]; start1[1] = boxmaxs[1]; start1[2] = boxmins[2]; + end1[0] = boxmaxs[0]; end1[1] = boxmaxs[1]; end1[2] = boxmaxs[2]; + R_DrawLine(start1,end1,colorVec); + + start1[0] = boxmaxs[0]; start1[1] = boxmins[1]; start1[2] = boxmins[2]; + end1[0] = boxmaxs[0]; end1[1] = boxmins[1]; end1[2] = boxmaxs[2]; + R_DrawLine(start1,end1,colorVec); +} +///////////////////////////////////////////// +int DebugTraceMove(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *ent) +{ + if(start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) + { + return 1; + } + vec3_t forward, up; + float HorDist; + vec3_t HorGoal; + vec3_t tempHorGoal; + + up[0] = 0; + up[1] = 0; + up[2] = 1; + + HorGoal[0] = end[0]; + HorGoal[1] = end[1]; + HorGoal[2] = start[2]; + + VectorSubtract(HorGoal,start,forward); + HorDist = VectorLength(forward); + VectorNormalize(forward); + + vec3_t CurrentPos; + + VectorCopy(start,CurrentPos); + VectorCopy(HorGoal,tempHorGoal); + + float CurrentDist = 0;//2d distance from initial 3d positionvector + + trace_t trace1, trace2; + float tempDist; + vec3_t tempVec; + vec3_t tempVec2; + float i; + int STEPSIZEB = 18;//other declaration isn't declared yet + float SLOPELEN = 10.4;//18/tan(60) = 10.4, the the length of the triangle formed by the max walkable slope of 60 degrees. + int skip = 0; + int LoopBreak = 0; + + while(CurrentDist < HorDist) + { + if(LoopBreak > 50) + { + Con_Printf("AI Warning: There is a ledge that is greater than 650 meters.\n"); + return -1; + } + + trace1 = SV_Move(CurrentPos, mins, maxs, tempHorGoal, MOVE_NOMONSTERS, ent); + //Goal + //drawbbox(CurrentPos,mins,maxs,tempHorGoal,trace1.fraction + 6); + + VectorSubtract(tempHorGoal,CurrentPos,tempVec); + tempDist = trace1.fraction * VectorLength(tempVec); + //Check if we fell along the path + for(i = (maxs[0] * 1); i < tempDist; i += (maxs[0] * 1)) + { + VectorScale(forward,i,tempVec); + VectorAdd(tempVec,CurrentPos,tempVec); + VectorScale(up,-500,tempVec2);//500 inches is about 13 meters + VectorAdd(tempVec,tempVec2,tempVec2); + trace2 = SV_Move(tempVec, mins, maxs, tempVec2, MOVE_NOMONSTERS, ent); + //Down + if(trace2.fraction > 0) + { + vec3_t tVec; + VectorScale(up,-500,tVec); + VectorScale(tVec,trace2.fraction,tVec); + VectorAdd(tempVec,tVec,tVec); + drawbbox(tempVec,mins,maxs,tVec,3); + //drawbbox(tempVec,mins,maxs,tempVec2,3); + + } + + if(trace2.fraction > 0) + { + VectorScale(up,trace2.fraction * -500,tempVec2); + VectorAdd(tempVec,tempVec2,CurrentPos); + VectorAdd(tempHorGoal,tempVec2,tempHorGoal); + skip = 1; + CurrentDist += i; + if(trace2.fraction == 1) + { + //We fell the full 13 meters!, we need to be careful here, + //because if we're checking over the void, then we could be stuck in an infinite loop and crash the game + //So we're going to keep track of how many times we fall 13 meters + LoopBreak++; + } + else + { + LoopBreak = 0; + } + break; + } + } + //If we fell at any location along path, then we don't try to step up + if(skip == 1) + { + trace2.fraction = 0; + skip = 0; + continue; + } + //We need to advance it as much as possible along path before step up + if(trace1.fraction > 0 && trace1.fraction < 1) + { + VectorCopy(trace1.endpos,CurrentPos); + trace1.fraction = 0; + } + //Check step up + if(trace1.fraction < 1) + { + VectorScale(up,STEPSIZEB,tempVec2); + VectorAdd(CurrentPos,tempVec2,tempVec); + + VectorAdd(tempHorGoal,tempVec2,tempVec2); + + trace2 = SV_Move(tempVec, mins, maxs, tempVec2, MOVE_NOMONSTERS, ent); + + //up + //=============================== Debug ============================================================== + vec3_t debugVec;//temp, we need to do the conditional for success step up but we copy vector after this, so we need to do a temp version of the copy to test in color assignment + VectorSubtract(tempVec2,tempVec,debugVec); + if(trace2.fraction <= (trace1.fraction + (SLOPELEN/VectorLength(debugVec))) && trace2.fraction != 1) + { + drawbbox(tempVec,mins,maxs,tempVec,4); + } + drawbbox(CurrentPos,mins,maxs,CurrentPos,5); + //=============================== / Debug ============================================================== + + + //10.4 is minimum length for a slope of 60 degrees, we need to at least advance this much to know the surface is walkable + VectorSubtract(tempVec2,tempVec,tempVec2); + if(trace2.fraction > (trace1.fraction + (SLOPELEN/VectorLength(tempVec2))) || trace2.fraction == 1) + { + //we check for slope above + //This is for advancing within the step up + //VectorCopy(trace2.endpos,CurrentPos); + //tempHorGoal[2] = CurrentPos[2]; + //VectorSubtract(trace2.endpos,tempVec,tempVec2); + //CurrentDist += VectorLength(tempVec2); + + //this is for continuing the search with the step up + VectorCopy(tempVec,CurrentPos); + tempHorGoal[2] = CurrentPos[2]; + + continue; + } + else + { + //Con_Printf("t2:%f,t1:%f, delta: %f",trace2.fraction,trace1.fraction,trace1.fraction + (SLOPELEN/VectorLength(tempVec2))); + //Con_Printf("hitwall "); + return 0;//stepping up didn't advance so we've hit a wall, we failed + } + } + if(trace1.fraction == 1)//we've made it horizontally to our goal... so check if we've made it vertically... + { + if((end[2] - tempHorGoal[2] < STEPSIZEB) && (end[2] - tempHorGoal[2]) > -1 * STEPSIZEB) + return 1; + else + { + Con_Printf("vert "); + return 0; + } + } + } + Con_Printf("end "); + return 0; +} + +void tryLine() +{ + edict_t *p; + int pnum; + pnum = EN_Find(0,"player"); + p = EDICT_NUM(pnum); + + vec3_t cv; + + + int result; + result = DebugTraceMove(p->v.origin,p->v.mins,p->v.maxs,PROG_TO_EDICT(p->v.enemy)->v.origin ,0,p); + + + cv[0] = 1; cv[1] = 0; cv[2] = 0; + + if(result == 1) + { + cv[0] = 0; cv[1] = 1; cv[2] = 0; + } + R_DrawLine(p->v.origin,PROG_TO_EDICT(p->v.enemy)->v.origin,cv); + + //drawbbox(p->v.origin,p->v.mins,p->v.maxs,PROG_TO_EDICT(p->v.enemy)->v.origin,result); + + drawbbox(PROG_TO_EDICT(p->v.enemy)->v.origin,p->v.mins,p->v.maxs,PROG_TO_EDICT(p->v.enemy)->v.origin,1); + drawbbox(p->v.origin,p->v.mins,p->v.maxs,p->v.origin,1); +}*/ +//End blubs debug functions + + +/* +================== +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 && (cl.maxclients > 1 || key_dest == key_game) ) + { + V_CalcRefdef (); + } + } + R_PushDlights (); + + if (lcd_x.value)//blubs: psp doesn't appear to use these + { + // + // render two interleaved views + // + int i; + + vid.rowbytes <<= 1; + vid.aspect *= 0.5; + + r_refdef.viewangles[YAW] -= lcd_yaw.value; + for (i=0 ; i<3 ; i++) + r_refdef.vieworg[i] -= right[i]*lcd_x.value; + R_RenderView (); + + vid.buffer += vid.rowbytes>>1; + + R_PushDlights (); + r_refdef.viewangles[YAW] += lcd_yaw.value*2; + for (i=0 ; i<3 ; i++) + r_refdef.vieworg[i] += 2*right[i]*lcd_x.value; + R_RenderView (); + vid.buffer -= vid.rowbytes>>1; + + r_refdef.vrect.height <<= 1; + + vid.rowbytes >>= 1; + vid.aspect *= 2; + } + else + { + R_RenderView (); + } + + //Blub's debug tracemove: to use: uncomment this, go above and uncomment the functions used above this one, and go in qc and make the player spawn an entity of .enemy + //tryLine(); +} + +//============================================================================ + +/* +============= +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 (&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 (&cl_sidebobbing); + Cvar_RegisterVariable (&cl_bobside); + Cvar_RegisterVariable (&cl_bobsidecycle); + Cvar_RegisterVariable (&cl_bobsideup); + + Cvar_RegisterVariable (&v_kicktime); + Cvar_RegisterVariable (&v_kickroll); + Cvar_RegisterVariable (&v_kickpitch); + + BuildGammaTable (1.0); // no gamma yet + Cvar_RegisterVariable (&v_gamma); +} + + diff --git a/source/view.h b/source/view.h new file mode 100644 index 0000000..e7c30b1 --- /dev/null +++ b/source/view.h @@ -0,0 +1,38 @@ +/* +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; + + +extern vec3_t CWeaponOffset;//blubs declared this +extern vec3_t CWeaponRot; + +void V_Init (void); +void V_RenderView (void); +float V_CalcRoll (vec3_t angles, vec3_t velocity); +void V_UpdatePalette (void); + diff --git a/source/wad.c b/source/wad.c new file mode 100644 index 0000000..cb38c41 --- /dev/null +++ b/source/wad.c @@ -0,0 +1,160 @@ +/* +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 (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); + 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 (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 (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); +} + + diff --git a/source/wad.h b/source/wad.h new file mode 100644 index 0000000..c47a7f0 --- /dev/null +++ b/source/wad.h @@ -0,0 +1,88 @@ +/* +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 (char *in, char *out); +lumpinfo_t *W_GetLumpinfo (char *name); +void *W_GetLumpName (char *name); +void *W_GetLumpNum (int num); + +void SwapPic (qpic_t *pic); +/* +byte *WAD3_LoadTexture(miptex_t *mt); +byte *ConvertWad3ToRGBA(miptex_t *tex); +*/ +int WAD3_LoadTexture(miptex_t *mt); +int WAD3_LoadTextureName(char *name); +int ConvertWad3ToRGBA(miptex_t *tex); +void WAD3_LoadTextureWadFile (char *filename); + +void W_LoadTextureWadFileHL (char *filename, int complain); +byte *W_ConvertWAD3TextureHL(miptex_t *tex); +byte *W_GetTextureHL(char *name); + diff --git a/source/world.c b/source/world.c new file mode 100644 index 0000000..8a6aa40 --- /dev/null +++ b/source/world.c @@ -0,0 +1,972 @@ +/* +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. + +*/ +// world.c -- world query functions + +#include "quakedef.h" + +/*#ifdef PSP_VFPU +#include +#endif*/ + +/* + +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 dclipnode_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; + int 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; + vec3_t 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 || model->bspversion == NZP_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 + +=============================================================================== +*/ + +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; + +#define AREA_DEPTH 4 +#define AREA_NODES 32 + +static areanode_t sv_areanodes[AREA_NODES]; +static int sv_numareanodes; + +/* +=============== +SV_CreateAreaNode + +=============== +*/ +areanode_t *SV_CreateAreaNode (int depth, vec3_t mins, vec3_t maxs) +{ + areanode_t *anode; + vec3_t size; + vec3_t 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); + if (size[0] > size[1]) + anode->axis = 0; + else + anode->axis = 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; +} + +/* +=============== +SV_ClearWorld + +=============== +*/ +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); +} + + +/* +=============== +SV_UnlinkEdict + +=============== +*/ +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; +} + + +/* +==================== +SV_TouchLinks +==================== +*/ +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) + { + 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; + int 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]); +} + +/* +=============== +SV_LinkEdict + +=============== +*/ +void SV_LinkEdict (edict_t *ent, qboolean 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 + if (ent->v.solid == SOLID_BSP && + (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2])&& ent != sv.edicts )//dr_mabuse1981: rotating fix added + { // 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 + { + 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 + +=============================================================================== +*/ + +#if !id386 + +/* +================== +SV_HullPointContents + +================== +*/ +int SV_HullPointContents (hull_t *hull, int num, vec3_t p) +{ + float d; + dclipnode_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; + if (d < 0) + num = node->children[1]; + else + num = node->children[0]; + } + + return num; +} + +#endif // !id386 + + +/* +================== +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 + +================== +*/ +qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace) +{ + dclipnode_t *node; + mplane_t *plane; + float t1, t2; + float frac; + int i; + vec3_t mid; + int side; + float midf; + +// 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 1 + 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); +#else + if ( (t1 >= DIST_EPSILON && t2 >= DIST_EPSILON) || (t2 > t1 && t1 >= 0) ) + return SV_RecursiveHullCheck (hull, node->children[0], p1f, p2f, p1, p2, trace); + if ( (t1 <= -DIST_EPSILON && t2 <= -DIST_EPSILON) || (t2 < t1 && t1 <= 0) ) + return SV_RecursiveHullCheck (hull, node->children[1], p1f, p2f, p1, p2, trace); +#endif + +// 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; + + 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"); + 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; +} + + +/* +================== +SV_ClipMoveToEntity + +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; + vec3_t 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 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)//dr_mabuse1981: rotate fix... + { + //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); + } + + +// trace a line through the apropriate clipping hull + SV_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace); + + + // 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) + //dr_mabuse1981: rotate fix + { + 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 + if (trace.fraction != 1) + VectorAdd (trace.endpos, offset, trace.endpos); + +// 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 ); +} + + +/* +================== +SV_MoveBounds +================== +*/ +void SV_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs) +{ +#if 0 +// debug to test against everything +boxmins[0] = boxmins[1] = boxmins[2] = -9999; +boxmaxs[0] = boxmaxs[1] = boxmaxs[2] = 9999; +#else + 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; + } + } +#endif +} + +/* +================== +SV_Move +================== +*/ +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; +} + diff --git a/source/world.h b/source/world.h new file mode 100644 index 0000000..46fcda0 --- /dev/null +++ b/source/world.h @@ -0,0 +1,81 @@ +/* +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. + +*/ +// world.h + +typedef struct +{ + vec3_t normal; + float dist; +} plane_t; + +typedef struct +{ + qboolean allsolid; // if true, plane is not valid + qboolean startsolid; // if true, the initial point was in a solid area + qboolean 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 MOVE_NORMAL 0 +#define MOVE_NOMONSTERS 1 +#define MOVE_MISSILE 2 + + +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, qboolean 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_PointContents (vec3_t p); +int SV_TruePointContents (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 + +edict_t *SV_TestEntityPosition (edict_t *ent); +qboolean SV_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace); + +int SV_HullPointContents (hull_t *hull, int num, vec3_t p); + +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 reletive + +// 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) diff --git a/source/zone.c b/source/zone.c new file mode 100644 index 0000000..b27eecb --- /dev/null +++ b/source/zone.c @@ -0,0 +1,942 @@ +/* +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. + +*/ +// Z_zone.c + +#include "quakedef.h" +#ifdef SLIM +#define DYNAMIC_SIZE 0x100000//0xc000 Crow_Bar. UP for PSP +#else +#define DYNAMIC_SIZE 0x40000 +#endif + +#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; + +void Z_ClearZone (memzone_t *zone, int size); + + +/* +======================== +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 + buf = Z_TagMalloc (size, 1); + if (!buf) + Sys_Error ("Z_Malloc: failed on allocation of %i bytes",size); + Q_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"); + } +} + +//============================================================================ + +#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; + +qboolean hunk_tempactive; +int hunk_tempmark; + +void R_FreeTextures (void); + +/* +============== +Hunk_Check + +Run consistancy and sentinal trahing 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: trahsed 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 (qboolean all) +{ + hunk_t *h, *next, *endlow, *starthigh, *endhigh; + int count, sum; + int 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 consistancy checks + // + if (h->sentinal != HUNK_SENTINAL) + Sys_Error ("Hunk_Check: trahsed 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_AllocName +=================== +*/ +void *Hunk_AllocName (int size, char *name) +{ + hunk_t *h; + + if (size < 0) + { + Sys_Error ("Hunk_Alloc: bad size: %i", size); + //Con_Printf ("Hunk_Alloc: bad size: %i", size); + return NULL; + } + + size = sizeof(hunk_t) + ((size+15)&~15); + + if (hunk_size - hunk_low_used - hunk_high_used < size) + { + Sys_Error ("Hunk_Alloc: failed on %i bytes, %s", size, name); + //Con_Printf ("Hunk_Alloc: failed on %i bytes",size); + return NULL; + } + + 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; + Q_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; + } + + size = sizeof(hunk_t) + ((size+15)&~15); + + if (hunk_size - hunk_low_used - hunk_high_used < size) + { + Con_Printf ("Hunk_HighAlloc: failed on %i bytes, %s\n", size, name); + return NULL; + } + + 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; + Q_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, qboolean 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 + new = Cache_TryAlloc (c->size, true); + if (new) + { +// Con_Printf ("cache_move ok\n"); + + Q_memcpy ( new+1, c+1, c->size - sizeof(cache_system_t) ); + new->user = c->user; + Q_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, qboolean 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 + +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: allready 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) + { + cs = Cache_TryAlloc (size, false); + if (cs) + { + 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; + int zonesize = DYNAMIC_SIZE; + + hunk_base = buf; + hunk_size = size; + hunk_low_used = 0; + hunk_high_used = 0; + + Cache_Init (); + p = COM_CheckParm ("-zone"); + if (p) + { + if (p < com_argc-1) + zonesize = Q_atoi (com_argv[p+1]) * 1024; + else + Sys_Error ("Memory_Init: you must specify a size in KB after -zone"); + } + mainzone = Hunk_AllocName (zonesize, "zone" ); + Z_ClearZone (mainzone, zonesize); +} + diff --git a/source/zone.h b/source/zone.h new file mode 100644 index 0000000..7655ad9 --- /dev/null +++ b/source/zone.h @@ -0,0 +1,131 @@ +/* +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); + + +