yquake2-QUAKE2_5_32/0000755000175000017500000000000012615162034014617 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/stuff/0000755000175000017500000000000012615162034015746 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/stuff/yq2.cfg0000644000175000017500000000252012615162034017141 0ustar greffrathgreffrath// // This is an alternate startup script // for Yamagi Quake II, overriding the // default.cfg from pak0.pak. // // !!! PLEASE DO NOT ALTER THIS FILE !!! // // Instead, put your custom stuff in // autoexec.cfg. It's also executed // at startup. // // // KEY BINDINGS // unbindall bind 1 "use Blaster" bind 2 "use Shotgun" bind 3 "use Super Shotgun" bind 4 "use Machinegun" bind 5 "use Chaingun" bind 6 "use Grenade Launcher" bind 7 "use Rocket Launcher" bind 8 "use HyperBlaster" bind 9 "use Railgun" bind 0 "use BFG10K" bind MOUSE1 +attack bind d +back bind e +forward bind s +moveleft bind a +left bind f +moveright bind g +right bind TAB inven bind ENTER invuse bind o invprev bind p invnext bind BACKSPACE invdrop bind MWHEELDOWN weapprev bind MWHEELUP weapnext bind q "use quad damage" bind SHIFT +speed bind SPACE +movedown bind MOUSE2 +moveup bind PAUSE pause bind F1 "cmd help" bind F5 "echo Quick Saving...; wait; save quick" bind F9 "echo Quick Loading...; wait; load quick" bind F12 screenshot bind t messagemode //---------------------------------------------- // // DEFAULT CVARS // set viewsize 100 set vid_fullscreen 0 set sensitivity 3 set crosshair 1 set cl_run 1 set hand 0 set m_pitch 0.022 set m_yaw 0.022 set m_forward 1 set m_side 0.8 set lookspring 0 set lookstrafe 0 set freelook 1 yquake2-QUAKE2_5_32/stuff/cmake/0000755000175000017500000000000012615162034017026 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/stuff/cmake/modules/0000755000175000017500000000000012615162034020476 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/stuff/cmake/modules/FindOggVorbis.cmake0000644000175000017500000000700412615162034024203 0ustar greffrathgreffrath# - Try to find the OggVorbis libraries # Once done this will define # # OGGVORBIS_FOUND - system has OggVorbis # OGGVORBIS_VERSION - set either to 1 or 2 # OGGVORBIS_INCLUDE_DIR - the OggVorbis include directory # OGGVORBIS_LIBRARIES - The libraries needed to use OggVorbis # OGG_LIBRARY - The Ogg library # VORBIS_LIBRARY - The Vorbis library # VORBISFILE_LIBRARY - The VorbisFile library # VORBISENC_LIBRARY - The VorbisEnc library # Copyright (c) 2006, Richard Laerkaeng, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. include (CheckLibraryExists) find_path(VORBIS_INCLUDE_DIR vorbis/vorbisfile.h) find_path(OGG_INCLUDE_DIR ogg/ogg.h) find_library(OGG_LIBRARY NAMES ogg) find_library(VORBIS_LIBRARY NAMES vorbis) find_library(VORBISFILE_LIBRARY NAMES vorbisfile) find_library(VORBISENC_LIBRARY NAMES vorbisenc) mark_as_advanced(VORBIS_INCLUDE_DIR OGG_INCLUDE_DIR OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY VORBISENC_LIBRARY) if (VORBIS_INCLUDE_DIR AND VORBIS_LIBRARY AND VORBISFILE_LIBRARY AND VORBISENC_LIBRARY) set(OGGVORBIS_FOUND TRUE) set(OGGVORBIS_LIBRARIES ${OGG_LIBRARY} ${VORBIS_LIBRARY} ${VORBISFILE_LIBRARY} ${VORBISENC_LIBRARY}) set(_CMAKE_REQUIRED_LIBRARIES_TMP ${CMAKE_REQUIRED_LIBRARIES}) set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${OGGVORBIS_LIBRARIES}) check_library_exists(vorbis vorbis_bitrate_addblock "" HAVE_LIBVORBISENC2) set(CMAKE_REQUIRED_LIBRARIES ${_CMAKE_REQUIRED_LIBRARIES_TMP}) if (HAVE_LIBVORBISENC2) set (OGGVORBIS_VERSION 2) else (HAVE_LIBVORBISENC2) set (OGGVORBIS_VERSION 1) endif (HAVE_LIBVORBISENC2) else (VORBIS_INCLUDE_DIR AND VORBIS_LIBRARY AND VORBISFILE_LIBRARY AND VORBISENC_LIBRARY) set (OGGVORBIS_VERSION) set(OGGVORBIS_FOUND FALSE) endif (VORBIS_INCLUDE_DIR AND VORBIS_LIBRARY AND VORBISFILE_LIBRARY AND VORBISENC_LIBRARY) if (OGGVORBIS_FOUND) if (NOT OggVorbis_FIND_QUIETLY) message(STATUS "Found OggVorbis: ${OGGVORBIS_LIBRARIES}") endif (NOT OggVorbis_FIND_QUIETLY) else (OGGVORBIS_FOUND) if (OggVorbis_FIND_REQUIRED) message(FATAL_ERROR "Could NOT find OggVorbis libraries") endif (OggVorbis_FIND_REQUIRED) if (NOT OggVorbis_FIND_QUITELY) message(STATUS "Could NOT find OggVorbis libraries") endif (NOT OggVorbis_FIND_QUITELY) endif (OGGVORBIS_FOUND) #check_include_files(vorbis/vorbisfile.h HAVE_VORBISFILE_H) #check_library_exists(ogg ogg_page_version "" HAVE_LIBOGG) #check_library_exists(vorbis vorbis_info_init "" HAVE_LIBVORBIS) #check_library_exists(vorbisfile ov_open "" HAVE_LIBVORBISFILE) #check_library_exists(vorbisenc vorbis_info_clear "" HAVE_LIBVORBISENC) #check_library_exists(vorbis vorbis_bitrate_addblock "" HAVE_LIBVORBISENC2) #if (HAVE_LIBOGG AND HAVE_VORBISFILE_H AND HAVE_LIBVORBIS AND HAVE_LIBVORBISFILE AND HAVE_LIBVORBISENC) # message(STATUS "Ogg/Vorbis found") # set (VORBIS_LIBS "-lvorbis -logg") # set (VORBISFILE_LIBS "-lvorbisfile") # set (VORBISENC_LIBS "-lvorbisenc") # set (OGGVORBIS_FOUND TRUE) # if (HAVE_LIBVORBISENC2) # set (HAVE_VORBIS 2) # else (HAVE_LIBVORBISENC2) # set (HAVE_VORBIS 1) # endif (HAVE_LIBVORBISENC2) #else (HAVE_LIBOGG AND HAVE_VORBISFILE_H AND HAVE_LIBVORBIS AND HAVE_LIBVORBISFILE AND HAVE_LIBVORBISENC) # message(STATUS "Ogg/Vorbis not found") #endif (HAVE_LIBOGG AND HAVE_VORBISFILE_H AND HAVE_LIBVORBIS AND HAVE_LIBVORBISFILE AND HAVE_LIBVORBISENC) yquake2-QUAKE2_5_32/stuff/cmake/modules/FindSDL2.cmake0000644000175000017500000001421512615162034023010 0ustar greffrathgreffrath# Locate SDL2 library # This module defines # SDL2_LIBRARY, the name of the library to link against # SDL2_FOUND, if false, do not try to link to SDL2 # SDL2_INCLUDE_DIR, where to find SDL.h # # This module responds to the the flag: # SDL2_BUILDING_LIBRARY # If this is defined, then no SDL2main will be linked in because # only applications need main(). # Otherwise, it is assumed you are building an application and this # module will attempt to locate and set the the proper link flags # as part of the returned SDL2_LIBRARY variable. # # Don't forget to include SDLmain.h and SDLmain.m your project for the # OS X framework based version. (Other versions link to -lSDL2main which # this module will try to find on your behalf.) Also for OS X, this # module will automatically add the -framework Cocoa on your behalf. # # # Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration # and no SDL2_LIBRARY, it means CMake did not find your SDL2 library # (SDL2.dll, libsdl2.so, SDL2.framework, etc). # Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. # Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value # as appropriate. These values are used to generate the final SDL2_LIBRARY # variable, but when these values are unset, SDL2_LIBRARY does not get created. # # # $SDL2DIR is an environment variable that would # correspond to the ./configure --prefix=$SDL2DIR # used in building SDL2. # l.e.galup 9-20-02 # # Modified by Eric Wing. # Added code to assist with automated building by using environmental variables # and providing a more controlled/consistent search behavior. # Added new modifications to recognize OS X frameworks and # additional Unix paths (FreeBSD, etc). # Also corrected the header search path to follow "proper" SDL guidelines. # Added a search for SDL2main which is needed by some platforms. # Added a search for threads which is needed by some platforms. # Added needed compile switches for MinGW. # # On OSX, this will prefer the Framework version (if found) over others. # People will have to manually change the cache values of # SDL2_LIBRARY to override this selection or set the CMake environment # CMAKE_INCLUDE_PATH to modify the search paths. # # Note that the header path has changed from SDL2/SDL.h to just SDL.h # This needed to change because "proper" SDL convention # is #include "SDL.h", not . This is done for portability # reasons because not all systems place things in SDL2/ (see FreeBSD). #============================================================================= # Copyright 2003-2009 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) SET(SDL2_SEARCH_PATHS ~/Library/Frameworks ~/dev/fakeroot /Library/Frameworks /usr/local /usr /sw # Fink /opt/local # DarwinPorts /opt/csw # Blastwave /opt #Windows Search paths "C:/Program Files (x86)/" "C:/Program Files/" "$ENV{ProgramFiles}/" "C:/fakeroot/" "C:/dev/libs" ) FIND_PATH(SDL2_INCLUDE_DIR SDL.h HINTS $ENV{SDL2DIR} PATH_SUFFIXES include/SDL2 include PATHS ${SDL2_SEARCH_PATHS} ) FIND_LIBRARY(SDL2_LIBRARY_TEMP NAMES SDL2 HINTS $ENV{SDL2DIR} PATH_SUFFIXES lib64 lib PATHS ${SDL2_SEARCH_PATHS} ) IF(NOT SDL2_BUILDING_LIBRARY) IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") # Non-OS X framework versions expect you to also dynamically link to # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms # seem to provide SDL2main for compatibility even though they don't # necessarily need it. FIND_LIBRARY(SDL2MAIN_LIBRARY NAMES SDL2main HINTS $ENV{SDL2DIR} PATH_SUFFIXES lib64 lib PATHS ${SDL2_SEARCH_PATHS} ) ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") ENDIF(NOT SDL2_BUILDING_LIBRARY) # SDL2 may require threads on your system. # The Apple build may not need an explicit flag because one of the # frameworks may already provide it. # But for non-OSX systems, I will use the CMake Threads package. IF(NOT APPLE) FIND_PACKAGE(Threads) ENDIF(NOT APPLE) # MinGW needs an additional library, mwindows # It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows # (Actually on second look, I think it only needs one of the m* libraries.) IF(MINGW) SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") ENDIF(MINGW) IF(SDL2_LIBRARY_TEMP) # For SDL2main IF(NOT SDL2_BUILDING_LIBRARY) IF(SDL2MAIN_LIBRARY) SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) ENDIF(SDL2MAIN_LIBRARY) ENDIF(NOT SDL2_BUILDING_LIBRARY) # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. # CMake doesn't display the -framework Cocoa string in the UI even # though it actually is there if I modify a pre-used variable. # I think it has something to do with the CACHE STRING. # So I use a temporary variable until the end so I can set the # "real" variable in one-shot. IF(APPLE) SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") ENDIF(APPLE) # For threads, as mentioned Apple doesn't need this. # In fact, there seems to be a problem if I used the Threads package # and try using this line, so I'm just skipping it entirely for OS X. IF(NOT APPLE) SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) ENDIF(NOT APPLE) # For MinGW library IF(MINGW) SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) ENDIF(MINGW) # Set the final string here so the GUI reflects the final state. SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") # Set the temp variable to INTERNAL so it is not seen in the CMake GUI SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") ENDIF(SDL2_LIBRARY_TEMP) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) yquake2-QUAKE2_5_32/stuff/osx/0000755000175000017500000000000012615162034016557 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/stuff/misc/0000755000175000017500000000000012615162034016701 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/stuff/cdripper.sh0000755000175000017500000000101612615162034020113 0ustar greffrathgreffrath#!/bin/sh set -eu # simple script that rips CDs to a format useable by Yamagis Quake2 client # Needs cdparanoia and oggenc, useable with Quake II and both addons. # Create directory mkdir -p music cd music # rip all tracks beginning with second one (the first track is data) cdparanoia -B "2-" for I in track*.cdda.wav; do NUM="${I#track}" NUM="${NUM%.cdda.wav}" oggenc -q 6 -o "$NUM.ogg" "$I" done # remove .wav files rm *.wav echo ' Ripping done, move music/ directory to /your/path/to/quake2/game/music/' yquake2-QUAKE2_5_32/stuff/quake2-start.sh0000755000175000017500000000136112615162034020631 0ustar greffrathgreffrath#!/bin/sh set -eu # PID of "gnome-screensaver-command -i" (if used) GSC_PID="" # if gnome-screensaver is running in the background.. if ps auxww | grep -q 'gnome-screensaver'; then echo "inhibiting gnome screensaver" gnome-screensaver-command -i & # save the PID of the last command GSC_PID="$!" fi # Stop unclutter if ps auxww | grep -q 'unclutter'; then echo 'inhibiting unclutter' killall -STOP unclutter fi # enable core dumps ulimit -c unlimited # run quake 2 ./quake2 "$@" # Continue unclutter if ps auxww | grep -q 'unclutter'; then echo 'reactivating unclutter' killall -CONT unclutter fi # if gnome-screensaver was running.. if [ -n "$GSC_PID" ]; then echo "reactivating gnome screensaver" kill "$GSC_PID" fi yquake2-QUAKE2_5_32/stuff/icon/0000755000175000017500000000000012615162034016676 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/.gitignore0000644000175000017500000000003612615162034016606 0ustar greffrathgreffrath/build/ /release/ *.mk *.user yquake2-QUAKE2_5_32/CONTRIBUTE0000644000175000017500000001023212615162034016216 0ustar greffrathgreffrath * ****************************** * * Yamagi Quake II * * http://www.yamagi.org/quake2 * * http://github.com/yquake2 * * ****************************** * TODO List =============================================================================== This is a list of features that are currently not implemented in Yamagi Quake II, but would be nice to have. If you have some freetime to spare, fun coding or just want to contribute something, this list has some suggestions. All tasks need strong C knowledge, the necessary amount of understandig of the Quake II source code or external ABIs differs between the tasks. Some hints: - Sign up for a Github account and fork our yquake2 repository. This allows the easy integration of upstream changes into your branch and sending of pull requests. You'll get a wiki and a bugtracker for free. - To contribute your changes back into the main project send pull requests via Github. It's much easier to review and merge pull requests than patches. Please send only pull reqeuests from a distinct branch at not from your "master" branch! - Quake II has a very fragile and broken codebase. Even after years of cleanup it's still a disaster. Therefore: - Do only one change at a time! - Test after each change (play at least through base1.bsp) - Commit early and commit often to create a fine grained history. This helps "git bisect" to find bugs and errors. - Do not try to clean up things or even rewrite code that you do not understand to 110%! Even small behavioral changes can introduce gameplay changes and trigger new bugs! Especially everything that depends on map data (e.g. path finding or collision detection) is very likely to break in interesting ways! - Do not add new dependencies. If you must add a new one contact the Yamagi Quake II developers prior to it! Everything that adds dependencies should be hided behint preprocessor macros. - If your changes change the gameplay experience, make them optional by introducing a new cvar. - Linux is not the only operating system out there. All changes should be portable to other platform (writing pure ANSI-C or C99 is recommended but not always applicable). - x86 ist not the only CPU architecture. All changes should be done in pure C (e.g. no inline assembler) and in an endianess independed way. - gcc is not the only compiler. Test your changes with clang. =============================================================================== 1. Port Yamagi Quake II to new unixoid platforms (for example DragonflyBSD, NetBSD, Solaris, etc.) Difficulty: Easy Knowledge: Good knowledge of the target platform Yamagi Quake II runs fine on Linux and FreeBSD. Due to it's very low hardware requirements it's an ideal game for platforms without good 3D acceleration. Ports to new unixoid operating systems should be easy. In most cases only some #ifdef need to be added and the Makefile integration written. =============================================================================== 2. Source code cleanup Difficulty: Medium Knowledge: Good knowledge of the Yamagi Quake II source While the Yamagi Quake II source code was cleaned a few times there is still much left to do. Someone need to go through the source, read and audit it. Dead code should be removed, inefficient functions rewritten and bugs resolved. This can be done at one module (e.g. client, server, refresher, game, etc) at a time. =============================================================================== 3. Finish the port of Zaero Difficulty: Hard Knowledge: How to work with broken code Zaero is an unofficial but popular addon to Quake II. It was release as freeware. The Yamagi Quake II developers did an inital port, but it's unfinished and still buggy. Grab the source (take a look at our Github organization), clean it and debug it. Zaero will need some extensive testing, have fun while playing. :) =============================================================================== yquake2-QUAKE2_5_32/README0000644000175000017500000010603612615162034015505 0ustar greffrathgreffrath * ****************************** * * Yamagi Quake II * * http://www.yamagi.org/quake2 * * http://github.com/yquake2 * * ****************************** * =============================================================================== This is the Yamagi Quake II Client, an enhanced Version of id Software's Quake II. The main focus is single player, the gameplay and the graphics are unchanged, but many bugs were fixed. Unlike most other Quake II ports Yamagi Quake II is full 64 bit clean so it works perfectly on modern amd64 (x86_64) processors and operating systems. This code should run on Windows XP or later, Mac OS X 10.6 or higher and on most unix-like operating systems (only FreeBSD, Linux and OpenBSD are officially supported and tested, for other systems you'd at least have to edit the Makefile), just type "make" or "gmake" to compile. This code is based upon Icculus Quake II, which itself is built upon id Software's original code drop. Additional code and patches by many contributers were used. It's released under the terms of the GPL version 2. See the LICENSE file for further information. =============================================================================== Content of this file: -------------------- 1. Installation on (Free|Open)BSD and Linux 1.1 Supported Systems 1.2 Retail Version 1.3 Demo Version 1.4 Addons 1.5 Compiling 1.6 Default Configuration 2. Installation on Microsoft Windows 2.1 Supported Systems 2.2 Retail Version 2.3 Demo Version 2.4 Addons 2.5 Binary Installation 2.6 Compiling 2.7 Default Configuration 3. Installation on OS X 3.1 Supported Systems 3.2 Retail Version 3.3 Demo Version 3.4 Addons 3.5 Binary Installation 3.6 Compiling 3.7 Default Configuration 4. OGG/Vorbis playback 4.1 Setup for the original soundtrack 4.2 Setup for other music and playlists 4.3 Manual control 4.4 Console variables 5. Configuration 5.1 Video 5.2 Input 5.3 Sound 5.3.1 The classic sound system 5.3.2 The OpenAL sound system 6. Bugreports 7. FAQ =============================================================================== 1. Installation on (Free|Open)BSD and Linux =========================================== Note: Some Linux distributions have packages of Yamagi Quake II that might even assist you in installing the game data. In Debian and Ubuntu it's called "yamagi-quake2", the package "game-data-packager" should help you with installing the game data. Note however that those packages are usually outdated a few versions, so if you encounter any bugs in them it's possible that we have already fixed those bugs in a later release. 1.1 Supported Systems: ---------------------- Officially, only FreeBSD, Linux and OpenBSD on i386 (x86), amd64 (x86_64), sparc64 and compatible CPUs are supported. Other (Unix-like) Operating Systems and hardware architectures are untested and may need small changes, at least in the Makefile. Yamagi Quake II needs OpenGL 1.1 (better: 1.4) support in hardware and libGL; OpenGL ES will not work. The only tested compilers are gcc 4.2 (or later) and clang 3.0 (or later). Patches (or better Github pull request) for other platforms are welcome. :-) 1.2 Retail Version: ------------------- If you own Quake II, first get the official point release to Quake II 3.20: ftp://ftp.idsoftware.com/idstuff/quake2/q2-3.20-x86-full-ctf.exe Use this and only this file! Unofficial "linux pointreleases" or something like that will not work and may crash your game! Create a new directory "quake2/" and extract (with unzip) the file you just downloaded into it. Even if the file extension is ".exe" it's a self-extracting zip file. Now delete the following files and directories: - 3.20_Changes.txt - quake2.exe - ref_gl.dll - ref_soft.dll - baseq2/gamex86.dll - baseq2/maps.lst - ctf/ctf2.ico - ctf/gamex86.dll - ctf/readme.txt - ctf/server.cfg - xatrix/gamex86.dll - rogue/gamex86.dll Now copy the file "pak0.pak" and the directory "video/" from the Quake II CD-ROM to the "baseq2/" directory of your installation. 1.3 Demo Version: ----------------- If you haven't got Quake II, try the demo version. Get it here: ftp://ftp.idsoftware.com/idstuff/quake2/q2-314-demo-x86.exe Unzip this file (again, it's a self-extracting zip file). Create a new "quake2/" directory with a "baseq2/" sub-directory and put the "pak0.pak" and the "players/" sub-directory into it - you can find them within the unzipped files in Install/Data/baseq2/ - in your "baseq2/" directory. No patching is needed for the demo, in fact it would break it. 1.4 Addons ---------- Due to license issues - Yamagi Quake II is covered by the GPL and the addons are under the id Software SDK license - the addons are distributed separately. You can get them at http://www.yamagi.org/quake2, both contain installation instructions. But nevertheless you'll need an installation of the full Quake II game with our client for playing them. The same applies to the "ctf" capture the flag addon. 1.5 Compiling: -------------- After you have set up the game data (from the full version or the demo), you have to compile the Yamagi Quake II client. You will need the following dependencies (by editing the Makefile the requirement of most of this depencenies can be removed, but it'll lead to the loss of features): - A libGL implementation (Mesa3D, nVidia, AMD Catalyst, etc.) - OpenGL system headers - libogg with development headers - libvorbis with development headers - OpenAL with development headers - SDL 1.2 or 2.0 (the latter is recommended) with development headers and sdl-config(1) - ZLib Extract the source, change into the new created directory and type "make" (Linux) or "gmake" (FreeBSD, OpenBSD). After the compilation finished, copy the following files from "release/" to your installation directory preserving the directory structure: - q2ded - quake2 - baseq2/game.so 1.6 Default Configuration ------------------------- Quake II ships with an old and for today standards "insane" default configuration. This is no problem since you can alter everything. To make your life easier Yamagi Quake II contains an updated default configuration. If you want to use it just copy "stuff/yq2.cfg" to your "baseq2/" folder. Now you are ready to start your brand new Quake II. Have fun. =============================================================================== 2. Installation on Microsoft Windows ==================================== Yamagi Quake II has full support for Microsoft Windows. All features are supported, including the IPv6 network code and the OpenAL sound backend. Installation can be done by using the binary release (this is highly recommended) or by compiling the source with MinGW. 2.1 Supported Systems --------------------- Yamagi Quake II should run on Windows XP or higher, older versions are not supported. You'll need a graphics card with support for at least OpenGL 1.1 (OpenGL 1.4 is recommended). Both x86 and x86_64 Windows installations are supported, but x86 is much more tested. 2.2 Retail Version ------------------ If you own Quake II, first get the official point release to Quake II 3.20: ftp://ftp.idsoftware.com/idstuff/quake2/q2-3.20-x86-full-ctf.exe Use this and only this file! Unofficial pointreleases or something like that will not work and may crash your game! Extract the file into a new directory (we recommend quake2\) and remove the following files and directories: - 3.20_Changes.txt - quake2.exe - ref_gl.dll - ref_soft.dll - baseq2\gamex86.dll - baseq2\maps.lst - ctf\ctf2.ico - ctf\gamex86.dll - ctf\readme.txt - ctf\server.cfg - xatrix\gamex86.dll - rogue\gamex86.dll Now put the Quake II CD-ROM into your cd drive and cancel the installation. Copy "pak0.pak" and the directory "video\" to the "baseq2\" directory of your installation. 2.3 Demo Version ---------------- If you haven't got Quake II, try the demo version. Get it here: ftp://ftp.idsoftware.com/idstuff/quake2/q2-314-demo-x86.exe Extract this file into a new directory and delete everything but "baseq2\pak0.pak" and the "baseq2\players\" directory. No patching is needed for the demo, in fact it would break it. 2.4 Addons ---------- Due to license issues - Yamagi Quake II is covered by the GPL and the addons are under the id Software SDK license - the addons are distributed separately. You can get them at http://www.yamagi.org/quake2, both contain installation instructions. But nevertheless you'll need an installation of the full Quake II game with our client for playing them. The same applies to the "ctf" capture the flag addon. Please note, that support for the addons is included in the binary release (see below). 2.5 Binary Installation ----------------------- We highly recommend, that you use our binary release of Yamagi Quake 2. Just extract it over the directory created in step 2.3 or 2.4. If you want to copy the files by hand, just copy them over by preserving the directory structure. Please make sure that openal32.dll is copied too. Otherwise Yamagi Quake 2 may use a systemwide installed library, which may cause problems including a non starting game. 2.6 Compiling ------------- Compiling Yamagi Quake II from source is unnecessary as long as you do not want to use the github version or want to develop on Windows. If you really want to compile Yamagi Quake II by yourself follow these steps: 1. Grab an up to date version of the MinGW build environment from http://deponie.yamagi.org/quake2/windows/build/, extract it to C:\MinGW and start either the 32 bit or the 64 bit version by C:\MinGW\MSYS32 or C:\MinGW\MSYS64. 2. Navigate to your Yamagi Quake II source. If you need to check it out from git, first install git from http://git-scm.com/. Type "make" to compile. Please note, that compilation on network shares is somewhat shaky. After compiling, copy the following files from "release\" to your Quake II installation preserving the directory structure: - q2ded.exe - quake2.exe - baseq2\game.dll You'll need an "openal32.dll". You can use and rename the OpenAL DLL that that is provided by OpenAL Soft (see http://kcat.strangesoft.net/openal.html). Please note that the name of the DLL is always "openal32.dll", even if it's a 64 bit library. 2.7 Default Configuration ------------------------- Quake II ships with an old and for today standards "insane" default configuration. This is no problem since you can alter everything. To make your life easier Yamagi Quake II contains an updated default configuration. If you want to use it just copy "stuff\yq2.cfg" to your "baseq2\" folder. Now you are ready to start your brand new Quake II. Have fun. =============================================================================== 3. Installation on OS X ======================= Yamagi Quake II has full support for Apple OS X. All features are supported, including the IPv6 network code and the OpenAL sound backend. Installation can be done by using the binary release or by compiling the source. 3.1 Supported Systems --------------------- Yamagi Quake II should run on every Mac with Intel CPU and OS X 10.6 or higher. 3.2 Retail Version ------------------ If you own Quake II, first get the official point release to Quake II 3.20: ftp://ftp.idsoftware.com/idstuff/quake2/q2-3.20-x86-full-ctf.exe Use this and only this file! Unofficial pointreleases or something like that will not work and may crash your game! Extract the file into a new directory (we recommend quake2\) and remove the following files and directories: - 3.20_Changes.txt - quake2.exe - ref_gl.dll - ref_soft.dll - baseq2\gamex86.dll - baseq2\maps.lst - ctf\ctf2.ico - ctf\gamex86.dll - ctf\readme.txt - ctf\server.cfg - xatrix\gamex86.dll - rogue\gamex86.dll Now put the Quake II CD-ROM into your cd drive and copy "pak0.pak" and the directory "video\" to the "baseq2\" directory of your installation. 3.3 Demo Version ---------------- If you haven't got Quake II, try the demo version. Get it here: ftp://ftp.idsoftware.com/idstuff/quake2/q2-314-demo-x86.exe Extract this file into a new directory and delete everything but "baseq2\pak0.pak" and the "baseq2\players\" directory. No patching is needed for the demo, in fact it would break it. 3.4 Addons ---------- Due to license issues - Yamagi Quake II is covered by the GPL and the addons are under the id Software SDK license - the addons are distributed separately. You can get them at http://www.yamagi.org/quake2, both contain installation instructions. But nevertheless you'll need an installation of the full Quake II game with our client for playing them. The same applies to the "ctf" capture the flag addon. 3.5 Binary Installation ----------------------- Extract the zip archive and right click on the app. Select "Show Contents", a new finder window will open. Navigate to Contents/Resources/baseq2 and copy the contents of the baseq2/ folder (created while preparing the game data, see above) into it. Close the finder windows. Yamagi Quake II is now ready. 3.6 Compiling ------------- Compiling Yamagi Quake II from source is unnecessary as long as you will not use the github version or want to develop on OS X. If you really want to compile Yamagi Quake II by yourself follow these steps: 1. Make sure that you've installed XCode with the "Unix Develoment Tools". Depending on your OS X version the name of these can be different. 2. Install dependencies using Homebrew (http://brew.sh). Once you have Homebrew installed, run the following commands from the terminal to install dependencies: brew install sdl2 --universal brew install openal-soft --universal brew install libogg --universal brew install libvorbis --universal 3. Open a terminal and navigate to the Yamagi Quake II source code. Type "make" to compile it. After that the binaries can be found in the release/ directory. The binaries can be put into an app-bundle (see 3.5 Binary Installation) or used in the classic classic unix way. An empty .App template is included in /stuff/osx. In the latter case the game be started at the command line. Be aware that the app-bundle support has to be deactivated in the Makefile, otherwise the game is unable to find it's libraries! 3.7 Default Configuration ------------------------- Quake II ships with an old and for today standards "insane" default configuration. This is no problem since you can alter everything. To make your life easier Yamagi Quake II contains an updated default configuration. If you want to use it just copy "stuff\yq2.cfg" to your "baseq2\" folder. Now you are ready to start your brand new Quake II. Have fun. =============================================================================== 4. OGG/Vorbis playback ====================== Since most modern CD-ROM and DVD drives don't have an analog audio output and most sound codecs don't have the appropriate input header, it's not possible to use CD audio as background music on such systems. With SDL 2.0 CD audio is unsupported Therefore OGG/Vorbis music support has been added to Yamagi Quake II. 4.1 Setup for the original soundtrack: -------------------------------------- Put your Quake II CD-ROM into your drive, start your favorite CD extractor and rip the audiotracks into OGG/Vorbis files. These files must be named after their track number, beginning with 02, because the first track is data. If everything is done correct, you should have: 02.ogg, 03.ogg, ..., 11.ogg. Alternatively you can use a script which can be found in the folder "stuff/". It needs cdparanoia and oggenc and should work with the main game and both addons. Drop these files in "baseq2/music/", start Quake II, enter the "Options" menu and set "OGG music" to enabled. "CD music" will be automaticly disabled. Quake II will now play the OGG/Vorbis files instead of the Audio-CD. 4.2 Setup for other music and playlists: ---------------------------------------- You can put any OGG/Vorbis files into "baseq2/music/" or "your_mod/music/". If shuffle is enabled, Quake II will shuffle through all files, otherwise it will loop through the track associated with the map. A playlist is also supported. Just put the filenames into music/playlist (a plain text file) and start the game. 4.3 Manual control: ------------------- For manual control of ogg playback the following console commands are available: - ogg_play {file | #n | ? | >n | n which indicates to advance n positions (defaults to 1). * n | n, which indicates to advance n seconds. * 0" and "ogg_seek <0" to get the current position without changing it. - ogg_status Display status ("playing a file", "paused", "stopped", etc). 4.4 Console variables: ---------------------- - ogg_enable {0 | 1} Enables the Ogg Vorbis subsystem if set to "1". Defaults to "0". - ogg_playlist {name} Use "name" as a list of files instead of listing the contents of "music". Note that the files must be in "music" and follow ogg_play's syntax for files. Defaults to "playlist". - ogg_sequence {next | prev | random | loop | none} When a file ends, start playing another one, depending on the value: * next: play the next file. * prev: play the previous file. * random: play a random file. * loop: play the same file again. * none: stop playing. Defaults to "loop". - ogg_volume Volume of the music between 0 and 2. Defaults to "0.7". =============================================================================== 5. Configuration ================ While configuring Quake II is straight forward some rough edges can arise. Before reporting bugs or mailing us please read this section all the hints covered in it! 5.1 Video --------- For most people the options in the "Video" menu are sufficent. But there are some things that can and in some cases must be tuned via cvars. Here the most common questions are answered. - Yamagi Quake II has full support for widescreen setups. Just select your favorite resolution via the video menu. - If your resolution is not in the list, it's also possible to set custom resolutions via the console: Set gl_customwidth and gl_customheight to the desired values. Change gl_mode to -1 or enter the "Video" menu and select "Custom" as video mode. - Sync problems resulting in tearing and artifacts in the lower half of the screen: These orginiate in the fact, that in 1997 LCD flat panels were not widely used because they were very expensive and much too slow for gaming. Thus Quake II has problems when played on most flat panel monitors. The solution for this problem is simple: Just set "cl_maxfps" to about 95 FPS and enable the vsync by setting "gl_swapinterval" to 1. This should supress all of the problems. - Particle effects are broken. They're just squares and not perfectly round: This is a problem by your graphics driver, not implementing a special filter mode for "points". Set "gl_ext_pointparameters" to 0 to get better (but not perfect) particles. - The game is bright enough but it's also washed out and dull: You need more saturation. Just adjust the cvar "intensity". The default 2 should be enough for most cases, but some setups require higher levels. - If the colors look over-saturated try setting the cvar "intensity" to a lower value, e.g. 1. - Yamagi Quake II offers hardware gamma control in realtime in the "Video" menu. If Quake II is still too dark, set the "vid_gamma" cvar by hand to values above 1.5. If models and dynamic lights are too dark consider increasing the "gl_modulate" cvar. - Yamagi Quake II can draw shadows. Just set "gl_shadows" to 1. You most likely want to set "gl_stecilshadow" to 1 too. This enables high quality stencil buffer shadows. - Yamagi Quake II has support for anisotropic filtering. Activating it improves texture drawing over large distances a bit. Enter "gl_anisotropic_avail" in your console for the maximum amount of filtering supported by your video card and set the cvar "gl_anisotropic" to the desired value. It must be a power of 2, in most cases 2, 4, 8 or 16. - Yamagi Quake II has support for the high resolution retexturing pack, created by the community. Installation is easy: 1. Download q2_textures.zip from http://deponie.yamagi.org/quake2/texturepack/ (There's also models.zip, but not all contained textures fit on the original Quake II models and thus look broken.) 2. Extract the file into the "baseq2/" directory of your Quake II installation, so that the new directories "baseq2/textures/" is created. The retexturing pack is used by default if it's installed. It can be switched off at any time by setting "gl_retexturing" to "0" and executing "vid_restart" aftwards. - Yamagi Quake II has support for antialiasing. Set gl_msaa_samples to the desired antialiasing factor (most graphic cards support 2, 4, 8 and 16), followd by a vid_restart. Please note that very old graphic cards may not support antialiasing at all. - It's possible to upscale nearly all 2D artwork. This is especially useful on high resolution displays were the cosole and HUD can become very small or even unreadable. Scaling support is devided into 3 parts which are controlled by cvars: - gl_consolescale: Scale the console and most in-game texts - gl_menuscale: Scales the menu. Please note that the menu was never ment to be scaled and slight disalignement (especially in the "Player Setup" menu) are unavoidable. That's not considered a bug. - gl_hudscale: Scales the in-game HUD All 3 cvars work the same way. They're set to the scale factor. A factor of 1 (defaults) means no scaling at all. Values smaller than 1 but bigger than 0 downscale the artwork, it becomes smaller. A value of 0 means no artwork at all. Values greater than 1 enlarge the artwork. Please note that full numbers will give best results. For example 1.7 will lead to small distortions while 2 will not. Most users will set all 3 cvars to -1. In that case the game calculates a more or less optimal scaling factor, matching the artwork size at a resolution of 640x480. 5.2 Input --------- Quake II had a rather simple input system, even back in 1997. It just mapped Windows 95 mouse directly on movements. That was a very acurate way to do it, Quake II was - like all other id Software games - much more acurate than most games out there. But there were some problems. First the mouse input depends on the operation systems mouse driver. Another operating system or even another mouse and the input changed drastically. That sucked. Yamagi Quake II features a from scratch rewritten mouse backend based on SDL. It gives you the same mouse behavior, regardless of your operating system or hardware. But sadly it can't emulate the old behavior in all cases. There are some cvar to adjust: - in_mouse -> Set to 0 to disable the mouse. - sensitivity -> The sensistivity of the mouse. Adjust to your needs, via the cvar or via the "Options" menu. - m_filter -> A mouse filter. This was added in one of the countless point releases but it was broken. We fixed it. The effect is the same as in Quake III Arena, instead of using the raw movement signals, two of them are combined, filtering vibrations and things like that out. - exponential_speedup -> "0" is disabled. A very simple approach to mouse acceleration, much simpler than modern mouse acceleration. Sadly it's nearly impossible to add modern acceleration to Quake II since most of the needed data isn't available to the input backend. 5.3 Sound --------- Quake II featured one of the best sound systems of it's time (for example it had support for realtime calculated stereo effects) but sadly it was totaly broken. Therefore id Software rewrote it once, later it was rewritten again for the linux port. That fixed the most visible problems, but the code was just crap and broke again as time passed and sound on PCs evolved. For Yamagi Quake II 3.0 the sound system was overhauled, featuring a complete code audit of the upper layers with many bugfixes and memory leak plugs. The backend was rewritten from scratch. This should solve most if not all problems. Yamagi Quake II 4.20 featured an optional OpenAL sound system, enabling better stereo calculations and even surround support. 5.3.1 The classic sound system ------------------------------ This is the original sound implementation, as used in the first release of Quake II in 1997. It featured stereo calculations for most samples. It's disabled by default and can be reenabled by setting "s_openal" to "0", followed by "snd_restart. Common problems with the classic sound system are: - The earthquake sound sample is distorted This is not a fault of the sound code but of the sound sample itself. It's mostly made of very low frequency noices and sampled in only 22kHz, bringing cheap onboard soundcards to the limit. The only solution would be to change the sample... - The sound is stuttering and cracking This is most likely a problem on your side! First make sure that your SDL sound backend is installed properly. Does the sound work in other SDL games like ioquake3? If possible remove all sound servers from your stack and use plain OSS or ALSA via libasound. If everything fails try create an ~/.asoundrc with this contents: pcm.!default { type hw card 0 } ctl.!default { type hw card 0 } 5.3.2 The OpenAL sound system ----------------------------- This is a sound system based upon the popular OpenAL audio library. It features surround playback which gives a huge improvement in sound quality and gameplay experience. It's enabled by default, but can be disabled by setting "s_openal" to "0", followed by a "snd_restart". To work correctly it's in the need of a correctly configured OpenAL implementation! On OS X and Windows the default configuration is okay. On FreeBSD, Linux and OpenBSD OpenAL is configured in the file ~/.alsoftrc (for the openal-soft implementation, other implementations may vary). The most important options (tested with OpenAL Soft 1.14) are: - channels = surround51 -> Enable 5.1 surround support. Other values are "mono", "stereo", "quad", "surround61" and "surround71". - stereodup = true -> If set to "true" all raw stereo samples (in Quake II the background music and video sounds) are duplicated behind the listener. Otherwise they're played only through the front speakers. - resampler = cubic -> Use cubic resampling. While this requires more cpu power than the default linear resamling it's highly recommended since Quake II has several hard to resamples sound effects. Especially the earthquake sound can distort if a low quality resampler is employed! - hrtf = true -> When playing with headphones this gives a much better surround experience, even with only two channels. But playback will sound "broken" on normal speakers. If the sound is distorted and cracking, most likely the ingame volume is set too high. Lower it by setting the "s_volume" CVAR to 0.3 or even less and use the system mixer instead! If everything failes set s_openal_maxgain to a lower value like 0.3 to clamp the maximum preamplification gain. But beware! The side effect is a limited dynamic range! =============================================================================== 6. Bugreports ============= Something is not working as expected? An elevator is broken? An enemy doesn't move? Or the whole game is just crashing? Just open a new github issue at https://github.com/yquake2/yquake2. Please include a problem description and - if possible - a screenshot of the problematic situation and the name of the problematic map. In case of crashes, further helpful information (and instructions) are printed to stdout (your terminal, ...\Documents\YamagiQ2\ stdout.txt on Windows). But first, read this little FAQ: My SDL sound is not working! - Most reported sound problems exist between keyboard and chair. Please make sure, the the correct SDL sound backend is installed and configured! Does the sound work in other SDL games? Does your setup support at least five virtual channels? In most cases it's better to not use sound servers like Pulseaudio but the plain sound system like OSS or ALSA with libasound instead. Also see the "Sound" section in this file! My OpenGL is not working! - Make sure, that OpenGL is working in other games. Use "glxinfo" and "glxgears" to make sure, that hardware rendering is available. Otherwise, fix your setup. If reporting OpenGL bugs please include a copy of your xorg.conf (if available) and the Xorg.0.log. The game is crashing! - Make sure that your installation is complete. Missing files will crash Quake II on random occasions and will produce strange backtraces! This just wastes our time, so please check first and report then! Valgrind reports many, many memory leaks! - Yeah it does. But they're usually false positives due to Quake IIs caching architecture. There are some real memory leaks in SDL, Mesa3D, X11 and so on but they're out of our scope. So before reporting memory leaks please read the code, understand the code and be sure that's a real leak! =============================================================================== 7. FAQ ====== How do I open the console? - Press "^" or "~", depending on your keyboard layout. How do I get the frame counter? - Set "cl_drawfps" to 1 How do I make a benchmark? - Set "timedemo" to 1 and play a demo. How do I play demos? - "demomap name.dm2". Note that the extension .dm2 is important! How do I record a demo? - "record name" and "stop" to stop. When playing in window mode my cursor is locked onto the window. Can I change that, so that Quake II behaves like a normal window? - Open the console by pressing ~ or ^ or drop into the menu. If you want Quake II to never grab the mouse set "in_grab" to 0, if Quake II should never release the mouse set 1, for releasing the mouse when the console or the menu is opened set to 2. The default is 2. Hey, my screensaver crashes Quake II or I experience strange crashes after a fixed amount of time! - This is a known bug in some linux distributions. SDL fails to disable the screensaver even if we tell him to do so. See this Ubuntu bugreport: https://bugs.launchpad.net/ubuntu/+source/gnome-screensaver/+bug/32457 As a work around use the startscript in stuff/quake-start.sh It deactivates the screensaver before starting Quake II and reenables it after exiting the game. Okay, Yamagi Quake II is for single player and coop. But what's with us deathmatch and / or CTF freaks? - Use another client. There are clients out there which offer far better multi- player experiences. They're featuring greatly improved network code and a better client<->server integration. Take a look at EGL, r1q2 or AprQ2. At least r1q2 should work on unixlike operating systems. The movement is fucked up! I can jump much higher and longer as it used to be! What's wrong? - You're experiencing the Quake II version of the famous Q3A 125hz bug. When Quake II draws more than about 100 FPS the movement calculations go wrong and you can jump much higher. To solve this set "cl_maxfps" to about 95 FPS. And no, we won't fix it since it would be very invasive and most likely break a lot of other things. I'm creating a package or port for my system. Is a system wide install possible without patching the source? - Yes. Just set -DSYSTEMWIDE. If you want to change the default directory from /usr/share/games/quake2/, just set -DSYSTEMDIR to the desired path. Also have a look into the Makefile. How do I disable friendly fire in coop mode? - The same way as in team deathmatch. Via the menu select "deathmatch options" and set teamplay to "by skin" or by "by model" and friendly fire to disabled. Make sure, that all players have the same model or skin! If you're using the dedicated server or are already in the game, open the console and type "dmflags 336" für skinbased teamplay and "dmflags 400" for modelbased teamplay. Can I connect to an IPv6 server? - Yes, the same way as connecting to an IPv4 server. Since the Quake II console has problems with the characters ":", "[" and "]" we suggest to submit the connection command as command line argument: ./quake2 +connect "[2001:db8::1]" If you want to connect to a server with a non-standard port use the following syntax: ./quake2 +connect "[2001:db8::1]:12345" For your server to show up in the server list you need to supply a multicast interface to both the client and the server: ./q2ded +set multicast eth0 ./quake2 +set multicast eth0 Normaly the server will listen to all IPv4 and IPv6 addresses. You can bind it to an address with: ./q2ded +set ip "[2001:db8::1]" Where can I find the configuartion file? - It's located at ~/.yq2/game/config.cfg (FreeBSD, Linux, OpenBSD and OS X) or ...\Documents\YamagiQ2\game\config.cfg (Windows). Replace "game" by the mod name, e.g. "baseq2/" for the main game. My mod crashes at startup. - This is known problem of some mods. A workaround is to create the working directory by hand: mkdir -p ~/.yq2/$moddir (FreeBSD, Linux, OpenBSD and OS X) ...\Documents\YamagiQ2\$moddir (Windows) Only parts of the maps are rendered! - By default the maximum view distance is 2300 units. You can widen it up to 4096 units by setting "gl_farsee" to "1". Why has Yamagi Quake II no support for joysticks? - Because nobody has implemented it yet and egoshooters like Quake II are not really meant to be played with joysticks, gamepads or anything like that. If you really need joystick support you can use a joystick to keyboard translator like joytran (for FreeBSD, Linux and OpenBSD): http://chiselapp.com/user/beyert/repository/joytran/index What is yq2.cfg for? - yq2.cfg is an alternate startup script, used to override some bad decisions in the original defaults.cfg. Please do not alter it, unless you know what you're doing! It may beak the game! Why is the FOV different than in id Softwares client? - id Softwares client was designed to work an 4:3 screens only. Setting the FOV kept the aspect ratio, expanded the view angle in height and width. Setting a higher FOV on wider screens was common, but the image distorted lightly. Yamagi Quake II calculates a correct FOV without distortions. You can get the old behavior if you select an aspect ratio other than "auto" in the video menu or by setting the "horplus" cvar to "0". Why doesn't gl_showtris work? - gl_showtris requires gl_ext_multitexturing set to 0. How do I disable the vsync? - Set gl_swapinterval to 0 and type vid_restart. Beware that this may not work with SDL 1.2 due to bugs in SDL. ============================================================================== yquake2-QUAKE2_5_32/LICENSE0000644000175000017500000005446212615162034015637 0ustar greffrathgreffrathYamagi Quake II contains software developed by multiple individuals, projects and organisations. Following is a list of this software and copys for each license: - Quake II: GPLv2 - Info-ZIP (unzip): Info-ZIP License - Cocoa SDL entry points Parts of other Quake II Clients were included into the source. They covered by the same GPLv2 license as Quake II itself: - Hecatomb - Icculus Quake 2 - KMQuake2 - Q2Pro - QuDoS - r1q2 - zeq2 The following code is used in library form and thus not part of Yamagi Quake II: - libjpeg - libogg - libopenal - libvorbis - libz - libsdl - libX11 - libXxf86vm =============================================================================== 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 How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. =============================================================================== This is version 2009-Jan-02 of the Info-ZIP license. The definitive version of this document should be available at ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely and a copy at http://www.info-zip.org/pub/infozip/license.html. Copyright (c) 1990-2010 Info-ZIP. All rights reserved. For the purposes of this copyright and license, "Info-ZIP" is defined as the following set of individuals: Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, Jean-loup Gailly, Hunter Goatley, Ed Gordon, Ian Gorman, Chris Herborth, Dirk Haase, Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, Kai Uwe Rommel, Steve Salisbury, Dave Smith, Steven M. Schweda, Christian Spieler, Cosmin Truta, Antoine Verheijen, Paul von Behren, Rich Wales, Mike White. This software is provided "as is," without warranty of any kind, express or implied. In no event shall Info-ZIP or its contributors be held liable for any direct, indirect, incidental, special or consequential damages arising out of the use of or inability to use this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the above disclaimer and the following restrictions: 1. Redistributions of source code (in whole or in part) must retain the above copyright notice, definition, disclaimer, and this list of conditions. 2. Redistributions in binary form (compiled executables and libraries) must reproduce the above copyright notice, definition, disclaimer, and this list of conditions in documentation and/or other materials provided with the distribution. Additional documentation is not needed for executables where a command line license option provides these and a note regarding this option is in the executable's startup banner. The sole exception to this condition is redistribution of a standard UnZipSFX binary (including SFXWiz) as part of a self-extracting archive; that is permitted without inclusion of this license, as long as the normal SFX banner has not been removed from the binary or disabled. 3. Altered versions--including, but not limited to, ports to new operating systems, existing ports with new graphical interfaces, versions with modified or added functionality, and dynamic, shared, or static library versions not from Info-ZIP--must be plainly marked as such and must not be misrepresented as being the original source or, if binaries, compiled from the original source. Such altered versions also must not be misrepresented as being Info-ZIP releases--including, but not limited to, labeling of the altered versions with the names "Info-ZIP" (or any variation thereof, including, but not limited to, different capitalizations), "Pocket UnZip," "WiZ" or "MacZip" without the explicit permission of Info-ZIP. Such altered versions are further prohibited from misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or the Info-ZIP URL(s), such as to imply Info-ZIP will provide support for the altered versions. 4. Info-ZIP retains the right to use the names "Info-ZIP," "Zip," "UnZip," "UnZipSFX," "WiZ," "Pocket UnZip," "Pocket Zip," and "MacZip" for its own source and binary releases. =============================================================================== Main entry point for our Cocoa-ized SDL app Initial Version: Darrell Walisser Non-NIB-Code & other changes: Max Horn Feel free to customize this file to suit your needs =============================================================================== yquake2-QUAKE2_5_32/src/0000755000175000017500000000000012615162034015406 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/0000755000175000017500000000000012615162034016317 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/g_turret.c0000644000175000017500000002764012615162034020327 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Turrets aka big cannons with a driver. * * ======================================================================= */ #include "header/local.h" void infantry_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage); void infantry_stand(edict_t *self); void monster_use(edict_t *self, edict_t *other, edict_t *activator); qboolean FindTarget(edict_t *self); void AnglesNormalize(vec3_t vec) { while (vec[0] > 360) { vec[0] -= 360; } while (vec[0] < 0) { vec[0] += 360; } while (vec[1] > 360) { vec[1] -= 360; } while (vec[1] < 0) { vec[1] += 360; } } float SnapToEights(float x) { x *= 8.0; if (x > 0.0) { x += 0.5; } else { x -= 0.5; } return 0.125 * (int)x; } void turret_blocked(edict_t *self, edict_t *other) { edict_t *attacker; if (!self || !other) { return; } if (other->takedamage) { if (self->teammaster->owner) { attacker = self->teammaster->owner; } else { attacker = self->teammaster; } T_Damage(other, self, attacker, vec3_origin, other->s.origin, vec3_origin, self->teammaster->dmg, 10, 0, MOD_CRUSH); } } /* * QUAKED turret_breach (0 0 0) ? * This portion of the turret can change both pitch and yaw. * The model should be made with a flat pitch. * It (and the associated base) need to be oriented towards 0. * Use "angle" to set the starting angle. * * "speed" default 50 * "dmg" default 10 * "angle" point this forward * "target" point this at an info_notnull at the muzzle tip * "minpitch" min acceptable pitch angle : default -30 * "maxpitch" max acceptable pitch angle : default 30 * "minyaw" min acceptable yaw angle : default 0 * "maxyaw" max acceptable yaw angle : default 360 */ void turret_breach_fire(edict_t *self) { vec3_t f, r, u; vec3_t start; int damage; int speed; if (!self) { return; } AngleVectors(self->s.angles, f, r, u); VectorMA(self->s.origin, self->move_origin[0], f, start); VectorMA(start, self->move_origin[1], r, start); VectorMA(start, self->move_origin[2], u, start); damage = 100 + random() * 50; speed = 550 + 50 * skill->value; fire_rocket(self->teammaster->owner, start, f, damage, speed, 150, damage); gi.positioned_sound(start, self, CHAN_WEAPON, gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0); } void turret_breach_think(edict_t *self) { edict_t *ent; vec3_t current_angles; vec3_t delta; if (!self) { return; } VectorCopy(self->s.angles, current_angles); AnglesNormalize(current_angles); AnglesNormalize(self->move_angles); if (self->move_angles[PITCH] > 180) { self->move_angles[PITCH] -= 360; } /* clamp angles to mins & maxs */ if (self->move_angles[PITCH] > self->pos1[PITCH]) { self->move_angles[PITCH] = self->pos1[PITCH]; } else if (self->move_angles[PITCH] < self->pos2[PITCH]) { self->move_angles[PITCH] = self->pos2[PITCH]; } if ((self->move_angles[YAW] < self->pos1[YAW]) || (self->move_angles[YAW] > self->pos2[YAW])) { float dmin, dmax; dmin = fabs(self->pos1[YAW] - self->move_angles[YAW]); if (dmin < -180) { dmin += 360; } else if (dmin > 180) { dmin -= 360; } dmax = fabs(self->pos2[YAW] - self->move_angles[YAW]); if (dmax < -180) { dmax += 360; } else if (dmax > 180) { dmax -= 360; } if (fabs(dmin) < fabs(dmax)) { self->move_angles[YAW] = self->pos1[YAW]; } else { self->move_angles[YAW] = self->pos2[YAW]; } } VectorSubtract(self->move_angles, current_angles, delta); if (delta[0] < -180) { delta[0] += 360; } else if (delta[0] > 180) { delta[0] -= 360; } if (delta[1] < -180) { delta[1] += 360; } else if (delta[1] > 180) { delta[1] -= 360; } delta[2] = 0; if (delta[0] > self->speed * FRAMETIME) { delta[0] = self->speed * FRAMETIME; } if (delta[0] < -1 * self->speed * FRAMETIME) { delta[0] = -1 * self->speed * FRAMETIME; } if (delta[1] > self->speed * FRAMETIME) { delta[1] = self->speed * FRAMETIME; } if (delta[1] < -1 * self->speed * FRAMETIME) { delta[1] = -1 * self->speed * FRAMETIME; } VectorScale(delta, 1.0 / FRAMETIME, self->avelocity); self->nextthink = level.time + FRAMETIME; for (ent = self->teammaster; ent; ent = ent->teamchain) { ent->avelocity[1] = self->avelocity[1]; } /* if we have adriver, adjust his velocities */ if (self->owner) { float angle; float target_z; float diff; vec3_t target; vec3_t dir; /* angular is easy, just copy ours */ self->owner->avelocity[0] = self->avelocity[0]; self->owner->avelocity[1] = self->avelocity[1]; /* x & y */ angle = self->s.angles[1] + self->owner->move_origin[1]; angle *= (M_PI * 2 / 360); target[0] = SnapToEights(self->s.origin[0] + cos( angle) * self->owner->move_origin[0]); target[1] = SnapToEights(self->s.origin[1] + sin( angle) * self->owner->move_origin[0]); target[2] = self->owner->s.origin[2]; VectorSubtract(target, self->owner->s.origin, dir); self->owner->velocity[0] = dir[0] * 1.0 / FRAMETIME; self->owner->velocity[1] = dir[1] * 1.0 / FRAMETIME; /* z */ angle = self->s.angles[PITCH] * (M_PI * 2 / 360); target_z = SnapToEights( self->s.origin[2] + self->owner->move_origin[0] * tan( angle) + self->owner->move_origin[2]); diff = target_z - self->owner->s.origin[2]; self->owner->velocity[2] = diff * 1.0 / FRAMETIME; if (self->spawnflags & 65536) { turret_breach_fire(self); self->spawnflags &= ~65536; } } } void turret_breach_finish_init(edict_t *self) { if (!self) { return; } /* get and save info for muzzle location */ if (!self->target) { gi.dprintf("%s at %s needs a target\n", self->classname, vtos(self->s.origin)); } else { self->target_ent = G_PickTarget(self->target); VectorSubtract(self->target_ent->s.origin, self->s.origin, self->move_origin); G_FreeEdict(self->target_ent); } self->teammaster->dmg = self->dmg; self->think = turret_breach_think; self->think(self); } void SP_turret_breach(edict_t *self) { if (!self) { return; } self->solid = SOLID_BSP; self->movetype = MOVETYPE_PUSH; gi.setmodel(self, self->model); if (!self->speed) { self->speed = 50; } if (!self->dmg) { self->dmg = 10; } if (!st.minpitch) { st.minpitch = -30; } if (!st.maxpitch) { st.maxpitch = 30; } if (!st.maxyaw) { st.maxyaw = 360; } self->pos1[PITCH] = -1 * st.minpitch; self->pos1[YAW] = st.minyaw; self->pos2[PITCH] = -1 * st.maxpitch; self->pos2[YAW] = st.maxyaw; self->ideal_yaw = self->s.angles[YAW]; self->move_angles[YAW] = self->ideal_yaw; self->blocked = turret_blocked; self->think = turret_breach_finish_init; self->nextthink = level.time + FRAMETIME; gi.linkentity(self); } /* * QUAKED turret_base (0 0 0) ? * This portion of the turret changes yaw only. * MUST be teamed with a turret_breach. */ void SP_turret_base(edict_t *self) { if (!self) { return; } self->solid = SOLID_BSP; self->movetype = MOVETYPE_PUSH; gi.setmodel(self, self->model); self->blocked = turret_blocked; gi.linkentity(self); } /* * QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32) * Must NOT be on the team with the rest of the turret parts. * Instead it must target the turret_breach. */ void turret_driver_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point /* unused */) { edict_t *ent; if (!self || !inflictor || !attacker) { return; } /* level the gun */ self->target_ent->move_angles[0] = 0; /* remove the driver from the end of them team chain */ for (ent = self->target_ent->teammaster; ent->teamchain != self; ent = ent->teamchain) { } ent->teamchain = NULL; self->teammaster = NULL; self->flags &= ~FL_TEAMSLAVE; self->target_ent->owner = NULL; self->target_ent->teammaster->owner = NULL; infantry_die(self, inflictor, attacker, damage); } void turret_driver_think(edict_t *self) { vec3_t target; vec3_t dir; float reaction_time; if (!self) { return; } self->nextthink = level.time + FRAMETIME; if (self->enemy && (!self->enemy->inuse || (self->enemy->health <= 0))) { self->enemy = NULL; } if (!self->enemy) { if (!FindTarget(self)) { return; } self->monsterinfo.trail_time = level.time; self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; } else { if (visible(self, self->enemy)) { if (self->monsterinfo.aiflags & AI_LOST_SIGHT) { self->monsterinfo.trail_time = level.time; self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; } } else { self->monsterinfo.aiflags |= AI_LOST_SIGHT; return; } } /* let the turret know where we want it to aim */ VectorCopy(self->enemy->s.origin, target); target[2] += self->enemy->viewheight; VectorSubtract(target, self->target_ent->s.origin, dir); vectoangles(dir, self->target_ent->move_angles); /* decide if we should shoot */ if (level.time < self->monsterinfo.attack_finished) { return; } reaction_time = (3 - skill->value) * 1.0; if ((level.time - self->monsterinfo.trail_time) < reaction_time) { return; } self->monsterinfo.attack_finished = level.time + reaction_time + 1.0; self->target_ent->spawnflags |= 65536; } void turret_driver_link(edict_t *self) { vec3_t vec; edict_t *ent; if (!self) { return; } self->think = turret_driver_think; self->nextthink = level.time + FRAMETIME; self->target_ent = G_PickTarget(self->target); self->target_ent->owner = self; self->target_ent->teammaster->owner = self; VectorCopy(self->target_ent->s.angles, self->s.angles); vec[0] = self->target_ent->s.origin[0] - self->s.origin[0]; vec[1] = self->target_ent->s.origin[1] - self->s.origin[1]; vec[2] = 0; self->move_origin[0] = VectorLength(vec); VectorSubtract(self->s.origin, self->target_ent->s.origin, vec); vectoangles(vec, vec); AnglesNormalize(vec); self->move_origin[1] = vec[1]; self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2]; /* add the driver to the end of them team chain */ for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain) { } ent->teamchain = self; self->teammaster = self->target_ent->teammaster; self->flags |= FL_TEAMSLAVE; } void SP_turret_driver(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } self->movetype = MOVETYPE_PUSH; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2"); VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, 32); self->health = 100; self->gib_health = 0; self->mass = 200; self->viewheight = 24; self->die = turret_driver_die; self->monsterinfo.stand = infantry_stand; self->flags |= FL_NO_KNOCKBACK; level.total_monsters++; self->svflags |= SVF_MONSTER; self->s.renderfx |= RF_FRAMELERP; self->takedamage = DAMAGE_AIM; self->use = monster_use; self->clipmask = MASK_MONSTERSOLID; VectorCopy(self->s.origin, self->s.old_origin); self->monsterinfo.aiflags |= AI_STAND_GROUND | AI_DUCKED; if (st.item) { self->item = FindItemByClassname(st.item); if (!self->item) { gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item); } } self->think = turret_driver_link; self->nextthink = level.time + FRAMETIME; gi.linkentity(self); } yquake2-QUAKE2_5_32/src/game/g_target.c0000644000175000017500000005525312615162034020271 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Targets. * * ======================================================================= */ #include "header/local.h" /* * QUAKED target_temp_entity (1 0 0) (-8 -8 -8) (8 8 8) * Fire an origin based temp entity event to the clients. * * "style" type byte */ void Use_Target_Tent(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!ent) { return; } gi.WriteByte(svc_temp_entity); gi.WriteByte(ent->style); gi.WritePosition(ent->s.origin); gi.multicast(ent->s.origin, MULTICAST_PVS); } void SP_target_temp_entity(edict_t *ent) { if (!ent) { return; } ent->use = Use_Target_Tent; } /* ========================================================== */ /* * QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off reliable * * "noise" wav file to play * * "attenuation" * -1 = none, send to whole level * 1 = normal fighting sounds * 2 = idle sound level * 3 = ambient sound level * * "volume" 0.0 to 1.0 * * Normal sounds play each time the target is used. * The reliable flag can be set for crucial voiceovers. * * Looped sounds are always atten 3 / vol 1, and the use function toggles it on/off. * Multiple identical looping sounds will just increase volume without any speed cost. */ void Use_Target_Speaker(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* unused */) { int chan; if (!ent) { return; } if (ent->spawnflags & 3) { /* looping sound toggles */ if (ent->s.sound) { ent->s.sound = 0; /* turn it off */ } else { ent->s.sound = ent->noise_index; /* start it */ } } else { /* normal sound */ if (ent->spawnflags & 4) { chan = CHAN_VOICE | CHAN_RELIABLE; } else { chan = CHAN_VOICE; } /* use a positioned_sound, because this entity won't normally be sent to any clients because it is invisible */ gi.positioned_sound(ent->s.origin, ent, chan, ent->noise_index, ent->volume, ent->attenuation, 0); } } void SP_target_speaker(edict_t *ent) { char buffer[MAX_QPATH]; if (!ent) { return; } if (!st.noise) { gi.dprintf("target_speaker with no noise set at %s\n", vtos(ent->s.origin)); return; } if (!strstr(st.noise, ".wav")) { Com_sprintf(buffer, sizeof(buffer), "%s.wav", st.noise); } else { Q_strlcpy(buffer, st.noise, sizeof(buffer)); } ent->noise_index = gi.soundindex(buffer); if (!ent->volume) { ent->volume = 1.0; } if (!ent->attenuation) { ent->attenuation = 1.0; } else if (ent->attenuation == -1) /* use -1 so 0 defaults to 1 */ { ent->attenuation = 0; } /* check for prestarted looping sound */ if (ent->spawnflags & 1) { ent->s.sound = ent->noise_index; } ent->use = Use_Target_Speaker; /* must link the entity so we get areas and clusters so the server can determine who to send updates to */ gi.linkentity(ent); } /* ========================================================== */ void Use_Target_Help(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!ent) { return; } if (ent->spawnflags & 1) { Q_strlcpy(game.helpmessage1, ent->message, sizeof(game.helpmessage1)); } else { Q_strlcpy(game.helpmessage2, ent->message, sizeof(game.helpmessage2)); } game.helpchanged++; } /* * QUAKED target_help (1 0 1) (-16 -16 -24) (16 16 24) help1 * When fired, the "message" key becomes the current personal computer string, * and the message light will be set on all clients status bars. */ void SP_target_help(edict_t *ent) { if (!ent) { return; } if (deathmatch->value) { /* auto-remove for deathmatch */ G_FreeEdict(ent); return; } if (!ent->message) { gi.dprintf("%s with no message at %s\n", ent->classname, vtos(ent->s.origin)); G_FreeEdict(ent); return; } ent->use = Use_Target_Help; } /* ========================================================== */ /* * QUAKED target_secret (1 0 1) (-8 -8 -8) (8 8 8) * Counts a secret found. These are single use targets. */ void use_target_secret(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* acticator */) { if (!ent) { return; } gi.sound(ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0); level.found_secrets++; G_UseTargets(ent, activator); G_FreeEdict(ent); } void SP_target_secret(edict_t *ent) { if (!ent) { return; } if (deathmatch->value) { /* auto-remove for deathmatch */ G_FreeEdict(ent); return; } ent->use = use_target_secret; if (!st.noise) { st.noise = "misc/secret.wav"; } ent->noise_index = gi.soundindex(st.noise); ent->svflags = SVF_NOCLIENT; level.total_secrets++; /* Map quirk for mine3 */ if (!Q_stricmp(level.mapname, "mine3") && (ent->s.origin[0] == 280) && (ent->s.origin[1] == -2048) && (ent->s.origin[2] == -624)) { ent->message = "You have found a secret area."; } } /* ========================================================== */ /* * QUAKED target_goal (1 0 1) (-8 -8 -8) (8 8 8) * Counts a goal completed. These are single use targets. */ void use_target_goal(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!ent) { return; } gi.sound(ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0); level.found_goals++; if (level.found_goals == level.total_goals) { gi.configstring(CS_CDTRACK, "0"); } G_UseTargets(ent, activator); G_FreeEdict(ent); } void SP_target_goal(edict_t *ent) { if (!ent) { return; } if (deathmatch->value) { /* auto-remove for deathmatch */ G_FreeEdict(ent); return; } ent->use = use_target_goal; if (!st.noise) { st.noise = "misc/secret.wav"; } ent->noise_index = gi.soundindex(st.noise); ent->svflags = SVF_NOCLIENT; level.total_goals++; } /* ========================================================== */ /* * QUAKED target_explosion (1 0 0) (-8 -8 -8) (8 8 8) * Spawns an explosion temporary entity when used. * * "delay" wait this long before going off * "dmg" how much radius damage should be done, defaults to 0 */ void target_explosion_explode(edict_t *self) { float save; if (!self) { return; } gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_EXPLOSION1); gi.WritePosition(self->s.origin); gi.multicast(self->s.origin, MULTICAST_PHS); T_RadiusDamage(self, self->activator, self->dmg, NULL, self->dmg + 40, MOD_EXPLOSIVE); save = self->delay; self->delay = 0; G_UseTargets(self, self->activator); self->delay = save; } void use_target_explosion(edict_t *self, edict_t *other /* unused */, edict_t *activator) { self->activator = activator; if (!self || !activator) { return; } if (!self->delay) { target_explosion_explode(self); return; } self->think = target_explosion_explode; self->nextthink = level.time + self->delay; } void SP_target_explosion(edict_t *ent) { if (!ent) { return; } ent->use = use_target_explosion; ent->svflags = SVF_NOCLIENT; } /* ========================================================== */ /* * QUAKED target_changelevel (1 0 0) (-8 -8 -8) (8 8 8) * Changes level to "map" when fired */ void use_target_changelevel(edict_t *self, edict_t *other, edict_t *activator) { if (!self || !other || !activator) { return; } if (level.intermissiontime) { return; /* already activated */ } if (!deathmatch->value && !coop->value) { if (g_edicts[1].health <= 0) { return; } } /* if noexit, do a ton of damage to other */ if (deathmatch->value && !((int)dmflags->value & DF_ALLOW_EXIT) && (other != world)) { T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 10 * other->max_health, 1000, 0, MOD_EXIT); return; } /* if multiplayer, let everyone know who hit the exit */ if (deathmatch->value) { if (activator && activator->client) { gi.bprintf(PRINT_HIGH, "%s exited the level.\n", activator->client->pers.netname); } } /* if going to a new unit, clear cross triggers */ if (strstr(self->map, "*")) { game.serverflags &= ~(SFL_CROSS_TRIGGER_MASK); } BeginIntermission(self); } void SP_target_changelevel(edict_t *ent) { if (!ent) { return; } if (!ent->map) { gi.dprintf("target_changelevel with no map at %s\n", vtos(ent->s.origin)); G_FreeEdict(ent); return; } /* Mapquirk for secret exists in fact1 and fact3 */ if ((Q_stricmp(level.mapname, "fact1") == 0) && (Q_stricmp(ent->map, "fact3") == 0)) { ent->map = "fact3$secret1"; } ent->use = use_target_changelevel; ent->svflags = SVF_NOCLIENT; } /* ========================================================== */ /* * QUAKED target_splash (1 0 0) (-8 -8 -8) (8 8 8) * Creates a particle splash effect when used. * * Set "sounds" to one of the following: * 1) sparks * 2) blue water * 3) brown water * 4) slime * 5) lava * 6) blood * * "count" how many pixels in the splash * "dmg" if set, does a radius damage at this location when it splashes * useful for lava/sparks */ void use_target_splash(edict_t *self, edict_t *other /* unused */, edict_t *activator) { if (!self || !activator) { return; } gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_SPLASH); gi.WriteByte(self->count); gi.WritePosition(self->s.origin); gi.WriteDir(self->movedir); gi.WriteByte(self->sounds); gi.multicast(self->s.origin, MULTICAST_PVS); if (self->dmg) { T_RadiusDamage(self, activator, self->dmg, NULL, self->dmg + 40, MOD_SPLASH); } } void SP_target_splash(edict_t *self) { if (!self) { return; } self->use = use_target_splash; G_SetMovedir(self->s.angles, self->movedir); if (!self->count) { self->count = 32; } self->svflags = SVF_NOCLIENT; } /* ========================================================== */ /* * QUAKED target_spawner (1 0 0) (-8 -8 -8) (8 8 8) * Set target to the type of entity you want spawned. * Useful for spawning monsters and gibs in the factory levels. * * For monsters: * Set direction to the facing you want it to have. * * For gibs: * Set direction if you want it moving and * speed how fast it should be moving otherwise it * will just be dropped */ void ED_CallSpawn(edict_t *ent); void use_target_spawner(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { edict_t *ent; if (!self) { return; } ent = G_Spawn(); ent->classname = self->target; VectorCopy(self->s.origin, ent->s.origin); VectorCopy(self->s.angles, ent->s.angles); ED_CallSpawn(ent); gi.unlinkentity(ent); KillBox(ent); gi.linkentity(ent); if (self->speed) { VectorCopy(self->movedir, ent->velocity); } } void SP_target_spawner(edict_t *self) { vec3_t forward; vec3_t fact2spawnpoint1 = {-1504,512,72}; if (!self) { return; } self->use = use_target_spawner; self->svflags = SVF_NOCLIENT; /* Maphack for the insane spawner in Mobs-Egerlings beloved fact2. Found in KMQuake2 */ if (!Q_stricmp(level.mapname, "fact2") && VectorCompare(self->s.origin, fact2spawnpoint1) ) { VectorSet (forward, 0, 0, 1); VectorMA (self->s.origin, -8, forward, self->s.origin); } if (self->speed) { G_SetMovedir(self->s.angles, self->movedir); VectorScale(self->movedir, self->speed, self->movedir); } } /* ========================================================== */ /* * QUAKED target_blaster (1 0 0) (-8 -8 -8) (8 8 8) NOTRAIL NOEFFECTS * Fires a blaster bolt in the set direction when triggered. * * dmg default is 15 * speed default is 1000 */ void use_target_blaster(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!self) { return; } fire_blaster(self, self->s.origin, self->movedir, self->dmg, self->speed, EF_BLASTER, MOD_TARGET_BLASTER); gi.sound(self, CHAN_VOICE, self->noise_index, 1, ATTN_NORM, 0); } void SP_target_blaster(edict_t *self) { if (!self) { return; } self->use = use_target_blaster; G_SetMovedir(self->s.angles, self->movedir); self->noise_index = gi.soundindex("weapons/laser2.wav"); if (!self->dmg) { self->dmg = 15; } if (!self->speed) { self->speed = 1000; } self->svflags = SVF_NOCLIENT; } /* ========================================================== */ /* * QUAKED target_crosslevel_trigger (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8 * Once this trigger is touched/used, any trigger_crosslevel_target with * the same trigger number is automatically used when a level is started * within the same unit. It is OK to check multiple triggers. Message, * delay, target, and killtarget also work. */ void trigger_crosslevel_trigger_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) { if (!self || !activator) { return; } game.serverflags |= self->spawnflags; G_UseTargets (self, activator); G_FreeEdict(self); } void SP_target_crosslevel_trigger(edict_t *self) { if (!self) { return; } self->svflags = SVF_NOCLIENT; self->use = trigger_crosslevel_trigger_use; } /* * QUAKED target_crosslevel_target (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8 * Triggered by a trigger_crosslevel elsewhere within a unit. * If multiple triggers are checked, all must be true. Delay, * target and killtarget also work. * * "delay" delay before using targets if the trigger has been * activated (default 1) */ void target_crosslevel_target_think(edict_t *self) { if (!self) { return; } if (self->spawnflags == (game.serverflags & SFL_CROSS_TRIGGER_MASK & self->spawnflags)) { G_UseTargets(self, self); G_FreeEdict(self); } } void SP_target_crosslevel_target(edict_t *self) { if (!self) { return; } if (!self->delay) { self->delay = 1; } self->svflags = SVF_NOCLIENT; self->think = target_crosslevel_target_think; self->nextthink = level.time + self->delay; } /* ========================================================== */ /* * QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE YELLOW ORANGE FAT * When triggered, fires a laser. You can either set a target * or a direction. */ void target_laser_think(edict_t *self) { edict_t *ignore; vec3_t start; vec3_t end; trace_t tr; vec3_t point; vec3_t last_movedir; int count; if (!self) { return; } if (self->spawnflags & 0x80000000) { count = 8; } else { count = 4; } if (self->enemy) { VectorCopy(self->movedir, last_movedir); VectorMA(self->enemy->absmin, 0.5, self->enemy->size, point); VectorSubtract(point, self->s.origin, self->movedir); VectorNormalize(self->movedir); if (!VectorCompare(self->movedir, last_movedir)) { self->spawnflags |= 0x80000000; } } ignore = self; VectorCopy(self->s.origin, start); VectorMA(start, 2048, self->movedir, end); while (1) { tr = gi.trace(start, NULL, NULL, end, ignore, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_DEADMONSTER); if (!tr.ent) { break; } /* hurt it if we can */ if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER)) { T_Damage(tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER); } /* if we hit something that's not a monster or player or is immune to lasers, we're done */ if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) { if (self->spawnflags & 0x80000000) { self->spawnflags &= ~0x80000000; gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_LASER_SPARKS); gi.WriteByte(count); gi.WritePosition(tr.endpos); gi.WriteDir(tr.plane.normal); gi.WriteByte(self->s.skinnum); gi.multicast(tr.endpos, MULTICAST_PVS); } break; } ignore = tr.ent; VectorCopy(tr.endpos, start); } VectorCopy(tr.endpos, self->s.old_origin); self->nextthink = level.time + FRAMETIME; } void target_laser_on(edict_t *self) { if (!self) { return; } if (!self->activator) { self->activator = self; } self->spawnflags |= 0x80000001; self->svflags &= ~SVF_NOCLIENT; target_laser_think(self); } void target_laser_off(edict_t *self) { if (!self) { return; } self->spawnflags &= ~1; self->svflags |= SVF_NOCLIENT; self->nextthink = 0; } void target_laser_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) { if (!self || !activator) { return; } self->activator = activator; if (self->spawnflags & 1) { target_laser_off(self); } else { target_laser_on(self); } } void target_laser_start(edict_t *self) { edict_t *ent; if (!self) { return; } self->movetype = MOVETYPE_NONE; self->solid = SOLID_NOT; self->s.renderfx |= RF_BEAM | RF_TRANSLUCENT; self->s.modelindex = 1; /* must be non-zero */ /* set the beam diameter */ if (self->spawnflags & 64) { self->s.frame = 16; } else { self->s.frame = 4; } /* set the color */ if (self->spawnflags & 2) { self->s.skinnum = 0xf2f2f0f0; } else if (self->spawnflags & 4) { self->s.skinnum = 0xd0d1d2d3; } else if (self->spawnflags & 8) { self->s.skinnum = 0xf3f3f1f1; } else if (self->spawnflags & 16) { self->s.skinnum = 0xdcdddedf; } else if (self->spawnflags & 32) { self->s.skinnum = 0xe0e1e2e3; } if (!self->enemy) { if (self->target) { ent = G_Find(NULL, FOFS(targetname), self->target); if (!ent) { gi.dprintf("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target); } self->enemy = ent; } else { G_SetMovedir(self->s.angles, self->movedir); } } self->use = target_laser_use; self->think = target_laser_think; if (!self->dmg) { self->dmg = 1; } VectorSet(self->mins, -8, -8, -8); VectorSet(self->maxs, 8, 8, 8); gi.linkentity(self); if (self->spawnflags & 1) { target_laser_on(self); } else { target_laser_off(self); } } void SP_target_laser(edict_t *self) { if (!self) { return; } /* let everything else get spawned before we start firing */ self->think = target_laser_start; self->nextthink = level.time + 1; } /* ========================================================== */ /* * QUAKED target_lightramp (0 .5 .8) (-8 -8 -8) (8 8 8) TOGGLE * speed How many seconds the ramping will take * message two letters; starting lightlevel and ending lightlevel */ void target_lightramp_think(edict_t *self) { char style[2]; if (!self) { return; } style[0] = 'a' + self->movedir[0] + (level.time - self->timestamp) / FRAMETIME * self->movedir[2]; style[1] = 0; gi.configstring(CS_LIGHTS + self->enemy->style, style); if ((level.time - self->timestamp) < self->speed) { self->nextthink = level.time + FRAMETIME; } else if (self->spawnflags & 1) { char temp; temp = self->movedir[0]; self->movedir[0] = self->movedir[1]; self->movedir[1] = temp; self->movedir[2] *= -1; } } void target_lightramp_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!self) { return; } if (!self->enemy) { edict_t *e; /* check all the targets */ e = NULL; while (1) { e = G_Find(e, FOFS(targetname), self->target); if (!e) { break; } if (strcmp(e->classname, "light") != 0) { gi.dprintf("%s at %s ", self->classname, vtos(self->s.origin)); gi.dprintf("target %s (%s at %s) is not a light\n", self->target, e->classname, vtos(e->s.origin)); } else { self->enemy = e; } } if (!self->enemy) { gi.dprintf("%s target %s not found at %s\n", self->classname, self->target, vtos(self->s.origin)); G_FreeEdict(self); return; } } self->timestamp = level.time; target_lightramp_think(self); } void SP_target_lightramp(edict_t *self) { if (!self) { return; } if (!self->message || (strlen(self->message) != 2) || (self->message[0] < 'a') || (self->message[0] > 'z') || (self->message[1] < 'a') || (self->message[1] > 'z') || (self->message[0] == self->message[1])) { gi.dprintf("target_lightramp has bad ramp (%s) at %s\n", self->message, vtos(self->s.origin)); G_FreeEdict(self); return; } if (deathmatch->value) { G_FreeEdict(self); return; } if (!self->target) { gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin)); G_FreeEdict(self); return; } self->svflags |= SVF_NOCLIENT; self->use = target_lightramp_use; self->think = target_lightramp_think; self->movedir[0] = self->message[0] - 'a'; self->movedir[1] = self->message[1] - 'a'; self->movedir[2] = (self->movedir[1] - self->movedir[0]) / (self->speed / FRAMETIME); } /* ========================================================== */ /* * QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8) * When triggered, this initiates a level-wide earthquake. * All players and monsters are affected. * "speed" severity of the quake (default:200) * "count" duration of the quake (default:5) */ void target_earthquake_think(edict_t *self) { int i; edict_t *e; if (!self) { return; } if (self->last_move_time < level.time) { gi.positioned_sound(self->s.origin, self, CHAN_AUTO, self->noise_index, 1.0, ATTN_NONE, 0); self->last_move_time = level.time + 0.5; } for (i = 1, e = g_edicts + i; i < globals.num_edicts; i++, e++) { if (!e->inuse) { continue; } if (!e->client) { continue; } if (!e->groundentity) { continue; } e->groundentity = NULL; e->velocity[0] += crandom() * 150; e->velocity[1] += crandom() * 150; e->velocity[2] = self->speed * (100.0 / e->mass); } if (level.time < self->timestamp) { self->nextthink = level.time + FRAMETIME; } } void target_earthquake_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) { if (!self || !activator) { return; } self->timestamp = level.time + self->count; self->nextthink = level.time + FRAMETIME; self->activator = activator; self->last_move_time = 0; } void SP_target_earthquake(edict_t *self) { if (!self) { return; } if (!self->targetname) { gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin)); } if (!self->count) { self->count = 5; } if (!self->speed) { self->speed = 200; } self->svflags |= SVF_NOCLIENT; self->think = target_earthquake_think; self->use = target_earthquake_use; self->noise_index = gi.soundindex("world/quake.wav"); } yquake2-QUAKE2_5_32/src/game/g_chase.c0000644000175000017500000001076512615162034020065 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 cam. Only used in multiplayer mode. * * ======================================================================= */ #include "header/local.h" void UpdateChaseCam(edict_t *ent) { vec3_t o, ownerv, goal; edict_t *targ; vec3_t forward, right; trace_t trace; int i; vec3_t angles; if (!ent) { return; } /* is our chase target gone? */ if (!ent->client->chase_target->inuse || ent->client->chase_target->client->resp.spectator) { edict_t *old = ent->client->chase_target; ChaseNext(ent); if (ent->client->chase_target == old) { ent->client->chase_target = NULL; ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; return; } } targ = ent->client->chase_target; VectorCopy(targ->s.origin, ownerv); ownerv[2] += targ->viewheight; VectorCopy(targ->client->v_angle, angles); if (angles[PITCH] > 56) { angles[PITCH] = 56; } AngleVectors(angles, forward, right, NULL); VectorNormalize(forward); VectorMA(ownerv, -30, forward, o); if (o[2] < targ->s.origin[2] + 20) { o[2] = targ->s.origin[2] + 20; } /* jump animation lifts */ if (!targ->groundentity) { o[2] += 16; } trace = gi.trace(ownerv, vec3_origin, vec3_origin, o, targ, MASK_SOLID); VectorCopy(trace.endpos, goal); VectorMA(goal, 2, forward, goal); /* pad for floors and ceilings */ VectorCopy(goal, o); o[2] += 6; trace = gi.trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID); if (trace.fraction < 1) { VectorCopy(trace.endpos, goal); goal[2] -= 6; } VectorCopy(goal, o); o[2] -= 6; trace = gi.trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID); if (trace.fraction < 1) { VectorCopy(trace.endpos, goal); goal[2] += 6; } if (targ->deadflag) { ent->client->ps.pmove.pm_type = PM_DEAD; } else { ent->client->ps.pmove.pm_type = PM_FREEZE; } VectorCopy(goal, ent->s.origin); for (i = 0; i < 3; i++) { ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT( targ->client->v_angle[i] - ent->client->resp.cmd_angles[i]); } if (targ->deadflag) { ent->client->ps.viewangles[ROLL] = 40; ent->client->ps.viewangles[PITCH] = -15; ent->client->ps.viewangles[YAW] = targ->client->killer_yaw; } else { VectorCopy(targ->client->v_angle, ent->client->ps.viewangles); VectorCopy(targ->client->v_angle, ent->client->v_angle); } ent->viewheight = 0; ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; gi.linkentity(ent); } void ChaseNext(edict_t *ent) { int i; edict_t *e; if (!ent) { return; } if (!ent->client->chase_target) { return; } i = ent->client->chase_target - g_edicts; do { i++; if (i > maxclients->value) { i = 1; } e = g_edicts + i; if (!e->inuse) { continue; } if (!e->client->resp.spectator) { break; } } while (e != ent->client->chase_target); ent->client->chase_target = e; ent->client->update_chase = true; } void ChasePrev(edict_t *ent) { int i; edict_t *e; if (!ent) { return; } if (!ent->client->chase_target) { return; } i = ent->client->chase_target - g_edicts; do { i--; if (i < 1) { i = maxclients->value; } e = g_edicts + i; if (!e->inuse) { continue; } if (!e->client->resp.spectator) { break; } } while (e != ent->client->chase_target); ent->client->chase_target = e; ent->client->update_chase = true; } void GetChaseTarget(edict_t *ent) { int i; edict_t *other; if (!ent) { return; } for (i = 1; i <= maxclients->value; i++) { other = g_edicts + i; if (other->inuse && !other->client->resp.spectator) { ent->client->chase_target = other; ent->client->update_chase = true; UpdateChaseCam(ent); return; } } gi.centerprintf(ent, "No other players to chase."); } yquake2-QUAKE2_5_32/src/game/monster/0000755000175000017500000000000012615162034020006 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/flipper/0000755000175000017500000000000012615162034021447 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/flipper/flipper.h0000644000175000017500000001212412615162034023261 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Baracuda Shark animations. * * ======================================================================= */ #define FRAME_flpbit01 0 #define FRAME_flpbit02 1 #define FRAME_flpbit03 2 #define FRAME_flpbit04 3 #define FRAME_flpbit05 4 #define FRAME_flpbit06 5 #define FRAME_flpbit07 6 #define FRAME_flpbit08 7 #define FRAME_flpbit09 8 #define FRAME_flpbit10 9 #define FRAME_flpbit11 10 #define FRAME_flpbit12 11 #define FRAME_flpbit13 12 #define FRAME_flpbit14 13 #define FRAME_flpbit15 14 #define FRAME_flpbit16 15 #define FRAME_flpbit17 16 #define FRAME_flpbit18 17 #define FRAME_flpbit19 18 #define FRAME_flpbit20 19 #define FRAME_flptal01 20 #define FRAME_flptal02 21 #define FRAME_flptal03 22 #define FRAME_flptal04 23 #define FRAME_flptal05 24 #define FRAME_flptal06 25 #define FRAME_flptal07 26 #define FRAME_flptal08 27 #define FRAME_flptal09 28 #define FRAME_flptal10 29 #define FRAME_flptal11 30 #define FRAME_flptal12 31 #define FRAME_flptal13 32 #define FRAME_flptal14 33 #define FRAME_flptal15 34 #define FRAME_flptal16 35 #define FRAME_flptal17 36 #define FRAME_flptal18 37 #define FRAME_flptal19 38 #define FRAME_flptal20 39 #define FRAME_flptal21 40 #define FRAME_flphor01 41 #define FRAME_flphor02 42 #define FRAME_flphor03 43 #define FRAME_flphor04 44 #define FRAME_flphor05 45 #define FRAME_flphor06 46 #define FRAME_flphor07 47 #define FRAME_flphor08 48 #define FRAME_flphor09 49 #define FRAME_flphor10 50 #define FRAME_flphor11 51 #define FRAME_flphor12 52 #define FRAME_flphor13 53 #define FRAME_flphor14 54 #define FRAME_flphor15 55 #define FRAME_flphor16 56 #define FRAME_flphor17 57 #define FRAME_flphor18 58 #define FRAME_flphor19 59 #define FRAME_flphor20 60 #define FRAME_flphor21 61 #define FRAME_flphor22 62 #define FRAME_flphor23 63 #define FRAME_flphor24 64 #define FRAME_flpver01 65 #define FRAME_flpver02 66 #define FRAME_flpver03 67 #define FRAME_flpver04 68 #define FRAME_flpver05 69 #define FRAME_flpver06 70 #define FRAME_flpver07 71 #define FRAME_flpver08 72 #define FRAME_flpver09 73 #define FRAME_flpver10 74 #define FRAME_flpver11 75 #define FRAME_flpver12 76 #define FRAME_flpver13 77 #define FRAME_flpver14 78 #define FRAME_flpver15 79 #define FRAME_flpver16 80 #define FRAME_flpver17 81 #define FRAME_flpver18 82 #define FRAME_flpver19 83 #define FRAME_flpver20 84 #define FRAME_flpver21 85 #define FRAME_flpver22 86 #define FRAME_flpver23 87 #define FRAME_flpver24 88 #define FRAME_flpver25 89 #define FRAME_flpver26 90 #define FRAME_flpver27 91 #define FRAME_flpver28 92 #define FRAME_flpver29 93 #define FRAME_flppn101 94 #define FRAME_flppn102 95 #define FRAME_flppn103 96 #define FRAME_flppn104 97 #define FRAME_flppn105 98 #define FRAME_flppn201 99 #define FRAME_flppn202 100 #define FRAME_flppn203 101 #define FRAME_flppn204 102 #define FRAME_flppn205 103 #define FRAME_flpdth01 104 #define FRAME_flpdth02 105 #define FRAME_flpdth03 106 #define FRAME_flpdth04 107 #define FRAME_flpdth05 108 #define FRAME_flpdth06 109 #define FRAME_flpdth07 110 #define FRAME_flpdth08 111 #define FRAME_flpdth09 112 #define FRAME_flpdth10 113 #define FRAME_flpdth11 114 #define FRAME_flpdth12 115 #define FRAME_flpdth13 116 #define FRAME_flpdth14 117 #define FRAME_flpdth15 118 #define FRAME_flpdth16 119 #define FRAME_flpdth17 120 #define FRAME_flpdth18 121 #define FRAME_flpdth19 122 #define FRAME_flpdth20 123 #define FRAME_flpdth21 124 #define FRAME_flpdth22 125 #define FRAME_flpdth23 126 #define FRAME_flpdth24 127 #define FRAME_flpdth25 128 #define FRAME_flpdth26 129 #define FRAME_flpdth27 130 #define FRAME_flpdth28 131 #define FRAME_flpdth29 132 #define FRAME_flpdth30 133 #define FRAME_flpdth31 134 #define FRAME_flpdth32 135 #define FRAME_flpdth33 136 #define FRAME_flpdth34 137 #define FRAME_flpdth35 138 #define FRAME_flpdth36 139 #define FRAME_flpdth37 140 #define FRAME_flpdth38 141 #define FRAME_flpdth39 142 #define FRAME_flpdth40 143 #define FRAME_flpdth41 144 #define FRAME_flpdth42 145 #define FRAME_flpdth43 146 #define FRAME_flpdth44 147 #define FRAME_flpdth45 148 #define FRAME_flpdth46 149 #define FRAME_flpdth47 150 #define FRAME_flpdth48 151 #define FRAME_flpdth49 152 #define FRAME_flpdth50 153 #define FRAME_flpdth51 154 #define FRAME_flpdth52 155 #define FRAME_flpdth53 156 #define FRAME_flpdth54 157 #define FRAME_flpdth55 158 #define FRAME_flpdth56 159 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/flipper/flipper.c0000644000175000017500000002450312615162034023260 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Baracuda Shark. * * ======================================================================= */ #include "../../header/local.h" #include "flipper.h" #define FLIPPER_RUN_SPEED 24 static int sound_chomp; static int sound_attack; static int sound_pain1; static int sound_pain2; static int sound_death; static int sound_idle; static int sound_search; static int sound_sight; void flipper_stand(edict_t *self); mframe_t flipper_frames_stand[] = { {ai_stand, 0, NULL} }; mmove_t flipper_move_stand = { FRAME_flphor01, FRAME_flphor01, flipper_frames_stand, NULL }; void flipper_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &flipper_move_stand; } mframe_t flipper_frames_run[] = { {ai_run, FLIPPER_RUN_SPEED, NULL}, /* 6 */ {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, /* 10 */ {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, /* 20 */ {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL}, {ai_run, FLIPPER_RUN_SPEED, NULL} /* 29 */ }; mmove_t flipper_move_run_loop = { FRAME_flpver06, FRAME_flpver29, flipper_frames_run, NULL }; void flipper_run_loop(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &flipper_move_run_loop; } mframe_t flipper_frames_run_start[] = { {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL} }; mmove_t flipper_move_run_start = { FRAME_flpver01, FRAME_flpver06, flipper_frames_run_start, flipper_run_loop }; void flipper_run(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &flipper_move_run_start; } /* Standard Swimming */ mframe_t flipper_frames_walk[] = { {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL} }; mmove_t flipper_move_walk = { FRAME_flphor01, FRAME_flphor24, flipper_frames_walk, NULL }; void flipper_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &flipper_move_walk; } mframe_t flipper_frames_start_run[] = { {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, flipper_run} }; mmove_t flipper_move_start_run = { FRAME_flphor01, FRAME_flphor05, flipper_frames_start_run, NULL }; void flipper_start_run(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &flipper_move_start_run; } mframe_t flipper_frames_pain2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t flipper_move_pain2 = { FRAME_flppn101, FRAME_flppn105, flipper_frames_pain2, flipper_run }; mframe_t flipper_frames_pain1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t flipper_move_pain1 = { FRAME_flppn201, FRAME_flppn205, flipper_frames_pain1, flipper_run }; void flipper_bite(edict_t *self) { vec3_t aim; if (!self) { return; } VectorSet(aim, MELEE_DISTANCE, 0, 0); fire_hit(self, aim, 5, 0); } void flipper_preattack(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_chomp, 1, ATTN_NORM, 0); } mframe_t flipper_frames_attack[] = { {ai_charge, 0, flipper_preattack}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, flipper_bite}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, flipper_bite}, {ai_charge, 0, NULL} }; mmove_t flipper_move_attack = { FRAME_flpbit01, FRAME_flpbit20, flipper_frames_attack, flipper_run }; void flipper_melee(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &flipper_move_attack; } void flipper_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage) { int n; if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; if (skill->value == 3) { return; /* no pain anims in nightmare */ } n = (randk() + 1) % 2; if (n == 0) { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &flipper_move_pain1; } else { gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &flipper_move_pain2; } } void flipper_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, -8); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } mframe_t flipper_frames_death[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t flipper_move_death = { FRAME_flpdth01, FRAME_flpdth56, flipper_frames_death, flipper_dead }; void flipper_sight(edict_t *self, edict_t *other /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); } void flipper_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage, vec3_t point /* unused */) { int n; if (!self) { return; } /* check for gib */ if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex( "misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 2; n++) { ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < 2; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowHead(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } /* regular death */ gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0); self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; self->monsterinfo.currentmove = &flipper_move_death; } /* * QUAKED monster_flipper (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight */ void SP_monster_flipper(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_pain1 = gi.soundindex("flipper/flppain1.wav"); sound_pain2 = gi.soundindex("flipper/flppain2.wav"); sound_death = gi.soundindex("flipper/flpdeth1.wav"); sound_chomp = gi.soundindex("flipper/flpatck1.wav"); sound_attack = gi.soundindex("flipper/flpatck2.wav"); sound_idle = gi.soundindex("flipper/flpidle1.wav"); sound_search = gi.soundindex("flipper/flpsrch1.wav"); sound_sight = gi.soundindex("flipper/flpsght1.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/flipper/tris.md2"); VectorSet(self->mins, -16, -16, 0); VectorSet(self->maxs, 16, 16, 32); self->health = 50; self->gib_health = -30; self->mass = 100; self->pain = flipper_pain; self->die = flipper_die; self->monsterinfo.stand = flipper_stand; self->monsterinfo.walk = flipper_walk; self->monsterinfo.run = flipper_start_run; self->monsterinfo.melee = flipper_melee; self->monsterinfo.sight = flipper_sight; gi.linkentity(self); self->monsterinfo.currentmove = &flipper_move_stand; self->monsterinfo.scale = MODEL_SCALE; swimmonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/float/0000755000175000017500000000000012615162034021113 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/float/float.h0000644000175000017500000001655712615162034022407 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Mechanic animations. * * ======================================================================= */ #define FRAME_actvat01 0 #define FRAME_actvat02 1 #define FRAME_actvat03 2 #define FRAME_actvat04 3 #define FRAME_actvat05 4 #define FRAME_actvat06 5 #define FRAME_actvat07 6 #define FRAME_actvat08 7 #define FRAME_actvat09 8 #define FRAME_actvat10 9 #define FRAME_actvat11 10 #define FRAME_actvat12 11 #define FRAME_actvat13 12 #define FRAME_actvat14 13 #define FRAME_actvat15 14 #define FRAME_actvat16 15 #define FRAME_actvat17 16 #define FRAME_actvat18 17 #define FRAME_actvat19 18 #define FRAME_actvat20 19 #define FRAME_actvat21 20 #define FRAME_actvat22 21 #define FRAME_actvat23 22 #define FRAME_actvat24 23 #define FRAME_actvat25 24 #define FRAME_actvat26 25 #define FRAME_actvat27 26 #define FRAME_actvat28 27 #define FRAME_actvat29 28 #define FRAME_actvat30 29 #define FRAME_actvat31 30 #define FRAME_attak101 31 #define FRAME_attak102 32 #define FRAME_attak103 33 #define FRAME_attak104 34 #define FRAME_attak105 35 #define FRAME_attak106 36 #define FRAME_attak107 37 #define FRAME_attak108 38 #define FRAME_attak109 39 #define FRAME_attak110 40 #define FRAME_attak111 41 #define FRAME_attak112 42 #define FRAME_attak113 43 #define FRAME_attak114 44 #define FRAME_attak201 45 #define FRAME_attak202 46 #define FRAME_attak203 47 #define FRAME_attak204 48 #define FRAME_attak205 49 #define FRAME_attak206 50 #define FRAME_attak207 51 #define FRAME_attak208 52 #define FRAME_attak209 53 #define FRAME_attak210 54 #define FRAME_attak211 55 #define FRAME_attak212 56 #define FRAME_attak213 57 #define FRAME_attak214 58 #define FRAME_attak215 59 #define FRAME_attak216 60 #define FRAME_attak217 61 #define FRAME_attak218 62 #define FRAME_attak219 63 #define FRAME_attak220 64 #define FRAME_attak221 65 #define FRAME_attak222 66 #define FRAME_attak223 67 #define FRAME_attak224 68 #define FRAME_attak225 69 #define FRAME_attak301 70 #define FRAME_attak302 71 #define FRAME_attak303 72 #define FRAME_attak304 73 #define FRAME_attak305 74 #define FRAME_attak306 75 #define FRAME_attak307 76 #define FRAME_attak308 77 #define FRAME_attak309 78 #define FRAME_attak310 79 #define FRAME_attak311 80 #define FRAME_attak312 81 #define FRAME_attak313 82 #define FRAME_attak314 83 #define FRAME_attak315 84 #define FRAME_attak316 85 #define FRAME_attak317 86 #define FRAME_attak318 87 #define FRAME_attak319 88 #define FRAME_attak320 89 #define FRAME_attak321 90 #define FRAME_attak322 91 #define FRAME_attak323 92 #define FRAME_attak324 93 #define FRAME_attak325 94 #define FRAME_attak326 95 #define FRAME_attak327 96 #define FRAME_attak328 97 #define FRAME_attak329 98 #define FRAME_attak330 99 #define FRAME_attak331 100 #define FRAME_attak332 101 #define FRAME_attak333 102 #define FRAME_attak334 103 #define FRAME_death01 104 #define FRAME_death02 105 #define FRAME_death03 106 #define FRAME_death04 107 #define FRAME_death05 108 #define FRAME_death06 109 #define FRAME_death07 110 #define FRAME_death08 111 #define FRAME_death09 112 #define FRAME_death10 113 #define FRAME_death11 114 #define FRAME_death12 115 #define FRAME_death13 116 #define FRAME_pain101 117 #define FRAME_pain102 118 #define FRAME_pain103 119 #define FRAME_pain104 120 #define FRAME_pain105 121 #define FRAME_pain106 122 #define FRAME_pain107 123 #define FRAME_pain201 124 #define FRAME_pain202 125 #define FRAME_pain203 126 #define FRAME_pain204 127 #define FRAME_pain205 128 #define FRAME_pain206 129 #define FRAME_pain207 130 #define FRAME_pain208 131 #define FRAME_pain301 132 #define FRAME_pain302 133 #define FRAME_pain303 134 #define FRAME_pain304 135 #define FRAME_pain305 136 #define FRAME_pain306 137 #define FRAME_pain307 138 #define FRAME_pain308 139 #define FRAME_pain309 140 #define FRAME_pain310 141 #define FRAME_pain311 142 #define FRAME_pain312 143 #define FRAME_stand101 144 #define FRAME_stand102 145 #define FRAME_stand103 146 #define FRAME_stand104 147 #define FRAME_stand105 148 #define FRAME_stand106 149 #define FRAME_stand107 150 #define FRAME_stand108 151 #define FRAME_stand109 152 #define FRAME_stand110 153 #define FRAME_stand111 154 #define FRAME_stand112 155 #define FRAME_stand113 156 #define FRAME_stand114 157 #define FRAME_stand115 158 #define FRAME_stand116 159 #define FRAME_stand117 160 #define FRAME_stand118 161 #define FRAME_stand119 162 #define FRAME_stand120 163 #define FRAME_stand121 164 #define FRAME_stand122 165 #define FRAME_stand123 166 #define FRAME_stand124 167 #define FRAME_stand125 168 #define FRAME_stand126 169 #define FRAME_stand127 170 #define FRAME_stand128 171 #define FRAME_stand129 172 #define FRAME_stand130 173 #define FRAME_stand131 174 #define FRAME_stand132 175 #define FRAME_stand133 176 #define FRAME_stand134 177 #define FRAME_stand135 178 #define FRAME_stand136 179 #define FRAME_stand137 180 #define FRAME_stand138 181 #define FRAME_stand139 182 #define FRAME_stand140 183 #define FRAME_stand141 184 #define FRAME_stand142 185 #define FRAME_stand143 186 #define FRAME_stand144 187 #define FRAME_stand145 188 #define FRAME_stand146 189 #define FRAME_stand147 190 #define FRAME_stand148 191 #define FRAME_stand149 192 #define FRAME_stand150 193 #define FRAME_stand151 194 #define FRAME_stand152 195 #define FRAME_stand201 196 #define FRAME_stand202 197 #define FRAME_stand203 198 #define FRAME_stand204 199 #define FRAME_stand205 200 #define FRAME_stand206 201 #define FRAME_stand207 202 #define FRAME_stand208 203 #define FRAME_stand209 204 #define FRAME_stand210 205 #define FRAME_stand211 206 #define FRAME_stand212 207 #define FRAME_stand213 208 #define FRAME_stand214 209 #define FRAME_stand215 210 #define FRAME_stand216 211 #define FRAME_stand217 212 #define FRAME_stand218 213 #define FRAME_stand219 214 #define FRAME_stand220 215 #define FRAME_stand221 216 #define FRAME_stand222 217 #define FRAME_stand223 218 #define FRAME_stand224 219 #define FRAME_stand225 220 #define FRAME_stand226 221 #define FRAME_stand227 222 #define FRAME_stand228 223 #define FRAME_stand229 224 #define FRAME_stand230 225 #define FRAME_stand231 226 #define FRAME_stand232 227 #define FRAME_stand233 228 #define FRAME_stand234 229 #define FRAME_stand235 230 #define FRAME_stand236 231 #define FRAME_stand237 232 #define FRAME_stand238 233 #define FRAME_stand239 234 #define FRAME_stand240 235 #define FRAME_stand241 236 #define FRAME_stand242 237 #define FRAME_stand243 238 #define FRAME_stand244 239 #define FRAME_stand245 240 #define FRAME_stand246 241 #define FRAME_stand247 242 #define FRAME_stand248 243 #define FRAME_stand249 244 #define FRAME_stand250 245 #define FRAME_stand251 246 #define FRAME_stand252 247 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/float/float.c0000644000175000017500000004040412615162034022366 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Mechanic. * * ======================================================================= */ #include "../../header/local.h" #include "float.h" static int sound_attack2; static int sound_attack3; static int sound_death1; static int sound_idle; static int sound_pain1; static int sound_pain2; static int sound_sight; void floater_sight(edict_t *self, edict_t *other /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); } void floater_idle(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0); } void floater_dead(edict_t *self); void floater_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); void floater_run(edict_t *self); void floater_wham(edict_t *self); void floater_zap(edict_t *self); void floater_fire_blaster(edict_t *self) { vec3_t start; vec3_t forward, right; vec3_t end; vec3_t dir; int effect; if (!self) { return; } if ((self->s.frame == FRAME_attak104) || (self->s.frame == FRAME_attak107)) { effect = EF_HYPERBLASTER; } else { effect = 0; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_FLOAT_BLASTER_1], forward, right, start); VectorCopy(self->enemy->s.origin, end); end[2] += self->enemy->viewheight; VectorSubtract(end, start, dir); monster_fire_blaster(self, start, dir, 1, 1000, MZ2_FLOAT_BLASTER_1, effect); } mframe_t floater_frames_stand1[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t floater_move_stand1 = { FRAME_stand101, FRAME_stand152, floater_frames_stand1, NULL }; mframe_t floater_frames_stand2[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t floater_move_stand2 = { FRAME_stand201, FRAME_stand252, floater_frames_stand2, NULL }; void floater_stand(edict_t *self) { if (!self) { return; } if (random() <= 0.5) { self->monsterinfo.currentmove = &floater_move_stand1; } else { self->monsterinfo.currentmove = &floater_move_stand2; } } mframe_t floater_frames_activate[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t floater_move_activate = { FRAME_actvat01, FRAME_actvat31, floater_frames_activate, NULL }; mframe_t floater_frames_attack1[] = { {ai_charge, 0, NULL}, /* Blaster attack */ {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, floater_fire_blaster}, /* BOOM (0, -25.8, 32.5) -- LOOP Starts */ {ai_charge, 0, floater_fire_blaster}, {ai_charge, 0, floater_fire_blaster}, {ai_charge, 0, floater_fire_blaster}, {ai_charge, 0, floater_fire_blaster}, {ai_charge, 0, floater_fire_blaster}, {ai_charge, 0, floater_fire_blaster}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} /* -- LOOP Ends */ }; mmove_t floater_move_attack1 = { FRAME_attak101, FRAME_attak114, floater_frames_attack1, floater_run }; mframe_t floater_frames_attack2[] = { {ai_charge, 0, NULL}, /* Claws */ {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, floater_wham}, /* WHAM (0, -45, 29}.6) -- LOOP Starts */ {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, /* -- LOOP Ends */ {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t floater_move_attack2 = { FRAME_attak201, FRAME_attak225, floater_frames_attack2, floater_run }; mframe_t floater_frames_attack3[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, floater_zap}, /* -- LOOP Starts */ {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, /* -- LOOP Ends */ {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t floater_move_attack3 = { FRAME_attak301, FRAME_attak334, floater_frames_attack3, floater_run}; mframe_t floater_frames_death[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t floater_move_death = { FRAME_death01, FRAME_death13, floater_frames_death, floater_dead }; mframe_t floater_frames_pain1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t floater_move_pain1 = { FRAME_pain101, FRAME_pain107, floater_frames_pain1, floater_run }; mframe_t floater_frames_pain2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t floater_move_pain2 = { FRAME_pain201, FRAME_pain208, floater_frames_pain2, floater_run }; mframe_t floater_frames_pain3[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t floater_move_pain3 = { FRAME_pain301, FRAME_pain312, floater_frames_pain3, floater_run }; mframe_t floater_frames_walk[] = { {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL} }; mmove_t floater_move_walk = { FRAME_stand101, FRAME_stand152, floater_frames_walk, NULL }; mframe_t floater_frames_run[] = { {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL}, {ai_run, 13, NULL} }; mmove_t floater_move_run = { FRAME_stand101, FRAME_stand152, floater_frames_run, NULL }; void floater_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &floater_move_stand1; } else { self->monsterinfo.currentmove = &floater_move_run; } } void floater_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &floater_move_walk; } void floater_wham(edict_t *self) { static vec3_t aim = {MELEE_DISTANCE, 0, 0}; gi.sound(self, CHAN_WEAPON, sound_attack3, 1, ATTN_NORM, 0); fire_hit(self, aim, 5 + randk() % 6, -50); } void floater_zap(edict_t *self) { vec3_t forward, right; vec3_t origin; vec3_t dir; vec3_t offset; if (!self) { return; } VectorSubtract(self->enemy->s.origin, self->s.origin, dir); AngleVectors(self->s.angles, forward, right, NULL); VectorSet(offset, 18.5, -0.9, 10); G_ProjectSource(self->s.origin, offset, forward, right, origin); gi.sound(self, CHAN_WEAPON, sound_attack2, 1, ATTN_NORM, 0); gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_SPLASH); gi.WriteByte(32); gi.WritePosition(origin); gi.WriteDir(dir); gi.WriteByte(1); /* sparks */ gi.multicast(origin, MULTICAST_PVS); T_Damage(self->enemy, self, self, dir, self->enemy->s.origin, vec3_origin, 5 + randk() % 6, -10, DAMAGE_ENERGY, MOD_UNKNOWN); } void floater_attack(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &floater_move_attack1; } void floater_melee(edict_t *self) { if (!self) { return; } if (random() < 0.5) { self->monsterinfo.currentmove = &floater_move_attack3; } else { self->monsterinfo.currentmove = &floater_move_attack2; } } void floater_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage) { int n; if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; if (skill->value == 3) { return; /* no pain anims in nightmare */ } n = (randk() + 1) % 3; if (n == 0) { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &floater_move_pain1; } else { gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &floater_move_pain2; } } void floater_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, -8); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } void floater_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage /* unused */, vec3_t point /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_death1, 1, ATTN_NORM, 0); BecomeExplosion1(self); } /* * QUAKED monster_floater (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight */ void SP_monster_floater(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_attack2 = gi.soundindex("floater/fltatck2.wav"); sound_attack3 = gi.soundindex("floater/fltatck3.wav"); sound_death1 = gi.soundindex("floater/fltdeth1.wav"); sound_idle = gi.soundindex("floater/fltidle1.wav"); sound_pain1 = gi.soundindex("floater/fltpain1.wav"); sound_pain2 = gi.soundindex("floater/fltpain2.wav"); sound_sight = gi.soundindex("floater/fltsght1.wav"); gi.soundindex("floater/fltatck1.wav"); self->s.sound = gi.soundindex("floater/fltsrch1.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/float/tris.md2"); VectorSet(self->mins, -24, -24, -24); VectorSet(self->maxs, 24, 24, 32); self->health = 200; self->gib_health = -80; self->mass = 300; self->pain = floater_pain; self->die = floater_die; self->monsterinfo.stand = floater_stand; self->monsterinfo.walk = floater_walk; self->monsterinfo.run = floater_run; self->monsterinfo.attack = floater_attack; self->monsterinfo.melee = floater_melee; self->monsterinfo.sight = floater_sight; self->monsterinfo.idle = floater_idle; gi.linkentity(self); if (random() <= 0.5) { self->monsterinfo.currentmove = &floater_move_stand1; } else { self->monsterinfo.currentmove = &floater_move_stand2; } self->monsterinfo.scale = MODEL_SCALE; flymonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/berserker/0000755000175000017500000000000012615162034021772 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/berserker/berserker.h0000644000175000017500000001564112615162034024136 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Berserker animations. * * ======================================================================= */ #define FRAME_stand1 0 #define FRAME_stand2 1 #define FRAME_stand3 2 #define FRAME_stand4 3 #define FRAME_stand5 4 #define FRAME_standb1 5 #define FRAME_standb2 6 #define FRAME_standb3 7 #define FRAME_standb4 8 #define FRAME_standb5 9 #define FRAME_standb6 10 #define FRAME_standb7 11 #define FRAME_standb8 12 #define FRAME_standb9 13 #define FRAME_standb10 14 #define FRAME_standb11 15 #define FRAME_standb12 16 #define FRAME_standb13 17 #define FRAME_standb14 18 #define FRAME_standb15 19 #define FRAME_standb16 20 #define FRAME_standb17 21 #define FRAME_standb18 22 #define FRAME_standb19 23 #define FRAME_standb20 24 #define FRAME_walkc1 25 #define FRAME_walkc2 26 #define FRAME_walkc3 27 #define FRAME_walkc4 28 #define FRAME_walkc5 29 #define FRAME_walkc6 30 #define FRAME_walkc7 31 #define FRAME_walkc8 32 #define FRAME_walkc9 33 #define FRAME_walkc10 34 #define FRAME_walkc11 35 #define FRAME_run1 36 #define FRAME_run2 37 #define FRAME_run3 38 #define FRAME_run4 39 #define FRAME_run5 40 #define FRAME_run6 41 #define FRAME_att_a1 42 #define FRAME_att_a2 43 #define FRAME_att_a3 44 #define FRAME_att_a4 45 #define FRAME_att_a5 46 #define FRAME_att_a6 47 #define FRAME_att_a7 48 #define FRAME_att_a8 49 #define FRAME_att_a9 50 #define FRAME_att_a10 51 #define FRAME_att_a11 52 #define FRAME_att_a12 53 #define FRAME_att_a13 54 #define FRAME_att_b1 55 #define FRAME_att_b2 56 #define FRAME_att_b3 57 #define FRAME_att_b4 58 #define FRAME_att_b5 59 #define FRAME_att_b6 60 #define FRAME_att_b7 61 #define FRAME_att_b8 62 #define FRAME_att_b9 63 #define FRAME_att_b10 64 #define FRAME_att_b11 65 #define FRAME_att_b12 66 #define FRAME_att_b13 67 #define FRAME_att_b14 68 #define FRAME_att_b15 69 #define FRAME_att_b16 70 #define FRAME_att_b17 71 #define FRAME_att_b18 72 #define FRAME_att_b19 73 #define FRAME_att_b20 74 #define FRAME_att_b21 75 #define FRAME_att_c1 76 #define FRAME_att_c2 77 #define FRAME_att_c3 78 #define FRAME_att_c4 79 #define FRAME_att_c5 80 #define FRAME_att_c6 81 #define FRAME_att_c7 82 #define FRAME_att_c8 83 #define FRAME_att_c9 84 #define FRAME_att_c10 85 #define FRAME_att_c11 86 #define FRAME_att_c12 87 #define FRAME_att_c13 88 #define FRAME_att_c14 89 #define FRAME_att_c15 90 #define FRAME_att_c16 91 #define FRAME_att_c17 92 #define FRAME_att_c18 93 #define FRAME_att_c19 94 #define FRAME_att_c20 95 #define FRAME_att_c21 96 #define FRAME_att_c22 97 #define FRAME_att_c23 98 #define FRAME_att_c24 99 #define FRAME_att_c25 100 #define FRAME_att_c26 101 #define FRAME_att_c27 102 #define FRAME_att_c28 103 #define FRAME_att_c29 104 #define FRAME_att_c30 105 #define FRAME_att_c31 106 #define FRAME_att_c32 107 #define FRAME_att_c33 108 #define FRAME_att_c34 109 #define FRAME_r_att1 110 #define FRAME_r_att2 111 #define FRAME_r_att3 112 #define FRAME_r_att4 113 #define FRAME_r_att5 114 #define FRAME_r_att6 115 #define FRAME_r_att7 116 #define FRAME_r_att8 117 #define FRAME_r_att9 118 #define FRAME_r_att10 119 #define FRAME_r_att11 120 #define FRAME_r_att12 121 #define FRAME_r_att13 122 #define FRAME_r_att14 123 #define FRAME_r_att15 124 #define FRAME_r_att16 125 #define FRAME_r_att17 126 #define FRAME_r_att18 127 #define FRAME_r_attb1 128 #define FRAME_r_attb2 129 #define FRAME_r_attb3 130 #define FRAME_r_attb4 131 #define FRAME_r_attb5 132 #define FRAME_r_attb6 133 #define FRAME_r_attb7 134 #define FRAME_r_attb8 135 #define FRAME_r_attb9 136 #define FRAME_r_attb10 137 #define FRAME_r_attb11 138 #define FRAME_r_attb12 139 #define FRAME_r_attb13 140 #define FRAME_r_attb14 141 #define FRAME_r_attb15 142 #define FRAME_r_attb16 143 #define FRAME_r_attb17 144 #define FRAME_r_attb18 145 #define FRAME_slam1 146 #define FRAME_slam2 147 #define FRAME_slam3 148 #define FRAME_slam4 149 #define FRAME_slam5 150 #define FRAME_slam6 151 #define FRAME_slam7 152 #define FRAME_slam8 153 #define FRAME_slam9 154 #define FRAME_slam10 155 #define FRAME_slam11 156 #define FRAME_slam12 157 #define FRAME_slam13 158 #define FRAME_slam14 159 #define FRAME_slam15 160 #define FRAME_slam16 161 #define FRAME_slam17 162 #define FRAME_slam18 163 #define FRAME_slam19 164 #define FRAME_slam20 165 #define FRAME_slam21 166 #define FRAME_slam22 167 #define FRAME_slam23 168 #define FRAME_duck1 169 #define FRAME_duck2 170 #define FRAME_duck3 171 #define FRAME_duck4 172 #define FRAME_duck5 173 #define FRAME_duck6 174 #define FRAME_duck7 175 #define FRAME_duck8 176 #define FRAME_duck9 177 #define FRAME_duck10 178 #define FRAME_fall1 179 #define FRAME_fall2 180 #define FRAME_fall3 181 #define FRAME_fall4 182 #define FRAME_fall5 183 #define FRAME_fall6 184 #define FRAME_fall7 185 #define FRAME_fall8 186 #define FRAME_fall9 187 #define FRAME_fall10 188 #define FRAME_fall11 189 #define FRAME_fall12 190 #define FRAME_fall13 191 #define FRAME_fall14 192 #define FRAME_fall15 193 #define FRAME_fall16 194 #define FRAME_fall17 195 #define FRAME_fall18 196 #define FRAME_fall19 197 #define FRAME_fall20 198 #define FRAME_painc1 199 #define FRAME_painc2 200 #define FRAME_painc3 201 #define FRAME_painc4 202 #define FRAME_painb1 203 #define FRAME_painb2 204 #define FRAME_painb3 205 #define FRAME_painb4 206 #define FRAME_painb5 207 #define FRAME_painb6 208 #define FRAME_painb7 209 #define FRAME_painb8 210 #define FRAME_painb9 211 #define FRAME_painb10 212 #define FRAME_painb11 213 #define FRAME_painb12 214 #define FRAME_painb13 215 #define FRAME_painb14 216 #define FRAME_painb15 217 #define FRAME_painb16 218 #define FRAME_painb17 219 #define FRAME_painb18 220 #define FRAME_painb19 221 #define FRAME_painb20 222 #define FRAME_death1 223 #define FRAME_death2 224 #define FRAME_death3 225 #define FRAME_death4 226 #define FRAME_death5 227 #define FRAME_death6 228 #define FRAME_death7 229 #define FRAME_death8 230 #define FRAME_death9 231 #define FRAME_death10 232 #define FRAME_death11 233 #define FRAME_death12 234 #define FRAME_death13 235 #define FRAME_deathc1 236 #define FRAME_deathc2 237 #define FRAME_deathc3 238 #define FRAME_deathc4 239 #define FRAME_deathc5 240 #define FRAME_deathc6 241 #define FRAME_deathc7 242 #define FRAME_deathc8 243 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/berserker/berserker.c0000644000175000017500000002472512615162034024134 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 berserker. * * ======================================================================= */ #include "../../header/local.h" #include "berserker.h" static int sound_pain; static int sound_die; static int sound_idle; static int sound_punch; static int sound_sight; static int sound_search; void berserk_sight(edict_t *self, edict_t *other /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); } void berserk_search(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0); } void berserk_fidget(edict_t *self); mframe_t berserk_frames_stand[] = { {ai_stand, 0, berserk_fidget}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t berserk_move_stand = { FRAME_stand1, FRAME_stand5, berserk_frames_stand, NULL}; void berserk_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &berserk_move_stand; } mframe_t berserk_frames_stand_fidget[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t berserk_move_stand_fidget = { FRAME_standb1, FRAME_standb20, berserk_frames_stand_fidget, berserk_stand }; void berserk_fidget(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { return; } if (random() > 0.15) { return; } self->monsterinfo.currentmove = &berserk_move_stand_fidget; gi.sound(self, CHAN_WEAPON, sound_idle, 1, ATTN_IDLE, 0); } mframe_t berserk_frames_walk[] = { {ai_walk, 9.1, NULL}, {ai_walk, 6.3, NULL}, {ai_walk, 4.9, NULL}, {ai_walk, 6.7, NULL}, {ai_walk, 6.0, NULL}, {ai_walk, 8.2, NULL}, {ai_walk, 7.2, NULL}, {ai_walk, 6.1, NULL}, {ai_walk, 4.9, NULL}, {ai_walk, 4.7, NULL}, {ai_walk, 4.7, NULL}, {ai_walk, 4.8, NULL} }; mmove_t berserk_move_walk = { FRAME_walkc1, FRAME_walkc11, berserk_frames_walk, NULL }; void berserk_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &berserk_move_walk; } mframe_t berserk_frames_run1[] = { {ai_run, 21, NULL}, {ai_run, 11, NULL}, {ai_run, 21, NULL}, {ai_run, 25, NULL}, {ai_run, 18, NULL}, {ai_run, 19, NULL} }; mmove_t berserk_move_run1 = { FRAME_run1, FRAME_run6, berserk_frames_run1, NULL }; void berserk_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &berserk_move_stand; } else { self->monsterinfo.currentmove = &berserk_move_run1; } } void berserk_attack_spike(edict_t *self) { static vec3_t aim = {MELEE_DISTANCE, 0, -24}; if (!self) { return; } fire_hit(self, aim, (15 + (randk() % 6)), 400); /* Faster attack -- upwards and backwards */ } void berserk_swing(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_punch, 1, ATTN_NORM, 0); } mframe_t berserk_frames_attack_spike[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, berserk_swing}, {ai_charge, 0, berserk_attack_spike}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t berserk_move_attack_spike = { FRAME_att_c1, FRAME_att_c8, berserk_frames_attack_spike, berserk_run }; void berserk_attack_club(edict_t *self) { vec3_t aim; if (!self) { return; } VectorSet(aim, MELEE_DISTANCE, self->mins[0], -4); fire_hit(self, aim, (5 + (randk() % 6)), 400); /* Slower attack */ } mframe_t berserk_frames_attack_club[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, berserk_swing}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, berserk_attack_club}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t berserk_move_attack_club = { FRAME_att_c9, FRAME_att_c20, berserk_frames_attack_club, berserk_run }; void berserk_strike(edict_t *self) { /* Unused, but removal is very PITA. Let it be... */ } mframe_t berserk_frames_attack_strike[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, berserk_swing}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, berserk_strike}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 9.7, NULL}, {ai_move, 13.6, NULL} }; mmove_t berserk_move_attack_strike = { FRAME_att_c21, FRAME_att_c34, berserk_frames_attack_strike, berserk_run }; void berserk_melee(edict_t *self) { if (!self) { return; } if ((randk() % 2) == 0) { self->monsterinfo.currentmove = &berserk_move_attack_spike; } else { self->monsterinfo.currentmove = &berserk_move_attack_club; } } mframe_t berserk_frames_pain1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t berserk_move_pain1 = { FRAME_painc1, FRAME_painc4, berserk_frames_pain1, berserk_run}; mframe_t berserk_frames_pain2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t berserk_move_pain2 = { FRAME_painb1, FRAME_painb20, berserk_frames_pain2, berserk_run }; void berserk_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage) { if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; gi.sound(self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0); if (skill->value == 3) { return; /* no pain anims in nightmare */ } if ((damage < 20) || (random() < 0.5)) { self->monsterinfo.currentmove = &berserk_move_pain1; } else { self->monsterinfo.currentmove = &berserk_move_pain2; } } void berserk_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, -8); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } mframe_t berserk_frames_death1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t berserk_move_death1 = { FRAME_death1, FRAME_death13, berserk_frames_death1, berserk_dead }; mframe_t berserk_frames_death2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t berserk_move_death2 = { FRAME_deathc1, FRAME_deathc8, berserk_frames_death2, berserk_dead}; void berserk_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage, vec3_t point /* unused */) { int n; if (!self) { return; } if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex( "misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 2; n++) { ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0); self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; if (damage >= 50) { self->monsterinfo.currentmove = &berserk_move_death1; } else { self->monsterinfo.currentmove = &berserk_move_death2; } } /* * QUAKED monster_berserk (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight */ void SP_monster_berserk(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } /* pre-caches */ sound_pain = gi.soundindex("berserk/berpain2.wav"); sound_die = gi.soundindex("berserk/berdeth2.wav"); sound_idle = gi.soundindex("berserk/beridle1.wav"); sound_punch = gi.soundindex("berserk/attack.wav"); sound_search = gi.soundindex("berserk/bersrch1.wav"); sound_sight = gi.soundindex("berserk/sight.wav"); self->s.modelindex = gi.modelindex("models/monsters/berserk/tris.md2"); VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, 32); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->health = 240; self->gib_health = -60; self->mass = 250; self->pain = berserk_pain; self->die = berserk_die; self->monsterinfo.stand = berserk_stand; self->monsterinfo.walk = berserk_walk; self->monsterinfo.run = berserk_run; self->monsterinfo.dodge = NULL; self->monsterinfo.attack = NULL; self->monsterinfo.melee = berserk_melee; self->monsterinfo.sight = berserk_sight; self->monsterinfo.search = berserk_search; self->monsterinfo.currentmove = &berserk_move_stand; self->monsterinfo.scale = MODEL_SCALE; gi.linkentity(self); walkmonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/soldier/0000755000175000017500000000000012615162034021447 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/soldier/soldier.h0000644000175000017500000003226212615162034023266 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Soldier aka "Guard" animations. * * ======================================================================= */ #define FRAME_attak101 0 #define FRAME_attak102 1 #define FRAME_attak103 2 #define FRAME_attak104 3 #define FRAME_attak105 4 #define FRAME_attak106 5 #define FRAME_attak107 6 #define FRAME_attak108 7 #define FRAME_attak109 8 #define FRAME_attak110 9 #define FRAME_attak111 10 #define FRAME_attak112 11 #define FRAME_attak201 12 #define FRAME_attak202 13 #define FRAME_attak203 14 #define FRAME_attak204 15 #define FRAME_attak205 16 #define FRAME_attak206 17 #define FRAME_attak207 18 #define FRAME_attak208 19 #define FRAME_attak209 20 #define FRAME_attak210 21 #define FRAME_attak211 22 #define FRAME_attak212 23 #define FRAME_attak213 24 #define FRAME_attak214 25 #define FRAME_attak215 26 #define FRAME_attak216 27 #define FRAME_attak217 28 #define FRAME_attak218 29 #define FRAME_attak301 30 #define FRAME_attak302 31 #define FRAME_attak303 32 #define FRAME_attak304 33 #define FRAME_attak305 34 #define FRAME_attak306 35 #define FRAME_attak307 36 #define FRAME_attak308 37 #define FRAME_attak309 38 #define FRAME_attak401 39 #define FRAME_attak402 40 #define FRAME_attak403 41 #define FRAME_attak404 42 #define FRAME_attak405 43 #define FRAME_attak406 44 #define FRAME_duck01 45 #define FRAME_duck02 46 #define FRAME_duck03 47 #define FRAME_duck04 48 #define FRAME_duck05 49 #define FRAME_pain101 50 #define FRAME_pain102 51 #define FRAME_pain103 52 #define FRAME_pain104 53 #define FRAME_pain105 54 #define FRAME_pain201 55 #define FRAME_pain202 56 #define FRAME_pain203 57 #define FRAME_pain204 58 #define FRAME_pain205 59 #define FRAME_pain206 60 #define FRAME_pain207 61 #define FRAME_pain301 62 #define FRAME_pain302 63 #define FRAME_pain303 64 #define FRAME_pain304 65 #define FRAME_pain305 66 #define FRAME_pain306 67 #define FRAME_pain307 68 #define FRAME_pain308 69 #define FRAME_pain309 70 #define FRAME_pain310 71 #define FRAME_pain311 72 #define FRAME_pain312 73 #define FRAME_pain313 74 #define FRAME_pain314 75 #define FRAME_pain315 76 #define FRAME_pain316 77 #define FRAME_pain317 78 #define FRAME_pain318 79 #define FRAME_pain401 80 #define FRAME_pain402 81 #define FRAME_pain403 82 #define FRAME_pain404 83 #define FRAME_pain405 84 #define FRAME_pain406 85 #define FRAME_pain407 86 #define FRAME_pain408 87 #define FRAME_pain409 88 #define FRAME_pain410 89 #define FRAME_pain411 90 #define FRAME_pain412 91 #define FRAME_pain413 92 #define FRAME_pain414 93 #define FRAME_pain415 94 #define FRAME_pain416 95 #define FRAME_pain417 96 #define FRAME_run01 97 #define FRAME_run02 98 #define FRAME_run03 99 #define FRAME_run04 100 #define FRAME_run05 101 #define FRAME_run06 102 #define FRAME_run07 103 #define FRAME_run08 104 #define FRAME_run09 105 #define FRAME_run10 106 #define FRAME_run11 107 #define FRAME_run12 108 #define FRAME_runs01 109 #define FRAME_runs02 110 #define FRAME_runs03 111 #define FRAME_runs04 112 #define FRAME_runs05 113 #define FRAME_runs06 114 #define FRAME_runs07 115 #define FRAME_runs08 116 #define FRAME_runs09 117 #define FRAME_runs10 118 #define FRAME_runs11 119 #define FRAME_runs12 120 #define FRAME_runs13 121 #define FRAME_runs14 122 #define FRAME_runs15 123 #define FRAME_runs16 124 #define FRAME_runs17 125 #define FRAME_runs18 126 #define FRAME_runt01 127 #define FRAME_runt02 128 #define FRAME_runt03 129 #define FRAME_runt04 130 #define FRAME_runt05 131 #define FRAME_runt06 132 #define FRAME_runt07 133 #define FRAME_runt08 134 #define FRAME_runt09 135 #define FRAME_runt10 136 #define FRAME_runt11 137 #define FRAME_runt12 138 #define FRAME_runt13 139 #define FRAME_runt14 140 #define FRAME_runt15 141 #define FRAME_runt16 142 #define FRAME_runt17 143 #define FRAME_runt18 144 #define FRAME_runt19 145 #define FRAME_stand101 146 #define FRAME_stand102 147 #define FRAME_stand103 148 #define FRAME_stand104 149 #define FRAME_stand105 150 #define FRAME_stand106 151 #define FRAME_stand107 152 #define FRAME_stand108 153 #define FRAME_stand109 154 #define FRAME_stand110 155 #define FRAME_stand111 156 #define FRAME_stand112 157 #define FRAME_stand113 158 #define FRAME_stand114 159 #define FRAME_stand115 160 #define FRAME_stand116 161 #define FRAME_stand117 162 #define FRAME_stand118 163 #define FRAME_stand119 164 #define FRAME_stand120 165 #define FRAME_stand121 166 #define FRAME_stand122 167 #define FRAME_stand123 168 #define FRAME_stand124 169 #define FRAME_stand125 170 #define FRAME_stand126 171 #define FRAME_stand127 172 #define FRAME_stand128 173 #define FRAME_stand129 174 #define FRAME_stand130 175 #define FRAME_stand301 176 #define FRAME_stand302 177 #define FRAME_stand303 178 #define FRAME_stand304 179 #define FRAME_stand305 180 #define FRAME_stand306 181 #define FRAME_stand307 182 #define FRAME_stand308 183 #define FRAME_stand309 184 #define FRAME_stand310 185 #define FRAME_stand311 186 #define FRAME_stand312 187 #define FRAME_stand313 188 #define FRAME_stand314 189 #define FRAME_stand315 190 #define FRAME_stand316 191 #define FRAME_stand317 192 #define FRAME_stand318 193 #define FRAME_stand319 194 #define FRAME_stand320 195 #define FRAME_stand321 196 #define FRAME_stand322 197 #define FRAME_stand323 198 #define FRAME_stand324 199 #define FRAME_stand325 200 #define FRAME_stand326 201 #define FRAME_stand327 202 #define FRAME_stand328 203 #define FRAME_stand329 204 #define FRAME_stand330 205 #define FRAME_stand331 206 #define FRAME_stand332 207 #define FRAME_stand333 208 #define FRAME_stand334 209 #define FRAME_stand335 210 #define FRAME_stand336 211 #define FRAME_stand337 212 #define FRAME_stand338 213 #define FRAME_stand339 214 #define FRAME_walk101 215 #define FRAME_walk102 216 #define FRAME_walk103 217 #define FRAME_walk104 218 #define FRAME_walk105 219 #define FRAME_walk106 220 #define FRAME_walk107 221 #define FRAME_walk108 222 #define FRAME_walk109 223 #define FRAME_walk110 224 #define FRAME_walk111 225 #define FRAME_walk112 226 #define FRAME_walk113 227 #define FRAME_walk114 228 #define FRAME_walk115 229 #define FRAME_walk116 230 #define FRAME_walk117 231 #define FRAME_walk118 232 #define FRAME_walk119 233 #define FRAME_walk120 234 #define FRAME_walk121 235 #define FRAME_walk122 236 #define FRAME_walk123 237 #define FRAME_walk124 238 #define FRAME_walk125 239 #define FRAME_walk126 240 #define FRAME_walk127 241 #define FRAME_walk128 242 #define FRAME_walk129 243 #define FRAME_walk130 244 #define FRAME_walk131 245 #define FRAME_walk132 246 #define FRAME_walk133 247 #define FRAME_walk201 248 #define FRAME_walk202 249 #define FRAME_walk203 250 #define FRAME_walk204 251 #define FRAME_walk205 252 #define FRAME_walk206 253 #define FRAME_walk207 254 #define FRAME_walk208 255 #define FRAME_walk209 256 #define FRAME_walk210 257 #define FRAME_walk211 258 #define FRAME_walk212 259 #define FRAME_walk213 260 #define FRAME_walk214 261 #define FRAME_walk215 262 #define FRAME_walk216 263 #define FRAME_walk217 264 #define FRAME_walk218 265 #define FRAME_walk219 266 #define FRAME_walk220 267 #define FRAME_walk221 268 #define FRAME_walk222 269 #define FRAME_walk223 270 #define FRAME_walk224 271 #define FRAME_death101 272 #define FRAME_death102 273 #define FRAME_death103 274 #define FRAME_death104 275 #define FRAME_death105 276 #define FRAME_death106 277 #define FRAME_death107 278 #define FRAME_death108 279 #define FRAME_death109 280 #define FRAME_death110 281 #define FRAME_death111 282 #define FRAME_death112 283 #define FRAME_death113 284 #define FRAME_death114 285 #define FRAME_death115 286 #define FRAME_death116 287 #define FRAME_death117 288 #define FRAME_death118 289 #define FRAME_death119 290 #define FRAME_death120 291 #define FRAME_death121 292 #define FRAME_death122 293 #define FRAME_death123 294 #define FRAME_death124 295 #define FRAME_death125 296 #define FRAME_death126 297 #define FRAME_death127 298 #define FRAME_death128 299 #define FRAME_death129 300 #define FRAME_death130 301 #define FRAME_death131 302 #define FRAME_death132 303 #define FRAME_death133 304 #define FRAME_death134 305 #define FRAME_death135 306 #define FRAME_death136 307 #define FRAME_death201 308 #define FRAME_death202 309 #define FRAME_death203 310 #define FRAME_death204 311 #define FRAME_death205 312 #define FRAME_death206 313 #define FRAME_death207 314 #define FRAME_death208 315 #define FRAME_death209 316 #define FRAME_death210 317 #define FRAME_death211 318 #define FRAME_death212 319 #define FRAME_death213 320 #define FRAME_death214 321 #define FRAME_death215 322 #define FRAME_death216 323 #define FRAME_death217 324 #define FRAME_death218 325 #define FRAME_death219 326 #define FRAME_death220 327 #define FRAME_death221 328 #define FRAME_death222 329 #define FRAME_death223 330 #define FRAME_death224 331 #define FRAME_death225 332 #define FRAME_death226 333 #define FRAME_death227 334 #define FRAME_death228 335 #define FRAME_death229 336 #define FRAME_death230 337 #define FRAME_death231 338 #define FRAME_death232 339 #define FRAME_death233 340 #define FRAME_death234 341 #define FRAME_death235 342 #define FRAME_death301 343 #define FRAME_death302 344 #define FRAME_death303 345 #define FRAME_death304 346 #define FRAME_death305 347 #define FRAME_death306 348 #define FRAME_death307 349 #define FRAME_death308 350 #define FRAME_death309 351 #define FRAME_death310 352 #define FRAME_death311 353 #define FRAME_death312 354 #define FRAME_death313 355 #define FRAME_death314 356 #define FRAME_death315 357 #define FRAME_death316 358 #define FRAME_death317 359 #define FRAME_death318 360 #define FRAME_death319 361 #define FRAME_death320 362 #define FRAME_death321 363 #define FRAME_death322 364 #define FRAME_death323 365 #define FRAME_death324 366 #define FRAME_death325 367 #define FRAME_death326 368 #define FRAME_death327 369 #define FRAME_death328 370 #define FRAME_death329 371 #define FRAME_death330 372 #define FRAME_death331 373 #define FRAME_death332 374 #define FRAME_death333 375 #define FRAME_death334 376 #define FRAME_death335 377 #define FRAME_death336 378 #define FRAME_death337 379 #define FRAME_death338 380 #define FRAME_death339 381 #define FRAME_death340 382 #define FRAME_death341 383 #define FRAME_death342 384 #define FRAME_death343 385 #define FRAME_death344 386 #define FRAME_death345 387 #define FRAME_death401 388 #define FRAME_death402 389 #define FRAME_death403 390 #define FRAME_death404 391 #define FRAME_death405 392 #define FRAME_death406 393 #define FRAME_death407 394 #define FRAME_death408 395 #define FRAME_death409 396 #define FRAME_death410 397 #define FRAME_death411 398 #define FRAME_death412 399 #define FRAME_death413 400 #define FRAME_death414 401 #define FRAME_death415 402 #define FRAME_death416 403 #define FRAME_death417 404 #define FRAME_death418 405 #define FRAME_death419 406 #define FRAME_death420 407 #define FRAME_death421 408 #define FRAME_death422 409 #define FRAME_death423 410 #define FRAME_death424 411 #define FRAME_death425 412 #define FRAME_death426 413 #define FRAME_death427 414 #define FRAME_death428 415 #define FRAME_death429 416 #define FRAME_death430 417 #define FRAME_death431 418 #define FRAME_death432 419 #define FRAME_death433 420 #define FRAME_death434 421 #define FRAME_death435 422 #define FRAME_death436 423 #define FRAME_death437 424 #define FRAME_death438 425 #define FRAME_death439 426 #define FRAME_death440 427 #define FRAME_death441 428 #define FRAME_death442 429 #define FRAME_death443 430 #define FRAME_death444 431 #define FRAME_death445 432 #define FRAME_death446 433 #define FRAME_death447 434 #define FRAME_death448 435 #define FRAME_death449 436 #define FRAME_death450 437 #define FRAME_death451 438 #define FRAME_death452 439 #define FRAME_death453 440 #define FRAME_death501 441 #define FRAME_death502 442 #define FRAME_death503 443 #define FRAME_death504 444 #define FRAME_death505 445 #define FRAME_death506 446 #define FRAME_death507 447 #define FRAME_death508 448 #define FRAME_death509 449 #define FRAME_death510 450 #define FRAME_death511 451 #define FRAME_death512 452 #define FRAME_death513 453 #define FRAME_death514 454 #define FRAME_death515 455 #define FRAME_death516 456 #define FRAME_death517 457 #define FRAME_death518 458 #define FRAME_death519 459 #define FRAME_death520 460 #define FRAME_death521 461 #define FRAME_death522 462 #define FRAME_death523 463 #define FRAME_death524 464 #define FRAME_death601 465 #define FRAME_death602 466 #define FRAME_death603 467 #define FRAME_death604 468 #define FRAME_death605 469 #define FRAME_death606 470 #define FRAME_death607 471 #define FRAME_death608 472 #define FRAME_death609 473 #define FRAME_death610 474 #define MODEL_SCALE 1.200000 yquake2-QUAKE2_5_32/src/game/monster/soldier/soldier.c0000644000175000017500000007072212615162034023264 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Soldier aka "Guard". This is the most complex enemy in Quake 2, since * it uses all AI features (dodging, sight, crouching, etc) and comes * in a myriad of variants. * * ======================================================================= */ #include "../../header/local.h" #include "soldier.h" static int sound_idle; static int sound_sight1; static int sound_sight2; static int sound_pain_light; static int sound_pain; static int sound_pain_ss; static int sound_death_light; static int sound_death; static int sound_death_ss; static int sound_cock; void soldier_idle(edict_t *self) { if (!self) { return; } if (random() > 0.8) { gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0); } } void soldier_cock(edict_t *self) { if (!self) { return; } if (self->s.frame == FRAME_stand322) { gi.sound(self, CHAN_WEAPON, sound_cock, 1, ATTN_IDLE, 0); } else { gi.sound(self, CHAN_WEAPON, sound_cock, 1, ATTN_NORM, 0); } } void soldier_stand(edict_t *self); mframe_t soldier_frames_stand1[] = { {ai_stand, 0, soldier_idle}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t soldier_move_stand1 = { FRAME_stand101, FRAME_stand130, soldier_frames_stand1, soldier_stand }; mframe_t soldier_frames_stand3[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, soldier_cock}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t soldier_move_stand3 = { FRAME_stand301, FRAME_stand339, soldier_frames_stand3, soldier_stand }; void soldier_stand(edict_t *self) { if (!self) { return; } if ((self->monsterinfo.currentmove == &soldier_move_stand3) || (random() < 0.8)) { self->monsterinfo.currentmove = &soldier_move_stand1; } else { self->monsterinfo.currentmove = &soldier_move_stand3; } } void soldier_walk1_random(edict_t *self) { if (!self) { return; } if (random() > 0.1) { self->monsterinfo.nextframe = FRAME_walk101; } } mframe_t soldier_frames_walk1[] = { {ai_walk, 3, NULL}, {ai_walk, 6, NULL}, {ai_walk, 2, NULL}, {ai_walk, 2, NULL}, {ai_walk, 2, NULL}, {ai_walk, 1, NULL}, {ai_walk, 6, NULL}, {ai_walk, 5, NULL}, {ai_walk, 3, NULL}, {ai_walk, -1, soldier_walk1_random}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL} }; mmove_t soldier_move_walk1 = { FRAME_walk101, FRAME_walk133, soldier_frames_walk1, NULL }; mframe_t soldier_frames_walk2[] = { {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 9, NULL}, {ai_walk, 8, NULL}, {ai_walk, 5, NULL}, {ai_walk, 1, NULL}, {ai_walk, 3, NULL}, {ai_walk, 7, NULL}, {ai_walk, 6, NULL}, {ai_walk, 7, NULL} }; mmove_t soldier_move_walk2 = { FRAME_walk209, FRAME_walk218, soldier_frames_walk2, NULL }; void soldier_walk(edict_t *self) { if (!self) { return; } if (random() < 0.5) { self->monsterinfo.currentmove = &soldier_move_walk1; } else { self->monsterinfo.currentmove = &soldier_move_walk2; } } void soldier_run(edict_t *self); mframe_t soldier_frames_start_run[] = { {ai_run, 7, NULL}, {ai_run, 5, NULL} }; mmove_t soldier_move_start_run = { FRAME_run01, FRAME_run02, soldier_frames_start_run, soldier_run }; mframe_t soldier_frames_run[] = { {ai_run, 10, NULL}, {ai_run, 11, NULL}, {ai_run, 11, NULL}, {ai_run, 16, NULL}, {ai_run, 10, NULL}, {ai_run, 15, NULL} }; mmove_t soldier_move_run = { FRAME_run03, FRAME_run08, soldier_frames_run, NULL }; void soldier_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &soldier_move_stand1; return; } if ((self->monsterinfo.currentmove == &soldier_move_walk1) || (self->monsterinfo.currentmove == &soldier_move_walk2) || (self->monsterinfo.currentmove == &soldier_move_start_run)) { self->monsterinfo.currentmove = &soldier_move_run; } else { self->monsterinfo.currentmove = &soldier_move_start_run; } } mframe_t soldier_frames_pain1[] = { {ai_move, -3, NULL}, {ai_move, 4, NULL}, {ai_move, 1, NULL}, {ai_move, 1, NULL}, {ai_move, 0, NULL} }; mmove_t soldier_move_pain1 = { FRAME_pain101, FRAME_pain105, soldier_frames_pain1, soldier_run }; mframe_t soldier_frames_pain2[] = { {ai_move, -13, NULL}, {ai_move, -1, NULL}, {ai_move, 2, NULL}, {ai_move, 4, NULL}, {ai_move, 2, NULL}, {ai_move, 3, NULL}, {ai_move, 2, NULL} }; mmove_t soldier_move_pain2 = { FRAME_pain201, FRAME_pain207, soldier_frames_pain2, soldier_run }; mframe_t soldier_frames_pain3[] = { {ai_move, -8, NULL}, {ai_move, 10, NULL}, {ai_move, -4, NULL}, {ai_move, -1, NULL}, {ai_move, -3, NULL}, {ai_move, 0, NULL}, {ai_move, 3, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 1, NULL}, {ai_move, 0, NULL}, {ai_move, 1, NULL}, {ai_move, 2, NULL}, {ai_move, 4, NULL}, {ai_move, 3, NULL}, {ai_move, 2, NULL} }; mmove_t soldier_move_pain3 = { FRAME_pain301, FRAME_pain318, soldier_frames_pain3, soldier_run }; mframe_t soldier_frames_pain4[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -10, NULL}, {ai_move, -6, NULL}, {ai_move, 8, NULL}, {ai_move, 4, NULL}, {ai_move, 1, NULL}, {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, 5, NULL}, {ai_move, 2, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, 3, NULL}, {ai_move, 2, NULL}, {ai_move, 0, NULL} }; mmove_t soldier_move_pain4 = { FRAME_pain401, FRAME_pain417, soldier_frames_pain4, soldier_run }; void soldier_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage /* unused */) { float r; int n; if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum |= 1; } if (level.time < self->pain_debounce_time) { if ((self->velocity[2] > 100) && ((self->monsterinfo.currentmove == &soldier_move_pain1) || (self->monsterinfo.currentmove == &soldier_move_pain2) || (self->monsterinfo.currentmove == &soldier_move_pain3))) { self->monsterinfo.currentmove = &soldier_move_pain4; } return; } self->pain_debounce_time = level.time + 3; n = self->s.skinnum | 1; if (n == 1) { gi.sound(self, CHAN_VOICE, sound_pain_light, 1, ATTN_NORM, 0); } else if (n == 3) { gi.sound(self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_VOICE, sound_pain_ss, 1, ATTN_NORM, 0); } if (self->velocity[2] > 100) { self->monsterinfo.currentmove = &soldier_move_pain4; return; } if (skill->value == 3) { return; /* no pain anims in nightmare */ } r = random(); if (r < 0.33) { self->monsterinfo.currentmove = &soldier_move_pain1; } else if (r < 0.66) { self->monsterinfo.currentmove = &soldier_move_pain2; } else { self->monsterinfo.currentmove = &soldier_move_pain3; } } static int blaster_flash[] = { MZ2_SOLDIER_BLASTER_1, MZ2_SOLDIER_BLASTER_2, MZ2_SOLDIER_BLASTER_3, MZ2_SOLDIER_BLASTER_4, MZ2_SOLDIER_BLASTER_5, MZ2_SOLDIER_BLASTER_6, MZ2_SOLDIER_BLASTER_7, MZ2_SOLDIER_BLASTER_8 }; static int shotgun_flash[] = { MZ2_SOLDIER_SHOTGUN_1, MZ2_SOLDIER_SHOTGUN_2, MZ2_SOLDIER_SHOTGUN_3, MZ2_SOLDIER_SHOTGUN_4, MZ2_SOLDIER_SHOTGUN_5, MZ2_SOLDIER_SHOTGUN_6, MZ2_SOLDIER_SHOTGUN_7, MZ2_SOLDIER_SHOTGUN_8 }; static int machinegun_flash[] = { MZ2_SOLDIER_MACHINEGUN_1, MZ2_SOLDIER_MACHINEGUN_2, MZ2_SOLDIER_MACHINEGUN_3, MZ2_SOLDIER_MACHINEGUN_4, MZ2_SOLDIER_MACHINEGUN_5, MZ2_SOLDIER_MACHINEGUN_6, MZ2_SOLDIER_MACHINEGUN_7, MZ2_SOLDIER_MACHINEGUN_8 }; void soldier_fire(edict_t *self, int flash_number) { vec3_t start; vec3_t forward, right, up; vec3_t aim; vec3_t dir; vec3_t end; float r, u; int flash_index; if (!self) { return; } if (self->s.skinnum < 2) { flash_index = blaster_flash[flash_number]; } else if (self->s.skinnum < 4) { flash_index = shotgun_flash[flash_number]; } else { flash_index = machinegun_flash[flash_number]; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[flash_index], forward, right, start); if ((flash_number == 5) || (flash_number == 6)) { VectorCopy(forward, aim); } else { VectorCopy(self->enemy->s.origin, end); end[2] += self->enemy->viewheight; VectorSubtract(end, start, aim); vectoangles(aim, dir); AngleVectors(dir, forward, right, up); r = crandom() * 1000; u = crandom() * 500; VectorMA(start, 8192, forward, end); VectorMA(end, r, right, end); VectorMA(end, u, up, end); VectorSubtract(end, start, aim); VectorNormalize(aim); } if (self->s.skinnum <= 1) { monster_fire_blaster(self, start, aim, 5, 600, flash_index, EF_BLASTER); } else if (self->s.skinnum <= 3) { monster_fire_shotgun(self, start, aim, 2, 1, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SHOTGUN_COUNT, flash_index); } else { if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) { self->monsterinfo.pausetime = level.time + (3 + randk() % 8) * FRAMETIME; } monster_fire_bullet(self, start, aim, 2, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_index); if (level.time >= self->monsterinfo.pausetime) { self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; } else { self->monsterinfo.aiflags |= AI_HOLD_FRAME; } } } /* ATTACK1 (blaster/shotgun) */ void soldier_fire1(edict_t *self) { if (!self) { return; } soldier_fire(self, 0); } void soldier_attack1_refire1(edict_t *self) { if (!self) { return; } if (self->s.skinnum > 1) { return; } if (self->enemy->health <= 0) { return; } if (((skill->value == 3) && (random() < 0.5)) || (range(self, self->enemy) == RANGE_MELEE)) { self->monsterinfo.nextframe = FRAME_attak102; } else { self->monsterinfo.nextframe = FRAME_attak110; } } void soldier_attack1_refire2(edict_t *self) { if (!self) { return; } if (self->s.skinnum < 2) { return; } if (self->enemy->health <= 0) { return; } if (((skill->value == 3) && (random() < 0.5)) || (range(self, self->enemy) == RANGE_MELEE)) { self->monsterinfo.nextframe = FRAME_attak102; } } mframe_t soldier_frames_attack1[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, soldier_fire1}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, soldier_attack1_refire1}, {ai_charge, 0, NULL}, {ai_charge, 0, soldier_cock}, {ai_charge, 0, soldier_attack1_refire2}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t soldier_move_attack1 = { FRAME_attak101, FRAME_attak112, soldier_frames_attack1, soldier_run }; /* ATTACK2 (blaster/shotgun) */ void soldier_fire2(edict_t *self) { if (!self) { return; } soldier_fire(self, 1); } void soldier_attack2_refire1(edict_t *self) { if (!self) { return; } if (self->s.skinnum > 1) { return; } if (self->enemy->health <= 0) { return; } if (((skill->value == 3) && (random() < 0.5)) || (range(self, self->enemy) == RANGE_MELEE)) { self->monsterinfo.nextframe = FRAME_attak204; } else { self->monsterinfo.nextframe = FRAME_attak216; } } void soldier_attack2_refire2(edict_t *self) { if (!self) { return; } if (self->s.skinnum < 2) { return; } if (self->enemy->health <= 0) { return; } if (((skill->value == 3) && (random() < 0.5)) || (range(self, self->enemy) == RANGE_MELEE)) { self->monsterinfo.nextframe = FRAME_attak204; } } mframe_t soldier_frames_attack2[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, soldier_fire2}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, soldier_attack2_refire1}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, soldier_cock}, {ai_charge, 0, NULL}, {ai_charge, 0, soldier_attack2_refire2}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t soldier_move_attack2 = { FRAME_attak201, FRAME_attak218, soldier_frames_attack2, soldier_run }; /* ATTACK3 (duck and shoot) */ void soldier_duck_down(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_DUCKED) { return; } self->monsterinfo.aiflags |= AI_DUCKED; self->maxs[2] -= 32; self->takedamage = DAMAGE_YES; self->monsterinfo.pausetime = level.time + 1; gi.linkentity(self); } void soldier_duck_up(edict_t *self) { if (!self) { return; } self->monsterinfo.aiflags &= ~AI_DUCKED; self->maxs[2] += 32; self->takedamage = DAMAGE_AIM; gi.linkentity(self); } void soldier_fire3(edict_t *self) { if (!self) { return; } soldier_duck_down(self); soldier_fire(self, 2); } void soldier_attack3_refire(edict_t *self) { if (!self) { return; } if ((level.time + 0.4) < self->monsterinfo.pausetime) { self->monsterinfo.nextframe = FRAME_attak303; } } mframe_t soldier_frames_attack3[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, soldier_fire3}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, soldier_attack3_refire}, {ai_charge, 0, soldier_duck_up}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t soldier_move_attack3 = { FRAME_attak301, FRAME_attak309, soldier_frames_attack3, soldier_run }; /* ATTACK4 (machinegun) */ void soldier_fire4(edict_t *self) { if (!self) { return; } soldier_fire(self, 3); } mframe_t soldier_frames_attack4[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, soldier_fire4}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t soldier_move_attack4 = { FRAME_attak401, FRAME_attak406, soldier_frames_attack4, soldier_run }; /* ATTACK6 (run & shoot) */ void soldier_fire8(edict_t *self) { if (!self) { return; } soldier_fire(self, 7); } void soldier_attack6_refire(edict_t *self) { if (!self) { return; } if (self->enemy->health <= 0) { return; } if (range(self, self->enemy) < RANGE_MID) { return; } if (skill->value == 3) { self->monsterinfo.nextframe = FRAME_runs03; } } mframe_t soldier_frames_attack6[] = { {ai_charge, 10, NULL}, {ai_charge, 4, NULL}, {ai_charge, 12, NULL}, {ai_charge, 11, soldier_fire8}, {ai_charge, 13, NULL}, {ai_charge, 18, NULL}, {ai_charge, 15, NULL}, {ai_charge, 14, NULL}, {ai_charge, 11, NULL}, {ai_charge, 8, NULL}, {ai_charge, 11, NULL}, {ai_charge, 12, NULL}, {ai_charge, 12, NULL}, {ai_charge, 17, soldier_attack6_refire} }; mmove_t soldier_move_attack6 = { FRAME_runs01, FRAME_runs14, soldier_frames_attack6, soldier_run }; void soldier_attack(edict_t *self) { if (!self) { return; } if (self->s.skinnum < 4) { if (random() < 0.5) { self->monsterinfo.currentmove = &soldier_move_attack1; } else { self->monsterinfo.currentmove = &soldier_move_attack2; } } else { self->monsterinfo.currentmove = &soldier_move_attack4; } } void soldier_sight(edict_t *self, edict_t *other /* unused */) { if (!self) { return; } if (random() < 0.5) { gi.sound(self, CHAN_VOICE, sound_sight1, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_VOICE, sound_sight2, 1, ATTN_NORM, 0); } if ((skill->value > 0) && (range(self, self->enemy) >= RANGE_MID)) { if (random() > 0.5) { self->monsterinfo.currentmove = &soldier_move_attack6; } } } void soldier_duck_hold(edict_t *self) { if (!self) { return; } if (level.time >= self->monsterinfo.pausetime) { self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; } else { self->monsterinfo.aiflags |= AI_HOLD_FRAME; } } mframe_t soldier_frames_duck[] = { {ai_move, 5, soldier_duck_down}, {ai_move, -1, soldier_duck_hold}, {ai_move, 1, NULL}, {ai_move, 0, soldier_duck_up}, {ai_move, 5, NULL} }; mmove_t soldier_move_duck = { FRAME_duck01, FRAME_duck05, soldier_frames_duck, soldier_run }; void soldier_dodge(edict_t *self, edict_t *attacker, float eta) { float r; if (!self || !attacker) { return; } r = random(); if (r > 0.25) { return; } if (!self->enemy) { self->enemy = attacker; } if (skill->value == 0) { self->monsterinfo.currentmove = &soldier_move_duck; return; } self->monsterinfo.pausetime = level.time + eta + 0.3; r = random(); if (skill->value == 1) { if (r > 0.33) { self->monsterinfo.currentmove = &soldier_move_duck; } else { self->monsterinfo.currentmove = &soldier_move_attack3; } return; } if (skill->value >= 2) { if (r > 0.66) { self->monsterinfo.currentmove = &soldier_move_duck; } else { self->monsterinfo.currentmove = &soldier_move_attack3; } return; } self->monsterinfo.currentmove = &soldier_move_attack3; } void soldier_fire6(edict_t *self) { if (!self) { return; } soldier_fire(self, 5); } void soldier_fire7(edict_t *self) { if (!self) { return; } soldier_fire(self, 6); } void soldier_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, -8); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } mframe_t soldier_frames_death1[] = { {ai_move, 0, NULL}, {ai_move, -10, NULL}, {ai_move, -10, NULL}, {ai_move, -10, NULL}, {ai_move, -5, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, soldier_fire6}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, soldier_fire7}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t soldier_move_death1 = { FRAME_death101, FRAME_death136, soldier_frames_death1, soldier_dead }; mframe_t soldier_frames_death2[] = { {ai_move, -5, NULL}, {ai_move, -5, NULL}, {ai_move, -5, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t soldier_move_death2 = { FRAME_death201, FRAME_death235, soldier_frames_death2, soldier_dead }; mframe_t soldier_frames_death3[] = { {ai_move, -5, NULL}, {ai_move, -5, NULL}, {ai_move, -5, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, }; mmove_t soldier_move_death3 = { FRAME_death301, FRAME_death345, soldier_frames_death3, soldier_dead }; mframe_t soldier_frames_death4[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t soldier_move_death4 = { FRAME_death401, FRAME_death453, soldier_frames_death4, soldier_dead }; mframe_t soldier_frames_death5[] = { {ai_move, -5, NULL}, {ai_move, -5, NULL}, {ai_move, -5, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t soldier_move_death5 = { FRAME_death501, FRAME_death524, soldier_frames_death5, soldier_dead }; mframe_t soldier_frames_death6[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t soldier_move_death6 = { FRAME_death601, FRAME_death610, soldier_frames_death6, soldier_dead }; void soldier_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage, vec3_t point /* unused */) { int n; /* check for gib */ if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 3; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowGib(self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC); ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } /* regular death */ self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; self->s.skinnum |= 1; if (self->s.skinnum == 1) { gi.sound(self, CHAN_VOICE, sound_death_light, 1, ATTN_NORM, 0); } else if (self->s.skinnum == 3) { gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_VOICE, sound_death_ss, 1, ATTN_NORM, 0); } if (fabs((self->s.origin[2] + self->viewheight) - point[2]) <= 4) { /* head shot */ self->monsterinfo.currentmove = &soldier_move_death3; return; } n = randk() % 5; if (n == 0) { self->monsterinfo.currentmove = &soldier_move_death1; } else if (n == 1) { self->monsterinfo.currentmove = &soldier_move_death2; } else if (n == 2) { self->monsterinfo.currentmove = &soldier_move_death4; } else if (n == 3) { self->monsterinfo.currentmove = &soldier_move_death5; } else { self->monsterinfo.currentmove = &soldier_move_death6; } } void SP_monster_soldier_x(edict_t *self) { if (!self) { return; } self->s.modelindex = gi.modelindex("models/monsters/soldier/tris.md2"); self->monsterinfo.scale = MODEL_SCALE; VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, 32); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; sound_idle = gi.soundindex("soldier/solidle1.wav"); sound_sight1 = gi.soundindex("soldier/solsght1.wav"); sound_sight2 = gi.soundindex("soldier/solsrch1.wav"); sound_cock = gi.soundindex("infantry/infatck3.wav"); self->mass = 100; self->pain = soldier_pain; self->die = soldier_die; self->monsterinfo.stand = soldier_stand; self->monsterinfo.walk = soldier_walk; self->monsterinfo.run = soldier_run; self->monsterinfo.dodge = soldier_dodge; self->monsterinfo.attack = soldier_attack; self->monsterinfo.melee = NULL; self->monsterinfo.sight = soldier_sight; gi.linkentity(self); self->monsterinfo.stand(self); walkmonster_start(self); } /* * QUAKED monster_soldier_light (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight */ void SP_monster_soldier_light(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } SP_monster_soldier_x(self); sound_pain_light = gi.soundindex("soldier/solpain2.wav"); sound_death_light = gi.soundindex("soldier/soldeth2.wav"); gi.modelindex("models/objects/laser/tris.md2"); gi.soundindex("misc/lasfly.wav"); gi.soundindex("soldier/solatck2.wav"); self->s.skinnum = 0; self->health = 20; self->gib_health = -30; } /* * QUAKED monster_soldier (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight */ void SP_monster_soldier(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } SP_monster_soldier_x(self); sound_pain = gi.soundindex("soldier/solpain1.wav"); sound_death = gi.soundindex("soldier/soldeth1.wav"); gi.soundindex("soldier/solatck1.wav"); self->s.skinnum = 2; self->health = 30; self->gib_health = -30; } /* * QUAKED monster_soldier_ss (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight */ void SP_monster_soldier_ss(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } SP_monster_soldier_x(self); sound_pain_ss = gi.soundindex("soldier/solpain3.wav"); sound_death_ss = gi.soundindex("soldier/soldeth3.wav"); gi.soundindex("soldier/solatck3.wav"); self->s.skinnum = 4; self->health = 40; self->gib_health = -30; } yquake2-QUAKE2_5_32/src/game/monster/gunner/0000755000175000017500000000000012615162034021304 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/gunner/gunner.c0000644000175000017500000003742512615162034022761 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Gunner. * * ======================================================================= */ #include "../../header/local.h" #include "gunner.h" static int sound_pain; static int sound_pain2; static int sound_death; static int sound_idle; static int sound_open; static int sound_search; static int sound_sight; void gunner_idlesound(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0); } void gunner_sight(edict_t *self, edict_t *other /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); } void gunner_search(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0); } qboolean visible(edict_t *self, edict_t *other); void GunnerGrenade(edict_t *self); void GunnerFire(edict_t *self); void gunner_fire_chain(edict_t *self); void gunner_refire_chain(edict_t *self); void gunner_stand(edict_t *self); mframe_t gunner_frames_fidget[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, gunner_idlesound}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t gunner_move_fidget = { FRAME_stand31, FRAME_stand70, gunner_frames_fidget, gunner_stand }; void gunner_fidget(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { return; } if (random() <= 0.05) { self->monsterinfo.currentmove = &gunner_move_fidget; } } mframe_t gunner_frames_stand[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, gunner_fidget}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, gunner_fidget}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, gunner_fidget} }; mmove_t gunner_move_stand = { FRAME_stand01, FRAME_stand30, gunner_frames_stand, NULL }; void gunner_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &gunner_move_stand; } mframe_t gunner_frames_walk[] = { {ai_walk, 0, NULL}, {ai_walk, 3, NULL}, {ai_walk, 4, NULL}, {ai_walk, 5, NULL}, {ai_walk, 7, NULL}, {ai_walk, 2, NULL}, {ai_walk, 6, NULL}, {ai_walk, 4, NULL}, {ai_walk, 2, NULL}, {ai_walk, 7, NULL}, {ai_walk, 5, NULL}, {ai_walk, 7, NULL}, {ai_walk, 4, NULL} }; mmove_t gunner_move_walk = { FRAME_walk07, FRAME_walk19, gunner_frames_walk, NULL }; void gunner_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &gunner_move_walk; } mframe_t gunner_frames_run[] = { {ai_run, 26, NULL}, {ai_run, 9, NULL}, {ai_run, 9, NULL}, {ai_run, 9, NULL}, {ai_run, 15, NULL}, {ai_run, 10, NULL}, {ai_run, 13, NULL}, {ai_run, 6, NULL} }; mmove_t gunner_move_run = { FRAME_run01, FRAME_run08, gunner_frames_run, NULL }; void gunner_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &gunner_move_stand; } else { self->monsterinfo.currentmove = &gunner_move_run; } } mframe_t gunner_frames_runandshoot[] = { {ai_run, 32, NULL}, {ai_run, 15, NULL}, {ai_run, 10, NULL}, {ai_run, 18, NULL}, {ai_run, 8, NULL}, {ai_run, 20, NULL} }; mmove_t gunner_move_runandshoot = { FRAME_runs01, FRAME_runs06, gunner_frames_runandshoot, NULL }; void gunner_runandshoot(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &gunner_move_runandshoot; } mframe_t gunner_frames_pain3[] = { {ai_move, -3, NULL}, {ai_move, 1, NULL}, {ai_move, 1, NULL}, {ai_move, 0, NULL}, {ai_move, 1, NULL} }; mmove_t gunner_move_pain3 = { FRAME_pain301, FRAME_pain305, gunner_frames_pain3, gunner_run }; mframe_t gunner_frames_pain2[] = { {ai_move, -2, NULL}, {ai_move, 11, NULL}, {ai_move, 6, NULL}, {ai_move, 2, NULL}, {ai_move, -1, NULL}, {ai_move, -7, NULL}, {ai_move, -2, NULL}, {ai_move, -7, NULL} }; mmove_t gunner_move_pain2 = { FRAME_pain201, FRAME_pain208, gunner_frames_pain2, gunner_run }; mframe_t gunner_frames_pain1[] = { {ai_move, 2, NULL}, {ai_move, 0, NULL}, {ai_move, -5, NULL}, {ai_move, 3, NULL}, {ai_move, -1, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 1, NULL}, {ai_move, 1, NULL}, {ai_move, 2, NULL}, {ai_move, 1, NULL}, {ai_move, 0, NULL}, {ai_move, -2, NULL}, {ai_move, -2, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t gunner_move_pain1 = { FRAME_pain101, FRAME_pain118, gunner_frames_pain1, gunner_run }; void gunner_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage) { if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum |= 1; } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; if (randk() & 1) { gi.sound(self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); } if (skill->value == 3) { return; /* no pain anims in nightmare */ } if (damage <= 10) { self->monsterinfo.currentmove = &gunner_move_pain3; } else if (damage <= 25) { self->monsterinfo.currentmove = &gunner_move_pain2; } else { self->monsterinfo.currentmove = &gunner_move_pain1; } } void gunner_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, -8); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } mframe_t gunner_frames_death[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -7, NULL}, {ai_move, -3, NULL}, {ai_move, -5, NULL}, {ai_move, 8, NULL}, {ai_move, 6, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t gunner_move_death = { FRAME_death01, FRAME_death11, gunner_frames_death, gunner_dead }; void gunner_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage /* unused */, vec3_t point) { int n; if (!self) { return; } self->s.skinnum |= 1; self->monsterinfo.power_armor_type = POWER_ARMOR_NONE; /* check for gib */ if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex( "misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 2; n++) { ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } /* regular death */ gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0); self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; self->monsterinfo.currentmove = &gunner_move_death; } void gunner_duck_down(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_DUCKED) { return; } self->monsterinfo.aiflags |= AI_DUCKED; if (skill->value >= 2) { if (random() > 0.5) { GunnerGrenade(self); } } self->maxs[2] -= 32; self->takedamage = DAMAGE_YES; self->monsterinfo.pausetime = level.time + 1; gi.linkentity(self); } void gunner_duck_hold(edict_t *self) { if (!self) { return; } if (level.time >= self->monsterinfo.pausetime) { self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; } else { self->monsterinfo.aiflags |= AI_HOLD_FRAME; } } void gunner_duck_up(edict_t *self) { if (!self) { return; } self->monsterinfo.aiflags &= ~AI_DUCKED; self->maxs[2] += 32; self->takedamage = DAMAGE_AIM; gi.linkentity(self); } mframe_t gunner_frames_duck[] = { {ai_move, 1, gunner_duck_down}, {ai_move, 1, NULL}, {ai_move, 1, gunner_duck_hold}, {ai_move, 0, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, 0, gunner_duck_up}, {ai_move, -1, NULL} }; mmove_t gunner_move_duck = { FRAME_duck01, FRAME_duck08, gunner_frames_duck, gunner_run }; void gunner_dodge(edict_t *self, edict_t *attacker, float eta /* unused */) { if (!self || !attacker) { return; } if (random() > 0.25) { return; } if (!self->enemy) { self->enemy = attacker; } self->monsterinfo.currentmove = &gunner_move_duck; } void gunner_opengun(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_open, 1, ATTN_IDLE, 0); } void GunnerFire(edict_t *self) { vec3_t start; vec3_t forward, right; vec3_t target; vec3_t aim; int flash_number; if (!self) { return; } flash_number = MZ2_GUNNER_MACHINEGUN_1 + (self->s.frame - FRAME_attak216); AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start); /* project enemy back a bit and target there */ VectorCopy(self->enemy->s.origin, target); VectorMA(target, -0.2, self->enemy->velocity, target); target[2] += self->enemy->viewheight; VectorSubtract(target, start, aim); VectorNormalize(aim); monster_fire_bullet(self, start, aim, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number); } void GunnerGrenade(edict_t *self) { vec3_t start; vec3_t forward, right; vec3_t aim; int flash_number; if (!self) { return; } if (self->s.frame == FRAME_attak105) { flash_number = MZ2_GUNNER_GRENADE_1; } else if (self->s.frame == FRAME_attak108) { flash_number = MZ2_GUNNER_GRENADE_2; } else if (self->s.frame == FRAME_attak111) { flash_number = MZ2_GUNNER_GRENADE_3; } else { flash_number = MZ2_GUNNER_GRENADE_4; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start); VectorCopy(forward, aim); monster_fire_grenade(self, start, aim, 50, 600, flash_number); } mframe_t gunner_frames_attack_chain[] = { {ai_charge, 0, gunner_opengun}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t gunner_move_attack_chain = { FRAME_attak209, FRAME_attak215, gunner_frames_attack_chain, gunner_fire_chain }; mframe_t gunner_frames_fire_chain[] = { {ai_charge, 0, GunnerFire}, {ai_charge, 0, GunnerFire}, {ai_charge, 0, GunnerFire}, {ai_charge, 0, GunnerFire}, {ai_charge, 0, GunnerFire}, {ai_charge, 0, GunnerFire}, {ai_charge, 0, GunnerFire}, {ai_charge, 0, GunnerFire} }; mmove_t gunner_move_fire_chain = { FRAME_attak216, FRAME_attak223, gunner_frames_fire_chain, gunner_refire_chain }; mframe_t gunner_frames_endfire_chain[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t gunner_move_endfire_chain = { FRAME_attak224, FRAME_attak230, gunner_frames_endfire_chain, gunner_run }; mframe_t gunner_frames_attack_grenade[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, GunnerGrenade}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, GunnerGrenade}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, GunnerGrenade}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, GunnerGrenade}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t gunner_move_attack_grenade = { FRAME_attak101, FRAME_attak121, gunner_frames_attack_grenade, gunner_run }; void gunner_attack(edict_t *self) { if (!self) { return; } if (range(self, self->enemy) == RANGE_MELEE) { self->monsterinfo.currentmove = &gunner_move_attack_chain; } else { if (random() <= 0.5) { self->monsterinfo.currentmove = &gunner_move_attack_grenade; } else { self->monsterinfo.currentmove = &gunner_move_attack_chain; } } } void gunner_fire_chain(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &gunner_move_fire_chain; } void gunner_refire_chain(edict_t *self) { if (!self) { return; } if (self->enemy->health > 0) { if (visible(self, self->enemy)) { if (random() <= 0.5) { self->monsterinfo.currentmove = &gunner_move_fire_chain; return; } } } self->monsterinfo.currentmove = &gunner_move_endfire_chain; } /* * QUAKED monster_gunner (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight */ void SP_monster_gunner(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_death = gi.soundindex("gunner/death1.wav"); sound_pain = gi.soundindex("gunner/gunpain2.wav"); sound_pain2 = gi.soundindex("gunner/gunpain1.wav"); sound_idle = gi.soundindex("gunner/gunidle1.wav"); sound_open = gi.soundindex("gunner/gunatck1.wav"); sound_search = gi.soundindex("gunner/gunsrch1.wav"); sound_sight = gi.soundindex("gunner/sight1.wav"); gi.soundindex("gunner/gunatck2.wav"); gi.soundindex("gunner/gunatck3.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/gunner/tris.md2"); VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, 32); self->health = 175; self->gib_health = -70; self->mass = 200; self->pain = gunner_pain; self->die = gunner_die; self->monsterinfo.stand = gunner_stand; self->monsterinfo.walk = gunner_walk; self->monsterinfo.run = gunner_run; self->monsterinfo.dodge = gunner_dodge; self->monsterinfo.attack = gunner_attack; self->monsterinfo.melee = NULL; self->monsterinfo.sight = gunner_sight; self->monsterinfo.search = gunner_search; gi.linkentity(self); self->monsterinfo.currentmove = &gunner_move_stand; self->monsterinfo.scale = MODEL_SCALE; walkmonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/gunner/gunner.h0000644000175000017500000001424312615162034022757 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Gunner animations. * * ======================================================================= */ #define FRAME_stand01 0 #define FRAME_stand02 1 #define FRAME_stand03 2 #define FRAME_stand04 3 #define FRAME_stand05 4 #define FRAME_stand06 5 #define FRAME_stand07 6 #define FRAME_stand08 7 #define FRAME_stand09 8 #define FRAME_stand10 9 #define FRAME_stand11 10 #define FRAME_stand12 11 #define FRAME_stand13 12 #define FRAME_stand14 13 #define FRAME_stand15 14 #define FRAME_stand16 15 #define FRAME_stand17 16 #define FRAME_stand18 17 #define FRAME_stand19 18 #define FRAME_stand20 19 #define FRAME_stand21 20 #define FRAME_stand22 21 #define FRAME_stand23 22 #define FRAME_stand24 23 #define FRAME_stand25 24 #define FRAME_stand26 25 #define FRAME_stand27 26 #define FRAME_stand28 27 #define FRAME_stand29 28 #define FRAME_stand30 29 #define FRAME_stand31 30 #define FRAME_stand32 31 #define FRAME_stand33 32 #define FRAME_stand34 33 #define FRAME_stand35 34 #define FRAME_stand36 35 #define FRAME_stand37 36 #define FRAME_stand38 37 #define FRAME_stand39 38 #define FRAME_stand40 39 #define FRAME_stand41 40 #define FRAME_stand42 41 #define FRAME_stand43 42 #define FRAME_stand44 43 #define FRAME_stand45 44 #define FRAME_stand46 45 #define FRAME_stand47 46 #define FRAME_stand48 47 #define FRAME_stand49 48 #define FRAME_stand50 49 #define FRAME_stand51 50 #define FRAME_stand52 51 #define FRAME_stand53 52 #define FRAME_stand54 53 #define FRAME_stand55 54 #define FRAME_stand56 55 #define FRAME_stand57 56 #define FRAME_stand58 57 #define FRAME_stand59 58 #define FRAME_stand60 59 #define FRAME_stand61 60 #define FRAME_stand62 61 #define FRAME_stand63 62 #define FRAME_stand64 63 #define FRAME_stand65 64 #define FRAME_stand66 65 #define FRAME_stand67 66 #define FRAME_stand68 67 #define FRAME_stand69 68 #define FRAME_stand70 69 #define FRAME_walk01 70 #define FRAME_walk02 71 #define FRAME_walk03 72 #define FRAME_walk04 73 #define FRAME_walk05 74 #define FRAME_walk06 75 #define FRAME_walk07 76 #define FRAME_walk08 77 #define FRAME_walk09 78 #define FRAME_walk10 79 #define FRAME_walk11 80 #define FRAME_walk12 81 #define FRAME_walk13 82 #define FRAME_walk14 83 #define FRAME_walk15 84 #define FRAME_walk16 85 #define FRAME_walk17 86 #define FRAME_walk18 87 #define FRAME_walk19 88 #define FRAME_walk20 89 #define FRAME_walk21 90 #define FRAME_walk22 91 #define FRAME_walk23 92 #define FRAME_walk24 93 #define FRAME_run01 94 #define FRAME_run02 95 #define FRAME_run03 96 #define FRAME_run04 97 #define FRAME_run05 98 #define FRAME_run06 99 #define FRAME_run07 100 #define FRAME_run08 101 #define FRAME_runs01 102 #define FRAME_runs02 103 #define FRAME_runs03 104 #define FRAME_runs04 105 #define FRAME_runs05 106 #define FRAME_runs06 107 #define FRAME_attak101 108 #define FRAME_attak102 109 #define FRAME_attak103 110 #define FRAME_attak104 111 #define FRAME_attak105 112 #define FRAME_attak106 113 #define FRAME_attak107 114 #define FRAME_attak108 115 #define FRAME_attak109 116 #define FRAME_attak110 117 #define FRAME_attak111 118 #define FRAME_attak112 119 #define FRAME_attak113 120 #define FRAME_attak114 121 #define FRAME_attak115 122 #define FRAME_attak116 123 #define FRAME_attak117 124 #define FRAME_attak118 125 #define FRAME_attak119 126 #define FRAME_attak120 127 #define FRAME_attak121 128 #define FRAME_attak201 129 #define FRAME_attak202 130 #define FRAME_attak203 131 #define FRAME_attak204 132 #define FRAME_attak205 133 #define FRAME_attak206 134 #define FRAME_attak207 135 #define FRAME_attak208 136 #define FRAME_attak209 137 #define FRAME_attak210 138 #define FRAME_attak211 139 #define FRAME_attak212 140 #define FRAME_attak213 141 #define FRAME_attak214 142 #define FRAME_attak215 143 #define FRAME_attak216 144 #define FRAME_attak217 145 #define FRAME_attak218 146 #define FRAME_attak219 147 #define FRAME_attak220 148 #define FRAME_attak221 149 #define FRAME_attak222 150 #define FRAME_attak223 151 #define FRAME_attak224 152 #define FRAME_attak225 153 #define FRAME_attak226 154 #define FRAME_attak227 155 #define FRAME_attak228 156 #define FRAME_attak229 157 #define FRAME_attak230 158 #define FRAME_pain101 159 #define FRAME_pain102 160 #define FRAME_pain103 161 #define FRAME_pain104 162 #define FRAME_pain105 163 #define FRAME_pain106 164 #define FRAME_pain107 165 #define FRAME_pain108 166 #define FRAME_pain109 167 #define FRAME_pain110 168 #define FRAME_pain111 169 #define FRAME_pain112 170 #define FRAME_pain113 171 #define FRAME_pain114 172 #define FRAME_pain115 173 #define FRAME_pain116 174 #define FRAME_pain117 175 #define FRAME_pain118 176 #define FRAME_pain201 177 #define FRAME_pain202 178 #define FRAME_pain203 179 #define FRAME_pain204 180 #define FRAME_pain205 181 #define FRAME_pain206 182 #define FRAME_pain207 183 #define FRAME_pain208 184 #define FRAME_pain301 185 #define FRAME_pain302 186 #define FRAME_pain303 187 #define FRAME_pain304 188 #define FRAME_pain305 189 #define FRAME_death01 190 #define FRAME_death02 191 #define FRAME_death03 192 #define FRAME_death04 193 #define FRAME_death05 194 #define FRAME_death06 195 #define FRAME_death07 196 #define FRAME_death08 197 #define FRAME_death09 198 #define FRAME_death10 199 #define FRAME_death11 200 #define FRAME_duck01 201 #define FRAME_duck02 202 #define FRAME_duck03 203 #define FRAME_duck04 204 #define FRAME_duck05 205 #define FRAME_duck06 206 #define FRAME_duck07 207 #define FRAME_duck08 208 #define MODEL_SCALE 1.150000 yquake2-QUAKE2_5_32/src/game/monster/flyer/0000755000175000017500000000000012615162034021127 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/flyer/flyer.h0000644000175000017500000001153512615162034022426 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Flyer animations. * * ======================================================================= */ #define ACTION_nothing 0 #define ACTION_attack1 1 #define ACTION_attack2 2 #define ACTION_run 3 #define ACTION_walk 4 #define FRAME_start01 0 #define FRAME_start02 1 #define FRAME_start03 2 #define FRAME_start04 3 #define FRAME_start05 4 #define FRAME_start06 5 #define FRAME_stop01 6 #define FRAME_stop02 7 #define FRAME_stop03 8 #define FRAME_stop04 9 #define FRAME_stop05 10 #define FRAME_stop06 11 #define FRAME_stop07 12 #define FRAME_stand01 13 #define FRAME_stand02 14 #define FRAME_stand03 15 #define FRAME_stand04 16 #define FRAME_stand05 17 #define FRAME_stand06 18 #define FRAME_stand07 19 #define FRAME_stand08 20 #define FRAME_stand09 21 #define FRAME_stand10 22 #define FRAME_stand11 23 #define FRAME_stand12 24 #define FRAME_stand13 25 #define FRAME_stand14 26 #define FRAME_stand15 27 #define FRAME_stand16 28 #define FRAME_stand17 29 #define FRAME_stand18 30 #define FRAME_stand19 31 #define FRAME_stand20 32 #define FRAME_stand21 33 #define FRAME_stand22 34 #define FRAME_stand23 35 #define FRAME_stand24 36 #define FRAME_stand25 37 #define FRAME_stand26 38 #define FRAME_stand27 39 #define FRAME_stand28 40 #define FRAME_stand29 41 #define FRAME_stand30 42 #define FRAME_stand31 43 #define FRAME_stand32 44 #define FRAME_stand33 45 #define FRAME_stand34 46 #define FRAME_stand35 47 #define FRAME_stand36 48 #define FRAME_stand37 49 #define FRAME_stand38 50 #define FRAME_stand39 51 #define FRAME_stand40 52 #define FRAME_stand41 53 #define FRAME_stand42 54 #define FRAME_stand43 55 #define FRAME_stand44 56 #define FRAME_stand45 57 #define FRAME_attak101 58 #define FRAME_attak102 59 #define FRAME_attak103 60 #define FRAME_attak104 61 #define FRAME_attak105 62 #define FRAME_attak106 63 #define FRAME_attak107 64 #define FRAME_attak108 65 #define FRAME_attak109 66 #define FRAME_attak110 67 #define FRAME_attak111 68 #define FRAME_attak112 69 #define FRAME_attak113 70 #define FRAME_attak114 71 #define FRAME_attak115 72 #define FRAME_attak116 73 #define FRAME_attak117 74 #define FRAME_attak118 75 #define FRAME_attak119 76 #define FRAME_attak120 77 #define FRAME_attak121 78 #define FRAME_attak201 79 #define FRAME_attak202 80 #define FRAME_attak203 81 #define FRAME_attak204 82 #define FRAME_attak205 83 #define FRAME_attak206 84 #define FRAME_attak207 85 #define FRAME_attak208 86 #define FRAME_attak209 87 #define FRAME_attak210 88 #define FRAME_attak211 89 #define FRAME_attak212 90 #define FRAME_attak213 91 #define FRAME_attak214 92 #define FRAME_attak215 93 #define FRAME_attak216 94 #define FRAME_attak217 95 #define FRAME_bankl01 96 #define FRAME_bankl02 97 #define FRAME_bankl03 98 #define FRAME_bankl04 99 #define FRAME_bankl05 100 #define FRAME_bankl06 101 #define FRAME_bankl07 102 #define FRAME_bankr01 103 #define FRAME_bankr02 104 #define FRAME_bankr03 105 #define FRAME_bankr04 106 #define FRAME_bankr05 107 #define FRAME_bankr06 108 #define FRAME_bankr07 109 #define FRAME_rollf01 110 #define FRAME_rollf02 111 #define FRAME_rollf03 112 #define FRAME_rollf04 113 #define FRAME_rollf05 114 #define FRAME_rollf06 115 #define FRAME_rollf07 116 #define FRAME_rollf08 117 #define FRAME_rollf09 118 #define FRAME_rollr01 119 #define FRAME_rollr02 120 #define FRAME_rollr03 121 #define FRAME_rollr04 122 #define FRAME_rollr05 123 #define FRAME_rollr06 124 #define FRAME_rollr07 125 #define FRAME_rollr08 126 #define FRAME_rollr09 127 #define FRAME_defens01 128 #define FRAME_defens02 129 #define FRAME_defens03 130 #define FRAME_defens04 131 #define FRAME_defens05 132 #define FRAME_defens06 133 #define FRAME_pain101 134 #define FRAME_pain102 135 #define FRAME_pain103 136 #define FRAME_pain104 137 #define FRAME_pain105 138 #define FRAME_pain106 139 #define FRAME_pain107 140 #define FRAME_pain108 141 #define FRAME_pain109 142 #define FRAME_pain201 143 #define FRAME_pain202 144 #define FRAME_pain203 145 #define FRAME_pain204 146 #define FRAME_pain301 147 #define FRAME_pain302 148 #define FRAME_pain303 149 #define FRAME_pain304 150 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/flyer/flyer.c0000644000175000017500000003657412615162034022433 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Flyer. * * ======================================================================= */ #include "../../header/local.h" #include "flyer.h" qboolean visible(edict_t *self, edict_t *other); static int nextmove; /* Used for start/stop frames */ static int sound_sight; static int sound_idle; static int sound_pain1; static int sound_pain2; static int sound_slash; static int sound_sproing; static int sound_die; void flyer_check_melee(edict_t *self); void flyer_loop_melee(edict_t *self); void flyer_melee(edict_t *self); void flyer_setstart(edict_t *self); void flyer_stand(edict_t *self); void flyer_nextmove(edict_t *self); void flyer_sight(edict_t *self, edict_t *other /* other */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); } void flyer_idle(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0); } void flyer_pop_blades(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_sproing, 1, ATTN_NORM, 0); } mframe_t flyer_frames_stand[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t flyer_move_stand = { FRAME_stand01, FRAME_stand45, flyer_frames_stand, NULL }; mframe_t flyer_frames_walk[] = { {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL} }; mmove_t flyer_move_walk = { FRAME_stand01, FRAME_stand45, flyer_frames_walk, NULL }; mframe_t flyer_frames_run[] = { {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL} }; mmove_t flyer_move_run = { FRAME_stand01, FRAME_stand45, flyer_frames_run, NULL }; void flyer_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &flyer_move_stand; } else { self->monsterinfo.currentmove = &flyer_move_run; } } void flyer_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &flyer_move_walk; } void flyer_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &flyer_move_stand; } mframe_t flyer_frames_start[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, flyer_nextmove} }; mmove_t flyer_move_start = { FRAME_start01, FRAME_start06, flyer_frames_start, NULL }; mframe_t flyer_frames_stop[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, flyer_nextmove} }; mmove_t flyer_move_stop = { FRAME_stop01, FRAME_stop07, flyer_frames_stop, NULL }; void flyer_stop(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &flyer_move_stop; } void flyer_start(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &flyer_move_start; } mframe_t flyer_frames_rollright[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t flyer_move_rollright = { FRAME_rollr01, FRAME_rollr09, flyer_frames_rollright, NULL }; mframe_t flyer_frames_rollleft[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t flyer_move_rollleft = { FRAME_rollf01, FRAME_rollf09, flyer_frames_rollleft, NULL }; mframe_t flyer_frames_pain3[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t flyer_move_pain3 = { FRAME_pain301, FRAME_pain304, flyer_frames_pain3, flyer_run }; mframe_t flyer_frames_pain2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t flyer_move_pain2 = { FRAME_pain201, FRAME_pain204, flyer_frames_pain2, flyer_run }; mframe_t flyer_frames_pain1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t flyer_move_pain1 = { FRAME_pain101, FRAME_pain109, flyer_frames_pain1, flyer_run }; mframe_t flyer_frames_defense[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* Hold this frame */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t flyer_move_defense = { FRAME_defens01, FRAME_defens06, flyer_frames_defense, NULL }; mframe_t flyer_frames_bankright[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t flyer_move_bankright = { FRAME_bankr01, FRAME_bankr07, flyer_frames_bankright, NULL }; mframe_t flyer_frames_bankleft[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t flyer_move_bankleft = { FRAME_bankl01, FRAME_bankl07, flyer_frames_bankleft, NULL }; void flyer_fire(edict_t *self, int flash_number) { vec3_t start; vec3_t forward, right; vec3_t end; vec3_t dir; int effect; if (!self) { return; } if ((self->s.frame == FRAME_attak204) || (self->s.frame == FRAME_attak207) || (self->s.frame == FRAME_attak210)) { effect = EF_HYPERBLASTER; } else { effect = 0; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start); VectorCopy(self->enemy->s.origin, end); end[2] += self->enemy->viewheight; VectorSubtract(end, start, dir); monster_fire_blaster(self, start, dir, 1, 1000, flash_number, effect); } void flyer_fireleft(edict_t *self) { if (!self) { return; } flyer_fire(self, MZ2_FLYER_BLASTER_1); } void flyer_fireright(edict_t *self) { if (!self) { return; } flyer_fire(self, MZ2_FLYER_BLASTER_2); } mframe_t flyer_frames_attack2[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, -10, flyer_fireleft}, /* left gun */ {ai_charge, -10, flyer_fireright}, /* right gun */ {ai_charge, -10, flyer_fireleft}, /* left gun */ {ai_charge, -10, flyer_fireright}, /* right gun */ {ai_charge, -10, flyer_fireleft}, /* left gun */ {ai_charge, -10, flyer_fireright}, /* right gun */ {ai_charge, -10, flyer_fireleft}, /* left gun */ {ai_charge, -10, flyer_fireright}, /* right gun */ {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t flyer_move_attack2 = { FRAME_attak201, FRAME_attak217, flyer_frames_attack2, flyer_run }; void flyer_slash_left(edict_t *self) { vec3_t aim; if (!self) { return; } VectorSet(aim, MELEE_DISTANCE, self->mins[0], 0); fire_hit(self, aim, 5, 0); gi.sound(self, CHAN_WEAPON, sound_slash, 1, ATTN_NORM, 0); } void flyer_slash_right(edict_t *self) { vec3_t aim; if (!self) { return; } VectorSet(aim, MELEE_DISTANCE, self->maxs[0], 0); fire_hit(self, aim, 5, 0); gi.sound(self, CHAN_WEAPON, sound_slash, 1, ATTN_NORM, 0); } mframe_t flyer_frames_start_melee[] = { {ai_charge, 0, flyer_pop_blades}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t flyer_move_start_melee = { FRAME_attak101, FRAME_attak106, flyer_frames_start_melee, flyer_loop_melee }; mframe_t flyer_frames_end_melee[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t flyer_move_end_melee = { FRAME_attak119, FRAME_attak121, flyer_frames_end_melee, flyer_run }; mframe_t flyer_frames_loop_melee[] = { {ai_charge, 0, NULL}, /* Loop Start */ {ai_charge, 0, NULL}, {ai_charge, 0, flyer_slash_left}, /* Left Wing Strike */ {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, flyer_slash_right}, /* Right Wing Strike */ {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} /* Loop Ends */ }; mmove_t flyer_move_loop_melee = { FRAME_attak107, FRAME_attak118, flyer_frames_loop_melee, flyer_check_melee }; void flyer_loop_melee(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &flyer_move_loop_melee; } void flyer_attack(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &flyer_move_attack2; } void flyer_setstart(edict_t *self) { if (!self) { return; } nextmove = ACTION_run; self->monsterinfo.currentmove = &flyer_move_start; } void flyer_nextmove(edict_t *self) { if (!self) { return; } if (nextmove == ACTION_attack1) { self->monsterinfo.currentmove = &flyer_move_start_melee; } else if (nextmove == ACTION_attack2) { self->monsterinfo.currentmove = &flyer_move_attack2; } else if (nextmove == ACTION_run) { self->monsterinfo.currentmove = &flyer_move_run; } } void flyer_melee(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &flyer_move_start_melee; } void flyer_check_melee(edict_t *self) { if (!self) { return; } if (range(self, self->enemy) == RANGE_MELEE) { if (random() <= 0.8) { self->monsterinfo.currentmove = &flyer_move_loop_melee; } else { self->monsterinfo.currentmove = &flyer_move_end_melee; } } else { self->monsterinfo.currentmove = &flyer_move_end_melee; } } void flyer_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage) { int n; if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; if (skill->value == 3) { return; /* no pain anims in nightmare */ } n = randk() % 3; if (n == 0) { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &flyer_move_pain1; } else if (n == 1) { gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &flyer_move_pain2; } else { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &flyer_move_pain3; } } void flyer_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage /* unused */, vec3_t point /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0); BecomeExplosion1(self); } /* * QUAKED monster_flyer (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight */ void SP_monster_flyer(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } /* fix a map bug in jail5.bsp */ if (!Q_stricmp(level.mapname, "jail5") && (self->s.origin[2] == -104)) { self->targetname = self->target; self->target = NULL; } sound_sight = gi.soundindex("flyer/flysght1.wav"); sound_idle = gi.soundindex("flyer/flysrch1.wav"); sound_pain1 = gi.soundindex("flyer/flypain1.wav"); sound_pain2 = gi.soundindex("flyer/flypain2.wav"); sound_slash = gi.soundindex("flyer/flyatck2.wav"); sound_sproing = gi.soundindex("flyer/flyatck1.wav"); sound_die = gi.soundindex("flyer/flydeth1.wav"); gi.soundindex("flyer/flyatck3.wav"); self->s.modelindex = gi.modelindex("models/monsters/flyer/tris.md2"); VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, 32); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.sound = gi.soundindex("flyer/flyidle1.wav"); self->health = 50; self->mass = 50; self->pain = flyer_pain; self->die = flyer_die; self->monsterinfo.stand = flyer_stand; self->monsterinfo.walk = flyer_walk; self->monsterinfo.run = flyer_run; self->monsterinfo.attack = flyer_attack; self->monsterinfo.melee = flyer_melee; self->monsterinfo.sight = flyer_sight; self->monsterinfo.idle = flyer_idle; gi.linkentity(self); self->monsterinfo.currentmove = &flyer_move_stand; self->monsterinfo.scale = MODEL_SCALE; flymonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/supertank/0000755000175000017500000000000012615162034022022 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/supertank/supertank.c0000644000175000017500000004213112615162034024203 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Supertank aka "Boss1". * * ======================================================================= */ #include "../../header/local.h" #include "supertank.h" qboolean visible(edict_t *self, edict_t *other); static int sound_pain1; static int sound_pain2; static int sound_pain3; static int sound_death; static int sound_search1; static int sound_search2; static int tread_sound; void BossExplode(edict_t *self); void TreadSound(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, tread_sound, 1, ATTN_NORM, 0); } void supertank_search(edict_t *self) { if (!self) { return; } if (random() < 0.5) { gi.sound(self, CHAN_VOICE, sound_search1, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_VOICE, sound_search2, 1, ATTN_NORM, 0); } } void supertank_dead(edict_t *self); void supertankRocket(edict_t *self); void supertankMachineGun(edict_t *self); void supertank_reattack1(edict_t *self); mframe_t supertank_frames_stand[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t supertank_move_stand = { FRAME_stand_1, FRAME_stand_60, supertank_frames_stand, NULL }; void supertank_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &supertank_move_stand; } mframe_t supertank_frames_run[] = { {ai_run, 12, TreadSound}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL}, {ai_run, 12, NULL} }; mmove_t supertank_move_run = { FRAME_forwrd_1, FRAME_forwrd_18, supertank_frames_run, NULL }; mframe_t supertank_frames_forward[] = { {ai_walk, 4, TreadSound}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL} }; mmove_t supertank_move_forward = { FRAME_forwrd_1, FRAME_forwrd_18, supertank_frames_forward, NULL }; void supertank_forward(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &supertank_move_forward; } void supertank_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &supertank_move_forward; } void supertank_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &supertank_move_stand; } else { self->monsterinfo.currentmove = &supertank_move_run; } } mframe_t supertank_frames_turn_right[] = { {ai_move, 0, TreadSound}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t supertank_move_turn_right = { FRAME_right_1, FRAME_right_18, supertank_frames_turn_right, supertank_run }; mframe_t supertank_frames_turn_left[] = { {ai_move, 0, TreadSound}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t supertank_move_turn_left = { FRAME_left_1, FRAME_left_18, supertank_frames_turn_left, supertank_run }; mframe_t supertank_frames_pain3[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t supertank_move_pain3 = { FRAME_pain3_9, FRAME_pain3_12, supertank_frames_pain3, supertank_run }; mframe_t supertank_frames_pain2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t supertank_move_pain2 = { FRAME_pain2_5, FRAME_pain2_8, supertank_frames_pain2, supertank_run }; mframe_t supertank_frames_pain1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t supertank_move_pain1 = { FRAME_pain1_1, FRAME_pain1_4, supertank_frames_pain1, supertank_run }; mframe_t supertank_frames_death1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, BossExplode} }; mmove_t supertank_move_death = { FRAME_death_1, FRAME_death_24, supertank_frames_death1, supertank_dead }; mframe_t supertank_frames_backward[] = { {ai_walk, 0, TreadSound}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL} }; mmove_t supertank_move_backward = { FRAME_backwd_1, FRAME_backwd_18, supertank_frames_backward, NULL }; mframe_t supertank_frames_attack4[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t supertank_move_attack4 = { FRAME_attak4_1, FRAME_attak4_6, supertank_frames_attack4, supertank_run }; mframe_t supertank_frames_attack3[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t supertank_move_attack3 = { FRAME_attak3_1, FRAME_attak3_27, supertank_frames_attack3, supertank_run }; mframe_t supertank_frames_attack2[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, supertankRocket}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, supertankRocket}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, supertankRocket}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t supertank_move_attack2 = { FRAME_attak2_1, FRAME_attak2_27, supertank_frames_attack2, supertank_run }; mframe_t supertank_frames_attack1[] = { {ai_charge, 0, supertankMachineGun}, {ai_charge, 0, supertankMachineGun}, {ai_charge, 0, supertankMachineGun}, {ai_charge, 0, supertankMachineGun}, {ai_charge, 0, supertankMachineGun}, {ai_charge, 0, supertankMachineGun}, }; mmove_t supertank_move_attack1 = { FRAME_attak1_1, FRAME_attak1_6, supertank_frames_attack1, supertank_reattack1 }; mframe_t supertank_frames_end_attack1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t supertank_move_end_attack1 = { FRAME_attak1_7, FRAME_attak1_20, supertank_frames_end_attack1, supertank_run }; void supertank_reattack1(edict_t *self) { if (!self) { return; } if (visible(self, self->enemy)) { if (random() < 0.9) { self->monsterinfo.currentmove = &supertank_move_attack1; } else { self->monsterinfo.currentmove = &supertank_move_end_attack1; } } else { self->monsterinfo.currentmove = &supertank_move_end_attack1; } } void supertank_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage) { if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { return; } /* Lessen the chance of him going into his pain frames */ if (damage <= 25) { if (random() < 0.2) { return; } } /* Don't go into pain if he's firing his rockets */ if (skill->value >= 2) { if ((self->s.frame >= FRAME_attak2_1) && (self->s.frame <= FRAME_attak2_14)) { return; } } self->pain_debounce_time = level.time + 3; if (skill->value == 3) { return; /* no pain anims in nightmare */ } if (damage <= 10) { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &supertank_move_pain1; } else if (damage <= 25) { gi.sound(self, CHAN_VOICE, sound_pain3, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &supertank_move_pain2; } else { gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &supertank_move_pain3; } } void supertankRocket(edict_t *self) { vec3_t forward, right; vec3_t start; vec3_t dir; vec3_t vec; int flash_number; if (!self) { return; } if (self->s.frame == FRAME_attak2_8) { flash_number = MZ2_SUPERTANK_ROCKET_1; } else if (self->s.frame == FRAME_attak2_11) { flash_number = MZ2_SUPERTANK_ROCKET_2; } else { flash_number = MZ2_SUPERTANK_ROCKET_3; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start); VectorCopy(self->enemy->s.origin, vec); vec[2] += self->enemy->viewheight; VectorSubtract(vec, start, dir); VectorNormalize(dir); monster_fire_rocket(self, start, dir, 50, 500, flash_number); } void supertankMachineGun(edict_t *self) { vec3_t dir; vec3_t vec; vec3_t start; vec3_t forward, right; int flash_number; if (!self) { return; } flash_number = MZ2_SUPERTANK_MACHINEGUN_1 + (self->s.frame - FRAME_attak1_1); dir[0] = 0; dir[1] = self->s.angles[1]; dir[2] = 0; AngleVectors(dir, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start); if (self->enemy) { VectorCopy(self->enemy->s.origin, vec); VectorMA(vec, 0, self->enemy->velocity, vec); vec[2] += self->enemy->viewheight; VectorSubtract(vec, start, forward); VectorNormalize(forward); } monster_fire_bullet(self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number); } void supertank_attack(edict_t *self) { vec3_t vec; float range; if (!self) { return; } VectorSubtract(self->enemy->s.origin, self->s.origin, vec); range = VectorLength(vec); if (range <= 160) { self->monsterinfo.currentmove = &supertank_move_attack1; } else { /* fire rockets more often at distance */ if (random() < 0.3) { self->monsterinfo.currentmove = &supertank_move_attack1; } else { self->monsterinfo.currentmove = &supertank_move_attack2; } } } void supertank_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -60, -60, 0); VectorSet(self->maxs, 60, 60, 72); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } void BossExplode(edict_t *self) { vec3_t org; int n; if (!self) { return; } self->think = BossExplode; VectorCopy(self->s.origin, org); org[2] += 24 + (randk() & 15); switch (self->count++) { case 0: org[0] -= 24; org[1] -= 24; break; case 1: org[0] += 24; org[1] += 24; break; case 2: org[0] += 24; org[1] -= 24; break; case 3: org[0] -= 24; org[1] += 24; break; case 4: org[0] -= 48; org[1] -= 48; break; case 5: org[0] += 48; org[1] += 48; break; case 6: org[0] -= 48; org[1] += 48; break; case 7: org[0] += 48; org[1] -= 48; break; case 8: self->s.sound = 0; for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", 500, GIB_ORGANIC); } for (n = 0; n < 8; n++) { ThrowGib(self, "models/objects/gibs/sm_metal/tris.md2", 500, GIB_METALLIC); } ThrowGib(self, "models/objects/gibs/chest/tris.md2", 500, GIB_ORGANIC); ThrowHead(self, "models/objects/gibs/gear/tris.md2", 500, GIB_METALLIC); self->deadflag = DEAD_DEAD; return; } gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_EXPLOSION1); gi.WritePosition(org); gi.multicast(self->s.origin, MULTICAST_PVS); self->nextthink = level.time + 0.1; } void supertank_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage /* unused */, vec3_t point /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0); self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_NO; self->count = 0; self->monsterinfo.currentmove = &supertank_move_death; } /* * QUAKED monster_supertank (1 .5 0) (-64 -64 0) (64 64 72) Ambush Trigger_Spawn Sight */ void SP_monster_supertank(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_pain1 = gi.soundindex("bosstank/btkpain1.wav"); sound_pain2 = gi.soundindex("bosstank/btkpain2.wav"); sound_pain3 = gi.soundindex("bosstank/btkpain3.wav"); sound_death = gi.soundindex("bosstank/btkdeth1.wav"); sound_search1 = gi.soundindex("bosstank/btkunqv1.wav"); sound_search2 = gi.soundindex("bosstank/btkunqv2.wav"); tread_sound = gi.soundindex("bosstank/btkengn1.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/boss1/tris.md2"); VectorSet(self->mins, -64, -64, 0); VectorSet(self->maxs, 64, 64, 112); self->health = 1500; self->gib_health = -500; self->mass = 800; self->pain = supertank_pain; self->die = supertank_die; self->monsterinfo.stand = supertank_stand; self->monsterinfo.walk = supertank_walk; self->monsterinfo.run = supertank_run; self->monsterinfo.dodge = NULL; self->monsterinfo.attack = supertank_attack; self->monsterinfo.search = supertank_search; self->monsterinfo.melee = NULL; self->monsterinfo.sight = NULL; gi.linkentity(self); self->monsterinfo.currentmove = &supertank_move_stand; self->monsterinfo.scale = MODEL_SCALE; walkmonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/supertank/supertank.h0000644000175000017500000001711012615162034024207 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Supertank aka "Boss1" animations. * * ======================================================================= */ #define FRAME_attak1_1 0 #define FRAME_attak1_2 1 #define FRAME_attak1_3 2 #define FRAME_attak1_4 3 #define FRAME_attak1_5 4 #define FRAME_attak1_6 5 #define FRAME_attak1_7 6 #define FRAME_attak1_8 7 #define FRAME_attak1_9 8 #define FRAME_attak1_10 9 #define FRAME_attak1_11 10 #define FRAME_attak1_12 11 #define FRAME_attak1_13 12 #define FRAME_attak1_14 13 #define FRAME_attak1_15 14 #define FRAME_attak1_16 15 #define FRAME_attak1_17 16 #define FRAME_attak1_18 17 #define FRAME_attak1_19 18 #define FRAME_attak1_20 19 #define FRAME_attak2_1 20 #define FRAME_attak2_2 21 #define FRAME_attak2_3 22 #define FRAME_attak2_4 23 #define FRAME_attak2_5 24 #define FRAME_attak2_6 25 #define FRAME_attak2_7 26 #define FRAME_attak2_8 27 #define FRAME_attak2_9 28 #define FRAME_attak2_10 29 #define FRAME_attak2_11 30 #define FRAME_attak2_12 31 #define FRAME_attak2_13 32 #define FRAME_attak2_14 33 #define FRAME_attak2_15 34 #define FRAME_attak2_16 35 #define FRAME_attak2_17 36 #define FRAME_attak2_18 37 #define FRAME_attak2_19 38 #define FRAME_attak2_20 39 #define FRAME_attak2_21 40 #define FRAME_attak2_22 41 #define FRAME_attak2_23 42 #define FRAME_attak2_24 43 #define FRAME_attak2_25 44 #define FRAME_attak2_26 45 #define FRAME_attak2_27 46 #define FRAME_attak3_1 47 #define FRAME_attak3_2 48 #define FRAME_attak3_3 49 #define FRAME_attak3_4 50 #define FRAME_attak3_5 51 #define FRAME_attak3_6 52 #define FRAME_attak3_7 53 #define FRAME_attak3_8 54 #define FRAME_attak3_9 55 #define FRAME_attak3_10 56 #define FRAME_attak3_11 57 #define FRAME_attak3_12 58 #define FRAME_attak3_13 59 #define FRAME_attak3_14 60 #define FRAME_attak3_15 61 #define FRAME_attak3_16 62 #define FRAME_attak3_17 63 #define FRAME_attak3_18 64 #define FRAME_attak3_19 65 #define FRAME_attak3_20 66 #define FRAME_attak3_21 67 #define FRAME_attak3_22 68 #define FRAME_attak3_23 69 #define FRAME_attak3_24 70 #define FRAME_attak3_25 71 #define FRAME_attak3_26 72 #define FRAME_attak3_27 73 #define FRAME_attak4_1 74 #define FRAME_attak4_2 75 #define FRAME_attak4_3 76 #define FRAME_attak4_4 77 #define FRAME_attak4_5 78 #define FRAME_attak4_6 79 #define FRAME_backwd_1 80 #define FRAME_backwd_2 81 #define FRAME_backwd_3 82 #define FRAME_backwd_4 83 #define FRAME_backwd_5 84 #define FRAME_backwd_6 85 #define FRAME_backwd_7 86 #define FRAME_backwd_8 87 #define FRAME_backwd_9 88 #define FRAME_backwd_10 89 #define FRAME_backwd_11 90 #define FRAME_backwd_12 91 #define FRAME_backwd_13 92 #define FRAME_backwd_14 93 #define FRAME_backwd_15 94 #define FRAME_backwd_16 95 #define FRAME_backwd_17 96 #define FRAME_backwd_18 97 #define FRAME_death_1 98 #define FRAME_death_2 99 #define FRAME_death_3 100 #define FRAME_death_4 101 #define FRAME_death_5 102 #define FRAME_death_6 103 #define FRAME_death_7 104 #define FRAME_death_8 105 #define FRAME_death_9 106 #define FRAME_death_10 107 #define FRAME_death_11 108 #define FRAME_death_12 109 #define FRAME_death_13 110 #define FRAME_death_14 111 #define FRAME_death_15 112 #define FRAME_death_16 113 #define FRAME_death_17 114 #define FRAME_death_18 115 #define FRAME_death_19 116 #define FRAME_death_20 117 #define FRAME_death_21 118 #define FRAME_death_22 119 #define FRAME_death_23 120 #define FRAME_death_24 121 #define FRAME_death_31 122 #define FRAME_death_32 123 #define FRAME_death_33 124 #define FRAME_death_45 125 #define FRAME_death_46 126 #define FRAME_death_47 127 #define FRAME_forwrd_1 128 #define FRAME_forwrd_2 129 #define FRAME_forwrd_3 130 #define FRAME_forwrd_4 131 #define FRAME_forwrd_5 132 #define FRAME_forwrd_6 133 #define FRAME_forwrd_7 134 #define FRAME_forwrd_8 135 #define FRAME_forwrd_9 136 #define FRAME_forwrd_10 137 #define FRAME_forwrd_11 138 #define FRAME_forwrd_12 139 #define FRAME_forwrd_13 140 #define FRAME_forwrd_14 141 #define FRAME_forwrd_15 142 #define FRAME_forwrd_16 143 #define FRAME_forwrd_17 144 #define FRAME_forwrd_18 145 #define FRAME_left_1 146 #define FRAME_left_2 147 #define FRAME_left_3 148 #define FRAME_left_4 149 #define FRAME_left_5 150 #define FRAME_left_6 151 #define FRAME_left_7 152 #define FRAME_left_8 153 #define FRAME_left_9 154 #define FRAME_left_10 155 #define FRAME_left_11 156 #define FRAME_left_12 157 #define FRAME_left_13 158 #define FRAME_left_14 159 #define FRAME_left_15 160 #define FRAME_left_16 161 #define FRAME_left_17 162 #define FRAME_left_18 163 #define FRAME_pain1_1 164 #define FRAME_pain1_2 165 #define FRAME_pain1_3 166 #define FRAME_pain1_4 167 #define FRAME_pain2_5 168 #define FRAME_pain2_6 169 #define FRAME_pain2_7 170 #define FRAME_pain2_8 171 #define FRAME_pain3_9 172 #define FRAME_pain3_10 173 #define FRAME_pain3_11 174 #define FRAME_pain3_12 175 #define FRAME_right_1 176 #define FRAME_right_2 177 #define FRAME_right_3 178 #define FRAME_right_4 179 #define FRAME_right_5 180 #define FRAME_right_6 181 #define FRAME_right_7 182 #define FRAME_right_8 183 #define FRAME_right_9 184 #define FRAME_right_10 185 #define FRAME_right_11 186 #define FRAME_right_12 187 #define FRAME_right_13 188 #define FRAME_right_14 189 #define FRAME_right_15 190 #define FRAME_right_16 191 #define FRAME_right_17 192 #define FRAME_right_18 193 #define FRAME_stand_1 194 #define FRAME_stand_2 195 #define FRAME_stand_3 196 #define FRAME_stand_4 197 #define FRAME_stand_5 198 #define FRAME_stand_6 199 #define FRAME_stand_7 200 #define FRAME_stand_8 201 #define FRAME_stand_9 202 #define FRAME_stand_10 203 #define FRAME_stand_11 204 #define FRAME_stand_12 205 #define FRAME_stand_13 206 #define FRAME_stand_14 207 #define FRAME_stand_15 208 #define FRAME_stand_16 209 #define FRAME_stand_17 210 #define FRAME_stand_18 211 #define FRAME_stand_19 212 #define FRAME_stand_20 213 #define FRAME_stand_21 214 #define FRAME_stand_22 215 #define FRAME_stand_23 216 #define FRAME_stand_24 217 #define FRAME_stand_25 218 #define FRAME_stand_26 219 #define FRAME_stand_27 220 #define FRAME_stand_28 221 #define FRAME_stand_29 222 #define FRAME_stand_30 223 #define FRAME_stand_31 224 #define FRAME_stand_32 225 #define FRAME_stand_33 226 #define FRAME_stand_34 227 #define FRAME_stand_35 228 #define FRAME_stand_36 229 #define FRAME_stand_37 230 #define FRAME_stand_38 231 #define FRAME_stand_39 232 #define FRAME_stand_40 233 #define FRAME_stand_41 234 #define FRAME_stand_42 235 #define FRAME_stand_43 236 #define FRAME_stand_44 237 #define FRAME_stand_45 238 #define FRAME_stand_46 239 #define FRAME_stand_47 240 #define FRAME_stand_48 241 #define FRAME_stand_49 242 #define FRAME_stand_50 243 #define FRAME_stand_51 244 #define FRAME_stand_52 245 #define FRAME_stand_53 246 #define FRAME_stand_54 247 #define FRAME_stand_55 248 #define FRAME_stand_56 249 #define FRAME_stand_57 250 #define FRAME_stand_58 251 #define FRAME_stand_59 252 #define FRAME_stand_60 253 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/misc/0000755000175000017500000000000012615162034020741 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/misc/player.h0000644000175000017500000001365412615162034022417 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Player (the arm and the weapons) animation. * * ======================================================================= */ #define FRAME_stand01 0 #define FRAME_stand02 1 #define FRAME_stand03 2 #define FRAME_stand04 3 #define FRAME_stand05 4 #define FRAME_stand06 5 #define FRAME_stand07 6 #define FRAME_stand08 7 #define FRAME_stand09 8 #define FRAME_stand10 9 #define FRAME_stand11 10 #define FRAME_stand12 11 #define FRAME_stand13 12 #define FRAME_stand14 13 #define FRAME_stand15 14 #define FRAME_stand16 15 #define FRAME_stand17 16 #define FRAME_stand18 17 #define FRAME_stand19 18 #define FRAME_stand20 19 #define FRAME_stand21 20 #define FRAME_stand22 21 #define FRAME_stand23 22 #define FRAME_stand24 23 #define FRAME_stand25 24 #define FRAME_stand26 25 #define FRAME_stand27 26 #define FRAME_stand28 27 #define FRAME_stand29 28 #define FRAME_stand30 29 #define FRAME_stand31 30 #define FRAME_stand32 31 #define FRAME_stand33 32 #define FRAME_stand34 33 #define FRAME_stand35 34 #define FRAME_stand36 35 #define FRAME_stand37 36 #define FRAME_stand38 37 #define FRAME_stand39 38 #define FRAME_stand40 39 #define FRAME_run1 40 #define FRAME_run2 41 #define FRAME_run3 42 #define FRAME_run4 43 #define FRAME_run5 44 #define FRAME_run6 45 #define FRAME_attack1 46 #define FRAME_attack2 47 #define FRAME_attack3 48 #define FRAME_attack4 49 #define FRAME_attack5 50 #define FRAME_attack6 51 #define FRAME_attack7 52 #define FRAME_attack8 53 #define FRAME_pain101 54 #define FRAME_pain102 55 #define FRAME_pain103 56 #define FRAME_pain104 57 #define FRAME_pain201 58 #define FRAME_pain202 59 #define FRAME_pain203 60 #define FRAME_pain204 61 #define FRAME_pain301 62 #define FRAME_pain302 63 #define FRAME_pain303 64 #define FRAME_pain304 65 #define FRAME_jump1 66 #define FRAME_jump2 67 #define FRAME_jump3 68 #define FRAME_jump4 69 #define FRAME_jump5 70 #define FRAME_jump6 71 #define FRAME_flip01 72 #define FRAME_flip02 73 #define FRAME_flip03 74 #define FRAME_flip04 75 #define FRAME_flip05 76 #define FRAME_flip06 77 #define FRAME_flip07 78 #define FRAME_flip08 79 #define FRAME_flip09 80 #define FRAME_flip10 81 #define FRAME_flip11 82 #define FRAME_flip12 83 #define FRAME_salute01 84 #define FRAME_salute02 85 #define FRAME_salute03 86 #define FRAME_salute04 87 #define FRAME_salute05 88 #define FRAME_salute06 89 #define FRAME_salute07 90 #define FRAME_salute08 91 #define FRAME_salute09 92 #define FRAME_salute10 93 #define FRAME_salute11 94 #define FRAME_taunt01 95 #define FRAME_taunt02 96 #define FRAME_taunt03 97 #define FRAME_taunt04 98 #define FRAME_taunt05 99 #define FRAME_taunt06 100 #define FRAME_taunt07 101 #define FRAME_taunt08 102 #define FRAME_taunt09 103 #define FRAME_taunt10 104 #define FRAME_taunt11 105 #define FRAME_taunt12 106 #define FRAME_taunt13 107 #define FRAME_taunt14 108 #define FRAME_taunt15 109 #define FRAME_taunt16 110 #define FRAME_taunt17 111 #define FRAME_wave01 112 #define FRAME_wave02 113 #define FRAME_wave03 114 #define FRAME_wave04 115 #define FRAME_wave05 116 #define FRAME_wave06 117 #define FRAME_wave07 118 #define FRAME_wave08 119 #define FRAME_wave09 120 #define FRAME_wave10 121 #define FRAME_wave11 122 #define FRAME_point01 123 #define FRAME_point02 124 #define FRAME_point03 125 #define FRAME_point04 126 #define FRAME_point05 127 #define FRAME_point06 128 #define FRAME_point07 129 #define FRAME_point08 130 #define FRAME_point09 131 #define FRAME_point10 132 #define FRAME_point11 133 #define FRAME_point12 134 #define FRAME_crstnd01 135 #define FRAME_crstnd02 136 #define FRAME_crstnd03 137 #define FRAME_crstnd04 138 #define FRAME_crstnd05 139 #define FRAME_crstnd06 140 #define FRAME_crstnd07 141 #define FRAME_crstnd08 142 #define FRAME_crstnd09 143 #define FRAME_crstnd10 144 #define FRAME_crstnd11 145 #define FRAME_crstnd12 146 #define FRAME_crstnd13 147 #define FRAME_crstnd14 148 #define FRAME_crstnd15 149 #define FRAME_crstnd16 150 #define FRAME_crstnd17 151 #define FRAME_crstnd18 152 #define FRAME_crstnd19 153 #define FRAME_crwalk1 154 #define FRAME_crwalk2 155 #define FRAME_crwalk3 156 #define FRAME_crwalk4 157 #define FRAME_crwalk5 158 #define FRAME_crwalk6 159 #define FRAME_crattak1 160 #define FRAME_crattak2 161 #define FRAME_crattak3 162 #define FRAME_crattak4 163 #define FRAME_crattak5 164 #define FRAME_crattak6 165 #define FRAME_crattak7 166 #define FRAME_crattak8 167 #define FRAME_crattak9 168 #define FRAME_crpain1 169 #define FRAME_crpain2 170 #define FRAME_crpain3 171 #define FRAME_crpain4 172 #define FRAME_crdeath1 173 #define FRAME_crdeath2 174 #define FRAME_crdeath3 175 #define FRAME_crdeath4 176 #define FRAME_crdeath5 177 #define FRAME_death101 178 #define FRAME_death102 179 #define FRAME_death103 180 #define FRAME_death104 181 #define FRAME_death105 182 #define FRAME_death106 183 #define FRAME_death201 184 #define FRAME_death202 185 #define FRAME_death203 186 #define FRAME_death204 187 #define FRAME_death205 188 #define FRAME_death206 189 #define FRAME_death301 190 #define FRAME_death302 191 #define FRAME_death303 192 #define FRAME_death304 193 #define FRAME_death305 194 #define FRAME_death306 195 #define FRAME_death307 196 #define FRAME_death308 197 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/misc/move.c0000644000175000017500000002733712615162034022067 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Monster movement support functions. * * ======================================================================= */ #include "../../header/local.h" #define STEPSIZE 18 #define DI_NODIR -1 int c_yes, c_no; /* * Returns false if any part of the * bottom of the entity is off an edge * that is not a staircase. */ qboolean M_CheckBottom(edict_t *ent) { vec3_t mins, maxs, start, stop; trace_t trace; int x, y; float mid, bottom; if (!ent) { return false; } VectorAdd(ent->s.origin, ent->mins, mins); VectorAdd(ent->s.origin, ent->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 (gi.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 = gi.trace(start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID); 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 = gi.trace(start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID); 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; } /* * 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) { float dz; vec3_t oldorg, neworg, end; trace_t trace; int i; float stepsize; vec3_t test; int contents; if (!ent) { return false; } /* try the move */ VectorCopy(ent->s.origin, oldorg); VectorAdd(ent->s.origin, move, neworg); /* flying monsters don't step up */ if (ent->flags & (FL_SWIM | FL_FLY)) { /* try one move with vertical motion, then one without */ for (i = 0; i < 2; i++) { VectorAdd(ent->s.origin, move, neworg); if ((i == 0) && ent->enemy) { if (!ent->goalentity) { ent->goalentity = ent->enemy; } dz = ent->s.origin[2] - ent->goalentity->s.origin[2]; if (ent->goalentity->client) { if (dz > 40) { neworg[2] -= 8; } if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2))) { if (dz < 30) { neworg[2] += 8; } } } else { if (dz > 8) { neworg[2] -= 8; } else if (dz > 0) { neworg[2] -= dz; } else if (dz < -8) { neworg[2] += 8; } else { neworg[2] += dz; } } } trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID); /* fly monsters don't enter water voluntarily */ if (ent->flags & FL_FLY) { if (!ent->waterlevel) { test[0] = trace.endpos[0]; test[1] = trace.endpos[1]; test[2] = trace.endpos[2] + ent->mins[2] + 1; contents = gi.pointcontents(test); if (contents & MASK_WATER) { return false; } } } /* swim monsters don't exit water voluntarily */ if (ent->flags & FL_SWIM) { if (ent->waterlevel < 2) { test[0] = trace.endpos[0]; test[1] = trace.endpos[1]; test[2] = trace.endpos[2] + ent->mins[2] + 1; contents = gi.pointcontents(test); if (!(contents & MASK_WATER)) { return false; } } } if (trace.fraction == 1) { VectorCopy(trace.endpos, ent->s.origin); if (relink) { gi.linkentity(ent); G_TouchTriggers(ent); } return true; } if (!ent->enemy) { break; } } return false; } /* push down from a step height above the wished position */ if (!(ent->monsterinfo.aiflags & AI_NOSTEP)) { stepsize = STEPSIZE; } else { stepsize = 1; } neworg[2] += stepsize; VectorCopy(neworg, end); end[2] -= stepsize * 2; trace = gi.trace(neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID); if (trace.allsolid) { return false; } if (trace.startsolid) { neworg[2] -= stepsize; trace = gi.trace(neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID); if (trace.allsolid || trace.startsolid) { return false; } } /* don't go in to water */ if (ent->waterlevel == 0) { test[0] = trace.endpos[0]; test[1] = trace.endpos[1]; test[2] = trace.endpos[2] + ent->mins[2] + 1; contents = gi.pointcontents(test); if (contents & MASK_WATER) { return false; } } if (trace.fraction == 1) { /* if monster had the ground pulled out, go ahead and fall */ if (ent->flags & FL_PARTIALGROUND) { VectorAdd(ent->s.origin, move, ent->s.origin); if (relink) { gi.linkentity(ent); G_TouchTriggers(ent); } ent->groundentity = NULL; return true; } return false; /* walked off an edge */ } /* check point traces down for dangling corners */ VectorCopy(trace.endpos, ent->s.origin); if (!M_CheckBottom(ent)) { if (ent->flags & FL_PARTIALGROUND) { /* entity had floor mostly pulled out from underneath it and is trying to correct */ if (relink) { gi.linkentity(ent); G_TouchTriggers(ent); } return true; } VectorCopy(oldorg, ent->s.origin); return false; } if (ent->flags & FL_PARTIALGROUND) { ent->flags &= ~FL_PARTIALGROUND; } ent->groundentity = trace.ent; ent->groundentity_linkcount = trace.ent->linkcount; /* the move is ok */ if (relink) { gi.linkentity(ent); G_TouchTriggers(ent); } return true; } /* ============================================================================ */ void M_ChangeYaw(edict_t *ent) { float ideal; float current; float move; float speed; if (!ent) { return; } if (!ent) { return; } current = anglemod(ent->s.angles[YAW]); ideal = ent->ideal_yaw; if (current == ideal) { return; } move = ideal - current; speed = ent->yaw_speed; 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->s.angles[YAW] = anglemod(current + move); } /* * Turns to the movement direction, and * walks the current distance if facing it. */ qboolean SV_StepDirection(edict_t *ent, float yaw, float dist) { vec3_t move, oldorigin; float delta; if (!ent) { return false; } ent->ideal_yaw = yaw; M_ChangeYaw(ent); yaw = yaw * M_PI * 2 / 360; move[0] = cos(yaw) * dist; move[1] = sin(yaw) * dist; move[2] = 0; VectorCopy(ent->s.origin, oldorigin); if (SV_movestep(ent, move, false)) { delta = ent->s.angles[YAW] - ent->ideal_yaw; if ((delta > 45) && (delta < 315)) { /* not turned far enough, so don't take the step */ VectorCopy(oldorigin, ent->s.origin); } gi.linkentity(ent); G_TouchTriggers(ent); return true; } gi.linkentity(ent); G_TouchTriggers(ent); return false; } void SV_FixCheckBottom(edict_t *ent) { if (!ent) { return; } ent->flags |= FL_PARTIALGROUND; } void SV_NewChaseDir(edict_t *actor, edict_t *enemy, float dist) { float deltax, deltay; float d[3]; float tdir, olddir, turnaround; if (!actor || !enemy) { return; } if (!enemy) { return; } olddir = anglemod((int)(actor->ideal_yaw / 45) * 45); turnaround = anglemod(olddir - 180); deltax = enemy->s.origin[0] - actor->s.origin[0]; deltay = enemy->s.origin[1] - actor->s.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 (((randk() & 3) & 1) || (fabsf(deltay) > fabsf(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 (randk() & 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->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 (!M_CheckBottom(actor)) { SV_FixCheckBottom(actor); } } qboolean SV_CloseEnough(edict_t *ent, edict_t *goal, float dist) { int i; if (!ent || !goal) { return false; } for (i = 0; i < 3; i++) { if (goal->absmin[i] > ent->absmax[i] + dist) { return false; } if (goal->absmax[i] < ent->absmin[i] - dist) { return false; } } return true; } void M_MoveToGoal(edict_t *ent, float dist) { edict_t *goal; if (!ent) { return; } goal = ent->goalentity; if (!ent->groundentity && !(ent->flags & (FL_FLY | FL_SWIM))) { return; } /* if the next step hits the enemy, return immediately */ if (ent->enemy && SV_CloseEnough(ent, ent->enemy, dist)) { return; } /* bump around... */ if (((randk() & 3) == 1) || !SV_StepDirection(ent, ent->ideal_yaw, dist)) { if (ent->inuse) { SV_NewChaseDir(ent, goal, dist); } } } qboolean M_walkmove(edict_t *ent, float yaw, float dist) { vec3_t move; if (!ent) { return false; } if (!ent->groundentity && !(ent->flags & (FL_FLY | FL_SWIM))) { return false; } yaw = yaw * M_PI * 2 / 360; move[0] = cos(yaw) * dist; move[1] = sin(yaw) * dist; move[2] = 0; return SV_movestep(ent, move, true); } yquake2-QUAKE2_5_32/src/game/monster/insane/0000755000175000017500000000000012615162034021263 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/insane/insane.h0000644000175000017500000002013112615162034022706 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Insane animations * * ======================================================================= */ #define FRAME_stand1 0 #define FRAME_stand2 1 #define FRAME_stand3 2 #define FRAME_stand4 3 #define FRAME_stand5 4 #define FRAME_stand6 5 #define FRAME_stand7 6 #define FRAME_stand8 7 #define FRAME_stand9 8 #define FRAME_stand10 9 #define FRAME_stand11 10 #define FRAME_stand12 11 #define FRAME_stand13 12 #define FRAME_stand14 13 #define FRAME_stand15 14 #define FRAME_stand16 15 #define FRAME_stand17 16 #define FRAME_stand18 17 #define FRAME_stand19 18 #define FRAME_stand20 19 #define FRAME_stand21 20 #define FRAME_stand22 21 #define FRAME_stand23 22 #define FRAME_stand24 23 #define FRAME_stand25 24 #define FRAME_stand26 25 #define FRAME_stand27 26 #define FRAME_stand28 27 #define FRAME_stand29 28 #define FRAME_stand30 29 #define FRAME_stand31 30 #define FRAME_stand32 31 #define FRAME_stand33 32 #define FRAME_stand34 33 #define FRAME_stand35 34 #define FRAME_stand36 35 #define FRAME_stand37 36 #define FRAME_stand38 37 #define FRAME_stand39 38 #define FRAME_stand40 39 #define FRAME_stand41 40 #define FRAME_stand42 41 #define FRAME_stand43 42 #define FRAME_stand44 43 #define FRAME_stand45 44 #define FRAME_stand46 45 #define FRAME_stand47 46 #define FRAME_stand48 47 #define FRAME_stand49 48 #define FRAME_stand50 49 #define FRAME_stand51 50 #define FRAME_stand52 51 #define FRAME_stand53 52 #define FRAME_stand54 53 #define FRAME_stand55 54 #define FRAME_stand56 55 #define FRAME_stand57 56 #define FRAME_stand58 57 #define FRAME_stand59 58 #define FRAME_stand60 59 #define FRAME_stand61 60 #define FRAME_stand62 61 #define FRAME_stand63 62 #define FRAME_stand64 63 #define FRAME_stand65 64 #define FRAME_stand66 65 #define FRAME_stand67 66 #define FRAME_stand68 67 #define FRAME_stand69 68 #define FRAME_stand70 69 #define FRAME_stand71 70 #define FRAME_stand72 71 #define FRAME_stand73 72 #define FRAME_stand74 73 #define FRAME_stand75 74 #define FRAME_stand76 75 #define FRAME_stand77 76 #define FRAME_stand78 77 #define FRAME_stand79 78 #define FRAME_stand80 79 #define FRAME_stand81 80 #define FRAME_stand82 81 #define FRAME_stand83 82 #define FRAME_stand84 83 #define FRAME_stand85 84 #define FRAME_stand86 85 #define FRAME_stand87 86 #define FRAME_stand88 87 #define FRAME_stand89 88 #define FRAME_stand90 89 #define FRAME_stand91 90 #define FRAME_stand92 91 #define FRAME_stand93 92 #define FRAME_stand94 93 #define FRAME_stand95 94 #define FRAME_stand96 95 #define FRAME_stand97 96 #define FRAME_stand98 97 #define FRAME_stand99 98 #define FRAME_stand100 99 #define FRAME_stand101 100 #define FRAME_stand102 101 #define FRAME_stand103 102 #define FRAME_stand104 103 #define FRAME_stand105 104 #define FRAME_stand106 105 #define FRAME_stand107 106 #define FRAME_stand108 107 #define FRAME_stand109 108 #define FRAME_stand110 109 #define FRAME_stand111 110 #define FRAME_stand112 111 #define FRAME_stand113 112 #define FRAME_stand114 113 #define FRAME_stand115 114 #define FRAME_stand116 115 #define FRAME_stand117 116 #define FRAME_stand118 117 #define FRAME_stand119 118 #define FRAME_stand120 119 #define FRAME_stand121 120 #define FRAME_stand122 121 #define FRAME_stand123 122 #define FRAME_stand124 123 #define FRAME_stand125 124 #define FRAME_stand126 125 #define FRAME_stand127 126 #define FRAME_stand128 127 #define FRAME_stand129 128 #define FRAME_stand130 129 #define FRAME_stand131 130 #define FRAME_stand132 131 #define FRAME_stand133 132 #define FRAME_stand134 133 #define FRAME_stand135 134 #define FRAME_stand136 135 #define FRAME_stand137 136 #define FRAME_stand138 137 #define FRAME_stand139 138 #define FRAME_stand140 139 #define FRAME_stand141 140 #define FRAME_stand142 141 #define FRAME_stand143 142 #define FRAME_stand144 143 #define FRAME_stand145 144 #define FRAME_stand146 145 #define FRAME_stand147 146 #define FRAME_stand148 147 #define FRAME_stand149 148 #define FRAME_stand150 149 #define FRAME_stand151 150 #define FRAME_stand152 151 #define FRAME_stand153 152 #define FRAME_stand154 153 #define FRAME_stand155 154 #define FRAME_stand156 155 #define FRAME_stand157 156 #define FRAME_stand158 157 #define FRAME_stand159 158 #define FRAME_stand160 159 #define FRAME_walk27 160 #define FRAME_walk28 161 #define FRAME_walk29 162 #define FRAME_walk30 163 #define FRAME_walk31 164 #define FRAME_walk32 165 #define FRAME_walk33 166 #define FRAME_walk34 167 #define FRAME_walk35 168 #define FRAME_walk36 169 #define FRAME_walk37 170 #define FRAME_walk38 171 #define FRAME_walk39 172 #define FRAME_walk1 173 #define FRAME_walk2 174 #define FRAME_walk3 175 #define FRAME_walk4 176 #define FRAME_walk5 177 #define FRAME_walk6 178 #define FRAME_walk7 179 #define FRAME_walk8 180 #define FRAME_walk9 181 #define FRAME_walk10 182 #define FRAME_walk11 183 #define FRAME_walk12 184 #define FRAME_walk13 185 #define FRAME_walk14 186 #define FRAME_walk15 187 #define FRAME_walk16 188 #define FRAME_walk17 189 #define FRAME_walk18 190 #define FRAME_walk19 191 #define FRAME_walk20 192 #define FRAME_walk21 193 #define FRAME_walk22 194 #define FRAME_walk23 195 #define FRAME_walk24 196 #define FRAME_walk25 197 #define FRAME_walk26 198 #define FRAME_st_pain2 199 #define FRAME_st_pain3 200 #define FRAME_st_pain4 201 #define FRAME_st_pain5 202 #define FRAME_st_pain6 203 #define FRAME_st_pain7 204 #define FRAME_st_pain8 205 #define FRAME_st_pain9 206 #define FRAME_st_pain10 207 #define FRAME_st_pain11 208 #define FRAME_st_pain12 209 #define FRAME_st_death2 210 #define FRAME_st_death3 211 #define FRAME_st_death4 212 #define FRAME_st_death5 213 #define FRAME_st_death6 214 #define FRAME_st_death7 215 #define FRAME_st_death8 216 #define FRAME_st_death9 217 #define FRAME_st_death10 218 #define FRAME_st_death11 219 #define FRAME_st_death12 220 #define FRAME_st_death13 221 #define FRAME_st_death14 222 #define FRAME_st_death15 223 #define FRAME_st_death16 224 #define FRAME_st_death17 225 #define FRAME_st_death18 226 #define FRAME_crawl1 227 #define FRAME_crawl2 228 #define FRAME_crawl3 229 #define FRAME_crawl4 230 #define FRAME_crawl5 231 #define FRAME_crawl6 232 #define FRAME_crawl7 233 #define FRAME_crawl8 234 #define FRAME_crawl9 235 #define FRAME_cr_pain2 236 #define FRAME_cr_pain3 237 #define FRAME_cr_pain4 238 #define FRAME_cr_pain5 239 #define FRAME_cr_pain6 240 #define FRAME_cr_pain7 241 #define FRAME_cr_pain8 242 #define FRAME_cr_pain9 243 #define FRAME_cr_pain10 244 #define FRAME_cr_death10 245 #define FRAME_cr_death11 246 #define FRAME_cr_death12 247 #define FRAME_cr_death13 248 #define FRAME_cr_death14 249 #define FRAME_cr_death15 250 #define FRAME_cr_death16 251 #define FRAME_cross1 252 #define FRAME_cross2 253 #define FRAME_cross3 254 #define FRAME_cross4 255 #define FRAME_cross5 256 #define FRAME_cross6 257 #define FRAME_cross7 258 #define FRAME_cross8 259 #define FRAME_cross9 260 #define FRAME_cross10 261 #define FRAME_cross11 262 #define FRAME_cross12 263 #define FRAME_cross13 264 #define FRAME_cross14 265 #define FRAME_cross15 266 #define FRAME_cross16 267 #define FRAME_cross17 268 #define FRAME_cross18 269 #define FRAME_cross19 270 #define FRAME_cross20 271 #define FRAME_cross21 272 #define FRAME_cross22 273 #define FRAME_cross23 274 #define FRAME_cross24 275 #define FRAME_cross25 276 #define FRAME_cross26 277 #define FRAME_cross27 278 #define FRAME_cross28 279 #define FRAME_cross29 280 #define FRAME_cross30 281 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/insane/insane.c0000644000175000017500000004376712615162034022725 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 insane earth soldiers. * * ======================================================================= */ #include "../../header/local.h" #include "insane.h" static int sound_fist; static int sound_shake; static int sound_moan; static int sound_scream[8]; void insane_fist(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_fist, 1, ATTN_IDLE, 0); } void insane_shake(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_shake, 1, ATTN_IDLE, 0); } void insane_moan(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_moan, 1, ATTN_IDLE, 0); } void insane_scream(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_scream[randk() % 8], 1, ATTN_IDLE, 0); } void insane_stand(edict_t *self); void insane_dead(edict_t *self); void insane_cross(edict_t *self); void insane_walk(edict_t *self); void insane_run(edict_t *self); void insane_checkdown(edict_t *self); void insane_checkup(edict_t *self); void insane_onground(edict_t *self); mframe_t insane_frames_stand_normal[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, insane_checkdown} }; mmove_t insane_move_stand_normal = { FRAME_stand60, FRAME_stand65, insane_frames_stand_normal, insane_stand }; mframe_t insane_frames_stand_insane[] = { {ai_stand, 0, insane_shake}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, insane_checkdown} }; mmove_t insane_move_stand_insane = { FRAME_stand65, FRAME_stand94, insane_frames_stand_insane, insane_stand }; mframe_t insane_frames_uptodown[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, insane_moan}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 2.7, NULL}, {ai_move, 4.1, NULL}, {ai_move, 6, NULL}, {ai_move, 7.6, NULL}, {ai_move, 3.6, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, insane_fist}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, insane_fist}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t insane_move_uptodown = { FRAME_stand1, FRAME_stand40, insane_frames_uptodown, insane_onground }; mframe_t insane_frames_downtoup[] = { {ai_move, -0.7, NULL}, /* 41 */ {ai_move, -1.2, NULL}, /* 42 */ {ai_move, -1.5, NULL}, /* 43 */ {ai_move, -4.5, NULL}, /* 44 */ {ai_move, -3.5, NULL}, /* 45 */ {ai_move, -0.2, NULL}, /* 46 */ {ai_move, 0, NULL}, /* 47 */ {ai_move, -1.3, NULL}, /* 48 */ {ai_move, -3, NULL}, /* 49 */ {ai_move, -2, NULL}, /* 50 */ {ai_move, 0, NULL}, /* 51 */ {ai_move, 0, NULL}, /* 52 */ {ai_move, 0, NULL}, /* 53 */ {ai_move, -3.3, NULL}, /* 54 */ {ai_move, -1.6, NULL}, /* 55 */ {ai_move, -0.3, NULL}, /* 56 */ {ai_move, 0, NULL}, /* 57 */ {ai_move, 0, NULL}, /* 58 */ {ai_move, 0, NULL} /* 59 */ }; mmove_t insane_move_downtoup = { FRAME_stand41, FRAME_stand59, insane_frames_downtoup, insane_stand }; mframe_t insane_frames_jumpdown[] = { {ai_move, 0.2, NULL}, {ai_move, 11.5, NULL}, {ai_move, 5.1, NULL}, {ai_move, 7.1, NULL}, {ai_move, 0, NULL} }; mmove_t insane_move_jumpdown = { FRAME_stand96, FRAME_stand100, insane_frames_jumpdown, insane_onground }; mframe_t insane_frames_down[] = { {ai_move, 0, NULL}, /* 100 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 110 */ {ai_move, -1.7, NULL}, {ai_move, -1.6, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, insane_fist}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 120 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 130 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, insane_moan}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 140 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 150 */ {ai_move, 0.5, NULL}, {ai_move, 0, NULL}, {ai_move, -0.2, insane_scream}, {ai_move, 0, NULL}, {ai_move, 0.2, NULL}, {ai_move, 0.4, NULL}, {ai_move, 0.6, NULL}, {ai_move, 0.8, NULL}, {ai_move, 0.7, NULL}, {ai_move, 0, insane_checkup} /* 160 */ }; mmove_t insane_move_down = { FRAME_stand100, FRAME_stand160, insane_frames_down, insane_onground }; mframe_t insane_frames_walk_normal[] = { {ai_walk, 0, insane_scream}, {ai_walk, 2.5, NULL}, {ai_walk, 3.5, NULL}, {ai_walk, 1.7, NULL}, {ai_walk, 2.3, NULL}, {ai_walk, 2.4, NULL}, {ai_walk, 2.2, NULL}, {ai_walk, 4.2, NULL}, {ai_walk, 5.6, NULL}, {ai_walk, 3.3, NULL}, {ai_walk, 2.4, NULL}, {ai_walk, 0.9, NULL}, {ai_walk, 0, NULL} }; mmove_t insane_move_walk_normal = { FRAME_walk27, FRAME_walk39, insane_frames_walk_normal, insane_walk}; mmove_t insane_move_run_normal = { FRAME_walk27, FRAME_walk39, insane_frames_walk_normal, insane_run }; mframe_t insane_frames_walk_insane[] = { {ai_walk, 0, insane_scream}, /* walk 1 */ {ai_walk, 3.4, NULL}, /* walk 2 */ {ai_walk, 3.6, NULL}, /* 3 */ {ai_walk, 2.9, NULL}, /* 4 */ {ai_walk, 2.2, NULL}, /* 5 */ {ai_walk, 2.6, NULL}, /* 6 */ {ai_walk, 0, NULL}, /* 7 */ {ai_walk, 0.7, NULL}, /* 8 */ {ai_walk, 4.8, NULL}, /* 9 */ {ai_walk, 5.3, NULL}, /* 10 */ {ai_walk, 1.1, NULL}, /* 11 */ {ai_walk, 2, NULL}, /* 12 */ {ai_walk, 0.5, NULL}, /* 13 */ {ai_walk, 0, NULL}, /* 14 */ {ai_walk, 0, NULL}, /* 15 */ {ai_walk, 4.9, NULL}, /* 16 */ {ai_walk, 6.7, NULL}, /* 17 */ {ai_walk, 3.8, NULL}, /* 18 */ {ai_walk, 2, NULL}, /* 19 */ {ai_walk, 0.2, NULL}, /* 20 */ {ai_walk, 0, NULL}, /* 21 */ {ai_walk, 3.4, NULL}, /* 22 */ {ai_walk, 6.4, NULL}, /* 23 */ {ai_walk, 5, NULL}, /* 24 */ {ai_walk, 1.8, NULL}, /* 25 */ {ai_walk, 0, NULL} /* 26 */ }; mmove_t insane_move_walk_insane = { FRAME_walk1, FRAME_walk26, insane_frames_walk_insane, insane_walk }; mmove_t insane_move_run_insane = { FRAME_walk1, FRAME_walk26, insane_frames_walk_insane, insane_run }; mframe_t insane_frames_stand_pain[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t insane_move_stand_pain = { FRAME_st_pain2, FRAME_st_pain12, insane_frames_stand_pain, insane_run }; mframe_t insane_frames_stand_death[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t insane_move_stand_death = { FRAME_st_death2, FRAME_st_death18, insane_frames_stand_death, insane_dead }; mframe_t insane_frames_crawl[] = { {ai_walk, 0, insane_scream}, {ai_walk, 1.5, NULL}, {ai_walk, 2.1, NULL}, {ai_walk, 3.6, NULL}, {ai_walk, 2, NULL}, {ai_walk, 0.9, NULL}, {ai_walk, 3, NULL}, {ai_walk, 3.4, NULL}, {ai_walk, 2.4, NULL} }; mmove_t insane_move_crawl = { FRAME_crawl1, FRAME_crawl9, insane_frames_crawl, NULL }; mmove_t insane_move_runcrawl = { FRAME_crawl1, FRAME_crawl9, insane_frames_crawl, NULL }; mframe_t insane_frames_crawl_pain[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t insane_move_crawl_pain = { FRAME_cr_pain2, FRAME_cr_pain10, insane_frames_crawl_pain, insane_run }; mframe_t insane_frames_crawl_death[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t insane_move_crawl_death = { FRAME_cr_death10, FRAME_cr_death16, insane_frames_crawl_death, insane_dead }; mframe_t insane_frames_cross[] = { {ai_move, 0, insane_moan}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t insane_move_cross = { FRAME_cross1, FRAME_cross15, insane_frames_cross, insane_cross }; mframe_t insane_frames_struggle_cross[] = { {ai_move, 0, insane_scream}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t insane_move_struggle_cross = { FRAME_cross16, FRAME_cross30, insane_frames_struggle_cross, insane_cross }; void insane_cross(edict_t *self) { if (!self) { return; } if (random() < 0.8) { self->monsterinfo.currentmove = &insane_move_cross; } else { self->monsterinfo.currentmove = &insane_move_struggle_cross; } } void insane_walk(edict_t *self) { if (!self) { return; } if (self->spawnflags & 16) { if (self->s.frame == FRAME_cr_pain10) { self->monsterinfo.currentmove = &insane_move_down; return; } } if (self->spawnflags & 4) { self->monsterinfo.currentmove = &insane_move_crawl; } else if (random() <= 0.5) { self->monsterinfo.currentmove = &insane_move_walk_normal; } else { self->monsterinfo.currentmove = &insane_move_walk_insane; } } void insane_run(edict_t *self) { if (!self) { return; } if (self->spawnflags & 16) { if (self->s.frame == FRAME_cr_pain10) { self->monsterinfo.currentmove = &insane_move_down; return; } } if (self->spawnflags & 4) { self->monsterinfo.currentmove = &insane_move_runcrawl; } else if (random() <= 0.5) /* Else, mix it up */ { self->monsterinfo.currentmove = &insane_move_run_normal; } else { self->monsterinfo.currentmove = &insane_move_run_insane; } } void insane_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage) { int l, r; if (!self) { return; } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; r = 1 + (randk() & 1); if (self->health < 25) { l = 25; } else if (self->health < 50) { l = 50; } else if (self->health < 75) { l = 75; } else { l = 100; } gi.sound(self, CHAN_VOICE, gi.soundindex(va("player/male/pain%i_%i.wav", l, r)), 1, ATTN_IDLE, 0); if (skill->value == 3) { return; /* no pain anims in nightmare */ } /* Don't go into pain frames if crucified. */ if (self->spawnflags & 8) { self->monsterinfo.currentmove = &insane_move_struggle_cross; return; } if (((self->s.frame >= FRAME_crawl1) && (self->s.frame <= FRAME_crawl9)) || ((self->s.frame >= FRAME_stand99) && (self->s.frame <= FRAME_stand160))) { self->monsterinfo.currentmove = &insane_move_crawl_pain; } else { self->monsterinfo.currentmove = &insane_move_stand_pain; } } void insane_onground(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &insane_move_down; } void insane_checkdown(edict_t *self) { if (!self) { return; } if (self->spawnflags & 32) /* Always stand */ { return; } if (random() < 0.3) { if (random() < 0.5) { self->monsterinfo.currentmove = &insane_move_uptodown; } else { self->monsterinfo.currentmove = &insane_move_jumpdown; } } } void insane_checkup(edict_t *self) { if (!self) { return; } /* If Hold_Ground and Crawl are set */ if ((self->spawnflags & 4) && (self->spawnflags & 16)) { return; } if (random() < 0.5) { self->monsterinfo.currentmove = &insane_move_downtoup; } } void insane_stand(edict_t *self) { if (!self) { return; } if (self->spawnflags & 8) /* If crucified */ { self->monsterinfo.currentmove = &insane_move_cross; self->monsterinfo.aiflags |= AI_STAND_GROUND; } /* If Hold_Ground and Crawl are set */ else if ((self->spawnflags & 4) && (self->spawnflags & 16)) { self->monsterinfo.currentmove = &insane_move_down; } else if (random() < 0.5) { self->monsterinfo.currentmove = &insane_move_stand_normal; } else { self->monsterinfo.currentmove = &insane_move_stand_insane; } } void insane_dead(edict_t *self) { if (!self) { return; } if (self->spawnflags & 8) { self->flags |= FL_FLY; } else { VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, -8); self->movetype = MOVETYPE_TOSS; } self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } void insane_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage, vec3_t point /* unused */) { int n; if (!self) { return; } if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex( "misc/udeath.wav"), 1, ATTN_IDLE, 0); for (n = 0; n < 2; n++) { ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } gi.sound(self, CHAN_VOICE, gi.soundindex(va("player/male/death%i.wav", (randk() % 4) + 1)), 1, ATTN_IDLE, 0); self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; if (self->spawnflags & 8) { insane_dead(self); } else { if (((self->s.frame >= FRAME_crawl1) && (self->s.frame <= FRAME_crawl9)) || ((self->s.frame >= FRAME_stand99) && (self->s.frame <= FRAME_stand160))) { self->monsterinfo.currentmove = &insane_move_crawl_death; } else { self->monsterinfo.currentmove = &insane_move_stand_death; } } } /* * QUAKED misc_insane (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn CRAWL CRUCIFIED STAND_GROUND ALWAYS_STAND */ void SP_misc_insane(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_fist = gi.soundindex("insane/insane11.wav"); sound_shake = gi.soundindex("insane/insane5.wav"); sound_moan = gi.soundindex("insane/insane7.wav"); sound_scream[0] = gi.soundindex("insane/insane1.wav"); sound_scream[1] = gi.soundindex("insane/insane2.wav"); sound_scream[2] = gi.soundindex("insane/insane3.wav"); sound_scream[3] = gi.soundindex("insane/insane4.wav"); sound_scream[4] = gi.soundindex("insane/insane6.wav"); sound_scream[5] = gi.soundindex("insane/insane8.wav"); sound_scream[6] = gi.soundindex("insane/insane9.wav"); sound_scream[7] = gi.soundindex("insane/insane10.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/insane/tris.md2"); VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, 32); self->health = 100; self->gib_health = -50; self->mass = 300; self->pain = insane_pain; self->die = insane_die; self->monsterinfo.stand = insane_stand; self->monsterinfo.walk = insane_walk; self->monsterinfo.run = insane_run; self->monsterinfo.dodge = NULL; self->monsterinfo.attack = NULL; self->monsterinfo.melee = NULL; self->monsterinfo.sight = NULL; self->monsterinfo.aiflags |= AI_GOOD_GUY; gi.linkentity(self); if (self->spawnflags & 16) /* Stand Ground */ { self->monsterinfo.aiflags |= AI_STAND_GROUND; } self->monsterinfo.currentmove = &insane_move_stand_normal; self->monsterinfo.scale = MODEL_SCALE; if (self->spawnflags & 8) /* Crucified ? */ { VectorSet(self->mins, -16, 0, 0); VectorSet(self->maxs, 16, 8, 32); self->flags |= FL_NO_KNOCKBACK; flymonster_start(self); } else { walkmonster_start(self); self->s.skinnum = randk() % 3; } } yquake2-QUAKE2_5_32/src/game/monster/tank/0000755000175000017500000000000012615162034020743 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/tank/tank.c0000644000175000017500000005142612615162034022054 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Tank and Tank Commander. * * ======================================================================= */ #include "../../header/local.h" #include "tank.h" void tank_refire_rocket(edict_t *self); void tank_doattack_rocket(edict_t *self); void tank_reattack_blaster(edict_t *self); static int sound_thud; static int sound_pain; static int sound_idle; static int sound_die; static int sound_step; static int sound_sight; static int sound_windup; static int sound_strike; void tank_sight(edict_t *self, edict_t *other) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); } void tank_footstep(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_BODY, sound_step, 1, ATTN_NORM, 0); } void tank_thud(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_BODY, sound_thud, 1, ATTN_NORM, 0); } void tank_windup(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_windup, 1, ATTN_NORM, 0); } void tank_idle(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0); } mframe_t tank_frames_stand[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t tank_move_stand = { FRAME_stand01, FRAME_stand30, tank_frames_stand, NULL }; void tank_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &tank_move_stand; } void tank_walk(edict_t *self); mframe_t tank_frames_start_walk[] = { {ai_walk, 0, NULL}, {ai_walk, 6, NULL}, {ai_walk, 6, NULL}, {ai_walk, 11, tank_footstep} }; mmove_t tank_move_start_walk = { FRAME_walk01, FRAME_walk04, tank_frames_start_walk, tank_walk }; mframe_t tank_frames_walk[] = { {ai_walk, 4, NULL}, {ai_walk, 5, NULL}, {ai_walk, 3, NULL}, {ai_walk, 2, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, tank_footstep}, {ai_walk, 3, NULL}, {ai_walk, 5, NULL}, {ai_walk, 4, NULL}, {ai_walk, 5, NULL}, {ai_walk, 7, NULL}, {ai_walk, 7, NULL}, {ai_walk, 6, NULL}, {ai_walk, 6, tank_footstep} }; mmove_t tank_move_walk = { FRAME_walk05, FRAME_walk20, tank_frames_walk, NULL }; mframe_t tank_frames_stop_walk[] = { {ai_walk, 3, NULL}, {ai_walk, 3, NULL}, {ai_walk, 2, NULL}, {ai_walk, 2, NULL}, {ai_walk, 4, tank_footstep} }; mmove_t tank_move_stop_walk = { FRAME_walk21, FRAME_walk25, tank_frames_stop_walk, tank_stand }; void tank_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &tank_move_walk; } void tank_run(edict_t *self); mframe_t tank_frames_start_run[] = { {ai_run, 0, NULL}, {ai_run, 6, NULL}, {ai_run, 6, NULL}, {ai_run, 11, tank_footstep} }; mmove_t tank_move_start_run = { FRAME_walk01, FRAME_walk04, tank_frames_start_run, tank_run }; mframe_t tank_frames_run[] = { {ai_run, 4, NULL}, {ai_run, 5, NULL}, {ai_run, 3, NULL}, {ai_run, 2, NULL}, {ai_run, 5, NULL}, {ai_run, 5, NULL}, {ai_run, 4, NULL}, {ai_run, 4, tank_footstep}, {ai_run, 3, NULL}, {ai_run, 5, NULL}, {ai_run, 4, NULL}, {ai_run, 5, NULL}, {ai_run, 7, NULL}, {ai_run, 7, NULL}, {ai_run, 6, NULL}, {ai_run, 6, tank_footstep} }; mmove_t tank_move_run = { FRAME_walk05, FRAME_walk20, tank_frames_run, NULL }; mframe_t tank_frames_stop_run[] = { {ai_run, 3, NULL}, {ai_run, 3, NULL}, {ai_run, 2, NULL}, {ai_run, 2, NULL}, {ai_run, 4, tank_footstep} }; mmove_t tank_move_stop_run = { FRAME_walk21, FRAME_walk25, tank_frames_stop_run, tank_walk }; void tank_run(edict_t *self) { if (!self) { return; } if (self->enemy && self->enemy->client) { self->monsterinfo.aiflags |= AI_BRUTAL; } else { self->monsterinfo.aiflags &= ~AI_BRUTAL; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &tank_move_stand; return; } if ((self->monsterinfo.currentmove == &tank_move_walk) || (self->monsterinfo.currentmove == &tank_move_start_run)) { self->monsterinfo.currentmove = &tank_move_run; } else { self->monsterinfo.currentmove = &tank_move_start_run; } } mframe_t tank_frames_pain1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t tank_move_pain1 = { FRAME_pain101, FRAME_pain104, tank_frames_pain1, tank_run }; mframe_t tank_frames_pain2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t tank_move_pain2 = { FRAME_pain201, FRAME_pain205, tank_frames_pain2, tank_run }; mframe_t tank_frames_pain3[] = { {ai_move, -7, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 3, NULL}, {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, tank_footstep} }; mmove_t tank_move_pain3 = { FRAME_pain301, FRAME_pain316, tank_frames_pain3, tank_run }; void tank_pain(edict_t *self, edict_t *other /* other */, float kick /* other */, int damage) { if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum |= 1; } if (damage <= 10) { return; } if (level.time < self->pain_debounce_time) { return; } if (damage <= 30) { if (random() > 0.2) { return; } } /* If hard or nightmare, don't go into pain while attacking */ if (skill->value >= 2) { if ((self->s.frame >= FRAME_attak301) && (self->s.frame <= FRAME_attak330)) { return; } if ((self->s.frame >= FRAME_attak101) && (self->s.frame <= FRAME_attak116)) { return; } } self->pain_debounce_time = level.time + 3; gi.sound(self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0); if (skill->value == 3) { return; /* no pain anims in nightmare */ } if (damage <= 30) { self->monsterinfo.currentmove = &tank_move_pain1; } else if (damage <= 60) { self->monsterinfo.currentmove = &tank_move_pain2; } else { self->monsterinfo.currentmove = &tank_move_pain3; } } void TankBlaster(edict_t *self) { vec3_t forward, right; vec3_t start; vec3_t end; vec3_t dir; int flash_number; if (!self) { return; } if (self->s.frame == FRAME_attak110) { flash_number = MZ2_TANK_BLASTER_1; } else if (self->s.frame == FRAME_attak113) { flash_number = MZ2_TANK_BLASTER_2; } else { flash_number = MZ2_TANK_BLASTER_3; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start); VectorCopy(self->enemy->s.origin, end); end[2] += self->enemy->viewheight; VectorSubtract(end, start, dir); monster_fire_blaster(self, start, dir, 30, 800, flash_number, EF_BLASTER); } void TankStrike(edict_t *self) { gi.sound(self, CHAN_WEAPON, sound_strike, 1, ATTN_NORM, 0); } void TankRocket(edict_t *self) { vec3_t forward, right; vec3_t start; vec3_t dir; vec3_t vec; int flash_number; if (!self) { return; } if (self->s.frame == FRAME_attak324) { flash_number = MZ2_TANK_ROCKET_1; } else if (self->s.frame == FRAME_attak327) { flash_number = MZ2_TANK_ROCKET_2; } else { flash_number = MZ2_TANK_ROCKET_3; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start); VectorCopy(self->enemy->s.origin, vec); vec[2] += self->enemy->viewheight; VectorSubtract(vec, start, dir); VectorNormalize(dir); monster_fire_rocket(self, start, dir, 50, 550, flash_number); } void TankMachineGun(edict_t *self) { vec3_t dir; vec3_t vec; vec3_t start; vec3_t forward, right; int flash_number; if (!self) { return; } flash_number = MZ2_TANK_MACHINEGUN_1 + (self->s.frame - FRAME_attak406); AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start); if (self->enemy) { VectorCopy(self->enemy->s.origin, vec); vec[2] += self->enemy->viewheight; VectorSubtract(vec, start, vec); vectoangles(vec, vec); dir[0] = vec[0]; } else { dir[0] = 0; } if (self->s.frame <= FRAME_attak415) { dir[1] = self->s.angles[1] - 8 * (self->s.frame - FRAME_attak411); } else { dir[1] = self->s.angles[1] + 8 * (self->s.frame - FRAME_attak419); } dir[2] = 0; AngleVectors(dir, forward, NULL, NULL); monster_fire_bullet(self, start, forward, 20, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number); } mframe_t tank_frames_attack_blast[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, -1, NULL}, {ai_charge, -2, NULL}, {ai_charge, -1, NULL}, {ai_charge, -1, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, TankBlaster}, /* 10 */ {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, TankBlaster}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, TankBlaster} /* 16 */ }; mmove_t tank_move_attack_blast = { FRAME_attak101, FRAME_attak116, tank_frames_attack_blast, tank_reattack_blaster }; mframe_t tank_frames_reattack_blast[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, TankBlaster}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, TankBlaster} /* 16 */ }; mmove_t tank_move_reattack_blast = { FRAME_attak111, FRAME_attak116, tank_frames_reattack_blast, tank_reattack_blaster }; mframe_t tank_frames_attack_post_blast[] = { {ai_move, 0, NULL}, /* 17 */ {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, 3, NULL}, {ai_move, 2, NULL}, {ai_move, -2, tank_footstep} /* 22 */ }; mmove_t tank_move_attack_post_blast = { FRAME_attak117, FRAME_attak122, tank_frames_attack_post_blast, tank_run }; void tank_reattack_blaster(edict_t *self) { if (!self) { return; } if (skill->value >= 2) { if (visible(self, self->enemy)) { if (self->enemy->health > 0) { if (random() <= 0.6) { self->monsterinfo.currentmove = &tank_move_reattack_blast; return; } } } } self->monsterinfo.currentmove = &tank_move_attack_post_blast; } void tank_poststrike(edict_t *self) { if (!self) { return; } self->enemy = NULL; tank_run(self); } mframe_t tank_frames_attack_strike[] = { {ai_move, 3, NULL}, {ai_move, 2, NULL}, {ai_move, 2, NULL}, {ai_move, 1, NULL}, {ai_move, 6, NULL}, {ai_move, 7, NULL}, {ai_move, 9, tank_footstep}, {ai_move, 2, NULL}, {ai_move, 1, NULL}, {ai_move, 2, NULL}, {ai_move, 2, tank_footstep}, {ai_move, 2, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -2, NULL}, {ai_move, -2, NULL}, {ai_move, 0, tank_windup}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, TankStrike}, {ai_move, 0, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, -3, NULL}, {ai_move, -10, NULL}, {ai_move, -10, NULL}, {ai_move, -2, NULL}, {ai_move, -3, NULL}, {ai_move, -2, tank_footstep} }; mmove_t tank_move_attack_strike = { FRAME_attak201, FRAME_attak238, tank_frames_attack_strike, tank_poststrike }; mframe_t tank_frames_attack_pre_rocket[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, /* 10 */ {ai_charge, 0, NULL}, {ai_charge, 1, NULL}, {ai_charge, 2, NULL}, {ai_charge, 7, NULL}, {ai_charge, 7, NULL}, {ai_charge, 7, tank_footstep}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, /* 20 */ {ai_charge, -3, NULL} }; mmove_t tank_move_attack_pre_rocket = { FRAME_attak301, FRAME_attak321, tank_frames_attack_pre_rocket, tank_doattack_rocket }; mframe_t tank_frames_attack_fire_rocket[] = { {ai_charge, -3, NULL}, /* Loop Start 22 */ {ai_charge, 0, NULL}, {ai_charge, 0, TankRocket}, /* 24 */ {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, TankRocket}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, -1, TankRocket} /* 30 Loop End */ }; mmove_t tank_move_attack_fire_rocket = { FRAME_attak322, FRAME_attak330, tank_frames_attack_fire_rocket, tank_refire_rocket }; mframe_t tank_frames_attack_post_rocket[] = { {ai_charge, 0, NULL}, /* 31 */ {ai_charge, -1, NULL}, {ai_charge, -1, NULL}, {ai_charge, 0, NULL}, {ai_charge, 2, NULL}, {ai_charge, 3, NULL}, {ai_charge, 4, NULL}, {ai_charge, 2, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, /* 40 */ {ai_charge, 0, NULL}, {ai_charge, -9, NULL}, {ai_charge, -8, NULL}, {ai_charge, -7, NULL}, {ai_charge, -1, NULL}, {ai_charge, -1, tank_footstep}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, /* 50 */ {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t tank_move_attack_post_rocket = { FRAME_attak331, FRAME_attak353, tank_frames_attack_post_rocket, tank_run }; mframe_t tank_frames_attack_chain[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {NULL, 0, TankMachineGun}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t tank_move_attack_chain = { FRAME_attak401, FRAME_attak429, tank_frames_attack_chain, tank_run }; void tank_refire_rocket(edict_t *self) { if (!self) { return; } /* Only on hard or nightmare */ if (skill->value >= 2) { if (self->enemy->health > 0) { if (visible(self, self->enemy)) { if (random() <= 0.4) { self->monsterinfo.currentmove = &tank_move_attack_fire_rocket; return; } } } } self->monsterinfo.currentmove = &tank_move_attack_post_rocket; } void tank_doattack_rocket(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &tank_move_attack_fire_rocket; } void tank_attack(edict_t *self) { vec3_t vec; float range; float r; if (!self) { return; } if (self->enemy->health < 0) { self->monsterinfo.currentmove = &tank_move_attack_strike; self->monsterinfo.aiflags &= ~AI_BRUTAL; return; } VectorSubtract(self->enemy->s.origin, self->s.origin, vec); range = VectorLength(vec); r = random(); if (range <= 125) { if (r < 0.4) { self->monsterinfo.currentmove = &tank_move_attack_chain; } else { self->monsterinfo.currentmove = &tank_move_attack_blast; } } else if (range <= 250) { if (r < 0.5) { self->monsterinfo.currentmove = &tank_move_attack_chain; } else { self->monsterinfo.currentmove = &tank_move_attack_blast; } } else { if (r < 0.33) { self->monsterinfo.currentmove = &tank_move_attack_chain; } else if (r < 0.66) { self->monsterinfo.currentmove = &tank_move_attack_pre_rocket; self->pain_debounce_time = level.time + 5.0; /* no pain for a while */ } else { self->monsterinfo.currentmove = &tank_move_attack_blast; } } } void tank_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -16, -16, -16); VectorSet(self->maxs, 16, 16, -0); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } mframe_t tank_frames_death1[] = { {ai_move, -7, NULL}, {ai_move, -2, NULL}, {ai_move, -2, NULL}, {ai_move, 1, NULL}, {ai_move, 3, NULL}, {ai_move, 6, NULL}, {ai_move, 1, NULL}, {ai_move, 1, NULL}, {ai_move, 2, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -2, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -3, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -4, NULL}, {ai_move, -6, NULL}, {ai_move, -4, NULL}, {ai_move, -5, NULL}, {ai_move, -7, NULL}, {ai_move, -15, tank_thud}, {ai_move, -5, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t tank_move_death = { FRAME_death101, FRAME_death132, tank_frames_death1, tank_dead }; void tank_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage, vec3_t point /* unused */) { int n; if (!self) { return; } /* check for gib */ if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 1; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_metal/tris.md2", damage, GIB_METALLIC); } ThrowGib(self, "models/objects/gibs/chest/tris.md2", damage, GIB_ORGANIC); ThrowHead(self, "models/objects/gibs/gear/tris.md2", damage, GIB_METALLIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } /* regular death */ gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0); self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; self->monsterinfo.currentmove = &tank_move_death; } /* * QUAKED monster_tank (1 .5 0) (-32 -32 -16) (32 32 72) Ambush Trigger_Spawn Sight * QUAKED monster_tank_commander (1 .5 0) (-32 -32 -16) (32 32 72) Ambush Trigger_Spawn Sight */ void SP_monster_tank(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } self->s.modelindex = gi.modelindex("models/monsters/tank/tris.md2"); VectorSet(self->mins, -32, -32, -16); VectorSet(self->maxs, 32, 32, 72); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; sound_pain = gi.soundindex("tank/tnkpain2.wav"); sound_thud = gi.soundindex("tank/tnkdeth2.wav"); sound_idle = gi.soundindex("tank/tnkidle1.wav"); sound_die = gi.soundindex("tank/death.wav"); sound_step = gi.soundindex("tank/step.wav"); sound_windup = gi.soundindex("tank/tnkatck4.wav"); sound_strike = gi.soundindex("tank/tnkatck5.wav"); sound_sight = gi.soundindex("tank/sight1.wav"); gi.soundindex("tank/tnkatck1.wav"); gi.soundindex("tank/tnkatk2a.wav"); gi.soundindex("tank/tnkatk2b.wav"); gi.soundindex("tank/tnkatk2c.wav"); gi.soundindex("tank/tnkatk2d.wav"); gi.soundindex("tank/tnkatk2e.wav"); gi.soundindex("tank/tnkatck3.wav"); if (strcmp(self->classname, "monster_tank_commander") == 0) { self->health = 1000; self->gib_health = -225; } else { self->health = 750; self->gib_health = -200; } self->mass = 500; self->pain = tank_pain; self->die = tank_die; self->monsterinfo.stand = tank_stand; self->monsterinfo.walk = tank_walk; self->monsterinfo.run = tank_run; self->monsterinfo.dodge = NULL; self->monsterinfo.attack = tank_attack; self->monsterinfo.melee = NULL; self->monsterinfo.sight = tank_sight; self->monsterinfo.idle = tank_idle; gi.linkentity(self); self->monsterinfo.currentmove = &tank_move_stand; self->monsterinfo.scale = MODEL_SCALE; walkmonster_start(self); if (strcmp(self->classname, "monster_tank_commander") == 0) { self->s.skinnum = 2; } } yquake2-QUAKE2_5_32/src/game/monster/tank/tank.h0000644000175000017500000002102612615162034022052 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Tank and Tank Commander animations. * * ======================================================================= */ #define FRAME_stand01 0 #define FRAME_stand02 1 #define FRAME_stand03 2 #define FRAME_stand04 3 #define FRAME_stand05 4 #define FRAME_stand06 5 #define FRAME_stand07 6 #define FRAME_stand08 7 #define FRAME_stand09 8 #define FRAME_stand10 9 #define FRAME_stand11 10 #define FRAME_stand12 11 #define FRAME_stand13 12 #define FRAME_stand14 13 #define FRAME_stand15 14 #define FRAME_stand16 15 #define FRAME_stand17 16 #define FRAME_stand18 17 #define FRAME_stand19 18 #define FRAME_stand20 19 #define FRAME_stand21 20 #define FRAME_stand22 21 #define FRAME_stand23 22 #define FRAME_stand24 23 #define FRAME_stand25 24 #define FRAME_stand26 25 #define FRAME_stand27 26 #define FRAME_stand28 27 #define FRAME_stand29 28 #define FRAME_stand30 29 #define FRAME_walk01 30 #define FRAME_walk02 31 #define FRAME_walk03 32 #define FRAME_walk04 33 #define FRAME_walk05 34 #define FRAME_walk06 35 #define FRAME_walk07 36 #define FRAME_walk08 37 #define FRAME_walk09 38 #define FRAME_walk10 39 #define FRAME_walk11 40 #define FRAME_walk12 41 #define FRAME_walk13 42 #define FRAME_walk14 43 #define FRAME_walk15 44 #define FRAME_walk16 45 #define FRAME_walk17 46 #define FRAME_walk18 47 #define FRAME_walk19 48 #define FRAME_walk20 49 #define FRAME_walk21 50 #define FRAME_walk22 51 #define FRAME_walk23 52 #define FRAME_walk24 53 #define FRAME_walk25 54 #define FRAME_attak101 55 #define FRAME_attak102 56 #define FRAME_attak103 57 #define FRAME_attak104 58 #define FRAME_attak105 59 #define FRAME_attak106 60 #define FRAME_attak107 61 #define FRAME_attak108 62 #define FRAME_attak109 63 #define FRAME_attak110 64 #define FRAME_attak111 65 #define FRAME_attak112 66 #define FRAME_attak113 67 #define FRAME_attak114 68 #define FRAME_attak115 69 #define FRAME_attak116 70 #define FRAME_attak117 71 #define FRAME_attak118 72 #define FRAME_attak119 73 #define FRAME_attak120 74 #define FRAME_attak121 75 #define FRAME_attak122 76 #define FRAME_attak201 77 #define FRAME_attak202 78 #define FRAME_attak203 79 #define FRAME_attak204 80 #define FRAME_attak205 81 #define FRAME_attak206 82 #define FRAME_attak207 83 #define FRAME_attak208 84 #define FRAME_attak209 85 #define FRAME_attak210 86 #define FRAME_attak211 87 #define FRAME_attak212 88 #define FRAME_attak213 89 #define FRAME_attak214 90 #define FRAME_attak215 91 #define FRAME_attak216 92 #define FRAME_attak217 93 #define FRAME_attak218 94 #define FRAME_attak219 95 #define FRAME_attak220 96 #define FRAME_attak221 97 #define FRAME_attak222 98 #define FRAME_attak223 99 #define FRAME_attak224 100 #define FRAME_attak225 101 #define FRAME_attak226 102 #define FRAME_attak227 103 #define FRAME_attak228 104 #define FRAME_attak229 105 #define FRAME_attak230 106 #define FRAME_attak231 107 #define FRAME_attak232 108 #define FRAME_attak233 109 #define FRAME_attak234 110 #define FRAME_attak235 111 #define FRAME_attak236 112 #define FRAME_attak237 113 #define FRAME_attak238 114 #define FRAME_attak301 115 #define FRAME_attak302 116 #define FRAME_attak303 117 #define FRAME_attak304 118 #define FRAME_attak305 119 #define FRAME_attak306 120 #define FRAME_attak307 121 #define FRAME_attak308 122 #define FRAME_attak309 123 #define FRAME_attak310 124 #define FRAME_attak311 125 #define FRAME_attak312 126 #define FRAME_attak313 127 #define FRAME_attak314 128 #define FRAME_attak315 129 #define FRAME_attak316 130 #define FRAME_attak317 131 #define FRAME_attak318 132 #define FRAME_attak319 133 #define FRAME_attak320 134 #define FRAME_attak321 135 #define FRAME_attak322 136 #define FRAME_attak323 137 #define FRAME_attak324 138 #define FRAME_attak325 139 #define FRAME_attak326 140 #define FRAME_attak327 141 #define FRAME_attak328 142 #define FRAME_attak329 143 #define FRAME_attak330 144 #define FRAME_attak331 145 #define FRAME_attak332 146 #define FRAME_attak333 147 #define FRAME_attak334 148 #define FRAME_attak335 149 #define FRAME_attak336 150 #define FRAME_attak337 151 #define FRAME_attak338 152 #define FRAME_attak339 153 #define FRAME_attak340 154 #define FRAME_attak341 155 #define FRAME_attak342 156 #define FRAME_attak343 157 #define FRAME_attak344 158 #define FRAME_attak345 159 #define FRAME_attak346 160 #define FRAME_attak347 161 #define FRAME_attak348 162 #define FRAME_attak349 163 #define FRAME_attak350 164 #define FRAME_attak351 165 #define FRAME_attak352 166 #define FRAME_attak353 167 #define FRAME_attak401 168 #define FRAME_attak402 169 #define FRAME_attak403 170 #define FRAME_attak404 171 #define FRAME_attak405 172 #define FRAME_attak406 173 #define FRAME_attak407 174 #define FRAME_attak408 175 #define FRAME_attak409 176 #define FRAME_attak410 177 #define FRAME_attak411 178 #define FRAME_attak412 179 #define FRAME_attak413 180 #define FRAME_attak414 181 #define FRAME_attak415 182 #define FRAME_attak416 183 #define FRAME_attak417 184 #define FRAME_attak418 185 #define FRAME_attak419 186 #define FRAME_attak420 187 #define FRAME_attak421 188 #define FRAME_attak422 189 #define FRAME_attak423 190 #define FRAME_attak424 191 #define FRAME_attak425 192 #define FRAME_attak426 193 #define FRAME_attak427 194 #define FRAME_attak428 195 #define FRAME_attak429 196 #define FRAME_pain101 197 #define FRAME_pain102 198 #define FRAME_pain103 199 #define FRAME_pain104 200 #define FRAME_pain201 201 #define FRAME_pain202 202 #define FRAME_pain203 203 #define FRAME_pain204 204 #define FRAME_pain205 205 #define FRAME_pain301 206 #define FRAME_pain302 207 #define FRAME_pain303 208 #define FRAME_pain304 209 #define FRAME_pain305 210 #define FRAME_pain306 211 #define FRAME_pain307 212 #define FRAME_pain308 213 #define FRAME_pain309 214 #define FRAME_pain310 215 #define FRAME_pain311 216 #define FRAME_pain312 217 #define FRAME_pain313 218 #define FRAME_pain314 219 #define FRAME_pain315 220 #define FRAME_pain316 221 #define FRAME_death101 222 #define FRAME_death102 223 #define FRAME_death103 224 #define FRAME_death104 225 #define FRAME_death105 226 #define FRAME_death106 227 #define FRAME_death107 228 #define FRAME_death108 229 #define FRAME_death109 230 #define FRAME_death110 231 #define FRAME_death111 232 #define FRAME_death112 233 #define FRAME_death113 234 #define FRAME_death114 235 #define FRAME_death115 236 #define FRAME_death116 237 #define FRAME_death117 238 #define FRAME_death118 239 #define FRAME_death119 240 #define FRAME_death120 241 #define FRAME_death121 242 #define FRAME_death122 243 #define FRAME_death123 244 #define FRAME_death124 245 #define FRAME_death125 246 #define FRAME_death126 247 #define FRAME_death127 248 #define FRAME_death128 249 #define FRAME_death129 250 #define FRAME_death130 251 #define FRAME_death131 252 #define FRAME_death132 253 #define FRAME_recln101 254 #define FRAME_recln102 255 #define FRAME_recln103 256 #define FRAME_recln104 257 #define FRAME_recln105 258 #define FRAME_recln106 259 #define FRAME_recln107 260 #define FRAME_recln108 261 #define FRAME_recln109 262 #define FRAME_recln110 263 #define FRAME_recln111 264 #define FRAME_recln112 265 #define FRAME_recln113 266 #define FRAME_recln114 267 #define FRAME_recln115 268 #define FRAME_recln116 269 #define FRAME_recln117 270 #define FRAME_recln118 271 #define FRAME_recln119 272 #define FRAME_recln120 273 #define FRAME_recln121 274 #define FRAME_recln122 275 #define FRAME_recln123 276 #define FRAME_recln124 277 #define FRAME_recln125 278 #define FRAME_recln126 279 #define FRAME_recln127 280 #define FRAME_recln128 281 #define FRAME_recln129 282 #define FRAME_recln130 283 #define FRAME_recln131 284 #define FRAME_recln132 285 #define FRAME_recln133 286 #define FRAME_recln134 287 #define FRAME_recln135 288 #define FRAME_recln136 289 #define FRAME_recln137 290 #define FRAME_recln138 291 #define FRAME_recln139 292 #define FRAME_recln140 293 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/medic/0000755000175000017500000000000012615162034021067 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/medic/medic.h0000644000175000017500000001536312615162034022331 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Medic animations. * * ======================================================================= */ #define FRAME_walk1 0 #define FRAME_walk2 1 #define FRAME_walk3 2 #define FRAME_walk4 3 #define FRAME_walk5 4 #define FRAME_walk6 5 #define FRAME_walk7 6 #define FRAME_walk8 7 #define FRAME_walk9 8 #define FRAME_walk10 9 #define FRAME_walk11 10 #define FRAME_walk12 11 #define FRAME_wait1 12 #define FRAME_wait2 13 #define FRAME_wait3 14 #define FRAME_wait4 15 #define FRAME_wait5 16 #define FRAME_wait6 17 #define FRAME_wait7 18 #define FRAME_wait8 19 #define FRAME_wait9 20 #define FRAME_wait10 21 #define FRAME_wait11 22 #define FRAME_wait12 23 #define FRAME_wait13 24 #define FRAME_wait14 25 #define FRAME_wait15 26 #define FRAME_wait16 27 #define FRAME_wait17 28 #define FRAME_wait18 29 #define FRAME_wait19 30 #define FRAME_wait20 31 #define FRAME_wait21 32 #define FRAME_wait22 33 #define FRAME_wait23 34 #define FRAME_wait24 35 #define FRAME_wait25 36 #define FRAME_wait26 37 #define FRAME_wait27 38 #define FRAME_wait28 39 #define FRAME_wait29 40 #define FRAME_wait30 41 #define FRAME_wait31 42 #define FRAME_wait32 43 #define FRAME_wait33 44 #define FRAME_wait34 45 #define FRAME_wait35 46 #define FRAME_wait36 47 #define FRAME_wait37 48 #define FRAME_wait38 49 #define FRAME_wait39 50 #define FRAME_wait40 51 #define FRAME_wait41 52 #define FRAME_wait42 53 #define FRAME_wait43 54 #define FRAME_wait44 55 #define FRAME_wait45 56 #define FRAME_wait46 57 #define FRAME_wait47 58 #define FRAME_wait48 59 #define FRAME_wait49 60 #define FRAME_wait50 61 #define FRAME_wait51 62 #define FRAME_wait52 63 #define FRAME_wait53 64 #define FRAME_wait54 65 #define FRAME_wait55 66 #define FRAME_wait56 67 #define FRAME_wait57 68 #define FRAME_wait58 69 #define FRAME_wait59 70 #define FRAME_wait60 71 #define FRAME_wait61 72 #define FRAME_wait62 73 #define FRAME_wait63 74 #define FRAME_wait64 75 #define FRAME_wait65 76 #define FRAME_wait66 77 #define FRAME_wait67 78 #define FRAME_wait68 79 #define FRAME_wait69 80 #define FRAME_wait70 81 #define FRAME_wait71 82 #define FRAME_wait72 83 #define FRAME_wait73 84 #define FRAME_wait74 85 #define FRAME_wait75 86 #define FRAME_wait76 87 #define FRAME_wait77 88 #define FRAME_wait78 89 #define FRAME_wait79 90 #define FRAME_wait80 91 #define FRAME_wait81 92 #define FRAME_wait82 93 #define FRAME_wait83 94 #define FRAME_wait84 95 #define FRAME_wait85 96 #define FRAME_wait86 97 #define FRAME_wait87 98 #define FRAME_wait88 99 #define FRAME_wait89 100 #define FRAME_wait90 101 #define FRAME_run1 102 #define FRAME_run2 103 #define FRAME_run3 104 #define FRAME_run4 105 #define FRAME_run5 106 #define FRAME_run6 107 #define FRAME_paina1 108 #define FRAME_paina2 109 #define FRAME_paina3 110 #define FRAME_paina4 111 #define FRAME_paina5 112 #define FRAME_paina6 113 #define FRAME_paina7 114 #define FRAME_paina8 115 #define FRAME_painb1 116 #define FRAME_painb2 117 #define FRAME_painb3 118 #define FRAME_painb4 119 #define FRAME_painb5 120 #define FRAME_painb6 121 #define FRAME_painb7 122 #define FRAME_painb8 123 #define FRAME_painb9 124 #define FRAME_painb10 125 #define FRAME_painb11 126 #define FRAME_painb12 127 #define FRAME_painb13 128 #define FRAME_painb14 129 #define FRAME_painb15 130 #define FRAME_duck1 131 #define FRAME_duck2 132 #define FRAME_duck3 133 #define FRAME_duck4 134 #define FRAME_duck5 135 #define FRAME_duck6 136 #define FRAME_duck7 137 #define FRAME_duck8 138 #define FRAME_duck9 139 #define FRAME_duck10 140 #define FRAME_duck11 141 #define FRAME_duck12 142 #define FRAME_duck13 143 #define FRAME_duck14 144 #define FRAME_duck15 145 #define FRAME_duck16 146 #define FRAME_death1 147 #define FRAME_death2 148 #define FRAME_death3 149 #define FRAME_death4 150 #define FRAME_death5 151 #define FRAME_death6 152 #define FRAME_death7 153 #define FRAME_death8 154 #define FRAME_death9 155 #define FRAME_death10 156 #define FRAME_death11 157 #define FRAME_death12 158 #define FRAME_death13 159 #define FRAME_death14 160 #define FRAME_death15 161 #define FRAME_death16 162 #define FRAME_death17 163 #define FRAME_death18 164 #define FRAME_death19 165 #define FRAME_death20 166 #define FRAME_death21 167 #define FRAME_death22 168 #define FRAME_death23 169 #define FRAME_death24 170 #define FRAME_death25 171 #define FRAME_death26 172 #define FRAME_death27 173 #define FRAME_death28 174 #define FRAME_death29 175 #define FRAME_death30 176 #define FRAME_attack1 177 #define FRAME_attack2 178 #define FRAME_attack3 179 #define FRAME_attack4 180 #define FRAME_attack5 181 #define FRAME_attack6 182 #define FRAME_attack7 183 #define FRAME_attack8 184 #define FRAME_attack9 185 #define FRAME_attack10 186 #define FRAME_attack11 187 #define FRAME_attack12 188 #define FRAME_attack13 189 #define FRAME_attack14 190 #define FRAME_attack15 191 #define FRAME_attack16 192 #define FRAME_attack17 193 #define FRAME_attack18 194 #define FRAME_attack19 195 #define FRAME_attack20 196 #define FRAME_attack21 197 #define FRAME_attack22 198 #define FRAME_attack23 199 #define FRAME_attack24 200 #define FRAME_attack25 201 #define FRAME_attack26 202 #define FRAME_attack27 203 #define FRAME_attack28 204 #define FRAME_attack29 205 #define FRAME_attack30 206 #define FRAME_attack31 207 #define FRAME_attack32 208 #define FRAME_attack33 209 #define FRAME_attack34 210 #define FRAME_attack35 211 #define FRAME_attack36 212 #define FRAME_attack37 213 #define FRAME_attack38 214 #define FRAME_attack39 215 #define FRAME_attack40 216 #define FRAME_attack41 217 #define FRAME_attack42 218 #define FRAME_attack43 219 #define FRAME_attack44 220 #define FRAME_attack45 221 #define FRAME_attack46 222 #define FRAME_attack47 223 #define FRAME_attack48 224 #define FRAME_attack49 225 #define FRAME_attack50 226 #define FRAME_attack51 227 #define FRAME_attack52 228 #define FRAME_attack53 229 #define FRAME_attack54 230 #define FRAME_attack55 231 #define FRAME_attack56 232 #define FRAME_attack57 233 #define FRAME_attack58 234 #define FRAME_attack59 235 #define FRAME_attack60 236 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/medic/medic.c0000644000175000017500000004606212615162034022324 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Medic. * * ======================================================================= */ #include "../../header/local.h" #include "medic.h" qboolean visible(edict_t *self, edict_t *other); static int sound_idle1; static int sound_pain1; static int sound_pain2; static int sound_die; static int sound_sight; static int sound_search; static int sound_hook_launch; static int sound_hook_hit; static int sound_hook_heal; static int sound_hook_retract; edict_t * medic_FindDeadMonster(edict_t *self) { edict_t *ent = NULL; edict_t *best = NULL; if (!self) { return NULL; } while ((ent = findradius(ent, self->s.origin, 1024)) != NULL) { if (ent == self) { continue; } if (!(ent->svflags & SVF_MONSTER)) { continue; } if (ent->monsterinfo.aiflags & AI_GOOD_GUY) { continue; } if (ent->owner) { continue; } if (ent->health > 0) { continue; } if (ent->nextthink) { continue; } if (!visible(self, ent)) { continue; } if (!best) { best = ent; continue; } if (ent->max_health <= best->max_health) { continue; } best = ent; } return best; } void medic_idle(edict_t *self) { edict_t *ent; if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_idle1, 1, ATTN_IDLE, 0); ent = medic_FindDeadMonster(self); if (ent) { self->enemy = ent; self->enemy->owner = self; self->monsterinfo.aiflags |= AI_MEDIC; FoundTarget(self); } } void medic_search(edict_t *self) { edict_t *ent; if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_IDLE, 0); if (!self->oldenemy) { ent = medic_FindDeadMonster(self); if (ent) { self->oldenemy = self->enemy; self->enemy = ent; self->enemy->owner = self; self->monsterinfo.aiflags |= AI_MEDIC; FoundTarget(self); } } } void medic_sight(edict_t *self, edict_t *other /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); } mframe_t medic_frames_stand[] = { {ai_stand, 0, medic_idle}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, }; mmove_t medic_move_stand = { FRAME_wait1, FRAME_wait90, medic_frames_stand, NULL }; void medic_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &medic_move_stand; } mframe_t medic_frames_walk[] = { {ai_walk, 6.2, NULL}, {ai_walk, 18.1, NULL}, {ai_walk, 1, NULL}, {ai_walk, 9, NULL}, {ai_walk, 10, NULL}, {ai_walk, 9, NULL}, {ai_walk, 11, NULL}, {ai_walk, 11.6, NULL}, {ai_walk, 2, NULL}, {ai_walk, 9.9, NULL}, {ai_walk, 14, NULL}, {ai_walk, 9.3, NULL} }; mmove_t medic_move_walk = { FRAME_walk1, FRAME_walk12, medic_frames_walk, NULL }; void medic_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &medic_move_walk; } mframe_t medic_frames_run[] = { {ai_run, 18, NULL}, {ai_run, 22.5, NULL}, {ai_run, 25.4, NULL}, {ai_run, 23.4, NULL}, {ai_run, 24, NULL}, {ai_run, 35.6, NULL} }; mmove_t medic_move_run = { FRAME_run1, FRAME_run6, medic_frames_run, NULL }; void medic_run(edict_t *self) { if (!self) { return; } if (!(self->monsterinfo.aiflags & AI_MEDIC)) { edict_t *ent; ent = medic_FindDeadMonster(self); if (ent) { self->oldenemy = self->enemy; self->enemy = ent; self->enemy->owner = self; self->monsterinfo.aiflags |= AI_MEDIC; FoundTarget(self); return; } } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &medic_move_stand; } else { self->monsterinfo.currentmove = &medic_move_run; } } mframe_t medic_frames_pain1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t medic_move_pain1 = { FRAME_paina1, FRAME_paina8, medic_frames_pain1, medic_run }; mframe_t medic_frames_pain2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t medic_move_pain2 = { FRAME_painb1, FRAME_painb15, medic_frames_pain2, medic_run }; void medic_pain(edict_t *self, edict_t *other /* unused */, float kick, int damage /* unused */) { if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; if (skill->value == 3) { return; /* no pain anims in nightmare */ } if (random() < 0.5) { self->monsterinfo.currentmove = &medic_move_pain1; gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); } else { self->monsterinfo.currentmove = &medic_move_pain2; gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); } } void medic_fire_blaster(edict_t *self) { vec3_t start; vec3_t forward, right; vec3_t end; vec3_t dir; int effect; if (!self) { return; } if ((self->s.frame == FRAME_attack9) || (self->s.frame == FRAME_attack12)) { effect = EF_BLASTER; } else if ((self->s.frame == FRAME_attack19) || (self->s.frame == FRAME_attack22) || (self->s.frame == FRAME_attack25) || (self->s.frame == FRAME_attack28)) { effect = EF_HYPERBLASTER; } else { effect = 0; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_MEDIC_BLASTER_1], forward, right, start); VectorCopy(self->enemy->s.origin, end); end[2] += self->enemy->viewheight; VectorSubtract(end, start, dir); monster_fire_blaster(self, start, dir, 2, 1000, MZ2_MEDIC_BLASTER_1, effect); } void medic_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, -8); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } mframe_t medic_frames_death[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t medic_move_death = { FRAME_death1, FRAME_death30, medic_frames_death, medic_dead }; void medic_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage, vec3_t point /* unused */) { int n; if (!self) { return; } /* if we had a pending patient, free him up for another medic */ if ((self->enemy) && (self->enemy->owner == self)) { self->enemy->owner = NULL; } /* check for gib */ if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex( "misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 2; n++) { ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } /* regular death */ gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0); self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; self->monsterinfo.currentmove = &medic_move_death; } void medic_duck_down(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_DUCKED) { return; } self->monsterinfo.aiflags |= AI_DUCKED; self->maxs[2] -= 32; self->takedamage = DAMAGE_YES; self->monsterinfo.pausetime = level.time + 1; gi.linkentity(self); } void medic_duck_hold(edict_t *self) { if (!self) { return; } if (level.time >= self->monsterinfo.pausetime) { self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; } else { self->monsterinfo.aiflags |= AI_HOLD_FRAME; } } void medic_duck_up(edict_t *self) { if (!self) { return; } self->monsterinfo.aiflags &= ~AI_DUCKED; self->maxs[2] += 32; self->takedamage = DAMAGE_AIM; gi.linkentity(self); } mframe_t medic_frames_duck[] = { {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, -1, medic_duck_down}, {ai_move, -1, medic_duck_hold}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, -1, medic_duck_up}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL} }; mmove_t medic_move_duck = { FRAME_duck1, FRAME_duck16, medic_frames_duck, medic_run }; void medic_dodge(edict_t *self, edict_t *attacker, float eta) { if (random() > 0.25) { return; } if (!self->enemy) { self->enemy = attacker; } self->monsterinfo.currentmove = &medic_move_duck; } mframe_t medic_frames_attackHyperBlaster[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, medic_fire_blaster}, {ai_charge, 0, medic_fire_blaster}, {ai_charge, 0, medic_fire_blaster}, {ai_charge, 0, medic_fire_blaster}, {ai_charge, 0, medic_fire_blaster}, {ai_charge, 0, medic_fire_blaster}, {ai_charge, 0, medic_fire_blaster}, {ai_charge, 0, medic_fire_blaster}, {ai_charge, 0, medic_fire_blaster}, {ai_charge, 0, medic_fire_blaster}, {ai_charge, 0, medic_fire_blaster}, {ai_charge, 0, medic_fire_blaster} }; mmove_t medic_move_attackHyperBlaster = { FRAME_attack15, FRAME_attack30, medic_frames_attackHyperBlaster, medic_run }; void medic_continue(edict_t *self) { if (!self) { return; } if (visible(self, self->enemy)) { if (random() <= 0.95) { self->monsterinfo.currentmove = &medic_move_attackHyperBlaster; } } } mframe_t medic_frames_attackBlaster[] = { {ai_charge, 0, NULL}, {ai_charge, 5, NULL}, {ai_charge, 5, NULL}, {ai_charge, 3, NULL}, {ai_charge, 2, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, medic_fire_blaster}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, medic_fire_blaster}, {ai_charge, 0, NULL}, {ai_charge, 0, medic_continue} }; mmove_t medic_move_attackBlaster = {FRAME_attack1, FRAME_attack14, medic_frames_attackBlaster, medic_run}; void medic_hook_launch(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_hook_launch, 1, ATTN_NORM, 0); } void ED_CallSpawn(edict_t *ent); static vec3_t medic_cable_offsets[] = { {45.0, -9.2, 15.5}, {48.4, -9.7, 15.2}, {47.8, -9.8, 15.8}, {47.3, -9.3, 14.3}, {45.4, -10.1, 13.1}, {41.9, -12.7, 12.0}, {37.8, -15.8, 11.2}, {34.3, -18.4, 10.7}, {32.7, -19.7, 10.4}, {32.7, -19.7, 10.4} }; void medic_cable_attack(edict_t *self) { vec3_t offset, start, end, f, r; trace_t tr; vec3_t dir, angles; float distance; if (!self) { return; } if (!self->enemy->inuse) { return; } AngleVectors(self->s.angles, f, r, NULL); VectorCopy(medic_cable_offsets[self->s.frame - FRAME_attack42], offset); G_ProjectSource(self->s.origin, offset, f, r, start); /* check for max distance */ VectorSubtract(start, self->enemy->s.origin, dir); distance = VectorLength(dir); if (distance > 256) { return; } /* check for min/max pitch */ vectoangles(dir, angles); if (angles[0] < -180) { angles[0] += 360; } if (fabs(angles[0]) > 45) { return; } tr = gi.trace(start, NULL, NULL, self->enemy->s.origin, self, MASK_SHOT); if ((tr.fraction != 1.0) && (tr.ent != self->enemy)) { return; } if (self->s.frame == FRAME_attack43) { gi.sound(self->enemy, CHAN_AUTO, sound_hook_hit, 1, ATTN_NORM, 0); self->enemy->monsterinfo.aiflags |= AI_RESURRECTING; } else if (self->s.frame == FRAME_attack50) { self->enemy->spawnflags = 0; self->enemy->monsterinfo.aiflags = 0; self->enemy->target = NULL; self->enemy->targetname = NULL; self->enemy->combattarget = NULL; self->enemy->deathtarget = NULL; self->enemy->owner = self; ED_CallSpawn(self->enemy); self->enemy->owner = NULL; if (self->enemy->think) { self->enemy->nextthink = level.time; self->enemy->think(self->enemy); } self->enemy->monsterinfo.aiflags |= AI_RESURRECTING; if (self->oldenemy && self->oldenemy->client) { self->enemy->enemy = self->oldenemy; FoundTarget(self->enemy); } } else { if (self->s.frame == FRAME_attack44) { gi.sound(self, CHAN_WEAPON, sound_hook_heal, 1, ATTN_NORM, 0); } } /* adjust start for beam origin being in middle of a segment */ VectorMA(start, 8, f, start); /* adjust end z for end spot since the monster is currently dead */ VectorCopy(self->enemy->s.origin, end); end[2] = self->enemy->absmin[2] + self->enemy->size[2] / 2; gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_MEDIC_CABLE_ATTACK); gi.WriteShort(self - g_edicts); gi.WritePosition(start); gi.WritePosition(end); gi.multicast(self->s.origin, MULTICAST_PVS); } void medic_hook_retract(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_hook_retract, 1, ATTN_NORM, 0); self->enemy->monsterinfo.aiflags &= ~AI_RESURRECTING; } mframe_t medic_frames_attackCable[] = { {ai_move, 2, NULL}, {ai_move, 3, NULL}, {ai_move, 5, NULL}, {ai_move, 4.4, NULL}, {ai_charge, 4.7, NULL}, {ai_charge, 5, NULL}, {ai_charge, 6, NULL}, {ai_charge, 4, NULL}, {ai_charge, 0, NULL}, {ai_move, 0, medic_hook_launch}, {ai_move, 0, medic_cable_attack}, {ai_move, 0, medic_cable_attack}, {ai_move, 0, medic_cable_attack}, {ai_move, 0, medic_cable_attack}, {ai_move, 0, medic_cable_attack}, {ai_move, 0, medic_cable_attack}, {ai_move, 0, medic_cable_attack}, {ai_move, 0, medic_cable_attack}, {ai_move, 0, medic_cable_attack}, {ai_move, -15, medic_hook_retract}, {ai_move, -1.5, NULL}, {ai_move, -1.2, NULL}, {ai_move, -3, NULL}, {ai_move, -2, NULL}, {ai_move, 0.3, NULL}, {ai_move, 0.7, NULL}, {ai_move, 1.2, NULL}, {ai_move, 1.3, NULL} }; mmove_t medic_move_attackCable = { FRAME_attack33, FRAME_attack60, medic_frames_attackCable, medic_run }; void medic_attack(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_MEDIC) { self->monsterinfo.currentmove = &medic_move_attackCable; } else { self->monsterinfo.currentmove = &medic_move_attackBlaster; } } qboolean medic_checkattack(edict_t *self) { if (!self) { return false; } if (self->monsterinfo.aiflags & AI_MEDIC) { medic_attack(self); return true; } return M_CheckAttack(self); } /* * QUAKED monster_medic (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight */ void SP_monster_medic(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_idle1 = gi.soundindex("medic/idle.wav"); sound_pain1 = gi.soundindex("medic/medpain1.wav"); sound_pain2 = gi.soundindex("medic/medpain2.wav"); sound_die = gi.soundindex("medic/meddeth1.wav"); sound_sight = gi.soundindex("medic/medsght1.wav"); sound_search = gi.soundindex("medic/medsrch1.wav"); sound_hook_launch = gi.soundindex("medic/medatck2.wav"); sound_hook_hit = gi.soundindex("medic/medatck3.wav"); sound_hook_heal = gi.soundindex("medic/medatck4.wav"); sound_hook_retract = gi.soundindex("medic/medatck5.wav"); gi.soundindex("medic/medatck1.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/medic/tris.md2"); VectorSet(self->mins, -24, -24, -24); VectorSet(self->maxs, 24, 24, 32); self->health = 300; self->gib_health = -130; self->mass = 400; self->pain = medic_pain; self->die = medic_die; self->monsterinfo.stand = medic_stand; self->monsterinfo.walk = medic_walk; self->monsterinfo.run = medic_run; self->monsterinfo.dodge = medic_dodge; self->monsterinfo.attack = medic_attack; self->monsterinfo.melee = NULL; self->monsterinfo.sight = medic_sight; self->monsterinfo.idle = medic_idle; self->monsterinfo.search = medic_search; self->monsterinfo.checkattack = medic_checkattack; gi.linkentity(self); self->monsterinfo.currentmove = &medic_move_stand; self->monsterinfo.scale = MODEL_SCALE; walkmonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/infantry/0000755000175000017500000000000012615162034021640 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/infantry/infantry.c0000644000175000017500000003671312615162034023650 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Infantry. * * ======================================================================= */ #include "../../header/local.h" #include "infantry.h" void InfantryMachineGun(edict_t *self); static int sound_pain1; static int sound_pain2; static int sound_die1; static int sound_die2; static int sound_gunshot; static int sound_weapon_cock; static int sound_punch_swing; static int sound_punch_hit; static int sound_sight; static int sound_search; static int sound_idle; mframe_t infantry_frames_stand[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t infantry_move_stand = { FRAME_stand50, FRAME_stand71, infantry_frames_stand, NULL }; void infantry_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &infantry_move_stand; } mframe_t infantry_frames_fidget[] = { {ai_stand, 1, NULL}, {ai_stand, 0, NULL}, {ai_stand, 1, NULL}, {ai_stand, 3, NULL}, {ai_stand, 6, NULL}, {ai_stand, 3, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 1, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 1, NULL}, {ai_stand, 0, NULL}, {ai_stand, -1, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 1, NULL}, {ai_stand, 0, NULL}, {ai_stand, -2, NULL}, {ai_stand, 1, NULL}, {ai_stand, 1, NULL}, {ai_stand, 1, NULL}, {ai_stand, -1, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, -1, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, -1, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 1, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, -1, NULL}, {ai_stand, -1, NULL}, {ai_stand, 0, NULL}, {ai_stand, -3, NULL}, {ai_stand, -2, NULL}, {ai_stand, -3, NULL}, {ai_stand, -3, NULL}, {ai_stand, -2, NULL} }; mmove_t infantry_move_fidget = { FRAME_stand01, FRAME_stand49, infantry_frames_fidget, infantry_stand }; void infantry_fidget(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &infantry_move_fidget; gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0); } mframe_t infantry_frames_walk[] = { {ai_walk, 5, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 5, NULL}, {ai_walk, 4, NULL}, {ai_walk, 5, NULL}, {ai_walk, 6, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 5, NULL} }; mmove_t infantry_move_walk = { FRAME_walk03, FRAME_walk14, infantry_frames_walk, NULL }; void infantry_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &infantry_move_walk; } mframe_t infantry_frames_run[] = { {ai_run, 10, NULL}, {ai_run, 20, NULL}, {ai_run, 5, NULL}, {ai_run, 7, NULL}, {ai_run, 30, NULL}, {ai_run, 35, NULL}, {ai_run, 2, NULL}, {ai_run, 6, NULL} }; mmove_t infantry_move_run = { FRAME_run01, FRAME_run08, infantry_frames_run, NULL }; void infantry_run(edict_t *self) { if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &infantry_move_stand; } else { self->monsterinfo.currentmove = &infantry_move_run; } } mframe_t infantry_frames_pain1[] = { {ai_move, -3, NULL}, {ai_move, -2, NULL}, {ai_move, -1, NULL}, {ai_move, -2, NULL}, {ai_move, -1, NULL}, {ai_move, 1, NULL}, {ai_move, -1, NULL}, {ai_move, 1, NULL}, {ai_move, 6, NULL}, {ai_move, 2, NULL} }; mmove_t infantry_move_pain1 = { FRAME_pain101, FRAME_pain110, infantry_frames_pain1, infantry_run }; mframe_t infantry_frames_pain2[] = { {ai_move, -3, NULL}, {ai_move, -3, NULL}, {ai_move, 0, NULL}, {ai_move, -1, NULL}, {ai_move, -2, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, 5, NULL}, {ai_move, 2, NULL} }; mmove_t infantry_move_pain2 = { FRAME_pain201, FRAME_pain210, infantry_frames_pain2, infantry_run }; void infantry_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage) { int n; if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; if (skill->value == 3) { return; /* no pain anims in nightmare */ } n = randk() % 2; if (n == 0) { self->monsterinfo.currentmove = &infantry_move_pain1; gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); } else { self->monsterinfo.currentmove = &infantry_move_pain2; gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); } } vec3_t aimangles[] = { {0.0, 5.0, 0.0}, {10.0, 15.0, 0.0}, {20.0, 25.0, 0.0}, {25.0, 35.0, 0.0}, {30.0, 40.0, 0.0}, {30.0, 45.0, 0.0}, {25.0, 50.0, 0.0}, {20.0, 40.0, 0.0}, {15.0, 35.0, 0.0}, {40.0, 35.0, 0.0}, {70.0, 35.0, 0.0}, {90.0, 35.0, 0.0} }; void InfantryMachineGun(edict_t *self) { vec3_t start, target; vec3_t forward, right; vec3_t vec; int flash_number; if (!self) { return; } if (self->s.frame == FRAME_attak111) { flash_number = MZ2_INFANTRY_MACHINEGUN_1; AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start); if (self->enemy) { VectorMA(self->enemy->s.origin, -0.2, self->enemy->velocity, target); target[2] += self->enemy->viewheight; VectorSubtract(target, start, forward); VectorNormalize(forward); } else { AngleVectors(self->s.angles, forward, right, NULL); } } else { flash_number = MZ2_INFANTRY_MACHINEGUN_2 + (self->s.frame - FRAME_death211); AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start); VectorSubtract(self->s.angles, aimangles[flash_number - MZ2_INFANTRY_MACHINEGUN_2], vec); AngleVectors(vec, forward, NULL, NULL); } monster_fire_bullet(self, start, forward, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number); } void infantry_sight(edict_t *self, edict_t *other /* unused */) { if (!self) { return; } gi.sound(self, CHAN_BODY, sound_sight, 1, ATTN_NORM, 0); } void infantry_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, -8); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; gi.linkentity(self); M_FlyCheck(self); } mframe_t infantry_frames_death1[] = { {ai_move, -4, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -1, NULL}, {ai_move, -4, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -1, NULL}, {ai_move, 3, NULL}, {ai_move, 1, NULL}, {ai_move, 1, NULL}, {ai_move, -2, NULL}, {ai_move, 2, NULL}, {ai_move, 2, NULL}, {ai_move, 9, NULL}, {ai_move, 9, NULL}, {ai_move, 5, NULL}, {ai_move, -3, NULL}, {ai_move, -3, NULL} }; mmove_t infantry_move_death1 = { FRAME_death101, FRAME_death120, infantry_frames_death1, infantry_dead }; /* Off with his head */ mframe_t infantry_frames_death2[] = { {ai_move, 0, NULL}, {ai_move, 1, NULL}, {ai_move, 5, NULL}, {ai_move, -1, NULL}, {ai_move, 0, NULL}, {ai_move, 1, NULL}, {ai_move, 1, NULL}, {ai_move, 4, NULL}, {ai_move, 3, NULL}, {ai_move, 0, NULL}, {ai_move, -2, InfantryMachineGun}, {ai_move, -2, InfantryMachineGun}, {ai_move, -3, InfantryMachineGun}, {ai_move, -1, InfantryMachineGun}, {ai_move, -2, InfantryMachineGun}, {ai_move, 0, InfantryMachineGun}, {ai_move, 2, InfantryMachineGun}, {ai_move, 2, InfantryMachineGun}, {ai_move, 3, InfantryMachineGun}, {ai_move, -10, InfantryMachineGun}, {ai_move, -7, InfantryMachineGun}, {ai_move, -8, InfantryMachineGun}, {ai_move, -6, NULL}, {ai_move, 4, NULL}, {ai_move, 0, NULL} }; mmove_t infantry_move_death2 = { FRAME_death201, FRAME_death225, infantry_frames_death2, infantry_dead }; mframe_t infantry_frames_death3[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -6, NULL}, {ai_move, -11, NULL}, {ai_move, -3, NULL}, {ai_move, -11, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t infantry_move_death3 = { FRAME_death301, FRAME_death309, infantry_frames_death3, infantry_dead }; void infantry_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage, vec3_t point /* unused */) { int n; if (!self) { return; } /* check for gib */ if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex( "misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 2; n++) { ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } /* regular death */ self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; n = randk() % 3; if (n == 0) { self->monsterinfo.currentmove = &infantry_move_death1; gi.sound(self, CHAN_VOICE, sound_die2, 1, ATTN_NORM, 0); } else if (n == 1) { self->monsterinfo.currentmove = &infantry_move_death2; gi.sound(self, CHAN_VOICE, sound_die1, 1, ATTN_NORM, 0); } else { self->monsterinfo.currentmove = &infantry_move_death3; gi.sound(self, CHAN_VOICE, sound_die2, 1, ATTN_NORM, 0); } } void infantry_duck_down(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_DUCKED) { return; } self->monsterinfo.aiflags |= AI_DUCKED; self->maxs[2] -= 32; self->takedamage = DAMAGE_YES; self->monsterinfo.pausetime = level.time + 1; gi.linkentity(self); } void infantry_duck_hold(edict_t *self) { if (!self) { return; } if (level.time >= self->monsterinfo.pausetime) { self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; } else { self->monsterinfo.aiflags |= AI_HOLD_FRAME; } } void infantry_duck_up(edict_t *self) { if (!self) { return; } self->monsterinfo.aiflags &= ~AI_DUCKED; self->maxs[2] += 32; self->takedamage = DAMAGE_AIM; gi.linkentity(self); } mframe_t infantry_frames_duck[] = { {ai_move, -2, infantry_duck_down}, {ai_move, -5, infantry_duck_hold}, {ai_move, 3, NULL}, {ai_move, 4, infantry_duck_up}, {ai_move, 0, NULL} }; mmove_t infantry_move_duck = { FRAME_duck01, FRAME_duck05, infantry_frames_duck, infantry_run }; void infantry_dodge(edict_t *self, edict_t *attacker, float eta /* unused */) { if (!self || !attacker) { return; } if (random() > 0.25) { return; } if (!self->enemy) { self->enemy = attacker; } self->monsterinfo.currentmove = &infantry_move_duck; } void infantry_cock_gun(edict_t *self) { int n; if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_weapon_cock, 1, ATTN_NORM, 0); n = (randk() & 15) + 3 + 7; self->monsterinfo.pausetime = level.time + n * FRAMETIME; } void infantry_fire(edict_t *self) { if (!self) { return; } InfantryMachineGun(self); if (level.time >= self->monsterinfo.pausetime) { self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; } else { self->monsterinfo.aiflags |= AI_HOLD_FRAME; } } mframe_t infantry_frames_attack1[] = { {ai_charge, 4, NULL}, {ai_charge, -1, NULL}, {ai_charge, -1, NULL}, {ai_charge, 0, infantry_cock_gun}, {ai_charge, -1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 2, NULL}, {ai_charge, -2, NULL}, {ai_charge, -3, NULL}, {ai_charge, 1, infantry_fire}, {ai_charge, 5, NULL}, {ai_charge, -1, NULL}, {ai_charge, -2, NULL}, {ai_charge, -3, NULL} }; mmove_t infantry_move_attack1 = { FRAME_attak101, FRAME_attak115, infantry_frames_attack1, infantry_run }; void infantry_swing(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_punch_swing, 1, ATTN_NORM, 0); } void infantry_smack(edict_t *self) { vec3_t aim; if (!self) { return; } VectorSet(aim, MELEE_DISTANCE, 0, 0); if (fire_hit(self, aim, (5 + (randk() % 5)), 50)) { gi.sound(self, CHAN_WEAPON, sound_punch_hit, 1, ATTN_NORM, 0); } } mframe_t infantry_frames_attack2[] = { {ai_charge, 3, NULL}, {ai_charge, 6, NULL}, {ai_charge, 0, infantry_swing}, {ai_charge, 8, NULL}, {ai_charge, 5, NULL}, {ai_charge, 8, infantry_smack}, {ai_charge, 6, NULL}, {ai_charge, 3, NULL}, }; mmove_t infantry_move_attack2 = { FRAME_attak201, FRAME_attak208, infantry_frames_attack2, infantry_run }; void infantry_attack(edict_t *self) { if (!self) { return; } if (range(self, self->enemy) == RANGE_MELEE) { self->monsterinfo.currentmove = &infantry_move_attack2; } else { self->monsterinfo.currentmove = &infantry_move_attack1; } } /* * QUAKED monster_infantry (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight */ void SP_monster_infantry(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_pain1 = gi.soundindex("infantry/infpain1.wav"); sound_pain2 = gi.soundindex("infantry/infpain2.wav"); sound_die1 = gi.soundindex("infantry/infdeth1.wav"); sound_die2 = gi.soundindex("infantry/infdeth2.wav"); sound_gunshot = gi.soundindex("infantry/infatck1.wav"); sound_weapon_cock = gi.soundindex("infantry/infatck3.wav"); sound_punch_swing = gi.soundindex("infantry/infatck2.wav"); sound_punch_hit = gi.soundindex("infantry/melee2.wav"); sound_sight = gi.soundindex("infantry/infsght1.wav"); sound_search = gi.soundindex("infantry/infsrch1.wav"); sound_idle = gi.soundindex("infantry/infidle1.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2"); VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, 32); self->health = 100; self->gib_health = -40; self->mass = 200; self->pain = infantry_pain; self->die = infantry_die; self->monsterinfo.stand = infantry_stand; self->monsterinfo.walk = infantry_walk; self->monsterinfo.run = infantry_run; self->monsterinfo.dodge = infantry_dodge; self->monsterinfo.attack = infantry_attack; self->monsterinfo.melee = NULL; self->monsterinfo.sight = infantry_sight; self->monsterinfo.idle = infantry_fidget; gi.linkentity(self); self->monsterinfo.currentmove = &infantry_move_stand; self->monsterinfo.scale = MODEL_SCALE; walkmonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/infantry/infantry.h0000644000175000017500000001422612615162034023650 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Infantry animations. * * ======================================================================= */ #define FRAME_gun02 0 #define FRAME_stand01 1 #define FRAME_stand02 2 #define FRAME_stand03 3 #define FRAME_stand04 4 #define FRAME_stand05 5 #define FRAME_stand06 6 #define FRAME_stand07 7 #define FRAME_stand08 8 #define FRAME_stand09 9 #define FRAME_stand10 10 #define FRAME_stand11 11 #define FRAME_stand12 12 #define FRAME_stand13 13 #define FRAME_stand14 14 #define FRAME_stand15 15 #define FRAME_stand16 16 #define FRAME_stand17 17 #define FRAME_stand18 18 #define FRAME_stand19 19 #define FRAME_stand20 20 #define FRAME_stand21 21 #define FRAME_stand22 22 #define FRAME_stand23 23 #define FRAME_stand24 24 #define FRAME_stand25 25 #define FRAME_stand26 26 #define FRAME_stand27 27 #define FRAME_stand28 28 #define FRAME_stand29 29 #define FRAME_stand30 30 #define FRAME_stand31 31 #define FRAME_stand32 32 #define FRAME_stand33 33 #define FRAME_stand34 34 #define FRAME_stand35 35 #define FRAME_stand36 36 #define FRAME_stand37 37 #define FRAME_stand38 38 #define FRAME_stand39 39 #define FRAME_stand40 40 #define FRAME_stand41 41 #define FRAME_stand42 42 #define FRAME_stand43 43 #define FRAME_stand44 44 #define FRAME_stand45 45 #define FRAME_stand46 46 #define FRAME_stand47 47 #define FRAME_stand48 48 #define FRAME_stand49 49 #define FRAME_stand50 50 #define FRAME_stand51 51 #define FRAME_stand52 52 #define FRAME_stand53 53 #define FRAME_stand54 54 #define FRAME_stand55 55 #define FRAME_stand56 56 #define FRAME_stand57 57 #define FRAME_stand58 58 #define FRAME_stand59 59 #define FRAME_stand60 60 #define FRAME_stand61 61 #define FRAME_stand62 62 #define FRAME_stand63 63 #define FRAME_stand64 64 #define FRAME_stand65 65 #define FRAME_stand66 66 #define FRAME_stand67 67 #define FRAME_stand68 68 #define FRAME_stand69 69 #define FRAME_stand70 70 #define FRAME_stand71 71 #define FRAME_walk01 72 #define FRAME_walk02 73 #define FRAME_walk03 74 #define FRAME_walk04 75 #define FRAME_walk05 76 #define FRAME_walk06 77 #define FRAME_walk07 78 #define FRAME_walk08 79 #define FRAME_walk09 80 #define FRAME_walk10 81 #define FRAME_walk11 82 #define FRAME_walk12 83 #define FRAME_walk13 84 #define FRAME_walk14 85 #define FRAME_walk15 86 #define FRAME_walk16 87 #define FRAME_walk17 88 #define FRAME_walk18 89 #define FRAME_walk19 90 #define FRAME_walk20 91 #define FRAME_run01 92 #define FRAME_run02 93 #define FRAME_run03 94 #define FRAME_run04 95 #define FRAME_run05 96 #define FRAME_run06 97 #define FRAME_run07 98 #define FRAME_run08 99 #define FRAME_pain101 100 #define FRAME_pain102 101 #define FRAME_pain103 102 #define FRAME_pain104 103 #define FRAME_pain105 104 #define FRAME_pain106 105 #define FRAME_pain107 106 #define FRAME_pain108 107 #define FRAME_pain109 108 #define FRAME_pain110 109 #define FRAME_pain201 110 #define FRAME_pain202 111 #define FRAME_pain203 112 #define FRAME_pain204 113 #define FRAME_pain205 114 #define FRAME_pain206 115 #define FRAME_pain207 116 #define FRAME_pain208 117 #define FRAME_pain209 118 #define FRAME_pain210 119 #define FRAME_duck01 120 #define FRAME_duck02 121 #define FRAME_duck03 122 #define FRAME_duck04 123 #define FRAME_duck05 124 #define FRAME_death101 125 #define FRAME_death102 126 #define FRAME_death103 127 #define FRAME_death104 128 #define FRAME_death105 129 #define FRAME_death106 130 #define FRAME_death107 131 #define FRAME_death108 132 #define FRAME_death109 133 #define FRAME_death110 134 #define FRAME_death111 135 #define FRAME_death112 136 #define FRAME_death113 137 #define FRAME_death114 138 #define FRAME_death115 139 #define FRAME_death116 140 #define FRAME_death117 141 #define FRAME_death118 142 #define FRAME_death119 143 #define FRAME_death120 144 #define FRAME_death201 145 #define FRAME_death202 146 #define FRAME_death203 147 #define FRAME_death204 148 #define FRAME_death205 149 #define FRAME_death206 150 #define FRAME_death207 151 #define FRAME_death208 152 #define FRAME_death209 153 #define FRAME_death210 154 #define FRAME_death211 155 #define FRAME_death212 156 #define FRAME_death213 157 #define FRAME_death214 158 #define FRAME_death215 159 #define FRAME_death216 160 #define FRAME_death217 161 #define FRAME_death218 162 #define FRAME_death219 163 #define FRAME_death220 164 #define FRAME_death221 165 #define FRAME_death222 166 #define FRAME_death223 167 #define FRAME_death224 168 #define FRAME_death225 169 #define FRAME_death301 170 #define FRAME_death302 171 #define FRAME_death303 172 #define FRAME_death304 173 #define FRAME_death305 174 #define FRAME_death306 175 #define FRAME_death307 176 #define FRAME_death308 177 #define FRAME_death309 178 #define FRAME_block01 179 #define FRAME_block02 180 #define FRAME_block03 181 #define FRAME_block04 182 #define FRAME_block05 183 #define FRAME_attak101 184 #define FRAME_attak102 185 #define FRAME_attak103 186 #define FRAME_attak104 187 #define FRAME_attak105 188 #define FRAME_attak106 189 #define FRAME_attak107 190 #define FRAME_attak108 191 #define FRAME_attak109 192 #define FRAME_attak110 193 #define FRAME_attak111 194 #define FRAME_attak112 195 #define FRAME_attak113 196 #define FRAME_attak114 197 #define FRAME_attak115 198 #define FRAME_attak201 199 #define FRAME_attak202 200 #define FRAME_attak203 201 #define FRAME_attak204 202 #define FRAME_attak205 203 #define FRAME_attak206 204 #define FRAME_attak207 205 #define FRAME_attak208 206 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/mutant/0000755000175000017500000000000012615162034021316 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/mutant/mutant.c0000644000175000017500000003673512615162034023010 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Mutant. * * ======================================================================= */ #include "../../header/local.h" #include "mutant.h" static int sound_swing; static int sound_hit; static int sound_hit2; static int sound_death; static int sound_idle; static int sound_pain1; static int sound_pain2; static int sound_sight; static int sound_search; static int sound_step1; static int sound_step2; static int sound_step3; static int sound_thud; void mutant_step(edict_t *self) { int n; if (!self) { return; } n = (randk() + 1) % 3; if (n == 0) { gi.sound(self, CHAN_VOICE, sound_step1, 1, ATTN_NORM, 0); } else if (n == 1) { gi.sound(self, CHAN_VOICE, sound_step2, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_VOICE, sound_step3, 1, ATTN_NORM, 0); } } void mutant_sight(edict_t *self, edict_t *other /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); } void mutant_search(edict_t *self) { gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0); } void mutant_swing(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_swing, 1, ATTN_NORM, 0); } mframe_t mutant_frames_stand[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, /* 10 */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, /* 20 */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, /* 30 */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, /* 40 */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, /* 50 */ {ai_stand, 0, NULL} }; mmove_t mutant_move_stand = { FRAME_stand101, FRAME_stand151, mutant_frames_stand, NULL }; void mutant_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &mutant_move_stand; } void mutant_idle_loop(edict_t *self) { if (!self) { return; } if (random() < 0.75) { self->monsterinfo.nextframe = FRAME_stand155; } } mframe_t mutant_frames_idle[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, /* scratch loop start */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, mutant_idle_loop}, /* scratch loop end */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t mutant_move_idle = { FRAME_stand152, FRAME_stand164, mutant_frames_idle, mutant_stand }; void mutant_idle(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &mutant_move_idle; gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0); } void mutant_walk(edict_t *self); mframe_t mutant_frames_walk[] = { {ai_walk, 3, NULL}, {ai_walk, 1, NULL}, {ai_walk, 5, NULL}, {ai_walk, 10, NULL}, {ai_walk, 13, NULL}, {ai_walk, 10, NULL}, {ai_walk, 0, NULL}, {ai_walk, 5, NULL}, {ai_walk, 6, NULL}, {ai_walk, 16, NULL}, {ai_walk, 15, NULL}, {ai_walk, 6, NULL} }; mmove_t mutant_move_walk = { FRAME_walk05, FRAME_walk16, mutant_frames_walk, NULL }; void mutant_walk_loop(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &mutant_move_walk; } mframe_t mutant_frames_start_walk[] = { {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, -2, NULL}, {ai_walk, 1, NULL} }; mmove_t mutant_move_start_walk = { FRAME_walk01, FRAME_walk04, mutant_frames_start_walk, mutant_walk_loop }; void mutant_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &mutant_move_start_walk; } mframe_t mutant_frames_run[] = { {ai_run, 40, NULL}, {ai_run, 40, mutant_step}, {ai_run, 24, NULL}, {ai_run, 5, mutant_step}, {ai_run, 17, NULL}, {ai_run, 10, NULL} }; mmove_t mutant_move_run = { FRAME_run03, FRAME_run08, mutant_frames_run, NULL }; void mutant_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &mutant_move_stand; } else { self->monsterinfo.currentmove = &mutant_move_run; } } void mutant_hit_left(edict_t *self) { vec3_t aim; if (!self) { return; } VectorSet(aim, MELEE_DISTANCE, self->mins[0], 8); if (fire_hit(self, aim, (10 + (randk() % 5)), 100)) { gi.sound(self, CHAN_WEAPON, sound_hit, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_WEAPON, sound_swing, 1, ATTN_NORM, 0); } } void mutant_hit_right(edict_t *self) { vec3_t aim; if (!self) { return; } VectorSet(aim, MELEE_DISTANCE, self->maxs[0], 8); if (fire_hit(self, aim, (10 + (randk() % 5)), 100)) { gi.sound(self, CHAN_WEAPON, sound_hit2, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_WEAPON, sound_swing, 1, ATTN_NORM, 0); } } void mutant_check_refire(edict_t *self) { if (!self) { return; } if (!self->enemy || !self->enemy->inuse || (self->enemy->health <= 0)) { return; } if (((skill->value == 3) && (random() < 0.5)) || (range(self, self->enemy) == RANGE_MELEE)) { self->monsterinfo.nextframe = FRAME_attack09; } } mframe_t mutant_frames_attack[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, mutant_hit_left}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, mutant_hit_right}, {ai_charge, 0, mutant_check_refire} }; mmove_t mutant_move_attack = { FRAME_attack09, FRAME_attack15, mutant_frames_attack, mutant_run }; void mutant_melee(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &mutant_move_attack; } void mutant_jump_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { if (!self) { return; } if (self->health <= 0) { self->touch = NULL; return; } if (other->takedamage) { if (VectorLength(self->velocity) > 400) { vec3_t point; vec3_t normal; int damage; VectorCopy(self->velocity, normal); VectorNormalize(normal); VectorMA(self->s.origin, self->maxs[0], normal, point); damage = 40 + 10 * random(); T_Damage(other, self, self, self->velocity, point, normal, damage, damage, 0, MOD_UNKNOWN); } } if (!M_CheckBottom(self)) { if (self->groundentity) { self->monsterinfo.nextframe = FRAME_attack02; self->touch = NULL; } return; } self->touch = NULL; } void mutant_jump_takeoff(edict_t *self) { vec3_t forward; if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); AngleVectors(self->s.angles, forward, NULL, NULL); self->s.origin[2] += 1; VectorScale(forward, 600, self->velocity); self->velocity[2] = 250; self->groundentity = NULL; self->monsterinfo.aiflags |= AI_DUCKED; self->monsterinfo.attack_finished = level.time + 3; self->touch = mutant_jump_touch; } void mutant_check_landing(edict_t *self) { if (!self) { return; } if (self->groundentity) { gi.sound(self, CHAN_WEAPON, sound_thud, 1, ATTN_NORM, 0); self->monsterinfo.attack_finished = 0; self->monsterinfo.aiflags &= ~AI_DUCKED; return; } if (level.time > self->monsterinfo.attack_finished) { self->monsterinfo.nextframe = FRAME_attack02; } else { self->monsterinfo.nextframe = FRAME_attack05; } } mframe_t mutant_frames_jump[] = { {ai_charge, 0, NULL}, {ai_charge, 17, NULL}, {ai_charge, 15, mutant_jump_takeoff}, {ai_charge, 15, NULL}, {ai_charge, 15, mutant_check_landing}, {ai_charge, 0, NULL}, {ai_charge, 3, NULL}, {ai_charge, 0, NULL} }; mmove_t mutant_move_jump = { FRAME_attack01, FRAME_attack08, mutant_frames_jump, mutant_run }; void mutant_jump(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &mutant_move_jump; } qboolean mutant_check_melee(edict_t *self) { if (!self) { return false; } if (range(self, self->enemy) == RANGE_MELEE) { return true; } return false; } qboolean mutant_check_jump(edict_t *self) { vec3_t v; float distance; if (!self) { return false; } if (self->absmin[2] > (self->enemy->absmin[2] + 0.75 * self->enemy->size[2])) { return false; } if (self->absmax[2] < (self->enemy->absmin[2] + 0.25 * self->enemy->size[2])) { return false; } v[0] = self->s.origin[0] - self->enemy->s.origin[0]; v[1] = self->s.origin[1] - self->enemy->s.origin[1]; v[2] = 0; distance = VectorLength(v); if (distance < 100) { return false; } if (distance > 100) { if (random() < 0.9) { return false; } } return true; } qboolean mutant_checkattack(edict_t *self) { if (!self) { return false; } if (!self->enemy || (self->enemy->health <= 0)) { return false; } if (mutant_check_melee(self)) { self->monsterinfo.attack_state = AS_MELEE; return true; } if (mutant_check_jump(self)) { self->monsterinfo.attack_state = AS_MISSILE; return true; } return false; } mframe_t mutant_frames_pain1[] = { {ai_move, 4, NULL}, {ai_move, -3, NULL}, {ai_move, -8, NULL}, {ai_move, 2, NULL}, {ai_move, 5, NULL} }; mmove_t mutant_move_pain1 = { FRAME_pain101, FRAME_pain105, mutant_frames_pain1, mutant_run }; mframe_t mutant_frames_pain2[] = { {ai_move, -24, NULL}, {ai_move, 11, NULL}, {ai_move, 5, NULL}, {ai_move, -2, NULL}, {ai_move, 6, NULL}, {ai_move, 4, NULL} }; mmove_t mutant_move_pain2 = { FRAME_pain201, FRAME_pain206, mutant_frames_pain2, mutant_run }; mframe_t mutant_frames_pain3[] = { {ai_move, -22, NULL}, {ai_move, 3, NULL}, {ai_move, 3, NULL}, {ai_move, 2, NULL}, {ai_move, 1, NULL}, {ai_move, 1, NULL}, {ai_move, 6, NULL}, {ai_move, 3, NULL}, {ai_move, 2, NULL}, {ai_move, 0, NULL}, {ai_move, 1, NULL} }; mmove_t mutant_move_pain3 = { FRAME_pain301, FRAME_pain311, mutant_frames_pain3, mutant_run }; void mutant_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage /* unused */) { float r; if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; if (skill->value == 3) { return; /* no pain anims in nightmare */ } r = random(); if (r < 0.33) { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &mutant_move_pain1; } else if (r < 0.66) { gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &mutant_move_pain2; } else { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &mutant_move_pain3; } } void mutant_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, -8); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; gi.linkentity(self); M_FlyCheck(self); } mframe_t mutant_frames_death1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t mutant_move_death1 = { FRAME_death101, FRAME_death109, mutant_frames_death1, mutant_dead }; mframe_t mutant_frames_death2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t mutant_move_death2 = { FRAME_death201, FRAME_death210, mutant_frames_death2, mutant_dead }; void mutant_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage, vec3_t point /* unused */) { int n; if (!self) { return; } if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex( "misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 2; n++) { ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0); self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; self->s.skinnum = 1; if (random() < 0.5) { self->monsterinfo.currentmove = &mutant_move_death1; } else { self->monsterinfo.currentmove = &mutant_move_death2; } } /* * QUAKED monster_mutant (1 .5 0) (-32 -32 -24) (32 32 32) Ambush Trigger_Spawn Sight */ void SP_monster_mutant(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_swing = gi.soundindex("mutant/mutatck1.wav"); sound_hit = gi.soundindex("mutant/mutatck2.wav"); sound_hit2 = gi.soundindex("mutant/mutatck3.wav"); sound_death = gi.soundindex("mutant/mutdeth1.wav"); sound_idle = gi.soundindex("mutant/mutidle1.wav"); sound_pain1 = gi.soundindex("mutant/mutpain1.wav"); sound_pain2 = gi.soundindex("mutant/mutpain2.wav"); sound_sight = gi.soundindex("mutant/mutsght1.wav"); sound_search = gi.soundindex("mutant/mutsrch1.wav"); sound_step1 = gi.soundindex("mutant/step1.wav"); sound_step2 = gi.soundindex("mutant/step2.wav"); sound_step3 = gi.soundindex("mutant/step3.wav"); sound_thud = gi.soundindex("mutant/thud1.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/mutant/tris.md2"); VectorSet(self->mins, -32, -32, -24); VectorSet(self->maxs, 32, 32, 48); self->health = 300; self->gib_health = -120; self->mass = 300; self->pain = mutant_pain; self->die = mutant_die; self->monsterinfo.stand = mutant_stand; self->monsterinfo.walk = mutant_walk; self->monsterinfo.run = mutant_run; self->monsterinfo.dodge = NULL; self->monsterinfo.attack = mutant_jump; self->monsterinfo.melee = mutant_melee; self->monsterinfo.sight = mutant_sight; self->monsterinfo.search = mutant_search; self->monsterinfo.idle = mutant_idle; self->monsterinfo.checkattack = mutant_checkattack; gi.linkentity(self); self->monsterinfo.currentmove = &mutant_move_stand; self->monsterinfo.scale = MODEL_SCALE; walkmonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/mutant/mutant.h0000644000175000017500000001131512615162034023000 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Mutant animations. * * ======================================================================= */ #define FRAME_attack01 0 #define FRAME_attack02 1 #define FRAME_attack03 2 #define FRAME_attack04 3 #define FRAME_attack05 4 #define FRAME_attack06 5 #define FRAME_attack07 6 #define FRAME_attack08 7 #define FRAME_attack09 8 #define FRAME_attack10 9 #define FRAME_attack11 10 #define FRAME_attack12 11 #define FRAME_attack13 12 #define FRAME_attack14 13 #define FRAME_attack15 14 #define FRAME_death101 15 #define FRAME_death102 16 #define FRAME_death103 17 #define FRAME_death104 18 #define FRAME_death105 19 #define FRAME_death106 20 #define FRAME_death107 21 #define FRAME_death108 22 #define FRAME_death109 23 #define FRAME_death201 24 #define FRAME_death202 25 #define FRAME_death203 26 #define FRAME_death204 27 #define FRAME_death205 28 #define FRAME_death206 29 #define FRAME_death207 30 #define FRAME_death208 31 #define FRAME_death209 32 #define FRAME_death210 33 #define FRAME_pain101 34 #define FRAME_pain102 35 #define FRAME_pain103 36 #define FRAME_pain104 37 #define FRAME_pain105 38 #define FRAME_pain201 39 #define FRAME_pain202 40 #define FRAME_pain203 41 #define FRAME_pain204 42 #define FRAME_pain205 43 #define FRAME_pain206 44 #define FRAME_pain301 45 #define FRAME_pain302 46 #define FRAME_pain303 47 #define FRAME_pain304 48 #define FRAME_pain305 49 #define FRAME_pain306 50 #define FRAME_pain307 51 #define FRAME_pain308 52 #define FRAME_pain309 53 #define FRAME_pain310 54 #define FRAME_pain311 55 #define FRAME_run03 56 #define FRAME_run04 57 #define FRAME_run05 58 #define FRAME_run06 59 #define FRAME_run07 60 #define FRAME_run08 61 #define FRAME_stand101 62 #define FRAME_stand102 63 #define FRAME_stand103 64 #define FRAME_stand104 65 #define FRAME_stand105 66 #define FRAME_stand106 67 #define FRAME_stand107 68 #define FRAME_stand108 69 #define FRAME_stand109 70 #define FRAME_stand110 71 #define FRAME_stand111 72 #define FRAME_stand112 73 #define FRAME_stand113 74 #define FRAME_stand114 75 #define FRAME_stand115 76 #define FRAME_stand116 77 #define FRAME_stand117 78 #define FRAME_stand118 79 #define FRAME_stand119 80 #define FRAME_stand120 81 #define FRAME_stand121 82 #define FRAME_stand122 83 #define FRAME_stand123 84 #define FRAME_stand124 85 #define FRAME_stand125 86 #define FRAME_stand126 87 #define FRAME_stand127 88 #define FRAME_stand128 89 #define FRAME_stand129 90 #define FRAME_stand130 91 #define FRAME_stand131 92 #define FRAME_stand132 93 #define FRAME_stand133 94 #define FRAME_stand134 95 #define FRAME_stand135 96 #define FRAME_stand136 97 #define FRAME_stand137 98 #define FRAME_stand138 99 #define FRAME_stand139 100 #define FRAME_stand140 101 #define FRAME_stand141 102 #define FRAME_stand142 103 #define FRAME_stand143 104 #define FRAME_stand144 105 #define FRAME_stand145 106 #define FRAME_stand146 107 #define FRAME_stand147 108 #define FRAME_stand148 109 #define FRAME_stand149 110 #define FRAME_stand150 111 #define FRAME_stand151 112 #define FRAME_stand152 113 #define FRAME_stand153 114 #define FRAME_stand154 115 #define FRAME_stand155 116 #define FRAME_stand156 117 #define FRAME_stand157 118 #define FRAME_stand158 119 #define FRAME_stand159 120 #define FRAME_stand160 121 #define FRAME_stand161 122 #define FRAME_stand162 123 #define FRAME_stand163 124 #define FRAME_stand164 125 #define FRAME_walk01 126 #define FRAME_walk02 127 #define FRAME_walk03 128 #define FRAME_walk04 129 #define FRAME_walk05 130 #define FRAME_walk06 131 #define FRAME_walk07 132 #define FRAME_walk08 133 #define FRAME_walk09 134 #define FRAME_walk10 135 #define FRAME_walk11 136 #define FRAME_walk12 137 #define FRAME_walk13 138 #define FRAME_walk14 139 #define FRAME_walk15 140 #define FRAME_walk16 141 #define FRAME_walk17 142 #define FRAME_walk18 143 #define FRAME_walk19 144 #define FRAME_walk20 145 #define FRAME_walk21 146 #define FRAME_walk22 147 #define FRAME_walk23 148 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/brain/0000755000175000017500000000000012615162034021101 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/brain/brain.h0000644000175000017500000001506112615162034022350 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Brain animations. * * ======================================================================= */ #define FRAME_walk101 0 #define FRAME_walk102 1 #define FRAME_walk103 2 #define FRAME_walk104 3 #define FRAME_walk105 4 #define FRAME_walk106 5 #define FRAME_walk107 6 #define FRAME_walk108 7 #define FRAME_walk109 8 #define FRAME_walk110 9 #define FRAME_walk111 10 #define FRAME_walk112 11 #define FRAME_walk113 12 #define FRAME_walk201 13 #define FRAME_walk202 14 #define FRAME_walk203 15 #define FRAME_walk204 16 #define FRAME_walk205 17 #define FRAME_walk206 18 #define FRAME_walk207 19 #define FRAME_walk208 20 #define FRAME_walk209 21 #define FRAME_walk210 22 #define FRAME_walk211 23 #define FRAME_walk212 24 #define FRAME_walk213 25 #define FRAME_walk214 26 #define FRAME_walk215 27 #define FRAME_walk216 28 #define FRAME_walk217 29 #define FRAME_walk218 30 #define FRAME_walk219 31 #define FRAME_walk220 32 #define FRAME_walk221 33 #define FRAME_walk222 34 #define FRAME_walk223 35 #define FRAME_walk224 36 #define FRAME_walk225 37 #define FRAME_walk226 38 #define FRAME_walk227 39 #define FRAME_walk228 40 #define FRAME_walk229 41 #define FRAME_walk230 42 #define FRAME_walk231 43 #define FRAME_walk232 44 #define FRAME_walk233 45 #define FRAME_walk234 46 #define FRAME_walk235 47 #define FRAME_walk236 48 #define FRAME_walk237 49 #define FRAME_walk238 50 #define FRAME_walk239 51 #define FRAME_walk240 52 #define FRAME_attak101 53 #define FRAME_attak102 54 #define FRAME_attak103 55 #define FRAME_attak104 56 #define FRAME_attak105 57 #define FRAME_attak106 58 #define FRAME_attak107 59 #define FRAME_attak108 60 #define FRAME_attak109 61 #define FRAME_attak110 62 #define FRAME_attak111 63 #define FRAME_attak112 64 #define FRAME_attak113 65 #define FRAME_attak114 66 #define FRAME_attak115 67 #define FRAME_attak116 68 #define FRAME_attak117 69 #define FRAME_attak118 70 #define FRAME_attak201 71 #define FRAME_attak202 72 #define FRAME_attak203 73 #define FRAME_attak204 74 #define FRAME_attak205 75 #define FRAME_attak206 76 #define FRAME_attak207 77 #define FRAME_attak208 78 #define FRAME_attak209 79 #define FRAME_attak210 80 #define FRAME_attak211 81 #define FRAME_attak212 82 #define FRAME_attak213 83 #define FRAME_attak214 84 #define FRAME_attak215 85 #define FRAME_attak216 86 #define FRAME_attak217 87 #define FRAME_pain101 88 #define FRAME_pain102 89 #define FRAME_pain103 90 #define FRAME_pain104 91 #define FRAME_pain105 92 #define FRAME_pain106 93 #define FRAME_pain107 94 #define FRAME_pain108 95 #define FRAME_pain109 96 #define FRAME_pain110 97 #define FRAME_pain111 98 #define FRAME_pain112 99 #define FRAME_pain113 100 #define FRAME_pain114 101 #define FRAME_pain115 102 #define FRAME_pain116 103 #define FRAME_pain117 104 #define FRAME_pain118 105 #define FRAME_pain119 106 #define FRAME_pain120 107 #define FRAME_pain121 108 #define FRAME_pain201 109 #define FRAME_pain202 110 #define FRAME_pain203 111 #define FRAME_pain204 112 #define FRAME_pain205 113 #define FRAME_pain206 114 #define FRAME_pain207 115 #define FRAME_pain208 116 #define FRAME_pain301 117 #define FRAME_pain302 118 #define FRAME_pain303 119 #define FRAME_pain304 120 #define FRAME_pain305 121 #define FRAME_pain306 122 #define FRAME_death101 123 #define FRAME_death102 124 #define FRAME_death103 125 #define FRAME_death104 126 #define FRAME_death105 127 #define FRAME_death106 128 #define FRAME_death107 129 #define FRAME_death108 130 #define FRAME_death109 131 #define FRAME_death110 132 #define FRAME_death111 133 #define FRAME_death112 134 #define FRAME_death113 135 #define FRAME_death114 136 #define FRAME_death115 137 #define FRAME_death116 138 #define FRAME_death117 139 #define FRAME_death118 140 #define FRAME_death201 141 #define FRAME_death202 142 #define FRAME_death203 143 #define FRAME_death204 144 #define FRAME_death205 145 #define FRAME_duck01 146 #define FRAME_duck02 147 #define FRAME_duck03 148 #define FRAME_duck04 149 #define FRAME_duck05 150 #define FRAME_duck06 151 #define FRAME_duck07 152 #define FRAME_duck08 153 #define FRAME_defens01 154 #define FRAME_defens02 155 #define FRAME_defens03 156 #define FRAME_defens04 157 #define FRAME_defens05 158 #define FRAME_defens06 159 #define FRAME_defens07 160 #define FRAME_defens08 161 #define FRAME_stand01 162 #define FRAME_stand02 163 #define FRAME_stand03 164 #define FRAME_stand04 165 #define FRAME_stand05 166 #define FRAME_stand06 167 #define FRAME_stand07 168 #define FRAME_stand08 169 #define FRAME_stand09 170 #define FRAME_stand10 171 #define FRAME_stand11 172 #define FRAME_stand12 173 #define FRAME_stand13 174 #define FRAME_stand14 175 #define FRAME_stand15 176 #define FRAME_stand16 177 #define FRAME_stand17 178 #define FRAME_stand18 179 #define FRAME_stand19 180 #define FRAME_stand20 181 #define FRAME_stand21 182 #define FRAME_stand22 183 #define FRAME_stand23 184 #define FRAME_stand24 185 #define FRAME_stand25 186 #define FRAME_stand26 187 #define FRAME_stand27 188 #define FRAME_stand28 189 #define FRAME_stand29 190 #define FRAME_stand30 191 #define FRAME_stand31 192 #define FRAME_stand32 193 #define FRAME_stand33 194 #define FRAME_stand34 195 #define FRAME_stand35 196 #define FRAME_stand36 197 #define FRAME_stand37 198 #define FRAME_stand38 199 #define FRAME_stand39 200 #define FRAME_stand40 201 #define FRAME_stand41 202 #define FRAME_stand42 203 #define FRAME_stand43 204 #define FRAME_stand44 205 #define FRAME_stand45 206 #define FRAME_stand46 207 #define FRAME_stand47 208 #define FRAME_stand48 209 #define FRAME_stand49 210 #define FRAME_stand50 211 #define FRAME_stand51 212 #define FRAME_stand52 213 #define FRAME_stand53 214 #define FRAME_stand54 215 #define FRAME_stand55 216 #define FRAME_stand56 217 #define FRAME_stand57 218 #define FRAME_stand58 219 #define FRAME_stand59 220 #define FRAME_stand60 221 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/brain/brain.c0000644000175000017500000003571212615162034022350 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Brain. * * ======================================================================= */ #include "../../header/local.h" #include "brain.h" static int sound_chest_open; static int sound_tentacles_extend; static int sound_tentacles_retract; static int sound_death; static int sound_idle1; static int sound_idle2; static int sound_idle3; static int sound_pain1; static int sound_pain2; static int sound_sight; static int sound_search; static int sound_melee1; static int sound_melee2; static int sound_melee3; void brain_sight(edict_t *self, edict_t *other /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); } void brain_search(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0); } void brain_run(edict_t *self); void brain_dead(edict_t *self); mframe_t brain_frames_stand[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t brain_move_stand = { FRAME_stand01, FRAME_stand30, brain_frames_stand, NULL }; void brain_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &brain_move_stand; } mframe_t brain_frames_idle[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t brain_move_idle = { FRAME_stand31, FRAME_stand60, brain_frames_idle, brain_stand }; void brain_idle(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_AUTO, sound_idle3, 1, ATTN_IDLE, 0); self->monsterinfo.currentmove = &brain_move_idle; } mframe_t brain_frames_walk1[] = { {ai_walk, 7, NULL}, {ai_walk, 2, NULL}, {ai_walk, 3, NULL}, {ai_walk, 3, NULL}, {ai_walk, 1, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 9, NULL}, {ai_walk, -4, NULL}, {ai_walk, -1, NULL}, {ai_walk, 2, NULL} }; mmove_t brain_move_walk1 = { FRAME_walk101, FRAME_walk111, brain_frames_walk1, NULL }; void brain_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &brain_move_walk1; } mframe_t brain_frames_defense[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t brain_move_defense = { FRAME_defens01, FRAME_defens08, brain_frames_defense, NULL }; mframe_t brain_frames_pain3[] = { {ai_move, -2, NULL}, {ai_move, 2, NULL}, {ai_move, 1, NULL}, {ai_move, 3, NULL}, {ai_move, 0, NULL}, {ai_move, -4, NULL} }; mmove_t brain_move_pain3 = { FRAME_pain301, FRAME_pain306, brain_frames_pain3, brain_run }; mframe_t brain_frames_pain2[] = { {ai_move, -2, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 3, NULL}, {ai_move, 1, NULL}, {ai_move, -2, NULL} }; mmove_t brain_move_pain2 = { FRAME_pain201, FRAME_pain208, brain_frames_pain2, brain_run }; mframe_t brain_frames_pain1[] = { {ai_move, -6, NULL}, {ai_move, -2, NULL}, {ai_move, -6, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, 1, NULL}, {ai_move, 7, NULL}, {ai_move, 0, NULL}, {ai_move, 3, NULL}, {ai_move, -1, NULL} }; mmove_t brain_move_pain1 = { FRAME_pain101, FRAME_pain121, brain_frames_pain1, brain_run }; void brain_duck_down(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_DUCKED) { return; } self->monsterinfo.aiflags |= AI_DUCKED; self->maxs[2] -= 32; self->takedamage = DAMAGE_YES; gi.linkentity(self); } void brain_duck_hold(edict_t *self) { if (!self) { return; } if (level.time >= self->monsterinfo.pausetime) { self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; } else { self->monsterinfo.aiflags |= AI_HOLD_FRAME; } } void brain_duck_up(edict_t *self) { if (!self) { return; } self->monsterinfo.aiflags &= ~AI_DUCKED; self->maxs[2] += 32; self->takedamage = DAMAGE_AIM; gi.linkentity(self); } mframe_t brain_frames_duck[] = { {ai_move, 0, NULL}, {ai_move, -2, brain_duck_down}, {ai_move, 17, brain_duck_hold}, {ai_move, -3, NULL}, {ai_move, -1, brain_duck_up}, {ai_move, -5, NULL}, {ai_move, -6, NULL}, {ai_move, -6, NULL} }; mmove_t brain_move_duck = { FRAME_duck01, FRAME_duck08, brain_frames_duck, brain_run }; void brain_dodge(edict_t *self, edict_t *attacker, float eta) { if (!self || !attacker) { return; } if (random() > 0.25) { return; } if (!self->enemy) { self->enemy = attacker; } self->monsterinfo.pausetime = level.time + eta + 0.5; self->monsterinfo.currentmove = &brain_move_duck; } mframe_t brain_frames_death2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 9, NULL}, {ai_move, 0, NULL} }; mmove_t brain_move_death2 = { FRAME_death201, FRAME_death205, brain_frames_death2, brain_dead }; mframe_t brain_frames_death1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -2, NULL}, {ai_move, 9, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t brain_move_death1 = { FRAME_death101, FRAME_death118, brain_frames_death1, brain_dead }; void brain_swing_right(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_BODY, sound_melee1, 1, ATTN_NORM, 0); } void brain_hit_right(edict_t *self) { vec3_t aim; if (!self) { return; } VectorSet(aim, MELEE_DISTANCE, self->maxs[0], 8); if (fire_hit(self, aim, (15 + (randk() % 5)), 40)) { gi.sound(self, CHAN_WEAPON, sound_melee3, 1, ATTN_NORM, 0); } } void brain_swing_left(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_BODY, sound_melee2, 1, ATTN_NORM, 0); } void brain_hit_left(edict_t *self) { vec3_t aim; if (!self) { return; } VectorSet(aim, MELEE_DISTANCE, self->mins[0], 8); if (fire_hit(self, aim, (15 + (randk() % 5)), 40)) { gi.sound(self, CHAN_WEAPON, sound_melee3, 1, ATTN_NORM, 0); } } mframe_t brain_frames_attack1[] = { {ai_charge, 8, NULL}, {ai_charge, 3, NULL}, {ai_charge, 5, NULL}, {ai_charge, 0, NULL}, {ai_charge, -3, brain_swing_right}, {ai_charge, 0, NULL}, {ai_charge, -5, NULL}, {ai_charge, -7, brain_hit_right}, {ai_charge, 0, NULL}, {ai_charge, 6, brain_swing_left}, {ai_charge, 1, NULL}, {ai_charge, 2, brain_hit_left}, {ai_charge, -3, NULL}, {ai_charge, 6, NULL}, {ai_charge, -1, NULL}, {ai_charge, -3, NULL}, {ai_charge, 2, NULL}, {ai_charge, -11, NULL} }; mmove_t brain_move_attack1 = { FRAME_attak101, FRAME_attak118, brain_frames_attack1, brain_run }; void brain_chest_open(edict_t *self) { if (!self) { return; } self->spawnflags &= ~65536; self->monsterinfo.power_armor_type = POWER_ARMOR_NONE; gi.sound(self, CHAN_BODY, sound_chest_open, 1, ATTN_NORM, 0); } void brain_tentacle_attack(edict_t *self) { vec3_t aim; if (!self) { return; } VectorSet(aim, MELEE_DISTANCE, 0, 8); if (fire_hit(self, aim, (10 + (randk() % 5)), -600) && (skill->value > 0)) { self->spawnflags |= 65536; } gi.sound(self, CHAN_WEAPON, sound_tentacles_retract, 1, ATTN_NORM, 0); } void brain_chest_closed(edict_t *self) { if (!self) { return; } self->monsterinfo.power_armor_type = POWER_ARMOR_SCREEN; if (self->spawnflags & 65536) { self->spawnflags &= ~65536; self->monsterinfo.currentmove = &brain_move_attack1; } } mframe_t brain_frames_attack2[] = { {ai_charge, 5, NULL}, {ai_charge, -4, NULL}, {ai_charge, -4, NULL}, {ai_charge, -3, NULL}, {ai_charge, 0, brain_chest_open}, {ai_charge, 0, NULL}, {ai_charge, 13, brain_tentacle_attack}, {ai_charge, 0, NULL}, {ai_charge, 2, NULL}, {ai_charge, 0, NULL}, {ai_charge, -9, brain_chest_closed}, {ai_charge, 0, NULL}, {ai_charge, 4, NULL}, {ai_charge, 3, NULL}, {ai_charge, 2, NULL}, {ai_charge, -3, NULL}, {ai_charge, -6, NULL} }; mmove_t brain_move_attack2 = { FRAME_attak201, FRAME_attak217, brain_frames_attack2, brain_run }; void brain_melee(edict_t *self) { if (!self) { return; } if (random() <= 0.5) { self->monsterinfo.currentmove = &brain_move_attack1; } else { self->monsterinfo.currentmove = &brain_move_attack2; } } mframe_t brain_frames_run[] = { {ai_run, 9, NULL}, {ai_run, 2, NULL}, {ai_run, 3, NULL}, {ai_run, 3, NULL}, {ai_run, 1, NULL}, {ai_run, 0, NULL}, {ai_run, 0, NULL}, {ai_run, 10, NULL}, {ai_run, -4, NULL}, {ai_run, -1, NULL}, {ai_run, 2, NULL} }; mmove_t brain_move_run = { FRAME_walk101, FRAME_walk111, brain_frames_run, NULL }; void brain_run(edict_t *self) { if (!self) { return; } self->monsterinfo.power_armor_type = POWER_ARMOR_SCREEN; if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &brain_move_stand; } else { self->monsterinfo.currentmove = &brain_move_run; } } void brain_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage /* unused */) { float r; if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; if (skill->value == 3) { return; /* no pain anims in nightmare */ } r = random(); if (r < 0.33) { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &brain_move_pain1; } else if (r < 0.66) { gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &brain_move_pain2; } else { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &brain_move_pain3; } } void brain_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, -8); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } void brain_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage, vec3_t point /* unused */) { int n; if (!self) { return; } self->s.effects = 0; self->monsterinfo.power_armor_type = POWER_ARMOR_NONE; /* check for gib */ if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex( "misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 2; n++) { ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } /* regular death */ gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0); self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; if (random() <= 0.5) { self->monsterinfo.currentmove = &brain_move_death1; } else { self->monsterinfo.currentmove = &brain_move_death2; } } /* * QUAKED monster_brain (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight */ void SP_monster_brain(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_chest_open = gi.soundindex("brain/brnatck1.wav"); sound_tentacles_extend = gi.soundindex("brain/brnatck2.wav"); sound_tentacles_retract = gi.soundindex("brain/brnatck3.wav"); sound_death = gi.soundindex("brain/brndeth1.wav"); sound_idle1 = gi.soundindex("brain/brnidle1.wav"); sound_idle2 = gi.soundindex("brain/brnidle2.wav"); sound_idle3 = gi.soundindex("brain/brnlens1.wav"); sound_pain1 = gi.soundindex("brain/brnpain1.wav"); sound_pain2 = gi.soundindex("brain/brnpain2.wav"); sound_sight = gi.soundindex("brain/brnsght1.wav"); sound_search = gi.soundindex("brain/brnsrch1.wav"); sound_melee1 = gi.soundindex("brain/melee1.wav"); sound_melee2 = gi.soundindex("brain/melee2.wav"); sound_melee3 = gi.soundindex("brain/melee3.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/brain/tris.md2"); VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, 32); self->health = 300; self->gib_health = -150; self->mass = 400; self->pain = brain_pain; self->die = brain_die; self->monsterinfo.stand = brain_stand; self->monsterinfo.walk = brain_walk; self->monsterinfo.run = brain_run; self->monsterinfo.dodge = brain_dodge; self->monsterinfo.melee = brain_melee; self->monsterinfo.sight = brain_sight; self->monsterinfo.search = brain_search; self->monsterinfo.idle = brain_idle; self->monsterinfo.power_armor_type = POWER_ARMOR_SCREEN; self->monsterinfo.power_armor_power = 100; gi.linkentity(self); self->monsterinfo.currentmove = &brain_move_stand; self->monsterinfo.scale = MODEL_SCALE; walkmonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/hover/0000755000175000017500000000000012615162034021131 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/hover/hover.c0000644000175000017500000003607512615162034022433 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Icarus. * * ======================================================================= */ #include "../../header/local.h" #include "hover.h" qboolean visible(edict_t *self, edict_t *other); static int sound_pain1; static int sound_pain2; static int sound_death1; static int sound_death2; static int sound_sight; static int sound_search1; static int sound_search2; void hover_sight(edict_t *self, edict_t *other /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); } void hover_search(edict_t *self) { if (random() < 0.5) { gi.sound(self, CHAN_VOICE, sound_search1, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_VOICE, sound_search2, 1, ATTN_NORM, 0); } } void hover_run(edict_t *self); void hover_stand(edict_t *self); void hover_dead(edict_t *self); void hover_attack(edict_t *self); void hover_reattack(edict_t *self); void hover_fire_blaster(edict_t *self); void hover_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); mframe_t hover_frames_stand[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t hover_move_stand = { FRAME_stand01, FRAME_stand30, hover_frames_stand, NULL }; mframe_t hover_frames_stop1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t hover_move_stop1 = { FRAME_stop101, FRAME_stop109, hover_frames_stop1, NULL }; mframe_t hover_frames_stop2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t hover_move_stop2 = { FRAME_stop201, FRAME_stop208, hover_frames_stop2, NULL }; mframe_t hover_frames_takeoff[] = { {ai_move, 0, NULL}, {ai_move, -2, NULL}, {ai_move, 5, NULL}, {ai_move, -1, NULL}, {ai_move, 1, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, -1, NULL}, {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, 2, NULL}, {ai_move, 1, NULL}, {ai_move, 1, NULL}, {ai_move, -6, NULL}, {ai_move, -9, NULL}, {ai_move, 1, NULL}, {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, 2, NULL}, {ai_move, 1, NULL}, {ai_move, 1, NULL}, {ai_move, 1, NULL}, {ai_move, 2, NULL}, {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, 3, NULL}, {ai_move, 2, NULL}, {ai_move, 0, NULL} }; mmove_t hover_move_takeoff = { FRAME_takeof01, FRAME_takeof30, hover_frames_takeoff, NULL }; mframe_t hover_frames_pain3[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t hover_move_pain3 = { FRAME_pain301, FRAME_pain309, hover_frames_pain3, hover_run }; mframe_t hover_frames_pain2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t hover_move_pain2 = { FRAME_pain201, FRAME_pain212, hover_frames_pain2, hover_run }; mframe_t hover_frames_pain1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, -8, NULL}, {ai_move, -4, NULL}, {ai_move, -6, NULL}, {ai_move, -4, NULL}, {ai_move, -3, NULL}, {ai_move, 1, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 3, NULL}, {ai_move, 1, NULL}, {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, 3, NULL}, {ai_move, 2, NULL}, {ai_move, 7, NULL}, {ai_move, 1, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 5, NULL}, {ai_move, 3, NULL}, {ai_move, 4, NULL} }; mmove_t hover_move_pain1 = { FRAME_pain101, FRAME_pain128, hover_frames_pain1, hover_run }; mframe_t hover_frames_land[] = { {ai_move, 0, NULL} }; mmove_t hover_move_land = { FRAME_land01, FRAME_land01, hover_frames_land, NULL }; mframe_t hover_frames_forward[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t hover_move_forward = { FRAME_forwrd01, FRAME_forwrd35, hover_frames_forward, NULL }; mframe_t hover_frames_walk[] = { {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL} }; mmove_t hover_move_walk = { FRAME_forwrd01, FRAME_forwrd35, hover_frames_walk, NULL }; mframe_t hover_frames_run[] = { {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL}, {ai_run, 10, NULL} }; mmove_t hover_move_run = { FRAME_forwrd01, FRAME_forwrd35, hover_frames_run, NULL }; mframe_t hover_frames_death1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -10, NULL}, {ai_move, 3, NULL}, {ai_move, 5, NULL}, {ai_move, 4, NULL}, {ai_move, 7, NULL} }; mmove_t hover_move_death1 = { FRAME_death101, FRAME_death111, hover_frames_death1, hover_dead }; mframe_t hover_frames_backward[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t hover_move_backward = { FRAME_backwd01, FRAME_backwd24, hover_frames_backward, NULL }; mframe_t hover_frames_start_attack[] = { {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL} }; mmove_t hover_move_start_attack = { FRAME_attak101, FRAME_attak103, hover_frames_start_attack, hover_attack }; mframe_t hover_frames_attack1[] = { {ai_charge, -10, hover_fire_blaster}, {ai_charge, -10, hover_fire_blaster}, {ai_charge, 0, hover_reattack}, }; mmove_t hover_move_attack1 = { FRAME_attak104, FRAME_attak106, hover_frames_attack1, NULL }; mframe_t hover_frames_end_attack[] = { {ai_charge, 1, NULL}, {ai_charge, 1, NULL} }; mmove_t hover_move_end_attack = { FRAME_attak107, FRAME_attak108, hover_frames_end_attack, hover_run }; void hover_reattack(edict_t *self) { if (!self) { return; } if (self->enemy->health > 0) { if (visible(self, self->enemy)) { if (random() <= 0.6) { self->monsterinfo.currentmove = &hover_move_attack1; return; } } } self->monsterinfo.currentmove = &hover_move_end_attack; } void hover_fire_blaster(edict_t *self) { vec3_t start; vec3_t forward, right; vec3_t end; vec3_t dir; int effect; if (!self) { return; } if (self->s.frame == FRAME_attak104) { effect = EF_HYPERBLASTER; } else { effect = 0; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_HOVER_BLASTER_1], forward, right, start); VectorCopy(self->enemy->s.origin, end); end[2] += self->enemy->viewheight; VectorSubtract(end, start, dir); monster_fire_blaster(self, start, dir, 1, 1000, MZ2_HOVER_BLASTER_1, effect); } void hover_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &hover_move_stand; } void hover_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &hover_move_stand; } else { self->monsterinfo.currentmove = &hover_move_run; } } void hover_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &hover_move_walk; } void hover_start_attack(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &hover_move_start_attack; } void hover_attack(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &hover_move_attack1; } void hover_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage) { if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; if (skill->value == 3) { return; /* no pain anims in nightmare */ } if (damage <= 25) { if (random() < 0.5) { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &hover_move_pain3; } else { gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &hover_move_pain2; } } else { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &hover_move_pain1; } } void hover_deadthink(edict_t *self) { if (!self) { return; } if (!self->groundentity && (level.time < self->timestamp)) { self->nextthink = level.time + FRAMETIME; return; } BecomeExplosion1(self); } void hover_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, -8); self->movetype = MOVETYPE_TOSS; self->think = hover_deadthink; self->nextthink = level.time + FRAMETIME; self->timestamp = level.time + 15; gi.linkentity(self); } void hover_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage, vec3_t point /* unused */) { int n; if (!self) { return; } /* check for gib */ if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex( "misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 2; n++) { ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < 2; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowHead(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } /* regular death */ if (random() < 0.5) { gi.sound(self, CHAN_VOICE, sound_death1, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_VOICE, sound_death2, 1, ATTN_NORM, 0); } self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; self->monsterinfo.currentmove = &hover_move_death1; } /* * QUAKED monster_hover (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight */ void SP_monster_hover(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_pain1 = gi.soundindex("hover/hovpain1.wav"); sound_pain2 = gi.soundindex("hover/hovpain2.wav"); sound_death1 = gi.soundindex("hover/hovdeth1.wav"); sound_death2 = gi.soundindex("hover/hovdeth2.wav"); sound_sight = gi.soundindex("hover/hovsght1.wav"); sound_search1 = gi.soundindex("hover/hovsrch1.wav"); sound_search2 = gi.soundindex("hover/hovsrch2.wav"); gi.soundindex("hover/hovatck1.wav"); self->s.sound = gi.soundindex("hover/hovidle1.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/hover/tris.md2"); VectorSet(self->mins, -24, -24, -24); VectorSet(self->maxs, 24, 24, 32); self->health = 240; self->gib_health = -100; self->mass = 150; self->pain = hover_pain; self->die = hover_die; self->monsterinfo.stand = hover_stand; self->monsterinfo.walk = hover_walk; self->monsterinfo.run = hover_run; self->monsterinfo.attack = hover_start_attack; self->monsterinfo.sight = hover_sight; self->monsterinfo.search = hover_search; gi.linkentity(self); self->monsterinfo.currentmove = &hover_move_stand; self->monsterinfo.scale = MODEL_SCALE; flymonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/hover/hover.h0000644000175000017500000001425112615162034022430 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Icarus animations. * * ======================================================================= */ #define FRAME_stand01 0 #define FRAME_stand02 1 #define FRAME_stand03 2 #define FRAME_stand04 3 #define FRAME_stand05 4 #define FRAME_stand06 5 #define FRAME_stand07 6 #define FRAME_stand08 7 #define FRAME_stand09 8 #define FRAME_stand10 9 #define FRAME_stand11 10 #define FRAME_stand12 11 #define FRAME_stand13 12 #define FRAME_stand14 13 #define FRAME_stand15 14 #define FRAME_stand16 15 #define FRAME_stand17 16 #define FRAME_stand18 17 #define FRAME_stand19 18 #define FRAME_stand20 19 #define FRAME_stand21 20 #define FRAME_stand22 21 #define FRAME_stand23 22 #define FRAME_stand24 23 #define FRAME_stand25 24 #define FRAME_stand26 25 #define FRAME_stand27 26 #define FRAME_stand28 27 #define FRAME_stand29 28 #define FRAME_stand30 29 #define FRAME_forwrd01 30 #define FRAME_forwrd02 31 #define FRAME_forwrd03 32 #define FRAME_forwrd04 33 #define FRAME_forwrd05 34 #define FRAME_forwrd06 35 #define FRAME_forwrd07 36 #define FRAME_forwrd08 37 #define FRAME_forwrd09 38 #define FRAME_forwrd10 39 #define FRAME_forwrd11 40 #define FRAME_forwrd12 41 #define FRAME_forwrd13 42 #define FRAME_forwrd14 43 #define FRAME_forwrd15 44 #define FRAME_forwrd16 45 #define FRAME_forwrd17 46 #define FRAME_forwrd18 47 #define FRAME_forwrd19 48 #define FRAME_forwrd20 49 #define FRAME_forwrd21 50 #define FRAME_forwrd22 51 #define FRAME_forwrd23 52 #define FRAME_forwrd24 53 #define FRAME_forwrd25 54 #define FRAME_forwrd26 55 #define FRAME_forwrd27 56 #define FRAME_forwrd28 57 #define FRAME_forwrd29 58 #define FRAME_forwrd30 59 #define FRAME_forwrd31 60 #define FRAME_forwrd32 61 #define FRAME_forwrd33 62 #define FRAME_forwrd34 63 #define FRAME_forwrd35 64 #define FRAME_stop101 65 #define FRAME_stop102 66 #define FRAME_stop103 67 #define FRAME_stop104 68 #define FRAME_stop105 69 #define FRAME_stop106 70 #define FRAME_stop107 71 #define FRAME_stop108 72 #define FRAME_stop109 73 #define FRAME_stop201 74 #define FRAME_stop202 75 #define FRAME_stop203 76 #define FRAME_stop204 77 #define FRAME_stop205 78 #define FRAME_stop206 79 #define FRAME_stop207 80 #define FRAME_stop208 81 #define FRAME_takeof01 82 #define FRAME_takeof02 83 #define FRAME_takeof03 84 #define FRAME_takeof04 85 #define FRAME_takeof05 86 #define FRAME_takeof06 87 #define FRAME_takeof07 88 #define FRAME_takeof08 89 #define FRAME_takeof09 90 #define FRAME_takeof10 91 #define FRAME_takeof11 92 #define FRAME_takeof12 93 #define FRAME_takeof13 94 #define FRAME_takeof14 95 #define FRAME_takeof15 96 #define FRAME_takeof16 97 #define FRAME_takeof17 98 #define FRAME_takeof18 99 #define FRAME_takeof19 100 #define FRAME_takeof20 101 #define FRAME_takeof21 102 #define FRAME_takeof22 103 #define FRAME_takeof23 104 #define FRAME_takeof24 105 #define FRAME_takeof25 106 #define FRAME_takeof26 107 #define FRAME_takeof27 108 #define FRAME_takeof28 109 #define FRAME_takeof29 110 #define FRAME_takeof30 111 #define FRAME_land01 112 #define FRAME_pain101 113 #define FRAME_pain102 114 #define FRAME_pain103 115 #define FRAME_pain104 116 #define FRAME_pain105 117 #define FRAME_pain106 118 #define FRAME_pain107 119 #define FRAME_pain108 120 #define FRAME_pain109 121 #define FRAME_pain110 122 #define FRAME_pain111 123 #define FRAME_pain112 124 #define FRAME_pain113 125 #define FRAME_pain114 126 #define FRAME_pain115 127 #define FRAME_pain116 128 #define FRAME_pain117 129 #define FRAME_pain118 130 #define FRAME_pain119 131 #define FRAME_pain120 132 #define FRAME_pain121 133 #define FRAME_pain122 134 #define FRAME_pain123 135 #define FRAME_pain124 136 #define FRAME_pain125 137 #define FRAME_pain126 138 #define FRAME_pain127 139 #define FRAME_pain128 140 #define FRAME_pain201 141 #define FRAME_pain202 142 #define FRAME_pain203 143 #define FRAME_pain204 144 #define FRAME_pain205 145 #define FRAME_pain206 146 #define FRAME_pain207 147 #define FRAME_pain208 148 #define FRAME_pain209 149 #define FRAME_pain210 150 #define FRAME_pain211 151 #define FRAME_pain212 152 #define FRAME_pain301 153 #define FRAME_pain302 154 #define FRAME_pain303 155 #define FRAME_pain304 156 #define FRAME_pain305 157 #define FRAME_pain306 158 #define FRAME_pain307 159 #define FRAME_pain308 160 #define FRAME_pain309 161 #define FRAME_death101 162 #define FRAME_death102 163 #define FRAME_death103 164 #define FRAME_death104 165 #define FRAME_death105 166 #define FRAME_death106 167 #define FRAME_death107 168 #define FRAME_death108 169 #define FRAME_death109 170 #define FRAME_death110 171 #define FRAME_death111 172 #define FRAME_backwd01 173 #define FRAME_backwd02 174 #define FRAME_backwd03 175 #define FRAME_backwd04 176 #define FRAME_backwd05 177 #define FRAME_backwd06 178 #define FRAME_backwd07 179 #define FRAME_backwd08 180 #define FRAME_backwd09 181 #define FRAME_backwd10 182 #define FRAME_backwd11 183 #define FRAME_backwd12 184 #define FRAME_backwd13 185 #define FRAME_backwd14 186 #define FRAME_backwd15 187 #define FRAME_backwd16 188 #define FRAME_backwd17 189 #define FRAME_backwd18 190 #define FRAME_backwd19 191 #define FRAME_backwd20 192 #define FRAME_backwd21 193 #define FRAME_backwd22 194 #define FRAME_backwd23 195 #define FRAME_backwd24 196 #define FRAME_attak101 197 #define FRAME_attak102 198 #define FRAME_attak103 199 #define FRAME_attak104 200 #define FRAME_attak105 201 #define FRAME_attak106 202 #define FRAME_attak107 203 #define FRAME_attak108 204 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/boss3/0000755000175000017500000000000012615162034021037 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/boss3/boss3.c0000644000175000017500000000433312615162034022237 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Final boss, just standing and waiting at "inner chamber". * * ======================================================================= */ #include "../../header/local.h" #include "boss32.h" void Use_Boss3(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!ent) { return; } gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_BOSSTPORT); gi.WritePosition(ent->s.origin); gi.multicast(ent->s.origin, MULTICAST_PVS); G_FreeEdict(ent); } void Think_Boss3Stand(edict_t *ent) { if (!ent) { return; } if (ent->s.frame == FRAME_stand260) { ent->s.frame = FRAME_stand201; } else { ent->s.frame++; } ent->nextthink = level.time + FRAMETIME; } /* * QUAKED monster_boss3_stand (1 .5 0) (-32 -32 0) (32 32 90) * * Just stands and cycles in one place until targeted, then teleports away. */ void SP_monster_boss3_stand(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->model = "models/monsters/boss3/rider/tris.md2"; self->s.modelindex = gi.modelindex(self->model); self->s.frame = FRAME_stand201; gi.soundindex("misc/bigtele.wav"); VectorSet(self->mins, -32, -32, 0); VectorSet(self->maxs, 32, 32, 90); self->use = Use_Boss3; self->think = Think_Boss3Stand; self->nextthink = level.time + FRAMETIME; gi.linkentity(self); } yquake2-QUAKE2_5_32/src/game/monster/boss3/boss32.c0000644000175000017500000005366412615162034022334 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Final boss, stage 2 (makron). * * ======================================================================= */ #include "../../header/local.h" #include "boss32.h" qboolean visible(edict_t *self, edict_t *other); void MakronRailgun(edict_t *self); void MakronSaveloc(edict_t *self); void MakronHyperblaster(edict_t *self); void makron_step_left(edict_t *self); void makron_step_right(edict_t *self); void makronBFG(edict_t *self); void makron_dead(edict_t *self); static int sound_pain4; static int sound_pain5; static int sound_pain6; static int sound_death; static int sound_step_left; static int sound_step_right; static int sound_attack_bfg; static int sound_brainsplorch; static int sound_prerailgun; static int sound_popup; static int sound_taunt1; static int sound_taunt2; static int sound_taunt3; static int sound_hit; void makron_taunt(edict_t *self) { float r; if (!self) { return; } r = random(); if (r <= 0.3) { gi.sound(self, CHAN_AUTO, sound_taunt1, 1, ATTN_NONE, 0); } else if (r <= 0.6) { gi.sound(self, CHAN_AUTO, sound_taunt2, 1, ATTN_NONE, 0); } else { gi.sound(self, CHAN_AUTO, sound_taunt3, 1, ATTN_NONE, 0); } } mframe_t makron_frames_stand[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, /* 10 */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, /* 20 */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, /* 30 */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, /* 40 */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, /* 50 */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} /* 60 */ }; mmove_t makron_move_stand = { FRAME_stand201, FRAME_stand260, makron_frames_stand, NULL }; void makron_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &makron_move_stand; } mframe_t makron_frames_run[] = { {ai_run, 3, makron_step_left}, {ai_run, 12, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, makron_step_right}, {ai_run, 6, NULL}, {ai_run, 12, NULL}, {ai_run, 9, NULL}, {ai_run, 6, NULL}, {ai_run, 12, NULL} }; mmove_t makron_move_run = { FRAME_walk204, FRAME_walk213, makron_frames_run, NULL }; void makron_hit(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_AUTO, sound_hit, 1, ATTN_NONE, 0); } void makron_popup(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_BODY, sound_popup, 1, ATTN_NONE, 0); } void makron_step_left(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_BODY, sound_step_left, 1, ATTN_NORM, 0); } void makron_step_right(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_BODY, sound_step_right, 1, ATTN_NORM, 0); } void makron_brainsplorch(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_brainsplorch, 1, ATTN_NORM, 0); } void makron_prerailgun(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_prerailgun, 1, ATTN_NORM, 0); } mframe_t makron_frames_walk[] = { {ai_walk, 3, makron_step_left}, {ai_walk, 12, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, makron_step_right}, {ai_walk, 6, NULL}, {ai_walk, 12, NULL}, {ai_walk, 9, NULL}, {ai_walk, 6, NULL}, {ai_walk, 12, NULL} }; mmove_t makron_move_walk = { FRAME_walk204, FRAME_walk213, makron_frames_run, NULL }; void makron_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &makron_move_walk; } void makron_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &makron_move_stand; } else { self->monsterinfo.currentmove = &makron_move_run; } } mframe_t makron_frames_pain6[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 10 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, makron_popup}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 20 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, makron_taunt}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t makron_move_pain6 = { FRAME_pain601, FRAME_pain627, makron_frames_pain6, makron_run }; mframe_t makron_frames_pain5[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t makron_move_pain5 = { FRAME_pain501, FRAME_pain504, makron_frames_pain5, makron_run }; mframe_t makron_frames_pain4[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t makron_move_pain4 = { FRAME_pain401, FRAME_pain404, makron_frames_pain4, makron_run }; mframe_t makron_frames_death2[] = { {ai_move, -15, NULL}, {ai_move, 3, NULL}, {ai_move, -12, NULL}, {ai_move, 0, makron_step_left}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 10 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 11, NULL}, {ai_move, 12, NULL}, {ai_move, 11, makron_step_right}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 20 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 30 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 5, NULL}, {ai_move, 7, NULL}, {ai_move, 6, makron_step_left}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -1, NULL}, {ai_move, 2, NULL}, /* 40 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 50 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -6, NULL}, {ai_move, -4, NULL}, {ai_move, -6, makron_step_right}, {ai_move, -4, NULL}, {ai_move, -4, makron_step_left}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 60 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -2, NULL}, {ai_move, -5, NULL}, {ai_move, -3, makron_step_right}, {ai_move, -8, NULL}, {ai_move, -3, makron_step_left}, {ai_move, -7, NULL}, {ai_move, -4, NULL}, {ai_move, -4, makron_step_right}, /* 70 */ {ai_move, -6, NULL}, {ai_move, -7, NULL}, {ai_move, 0, makron_step_left}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 80 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -2, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, 0, NULL}, /* 90 */ {ai_move, 27, makron_hit}, {ai_move, 26, NULL}, {ai_move, 0, makron_brainsplorch}, {ai_move, 0, NULL}, {ai_move, 0, NULL} /* 95 */ }; mmove_t makron_move_death2 = { FRAME_death201, FRAME_death295, makron_frames_death2, makron_dead }; mframe_t makron_frames_death3[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t makron_move_death3 = { FRAME_death301, FRAME_death320, makron_frames_death3, NULL }; mframe_t makron_frames_sight[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t makron_move_sight = { FRAME_active01, FRAME_active13, makron_frames_sight, makron_run }; void makronBFG(edict_t *self) { vec3_t forward, right; vec3_t start; vec3_t dir; vec3_t vec; if (!self) { return; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_MAKRON_BFG], forward, right, start); VectorCopy(self->enemy->s.origin, vec); vec[2] += self->enemy->viewheight; VectorSubtract(vec, start, dir); VectorNormalize(dir); gi.sound(self, CHAN_VOICE, sound_attack_bfg, 1, ATTN_NORM, 0); monster_fire_bfg(self, start, dir, 50, 300, 100, 300, MZ2_MAKRON_BFG); } mframe_t makron_frames_attack3[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, makronBFG}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t makron_move_attack3 = { FRAME_attak301, FRAME_attak308, makron_frames_attack3, makron_run }; mframe_t makron_frames_attack4[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, MakronHyperblaster}, /* fire */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t makron_move_attack4 = { FRAME_attak401, FRAME_attak426, makron_frames_attack4, makron_run }; mframe_t makron_frames_attack5[] = { {ai_charge, 0, makron_prerailgun}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, MakronSaveloc}, {ai_move, 0, MakronRailgun}, /* Fire railgun */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t makron_move_attack5 = { FRAME_attak501, FRAME_attak516, makron_frames_attack5, makron_run }; void MakronSaveloc(edict_t *self) { if (!self) { return; } VectorCopy(self->enemy->s.origin, self->pos1); /* save for aiming the shot */ self->pos1[2] += self->enemy->viewheight; } void MakronRailgun(edict_t *self) { vec3_t start; vec3_t dir; vec3_t forward, right; if (!self) { return; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_MAKRON_RAILGUN_1], forward, right, start); /* calc direction to where we targted */ VectorSubtract(self->pos1, start, dir); VectorNormalize(dir); monster_fire_railgun(self, start, dir, 50, 100, MZ2_MAKRON_RAILGUN_1); } void MakronHyperblaster(edict_t *self) { vec3_t dir; vec3_t vec; vec3_t start; vec3_t forward, right; int flash_number; if (!self) { return; } flash_number = MZ2_MAKRON_BLASTER_1 + (self->s.frame - FRAME_attak405); AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[flash_number], forward, right, start); if (self->enemy) { VectorCopy(self->enemy->s.origin, vec); vec[2] += self->enemy->viewheight; VectorSubtract(vec, start, vec); vectoangles(vec, vec); dir[0] = vec[0]; } else { dir[0] = 0; } if (self->s.frame <= FRAME_attak413) { dir[1] = self->s.angles[1] - 10 * (self->s.frame - FRAME_attak413); } else { dir[1] = self->s.angles[1] + 10 * (self->s.frame - FRAME_attak421); } dir[2] = 0; AngleVectors(dir, forward, NULL, NULL); monster_fire_blaster(self, start, forward, 15, 1000, MZ2_MAKRON_BLASTER_1, EF_BLASTER); } void makron_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage) { if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { return; } /* Lessen the chance of him going into his pain frames */ if (damage <= 25) { if (random() < 0.2) { return; } } self->pain_debounce_time = level.time + 3; if (skill->value == 3) { return; /* no pain anims in nightmare */ } if (damage <= 40) { gi.sound(self, CHAN_VOICE, sound_pain4, 1, ATTN_NONE, 0); self->monsterinfo.currentmove = &makron_move_pain4; } else if (damage <= 110) { gi.sound(self, CHAN_VOICE, sound_pain5, 1, ATTN_NONE, 0); self->monsterinfo.currentmove = &makron_move_pain5; } else { if (damage <= 150) { if (random() <= 0.45) { gi.sound(self, CHAN_VOICE, sound_pain6, 1, ATTN_NONE, 0); self->monsterinfo.currentmove = &makron_move_pain6; } } else { if (random() <= 0.35) { gi.sound(self, CHAN_VOICE, sound_pain6, 1, ATTN_NONE, 0); self->monsterinfo.currentmove = &makron_move_pain6; } } } } void makron_sight(edict_t *self, edict_t *other /* unused */) { if (!self) { return; } self->monsterinfo.currentmove = &makron_move_sight; } void makron_attack(edict_t *self) { float r; if (!self) { return; } r = random(); if (r <= 0.3) { self->monsterinfo.currentmove = &makron_move_attack3; } else if (r <= 0.6) { self->monsterinfo.currentmove = &makron_move_attack4; } else { self->monsterinfo.currentmove = &makron_move_attack5; } } /* * Makron Torso. This needs to be spawned in */ void makron_torso_think(edict_t *self) { if (!self) { return; } if (++self->s.frame < 365) { self->nextthink = level.time + FRAMETIME; } else { self->s.frame = 346; self->nextthink = level.time + FRAMETIME; } } void makron_torso(edict_t *ent) { if (!ent) { return; } ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_NOT; VectorSet(ent->mins, -8, -8, 0); VectorSet(ent->maxs, 8, 8, 8); ent->s.frame = 346; ent->s.modelindex = gi.modelindex("models/monsters/boss3/rider/tris.md2"); ent->think = makron_torso_think; ent->nextthink = level.time + 2 * FRAMETIME; ent->s.sound = gi.soundindex("makron/spine.wav"); gi.linkentity(ent); } void makron_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -60, -60, 0); VectorSet(self->maxs, 60, 60, 72); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } void makron_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage /* unused */, vec3_t point /* unused */) { edict_t *tempent; int n; if (!self) { return; } self->s.sound = 0; /* check for gib */ if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex( "misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 1; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_metal/tris.md2", damage, GIB_METALLIC); } ThrowHead(self, "models/objects/gibs/gear/tris.md2", damage, GIB_METALLIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } /* regular death */ gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NONE, 0); self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; tempent = G_Spawn(); VectorCopy(self->s.origin, tempent->s.origin); VectorCopy(self->s.angles, tempent->s.angles); tempent->s.origin[1] -= 84; makron_torso(tempent); self->monsterinfo.currentmove = &makron_move_death2; } qboolean Makron_CheckAttack(edict_t *self) { vec3_t spot1, spot2; vec3_t temp; float chance; trace_t tr; int enemy_range; float enemy_yaw; if (!self) { return false; } if (self->enemy->health > 0) { /* see if any entities are in the way of the shot */ VectorCopy(self->s.origin, spot1); spot1[2] += self->viewheight; VectorCopy(self->enemy->s.origin, spot2); spot2[2] += self->enemy->viewheight; tr = gi.trace( spot1, NULL, NULL, spot2, self, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA); /* do we have a clear shot? */ if (tr.ent != self->enemy) { return false; } } enemy_range = range(self, self->enemy); VectorSubtract(self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw(temp); self->ideal_yaw = enemy_yaw; /* melee attack */ if (enemy_range == RANGE_MELEE) { if (self->monsterinfo.melee) { self->monsterinfo.attack_state = AS_MELEE; } else { self->monsterinfo.attack_state = AS_MISSILE; } return true; } /* missile attack */ if (!self->monsterinfo.attack) { return false; } if (level.time < self->monsterinfo.attack_finished) { return false; } if (enemy_range == RANGE_FAR) { return false; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { chance = 0.4; } else if (enemy_range == RANGE_MELEE) { chance = 0.8; } else if (enemy_range == RANGE_NEAR) { chance = 0.4; } else if (enemy_range == RANGE_MID) { chance = 0.2; } else { return false; } if (random() < chance) { self->monsterinfo.attack_state = AS_MISSILE; self->monsterinfo.attack_finished = level.time + 2 * random(); return true; } if (self->flags & FL_FLY) { if (random() < 0.3) { self->monsterinfo.attack_state = AS_SLIDING; } else { self->monsterinfo.attack_state = AS_STRAIGHT; } } return false; } void MakronPrecache(void) { sound_pain4 = gi.soundindex("makron/pain3.wav"); sound_pain5 = gi.soundindex("makron/pain2.wav"); sound_pain6 = gi.soundindex("makron/pain1.wav"); sound_death = gi.soundindex("makron/death.wav"); sound_step_left = gi.soundindex("makron/step1.wav"); sound_step_right = gi.soundindex("makron/step2.wav"); sound_attack_bfg = gi.soundindex("makron/bfg_fire.wav"); sound_brainsplorch = gi.soundindex("makron/brain1.wav"); sound_prerailgun = gi.soundindex("makron/rail_up.wav"); sound_popup = gi.soundindex("makron/popup.wav"); sound_taunt1 = gi.soundindex("makron/voice4.wav"); sound_taunt2 = gi.soundindex("makron/voice3.wav"); sound_taunt3 = gi.soundindex("makron/voice.wav"); sound_hit = gi.soundindex("makron/bhit.wav"); gi.modelindex("models/monsters/boss3/rider/tris.md2"); } /* * QUAKED monster_makron (1 .5 0) (-30 -30 0) (30 30 90) Ambush Trigger_Spawn Sight */ void SP_monster_makron(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } MakronPrecache(); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/boss3/rider/tris.md2"); VectorSet(self->mins, -30, -30, 0); VectorSet(self->maxs, 30, 30, 90); self->health = 3000; self->gib_health = -2000; self->mass = 500; self->pain = makron_pain; self->die = makron_die; self->monsterinfo.stand = makron_stand; self->monsterinfo.walk = makron_walk; self->monsterinfo.run = makron_run; self->monsterinfo.dodge = NULL; self->monsterinfo.attack = makron_attack; self->monsterinfo.melee = NULL; self->monsterinfo.sight = makron_sight; self->monsterinfo.checkattack = Makron_CheckAttack; gi.linkentity(self); self->monsterinfo.currentmove = &makron_move_sight; self->monsterinfo.scale = MODEL_SCALE; walkmonster_start(self); } void MakronSpawn(edict_t *self) { vec3_t vec; edict_t *player; if (!self) { return; } SP_monster_makron(self); /* jump at player */ player = level.sight_client; if (!player) { return; } VectorSubtract(player->s.origin, self->s.origin, vec); self->s.angles[YAW] = vectoyaw(vec); VectorNormalize(vec); VectorMA(vec3_origin, 400, vec, self->velocity); self->velocity[2] = 200; self->groundentity = NULL; } /* * Jorg is just about dead, so * set up to launch Makron out */ void MakronToss(edict_t *self) { edict_t *ent; if (!self) { return; } ent = G_Spawn(); ent->nextthink = level.time + 0.8; ent->think = MakronSpawn; ent->target = self->target; VectorCopy(self->s.origin, ent->s.origin); } yquake2-QUAKE2_5_32/src/game/monster/boss3/boss32.h0000644000175000017500000003307412615162034022332 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Final boss, stage 2 (makron). * * ======================================================================= */ #define FRAME_attak101 0 #define FRAME_attak102 1 #define FRAME_attak103 2 #define FRAME_attak104 3 #define FRAME_attak105 4 #define FRAME_attak106 5 #define FRAME_attak107 6 #define FRAME_attak108 7 #define FRAME_attak109 8 #define FRAME_attak110 9 #define FRAME_attak111 10 #define FRAME_attak112 11 #define FRAME_attak113 12 #define FRAME_attak114 13 #define FRAME_attak115 14 #define FRAME_attak116 15 #define FRAME_attak117 16 #define FRAME_attak118 17 #define FRAME_attak201 18 #define FRAME_attak202 19 #define FRAME_attak203 20 #define FRAME_attak204 21 #define FRAME_attak205 22 #define FRAME_attak206 23 #define FRAME_attak207 24 #define FRAME_attak208 25 #define FRAME_attak209 26 #define FRAME_attak210 27 #define FRAME_attak211 28 #define FRAME_attak212 29 #define FRAME_attak213 30 #define FRAME_death01 31 #define FRAME_death02 32 #define FRAME_death03 33 #define FRAME_death04 34 #define FRAME_death05 35 #define FRAME_death06 36 #define FRAME_death07 37 #define FRAME_death08 38 #define FRAME_death09 39 #define FRAME_death10 40 #define FRAME_death11 41 #define FRAME_death12 42 #define FRAME_death13 43 #define FRAME_death14 44 #define FRAME_death15 45 #define FRAME_death16 46 #define FRAME_death17 47 #define FRAME_death18 48 #define FRAME_death19 49 #define FRAME_death20 50 #define FRAME_death21 51 #define FRAME_death22 52 #define FRAME_death23 53 #define FRAME_death24 54 #define FRAME_death25 55 #define FRAME_death26 56 #define FRAME_death27 57 #define FRAME_death28 58 #define FRAME_death29 59 #define FRAME_death30 60 #define FRAME_death31 61 #define FRAME_death32 62 #define FRAME_death33 63 #define FRAME_death34 64 #define FRAME_death35 65 #define FRAME_death36 66 #define FRAME_death37 67 #define FRAME_death38 68 #define FRAME_death39 69 #define FRAME_death40 70 #define FRAME_death41 71 #define FRAME_death42 72 #define FRAME_death43 73 #define FRAME_death44 74 #define FRAME_death45 75 #define FRAME_death46 76 #define FRAME_death47 77 #define FRAME_death48 78 #define FRAME_death49 79 #define FRAME_death50 80 #define FRAME_pain101 81 #define FRAME_pain102 82 #define FRAME_pain103 83 #define FRAME_pain201 84 #define FRAME_pain202 85 #define FRAME_pain203 86 #define FRAME_pain301 87 #define FRAME_pain302 88 #define FRAME_pain303 89 #define FRAME_pain304 90 #define FRAME_pain305 91 #define FRAME_pain306 92 #define FRAME_pain307 93 #define FRAME_pain308 94 #define FRAME_pain309 95 #define FRAME_pain310 96 #define FRAME_pain311 97 #define FRAME_pain312 98 #define FRAME_pain313 99 #define FRAME_pain314 100 #define FRAME_pain315 101 #define FRAME_pain316 102 #define FRAME_pain317 103 #define FRAME_pain318 104 #define FRAME_pain319 105 #define FRAME_pain320 106 #define FRAME_pain321 107 #define FRAME_pain322 108 #define FRAME_pain323 109 #define FRAME_pain324 110 #define FRAME_pain325 111 #define FRAME_stand01 112 #define FRAME_stand02 113 #define FRAME_stand03 114 #define FRAME_stand04 115 #define FRAME_stand05 116 #define FRAME_stand06 117 #define FRAME_stand07 118 #define FRAME_stand08 119 #define FRAME_stand09 120 #define FRAME_stand10 121 #define FRAME_stand11 122 #define FRAME_stand12 123 #define FRAME_stand13 124 #define FRAME_stand14 125 #define FRAME_stand15 126 #define FRAME_stand16 127 #define FRAME_stand17 128 #define FRAME_stand18 129 #define FRAME_stand19 130 #define FRAME_stand20 131 #define FRAME_stand21 132 #define FRAME_stand22 133 #define FRAME_stand23 134 #define FRAME_stand24 135 #define FRAME_stand25 136 #define FRAME_stand26 137 #define FRAME_stand27 138 #define FRAME_stand28 139 #define FRAME_stand29 140 #define FRAME_stand30 141 #define FRAME_stand31 142 #define FRAME_stand32 143 #define FRAME_stand33 144 #define FRAME_stand34 145 #define FRAME_stand35 146 #define FRAME_stand36 147 #define FRAME_stand37 148 #define FRAME_stand38 149 #define FRAME_stand39 150 #define FRAME_stand40 151 #define FRAME_stand41 152 #define FRAME_stand42 153 #define FRAME_stand43 154 #define FRAME_stand44 155 #define FRAME_stand45 156 #define FRAME_stand46 157 #define FRAME_stand47 158 #define FRAME_stand48 159 #define FRAME_stand49 160 #define FRAME_stand50 161 #define FRAME_stand51 162 #define FRAME_walk01 163 #define FRAME_walk02 164 #define FRAME_walk03 165 #define FRAME_walk04 166 #define FRAME_walk05 167 #define FRAME_walk06 168 #define FRAME_walk07 169 #define FRAME_walk08 170 #define FRAME_walk09 171 #define FRAME_walk10 172 #define FRAME_walk11 173 #define FRAME_walk12 174 #define FRAME_walk13 175 #define FRAME_walk14 176 #define FRAME_walk15 177 #define FRAME_walk16 178 #define FRAME_walk17 179 #define FRAME_walk18 180 #define FRAME_walk19 181 #define FRAME_walk20 182 #define FRAME_walk21 183 #define FRAME_walk22 184 #define FRAME_walk23 185 #define FRAME_walk24 186 #define FRAME_walk25 187 #define FRAME_active01 188 #define FRAME_active02 189 #define FRAME_active03 190 #define FRAME_active04 191 #define FRAME_active05 192 #define FRAME_active06 193 #define FRAME_active07 194 #define FRAME_active08 195 #define FRAME_active09 196 #define FRAME_active10 197 #define FRAME_active11 198 #define FRAME_active12 199 #define FRAME_active13 200 #define FRAME_attak301 201 #define FRAME_attak302 202 #define FRAME_attak303 203 #define FRAME_attak304 204 #define FRAME_attak305 205 #define FRAME_attak306 206 #define FRAME_attak307 207 #define FRAME_attak308 208 #define FRAME_attak401 209 #define FRAME_attak402 210 #define FRAME_attak403 211 #define FRAME_attak404 212 #define FRAME_attak405 213 #define FRAME_attak406 214 #define FRAME_attak407 215 #define FRAME_attak408 216 #define FRAME_attak409 217 #define FRAME_attak410 218 #define FRAME_attak411 219 #define FRAME_attak412 220 #define FRAME_attak413 221 #define FRAME_attak414 222 #define FRAME_attak415 223 #define FRAME_attak416 224 #define FRAME_attak417 225 #define FRAME_attak418 226 #define FRAME_attak419 227 #define FRAME_attak420 228 #define FRAME_attak421 229 #define FRAME_attak422 230 #define FRAME_attak423 231 #define FRAME_attak424 232 #define FRAME_attak425 233 #define FRAME_attak426 234 #define FRAME_attak501 235 #define FRAME_attak502 236 #define FRAME_attak503 237 #define FRAME_attak504 238 #define FRAME_attak505 239 #define FRAME_attak506 240 #define FRAME_attak507 241 #define FRAME_attak508 242 #define FRAME_attak509 243 #define FRAME_attak510 244 #define FRAME_attak511 245 #define FRAME_attak512 246 #define FRAME_attak513 247 #define FRAME_attak514 248 #define FRAME_attak515 249 #define FRAME_attak516 250 #define FRAME_death201 251 #define FRAME_death202 252 #define FRAME_death203 253 #define FRAME_death204 254 #define FRAME_death205 255 #define FRAME_death206 256 #define FRAME_death207 257 #define FRAME_death208 258 #define FRAME_death209 259 #define FRAME_death210 260 #define FRAME_death211 261 #define FRAME_death212 262 #define FRAME_death213 263 #define FRAME_death214 264 #define FRAME_death215 265 #define FRAME_death216 266 #define FRAME_death217 267 #define FRAME_death218 268 #define FRAME_death219 269 #define FRAME_death220 270 #define FRAME_death221 271 #define FRAME_death222 272 #define FRAME_death223 273 #define FRAME_death224 274 #define FRAME_death225 275 #define FRAME_death226 276 #define FRAME_death227 277 #define FRAME_death228 278 #define FRAME_death229 279 #define FRAME_death230 280 #define FRAME_death231 281 #define FRAME_death232 282 #define FRAME_death233 283 #define FRAME_death234 284 #define FRAME_death235 285 #define FRAME_death236 286 #define FRAME_death237 287 #define FRAME_death238 288 #define FRAME_death239 289 #define FRAME_death240 290 #define FRAME_death241 291 #define FRAME_death242 292 #define FRAME_death243 293 #define FRAME_death244 294 #define FRAME_death245 295 #define FRAME_death246 296 #define FRAME_death247 297 #define FRAME_death248 298 #define FRAME_death249 299 #define FRAME_death250 300 #define FRAME_death251 301 #define FRAME_death252 302 #define FRAME_death253 303 #define FRAME_death254 304 #define FRAME_death255 305 #define FRAME_death256 306 #define FRAME_death257 307 #define FRAME_death258 308 #define FRAME_death259 309 #define FRAME_death260 310 #define FRAME_death261 311 #define FRAME_death262 312 #define FRAME_death263 313 #define FRAME_death264 314 #define FRAME_death265 315 #define FRAME_death266 316 #define FRAME_death267 317 #define FRAME_death268 318 #define FRAME_death269 319 #define FRAME_death270 320 #define FRAME_death271 321 #define FRAME_death272 322 #define FRAME_death273 323 #define FRAME_death274 324 #define FRAME_death275 325 #define FRAME_death276 326 #define FRAME_death277 327 #define FRAME_death278 328 #define FRAME_death279 329 #define FRAME_death280 330 #define FRAME_death281 331 #define FRAME_death282 332 #define FRAME_death283 333 #define FRAME_death284 334 #define FRAME_death285 335 #define FRAME_death286 336 #define FRAME_death287 337 #define FRAME_death288 338 #define FRAME_death289 339 #define FRAME_death290 340 #define FRAME_death291 341 #define FRAME_death292 342 #define FRAME_death293 343 #define FRAME_death294 344 #define FRAME_death295 345 #define FRAME_death301 346 #define FRAME_death302 347 #define FRAME_death303 348 #define FRAME_death304 349 #define FRAME_death305 350 #define FRAME_death306 351 #define FRAME_death307 352 #define FRAME_death308 353 #define FRAME_death309 354 #define FRAME_death310 355 #define FRAME_death311 356 #define FRAME_death312 357 #define FRAME_death313 358 #define FRAME_death314 359 #define FRAME_death315 360 #define FRAME_death316 361 #define FRAME_death317 362 #define FRAME_death318 363 #define FRAME_death319 364 #define FRAME_death320 365 #define FRAME_jump01 366 #define FRAME_jump02 367 #define FRAME_jump03 368 #define FRAME_jump04 369 #define FRAME_jump05 370 #define FRAME_jump06 371 #define FRAME_jump07 372 #define FRAME_jump08 373 #define FRAME_jump09 374 #define FRAME_jump10 375 #define FRAME_jump11 376 #define FRAME_jump12 377 #define FRAME_jump13 378 #define FRAME_pain401 379 #define FRAME_pain402 380 #define FRAME_pain403 381 #define FRAME_pain404 382 #define FRAME_pain501 383 #define FRAME_pain502 384 #define FRAME_pain503 385 #define FRAME_pain504 386 #define FRAME_pain601 387 #define FRAME_pain602 388 #define FRAME_pain603 389 #define FRAME_pain604 390 #define FRAME_pain605 391 #define FRAME_pain606 392 #define FRAME_pain607 393 #define FRAME_pain608 394 #define FRAME_pain609 395 #define FRAME_pain610 396 #define FRAME_pain611 397 #define FRAME_pain612 398 #define FRAME_pain613 399 #define FRAME_pain614 400 #define FRAME_pain615 401 #define FRAME_pain616 402 #define FRAME_pain617 403 #define FRAME_pain618 404 #define FRAME_pain619 405 #define FRAME_pain620 406 #define FRAME_pain621 407 #define FRAME_pain622 408 #define FRAME_pain623 409 #define FRAME_pain624 410 #define FRAME_pain625 411 #define FRAME_pain626 412 #define FRAME_pain627 413 #define FRAME_stand201 414 #define FRAME_stand202 415 #define FRAME_stand203 416 #define FRAME_stand204 417 #define FRAME_stand205 418 #define FRAME_stand206 419 #define FRAME_stand207 420 #define FRAME_stand208 421 #define FRAME_stand209 422 #define FRAME_stand210 423 #define FRAME_stand211 424 #define FRAME_stand212 425 #define FRAME_stand213 426 #define FRAME_stand214 427 #define FRAME_stand215 428 #define FRAME_stand216 429 #define FRAME_stand217 430 #define FRAME_stand218 431 #define FRAME_stand219 432 #define FRAME_stand220 433 #define FRAME_stand221 434 #define FRAME_stand222 435 #define FRAME_stand223 436 #define FRAME_stand224 437 #define FRAME_stand225 438 #define FRAME_stand226 439 #define FRAME_stand227 440 #define FRAME_stand228 441 #define FRAME_stand229 442 #define FRAME_stand230 443 #define FRAME_stand231 444 #define FRAME_stand232 445 #define FRAME_stand233 446 #define FRAME_stand234 447 #define FRAME_stand235 448 #define FRAME_stand236 449 #define FRAME_stand237 450 #define FRAME_stand238 451 #define FRAME_stand239 452 #define FRAME_stand240 453 #define FRAME_stand241 454 #define FRAME_stand242 455 #define FRAME_stand243 456 #define FRAME_stand244 457 #define FRAME_stand245 458 #define FRAME_stand246 459 #define FRAME_stand247 460 #define FRAME_stand248 461 #define FRAME_stand249 462 #define FRAME_stand250 463 #define FRAME_stand251 464 #define FRAME_stand252 465 #define FRAME_stand253 466 #define FRAME_stand254 467 #define FRAME_stand255 468 #define FRAME_stand256 469 #define FRAME_stand257 470 #define FRAME_stand258 471 #define FRAME_stand259 472 #define FRAME_stand260 473 #define FRAME_walk201 474 #define FRAME_walk202 475 #define FRAME_walk203 476 #define FRAME_walk204 477 #define FRAME_walk205 478 #define FRAME_walk206 479 #define FRAME_walk207 480 #define FRAME_walk208 481 #define FRAME_walk209 482 #define FRAME_walk210 483 #define FRAME_walk211 484 #define FRAME_walk212 485 #define FRAME_walk213 486 #define FRAME_walk214 487 #define FRAME_walk215 488 #define FRAME_walk216 489 #define FRAME_walk217 490 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/boss3/boss31.c0000644000175000017500000004323412615162034022323 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Final boss, stage 1 (jorg). * * ======================================================================= */ #include "../../header/local.h" #include "boss31.h" extern void SP_monster_makron(edict_t *self); qboolean visible(edict_t *self, edict_t *other); static int sound_pain1; static int sound_pain2; static int sound_pain3; static int sound_idle; static int sound_death; static int sound_search1; static int sound_search2; static int sound_search3; static int sound_attack1; static int sound_attack2; static int sound_firegun; static int sound_step_left; static int sound_step_right; static int sound_death_hit; void BossExplode(edict_t *self); void MakronToss(edict_t *self); void jorg_search(edict_t *self) { float r; if (!self) { return; } r = random(); if (r <= 0.3) { gi.sound(self, CHAN_VOICE, sound_search1, 1, ATTN_NORM, 0); } else if (r <= 0.6) { gi.sound(self, CHAN_VOICE, sound_search2, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_VOICE, sound_search3, 1, ATTN_NORM, 0); } } void jorg_dead(edict_t *self); void jorgBFG(edict_t *self); void jorgMachineGun(edict_t *self); void jorg_firebullet(edict_t *self); void jorg_reattack1(edict_t *self); void jorg_attack1(edict_t *self); void jorg_idle(edict_t *self); void jorg_step_left(edict_t *self); void jorg_step_right(edict_t *self); void jorg_death_hit(edict_t *self); mframe_t jorg_frames_stand[] = { {ai_stand, 0, jorg_idle}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, /* 10 */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, /* 20 */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, /* 30 */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 19, NULL}, {ai_stand, 11, jorg_step_left}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 6, NULL}, {ai_stand, 9, jorg_step_right}, {ai_stand, 0, NULL}, /* 40 */ {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, -2, NULL}, {ai_stand, -17, jorg_step_left}, {ai_stand, 0, NULL}, {ai_stand, -12, NULL}, /* 50 */ {ai_stand, -14, jorg_step_right} }; mmove_t jorg_move_stand = { FRAME_stand01, FRAME_stand51, jorg_frames_stand, NULL }; void jorg_idle(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_NORM, 0); } void jorg_death_hit(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_BODY, sound_death_hit, 1, ATTN_NORM, 0); } void jorg_step_left(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_BODY, sound_step_left, 1, ATTN_NORM, 0); } void jorg_step_right(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_BODY, sound_step_right, 1, ATTN_NORM, 0); } void jorg_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &jorg_move_stand; } mframe_t jorg_frames_run[] = { {ai_run, 17, jorg_step_left}, {ai_run, 0, NULL}, {ai_run, 0, NULL}, {ai_run, 0, NULL}, {ai_run, 12, NULL}, {ai_run, 8, NULL}, {ai_run, 10, NULL}, {ai_run, 33, jorg_step_right}, {ai_run, 0, NULL}, {ai_run, 0, NULL}, {ai_run, 0, NULL}, {ai_run, 9, NULL}, {ai_run, 9, NULL}, {ai_run, 9, NULL} }; mmove_t jorg_move_run = { FRAME_walk06, FRAME_walk19, jorg_frames_run, NULL }; mframe_t jorg_frames_start_walk[] = { {ai_walk, 5, NULL}, {ai_walk, 6, NULL}, {ai_walk, 7, NULL}, {ai_walk, 9, NULL}, {ai_walk, 15, NULL} }; mmove_t jorg_move_start_walk = { FRAME_walk01, FRAME_walk05, jorg_frames_start_walk, NULL }; mframe_t jorg_frames_walk[] = { {ai_walk, 17, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 12, NULL}, {ai_walk, 8, NULL}, {ai_walk, 10, NULL}, {ai_walk, 33, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 9, NULL}, {ai_walk, 9, NULL}, {ai_walk, 9, NULL} }; mmove_t jorg_move_walk = { FRAME_walk06, FRAME_walk19, jorg_frames_walk, NULL }; mframe_t jorg_frames_end_walk[] = { {ai_walk, 11, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 8, NULL}, {ai_walk, -8, NULL} }; mmove_t jorg_move_end_walk = { FRAME_walk20, FRAME_walk25, jorg_frames_end_walk, NULL }; void jorg_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &jorg_move_walk; } void jorg_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &jorg_move_stand; } else { self->monsterinfo.currentmove = &jorg_move_run; } } mframe_t jorg_frames_pain3[] = { {ai_move, -28, NULL}, {ai_move, -6, NULL}, {ai_move, -3, jorg_step_left}, {ai_move, -9, NULL}, {ai_move, 0, jorg_step_right}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -7, NULL}, {ai_move, 1, NULL}, {ai_move, -11, NULL}, {ai_move, -4, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 10, NULL}, {ai_move, 11, NULL}, {ai_move, 0, NULL}, {ai_move, 10, NULL}, {ai_move, 3, NULL}, {ai_move, 10, NULL}, {ai_move, 7, jorg_step_left}, {ai_move, 17, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, jorg_step_right} }; mmove_t jorg_move_pain3 = { FRAME_pain301, FRAME_pain325, jorg_frames_pain3, jorg_run }; mframe_t jorg_frames_pain2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t jorg_move_pain2 = { FRAME_pain201, FRAME_pain203, jorg_frames_pain2, jorg_run }; mframe_t jorg_frames_pain1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t jorg_move_pain1 = { FRAME_pain101, FRAME_pain103, jorg_frames_pain1, jorg_run }; mframe_t jorg_frames_death1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 10 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 20 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 30 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, /* 40 */ {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, MakronToss}, {ai_move, 0, BossExplode} /* 50 */ }; mmove_t jorg_move_death = { FRAME_death01, FRAME_death50, jorg_frames_death1, jorg_dead }; mframe_t jorg_frames_attack2[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, jorgBFG}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t jorg_move_attack2 = { FRAME_attak201, FRAME_attak213, jorg_frames_attack2, jorg_run }; mframe_t jorg_frames_start_attack1[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t jorg_move_start_attack1 = { FRAME_attak101, FRAME_attak108, jorg_frames_start_attack1, jorg_attack1 }; mframe_t jorg_frames_attack1[] = { {ai_charge, 0, jorg_firebullet}, {ai_charge, 0, jorg_firebullet}, {ai_charge, 0, jorg_firebullet}, {ai_charge, 0, jorg_firebullet}, {ai_charge, 0, jorg_firebullet}, {ai_charge, 0, jorg_firebullet} }; mmove_t jorg_move_attack1 = { FRAME_attak109, FRAME_attak114, jorg_frames_attack1, jorg_reattack1 }; mframe_t jorg_frames_end_attack1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t jorg_move_end_attack1 = { FRAME_attak115, FRAME_attak118, jorg_frames_end_attack1, jorg_run }; void jorg_reattack1(edict_t *self) { if (!self) { return; } if (visible(self, self->enemy)) { if (random() < 0.9) { self->monsterinfo.currentmove = &jorg_move_attack1; } else { self->s.sound = 0; self->monsterinfo.currentmove = &jorg_move_end_attack1; } } else { self->s.sound = 0; self->monsterinfo.currentmove = &jorg_move_end_attack1; } } void jorg_attack1(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &jorg_move_attack1; } void jorg_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage) { if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } self->s.sound = 0; if (level.time < self->pain_debounce_time) { return; } /* Lessen the chance of him going into his pain frames if he takes little damage */ if (damage <= 40) { if (random() <= 0.6) { return; } } /* If he's entering his attack1 or using attack1, lessen the chance of him going into pain */ if ((self->s.frame >= FRAME_attak101) && (self->s.frame <= FRAME_attak108)) { if (random() <= 0.005) { return; } } if ((self->s.frame >= FRAME_attak109) && (self->s.frame <= FRAME_attak114)) { if (random() <= 0.00005) { return; } } if ((self->s.frame >= FRAME_attak201) && (self->s.frame <= FRAME_attak208)) { if (random() <= 0.005) { return; } } self->pain_debounce_time = level.time + 3; if (skill->value == 3) { return; /* no pain anims in nightmare */ } if (damage <= 50) { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &jorg_move_pain1; } else if (damage <= 100) { gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &jorg_move_pain2; } else { if (random() <= 0.3) { gi.sound(self, CHAN_VOICE, sound_pain3, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &jorg_move_pain3; } } } void jorgBFG(edict_t *self) { vec3_t forward, right; vec3_t start; vec3_t dir; vec3_t vec; if (!self) { return; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_JORG_BFG_1], forward, right, start); VectorCopy(self->enemy->s.origin, vec); vec[2] += self->enemy->viewheight; VectorSubtract(vec, start, dir); VectorNormalize(dir); gi.sound(self, CHAN_VOICE, sound_attack2, 1, ATTN_NORM, 0); monster_fire_bfg(self, start, dir, 50, 300, 100, 200, MZ2_JORG_BFG_1); } void jorg_firebullet_right(edict_t *self) { vec3_t forward, right, target; vec3_t start; if (!self) { return; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_JORG_MACHINEGUN_R1], forward, right, start); VectorMA(self->enemy->s.origin, -0.2, self->enemy->velocity, target); target[2] += self->enemy->viewheight; VectorSubtract(target, start, forward); VectorNormalize(forward); monster_fire_bullet(self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_JORG_MACHINEGUN_R1); } void jorg_firebullet_left(edict_t *self) { vec3_t forward, right, target; vec3_t start; if (!self) { return; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_JORG_MACHINEGUN_L1], forward, right, start); VectorMA(self->enemy->s.origin, -0.2, self->enemy->velocity, target); target[2] += self->enemy->viewheight; VectorSubtract(target, start, forward); VectorNormalize(forward); monster_fire_bullet(self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_JORG_MACHINEGUN_L1); } void jorg_firebullet(edict_t *self) { if (!self) { return; } jorg_firebullet_left(self); jorg_firebullet_right(self); } void jorg_attack(edict_t *self) { if (!self) { return; } if (random() <= 0.75) { gi.sound(self, CHAN_VOICE, sound_attack1, 1, ATTN_NORM, 0); self->s.sound = gi.soundindex("boss3/w_loop.wav"); self->monsterinfo.currentmove = &jorg_move_start_attack1; } else { gi.sound(self, CHAN_VOICE, sound_attack2, 1, ATTN_NORM, 0); self->monsterinfo.currentmove = &jorg_move_attack2; } } void jorg_dead(edict_t *self /* unused */) { /* unused, but removal is PITA */ } void jorg_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage /* unused */, vec3_t point /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0); self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_NO; self->s.sound = 0; self->count = 0; self->monsterinfo.currentmove = &jorg_move_death; } qboolean Jorg_CheckAttack(edict_t *self) { vec3_t spot1, spot2; vec3_t temp; float chance; trace_t tr; int enemy_range; float enemy_yaw; if (!self) { return false; } if (self->enemy->health > 0) { /* see if any entities are in the way of the shot */ VectorCopy(self->s.origin, spot1); spot1[2] += self->viewheight; VectorCopy(self->enemy->s.origin, spot2); spot2[2] += self->enemy->viewheight; tr = gi.trace( spot1, NULL, NULL, spot2, self, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA); /* do we have a clear shot? */ if (tr.ent != self->enemy) { return false; } } enemy_range = range(self, self->enemy); VectorSubtract(self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw(temp); self->ideal_yaw = enemy_yaw; /* melee attack */ if (enemy_range == RANGE_MELEE) { if (self->monsterinfo.melee) { self->monsterinfo.attack_state = AS_MELEE; } else { self->monsterinfo.attack_state = AS_MISSILE; } return true; } /* missile attack */ if (!self->monsterinfo.attack) { return false; } if (level.time < self->monsterinfo.attack_finished) { return false; } if (enemy_range == RANGE_FAR) { return false; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { chance = 0.4; } else if (enemy_range == RANGE_MELEE) { chance = 0.8; } else if (enemy_range == RANGE_NEAR) { chance = 0.4; } else if (enemy_range == RANGE_MID) { chance = 0.2; } else { return false; } if (random() < chance) { self->monsterinfo.attack_state = AS_MISSILE; self->monsterinfo.attack_finished = level.time + 2 * random(); return true; } if (self->flags & FL_FLY) { if (random() < 0.3) { self->monsterinfo.attack_state = AS_SLIDING; } else { self->monsterinfo.attack_state = AS_STRAIGHT; } } return false; } void MakronPrecache(void); /* * QUAKED monster_jorg (1 .5 0) (-80 -80 0) (90 90 140) Ambush Trigger_Spawn Sight */ void SP_monster_jorg(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_pain1 = gi.soundindex("boss3/bs3pain1.wav"); sound_pain2 = gi.soundindex("boss3/bs3pain2.wav"); sound_pain3 = gi.soundindex("boss3/bs3pain3.wav"); sound_death = gi.soundindex("boss3/bs3deth1.wav"); sound_attack1 = gi.soundindex("boss3/bs3atck1.wav"); sound_attack2 = gi.soundindex("boss3/bs3atck2.wav"); sound_search1 = gi.soundindex("boss3/bs3srch1.wav"); sound_search2 = gi.soundindex("boss3/bs3srch2.wav"); sound_search3 = gi.soundindex("boss3/bs3srch3.wav"); sound_idle = gi.soundindex("boss3/bs3idle1.wav"); sound_step_left = gi.soundindex("boss3/step1.wav"); sound_step_right = gi.soundindex("boss3/step2.wav"); sound_firegun = gi.soundindex("boss3/xfire.wav"); sound_death_hit = gi.soundindex("boss3/d_hit.wav"); MakronPrecache(); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/boss3/rider/tris.md2"); self->s.modelindex2 = gi.modelindex("models/monsters/boss3/jorg/tris.md2"); VectorSet(self->mins, -80, -80, 0); VectorSet(self->maxs, 80, 80, 140); self->health = 3000; self->gib_health = -2000; self->mass = 1000; self->pain = jorg_pain; self->die = jorg_die; self->monsterinfo.stand = jorg_stand; self->monsterinfo.walk = jorg_walk; self->monsterinfo.run = jorg_run; self->monsterinfo.dodge = NULL; self->monsterinfo.attack = jorg_attack; self->monsterinfo.search = jorg_search; self->monsterinfo.melee = NULL; self->monsterinfo.sight = NULL; self->monsterinfo.checkattack = Jorg_CheckAttack; gi.linkentity(self); self->monsterinfo.currentmove = &jorg_move_stand; self->monsterinfo.scale = MODEL_SCALE; walkmonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/boss3/boss31.h0000644000175000017500000001323212615162034022323 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Animations for final boss stage 1. * * ======================================================================= */ #define FRAME_attak101 0 #define FRAME_attak102 1 #define FRAME_attak103 2 #define FRAME_attak104 3 #define FRAME_attak105 4 #define FRAME_attak106 5 #define FRAME_attak107 6 #define FRAME_attak108 7 #define FRAME_attak109 8 #define FRAME_attak110 9 #define FRAME_attak111 10 #define FRAME_attak112 11 #define FRAME_attak113 12 #define FRAME_attak114 13 #define FRAME_attak115 14 #define FRAME_attak116 15 #define FRAME_attak117 16 #define FRAME_attak118 17 #define FRAME_attak201 18 #define FRAME_attak202 19 #define FRAME_attak203 20 #define FRAME_attak204 21 #define FRAME_attak205 22 #define FRAME_attak206 23 #define FRAME_attak207 24 #define FRAME_attak208 25 #define FRAME_attak209 26 #define FRAME_attak210 27 #define FRAME_attak211 28 #define FRAME_attak212 29 #define FRAME_attak213 30 #define FRAME_death01 31 #define FRAME_death02 32 #define FRAME_death03 33 #define FRAME_death04 34 #define FRAME_death05 35 #define FRAME_death06 36 #define FRAME_death07 37 #define FRAME_death08 38 #define FRAME_death09 39 #define FRAME_death10 40 #define FRAME_death11 41 #define FRAME_death12 42 #define FRAME_death13 43 #define FRAME_death14 44 #define FRAME_death15 45 #define FRAME_death16 46 #define FRAME_death17 47 #define FRAME_death18 48 #define FRAME_death19 49 #define FRAME_death20 50 #define FRAME_death21 51 #define FRAME_death22 52 #define FRAME_death23 53 #define FRAME_death24 54 #define FRAME_death25 55 #define FRAME_death26 56 #define FRAME_death27 57 #define FRAME_death28 58 #define FRAME_death29 59 #define FRAME_death30 60 #define FRAME_death31 61 #define FRAME_death32 62 #define FRAME_death33 63 #define FRAME_death34 64 #define FRAME_death35 65 #define FRAME_death36 66 #define FRAME_death37 67 #define FRAME_death38 68 #define FRAME_death39 69 #define FRAME_death40 70 #define FRAME_death41 71 #define FRAME_death42 72 #define FRAME_death43 73 #define FRAME_death44 74 #define FRAME_death45 75 #define FRAME_death46 76 #define FRAME_death47 77 #define FRAME_death48 78 #define FRAME_death49 79 #define FRAME_death50 80 #define FRAME_pain101 81 #define FRAME_pain102 82 #define FRAME_pain103 83 #define FRAME_pain201 84 #define FRAME_pain202 85 #define FRAME_pain203 86 #define FRAME_pain301 87 #define FRAME_pain302 88 #define FRAME_pain303 89 #define FRAME_pain304 90 #define FRAME_pain305 91 #define FRAME_pain306 92 #define FRAME_pain307 93 #define FRAME_pain308 94 #define FRAME_pain309 95 #define FRAME_pain310 96 #define FRAME_pain311 97 #define FRAME_pain312 98 #define FRAME_pain313 99 #define FRAME_pain314 100 #define FRAME_pain315 101 #define FRAME_pain316 102 #define FRAME_pain317 103 #define FRAME_pain318 104 #define FRAME_pain319 105 #define FRAME_pain320 106 #define FRAME_pain321 107 #define FRAME_pain322 108 #define FRAME_pain323 109 #define FRAME_pain324 110 #define FRAME_pain325 111 #define FRAME_stand01 112 #define FRAME_stand02 113 #define FRAME_stand03 114 #define FRAME_stand04 115 #define FRAME_stand05 116 #define FRAME_stand06 117 #define FRAME_stand07 118 #define FRAME_stand08 119 #define FRAME_stand09 120 #define FRAME_stand10 121 #define FRAME_stand11 122 #define FRAME_stand12 123 #define FRAME_stand13 124 #define FRAME_stand14 125 #define FRAME_stand15 126 #define FRAME_stand16 127 #define FRAME_stand17 128 #define FRAME_stand18 129 #define FRAME_stand19 130 #define FRAME_stand20 131 #define FRAME_stand21 132 #define FRAME_stand22 133 #define FRAME_stand23 134 #define FRAME_stand24 135 #define FRAME_stand25 136 #define FRAME_stand26 137 #define FRAME_stand27 138 #define FRAME_stand28 139 #define FRAME_stand29 140 #define FRAME_stand30 141 #define FRAME_stand31 142 #define FRAME_stand32 143 #define FRAME_stand33 144 #define FRAME_stand34 145 #define FRAME_stand35 146 #define FRAME_stand36 147 #define FRAME_stand37 148 #define FRAME_stand38 149 #define FRAME_stand39 150 #define FRAME_stand40 151 #define FRAME_stand41 152 #define FRAME_stand42 153 #define FRAME_stand43 154 #define FRAME_stand44 155 #define FRAME_stand45 156 #define FRAME_stand46 157 #define FRAME_stand47 158 #define FRAME_stand48 159 #define FRAME_stand49 160 #define FRAME_stand50 161 #define FRAME_stand51 162 #define FRAME_walk01 163 #define FRAME_walk02 164 #define FRAME_walk03 165 #define FRAME_walk04 166 #define FRAME_walk05 167 #define FRAME_walk06 168 #define FRAME_walk07 169 #define FRAME_walk08 170 #define FRAME_walk09 171 #define FRAME_walk10 172 #define FRAME_walk11 173 #define FRAME_walk12 174 #define FRAME_walk13 175 #define FRAME_walk14 176 #define FRAME_walk15 177 #define FRAME_walk16 178 #define FRAME_walk17 179 #define FRAME_walk18 180 #define FRAME_walk19 181 #define FRAME_walk20 182 #define FRAME_walk21 183 #define FRAME_walk22 184 #define FRAME_walk23 185 #define FRAME_walk24 186 #define FRAME_walk25 187 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/parasite/0000755000175000017500000000000012615162034021616 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/parasite/parasite.h0000644000175000017500000000752312615162034023606 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Parasite animations. * * ======================================================================= */ #define FRAME_break01 0 #define FRAME_break02 1 #define FRAME_break03 2 #define FRAME_break04 3 #define FRAME_break05 4 #define FRAME_break06 5 #define FRAME_break07 6 #define FRAME_break08 7 #define FRAME_break09 8 #define FRAME_break10 9 #define FRAME_break11 10 #define FRAME_break12 11 #define FRAME_break13 12 #define FRAME_break14 13 #define FRAME_break15 14 #define FRAME_break16 15 #define FRAME_break17 16 #define FRAME_break18 17 #define FRAME_break19 18 #define FRAME_break20 19 #define FRAME_break21 20 #define FRAME_break22 21 #define FRAME_break23 22 #define FRAME_break24 23 #define FRAME_break25 24 #define FRAME_break26 25 #define FRAME_break27 26 #define FRAME_break28 27 #define FRAME_break29 28 #define FRAME_break30 29 #define FRAME_break31 30 #define FRAME_break32 31 #define FRAME_death101 32 #define FRAME_death102 33 #define FRAME_death103 34 #define FRAME_death104 35 #define FRAME_death105 36 #define FRAME_death106 37 #define FRAME_death107 38 #define FRAME_drain01 39 #define FRAME_drain02 40 #define FRAME_drain03 41 #define FRAME_drain04 42 #define FRAME_drain05 43 #define FRAME_drain06 44 #define FRAME_drain07 45 #define FRAME_drain08 46 #define FRAME_drain09 47 #define FRAME_drain10 48 #define FRAME_drain11 49 #define FRAME_drain12 50 #define FRAME_drain13 51 #define FRAME_drain14 52 #define FRAME_drain15 53 #define FRAME_drain16 54 #define FRAME_drain17 55 #define FRAME_drain18 56 #define FRAME_pain101 57 #define FRAME_pain102 58 #define FRAME_pain103 59 #define FRAME_pain104 60 #define FRAME_pain105 61 #define FRAME_pain106 62 #define FRAME_pain107 63 #define FRAME_pain108 64 #define FRAME_pain109 65 #define FRAME_pain110 66 #define FRAME_pain111 67 #define FRAME_run01 68 #define FRAME_run02 69 #define FRAME_run03 70 #define FRAME_run04 71 #define FRAME_run05 72 #define FRAME_run06 73 #define FRAME_run07 74 #define FRAME_run08 75 #define FRAME_run09 76 #define FRAME_run10 77 #define FRAME_run11 78 #define FRAME_run12 79 #define FRAME_run13 80 #define FRAME_run14 81 #define FRAME_run15 82 #define FRAME_stand01 83 #define FRAME_stand02 84 #define FRAME_stand03 85 #define FRAME_stand04 86 #define FRAME_stand05 87 #define FRAME_stand06 88 #define FRAME_stand07 89 #define FRAME_stand08 90 #define FRAME_stand09 91 #define FRAME_stand10 92 #define FRAME_stand11 93 #define FRAME_stand12 94 #define FRAME_stand13 95 #define FRAME_stand14 96 #define FRAME_stand15 97 #define FRAME_stand16 98 #define FRAME_stand17 99 #define FRAME_stand18 100 #define FRAME_stand19 101 #define FRAME_stand20 102 #define FRAME_stand21 103 #define FRAME_stand22 104 #define FRAME_stand23 105 #define FRAME_stand24 106 #define FRAME_stand25 107 #define FRAME_stand26 108 #define FRAME_stand27 109 #define FRAME_stand28 110 #define FRAME_stand29 111 #define FRAME_stand30 112 #define FRAME_stand31 113 #define FRAME_stand32 114 #define FRAME_stand33 115 #define FRAME_stand34 116 #define FRAME_stand35 117 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/parasite/parasite.c0000644000175000017500000003473312615162034023604 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Parasite. * * ======================================================================= */ #include "../../header/local.h" #include "parasite.h" static int sound_pain1; static int sound_pain2; static int sound_die; static int sound_launch; static int sound_impact; static int sound_suck; static int sound_reelin; static int sound_sight; static int sound_tap; static int sound_scratch; static int sound_search; void parasite_stand(edict_t *self); void parasite_start_run(edict_t *self); void parasite_run(edict_t *self); void parasite_walk(edict_t *self); void parasite_start_walk(edict_t *self); void parasite_end_fidget(edict_t *self); void parasite_do_fidget(edict_t *self); void parasite_refidget(edict_t *self); void parasite_launch(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_launch, 1, ATTN_NORM, 0); } void parasite_reel_in(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_reelin, 1, ATTN_NORM, 0); } void parasite_sight(edict_t *self, edict_t *other /* unused */) { if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_sight, 1, ATTN_NORM, 0); } void parasite_tap(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_tap, 1, ATTN_IDLE, 0); } void parasite_scratch(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_scratch, 1, ATTN_IDLE, 0); } void parasite_search(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_search, 1, ATTN_IDLE, 0); } mframe_t parasite_frames_start_fidget[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t parasite_move_start_fidget = { FRAME_stand18, FRAME_stand21, parasite_frames_start_fidget, parasite_do_fidget }; mframe_t parasite_frames_fidget[] = { {ai_stand, 0, parasite_scratch}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, parasite_scratch}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t parasite_move_fidget = { FRAME_stand22, FRAME_stand27, parasite_frames_fidget, parasite_refidget }; mframe_t parasite_frames_end_fidget[] = { {ai_stand, 0, parasite_scratch}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t parasite_move_end_fidget = { FRAME_stand28, FRAME_stand35, parasite_frames_end_fidget, parasite_stand }; void parasite_end_fidget(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = ¶site_move_end_fidget; } void parasite_do_fidget(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = ¶site_move_fidget; } void parasite_refidget(edict_t *self) { if (!self) { return; } if (random() <= 0.8) { self->monsterinfo.currentmove = ¶site_move_fidget; } else { self->monsterinfo.currentmove = ¶site_move_end_fidget; } } void parasite_idle(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = ¶site_move_start_fidget; } mframe_t parasite_frames_stand[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, parasite_tap}, {ai_stand, 0, NULL}, {ai_stand, 0, parasite_tap}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, parasite_tap}, {ai_stand, 0, NULL}, {ai_stand, 0, parasite_tap}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, parasite_tap}, {ai_stand, 0, NULL}, {ai_stand, 0, parasite_tap} }; mmove_t parasite_move_stand = { FRAME_stand01, FRAME_stand17, parasite_frames_stand, parasite_stand }; void parasite_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = ¶site_move_stand; } mframe_t parasite_frames_run[] = { {ai_run, 30, NULL}, {ai_run, 30, NULL}, {ai_run, 22, NULL}, {ai_run, 19, NULL}, {ai_run, 24, NULL}, {ai_run, 28, NULL}, {ai_run, 25, NULL} }; mmove_t parasite_move_run = { FRAME_run03, FRAME_run09, parasite_frames_run, NULL }; mframe_t parasite_frames_start_run[] = { {ai_run, 0, NULL}, {ai_run, 30, NULL}, }; mmove_t parasite_move_start_run = { FRAME_run01, FRAME_run02, parasite_frames_start_run, parasite_run }; mframe_t parasite_frames_stop_run[] = { {ai_run, 20, NULL}, {ai_run, 20, NULL}, {ai_run, 12, NULL}, {ai_run, 10, NULL}, {ai_run, 0, NULL}, {ai_run, 0, NULL} }; mmove_t parasite_move_stop_run = { FRAME_run10, FRAME_run15, parasite_frames_stop_run, NULL }; void parasite_start_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = ¶site_move_stand; } else { self->monsterinfo.currentmove = ¶site_move_start_run; } } void parasite_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = ¶site_move_stand; } else { self->monsterinfo.currentmove = ¶site_move_run; } } mframe_t parasite_frames_walk[] = { {ai_walk, 30, NULL}, {ai_walk, 30, NULL}, {ai_walk, 22, NULL}, {ai_walk, 19, NULL}, {ai_walk, 24, NULL}, {ai_walk, 28, NULL}, {ai_walk, 25, NULL} }; mmove_t parasite_move_walk = { FRAME_run03, FRAME_run09, parasite_frames_walk, parasite_walk }; mframe_t parasite_frames_start_walk[] = { {ai_walk, 0, NULL}, {ai_walk, 30, parasite_walk} }; mmove_t parasite_move_start_walk = { FRAME_run01, FRAME_run02, parasite_frames_start_walk, NULL }; mframe_t parasite_frames_stop_walk[] = { {ai_walk, 20, NULL}, {ai_walk, 20, NULL}, {ai_walk, 12, NULL}, {ai_walk, 10, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL} }; mmove_t parasite_move_stop_walk = { FRAME_run10, FRAME_run15, parasite_frames_stop_walk, NULL }; void parasite_start_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = ¶site_move_start_walk; } void parasite_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = ¶site_move_walk; } mframe_t parasite_frames_pain1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 6, NULL}, {ai_move, 16, NULL}, {ai_move, -6, NULL}, {ai_move, -7, NULL}, {ai_move, 0, NULL} }; mmove_t parasite_move_pain1 = { FRAME_pain101, FRAME_pain111, parasite_frames_pain1, parasite_start_run }; void parasite_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage /* unused */) { if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; if (skill->value == 3) { return; /* no pain anims in nightmare */ } if (random() < 0.5) { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); } self->monsterinfo.currentmove = ¶site_move_pain1; } qboolean parasite_drain_attack_ok(vec3_t start, vec3_t end) { vec3_t dir, angles; /* check for max distance */ VectorSubtract(start, end, dir); if (VectorLength(dir) > 256) { return false; } /* check for min/max pitch */ vectoangles(dir, angles); if (angles[0] < -180) { angles[0] += 360; } if (fabs(angles[0]) > 30) { return false; } return true; } void parasite_drain_attack(edict_t *self) { vec3_t offset, start, f, r, end, dir; trace_t tr; int damage; if (!self) { return; } AngleVectors(self->s.angles, f, r, NULL); VectorSet(offset, 24, 0, 6); G_ProjectSource(self->s.origin, offset, f, r, start); VectorCopy(self->enemy->s.origin, end); if (!parasite_drain_attack_ok(start, end)) { end[2] = self->enemy->s.origin[2] + self->enemy->maxs[2] - 8; if (!parasite_drain_attack_ok(start, end)) { end[2] = self->enemy->s.origin[2] + self->enemy->mins[2] + 8; if (!parasite_drain_attack_ok(start, end)) { return; } } } VectorCopy(self->enemy->s.origin, end); tr = gi.trace(start, NULL, NULL, end, self, MASK_SHOT); if (tr.ent != self->enemy) { return; } if (self->s.frame == FRAME_drain03) { damage = 5; gi.sound(self->enemy, CHAN_AUTO, sound_impact, 1, ATTN_NORM, 0); } else { if (self->s.frame == FRAME_drain04) { gi.sound(self, CHAN_WEAPON, sound_suck, 1, ATTN_NORM, 0); } damage = 2; } gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_PARASITE_ATTACK); gi.WriteShort(self - g_edicts); gi.WritePosition(start); gi.WritePosition(end); gi.multicast(self->s.origin, MULTICAST_PVS); VectorSubtract(start, end, dir); T_Damage(self->enemy, self, self, dir, self->enemy->s.origin, vec3_origin, damage, 0, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN); } mframe_t parasite_frames_drain[] = { {ai_charge, 0, parasite_launch}, {ai_charge, 0, NULL}, {ai_charge, 15, parasite_drain_attack}, /* Target hits */ {ai_charge, 0, parasite_drain_attack}, /* drain */ {ai_charge, 0, parasite_drain_attack}, /* drain */ {ai_charge, 0, parasite_drain_attack}, /* drain */ {ai_charge, 0, parasite_drain_attack}, /* drain */ {ai_charge, -2, parasite_drain_attack}, /* drain */ {ai_charge, -2, parasite_drain_attack}, /* drain */ {ai_charge, -3, parasite_drain_attack}, /* drain */ {ai_charge, -2, parasite_drain_attack}, /* drain */ {ai_charge, 0, parasite_drain_attack}, /* drain */ {ai_charge, -1, parasite_drain_attack}, /* drain */ {ai_charge, 0, parasite_reel_in}, /* let go */ {ai_charge, -2, NULL}, {ai_charge, -2, NULL}, {ai_charge, -3, NULL}, {ai_charge, 0, NULL} }; mmove_t parasite_move_drain = { FRAME_drain01, FRAME_drain18, parasite_frames_drain, parasite_start_run }; mframe_t parasite_frames_break[] = { {ai_charge, 0, NULL}, {ai_charge, -3, NULL}, {ai_charge, 1, NULL}, {ai_charge, 2, NULL}, {ai_charge, -3, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 3, NULL}, {ai_charge, 0, NULL}, {ai_charge, -18, NULL}, {ai_charge, 3, NULL}, {ai_charge, 9, NULL}, {ai_charge, 6, NULL}, {ai_charge, 0, NULL}, {ai_charge, -18, NULL}, {ai_charge, 0, NULL}, {ai_charge, 8, NULL}, {ai_charge, 9, NULL}, {ai_charge, 0, NULL}, {ai_charge, -18, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, /* airborne */ {ai_charge, 0, NULL}, /* airborne */ {ai_charge, 0, NULL}, /* slides */ {ai_charge, 0, NULL}, /* slides */ {ai_charge, 0, NULL}, /* slides */ {ai_charge, 0, NULL}, /* slides */ {ai_charge, 4, NULL}, {ai_charge, 11, NULL}, {ai_charge, -2, NULL}, {ai_charge, -5, NULL}, {ai_charge, 1, NULL} }; mmove_t parasite_move_break = { FRAME_break01, FRAME_break32, parasite_frames_break, parasite_start_run }; void parasite_attack(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = ¶site_move_drain; } void parasite_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, -8); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } mframe_t parasite_frames_death[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t parasite_move_death = { FRAME_death101, FRAME_death107, parasite_frames_death, parasite_dead }; void parasite_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage, vec3_t point /* unused */) { int n; /* check for gib */ if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 2; n++) { ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } /* regular death */ gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0); self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; self->monsterinfo.currentmove = ¶site_move_death; } /* * QUAKED monster_parasite (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight */ void SP_monster_parasite(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_pain1 = gi.soundindex("parasite/parpain1.wav"); sound_pain2 = gi.soundindex("parasite/parpain2.wav"); sound_die = gi.soundindex("parasite/pardeth1.wav"); sound_launch = gi.soundindex("parasite/paratck1.wav"); sound_impact = gi.soundindex("parasite/paratck2.wav"); sound_suck = gi.soundindex("parasite/paratck3.wav"); sound_reelin = gi.soundindex("parasite/paratck4.wav"); sound_sight = gi.soundindex("parasite/parsght1.wav"); sound_tap = gi.soundindex("parasite/paridle1.wav"); sound_scratch = gi.soundindex("parasite/paridle2.wav"); sound_search = gi.soundindex("parasite/parsrch1.wav"); self->s.modelindex = gi.modelindex("models/monsters/parasite/tris.md2"); VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, 24); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->health = 175; self->gib_health = -50; self->mass = 250; self->pain = parasite_pain; self->die = parasite_die; self->monsterinfo.stand = parasite_stand; self->monsterinfo.walk = parasite_start_walk; self->monsterinfo.run = parasite_start_run; self->monsterinfo.attack = parasite_attack; self->monsterinfo.sight = parasite_sight; self->monsterinfo.idle = parasite_idle; gi.linkentity(self); self->monsterinfo.currentmove = ¶site_move_stand; self->monsterinfo.scale = MODEL_SCALE; walkmonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/gladiator/0000755000175000017500000000000012615162034021754 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/gladiator/gladiator.h0000644000175000017500000000611512615162034024076 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Gladiator animations. * * ======================================================================= */ #define FRAME_stand1 0 #define FRAME_stand2 1 #define FRAME_stand3 2 #define FRAME_stand4 3 #define FRAME_stand5 4 #define FRAME_stand6 5 #define FRAME_stand7 6 #define FRAME_walk1 7 #define FRAME_walk2 8 #define FRAME_walk3 9 #define FRAME_walk4 10 #define FRAME_walk5 11 #define FRAME_walk6 12 #define FRAME_walk7 13 #define FRAME_walk8 14 #define FRAME_walk9 15 #define FRAME_walk10 16 #define FRAME_walk11 17 #define FRAME_walk12 18 #define FRAME_walk13 19 #define FRAME_walk14 20 #define FRAME_walk15 21 #define FRAME_walk16 22 #define FRAME_run1 23 #define FRAME_run2 24 #define FRAME_run3 25 #define FRAME_run4 26 #define FRAME_run5 27 #define FRAME_run6 28 #define FRAME_melee1 29 #define FRAME_melee2 30 #define FRAME_melee3 31 #define FRAME_melee4 32 #define FRAME_melee5 33 #define FRAME_melee6 34 #define FRAME_melee7 35 #define FRAME_melee8 36 #define FRAME_melee9 37 #define FRAME_melee10 38 #define FRAME_melee11 39 #define FRAME_melee12 40 #define FRAME_melee13 41 #define FRAME_melee14 42 #define FRAME_melee15 43 #define FRAME_melee16 44 #define FRAME_melee17 45 #define FRAME_attack1 46 #define FRAME_attack2 47 #define FRAME_attack3 48 #define FRAME_attack4 49 #define FRAME_attack5 50 #define FRAME_attack6 51 #define FRAME_attack7 52 #define FRAME_attack8 53 #define FRAME_attack9 54 #define FRAME_pain1 55 #define FRAME_pain2 56 #define FRAME_pain3 57 #define FRAME_pain4 58 #define FRAME_pain5 59 #define FRAME_pain6 60 #define FRAME_death1 61 #define FRAME_death2 62 #define FRAME_death3 63 #define FRAME_death4 64 #define FRAME_death5 65 #define FRAME_death6 66 #define FRAME_death7 67 #define FRAME_death8 68 #define FRAME_death9 69 #define FRAME_death10 70 #define FRAME_death11 71 #define FRAME_death12 72 #define FRAME_death13 73 #define FRAME_death14 74 #define FRAME_death15 75 #define FRAME_death16 76 #define FRAME_death17 77 #define FRAME_death18 78 #define FRAME_death19 79 #define FRAME_death20 80 #define FRAME_death21 81 #define FRAME_death22 82 #define FRAME_painup1 83 #define FRAME_painup2 84 #define FRAME_painup3 85 #define FRAME_painup4 86 #define FRAME_painup5 87 #define FRAME_painup6 88 #define FRAME_painup7 89 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/gladiator/gladiator.c0000644000175000017500000002501112615162034024065 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Gladiator. * * ======================================================================= */ #include "../../header/local.h" #include "gladiator.h" static int sound_pain1; static int sound_pain2; static int sound_die; static int sound_gun; static int sound_cleaver_swing; static int sound_cleaver_hit; static int sound_cleaver_miss; static int sound_idle; static int sound_search; static int sound_sight; void gladiator_idle(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0); } void gladiator_sight(edict_t *self, edict_t *other /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); } void gladiator_search(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0); } void gladiator_cleaver_swing(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_WEAPON, sound_cleaver_swing, 1, ATTN_NORM, 0); } mframe_t gladiator_frames_stand[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t gladiator_move_stand = { FRAME_stand1, FRAME_stand7, gladiator_frames_stand, NULL }; void gladiator_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &gladiator_move_stand; } mframe_t gladiator_frames_walk[] = { {ai_walk, 15, NULL}, {ai_walk, 7, NULL}, {ai_walk, 6, NULL}, {ai_walk, 5, NULL}, {ai_walk, 2, NULL}, {ai_walk, 0, NULL}, {ai_walk, 2, NULL}, {ai_walk, 8, NULL}, {ai_walk, 12, NULL}, {ai_walk, 8, NULL}, {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, 2, NULL}, {ai_walk, 2, NULL}, {ai_walk, 1, NULL}, {ai_walk, 8, NULL} }; mmove_t gladiator_move_walk = { FRAME_walk1, FRAME_walk16, gladiator_frames_walk, NULL }; void gladiator_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &gladiator_move_walk; } mframe_t gladiator_frames_run[] = { {ai_run, 23, NULL}, {ai_run, 14, NULL}, {ai_run, 14, NULL}, {ai_run, 21, NULL}, {ai_run, 12, NULL}, {ai_run, 13, NULL} }; mmove_t gladiator_move_run = { FRAME_run1, FRAME_run6, gladiator_frames_run, NULL }; void gladiator_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &gladiator_move_stand; } else { self->monsterinfo.currentmove = &gladiator_move_run; } } void GaldiatorMelee(edict_t *self) { vec3_t aim; if (!self) { return; } if (!self) { return; } VectorSet(aim, MELEE_DISTANCE, self->mins[0], -4); if (fire_hit(self, aim, (20 + (randk() % 5)), 300)) { gi.sound(self, CHAN_AUTO, sound_cleaver_hit, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_AUTO, sound_cleaver_miss, 1, ATTN_NORM, 0); } } mframe_t gladiator_frames_attack_melee[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, gladiator_cleaver_swing}, {ai_charge, 0, NULL}, {ai_charge, 0, GaldiatorMelee}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, gladiator_cleaver_swing}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, GaldiatorMelee}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t gladiator_move_attack_melee = { FRAME_melee1, FRAME_melee17, gladiator_frames_attack_melee, gladiator_run }; void gladiator_melee(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &gladiator_move_attack_melee; } void GladiatorGun(edict_t *self) { vec3_t start; vec3_t dir; vec3_t forward, right; if (!self) { return; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_GLADIATOR_RAILGUN_1], forward, right, start); /* calc direction to where we targted */ VectorSubtract(self->pos1, start, dir); VectorNormalize(dir); monster_fire_railgun(self, start, dir, 50, 100, MZ2_GLADIATOR_RAILGUN_1); } mframe_t gladiator_frames_attack_gun[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, GladiatorGun}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL} }; mmove_t gladiator_move_attack_gun = { FRAME_attack1, FRAME_attack9, gladiator_frames_attack_gun, gladiator_run }; void gladiator_attack(edict_t *self) { float range; vec3_t v; if (!self) { return; } /* a small safe zone */ VectorSubtract(self->s.origin, self->enemy->s.origin, v); range = VectorLength(v); if (range <= (MELEE_DISTANCE + 32)) { return; } /* charge up the railgun */ gi.sound(self, CHAN_WEAPON, sound_gun, 1, ATTN_NORM, 0); VectorCopy(self->enemy->s.origin, self->pos1); /* save for aiming the shot */ self->pos1[2] += self->enemy->viewheight; self->monsterinfo.currentmove = &gladiator_move_attack_gun; } mframe_t gladiator_frames_pain[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t gladiator_move_pain = { FRAME_pain1, FRAME_pain6, gladiator_frames_pain, gladiator_run }; mframe_t gladiator_frames_pain_air[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t gladiator_move_pain_air = { FRAME_painup1, FRAME_painup7, gladiator_frames_pain_air, gladiator_run }; void gladiator_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage) { if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { if ((self->velocity[2] > 100) && (self->monsterinfo.currentmove == &gladiator_move_pain)) { self->monsterinfo.currentmove = &gladiator_move_pain_air; } return; } self->pain_debounce_time = level.time + 3; if (random() < 0.5) { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); } if (skill->value == 3) { return; /* no pain anims in nightmare */ } if (self->velocity[2] > 100) { self->monsterinfo.currentmove = &gladiator_move_pain_air; } else { self->monsterinfo.currentmove = &gladiator_move_pain; } } void gladiator_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -16, -16, -24); VectorSet(self->maxs, 16, 16, -8); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } mframe_t gladiator_frames_death[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t gladiator_move_death = { FRAME_death1, FRAME_death22, gladiator_frames_death, gladiator_dead }; void gladiator_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage /*unused */, vec3_t point) { int n; if (!self) { return; } /* check for gib */ if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex( "misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 2; n++) { ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } /* regular death */ gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0); self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; self->monsterinfo.currentmove = &gladiator_move_death; } /* * QUAKED monster_gladiator (1 .5 0) (-32 -32 -24) (32 32 64) Ambush Trigger_Spawn Sight */ void SP_monster_gladiator(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_pain1 = gi.soundindex("gladiator/pain.wav"); sound_pain2 = gi.soundindex("gladiator/gldpain2.wav"); sound_die = gi.soundindex("gladiator/glddeth2.wav"); sound_gun = gi.soundindex("gladiator/railgun.wav"); sound_cleaver_swing = gi.soundindex("gladiator/melee1.wav"); sound_cleaver_hit = gi.soundindex("gladiator/melee2.wav"); sound_cleaver_miss = gi.soundindex("gladiator/melee3.wav"); sound_idle = gi.soundindex("gladiator/gldidle1.wav"); sound_search = gi.soundindex("gladiator/gldsrch1.wav"); sound_sight = gi.soundindex("gladiator/sight.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/gladiatr/tris.md2"); VectorSet(self->mins, -32, -32, -24); VectorSet(self->maxs, 32, 32, 64); self->health = 400; self->gib_health = -175; self->mass = 400; self->pain = gladiator_pain; self->die = gladiator_die; self->monsterinfo.stand = gladiator_stand; self->monsterinfo.walk = gladiator_walk; self->monsterinfo.run = gladiator_run; self->monsterinfo.dodge = NULL; self->monsterinfo.attack = gladiator_attack; self->monsterinfo.melee = gladiator_melee; self->monsterinfo.sight = gladiator_sight; self->monsterinfo.idle = gladiator_idle; self->monsterinfo.search = gladiator_search; gi.linkentity(self); self->monsterinfo.currentmove = &gladiator_move_stand; self->monsterinfo.scale = MODEL_SCALE; walkmonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/chick/0000755000175000017500000000000012615162034021067 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/chick/chick.c0000644000175000017500000004167512615162034022331 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Iron Maiden. * * ======================================================================= */ #include "../../header/local.h" #include "chick.h" qboolean visible(edict_t *self, edict_t *other); void chick_stand(edict_t *self); void chick_run(edict_t *self); void chick_reslash(edict_t *self); void chick_rerocket(edict_t *self); void chick_attack1(edict_t *self); static int sound_missile_prelaunch; static int sound_missile_launch; static int sound_melee_swing; static int sound_melee_hit; static int sound_missile_reload; static int sound_death1; static int sound_death2; static int sound_fall_down; static int sound_idle1; static int sound_idle2; static int sound_pain1; static int sound_pain2; static int sound_pain3; static int sound_sight; static int sound_search; void ChickMoan(edict_t *self) { if (!self) { return; } if (random() < 0.5) { gi.sound(self, CHAN_VOICE, sound_idle1, 1, ATTN_IDLE, 0); } else { gi.sound(self, CHAN_VOICE, sound_idle2, 1, ATTN_IDLE, 0); } } mframe_t chick_frames_fidget[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, ChickMoan}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t chick_move_fidget = { FRAME_stand201, FRAME_stand230, chick_frames_fidget, chick_stand }; void chick_fidget(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { return; } if (random() <= 0.3) { self->monsterinfo.currentmove = &chick_move_fidget; } } mframe_t chick_frames_stand[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, chick_fidget}, }; mmove_t chick_move_stand = { FRAME_stand101, FRAME_stand130, chick_frames_stand, NULL }; void chick_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &chick_move_stand; } mframe_t chick_frames_start_run[] = { {ai_run, 1, NULL}, {ai_run, 0, NULL}, {ai_run, 0, NULL}, {ai_run, -1, NULL}, {ai_run, -1, NULL}, {ai_run, 0, NULL}, {ai_run, 1, NULL}, {ai_run, 3, NULL}, {ai_run, 6, NULL}, {ai_run, 3, NULL} }; mmove_t chick_move_start_run = { FRAME_walk01, FRAME_walk10, chick_frames_start_run, chick_run }; mframe_t chick_frames_run[] = { {ai_run, 6, NULL}, {ai_run, 8, NULL}, {ai_run, 13, NULL}, {ai_run, 5, NULL}, {ai_run, 7, NULL}, {ai_run, 4, NULL}, {ai_run, 11, NULL}, {ai_run, 5, NULL}, {ai_run, 9, NULL}, {ai_run, 7, NULL} }; mmove_t chick_move_run = { FRAME_walk11, FRAME_walk20, chick_frames_run, NULL }; mframe_t chick_frames_walk[] = { {ai_walk, 6, NULL}, {ai_walk, 8, NULL}, {ai_walk, 13, NULL}, {ai_walk, 5, NULL}, {ai_walk, 7, NULL}, {ai_walk, 4, NULL}, {ai_walk, 11, NULL}, {ai_walk, 5, NULL}, {ai_walk, 9, NULL}, {ai_walk, 7, NULL} }; mmove_t chick_move_walk = { FRAME_walk11, FRAME_walk20, chick_frames_walk, NULL }; void chick_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &chick_move_walk; } void chick_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &chick_move_stand; return; } if ((self->monsterinfo.currentmove == &chick_move_walk) || (self->monsterinfo.currentmove == &chick_move_start_run)) { self->monsterinfo.currentmove = &chick_move_run; } else { self->monsterinfo.currentmove = &chick_move_start_run; } } mframe_t chick_frames_pain1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t chick_move_pain1 = { FRAME_pain101, FRAME_pain105, chick_frames_pain1, chick_run }; mframe_t chick_frames_pain2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t chick_move_pain2 = { FRAME_pain201, FRAME_pain205, chick_frames_pain2, chick_run }; mframe_t chick_frames_pain3[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -6, NULL}, {ai_move, 3, NULL}, {ai_move, 11, NULL}, {ai_move, 3, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 4, NULL}, {ai_move, 1, NULL}, {ai_move, 0, NULL}, {ai_move, -3, NULL}, {ai_move, -4, NULL}, {ai_move, 5, NULL}, {ai_move, 7, NULL}, {ai_move, -2, NULL}, {ai_move, 3, NULL}, {ai_move, -5, NULL}, {ai_move, -2, NULL}, {ai_move, -8, NULL}, {ai_move, 2, NULL} }; mmove_t chick_move_pain3 = { FRAME_pain301, FRAME_pain321, chick_frames_pain3, chick_run }; void chick_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage) { float r; if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; r = random(); if (r < 0.33) { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0); } else if (r < 0.66) { gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_VOICE, sound_pain3, 1, ATTN_NORM, 0); } if (skill->value == 3) { return; /* no pain anims in nightmare */ } if (damage <= 10) { self->monsterinfo.currentmove = &chick_move_pain1; } else if (damage <= 25) { self->monsterinfo.currentmove = &chick_move_pain2; } else { self->monsterinfo.currentmove = &chick_move_pain3; } } void chick_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -16, -16, 0); VectorSet(self->maxs, 16, 16, 16); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } mframe_t chick_frames_death2[] = { {ai_move, -6, NULL}, {ai_move, 0, NULL}, {ai_move, -1, NULL}, {ai_move, -5, NULL}, {ai_move, 0, NULL}, {ai_move, -1, NULL}, {ai_move, -2, NULL}, {ai_move, 1, NULL}, {ai_move, 10, NULL}, {ai_move, 2, NULL}, {ai_move, 3, NULL}, {ai_move, 1, NULL}, {ai_move, 2, NULL}, {ai_move, 0, NULL}, {ai_move, 3, NULL}, {ai_move, 3, NULL}, {ai_move, 1, NULL}, {ai_move, -3, NULL}, {ai_move, -5, NULL}, {ai_move, 4, NULL}, {ai_move, 15, NULL}, {ai_move, 14, NULL}, {ai_move, 1, NULL} }; mmove_t chick_move_death2 = { FRAME_death201, FRAME_death223, chick_frames_death2, chick_dead }; mframe_t chick_frames_death1[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -7, NULL}, {ai_move, 4, NULL}, {ai_move, 11, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t chick_move_death1 = { FRAME_death101, FRAME_death112, chick_frames_death1, chick_dead }; void chick_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage, vec3_t point /*unused */) { int n; if (!self) { return; } /* check for gib */ if (self->health <= self->gib_health) { gi.sound(self, CHAN_VOICE, gi.soundindex( "misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 2; n++) { ThrowGib(self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC); } for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); self->deadflag = DEAD_DEAD; return; } if (self->deadflag == DEAD_DEAD) { return; } /* regular death */ self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_YES; n = randk() % 2; if (n == 0) { self->monsterinfo.currentmove = &chick_move_death1; gi.sound(self, CHAN_VOICE, sound_death1, 1, ATTN_NORM, 0); } else { self->monsterinfo.currentmove = &chick_move_death2; gi.sound(self, CHAN_VOICE, sound_death2, 1, ATTN_NORM, 0); } } void chick_duck_down(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_DUCKED) { return; } self->monsterinfo.aiflags |= AI_DUCKED; self->maxs[2] -= 32; self->takedamage = DAMAGE_YES; self->monsterinfo.pausetime = level.time + 1; gi.linkentity(self); } void chick_duck_hold(edict_t *self) { if (!self) { return; } if (level.time >= self->monsterinfo.pausetime) { self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; } else { self->monsterinfo.aiflags |= AI_HOLD_FRAME; } } void chick_duck_up(edict_t *self) { if (!self) { return; } self->monsterinfo.aiflags &= ~AI_DUCKED; self->maxs[2] += 32; self->takedamage = DAMAGE_AIM; gi.linkentity(self); } mframe_t chick_frames_duck[] = { {ai_move, 0, chick_duck_down}, {ai_move, 1, NULL}, {ai_move, 4, chick_duck_hold}, {ai_move, -4, NULL}, {ai_move, -5, chick_duck_up}, {ai_move, 3, NULL}, {ai_move, 1, NULL} }; mmove_t chick_move_duck = { FRAME_duck01, FRAME_duck07, chick_frames_duck, chick_run }; void chick_dodge(edict_t *self, edict_t *attacker, float eta /* unused */) { if (!self || !attacker) { return; } if (random() > 0.25) { return; } if (!self->enemy) { self->enemy = attacker; } self->monsterinfo.currentmove = &chick_move_duck; } void ChickSlash(edict_t *self) { vec3_t aim; if (!self) { return; } VectorSet(aim, MELEE_DISTANCE, self->mins[0], 10); gi.sound(self, CHAN_WEAPON, sound_melee_swing, 1, ATTN_NORM, 0); fire_hit(self, aim, (10 + (randk() % 6)), 100); } void ChickRocket(edict_t *self) { if (!self) { return; } vec3_t forward, right; vec3_t start; vec3_t dir; vec3_t vec; AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_CHICK_ROCKET_1], forward, right, start); VectorCopy(self->enemy->s.origin, vec); vec[2] += self->enemy->viewheight; VectorSubtract(vec, start, dir); VectorNormalize(dir); monster_fire_rocket(self, start, dir, 50, 500, MZ2_CHICK_ROCKET_1); } void Chick_PreAttack1(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_missile_prelaunch, 1, ATTN_NORM, 0); } void ChickReload(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_missile_reload, 1, ATTN_NORM, 0); } mframe_t chick_frames_start_attack1[] = { {ai_charge, 0, Chick_PreAttack1}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 4, NULL}, {ai_charge, 0, NULL}, {ai_charge, -3, NULL}, {ai_charge, 3, NULL}, {ai_charge, 5, NULL}, {ai_charge, 7, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, chick_attack1} }; mmove_t chick_move_start_attack1 = { FRAME_attak101, FRAME_attak113, chick_frames_start_attack1, NULL}; mframe_t chick_frames_attack1[] = { {ai_charge, 19, ChickRocket}, {ai_charge, -6, NULL}, {ai_charge, -5, NULL}, {ai_charge, -2, NULL}, {ai_charge, -7, NULL}, {ai_charge, 0, NULL}, {ai_charge, 1, NULL}, {ai_charge, 10, ChickReload}, {ai_charge, 4, NULL}, {ai_charge, 5, NULL}, {ai_charge, 6, NULL}, {ai_charge, 6, NULL}, {ai_charge, 4, NULL}, {ai_charge, 3, chick_rerocket} }; mmove_t chick_move_attack1 = { FRAME_attak114, FRAME_attak127, chick_frames_attack1, NULL }; mframe_t chick_frames_end_attack1[] = { {ai_charge, -3, NULL}, {ai_charge, 0, NULL}, {ai_charge, -6, NULL}, {ai_charge, -4, NULL}, {ai_charge, -2, NULL} }; mmove_t chick_move_end_attack1 = { FRAME_attak128, FRAME_attak132, chick_frames_end_attack1, chick_run }; void chick_rerocket(edict_t *self) { if (!self) { return; } if (self->enemy->health > 0) { if (range(self, self->enemy) > RANGE_MELEE) { if (visible(self, self->enemy)) { if (random() <= 0.6) { self->monsterinfo.currentmove = &chick_move_attack1; return; } } } } self->monsterinfo.currentmove = &chick_move_end_attack1; } void chick_attack1(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &chick_move_attack1; } mframe_t chick_frames_slash[] = { {ai_charge, 1, NULL}, {ai_charge, 7, ChickSlash}, {ai_charge, -7, NULL}, {ai_charge, 1, NULL}, {ai_charge, -1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 0, NULL}, {ai_charge, 1, NULL}, {ai_charge, -2, chick_reslash} }; mmove_t chick_move_slash = { FRAME_attak204, FRAME_attak212, chick_frames_slash, NULL }; mframe_t chick_frames_end_slash[] = { {ai_charge, -6, NULL}, {ai_charge, -1, NULL}, {ai_charge, -6, NULL}, {ai_charge, 0, NULL} }; mmove_t chick_move_end_slash = { FRAME_attak213, FRAME_attak216, chick_frames_end_slash, chick_run }; void chick_reslash(edict_t *self) { if (!self) { return; } if (self->enemy->health > 0) { if (range(self, self->enemy) == RANGE_MELEE) { if (random() <= 0.9) { self->monsterinfo.currentmove = &chick_move_slash; return; } else { self->monsterinfo.currentmove = &chick_move_end_slash; return; } } } self->monsterinfo.currentmove = &chick_move_end_slash; } void chick_slash(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &chick_move_slash; } mframe_t chick_frames_start_slash[] = { {ai_charge, 1, NULL}, {ai_charge, 8, NULL}, {ai_charge, 3, NULL} }; mmove_t chick_move_start_slash = { FRAME_attak201, FRAME_attak203, chick_frames_start_slash, chick_slash }; void chick_melee(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &chick_move_start_slash; } void chick_attack(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &chick_move_start_attack1; } void chick_sight(edict_t *self, edict_t *other /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); } /* * QUAKED monster_chick (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight */ void SP_monster_chick(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_missile_prelaunch = gi.soundindex("chick/chkatck1.wav"); sound_missile_launch = gi.soundindex("chick/chkatck2.wav"); sound_melee_swing = gi.soundindex("chick/chkatck3.wav"); sound_melee_hit = gi.soundindex("chick/chkatck4.wav"); sound_missile_reload = gi.soundindex("chick/chkatck5.wav"); sound_death1 = gi.soundindex("chick/chkdeth1.wav"); sound_death2 = gi.soundindex("chick/chkdeth2.wav"); sound_fall_down = gi.soundindex("chick/chkfall1.wav"); sound_idle1 = gi.soundindex("chick/chkidle1.wav"); sound_idle2 = gi.soundindex("chick/chkidle2.wav"); sound_pain1 = gi.soundindex("chick/chkpain1.wav"); sound_pain2 = gi.soundindex("chick/chkpain2.wav"); sound_pain3 = gi.soundindex("chick/chkpain3.wav"); sound_sight = gi.soundindex("chick/chksght1.wav"); sound_search = gi.soundindex("chick/chksrch1.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/bitch/tris.md2"); VectorSet(self->mins, -16, -16, 0); VectorSet(self->maxs, 16, 16, 56); self->health = 175; self->gib_health = -70; self->mass = 200; self->pain = chick_pain; self->die = chick_die; self->monsterinfo.stand = chick_stand; self->monsterinfo.walk = chick_walk; self->monsterinfo.run = chick_run; self->monsterinfo.dodge = chick_dodge; self->monsterinfo.attack = chick_attack; self->monsterinfo.melee = chick_melee; self->monsterinfo.sight = chick_sight; gi.linkentity(self); self->monsterinfo.currentmove = &chick_move_stand; self->monsterinfo.scale = MODEL_SCALE; walkmonster_start(self); } yquake2-QUAKE2_5_32/src/game/monster/chick/chick.h0000644000175000017500000002055612615162034022331 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Iron Maiden animations. * * ======================================================================= */ #define FRAME_attak101 0 #define FRAME_attak102 1 #define FRAME_attak103 2 #define FRAME_attak104 3 #define FRAME_attak105 4 #define FRAME_attak106 5 #define FRAME_attak107 6 #define FRAME_attak108 7 #define FRAME_attak109 8 #define FRAME_attak110 9 #define FRAME_attak111 10 #define FRAME_attak112 11 #define FRAME_attak113 12 #define FRAME_attak114 13 #define FRAME_attak115 14 #define FRAME_attak116 15 #define FRAME_attak117 16 #define FRAME_attak118 17 #define FRAME_attak119 18 #define FRAME_attak120 19 #define FRAME_attak121 20 #define FRAME_attak122 21 #define FRAME_attak123 22 #define FRAME_attak124 23 #define FRAME_attak125 24 #define FRAME_attak126 25 #define FRAME_attak127 26 #define FRAME_attak128 27 #define FRAME_attak129 28 #define FRAME_attak130 29 #define FRAME_attak131 30 #define FRAME_attak132 31 #define FRAME_attak201 32 #define FRAME_attak202 33 #define FRAME_attak203 34 #define FRAME_attak204 35 #define FRAME_attak205 36 #define FRAME_attak206 37 #define FRAME_attak207 38 #define FRAME_attak208 39 #define FRAME_attak209 40 #define FRAME_attak210 41 #define FRAME_attak211 42 #define FRAME_attak212 43 #define FRAME_attak213 44 #define FRAME_attak214 45 #define FRAME_attak215 46 #define FRAME_attak216 47 #define FRAME_death101 48 #define FRAME_death102 49 #define FRAME_death103 50 #define FRAME_death104 51 #define FRAME_death105 52 #define FRAME_death106 53 #define FRAME_death107 54 #define FRAME_death108 55 #define FRAME_death109 56 #define FRAME_death110 57 #define FRAME_death111 58 #define FRAME_death112 59 #define FRAME_death201 60 #define FRAME_death202 61 #define FRAME_death203 62 #define FRAME_death204 63 #define FRAME_death205 64 #define FRAME_death206 65 #define FRAME_death207 66 #define FRAME_death208 67 #define FRAME_death209 68 #define FRAME_death210 69 #define FRAME_death211 70 #define FRAME_death212 71 #define FRAME_death213 72 #define FRAME_death214 73 #define FRAME_death215 74 #define FRAME_death216 75 #define FRAME_death217 76 #define FRAME_death218 77 #define FRAME_death219 78 #define FRAME_death220 79 #define FRAME_death221 80 #define FRAME_death222 81 #define FRAME_death223 82 #define FRAME_duck01 83 #define FRAME_duck02 84 #define FRAME_duck03 85 #define FRAME_duck04 86 #define FRAME_duck05 87 #define FRAME_duck06 88 #define FRAME_duck07 89 #define FRAME_pain101 90 #define FRAME_pain102 91 #define FRAME_pain103 92 #define FRAME_pain104 93 #define FRAME_pain105 94 #define FRAME_pain201 95 #define FRAME_pain202 96 #define FRAME_pain203 97 #define FRAME_pain204 98 #define FRAME_pain205 99 #define FRAME_pain301 100 #define FRAME_pain302 101 #define FRAME_pain303 102 #define FRAME_pain304 103 #define FRAME_pain305 104 #define FRAME_pain306 105 #define FRAME_pain307 106 #define FRAME_pain308 107 #define FRAME_pain309 108 #define FRAME_pain310 109 #define FRAME_pain311 110 #define FRAME_pain312 111 #define FRAME_pain313 112 #define FRAME_pain314 113 #define FRAME_pain315 114 #define FRAME_pain316 115 #define FRAME_pain317 116 #define FRAME_pain318 117 #define FRAME_pain319 118 #define FRAME_pain320 119 #define FRAME_pain321 120 #define FRAME_stand101 121 #define FRAME_stand102 122 #define FRAME_stand103 123 #define FRAME_stand104 124 #define FRAME_stand105 125 #define FRAME_stand106 126 #define FRAME_stand107 127 #define FRAME_stand108 128 #define FRAME_stand109 129 #define FRAME_stand110 130 #define FRAME_stand111 131 #define FRAME_stand112 132 #define FRAME_stand113 133 #define FRAME_stand114 134 #define FRAME_stand115 135 #define FRAME_stand116 136 #define FRAME_stand117 137 #define FRAME_stand118 138 #define FRAME_stand119 139 #define FRAME_stand120 140 #define FRAME_stand121 141 #define FRAME_stand122 142 #define FRAME_stand123 143 #define FRAME_stand124 144 #define FRAME_stand125 145 #define FRAME_stand126 146 #define FRAME_stand127 147 #define FRAME_stand128 148 #define FRAME_stand129 149 #define FRAME_stand130 150 #define FRAME_stand201 151 #define FRAME_stand202 152 #define FRAME_stand203 153 #define FRAME_stand204 154 #define FRAME_stand205 155 #define FRAME_stand206 156 #define FRAME_stand207 157 #define FRAME_stand208 158 #define FRAME_stand209 159 #define FRAME_stand210 160 #define FRAME_stand211 161 #define FRAME_stand212 162 #define FRAME_stand213 163 #define FRAME_stand214 164 #define FRAME_stand215 165 #define FRAME_stand216 166 #define FRAME_stand217 167 #define FRAME_stand218 168 #define FRAME_stand219 169 #define FRAME_stand220 170 #define FRAME_stand221 171 #define FRAME_stand222 172 #define FRAME_stand223 173 #define FRAME_stand224 174 #define FRAME_stand225 175 #define FRAME_stand226 176 #define FRAME_stand227 177 #define FRAME_stand228 178 #define FRAME_stand229 179 #define FRAME_stand230 180 #define FRAME_walk01 181 #define FRAME_walk02 182 #define FRAME_walk03 183 #define FRAME_walk04 184 #define FRAME_walk05 185 #define FRAME_walk06 186 #define FRAME_walk07 187 #define FRAME_walk08 188 #define FRAME_walk09 189 #define FRAME_walk10 190 #define FRAME_walk11 191 #define FRAME_walk12 192 #define FRAME_walk13 193 #define FRAME_walk14 194 #define FRAME_walk15 195 #define FRAME_walk16 196 #define FRAME_walk17 197 #define FRAME_walk18 198 #define FRAME_walk19 199 #define FRAME_walk20 200 #define FRAME_walk21 201 #define FRAME_walk22 202 #define FRAME_walk23 203 #define FRAME_walk24 204 #define FRAME_walk25 205 #define FRAME_walk26 206 #define FRAME_walk27 207 #define FRAME_recln201 208 #define FRAME_recln202 209 #define FRAME_recln203 210 #define FRAME_recln204 211 #define FRAME_recln205 212 #define FRAME_recln206 213 #define FRAME_recln207 214 #define FRAME_recln208 215 #define FRAME_recln209 216 #define FRAME_recln210 217 #define FRAME_recln211 218 #define FRAME_recln212 219 #define FRAME_recln213 220 #define FRAME_recln214 221 #define FRAME_recln215 222 #define FRAME_recln216 223 #define FRAME_recln217 224 #define FRAME_recln218 225 #define FRAME_recln219 226 #define FRAME_recln220 227 #define FRAME_recln221 228 #define FRAME_recln222 229 #define FRAME_recln223 230 #define FRAME_recln224 231 #define FRAME_recln225 232 #define FRAME_recln226 233 #define FRAME_recln227 234 #define FRAME_recln228 235 #define FRAME_recln229 236 #define FRAME_recln230 237 #define FRAME_recln231 238 #define FRAME_recln232 239 #define FRAME_recln233 240 #define FRAME_recln234 241 #define FRAME_recln235 242 #define FRAME_recln236 243 #define FRAME_recln237 244 #define FRAME_recln238 245 #define FRAME_recln239 246 #define FRAME_recln240 247 #define FRAME_recln101 248 #define FRAME_recln102 249 #define FRAME_recln103 250 #define FRAME_recln104 251 #define FRAME_recln105 252 #define FRAME_recln106 253 #define FRAME_recln107 254 #define FRAME_recln108 255 #define FRAME_recln109 256 #define FRAME_recln110 257 #define FRAME_recln111 258 #define FRAME_recln112 259 #define FRAME_recln113 260 #define FRAME_recln114 261 #define FRAME_recln115 262 #define FRAME_recln116 263 #define FRAME_recln117 264 #define FRAME_recln118 265 #define FRAME_recln119 266 #define FRAME_recln120 267 #define FRAME_recln121 268 #define FRAME_recln122 269 #define FRAME_recln123 270 #define FRAME_recln124 271 #define FRAME_recln125 272 #define FRAME_recln126 273 #define FRAME_recln127 274 #define FRAME_recln128 275 #define FRAME_recln129 276 #define FRAME_recln130 277 #define FRAME_recln131 278 #define FRAME_recln132 279 #define FRAME_recln133 280 #define FRAME_recln134 281 #define FRAME_recln135 282 #define FRAME_recln136 283 #define FRAME_recln137 284 #define FRAME_recln138 285 #define FRAME_recln139 286 #define FRAME_recln140 287 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/boss2/0000755000175000017500000000000012615162034021036 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/monster/boss2/boss2.h0000644000175000017500000001264412615162034022246 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Animations for boss2. * * ======================================================================= */ #define FRAME_stand30 0 #define FRAME_stand31 1 #define FRAME_stand32 2 #define FRAME_stand33 3 #define FRAME_stand34 4 #define FRAME_stand35 5 #define FRAME_stand36 6 #define FRAME_stand37 7 #define FRAME_stand38 8 #define FRAME_stand39 9 #define FRAME_stand40 10 #define FRAME_stand41 11 #define FRAME_stand42 12 #define FRAME_stand43 13 #define FRAME_stand44 14 #define FRAME_stand45 15 #define FRAME_stand46 16 #define FRAME_stand47 17 #define FRAME_stand48 18 #define FRAME_stand49 19 #define FRAME_stand50 20 #define FRAME_stand1 21 #define FRAME_stand2 22 #define FRAME_stand3 23 #define FRAME_stand4 24 #define FRAME_stand5 25 #define FRAME_stand6 26 #define FRAME_stand7 27 #define FRAME_stand8 28 #define FRAME_stand9 29 #define FRAME_stand10 30 #define FRAME_stand11 31 #define FRAME_stand12 32 #define FRAME_stand13 33 #define FRAME_stand14 34 #define FRAME_stand15 35 #define FRAME_stand16 36 #define FRAME_stand17 37 #define FRAME_stand18 38 #define FRAME_stand19 39 #define FRAME_stand20 40 #define FRAME_stand21 41 #define FRAME_stand22 42 #define FRAME_stand23 43 #define FRAME_stand24 44 #define FRAME_stand25 45 #define FRAME_stand26 46 #define FRAME_stand27 47 #define FRAME_stand28 48 #define FRAME_stand29 49 #define FRAME_walk1 50 #define FRAME_walk2 51 #define FRAME_walk3 52 #define FRAME_walk4 53 #define FRAME_walk5 54 #define FRAME_walk6 55 #define FRAME_walk7 56 #define FRAME_walk8 57 #define FRAME_walk9 58 #define FRAME_walk10 59 #define FRAME_walk11 60 #define FRAME_walk12 61 #define FRAME_walk13 62 #define FRAME_walk14 63 #define FRAME_walk15 64 #define FRAME_walk16 65 #define FRAME_walk17 66 #define FRAME_walk18 67 #define FRAME_walk19 68 #define FRAME_walk20 69 #define FRAME_attack1 70 #define FRAME_attack2 71 #define FRAME_attack3 72 #define FRAME_attack4 73 #define FRAME_attack5 74 #define FRAME_attack6 75 #define FRAME_attack7 76 #define FRAME_attack8 77 #define FRAME_attack9 78 #define FRAME_attack10 79 #define FRAME_attack11 80 #define FRAME_attack12 81 #define FRAME_attack13 82 #define FRAME_attack14 83 #define FRAME_attack15 84 #define FRAME_attack16 85 #define FRAME_attack17 86 #define FRAME_attack18 87 #define FRAME_attack19 88 #define FRAME_attack20 89 #define FRAME_attack21 90 #define FRAME_attack22 91 #define FRAME_attack23 92 #define FRAME_attack24 93 #define FRAME_attack25 94 #define FRAME_attack26 95 #define FRAME_attack27 96 #define FRAME_attack28 97 #define FRAME_attack29 98 #define FRAME_attack30 99 #define FRAME_attack31 100 #define FRAME_attack32 101 #define FRAME_attack33 102 #define FRAME_attack34 103 #define FRAME_attack35 104 #define FRAME_attack36 105 #define FRAME_attack37 106 #define FRAME_attack38 107 #define FRAME_attack39 108 #define FRAME_attack40 109 #define FRAME_pain2 110 #define FRAME_pain3 111 #define FRAME_pain4 112 #define FRAME_pain5 113 #define FRAME_pain6 114 #define FRAME_pain7 115 #define FRAME_pain8 116 #define FRAME_pain9 117 #define FRAME_pain10 118 #define FRAME_pain11 119 #define FRAME_pain12 120 #define FRAME_pain13 121 #define FRAME_pain14 122 #define FRAME_pain15 123 #define FRAME_pain16 124 #define FRAME_pain17 125 #define FRAME_pain18 126 #define FRAME_pain19 127 #define FRAME_pain20 128 #define FRAME_pain21 129 #define FRAME_pain22 130 #define FRAME_pain23 131 #define FRAME_death2 132 #define FRAME_death3 133 #define FRAME_death4 134 #define FRAME_death5 135 #define FRAME_death6 136 #define FRAME_death7 137 #define FRAME_death8 138 #define FRAME_death9 139 #define FRAME_death10 140 #define FRAME_death11 141 #define FRAME_death12 142 #define FRAME_death13 143 #define FRAME_death14 144 #define FRAME_death15 145 #define FRAME_death16 146 #define FRAME_death17 147 #define FRAME_death18 148 #define FRAME_death19 149 #define FRAME_death20 150 #define FRAME_death21 151 #define FRAME_death22 152 #define FRAME_death23 153 #define FRAME_death24 154 #define FRAME_death25 155 #define FRAME_death26 156 #define FRAME_death27 157 #define FRAME_death28 158 #define FRAME_death29 159 #define FRAME_death30 160 #define FRAME_death31 161 #define FRAME_death32 162 #define FRAME_death33 163 #define FRAME_death34 164 #define FRAME_death35 165 #define FRAME_death36 166 #define FRAME_death37 167 #define FRAME_death38 168 #define FRAME_death39 169 #define FRAME_death40 170 #define FRAME_death41 171 #define FRAME_death42 172 #define FRAME_death43 173 #define FRAME_death44 174 #define FRAME_death45 175 #define FRAME_death46 176 #define FRAME_death47 177 #define FRAME_death48 178 #define FRAME_death49 179 #define FRAME_death50 180 #define MODEL_SCALE 1.000000 yquake2-QUAKE2_5_32/src/game/monster/boss2/boss2.c0000644000175000017500000004006512615162034022237 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Boss 2 aka Hornet. Found in biggun and inner hangar. * * ======================================================================= */ #include "../../header/local.h" #include "boss2.h" void BossExplode(edict_t *self); qboolean infront(edict_t *self, edict_t *other); static int sound_pain1; static int sound_pain2; static int sound_pain3; static int sound_death; static int sound_search1; void boss2_search(edict_t *self) { if (!self) { return; } if (random() < 0.5) { gi.sound(self, CHAN_VOICE, sound_search1, 1, ATTN_NONE, 0); } } void boss2_run(edict_t *self); void boss2_stand(edict_t *self); void boss2_dead(edict_t *self); void boss2_attack(edict_t *self); void boss2_attack_mg(edict_t *self); void boss2_reattack_mg(edict_t *self); void boss2_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); void Boss2Rocket(edict_t *self) { vec3_t forward, right; vec3_t start; vec3_t dir; vec3_t vec; if (!self) { return; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_BOSS2_ROCKET_1], forward, right, start); VectorCopy(self->enemy->s.origin, vec); vec[2] += self->enemy->viewheight; VectorSubtract(vec, start, dir); VectorNormalize(dir); monster_fire_rocket(self, start, dir, 50, 500, MZ2_BOSS2_ROCKET_1); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_BOSS2_ROCKET_2], forward, right, start); VectorCopy(self->enemy->s.origin, vec); vec[2] += self->enemy->viewheight; VectorSubtract(vec, start, dir); VectorNormalize(dir); monster_fire_rocket(self, start, dir, 50, 500, MZ2_BOSS2_ROCKET_2); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_BOSS2_ROCKET_3], forward, right, start); VectorCopy(self->enemy->s.origin, vec); vec[2] += self->enemy->viewheight; VectorSubtract(vec, start, dir); VectorNormalize(dir); monster_fire_rocket(self, start, dir, 50, 500, MZ2_BOSS2_ROCKET_3); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_BOSS2_ROCKET_4], forward, right, start); VectorCopy(self->enemy->s.origin, vec); vec[2] += self->enemy->viewheight; VectorSubtract(vec, start, dir); VectorNormalize(dir); monster_fire_rocket(self, start, dir, 50, 500, MZ2_BOSS2_ROCKET_4); } void boss2_firebullet_right(edict_t *self) { vec3_t forward, right, target; vec3_t start; if (!self) { return; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_BOSS2_MACHINEGUN_R1], forward, right, start); VectorMA(self->enemy->s.origin, -0.2, self->enemy->velocity, target); target[2] += self->enemy->viewheight; VectorSubtract(target, start, forward); VectorNormalize(forward); monster_fire_bullet(self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_BOSS2_MACHINEGUN_R1); } void boss2_firebullet_left(edict_t *self) { vec3_t forward, right, target; vec3_t start; if (!self) { return; } AngleVectors(self->s.angles, forward, right, NULL); G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_BOSS2_MACHINEGUN_L1], forward, right, start); VectorMA(self->enemy->s.origin, -0.2, self->enemy->velocity, target); target[2] += self->enemy->viewheight; VectorSubtract(target, start, forward); VectorNormalize(forward); monster_fire_bullet(self, start, forward, 6, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MZ2_BOSS2_MACHINEGUN_L1); } void Boss2MachineGun(edict_t *self) { if (!self) { return; } boss2_firebullet_left(self); boss2_firebullet_right(self); } mframe_t boss2_frames_stand[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t boss2_move_stand = { FRAME_stand30, FRAME_stand50, boss2_frames_stand, NULL }; mframe_t boss2_frames_fidget[] = { {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL}, {ai_stand, 0, NULL} }; mmove_t boss2_move_fidget = { FRAME_stand1, FRAME_stand30, boss2_frames_fidget, NULL }; mframe_t boss2_frames_walk[] = { {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL}, {ai_walk, 8, NULL} }; mmove_t boss2_move_walk = { FRAME_walk1, FRAME_walk20, boss2_frames_walk, NULL }; mframe_t boss2_frames_run[] = { {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL}, {ai_run, 8, NULL} }; mmove_t boss2_move_run = { FRAME_walk1, FRAME_walk20, boss2_frames_run, NULL}; mframe_t boss2_frames_attack_pre_mg[] = { {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, boss2_attack_mg} }; mmove_t boss2_move_attack_pre_mg = { FRAME_attack1, FRAME_attack9, boss2_frames_attack_pre_mg, NULL }; /* Loop this */ mframe_t boss2_frames_attack_mg[] = { {ai_charge, 1, Boss2MachineGun}, {ai_charge, 1, Boss2MachineGun}, {ai_charge, 1, Boss2MachineGun}, {ai_charge, 1, Boss2MachineGun}, {ai_charge, 1, Boss2MachineGun}, {ai_charge, 1, boss2_reattack_mg} }; mmove_t boss2_move_attack_mg = { FRAME_attack10, FRAME_attack15, boss2_frames_attack_mg, NULL }; mframe_t boss2_frames_attack_post_mg[] = { {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL} }; mmove_t boss2_move_attack_post_mg = { FRAME_attack16, FRAME_attack19, boss2_frames_attack_post_mg, boss2_run }; mframe_t boss2_frames_attack_rocket[] = { {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_move, -20, Boss2Rocket}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL}, {ai_charge, 1, NULL} }; mmove_t boss2_move_attack_rocket = { FRAME_attack20, FRAME_attack40, boss2_frames_attack_rocket, boss2_run }; mframe_t boss2_frames_pain_heavy[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t boss2_move_pain_heavy = { FRAME_pain2, FRAME_pain19, boss2_frames_pain_heavy, boss2_run}; mframe_t boss2_frames_pain_light[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL} }; mmove_t boss2_move_pain_light = { FRAME_pain20, FRAME_pain23, boss2_frames_pain_light, boss2_run }; mframe_t boss2_frames_death[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, BossExplode} }; mmove_t boss2_move_death = { FRAME_death2, FRAME_death50, boss2_frames_death, boss2_dead }; void boss2_stand(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &boss2_move_stand; } void boss2_run(edict_t *self) { if (!self) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.currentmove = &boss2_move_stand; } else { self->monsterinfo.currentmove = &boss2_move_run; } } void boss2_walk(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &boss2_move_walk; } void boss2_attack(edict_t *self) { vec3_t vec; float range; if (!self) { return; } VectorSubtract(self->enemy->s.origin, self->s.origin, vec); range = VectorLength(vec); if (range <= 125) { self->monsterinfo.currentmove = &boss2_move_attack_pre_mg; } else { if (random() <= 0.6) { self->monsterinfo.currentmove = &boss2_move_attack_pre_mg; } else { self->monsterinfo.currentmove = &boss2_move_attack_rocket; } } } void boss2_attack_mg(edict_t *self) { if (!self) { return; } self->monsterinfo.currentmove = &boss2_move_attack_mg; } void boss2_reattack_mg(edict_t *self) { if (!self) { return; } if (infront(self, self->enemy)) { if (random() <= 0.7) { self->monsterinfo.currentmove = &boss2_move_attack_mg; } else { self->monsterinfo.currentmove = &boss2_move_attack_post_mg; } } else { self->monsterinfo.currentmove = &boss2_move_attack_post_mg; } } void boss2_pain(edict_t *self, edict_t *other /* unused */, float kick /* unused */, int damage) { if (!self) { return; } if (self->health < (self->max_health / 2)) { self->s.skinnum = 1; } if (level.time < self->pain_debounce_time) { return; } self->pain_debounce_time = level.time + 3; /* American wanted these at no attenuation */ if (damage < 10) { gi.sound(self, CHAN_VOICE, sound_pain3, 1, ATTN_NONE, 0); self->monsterinfo.currentmove = &boss2_move_pain_light; } else if (damage < 30) { gi.sound(self, CHAN_VOICE, sound_pain1, 1, ATTN_NONE, 0); self->monsterinfo.currentmove = &boss2_move_pain_light; } else { gi.sound(self, CHAN_VOICE, sound_pain2, 1, ATTN_NONE, 0); self->monsterinfo.currentmove = &boss2_move_pain_heavy; } } void boss2_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -56, -56, 0); VectorSet(self->maxs, 56, 56, 80); self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } void boss2_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage /* unused */, vec3_t point /* unused */) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NONE, 0); self->deadflag = DEAD_DEAD; self->takedamage = DAMAGE_NO; self->count = 0; self->monsterinfo.currentmove = &boss2_move_death; } qboolean Boss2_CheckAttack(edict_t *self) { vec3_t spot1, spot2; vec3_t temp; float chance; trace_t tr; int enemy_range; float enemy_yaw; if (!self) { return false; } if (self->enemy->health > 0) { /* see if any entities are in the way of the shot */ VectorCopy(self->s.origin, spot1); spot1[2] += self->viewheight; VectorCopy(self->enemy->s.origin, spot2); spot2[2] += self->enemy->viewheight; tr = gi.trace( spot1, NULL, NULL, spot2, self, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA); /* do we have a clear shot? */ if (tr.ent != self->enemy) { return false; } } enemy_range = range(self, self->enemy); VectorSubtract(self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw(temp); self->ideal_yaw = enemy_yaw; /* melee attack */ if (enemy_range == RANGE_MELEE) { if (self->monsterinfo.melee) { self->monsterinfo.attack_state = AS_MELEE; } else { self->monsterinfo.attack_state = AS_MISSILE; } return true; } /* missile attack */ if (!self->monsterinfo.attack) { return false; } if (level.time < self->monsterinfo.attack_finished) { return false; } if (enemy_range == RANGE_FAR) { return false; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { chance = 0.4; } else if (enemy_range == RANGE_MELEE) { chance = 0.8; } else if (enemy_range == RANGE_NEAR) { chance = 0.8; } else if (enemy_range == RANGE_MID) { chance = 0.8; } else { return false; } if (random() < chance) { self->monsterinfo.attack_state = AS_MISSILE; self->monsterinfo.attack_finished = level.time + 2 * random(); return true; } if (self->flags & FL_FLY) { if (random() < 0.3) { self->monsterinfo.attack_state = AS_SLIDING; } else { self->monsterinfo.attack_state = AS_STRAIGHT; } } return false; } /* * QUAKED monster_boss2 (1 .5 0) (-56 -56 0) (56 56 80) Ambush Trigger_Spawn Sight */ void SP_monster_boss2(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } sound_pain1 = gi.soundindex("bosshovr/bhvpain1.wav"); sound_pain2 = gi.soundindex("bosshovr/bhvpain2.wav"); sound_pain3 = gi.soundindex("bosshovr/bhvpain3.wav"); sound_death = gi.soundindex("bosshovr/bhvdeth1.wav"); sound_search1 = gi.soundindex("bosshovr/bhvunqv1.wav"); self->s.sound = gi.soundindex("bosshovr/bhvengn1.wav"); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; self->s.modelindex = gi.modelindex("models/monsters/boss2/tris.md2"); VectorSet(self->mins, -56, -56, 0); VectorSet(self->maxs, 56, 56, 80); self->health = 2000; self->gib_health = -200; self->mass = 1000; self->flags |= FL_IMMUNE_LASER; self->pain = boss2_pain; self->die = boss2_die; self->monsterinfo.stand = boss2_stand; self->monsterinfo.walk = boss2_walk; self->monsterinfo.run = boss2_run; self->monsterinfo.attack = boss2_attack; self->monsterinfo.search = boss2_search; self->monsterinfo.checkattack = Boss2_CheckAttack; gi.linkentity(self); self->monsterinfo.currentmove = &boss2_move_stand; self->monsterinfo.scale = MODEL_SCALE; flymonster_start(self); } yquake2-QUAKE2_5_32/src/game/savegame/0000755000175000017500000000000012615162034020107 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/savegame/tables/0000755000175000017500000000000012615162034021361 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/savegame/tables/clientfields.h0000644000175000017500000000221712615162034024201 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2011 Yamagi Burmeister * * This program is free software; you can 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. * * ======================================================================= * * Fields of the client to be saved. * * ======================================================================= */ {"pers.weapon", CLOFS(pers.weapon), F_ITEM}, {"pers.lastweapon", CLOFS(pers.lastweapon), F_ITEM}, {"newweapon", CLOFS(newweapon), F_ITEM}, {NULL, 0, F_INT} yquake2-QUAKE2_5_32/src/game/savegame/tables/gamefunc_decs.h0000644000175000017500000017131612615162034024326 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2011 Yamagi Burmeister * * This program is free software; you can 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. * * ======================================================================= * * Prototypes for every function in the game.so. * * ======================================================================= */ extern void ReadLevel ( const char * filename ) ; extern void ReadLevelLocals ( FILE * f ) ; extern void ReadEdict ( FILE * f , edict_t * ent ) ; extern void WriteLevel ( const char * filename ) ; extern void WriteLevelLocals ( FILE * f ) ; extern void WriteEdict ( FILE * f , edict_t * ent ) ; extern void ReadGame ( const char * filename ) ; extern void WriteGame ( const char * filename , qboolean autosave ) ; extern void ReadClient ( FILE * f , gclient_t * client ) ; extern void WriteClient ( FILE * f , gclient_t * client ) ; extern void ReadField ( FILE * f , field_t * field , byte * base ) ; extern void WriteField2 ( FILE * f , field_t * field , byte * base ) ; extern void WriteField1 ( FILE * f , field_t * field , byte * base ) ; extern mmove_t * FindMmoveByName ( char * name ) ; extern mmoveList_t * GetMmoveByAddress ( mmove_t * adr ) ; extern byte * FindFunctionByName ( char * name ) ; extern functionList_t * GetFunctionByAddress ( byte * adr ) ; extern void InitGame ( void ) ; extern void Info_SetValueForKey ( char * s , char * key , char * value ) ; extern qboolean Info_Validate ( char * s ) ; extern void Info_RemoveKey ( char * s , char * key ) ; extern char * Info_ValueForKey ( char * s , char * key ) ; extern int Q_strlcat ( char * dst , const char * src , int size ) ; extern int Q_strlcpy ( char * dst , const char * src , int size ) ; extern char * Q_strlwr ( char * s ) ; extern void Com_sprintf ( char * dest , int size , char * fmt , ... ) ; extern int Q_strcasecmp ( char * s1 , char * s2 ) ; extern int Q_strncasecmp ( char * s1 , char * s2 , int n ) ; extern int Q_stricmp ( const char * s1 , const char * s2 ) ; extern void Com_PageInMemory ( byte * buffer , int size ) ; extern char * COM_Parse ( char * * data_p ) ; extern char * va ( char * format , ... ) ; extern void Swap_Init ( void ) ; extern float FloatNoSwap ( float f ) ; extern float FloatSwap ( float f ) ; extern int LongNoSwap ( int l ) ; extern int LongSwap ( int l ) ; extern short ShortNoSwap ( short l ) ; extern short ShortSwap ( short l ) ; extern float LittleFloat ( float l ) ; extern float BigFloat ( float l ) ; extern int LittleLong ( int l ) ; extern int BigLong ( int l ) ; extern short LittleShort ( short l ) ; extern short BigShort ( short l ) ; extern void COM_DefaultExtension ( char * path , const char * extension ) ; extern void COM_FilePath ( const char * in , char * out ) ; extern void COM_FileBase ( char * in , char * out ) ; extern const char * COM_FileExtension ( const char * in ) ; extern void COM_StripExtension ( char * in , char * out ) ; extern char * COM_SkipPath ( char * pathname ) ; extern int Q_log2 ( int val ) ; extern void VectorScale ( vec3_t in , vec_t scale , vec3_t out ) ; extern void VectorInverse ( vec3_t v ) ; extern vec_t VectorLength ( vec3_t v ) ; extern void CrossProduct ( vec3_t v1 , vec3_t v2 , vec3_t cross ) ; extern void _VectorCopy ( vec3_t in , vec3_t out ) ; extern void _VectorAdd ( vec3_t veca , vec3_t vecb , vec3_t out ) ; extern void _VectorSubtract ( vec3_t veca , vec3_t vecb , vec3_t out ) ; extern vec_t _DotProduct ( vec3_t v1 , vec3_t v2 ) ; extern void VectorMA ( vec3_t veca , float scale , vec3_t vecb , vec3_t vecc ) ; extern vec_t VectorNormalize2 ( vec3_t v , vec3_t out ) ; extern vec_t VectorNormalize ( vec3_t v ) ; extern int VectorCompare ( vec3_t v1 , vec3_t v2 ) ; extern void AddPointToBounds ( vec3_t v , vec3_t mins , vec3_t maxs ) ; extern void ClearBounds ( vec3_t mins , vec3_t maxs ) ; extern int BoxOnPlaneSide2 ( vec3_t emins , vec3_t emaxs , struct cplane_s * p ) ; extern float anglemod ( float a ) ; extern float LerpAngle ( float a2 , float a1 , float frac ) ; extern float Q_fabs ( float f ) ; extern void R_ConcatTransforms ( float in1 [ 3 ] [ 4 ] , float in2 [ 3 ] [ 4 ] , float out [ 3 ] [ 4 ] ) ; extern void R_ConcatRotations ( float in1 [ 3 ] [ 3 ] , float in2 [ 3 ] [ 3 ] , float out [ 3 ] [ 3 ] ) ; extern void PerpendicularVector ( vec3_t dst , const vec3_t src ) ; extern void ProjectPointOnPlane ( vec3_t dst , const vec3_t p , const vec3_t normal ) ; extern void AngleVectors2 ( vec3_t value1 , vec3_t angles ) ; extern void AngleVectors ( vec3_t angles , vec3_t forward , vec3_t right , vec3_t up ) ; extern void RotatePointAroundVector ( vec3_t dst , const vec3_t dir , const vec3_t point , float degrees ) ; extern void Weapon_BFG ( edict_t * ent ) ; extern void weapon_bfg_fire ( edict_t * ent ) ; extern void Weapon_Railgun ( edict_t * ent ) ; extern void weapon_railgun_fire ( edict_t * ent ) ; extern void Weapon_SuperShotgun ( edict_t * ent ) ; extern void weapon_supershotgun_fire ( edict_t * ent ) ; extern void Weapon_Shotgun ( edict_t * ent ) ; extern void weapon_shotgun_fire ( edict_t * ent ) ; extern void Weapon_Chaingun ( edict_t * ent ) ; extern void Chaingun_Fire ( edict_t * ent ) ; extern void Weapon_Machinegun ( edict_t * ent ) ; extern void Machinegun_Fire ( edict_t * ent ) ; extern void Weapon_HyperBlaster ( edict_t * ent ) ; extern void Weapon_HyperBlaster_Fire ( edict_t * ent ) ; extern void Weapon_Blaster ( edict_t * ent ) ; extern void Weapon_Blaster_Fire ( edict_t * ent ) ; extern void Blaster_Fire ( edict_t * ent , vec3_t g_offset , int damage , qboolean hyper , int effect ) ; extern void Weapon_RocketLauncher ( edict_t * ent ) ; extern void Weapon_RocketLauncher_Fire ( edict_t * ent ) ; extern void Weapon_GrenadeLauncher ( edict_t * ent ) ; extern void weapon_grenadelauncher_fire ( edict_t * ent ) ; extern void Weapon_Grenade ( edict_t * ent ) ; extern void weapon_grenade_fire ( edict_t * ent , qboolean held ) ; extern void Weapon_Generic ( edict_t * ent , int FRAME_ACTIVATE_LAST , int FRAME_FIRE_LAST , int FRAME_IDLE_LAST , int FRAME_DEACTIVATE_LAST , int * pause_frames , int * fire_frames , void ( * fire ) ( edict_t * ent ) ) ; extern void Drop_Weapon ( edict_t * ent , gitem_t * item ) ; extern void Use_Weapon ( edict_t * ent , gitem_t * item ) ; extern void Think_Weapon ( edict_t * ent ) ; extern void NoAmmoWeaponChange ( edict_t * ent ) ; extern void ChangeWeapon ( edict_t * ent ) ; extern qboolean Pickup_Weapon ( edict_t * ent , edict_t * other ) ; extern void PlayerNoise ( edict_t * who , vec3_t where , int type ) ; extern void P_ProjectSource ( gclient_t * client , vec3_t point , vec3_t distance , vec3_t forward , vec3_t right , vec3_t result ) ; extern void ClientEndServerFrame ( edict_t * ent ) ; extern void G_SetClientFrame ( edict_t * ent ) ; extern void G_SetClientSound ( edict_t * ent ) ; extern void G_SetClientEvent ( edict_t * ent ) ; extern void G_SetClientEffects ( edict_t * ent ) ; extern void P_WorldEffects ( void ) ; extern void P_FallingDamage ( edict_t * ent ) ; extern void SV_CalcBlend ( edict_t * ent ) ; extern void SV_AddBlend ( float r , float g , float b , float a , float * v_blend ) ; extern void SV_CalcGunOffset ( edict_t * ent ) ; extern void SV_CalcViewOffset ( edict_t * ent ) ; extern void P_DamageFeedback ( edict_t * player ) ; extern float SV_CalcRoll ( vec3_t angles , vec3_t velocity ) ; extern edict_t * PlayerTrail_LastSpot ( void ) ; extern edict_t * PlayerTrail_PickNext ( edict_t * self ) ; extern edict_t * PlayerTrail_PickFirst ( edict_t * self ) ; extern void PlayerTrail_New ( vec3_t spot ) ; extern void PlayerTrail_Add ( vec3_t spot ) ; extern void PlayerTrail_Init ( void ) ; extern void G_SetSpectatorStats ( edict_t * ent ) ; extern void G_CheckChaseStats ( edict_t * ent ) ; extern void G_SetStats ( edict_t * ent ) ; extern void InventoryMessage ( edict_t * ent ) ; extern void HelpComputerMessage ( edict_t * ent ) ; extern void DeathmatchScoreboardMessage ( edict_t * ent , edict_t * killer ) ; extern void BeginIntermission ( edict_t * targ ) ; extern void MoveClientToIntermission ( edict_t * ent ) ; extern void ClientBeginServerFrame ( edict_t * ent ) ; extern void ClientThink ( edict_t * ent , usercmd_t * ucmd ) ; extern void PrintPmove ( pmove_t * pm ) ; extern unsigned CheckBlock ( void * b , int c ) ; extern trace_t PM_trace ( vec3_t start , vec3_t mins , vec3_t maxs , vec3_t end ) ; extern void ClientDisconnect ( edict_t * ent ) ; extern qboolean ClientConnect ( edict_t * ent , char * userinfo ) ; extern void ClientUserinfoChanged ( edict_t * ent , char * userinfo ) ; extern void ClientBegin ( edict_t * ent ) ; extern void ClientBeginDeathmatch ( edict_t * ent ) ; extern void PutClientInServer ( edict_t * ent ) ; extern void spectator_respawn ( edict_t * ent ) ; extern void respawn ( edict_t * self ) ; extern void CopyToBodyQue ( edict_t * ent ) ; extern void body_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void InitBodyQue ( void ) ; extern void SelectSpawnPoint ( edict_t * ent , vec3_t origin , vec3_t angles ) ; extern edict_t * SelectCoopSpawnPoint ( edict_t * ent ) ; extern edict_t * SelectDeathmatchSpawnPoint ( void ) ; extern edict_t * SelectFarthestDeathmatchSpawnPoint ( void ) ; extern edict_t * SelectRandomDeathmatchSpawnPoint ( void ) ; extern float PlayersRangeFromSpot ( edict_t * spot ) ; extern void FetchClientEntData ( edict_t * ent ) ; extern void SaveClientData ( void ) ; extern void InitClientResp ( gclient_t * client ) ; extern void InitClientPersistant ( gclient_t * client ) ; extern void player_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void LookAtKiller ( edict_t * self , edict_t * inflictor , edict_t * attacker ) ; extern void TossClientWeapon ( edict_t * self ) ; extern void ClientObituary ( edict_t * self , edict_t * inflictor , edict_t * attacker ) ; extern qboolean IsNeutral ( edict_t * ent ) ; extern qboolean IsFemale ( edict_t * ent ) ; extern void player_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void SP_info_player_intermission ( void ) ; extern void SP_info_player_coop ( edict_t * self ) ; extern void SP_info_player_deathmatch ( edict_t * self ) ; extern void SP_info_player_start ( edict_t * self ) ; extern void SP_CreateUnnamedSpawn ( edict_t * self ) ; extern void SP_CreateCoopSpots ( edict_t * self ) ; extern void SP_FixCoopSpots ( edict_t * self ) ; extern void SP_monster_tank ( edict_t * self ) ; extern void tank_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void tank_dead ( edict_t * self ) ; extern void tank_attack ( edict_t * self ) ; extern void tank_doattack_rocket ( edict_t * self ) ; extern void tank_refire_rocket ( edict_t * self ) ; extern void tank_poststrike ( edict_t * self ) ; extern void tank_reattack_blaster ( edict_t * self ) ; extern void TankMachineGun ( edict_t * self ) ; extern void TankRocket ( edict_t * self ) ; extern void TankStrike ( edict_t * self ) ; extern void TankBlaster ( edict_t * self ) ; extern void tank_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void tank_run ( edict_t * self ) ; extern void tank_walk ( edict_t * self ) ; extern void tank_stand ( edict_t * self ) ; extern void tank_idle ( edict_t * self ) ; extern void tank_windup ( edict_t * self ) ; extern void tank_thud ( edict_t * self ) ; extern void tank_footstep ( edict_t * self ) ; extern void tank_sight ( edict_t * self , edict_t * other ) ; extern void SP_monster_supertank ( edict_t * self ) ; extern void supertank_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void BossExplode ( edict_t * self ) ; extern void supertank_dead ( edict_t * self ) ; extern void supertank_attack ( edict_t * self ) ; extern void supertankMachineGun ( edict_t * self ) ; extern void supertankRocket ( edict_t * self ) ; extern void supertank_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void supertank_reattack1 ( edict_t * self ) ; extern void supertank_run ( edict_t * self ) ; extern void supertank_walk ( edict_t * self ) ; extern void supertank_forward ( edict_t * self ) ; extern void supertank_stand ( edict_t * self ) ; extern void supertank_search ( edict_t * self ) ; extern void TreadSound ( edict_t * self ) ; extern void SP_monster_soldier_ss ( edict_t * self ) ; extern void SP_monster_soldier ( edict_t * self ) ; extern void SP_monster_soldier_light ( edict_t * self ) ; extern void SP_monster_soldier_x ( edict_t * self ) ; extern void soldier_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void soldier_dead ( edict_t * self ) ; extern void soldier_fire7 ( edict_t * self ) ; extern void soldier_fire6 ( edict_t * self ) ; extern void soldier_dodge ( edict_t * self , edict_t * attacker , float eta ) ; extern void soldier_duck_hold ( edict_t * self ) ; extern void soldier_sight ( edict_t * self , edict_t * other ) ; extern void soldier_attack ( edict_t * self ) ; extern void soldier_attack6_refire ( edict_t * self ) ; extern void soldier_fire8 ( edict_t * self ) ; extern void soldier_fire4 ( edict_t * self ) ; extern void soldier_attack3_refire ( edict_t * self ) ; extern void soldier_fire3 ( edict_t * self ) ; extern void soldier_duck_up ( edict_t * self ) ; extern void soldier_duck_down ( edict_t * self ) ; extern void soldier_attack2_refire2 ( edict_t * self ) ; extern void soldier_attack2_refire1 ( edict_t * self ) ; extern void soldier_fire2 ( edict_t * self ) ; extern void soldier_attack1_refire2 ( edict_t * self ) ; extern void soldier_attack1_refire1 ( edict_t * self ) ; extern void soldier_fire1 ( edict_t * self ) ; extern void soldier_fire ( edict_t * self , int flash_number ) ; extern void soldier_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void soldier_run ( edict_t * self ) ; extern void soldier_walk ( edict_t * self ) ; extern void soldier_walk1_random ( edict_t * self ) ; extern void soldier_stand ( edict_t * self ) ; extern void soldier_cock ( edict_t * self ) ; extern void soldier_idle ( edict_t * self ) ; extern void SP_monster_parasite ( edict_t * self ) ; extern void parasite_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void parasite_dead ( edict_t * self ) ; extern void parasite_attack ( edict_t * self ) ; extern void parasite_drain_attack ( edict_t * self ) ; extern qboolean parasite_drain_attack_ok ( vec3_t start , vec3_t end ) ; extern void parasite_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void parasite_walk ( edict_t * self ) ; extern void parasite_start_walk ( edict_t * self ) ; extern void parasite_run ( edict_t * self ) ; extern void parasite_start_run ( edict_t * self ) ; extern void parasite_stand ( edict_t * self ) ; extern void parasite_idle ( edict_t * self ) ; extern void parasite_refidget ( edict_t * self ) ; extern void parasite_do_fidget ( edict_t * self ) ; extern void parasite_end_fidget ( edict_t * self ) ; extern void parasite_search ( edict_t * self ) ; extern void parasite_scratch ( edict_t * self ) ; extern void parasite_tap ( edict_t * self ) ; extern void parasite_sight ( edict_t * self , edict_t * other ) ; extern void parasite_reel_in ( edict_t * self ) ; extern void parasite_launch ( edict_t * self ) ; extern void SP_monster_mutant ( edict_t * self ) ; extern void mutant_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void mutant_dead ( edict_t * self ) ; extern void mutant_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern qboolean mutant_checkattack ( edict_t * self ) ; extern qboolean mutant_check_jump ( edict_t * self ) ; extern qboolean mutant_check_melee ( edict_t * self ) ; extern void mutant_jump ( edict_t * self ) ; extern void mutant_check_landing ( edict_t * self ) ; extern void mutant_jump_takeoff ( edict_t * self ) ; extern void mutant_jump_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void mutant_melee ( edict_t * self ) ; extern void mutant_check_refire ( edict_t * self ) ; extern void mutant_hit_right ( edict_t * self ) ; extern void mutant_hit_left ( edict_t * self ) ; extern void mutant_run ( edict_t * self ) ; extern void mutant_walk ( edict_t * self ) ; extern void mutant_walk_loop ( edict_t * self ) ; extern void mutant_idle ( edict_t * self ) ; extern void mutant_idle_loop ( edict_t * self ) ; extern void mutant_stand ( edict_t * self ) ; extern void mutant_swing ( edict_t * self ) ; extern void mutant_search ( edict_t * self ) ; extern void mutant_sight ( edict_t * self , edict_t * other ) ; extern void mutant_step ( edict_t * self ) ; extern qboolean M_walkmove ( edict_t * ent , float yaw , float dist ) ; extern void M_MoveToGoal ( edict_t * ent , float dist ) ; extern qboolean SV_CloseEnough ( edict_t * ent , edict_t * goal , float dist ) ; extern void SV_NewChaseDir ( edict_t * actor , edict_t * enemy , float dist ) ; extern void SV_FixCheckBottom ( edict_t * ent ) ; extern qboolean SV_StepDirection ( edict_t * ent , float yaw , float dist ) ; extern void M_ChangeYaw ( edict_t * ent ) ; extern qboolean SV_movestep ( edict_t * ent , vec3_t move , qboolean relink ) ; extern qboolean M_CheckBottom ( edict_t * ent ) ; extern void SP_monster_medic ( edict_t * self ) ; extern qboolean medic_checkattack ( edict_t * self ) ; extern void medic_attack ( edict_t * self ) ; extern void medic_hook_retract ( edict_t * self ) ; extern void medic_cable_attack ( edict_t * self ) ; extern void medic_hook_launch ( edict_t * self ) ; extern void medic_continue ( edict_t * self ) ; extern void medic_dodge ( edict_t * self , edict_t * attacker , float eta ) ; extern void medic_duck_up ( edict_t * self ) ; extern void medic_duck_hold ( edict_t * self ) ; extern void medic_duck_down ( edict_t * self ) ; extern void medic_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void medic_dead ( edict_t * self ) ; extern void medic_fire_blaster ( edict_t * self ) ; extern void medic_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void medic_run ( edict_t * self ) ; extern void medic_walk ( edict_t * self ) ; extern void medic_stand ( edict_t * self ) ; extern void medic_sight ( edict_t * self , edict_t * other ) ; extern void medic_search ( edict_t * self ) ; extern void medic_idle ( edict_t * self ) ; extern edict_t * medic_FindDeadMonster ( edict_t * self ) ; extern void SP_misc_insane ( edict_t * self ) ; extern void insane_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void insane_dead ( edict_t * self ) ; extern void insane_stand ( edict_t * self ) ; extern void insane_checkup ( edict_t * self ) ; extern void insane_checkdown ( edict_t * self ) ; extern void insane_onground ( edict_t * self ) ; extern void insane_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void insane_run ( edict_t * self ) ; extern void insane_walk ( edict_t * self ) ; extern void insane_cross ( edict_t * self ) ; extern void insane_scream ( edict_t * self ) ; extern void insane_moan ( edict_t * self ) ; extern void insane_shake ( edict_t * self ) ; extern void insane_fist ( edict_t * self ) ; extern void SP_monster_infantry ( edict_t * self ) ; extern void infantry_attack ( edict_t * self ) ; extern void infantry_smack ( edict_t * self ) ; extern void infantry_swing ( edict_t * self ) ; extern void infantry_fire ( edict_t * self ) ; extern void infantry_cock_gun ( edict_t * self ) ; extern void infantry_dodge ( edict_t * self , edict_t * attacker , float eta ) ; extern void infantry_duck_up ( edict_t * self ) ; extern void infantry_duck_hold ( edict_t * self ) ; extern void infantry_duck_down ( edict_t * self ) ; extern void infantry_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void infantry_dead ( edict_t * self ) ; extern void infantry_sight ( edict_t * self , edict_t * other ) ; extern void InfantryMachineGun ( edict_t * self ) ; extern void infantry_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void infantry_run ( edict_t * self ) ; extern void infantry_walk ( edict_t * self ) ; extern void infantry_fidget ( edict_t * self ) ; extern void infantry_stand ( edict_t * self ) ; extern void SP_monster_hover ( edict_t * self ) ; extern void hover_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void hover_dead ( edict_t * self ) ; extern void hover_deadthink ( edict_t * self ) ; extern void hover_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void hover_attack ( edict_t * self ) ; extern void hover_start_attack ( edict_t * self ) ; extern void hover_walk ( edict_t * self ) ; extern void hover_run ( edict_t * self ) ; extern void hover_stand ( edict_t * self ) ; extern void hover_fire_blaster ( edict_t * self ) ; extern void hover_reattack ( edict_t * self ) ; extern void hover_search ( edict_t * self ) ; extern void hover_sight ( edict_t * self , edict_t * other ) ; extern void SP_monster_gunner ( edict_t * self ) ; extern void gunner_refire_chain ( edict_t * self ) ; extern void gunner_fire_chain ( edict_t * self ) ; extern void gunner_attack ( edict_t * self ) ; extern void GunnerGrenade ( edict_t * self ) ; extern void GunnerFire ( edict_t * self ) ; extern void gunner_opengun ( edict_t * self ) ; extern void gunner_dodge ( edict_t * self , edict_t * attacker , float eta ) ; extern void gunner_duck_up ( edict_t * self ) ; extern void gunner_duck_hold ( edict_t * self ) ; extern void gunner_duck_down ( edict_t * self ) ; extern void gunner_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void gunner_dead ( edict_t * self ) ; extern void gunner_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void gunner_runandshoot ( edict_t * self ) ; extern void gunner_run ( edict_t * self ) ; extern void gunner_walk ( edict_t * self ) ; extern void gunner_stand ( edict_t * self ) ; extern void gunner_fidget ( edict_t * self ) ; extern void gunner_search ( edict_t * self ) ; extern void gunner_sight ( edict_t * self , edict_t * other ) ; extern void gunner_idlesound ( edict_t * self ) ; extern void SP_monster_gladiator ( edict_t * self ) ; extern void gladiator_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void gladiator_dead ( edict_t * self ) ; extern void gladiator_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void gladiator_attack ( edict_t * self ) ; extern void GladiatorGun ( edict_t * self ) ; extern void gladiator_melee ( edict_t * self ) ; extern void GaldiatorMelee ( edict_t * self ) ; extern void gladiator_run ( edict_t * self ) ; extern void gladiator_walk ( edict_t * self ) ; extern void gladiator_stand ( edict_t * self ) ; extern void gladiator_cleaver_swing ( edict_t * self ) ; extern void gladiator_search ( edict_t * self ) ; extern void gladiator_sight ( edict_t * self , edict_t * other ) ; extern void gladiator_idle ( edict_t * self ) ; extern void SP_monster_flyer ( edict_t * self ) ; extern void flyer_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void flyer_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void flyer_check_melee ( edict_t * self ) ; extern void flyer_melee ( edict_t * self ) ; extern void flyer_nextmove ( edict_t * self ) ; extern void flyer_setstart ( edict_t * self ) ; extern void flyer_attack ( edict_t * self ) ; extern void flyer_loop_melee ( edict_t * self ) ; extern void flyer_slash_right ( edict_t * self ) ; extern void flyer_slash_left ( edict_t * self ) ; extern void flyer_fireright ( edict_t * self ) ; extern void flyer_fireleft ( edict_t * self ) ; extern void flyer_fire ( edict_t * self , int flash_number ) ; extern void flyer_start ( edict_t * self ) ; extern void flyer_stop ( edict_t * self ) ; extern void flyer_stand ( edict_t * self ) ; extern void flyer_walk ( edict_t * self ) ; extern void flyer_run ( edict_t * self ) ; extern void flyer_pop_blades ( edict_t * self ) ; extern void flyer_idle ( edict_t * self ) ; extern void flyer_sight ( edict_t * self , edict_t * other ) ; extern void SP_monster_floater ( edict_t * self ) ; extern void floater_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void floater_dead ( edict_t * self ) ; extern void floater_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void floater_melee ( edict_t * self ) ; extern void floater_attack ( edict_t * self ) ; extern void floater_zap ( edict_t * self ) ; extern void floater_wham ( edict_t * self ) ; extern void floater_walk ( edict_t * self ) ; extern void floater_run ( edict_t * self ) ; extern void floater_stand ( edict_t * self ) ; extern void floater_fire_blaster ( edict_t * self ) ; extern void floater_idle ( edict_t * self ) ; extern void floater_sight ( edict_t * self , edict_t * other ) ; extern void SP_monster_flipper ( edict_t * self ) ; extern void flipper_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void flipper_sight ( edict_t * self , edict_t * other ) ; extern void flipper_dead ( edict_t * self ) ; extern void flipper_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void flipper_melee ( edict_t * self ) ; extern void flipper_preattack ( edict_t * self ) ; extern void flipper_bite ( edict_t * self ) ; extern void flipper_start_run ( edict_t * self ) ; extern void flipper_walk ( edict_t * self ) ; extern void flipper_run ( edict_t * self ) ; extern void flipper_run_loop ( edict_t * self ) ; extern void flipper_stand ( edict_t * self ) ; extern void SP_monster_chick ( edict_t * self ) ; extern void chick_sight ( edict_t * self , edict_t * other ) ; extern void chick_attack ( edict_t * self ) ; extern void chick_melee ( edict_t * self ) ; extern void chick_slash ( edict_t * self ) ; extern void chick_reslash ( edict_t * self ) ; extern void chick_attack1 ( edict_t * self ) ; extern void chick_rerocket ( edict_t * self ) ; extern void ChickReload ( edict_t * self ) ; extern void Chick_PreAttack1 ( edict_t * self ) ; extern void ChickRocket ( edict_t * self ) ; extern void ChickSlash ( edict_t * self ) ; extern void chick_dodge ( edict_t * self , edict_t * attacker , float eta ) ; extern void chick_duck_up ( edict_t * self ) ; extern void chick_duck_hold ( edict_t * self ) ; extern void chick_duck_down ( edict_t * self ) ; extern void chick_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void chick_dead ( edict_t * self ) ; extern void chick_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void chick_run ( edict_t * self ) ; extern void chick_walk ( edict_t * self ) ; extern void chick_stand ( edict_t * self ) ; extern void chick_fidget ( edict_t * self ) ; extern void ChickMoan ( edict_t * self ) ; extern void SP_monster_brain ( edict_t * self ) ; extern void brain_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void brain_dead ( edict_t * self ) ; extern void brain_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void brain_run ( edict_t * self ) ; extern void brain_melee ( edict_t * self ) ; extern void brain_chest_closed ( edict_t * self ) ; extern void brain_tentacle_attack ( edict_t * self ) ; extern void brain_chest_open ( edict_t * self ) ; extern void brain_hit_left ( edict_t * self ) ; extern void brain_swing_left ( edict_t * self ) ; extern void brain_hit_right ( edict_t * self ) ; extern void brain_swing_right ( edict_t * self ) ; extern void brain_dodge ( edict_t * self , edict_t * attacker , float eta ) ; extern void brain_duck_up ( edict_t * self ) ; extern void brain_duck_hold ( edict_t * self ) ; extern void brain_duck_down ( edict_t * self ) ; extern void brain_walk ( edict_t * self ) ; extern void brain_idle ( edict_t * self ) ; extern void brain_stand ( edict_t * self ) ; extern void brain_search ( edict_t * self ) ; extern void brain_sight ( edict_t * self , edict_t * other ) ; extern void MakronToss ( edict_t * self ) ; extern void MakronSpawn ( edict_t * self ) ; extern void SP_monster_makron ( edict_t * self ) ; extern void MakronPrecache ( void ) ; extern qboolean Makron_CheckAttack ( edict_t * self ) ; extern void makron_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void makron_dead ( edict_t * self ) ; extern void makron_torso ( edict_t * ent ) ; extern void makron_torso_think ( edict_t * self ) ; extern void makron_attack ( edict_t * self ) ; extern void makron_sight ( edict_t * self , edict_t * other ) ; extern void makron_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void MakronHyperblaster ( edict_t * self ) ; extern void MakronRailgun ( edict_t * self ) ; extern void MakronSaveloc ( edict_t * self ) ; extern void makronBFG ( edict_t * self ) ; extern void makron_run ( edict_t * self ) ; extern void makron_walk ( edict_t * self ) ; extern void makron_prerailgun ( edict_t * self ) ; extern void makron_brainsplorch ( edict_t * self ) ; extern void makron_step_right ( edict_t * self ) ; extern void makron_step_left ( edict_t * self ) ; extern void makron_popup ( edict_t * self ) ; extern void makron_hit ( edict_t * self ) ; extern void makron_stand ( edict_t * self ) ; extern void makron_taunt ( edict_t * self ) ; extern void SP_monster_jorg ( edict_t * self ) ; extern qboolean Jorg_CheckAttack ( edict_t * self ) ; extern void jorg_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void jorg_dead ( edict_t * self ) ; extern void jorg_attack ( edict_t * self ) ; extern void jorg_firebullet ( edict_t * self ) ; extern void jorg_firebullet_left ( edict_t * self ) ; extern void jorg_firebullet_right ( edict_t * self ) ; extern void jorgBFG ( edict_t * self ) ; extern void jorg_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void jorg_attack1 ( edict_t * self ) ; extern void jorg_reattack1 ( edict_t * self ) ; extern void jorg_run ( edict_t * self ) ; extern void jorg_walk ( edict_t * self ) ; extern void jorg_stand ( edict_t * self ) ; extern void jorg_step_right ( edict_t * self ) ; extern void jorg_step_left ( edict_t * self ) ; extern void jorg_death_hit ( edict_t * self ) ; extern void jorg_idle ( edict_t * self ) ; extern void jorg_search ( edict_t * self ) ; extern void SP_monster_boss3_stand ( edict_t * self ) ; extern void Think_Boss3Stand ( edict_t * ent ) ; extern void Use_Boss3 ( edict_t * ent , edict_t * other , edict_t * activator ) ; extern void SP_monster_boss2 ( edict_t * self ) ; extern qboolean Boss2_CheckAttack ( edict_t * self ) ; extern void boss2_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void boss2_dead ( edict_t * self ) ; extern void boss2_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void boss2_reattack_mg ( edict_t * self ) ; extern void boss2_attack_mg ( edict_t * self ) ; extern void boss2_attack ( edict_t * self ) ; extern void boss2_walk ( edict_t * self ) ; extern void boss2_run ( edict_t * self ) ; extern void boss2_stand ( edict_t * self ) ; extern void Boss2MachineGun ( edict_t * self ) ; extern void boss2_firebullet_left ( edict_t * self ) ; extern void boss2_firebullet_right ( edict_t * self ) ; extern void Boss2Rocket ( edict_t * self ) ; extern void boss2_search ( edict_t * self ) ; extern void SP_monster_berserk ( edict_t * self ) ; extern void berserk_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void berserk_dead ( edict_t * self ) ; extern void berserk_pain ( edict_t * self , edict_t * other , float kick , int damage ) ; extern void berserk_melee ( edict_t * self ) ; extern void berserk_strike ( edict_t * self ) ; extern void berserk_attack_club ( edict_t * self ) ; extern void berserk_swing ( edict_t * self ) ; extern void berserk_attack_spike ( edict_t * self ) ; extern void berserk_run ( edict_t * self ) ; extern void berserk_walk ( edict_t * self ) ; extern void berserk_fidget ( edict_t * self ) ; extern void berserk_stand ( edict_t * self ) ; extern void berserk_search ( edict_t * self ) ; extern void berserk_sight ( edict_t * self , edict_t * other ) ; extern void fire_bfg ( edict_t * self , vec3_t start , vec3_t dir , int damage , int speed , float damage_radius ) ; extern void bfg_think ( edict_t * self ) ; extern void bfg_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void bfg_explode ( edict_t * self ) ; extern void fire_rail ( edict_t * self , vec3_t start , vec3_t aimdir , int damage , int kick ) ; extern void fire_rocket ( edict_t * self , vec3_t start , vec3_t dir , int damage , int speed , float damage_radius , int radius_damage ) ; extern void rocket_touch ( edict_t * ent , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void fire_grenade2 ( edict_t * self , vec3_t start , vec3_t aimdir , int damage , int speed , float timer , float damage_radius , qboolean held ) ; extern void fire_grenade ( edict_t * self , vec3_t start , vec3_t aimdir , int damage , int speed , float timer , float damage_radius ) ; extern void Grenade_Touch ( edict_t * ent , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void Grenade_Explode ( edict_t * ent ) ; extern void fire_blaster ( edict_t * self , vec3_t start , vec3_t dir , int damage , int speed , int effect , qboolean hyper ) ; extern void blaster_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void fire_shotgun ( edict_t * self , vec3_t start , vec3_t aimdir , int damage , int kick , int hspread , int vspread , int count , int mod ) ; extern void fire_bullet ( edict_t * self , vec3_t start , vec3_t aimdir , int damage , int kick , int hspread , int vspread , int mod ) ; extern void fire_lead ( edict_t * self , vec3_t start , vec3_t aimdir , int damage , int kick , int te_impact , int hspread , int vspread , int mod ) ; extern qboolean fire_hit ( edict_t * self , vec3_t aim , int damage , int kick ) ; extern void check_dodge ( edict_t * self , vec3_t start , vec3_t dir , int speed ) ; extern qboolean KillBox ( edict_t * ent ) ; extern void G_TouchSolids ( edict_t * ent ) ; extern void G_TouchTriggers ( edict_t * ent ) ; extern void G_FreeEdict ( edict_t * ed ) ; extern edict_t * G_Spawn ( void ) ; extern void G_InitEdict ( edict_t * e ) ; extern char * G_CopyString ( char * in ) ; extern void vectoangles ( vec3_t value1 , vec3_t angles ) ; extern float vectoyaw ( vec3_t vec ) ; extern void G_SetMovedir ( vec3_t angles , vec3_t movedir ) ; extern char * vtos ( vec3_t v ) ; extern float * tv ( float x , float y , float z ) ; extern void G_UseTargets ( edict_t * ent , edict_t * activator ) ; extern void Think_Delay ( edict_t * ent ) ; extern edict_t * G_PickTarget ( char * targetname ) ; extern edict_t * findradius ( edict_t * from , vec3_t org , float rad ) ; extern edict_t * G_Find ( edict_t * from , int fieldofs , char * match ) ; extern void G_ProjectSource ( vec3_t point , vec3_t distance , vec3_t forward , vec3_t right , vec3_t result ) ; extern void SP_turret_driver ( edict_t * self ) ; extern void turret_driver_link ( edict_t * self ) ; extern void turret_driver_think ( edict_t * self ) ; extern void turret_driver_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void SP_turret_base ( edict_t * self ) ; extern void SP_turret_breach ( edict_t * self ) ; extern void turret_breach_finish_init ( edict_t * self ) ; extern void turret_breach_think ( edict_t * self ) ; extern void turret_breach_fire ( edict_t * self ) ; extern void turret_blocked ( edict_t * self , edict_t * other ) ; extern float SnapToEights ( float x ) ; extern void AnglesNormalize ( vec3_t vec ) ; extern void SP_trigger_monsterjump ( edict_t * self ) ; extern void trigger_monsterjump_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void SP_trigger_gravity ( edict_t * self ) ; extern void trigger_gravity_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void SP_trigger_hurt ( edict_t * self ) ; extern void hurt_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void hurt_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void SP_trigger_push ( edict_t * self ) ; extern void trigger_push_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void SP_trigger_always ( edict_t * ent ) ; extern void SP_trigger_counter ( edict_t * self ) ; extern void trigger_counter_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_trigger_key ( edict_t * self ) ; extern void trigger_key_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_trigger_relay ( edict_t * self ) ; extern void trigger_relay_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_trigger_once ( edict_t * ent ) ; extern void SP_trigger_multiple ( edict_t * ent ) ; extern void trigger_enable ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void Touch_Multi ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void Use_Multi ( edict_t * ent , edict_t * other , edict_t * activator ) ; extern void multi_trigger ( edict_t * ent ) ; extern void multi_wait ( edict_t * ent ) ; extern void InitTrigger ( edict_t * self ) ; extern void SP_target_earthquake ( edict_t * self ) ; extern void target_earthquake_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void target_earthquake_think ( edict_t * self ) ; extern void SP_target_lightramp ( edict_t * self ) ; extern void target_lightramp_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void target_lightramp_think ( edict_t * self ) ; extern void SP_target_laser ( edict_t * self ) ; extern void target_laser_start ( edict_t * self ) ; extern void target_laser_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void target_laser_off ( edict_t * self ) ; extern void target_laser_on ( edict_t * self ) ; extern void target_laser_think ( edict_t * self ) ; extern void SP_target_crosslevel_target ( edict_t * self ) ; extern void target_crosslevel_target_think ( edict_t * self ) ; extern void SP_target_crosslevel_trigger ( edict_t * self ) ; extern void trigger_crosslevel_trigger_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_target_blaster ( edict_t * self ) ; extern void use_target_blaster ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_target_spawner ( edict_t * self ) ; extern void use_target_spawner ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_target_splash ( edict_t * self ) ; extern void use_target_splash ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_target_changelevel ( edict_t * ent ) ; extern void use_target_changelevel ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_target_explosion ( edict_t * ent ) ; extern void use_target_explosion ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void target_explosion_explode ( edict_t * self ) ; extern void SP_target_goal ( edict_t * ent ) ; extern void use_target_goal ( edict_t * ent , edict_t * other , edict_t * activator ) ; extern void SP_target_secret ( edict_t * ent ) ; extern void use_target_secret ( edict_t * ent , edict_t * other , edict_t * activator ) ; extern void SP_target_help ( edict_t * ent ) ; extern void Use_Target_Help ( edict_t * ent , edict_t * other , edict_t * activator ) ; extern void SP_target_speaker ( edict_t * ent ) ; extern void Use_Target_Speaker ( edict_t * ent , edict_t * other , edict_t * activator ) ; extern void SP_target_temp_entity ( edict_t * ent ) ; extern void Use_Target_Tent ( edict_t * ent , edict_t * other , edict_t * activator ) ; extern void ServerCommand ( void ) ; extern void SVCmd_WriteIP_f ( void ) ; extern void SVCmd_ListIP_f ( void ) ; extern void SVCmd_RemoveIP_f ( void ) ; extern void SVCmd_AddIP_f ( void ) ; extern qboolean SV_FilterPacket ( char * from ) ; extern void Svcmd_Test_f ( void ) ; extern void SP_worldspawn ( edict_t * ent ) ; extern void SpawnEntities ( const char * mapname , char * entities , const char * spawnpoint ) ; extern void G_FindTeams ( void ) ; extern char * ED_ParseEdict ( char * data , edict_t * ent ) ; extern void ED_ParseField ( const char * key , const char * value , edict_t * ent ) ; extern char * ED_NewString ( const char * string ) ; extern void ED_CallSpawn ( edict_t * ent ) ; extern void G_RunEntity ( edict_t * ent ) ; extern void SV_Physics_Step ( edict_t * ent ) ; extern void SV_AddRotationalFriction ( edict_t * ent ) ; extern void SV_Physics_Toss ( edict_t * ent ) ; extern void SV_Physics_Noclip ( edict_t * ent ) ; extern void SV_Physics_None ( edict_t * ent ) ; extern void SV_Physics_Pusher ( edict_t * ent ) ; extern qboolean SV_Push ( edict_t * pusher , vec3_t move , vec3_t amove ) ; extern trace_t SV_PushEntity ( edict_t * ent , vec3_t push ) ; extern void RealBoundingBox ( edict_t * ent , vec3_t mins , vec3_t maxs ) ; extern void SV_AddGravity ( edict_t * ent ) ; extern int SV_FlyMove ( edict_t * ent , float time , int mask ) ; extern int ClipVelocity ( vec3_t in , vec3_t normal , vec3_t out , float overbounce ) ; extern void SV_Impact ( edict_t * e1 , trace_t * trace ) ; extern qboolean SV_RunThink ( edict_t * ent ) ; extern void SV_CheckVelocity ( edict_t * ent ) ; extern edict_t * SV_TestEntityPosition ( edict_t * ent ) ; extern void swimmonster_start ( edict_t * self ) ; extern void swimmonster_start_go ( edict_t * self ) ; extern void flymonster_start ( edict_t * self ) ; extern void flymonster_start_go ( edict_t * self ) ; extern void walkmonster_start ( edict_t * self ) ; extern void walkmonster_start_go ( edict_t * self ) ; extern void monster_start_go ( edict_t * self ) ; extern qboolean monster_start ( edict_t * self ) ; extern void monster_death_use ( edict_t * self ) ; extern void monster_triggered_start ( edict_t * self ) ; extern void monster_triggered_spawn_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void monster_triggered_spawn ( edict_t * self ) ; extern void monster_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void monster_think ( edict_t * self ) ; extern void M_MoveFrame ( edict_t * self ) ; extern void M_SetEffects ( edict_t * ent ) ; extern void M_droptofloor ( edict_t * ent ) ; extern void M_WorldEffects ( edict_t * ent ) ; extern void M_CatagorizePosition ( edict_t * ent ) ; extern void M_CheckGround ( edict_t * ent ) ; extern void AttackFinished ( edict_t * self , float time ) ; extern void M_FlyCheck ( edict_t * self ) ; extern void M_FliesOn ( edict_t * self ) ; extern void M_FliesOff ( edict_t * self ) ; extern void monster_fire_bfg ( edict_t * self , vec3_t start , vec3_t aimdir , int damage , int speed , int kick , float damage_radius , int flashtype ) ; extern void monster_fire_railgun ( edict_t * self , vec3_t start , vec3_t aimdir , int damage , int kick , int flashtype ) ; extern void monster_fire_rocket ( edict_t * self , vec3_t start , vec3_t dir , int damage , int speed , int flashtype ) ; extern void monster_fire_grenade ( edict_t * self , vec3_t start , vec3_t aimdir , int damage , int speed , int flashtype ) ; extern void monster_fire_blaster ( edict_t * self , vec3_t start , vec3_t dir , int damage , int speed , int flashtype , int effect ) ; extern void monster_fire_shotgun ( edict_t * self , vec3_t start , vec3_t aimdir , int damage , int kick , int hspread , int vspread , int count , int flashtype ) ; extern void monster_fire_bullet ( edict_t * self , vec3_t start , vec3_t dir , int damage , int kick , int hspread , int vspread , int flashtype ) ; extern void SP_misc_teleporter_dest ( edict_t * ent ) ; extern void SP_misc_teleporter ( edict_t * ent ) ; extern void teleporter_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void SP_func_clock ( edict_t * self ) ; extern void func_clock_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void func_clock_think ( edict_t * self ) ; extern void func_clock_format_countdown ( edict_t * self ) ; extern void func_clock_reset ( edict_t * self ) ; extern void SP_target_string ( edict_t * self ) ; extern void target_string_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_target_character ( edict_t * self ) ; extern void SP_misc_gib_head ( edict_t * ent ) ; extern void SP_misc_gib_leg ( edict_t * ent ) ; extern void SP_misc_gib_arm ( edict_t * ent ) ; extern void SP_light_mine2 ( edict_t * ent ) ; extern void SP_light_mine1 ( edict_t * ent ) ; extern void SP_misc_satellite_dish ( edict_t * ent ) ; extern void misc_satellite_dish_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void misc_satellite_dish_think ( edict_t * self ) ; extern void SP_misc_strogg_ship ( edict_t * ent ) ; extern void misc_strogg_ship_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_misc_viper_bomb ( edict_t * self ) ; extern void misc_viper_bomb_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void misc_viper_bomb_prethink ( edict_t * self ) ; extern void misc_viper_bomb_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void SP_misc_bigviper ( edict_t * ent ) ; extern void SP_misc_viper ( edict_t * ent ) ; extern void misc_viper_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_misc_deadsoldier ( edict_t * ent ) ; extern void misc_deadsoldier_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void SP_misc_banner ( edict_t * ent ) ; extern void misc_banner_think ( edict_t * ent ) ; extern void SP_monster_commander_body ( edict_t * self ) ; extern void commander_body_drop ( edict_t * self ) ; extern void commander_body_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void commander_body_think ( edict_t * self ) ; extern void SP_misc_easterchick2 ( edict_t * ent ) ; extern void misc_easterchick2_think ( edict_t * self ) ; extern void SP_misc_easterchick ( edict_t * ent ) ; extern void misc_easterchick_think ( edict_t * self ) ; extern void SP_misc_eastertank ( edict_t * ent ) ; extern void misc_eastertank_think ( edict_t * self ) ; extern void SP_misc_blackhole ( edict_t * ent ) ; extern void misc_blackhole_transparent ( edict_t * ent ) ; extern void misc_blackhole_think ( edict_t * self ) ; extern void misc_blackhole_use ( edict_t * ent , edict_t * other , edict_t * activator ) ; extern void SP_misc_explobox ( edict_t * self ) ; extern void barrel_delay ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void barrel_explode ( edict_t * self ) ; extern void barrel_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void SP_func_explosive ( edict_t * self ) ; extern void func_explosive_spawn ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void func_explosive_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void func_explosive_explode ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void SP_func_object ( edict_t * self ) ; extern void func_object_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void func_object_release ( edict_t * self ) ; extern void func_object_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void SP_func_wall ( edict_t * self ) ; extern void func_wall_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_light ( edict_t * self ) ; extern void light_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_info_notnull ( edict_t * self ) ; extern void SP_info_null ( edict_t * self ) ; extern void SP_viewthing ( edict_t * ent ) ; extern void TH_viewthing ( edict_t * ent ) ; extern void SP_point_combat ( edict_t * self ) ; extern void point_combat_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void SP_path_corner ( edict_t * self ) ; extern void path_corner_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void BecomeExplosion2 ( edict_t * self ) ; extern void BecomeExplosion1 ( edict_t * self ) ; extern void ThrowDebris ( edict_t * self , char * modelname , float speed , vec3_t origin ) ; extern void debris_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void ThrowClientHead ( edict_t * self , int damage ) ; extern void ThrowHead ( edict_t * self , char * gibname , int damage , int type ) ; extern void ThrowGib ( edict_t * self , char * gibname , int damage , int type ) ; extern void gib_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void gib_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void gib_think ( edict_t * self ) ; extern void ClipGibVelocity ( edict_t * ent ) ; extern void VelocityForDamage ( int damage , vec3_t v ) ; extern void SP_func_areaportal ( edict_t * ent ) ; extern void Use_Areaportal ( edict_t * ent , edict_t * other , edict_t * activator ) ; extern void G_RunFrame ( void ) ; extern void ExitLevel ( void ) ; extern void CheckDMRules ( void ) ; extern void CheckNeedPass ( void ) ; extern void EndDMLevel ( void ) ; extern edict_t * CreateTargetChangeLevel ( char * map ) ; extern void ClientEndServerFrames ( void ) ; extern void Com_Printf ( char * msg , ... ) ; extern void Sys_Error ( char * error , ... ) ; extern game_export_t * GetGameAPI ( game_import_t * import ) ; extern void ShutdownGame ( void ) ; extern void SetItemNames ( void ) ; extern void InitItems ( void ) ; extern void SP_item_health_mega ( edict_t * self ) ; extern void SP_item_health_large ( edict_t * self ) ; extern void SP_item_health_small ( edict_t * self ) ; extern void SP_item_health ( edict_t * self ) ; extern void SpawnItem ( edict_t * ent , gitem_t * item ) ; extern void PrecacheItem ( gitem_t * it ) ; extern void droptofloor ( edict_t * ent ) ; extern void Use_Item ( edict_t * ent , edict_t * other , edict_t * activator ) ; extern edict_t * Drop_Item ( edict_t * ent , gitem_t * item ) ; extern void drop_make_touchable ( edict_t * ent ) ; extern void drop_temp_touch ( edict_t * ent , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void Touch_Item ( edict_t * ent , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void Drop_PowerArmor ( edict_t * ent , gitem_t * item ) ; extern qboolean Pickup_PowerArmor ( edict_t * ent , edict_t * other ) ; extern void Use_PowerArmor ( edict_t * ent , gitem_t * item ) ; extern int PowerArmorType ( edict_t * ent ) ; extern qboolean Pickup_Armor ( edict_t * ent , edict_t * other ) ; extern int ArmorIndex ( edict_t * ent ) ; extern qboolean Pickup_Health ( edict_t * ent , edict_t * other ) ; extern void MegaHealth_think ( edict_t * self ) ; extern void Drop_Ammo ( edict_t * ent , gitem_t * item ) ; extern qboolean Pickup_Ammo ( edict_t * ent , edict_t * other ) ; extern qboolean Add_Ammo ( edict_t * ent , gitem_t * item , int count ) ; extern qboolean Pickup_Key ( edict_t * ent , edict_t * other ) ; extern void Use_Silencer ( edict_t * ent , gitem_t * item ) ; extern void Use_Invulnerability ( edict_t * ent , gitem_t * item ) ; extern void Use_Envirosuit ( edict_t * ent , gitem_t * item ) ; extern void Use_Breather ( edict_t * ent , gitem_t * item ) ; extern void Use_Quad ( edict_t * ent , gitem_t * item ) ; extern qboolean Pickup_Pack ( edict_t * ent , edict_t * other ) ; extern qboolean Pickup_Bandolier ( edict_t * ent , edict_t * other ) ; extern qboolean Pickup_AncientHead ( edict_t * ent , edict_t * other ) ; extern qboolean Pickup_Adrenaline ( edict_t * ent , edict_t * other ) ; extern void Drop_General ( edict_t * ent , gitem_t * item ) ; extern qboolean Pickup_Powerup ( edict_t * ent , edict_t * other ) ; extern void SetRespawn ( edict_t * ent , float delay ) ; extern void DoRespawn ( edict_t * ent ) ; extern gitem_t * FindItem ( char * pickup_name ) ; extern gitem_t * FindItemByClassname ( char * classname ) ; extern gitem_t * GetItemByIndex ( int index ) ; extern void SP_func_killbox ( edict_t * ent ) ; extern void use_killbox ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_func_door_secret ( edict_t * ent ) ; extern void door_secret_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void door_secret_blocked ( edict_t * self , edict_t * other ) ; extern void door_secret_done ( edict_t * self ) ; extern void door_secret_move6 ( edict_t * self ) ; extern void door_secret_move5 ( edict_t * self ) ; extern void door_secret_move4 ( edict_t * self ) ; extern void door_secret_move3 ( edict_t * self ) ; extern void door_secret_move2 ( edict_t * self ) ; extern void door_secret_move1 ( edict_t * self ) ; extern void door_secret_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_func_conveyor ( edict_t * self ) ; extern void func_conveyor_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_func_timer ( edict_t * self ) ; extern void func_timer_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void func_timer_think ( edict_t * self ) ; extern void SP_trigger_elevator ( edict_t * self ) ; extern void trigger_elevator_init ( edict_t * self ) ; extern void trigger_elevator_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void SP_func_train ( edict_t * self ) ; extern void train_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void func_train_find ( edict_t * self ) ; extern void train_resume ( edict_t * self ) ; extern void train_next ( edict_t * self ) ; extern void train_wait ( edict_t * self ) ; extern void train_blocked ( edict_t * self , edict_t * other ) ; extern void SP_func_water ( edict_t * self ) ; extern void SP_func_door_rotating ( edict_t * ent ) ; extern void SP_func_door ( edict_t * ent ) ; extern void door_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void door_killed ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void door_blocked ( edict_t * self , edict_t * other ) ; extern void Think_SpawnDoorTrigger ( edict_t * ent ) ; extern void Think_CalcMoveSpeed ( edict_t * self ) ; extern void Touch_DoorTrigger ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void door_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void door_go_up ( edict_t * self , edict_t * activator ) ; extern void door_go_down ( edict_t * self ) ; extern void door_hit_bottom ( edict_t * self ) ; extern void door_hit_top ( edict_t * self ) ; extern void door_use_areaportals ( edict_t * self , qboolean open ) ; extern void SP_func_button ( edict_t * ent ) ; extern void button_killed ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern void button_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void button_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void button_fire ( edict_t * self ) ; extern void button_wait ( edict_t * self ) ; extern void button_return ( edict_t * self ) ; extern void button_done ( edict_t * self ) ; extern void SP_func_rotating ( edict_t * ent ) ; extern void rotating_use ( edict_t * self , edict_t * other , edict_t * activator ) ; extern void rotating_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void rotating_blocked ( edict_t * self , edict_t * other ) ; extern void SP_func_plat ( edict_t * ent ) ; extern void plat_spawn_inside_trigger ( edict_t * ent ) ; extern void Touch_Plat_Center ( edict_t * ent , edict_t * other , cplane_t * plane , csurface_t * surf ) ; extern void Use_Plat ( edict_t * ent , edict_t * other , edict_t * activator ) ; extern void plat_blocked ( edict_t * self , edict_t * other ) ; extern void plat_go_up ( edict_t * ent ) ; extern void plat_go_down ( edict_t * ent ) ; extern void plat_hit_bottom ( edict_t * ent ) ; extern void plat_hit_top ( edict_t * ent ) ; extern void Think_AccelMove ( edict_t * ent ) ; extern void plat_Accelerate ( moveinfo_t * moveinfo ) ; extern void plat_CalcAcceleratedMove ( moveinfo_t * moveinfo ) ; extern void AngleMove_Calc ( edict_t * ent , void ( * func ) ( edict_t * ) ) ; extern void AngleMove_Begin ( edict_t * ent ) ; extern void AngleMove_Final ( edict_t * ent ) ; extern void AngleMove_Done ( edict_t * ent ) ; extern void Move_Calc ( edict_t * ent , vec3_t dest , void ( * func ) ( edict_t * ) ) ; extern void Move_Begin ( edict_t * ent ) ; extern void Move_Final ( edict_t * ent ) ; extern void Move_Done ( edict_t * ent ) ; extern void T_RadiusDamage ( edict_t * inflictor , edict_t * attacker , float damage , edict_t * ignore , float radius , int mod ) ; extern void T_Damage ( edict_t * targ , edict_t * inflictor , edict_t * attacker , vec3_t dir , vec3_t point , vec3_t normal , int damage , int knockback , int dflags , int mod ) ; extern void M_ReactToDamage ( edict_t * targ , edict_t * attacker ) ; extern int CheckArmor ( edict_t * ent , vec3_t point , vec3_t normal , int damage , int te_sparks , int dflags ) ; extern int CheckPowerArmor ( edict_t * ent , vec3_t point , vec3_t normal , int damage , int dflags ) ; extern void SpawnDamage ( int type , vec3_t origin , vec3_t normal ) ; extern void Killed ( edict_t * targ , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; extern qboolean CanDamage ( edict_t * targ , edict_t * inflictor ) ; extern void ClientCommand ( edict_t * ent ) ; extern void Cmd_PlayerList_f ( edict_t * ent ) ; extern void Cmd_Say_f ( edict_t * ent , qboolean team , qboolean arg0 ) ; extern void Cmd_Wave_f ( edict_t * ent ) ; extern void Cmd_Players_f ( edict_t * ent ) ; extern int PlayerSort ( void const * a , void const * b ) ; extern void Cmd_PutAway_f ( edict_t * ent ) ; extern void Cmd_Kill_f ( edict_t * ent ) ; extern void Cmd_InvDrop_f ( edict_t * ent ) ; extern void Cmd_WeapLast_f ( edict_t * ent ) ; extern void Cmd_WeapNext_f ( edict_t * ent ) ; extern void Cmd_WeapPrev_f ( edict_t * ent ) ; extern void Cmd_InvUse_f ( edict_t * ent ) ; extern void Cmd_Inven_f ( edict_t * ent ) ; extern void Cmd_Help_f ( edict_t * ent ) ; extern void Cmd_Score_f ( edict_t * ent ) ; extern void Cmd_Drop_f ( edict_t * ent ) ; extern void Cmd_Use_f ( edict_t * ent ) ; extern void Cmd_Noclip_f ( edict_t * ent ) ; extern void Cmd_Notarget_f ( edict_t * ent ) ; extern void Cmd_God_f ( edict_t * ent ) ; extern void Cmd_Give_f ( edict_t * ent ) ; extern void ValidateSelectedItem ( edict_t * ent ) ; extern void SelectPrevItem ( edict_t * ent , int itflags ) ; extern void SelectNextItem ( edict_t * ent , int itflags ) ; extern qboolean OnSameTeam ( edict_t * ent1 , edict_t * ent2 ) ; extern void GetChaseTarget ( edict_t * ent ) ; extern void ChasePrev ( edict_t * ent ) ; extern void ChaseNext ( edict_t * ent ) ; extern void UpdateChaseCam ( edict_t * ent ) ; extern void ai_run ( edict_t * self , float dist ) ; extern qboolean ai_checkattack ( edict_t * self ) ; extern void ai_run_slide ( edict_t * self , float distance ) ; extern void ai_run_missile ( edict_t * self ) ; extern void ai_run_melee ( edict_t * self ) ; extern qboolean M_CheckAttack ( edict_t * self ) ; extern qboolean FacingIdeal ( edict_t * self ) ; extern qboolean FindTarget ( edict_t * self ) ; extern void FoundTarget ( edict_t * self ) ; extern void HuntTarget ( edict_t * self ) ; extern qboolean infront ( edict_t * self , edict_t * other ) ; extern qboolean visible ( edict_t * self , edict_t * other ) ; extern int range ( edict_t * self , edict_t * other ) ; extern void ai_turn ( edict_t * self , float dist ) ; extern void ai_charge ( edict_t * self , float dist ) ; extern void ai_walk ( edict_t * self , float dist ) ; extern void ai_stand ( edict_t * self , float dist ) ; extern void ai_move ( edict_t * self , float dist ) ; extern void AI_SetSightClient ( void ) ; yquake2-QUAKE2_5_32/src/game/savegame/tables/levelfields.h0000644000175000017500000000242412615162034024032 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2011 Knightmare * Copyright (C) 2011 Yamagi Burmeister * * This program is free software; you can 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. * * ======================================================================= * * Fields inside a level to be saved. * * ======================================================================= */ {"changemap", LLOFS(changemap), F_LSTRING}, {"sight_client", LLOFS(sight_client), F_EDICT}, {"sight_entity", LLOFS(sight_entity), F_EDICT}, {"sound_entity", LLOFS(sound_entity), F_EDICT}, {"sound2_entity", LLOFS(sound2_entity), F_EDICT}, {NULL, 0, F_INT} yquake2-QUAKE2_5_32/src/game/savegame/tables/gamefunc_list.h0000644000175000017500000013251712615162034024363 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2011 Yamagi Burmeister * * This program is free software; you can 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. * * ======================================================================= * * Functionpointers to every function in the game.so. * * ======================================================================= */ {"ReadLevel", (byte *)ReadLevel}, {"ReadLevelLocals", (byte *)ReadLevelLocals}, {"ReadEdict", (byte *)ReadEdict}, {"WriteLevel", (byte *)WriteLevel}, {"WriteLevelLocals", (byte *)WriteLevelLocals}, {"WriteEdict", (byte *)WriteEdict}, {"ReadGame", (byte *)ReadGame}, {"WriteGame", (byte *)WriteGame}, {"ReadClient", (byte *)ReadClient}, {"WriteClient", (byte *)WriteClient}, {"ReadField", (byte *)ReadField}, {"WriteField2", (byte *)WriteField2}, {"WriteField1", (byte *)WriteField1}, {"FindMmoveByName", (byte *)FindMmoveByName}, {"GetMmoveByAddress", (byte *)GetMmoveByAddress}, {"FindFunctionByName", (byte *)FindFunctionByName}, {"GetFunctionByAddress", (byte *)GetFunctionByAddress}, {"InitGame", (byte *)InitGame}, {"Info_SetValueForKey", (byte *)Info_SetValueForKey}, {"Info_Validate", (byte *)Info_Validate}, {"Info_RemoveKey", (byte *)Info_RemoveKey}, {"Info_ValueForKey", (byte *)Info_ValueForKey}, {"Q_strlcat", (byte *)Q_strlcat}, {"Q_strlcpy", (byte *)Q_strlcpy}, {"Q_strlwr", (byte *)Q_strlwr}, {"Com_sprintf", (byte *)Com_sprintf}, {"Q_strcasecmp", (byte *)Q_strcasecmp}, {"Q_strncasecmp", (byte *)Q_strncasecmp}, {"Q_stricmp", (byte *)Q_stricmp}, {"Com_PageInMemory", (byte *)Com_PageInMemory}, {"COM_Parse", (byte *)COM_Parse}, {"va", (byte *)va}, {"Swap_Init", (byte *)Swap_Init}, {"FloatNoSwap", (byte *)FloatNoSwap}, {"FloatSwap", (byte *)FloatSwap}, {"LongNoSwap", (byte *)LongNoSwap}, {"LongSwap", (byte *)LongSwap}, {"ShortNoSwap", (byte *)ShortNoSwap}, {"ShortSwap", (byte *)ShortSwap}, {"LittleFloat", (byte *)LittleFloat}, {"BigFloat", (byte *)BigFloat}, {"LittleLong", (byte *)LittleLong}, {"BigLong", (byte *)BigLong}, {"LittleShort", (byte *)LittleShort}, {"BigShort", (byte *)BigShort}, {"COM_DefaultExtension", (byte *)COM_DefaultExtension}, {"COM_FilePath", (byte *)COM_FilePath}, {"COM_FileBase", (byte *)COM_FileBase}, {"COM_FileExtension", (byte *)COM_FileExtension}, {"COM_StripExtension", (byte *)COM_StripExtension}, {"COM_SkipPath", (byte *)COM_SkipPath}, {"Q_log2", (byte *)Q_log2}, {"VectorScale", (byte *)VectorScale}, {"VectorInverse", (byte *)VectorInverse}, {"VectorLength", (byte *)VectorLength}, {"CrossProduct", (byte *)CrossProduct}, {"_VectorCopy", (byte *)_VectorCopy}, {"_VectorAdd", (byte *)_VectorAdd}, {"_VectorSubtract", (byte *)_VectorSubtract}, {"_DotProduct", (byte *)_DotProduct}, {"VectorMA", (byte *)VectorMA}, {"VectorNormalize2", (byte *)VectorNormalize2}, {"VectorNormalize", (byte *)VectorNormalize}, {"VectorCompare", (byte *)VectorCompare}, {"AddPointToBounds", (byte *)AddPointToBounds}, {"ClearBounds", (byte *)ClearBounds}, {"BoxOnPlaneSide2", (byte *)BoxOnPlaneSide2}, {"anglemod", (byte *)anglemod}, {"LerpAngle", (byte *)LerpAngle}, {"Q_fabs", (byte *)Q_fabs}, {"R_ConcatTransforms", (byte *)R_ConcatTransforms}, {"R_ConcatRotations", (byte *)R_ConcatRotations}, {"PerpendicularVector", (byte *)PerpendicularVector}, {"ProjectPointOnPlane", (byte *)ProjectPointOnPlane}, {"AngleVectors2", (byte *)AngleVectors2}, {"AngleVectors", (byte *)AngleVectors}, {"RotatePointAroundVector", (byte *)RotatePointAroundVector}, {"Weapon_BFG", (byte *)Weapon_BFG}, {"weapon_bfg_fire", (byte *)weapon_bfg_fire}, {"Weapon_Railgun", (byte *)Weapon_Railgun}, {"weapon_railgun_fire", (byte *)weapon_railgun_fire}, {"Weapon_SuperShotgun", (byte *)Weapon_SuperShotgun}, {"weapon_supershotgun_fire", (byte *)weapon_supershotgun_fire}, {"Weapon_Shotgun", (byte *)Weapon_Shotgun}, {"weapon_shotgun_fire", (byte *)weapon_shotgun_fire}, {"Weapon_Chaingun", (byte *)Weapon_Chaingun}, {"Chaingun_Fire", (byte *)Chaingun_Fire}, {"Weapon_Machinegun", (byte *)Weapon_Machinegun}, {"Machinegun_Fire", (byte *)Machinegun_Fire}, {"Weapon_HyperBlaster", (byte *)Weapon_HyperBlaster}, {"Weapon_HyperBlaster_Fire", (byte *)Weapon_HyperBlaster_Fire}, {"Weapon_Blaster", (byte *)Weapon_Blaster}, {"Weapon_Blaster_Fire", (byte *)Weapon_Blaster_Fire}, {"Blaster_Fire", (byte *)Blaster_Fire}, {"Weapon_RocketLauncher", (byte *)Weapon_RocketLauncher}, {"Weapon_RocketLauncher_Fire", (byte *)Weapon_RocketLauncher_Fire}, {"Weapon_GrenadeLauncher", (byte *)Weapon_GrenadeLauncher}, {"weapon_grenadelauncher_fire", (byte *)weapon_grenadelauncher_fire}, {"Weapon_Grenade", (byte *)Weapon_Grenade}, {"weapon_grenade_fire", (byte *)weapon_grenade_fire}, {"Weapon_Generic", (byte *)Weapon_Generic}, {"Drop_Weapon", (byte *)Drop_Weapon}, {"Use_Weapon", (byte *)Use_Weapon}, {"Think_Weapon", (byte *)Think_Weapon}, {"NoAmmoWeaponChange", (byte *)NoAmmoWeaponChange}, {"ChangeWeapon", (byte *)ChangeWeapon}, {"Pickup_Weapon", (byte *)Pickup_Weapon}, {"PlayerNoise", (byte *)PlayerNoise}, {"P_ProjectSource", (byte *)P_ProjectSource}, {"ClientEndServerFrame", (byte *)ClientEndServerFrame}, {"G_SetClientFrame", (byte *)G_SetClientFrame}, {"G_SetClientSound", (byte *)G_SetClientSound}, {"G_SetClientEvent", (byte *)G_SetClientEvent}, {"G_SetClientEffects", (byte *)G_SetClientEffects}, {"P_WorldEffects", (byte *)P_WorldEffects}, {"P_FallingDamage", (byte *)P_FallingDamage}, {"SV_CalcBlend", (byte *)SV_CalcBlend}, {"SV_AddBlend", (byte *)SV_AddBlend}, {"SV_CalcGunOffset", (byte *)SV_CalcGunOffset}, {"SV_CalcViewOffset", (byte *)SV_CalcViewOffset}, {"P_DamageFeedback", (byte *)P_DamageFeedback}, {"SV_CalcRoll", (byte *)SV_CalcRoll}, {"PlayerTrail_LastSpot", (byte *)PlayerTrail_LastSpot}, {"PlayerTrail_PickNext", (byte *)PlayerTrail_PickNext}, {"PlayerTrail_PickFirst", (byte *)PlayerTrail_PickFirst}, {"PlayerTrail_New", (byte *)PlayerTrail_New}, {"PlayerTrail_Add", (byte *)PlayerTrail_Add}, {"PlayerTrail_Init", (byte *)PlayerTrail_Init}, {"G_SetSpectatorStats", (byte *)G_SetSpectatorStats}, {"G_CheckChaseStats", (byte *)G_CheckChaseStats}, {"G_SetStats", (byte *)G_SetStats}, {"InventoryMessage", (byte *)InventoryMessage}, {"HelpComputerMessage", (byte *)HelpComputerMessage}, {"DeathmatchScoreboardMessage", (byte *)DeathmatchScoreboardMessage}, {"BeginIntermission", (byte *)BeginIntermission}, {"MoveClientToIntermission", (byte *)MoveClientToIntermission}, {"ClientBeginServerFrame", (byte *)ClientBeginServerFrame}, {"ClientThink", (byte *)ClientThink}, {"PrintPmove", (byte *)PrintPmove}, {"CheckBlock", (byte *)CheckBlock}, {"PM_trace", (byte *)PM_trace}, {"ClientDisconnect", (byte *)ClientDisconnect}, {"ClientConnect", (byte *)ClientConnect}, {"ClientUserinfoChanged", (byte *)ClientUserinfoChanged}, {"ClientBegin", (byte *)ClientBegin}, {"ClientBeginDeathmatch", (byte *)ClientBeginDeathmatch}, {"PutClientInServer", (byte *)PutClientInServer}, {"spectator_respawn", (byte *)spectator_respawn}, {"respawn", (byte *)respawn}, {"CopyToBodyQue", (byte *)CopyToBodyQue}, {"body_die", (byte *)body_die}, {"InitBodyQue", (byte *)InitBodyQue}, {"SelectSpawnPoint", (byte *)SelectSpawnPoint}, {"SelectCoopSpawnPoint", (byte *)SelectCoopSpawnPoint}, {"SelectDeathmatchSpawnPoint", (byte *)SelectDeathmatchSpawnPoint}, {"SelectFarthestDeathmatchSpawnPoint", (byte *)SelectFarthestDeathmatchSpawnPoint}, {"SelectRandomDeathmatchSpawnPoint", (byte *)SelectRandomDeathmatchSpawnPoint}, {"PlayersRangeFromSpot", (byte *)PlayersRangeFromSpot}, {"FetchClientEntData", (byte *)FetchClientEntData}, {"SaveClientData", (byte *)SaveClientData}, {"InitClientResp", (byte *)InitClientResp}, {"InitClientPersistant", (byte *)InitClientPersistant}, {"player_die", (byte *)player_die}, {"LookAtKiller", (byte *)LookAtKiller}, {"TossClientWeapon", (byte *)TossClientWeapon}, {"ClientObituary", (byte *)ClientObituary}, {"IsNeutral", (byte *)IsNeutral}, {"IsFemale", (byte *)IsFemale}, {"player_pain", (byte *)player_pain}, {"SP_info_player_intermission", (byte *)SP_info_player_intermission}, {"SP_info_player_coop", (byte *)SP_info_player_coop}, {"SP_info_player_deathmatch", (byte *)SP_info_player_deathmatch}, {"SP_info_player_start", (byte *)SP_info_player_start}, {"SP_CreateUnnamedSpawn", (byte *)SP_CreateUnnamedSpawn}, {"SP_CreateCoopSpots", (byte *)SP_CreateCoopSpots}, {"SP_FixCoopSpots", (byte *)SP_FixCoopSpots}, {"SP_monster_tank", (byte *)SP_monster_tank}, {"tank_die", (byte *)tank_die}, {"tank_dead", (byte *)tank_dead}, {"tank_attack", (byte *)tank_attack}, {"tank_doattack_rocket", (byte *)tank_doattack_rocket}, {"tank_refire_rocket", (byte *)tank_refire_rocket}, {"tank_poststrike", (byte *)tank_poststrike}, {"tank_reattack_blaster", (byte *)tank_reattack_blaster}, {"TankMachineGun", (byte *)TankMachineGun}, {"TankRocket", (byte *)TankRocket}, {"TankStrike", (byte *)TankStrike}, {"TankBlaster", (byte *)TankBlaster}, {"tank_pain", (byte *)tank_pain}, {"tank_run", (byte *)tank_run}, {"tank_walk", (byte *)tank_walk}, {"tank_stand", (byte *)tank_stand}, {"tank_idle", (byte *)tank_idle}, {"tank_windup", (byte *)tank_windup}, {"tank_thud", (byte *)tank_thud}, {"tank_footstep", (byte *)tank_footstep}, {"tank_sight", (byte *)tank_sight}, {"SP_monster_supertank", (byte *)SP_monster_supertank}, {"supertank_die", (byte *)supertank_die}, {"BossExplode", (byte *)BossExplode}, {"supertank_dead", (byte *)supertank_dead}, {"supertank_attack", (byte *)supertank_attack}, {"supertankMachineGun", (byte *)supertankMachineGun}, {"supertankRocket", (byte *)supertankRocket}, {"supertank_pain", (byte *)supertank_pain}, {"supertank_reattack1", (byte *)supertank_reattack1}, {"supertank_run", (byte *)supertank_run}, {"supertank_walk", (byte *)supertank_walk}, {"supertank_forward", (byte *)supertank_forward}, {"supertank_stand", (byte *)supertank_stand}, {"supertank_search", (byte *)supertank_search}, {"TreadSound", (byte *)TreadSound}, {"SP_monster_soldier_ss", (byte *)SP_monster_soldier_ss}, {"SP_monster_soldier", (byte *)SP_monster_soldier}, {"SP_monster_soldier_light", (byte *)SP_monster_soldier_light}, {"SP_monster_soldier_x", (byte *)SP_monster_soldier_x}, {"soldier_die", (byte *)soldier_die}, {"soldier_dead", (byte *)soldier_dead}, {"soldier_fire7", (byte *)soldier_fire7}, {"soldier_fire6", (byte *)soldier_fire6}, {"soldier_dodge", (byte *)soldier_dodge}, {"soldier_duck_hold", (byte *)soldier_duck_hold}, {"soldier_sight", (byte *)soldier_sight}, {"soldier_attack", (byte *)soldier_attack}, {"soldier_attack6_refire", (byte *)soldier_attack6_refire}, {"soldier_fire8", (byte *)soldier_fire8}, {"soldier_fire4", (byte *)soldier_fire4}, {"soldier_attack3_refire", (byte *)soldier_attack3_refire}, {"soldier_fire3", (byte *)soldier_fire3}, {"soldier_duck_up", (byte *)soldier_duck_up}, {"soldier_duck_down", (byte *)soldier_duck_down}, {"soldier_attack2_refire2", (byte *)soldier_attack2_refire2}, {"soldier_attack2_refire1", (byte *)soldier_attack2_refire1}, {"soldier_fire2", (byte *)soldier_fire2}, {"soldier_attack1_refire2", (byte *)soldier_attack1_refire2}, {"soldier_attack1_refire1", (byte *)soldier_attack1_refire1}, {"soldier_fire1", (byte *)soldier_fire1}, {"soldier_fire", (byte *)soldier_fire}, {"soldier_pain", (byte *)soldier_pain}, {"soldier_run", (byte *)soldier_run}, {"soldier_walk", (byte *)soldier_walk}, {"soldier_walk1_random", (byte *)soldier_walk1_random}, {"soldier_stand", (byte *)soldier_stand}, {"soldier_cock", (byte *)soldier_cock}, {"soldier_idle", (byte *)soldier_idle}, {"SP_monster_parasite", (byte *)SP_monster_parasite}, {"parasite_die", (byte *)parasite_die}, {"parasite_dead", (byte *)parasite_dead}, {"parasite_attack", (byte *)parasite_attack}, {"parasite_drain_attack", (byte *)parasite_drain_attack}, {"parasite_drain_attack_ok", (byte *)parasite_drain_attack_ok}, {"parasite_pain", (byte *)parasite_pain}, {"parasite_walk", (byte *)parasite_walk}, {"parasite_start_walk", (byte *)parasite_start_walk}, {"parasite_run", (byte *)parasite_run}, {"parasite_start_run", (byte *)parasite_start_run}, {"parasite_stand", (byte *)parasite_stand}, {"parasite_idle", (byte *)parasite_idle}, {"parasite_refidget", (byte *)parasite_refidget}, {"parasite_do_fidget", (byte *)parasite_do_fidget}, {"parasite_end_fidget", (byte *)parasite_end_fidget}, {"parasite_search", (byte *)parasite_search}, {"parasite_scratch", (byte *)parasite_scratch}, {"parasite_tap", (byte *)parasite_tap}, {"parasite_sight", (byte *)parasite_sight}, {"parasite_reel_in", (byte *)parasite_reel_in}, {"parasite_launch", (byte *)parasite_launch}, {"SP_monster_mutant", (byte *)SP_monster_mutant}, {"mutant_die", (byte *)mutant_die}, {"mutant_dead", (byte *)mutant_dead}, {"mutant_pain", (byte *)mutant_pain}, {"mutant_checkattack", (byte *)mutant_checkattack}, {"mutant_check_jump", (byte *)mutant_check_jump}, {"mutant_check_melee", (byte *)mutant_check_melee}, {"mutant_jump", (byte *)mutant_jump}, {"mutant_check_landing", (byte *)mutant_check_landing}, {"mutant_jump_takeoff", (byte *)mutant_jump_takeoff}, {"mutant_jump_touch", (byte *)mutant_jump_touch}, {"mutant_melee", (byte *)mutant_melee}, {"mutant_check_refire", (byte *)mutant_check_refire}, {"mutant_hit_right", (byte *)mutant_hit_right}, {"mutant_hit_left", (byte *)mutant_hit_left}, {"mutant_run", (byte *)mutant_run}, {"mutant_walk", (byte *)mutant_walk}, {"mutant_walk_loop", (byte *)mutant_walk_loop}, {"mutant_idle", (byte *)mutant_idle}, {"mutant_idle_loop", (byte *)mutant_idle_loop}, {"mutant_stand", (byte *)mutant_stand}, {"mutant_swing", (byte *)mutant_swing}, {"mutant_search", (byte *)mutant_search}, {"mutant_sight", (byte *)mutant_sight}, {"mutant_step", (byte *)mutant_step}, {"M_walkmove", (byte *)M_walkmove}, {"M_MoveToGoal", (byte *)M_MoveToGoal}, {"SV_CloseEnough", (byte *)SV_CloseEnough}, {"SV_NewChaseDir", (byte *)SV_NewChaseDir}, {"SV_FixCheckBottom", (byte *)SV_FixCheckBottom}, {"SV_StepDirection", (byte *)SV_StepDirection}, {"M_ChangeYaw", (byte *)M_ChangeYaw}, {"SV_movestep", (byte *)SV_movestep}, {"M_CheckBottom", (byte *)M_CheckBottom}, {"SP_monster_medic", (byte *)SP_monster_medic}, {"medic_checkattack", (byte *)medic_checkattack}, {"medic_attack", (byte *)medic_attack}, {"medic_hook_retract", (byte *)medic_hook_retract}, {"medic_cable_attack", (byte *)medic_cable_attack}, {"medic_hook_launch", (byte *)medic_hook_launch}, {"medic_continue", (byte *)medic_continue}, {"medic_dodge", (byte *)medic_dodge}, {"medic_duck_up", (byte *)medic_duck_up}, {"medic_duck_hold", (byte *)medic_duck_hold}, {"medic_duck_down", (byte *)medic_duck_down}, {"medic_die", (byte *)medic_die}, {"medic_dead", (byte *)medic_dead}, {"medic_fire_blaster", (byte *)medic_fire_blaster}, {"medic_pain", (byte *)medic_pain}, {"medic_run", (byte *)medic_run}, {"medic_walk", (byte *)medic_walk}, {"medic_stand", (byte *)medic_stand}, {"medic_sight", (byte *)medic_sight}, {"medic_search", (byte *)medic_search}, {"medic_idle", (byte *)medic_idle}, {"medic_FindDeadMonster", (byte *)medic_FindDeadMonster}, {"SP_misc_insane", (byte *)SP_misc_insane}, {"insane_die", (byte *)insane_die}, {"insane_dead", (byte *)insane_dead}, {"insane_stand", (byte *)insane_stand}, {"insane_checkup", (byte *)insane_checkup}, {"insane_checkdown", (byte *)insane_checkdown}, {"insane_onground", (byte *)insane_onground}, {"insane_pain", (byte *)insane_pain}, {"insane_run", (byte *)insane_run}, {"insane_walk", (byte *)insane_walk}, {"insane_cross", (byte *)insane_cross}, {"insane_scream", (byte *)insane_scream}, {"insane_moan", (byte *)insane_moan}, {"insane_shake", (byte *)insane_shake}, {"insane_fist", (byte *)insane_fist}, {"SP_monster_infantry", (byte *)SP_monster_infantry}, {"infantry_attack", (byte *)infantry_attack}, {"infantry_smack", (byte *)infantry_smack}, {"infantry_swing", (byte *)infantry_swing}, {"infantry_fire", (byte *)infantry_fire}, {"infantry_cock_gun", (byte *)infantry_cock_gun}, {"infantry_dodge", (byte *)infantry_dodge}, {"infantry_duck_up", (byte *)infantry_duck_up}, {"infantry_duck_hold", (byte *)infantry_duck_hold}, {"infantry_duck_down", (byte *)infantry_duck_down}, {"infantry_die", (byte *)infantry_die}, {"infantry_dead", (byte *)infantry_dead}, {"infantry_sight", (byte *)infantry_sight}, {"InfantryMachineGun", (byte *)InfantryMachineGun}, {"infantry_pain", (byte *)infantry_pain}, {"infantry_run", (byte *)infantry_run}, {"infantry_walk", (byte *)infantry_walk}, {"infantry_fidget", (byte *)infantry_fidget}, {"infantry_stand", (byte *)infantry_stand}, {"SP_monster_hover", (byte *)SP_monster_hover}, {"hover_die", (byte *)hover_die}, {"hover_dead", (byte *)hover_dead}, {"hover_deadthink", (byte *)hover_deadthink}, {"hover_pain", (byte *)hover_pain}, {"hover_attack", (byte *)hover_attack}, {"hover_start_attack", (byte *)hover_start_attack}, {"hover_walk", (byte *)hover_walk}, {"hover_run", (byte *)hover_run}, {"hover_stand", (byte *)hover_stand}, {"hover_fire_blaster", (byte *)hover_fire_blaster}, {"hover_reattack", (byte *)hover_reattack}, {"hover_search", (byte *)hover_search}, {"hover_sight", (byte *)hover_sight}, {"SP_monster_gunner", (byte *)SP_monster_gunner}, {"gunner_refire_chain", (byte *)gunner_refire_chain}, {"gunner_fire_chain", (byte *)gunner_fire_chain}, {"gunner_attack", (byte *)gunner_attack}, {"GunnerGrenade", (byte *)GunnerGrenade}, {"GunnerFire", (byte *)GunnerFire}, {"gunner_opengun", (byte *)gunner_opengun}, {"gunner_dodge", (byte *)gunner_dodge}, {"gunner_duck_up", (byte *)gunner_duck_up}, {"gunner_duck_hold", (byte *)gunner_duck_hold}, {"gunner_duck_down", (byte *)gunner_duck_down}, {"gunner_die", (byte *)gunner_die}, {"gunner_dead", (byte *)gunner_dead}, {"gunner_pain", (byte *)gunner_pain}, {"gunner_runandshoot", (byte *)gunner_runandshoot}, {"gunner_run", (byte *)gunner_run}, {"gunner_walk", (byte *)gunner_walk}, {"gunner_stand", (byte *)gunner_stand}, {"gunner_fidget", (byte *)gunner_fidget}, {"gunner_search", (byte *)gunner_search}, {"gunner_sight", (byte *)gunner_sight}, {"gunner_idlesound", (byte *)gunner_idlesound}, {"SP_monster_gladiator", (byte *)SP_monster_gladiator}, {"gladiator_die", (byte *)gladiator_die}, {"gladiator_dead", (byte *)gladiator_dead}, {"gladiator_pain", (byte *)gladiator_pain}, {"gladiator_attack", (byte *)gladiator_attack}, {"GladiatorGun", (byte *)GladiatorGun}, {"gladiator_melee", (byte *)gladiator_melee}, {"GaldiatorMelee", (byte *)GaldiatorMelee}, {"gladiator_run", (byte *)gladiator_run}, {"gladiator_walk", (byte *)gladiator_walk}, {"gladiator_stand", (byte *)gladiator_stand}, {"gladiator_cleaver_swing", (byte *)gladiator_cleaver_swing}, {"gladiator_search", (byte *)gladiator_search}, {"gladiator_sight", (byte *)gladiator_sight}, {"gladiator_idle", (byte *)gladiator_idle}, {"SP_monster_flyer", (byte *)SP_monster_flyer}, {"flyer_die", (byte *)flyer_die}, {"flyer_pain", (byte *)flyer_pain}, {"flyer_check_melee", (byte *)flyer_check_melee}, {"flyer_melee", (byte *)flyer_melee}, {"flyer_nextmove", (byte *)flyer_nextmove}, {"flyer_setstart", (byte *)flyer_setstart}, {"flyer_attack", (byte *)flyer_attack}, {"flyer_loop_melee", (byte *)flyer_loop_melee}, {"flyer_slash_right", (byte *)flyer_slash_right}, {"flyer_slash_left", (byte *)flyer_slash_left}, {"flyer_fireright", (byte *)flyer_fireright}, {"flyer_fireleft", (byte *)flyer_fireleft}, {"flyer_fire", (byte *)flyer_fire}, {"flyer_start", (byte *)flyer_start}, {"flyer_stop", (byte *)flyer_stop}, {"flyer_stand", (byte *)flyer_stand}, {"flyer_walk", (byte *)flyer_walk}, {"flyer_run", (byte *)flyer_run}, {"flyer_pop_blades", (byte *)flyer_pop_blades}, {"flyer_idle", (byte *)flyer_idle}, {"flyer_sight", (byte *)flyer_sight}, {"SP_monster_floater", (byte *)SP_monster_floater}, {"floater_die", (byte *)floater_die}, {"floater_dead", (byte *)floater_dead}, {"floater_pain", (byte *)floater_pain}, {"floater_melee", (byte *)floater_melee}, {"floater_attack", (byte *)floater_attack}, {"floater_zap", (byte *)floater_zap}, {"floater_wham", (byte *)floater_wham}, {"floater_walk", (byte *)floater_walk}, {"floater_run", (byte *)floater_run}, {"floater_stand", (byte *)floater_stand}, {"floater_fire_blaster", (byte *)floater_fire_blaster}, {"floater_idle", (byte *)floater_idle}, {"floater_sight", (byte *)floater_sight}, {"SP_monster_flipper", (byte *)SP_monster_flipper}, {"flipper_die", (byte *)flipper_die}, {"flipper_sight", (byte *)flipper_sight}, {"flipper_dead", (byte *)flipper_dead}, {"flipper_pain", (byte *)flipper_pain}, {"flipper_melee", (byte *)flipper_melee}, {"flipper_preattack", (byte *)flipper_preattack}, {"flipper_bite", (byte *)flipper_bite}, {"flipper_start_run", (byte *)flipper_start_run}, {"flipper_walk", (byte *)flipper_walk}, {"flipper_run", (byte *)flipper_run}, {"flipper_run_loop", (byte *)flipper_run_loop}, {"flipper_stand", (byte *)flipper_stand}, {"SP_monster_chick", (byte *)SP_monster_chick}, {"chick_sight", (byte *)chick_sight}, {"chick_attack", (byte *)chick_attack}, {"chick_melee", (byte *)chick_melee}, {"chick_slash", (byte *)chick_slash}, {"chick_reslash", (byte *)chick_reslash}, {"chick_attack1", (byte *)chick_attack1}, {"chick_rerocket", (byte *)chick_rerocket}, {"ChickReload", (byte *)ChickReload}, {"Chick_PreAttack1", (byte *)Chick_PreAttack1}, {"ChickRocket", (byte *)ChickRocket}, {"ChickSlash", (byte *)ChickSlash}, {"chick_dodge", (byte *)chick_dodge}, {"chick_duck_up", (byte *)chick_duck_up}, {"chick_duck_hold", (byte *)chick_duck_hold}, {"chick_duck_down", (byte *)chick_duck_down}, {"chick_die", (byte *)chick_die}, {"chick_dead", (byte *)chick_dead}, {"chick_pain", (byte *)chick_pain}, {"chick_run", (byte *)chick_run}, {"chick_walk", (byte *)chick_walk}, {"chick_stand", (byte *)chick_stand}, {"chick_fidget", (byte *)chick_fidget}, {"ChickMoan", (byte *)ChickMoan}, {"SP_monster_brain", (byte *)SP_monster_brain}, {"brain_die", (byte *)brain_die}, {"brain_dead", (byte *)brain_dead}, {"brain_pain", (byte *)brain_pain}, {"brain_run", (byte *)brain_run}, {"brain_melee", (byte *)brain_melee}, {"brain_chest_closed", (byte *)brain_chest_closed}, {"brain_tentacle_attack", (byte *)brain_tentacle_attack}, {"brain_chest_open", (byte *)brain_chest_open}, {"brain_hit_left", (byte *)brain_hit_left}, {"brain_swing_left", (byte *)brain_swing_left}, {"brain_hit_right", (byte *)brain_hit_right}, {"brain_swing_right", (byte *)brain_swing_right}, {"brain_dodge", (byte *)brain_dodge}, {"brain_duck_up", (byte *)brain_duck_up}, {"brain_duck_hold", (byte *)brain_duck_hold}, {"brain_duck_down", (byte *)brain_duck_down}, {"brain_walk", (byte *)brain_walk}, {"brain_idle", (byte *)brain_idle}, {"brain_stand", (byte *)brain_stand}, {"brain_search", (byte *)brain_search}, {"brain_sight", (byte *)brain_sight}, {"MakronToss", (byte *)MakronToss}, {"MakronSpawn", (byte *)MakronSpawn}, {"SP_monster_makron", (byte *)SP_monster_makron}, {"MakronPrecache", (byte *)MakronPrecache}, {"Makron_CheckAttack", (byte *)Makron_CheckAttack}, {"makron_die", (byte *)makron_die}, {"makron_dead", (byte *)makron_dead}, {"makron_torso", (byte *)makron_torso}, {"makron_torso_think", (byte *)makron_torso_think}, {"makron_attack", (byte *)makron_attack}, {"makron_sight", (byte *)makron_sight}, {"makron_pain", (byte *)makron_pain}, {"MakronHyperblaster", (byte *)MakronHyperblaster}, {"MakronRailgun", (byte *)MakronRailgun}, {"MakronSaveloc", (byte *)MakronSaveloc}, {"makronBFG", (byte *)makronBFG}, {"makron_run", (byte *)makron_run}, {"makron_walk", (byte *)makron_walk}, {"makron_prerailgun", (byte *)makron_prerailgun}, {"makron_brainsplorch", (byte *)makron_brainsplorch}, {"makron_step_right", (byte *)makron_step_right}, {"makron_step_left", (byte *)makron_step_left}, {"makron_popup", (byte *)makron_popup}, {"makron_hit", (byte *)makron_hit}, {"makron_stand", (byte *)makron_stand}, {"makron_taunt", (byte *)makron_taunt}, {"SP_monster_jorg", (byte *)SP_monster_jorg}, {"Jorg_CheckAttack", (byte *)Jorg_CheckAttack}, {"jorg_die", (byte *)jorg_die}, {"jorg_dead", (byte *)jorg_dead}, {"jorg_attack", (byte *)jorg_attack}, {"jorg_firebullet", (byte *)jorg_firebullet}, {"jorg_firebullet_left", (byte *)jorg_firebullet_left}, {"jorg_firebullet_right", (byte *)jorg_firebullet_right}, {"jorgBFG", (byte *)jorgBFG}, {"jorg_pain", (byte *)jorg_pain}, {"jorg_attack1", (byte *)jorg_attack1}, {"jorg_reattack1", (byte *)jorg_reattack1}, {"jorg_run", (byte *)jorg_run}, {"jorg_walk", (byte *)jorg_walk}, {"jorg_stand", (byte *)jorg_stand}, {"jorg_step_right", (byte *)jorg_step_right}, {"jorg_step_left", (byte *)jorg_step_left}, {"jorg_death_hit", (byte *)jorg_death_hit}, {"jorg_idle", (byte *)jorg_idle}, {"jorg_search", (byte *)jorg_search}, {"SP_monster_boss3_stand", (byte *)SP_monster_boss3_stand}, {"Think_Boss3Stand", (byte *)Think_Boss3Stand}, {"Use_Boss3", (byte *)Use_Boss3}, {"SP_monster_boss2", (byte *)SP_monster_boss2}, {"Boss2_CheckAttack", (byte *)Boss2_CheckAttack}, {"boss2_die", (byte *)boss2_die}, {"boss2_dead", (byte *)boss2_dead}, {"boss2_pain", (byte *)boss2_pain}, {"boss2_reattack_mg", (byte *)boss2_reattack_mg}, {"boss2_attack_mg", (byte *)boss2_attack_mg}, {"boss2_attack", (byte *)boss2_attack}, {"boss2_walk", (byte *)boss2_walk}, {"boss2_run", (byte *)boss2_run}, {"boss2_stand", (byte *)boss2_stand}, {"Boss2MachineGun", (byte *)Boss2MachineGun}, {"boss2_firebullet_left", (byte *)boss2_firebullet_left}, {"boss2_firebullet_right", (byte *)boss2_firebullet_right}, {"Boss2Rocket", (byte *)Boss2Rocket}, {"boss2_search", (byte *)boss2_search}, {"SP_monster_berserk", (byte *)SP_monster_berserk}, {"berserk_die", (byte *)berserk_die}, {"berserk_dead", (byte *)berserk_dead}, {"berserk_pain", (byte *)berserk_pain}, {"berserk_melee", (byte *)berserk_melee}, {"berserk_strike", (byte *)berserk_strike}, {"berserk_attack_club", (byte *)berserk_attack_club}, {"berserk_swing", (byte *)berserk_swing}, {"berserk_attack_spike", (byte *)berserk_attack_spike}, {"berserk_run", (byte *)berserk_run}, {"berserk_walk", (byte *)berserk_walk}, {"berserk_fidget", (byte *)berserk_fidget}, {"berserk_stand", (byte *)berserk_stand}, {"berserk_search", (byte *)berserk_search}, {"berserk_sight", (byte *)berserk_sight}, {"fire_bfg", (byte *)fire_bfg}, {"bfg_think", (byte *)bfg_think}, {"bfg_touch", (byte *)bfg_touch}, {"bfg_explode", (byte *)bfg_explode}, {"fire_rail", (byte *)fire_rail}, {"fire_rocket", (byte *)fire_rocket}, {"rocket_touch", (byte *)rocket_touch}, {"fire_grenade2", (byte *)fire_grenade2}, {"fire_grenade", (byte *)fire_grenade}, {"Grenade_Touch", (byte *)Grenade_Touch}, {"Grenade_Explode", (byte *)Grenade_Explode}, {"fire_blaster", (byte *)fire_blaster}, {"blaster_touch", (byte *)blaster_touch}, {"fire_shotgun", (byte *)fire_shotgun}, {"fire_bullet", (byte *)fire_bullet}, {"fire_lead", (byte *)fire_lead}, {"fire_hit", (byte *)fire_hit}, {"check_dodge", (byte *)check_dodge}, {"KillBox", (byte *)KillBox}, {"G_TouchSolids", (byte *)G_TouchSolids}, {"G_TouchTriggers", (byte *)G_TouchTriggers}, {"G_FreeEdict", (byte *)G_FreeEdict}, {"G_Spawn", (byte *)G_Spawn}, {"G_InitEdict", (byte *)G_InitEdict}, {"G_CopyString", (byte *)G_CopyString}, {"vectoangles", (byte *)vectoangles}, {"vectoyaw", (byte *)vectoyaw}, {"G_SetMovedir", (byte *)G_SetMovedir}, {"vtos", (byte *)vtos}, {"tv", (byte *)tv}, {"G_UseTargets", (byte *)G_UseTargets}, {"Think_Delay", (byte *)Think_Delay}, {"G_PickTarget", (byte *)G_PickTarget}, {"findradius", (byte *)findradius}, {"G_Find", (byte *)G_Find}, {"G_ProjectSource", (byte *)G_ProjectSource}, {"SP_turret_driver", (byte *)SP_turret_driver}, {"turret_driver_link", (byte *)turret_driver_link}, {"turret_driver_think", (byte *)turret_driver_think}, {"turret_driver_die", (byte *)turret_driver_die}, {"SP_turret_base", (byte *)SP_turret_base}, {"SP_turret_breach", (byte *)SP_turret_breach}, {"turret_breach_finish_init", (byte *)turret_breach_finish_init}, {"turret_breach_think", (byte *)turret_breach_think}, {"turret_breach_fire", (byte *)turret_breach_fire}, {"turret_blocked", (byte *)turret_blocked}, {"SnapToEights", (byte *)SnapToEights}, {"AnglesNormalize", (byte *)AnglesNormalize}, {"SP_trigger_monsterjump", (byte *)SP_trigger_monsterjump}, {"trigger_monsterjump_touch", (byte *)trigger_monsterjump_touch}, {"SP_trigger_gravity", (byte *)SP_trigger_gravity}, {"trigger_gravity_touch", (byte *)trigger_gravity_touch}, {"SP_trigger_hurt", (byte *)SP_trigger_hurt}, {"hurt_use", (byte *)hurt_use}, {"hurt_touch", (byte *)hurt_touch}, {"SP_trigger_push", (byte *)SP_trigger_push}, {"trigger_push_touch", (byte *)trigger_push_touch}, {"SP_trigger_always", (byte *)SP_trigger_always}, {"SP_trigger_counter", (byte *)SP_trigger_counter}, {"trigger_counter_use", (byte *)trigger_counter_use}, {"SP_trigger_key", (byte *)SP_trigger_key}, {"trigger_key_use", (byte *)trigger_key_use}, {"SP_trigger_relay", (byte *)SP_trigger_relay}, {"trigger_relay_use", (byte *)trigger_relay_use}, {"SP_trigger_once", (byte *)SP_trigger_once}, {"SP_trigger_multiple", (byte *)SP_trigger_multiple}, {"trigger_enable", (byte *)trigger_enable}, {"Touch_Multi", (byte *)Touch_Multi}, {"Use_Multi", (byte *)Use_Multi}, {"multi_trigger", (byte *)multi_trigger}, {"multi_wait", (byte *)multi_wait}, {"InitTrigger", (byte *)InitTrigger}, {"SP_target_earthquake", (byte *)SP_target_earthquake}, {"target_earthquake_use", (byte *)target_earthquake_use}, {"target_earthquake_think", (byte *)target_earthquake_think}, {"SP_target_lightramp", (byte *)SP_target_lightramp}, {"target_lightramp_use", (byte *)target_lightramp_use}, {"target_lightramp_think", (byte *)target_lightramp_think}, {"SP_target_laser", (byte *)SP_target_laser}, {"target_laser_start", (byte *)target_laser_start}, {"target_laser_use", (byte *)target_laser_use}, {"target_laser_off", (byte *)target_laser_off}, {"target_laser_on", (byte *)target_laser_on}, {"target_laser_think", (byte *)target_laser_think}, {"SP_target_crosslevel_target", (byte *)SP_target_crosslevel_target}, {"target_crosslevel_target_think", (byte *)target_crosslevel_target_think}, {"SP_target_crosslevel_trigger", (byte *)SP_target_crosslevel_trigger}, {"trigger_crosslevel_trigger_use", (byte *)trigger_crosslevel_trigger_use}, {"SP_target_blaster", (byte *)SP_target_blaster}, {"use_target_blaster", (byte *)use_target_blaster}, {"SP_target_spawner", (byte *)SP_target_spawner}, {"use_target_spawner", (byte *)use_target_spawner}, {"SP_target_splash", (byte *)SP_target_splash}, {"use_target_splash", (byte *)use_target_splash}, {"SP_target_changelevel", (byte *)SP_target_changelevel}, {"use_target_changelevel", (byte *)use_target_changelevel}, {"SP_target_explosion", (byte *)SP_target_explosion}, {"use_target_explosion", (byte *)use_target_explosion}, {"target_explosion_explode", (byte *)target_explosion_explode}, {"SP_target_goal", (byte *)SP_target_goal}, {"use_target_goal", (byte *)use_target_goal}, {"SP_target_secret", (byte *)SP_target_secret}, {"use_target_secret", (byte *)use_target_secret}, {"SP_target_help", (byte *)SP_target_help}, {"Use_Target_Help", (byte *)Use_Target_Help}, {"SP_target_speaker", (byte *)SP_target_speaker}, {"Use_Target_Speaker", (byte *)Use_Target_Speaker}, {"SP_target_temp_entity", (byte *)SP_target_temp_entity}, {"Use_Target_Tent", (byte *)Use_Target_Tent}, {"ServerCommand", (byte *)ServerCommand}, {"SVCmd_WriteIP_f", (byte *)SVCmd_WriteIP_f}, {"SVCmd_ListIP_f", (byte *)SVCmd_ListIP_f}, {"SVCmd_RemoveIP_f", (byte *)SVCmd_RemoveIP_f}, {"SVCmd_AddIP_f", (byte *)SVCmd_AddIP_f}, {"SV_FilterPacket", (byte *)SV_FilterPacket}, {"Svcmd_Test_f", (byte *)Svcmd_Test_f}, {"SP_worldspawn", (byte *)SP_worldspawn}, {"SpawnEntities", (byte *)SpawnEntities}, {"G_FindTeams", (byte *)G_FindTeams}, {"ED_ParseEdict", (byte *)ED_ParseEdict}, {"ED_ParseField", (byte *)ED_ParseField}, {"ED_NewString", (byte *)ED_NewString}, {"ED_CallSpawn", (byte *)ED_CallSpawn}, {"G_RunEntity", (byte *)G_RunEntity}, {"SV_Physics_Step", (byte *)SV_Physics_Step}, {"SV_AddRotationalFriction", (byte *)SV_AddRotationalFriction}, {"SV_Physics_Toss", (byte *)SV_Physics_Toss}, {"SV_Physics_Noclip", (byte *)SV_Physics_Noclip}, {"SV_Physics_None", (byte *)SV_Physics_None}, {"SV_Physics_Pusher", (byte *)SV_Physics_Pusher}, {"SV_Push", (byte *)SV_Push}, {"SV_PushEntity", (byte *)SV_PushEntity}, {"RealBoundingBox", (byte *)RealBoundingBox}, {"SV_AddGravity", (byte *)SV_AddGravity}, {"SV_FlyMove", (byte *)SV_FlyMove}, {"ClipVelocity", (byte *)ClipVelocity}, {"SV_Impact", (byte *)SV_Impact}, {"SV_RunThink", (byte *)SV_RunThink}, {"SV_CheckVelocity", (byte *)SV_CheckVelocity}, {"SV_TestEntityPosition", (byte *)SV_TestEntityPosition}, {"swimmonster_start", (byte *)swimmonster_start}, {"swimmonster_start_go", (byte *)swimmonster_start_go}, {"flymonster_start", (byte *)flymonster_start}, {"flymonster_start_go", (byte *)flymonster_start_go}, {"walkmonster_start", (byte *)walkmonster_start}, {"walkmonster_start_go", (byte *)walkmonster_start_go}, {"monster_start_go", (byte *)monster_start_go}, {"monster_start", (byte *)monster_start}, {"monster_death_use", (byte *)monster_death_use}, {"monster_triggered_start", (byte *)monster_triggered_start}, {"monster_triggered_spawn_use", (byte *)monster_triggered_spawn_use}, {"monster_triggered_spawn", (byte *)monster_triggered_spawn}, {"monster_use", (byte *)monster_use}, {"monster_think", (byte *)monster_think}, {"M_MoveFrame", (byte *)M_MoveFrame}, {"M_SetEffects", (byte *)M_SetEffects}, {"M_droptofloor", (byte *)M_droptofloor}, {"M_WorldEffects", (byte *)M_WorldEffects}, {"M_CatagorizePosition", (byte *)M_CatagorizePosition}, {"M_CheckGround", (byte *)M_CheckGround}, {"AttackFinished", (byte *)AttackFinished}, {"M_FlyCheck", (byte *)M_FlyCheck}, {"M_FliesOn", (byte *)M_FliesOn}, {"M_FliesOff", (byte *)M_FliesOff}, {"monster_fire_bfg", (byte *)monster_fire_bfg}, {"monster_fire_railgun", (byte *)monster_fire_railgun}, {"monster_fire_rocket", (byte *)monster_fire_rocket}, {"monster_fire_grenade", (byte *)monster_fire_grenade}, {"monster_fire_blaster", (byte *)monster_fire_blaster}, {"monster_fire_shotgun", (byte *)monster_fire_shotgun}, {"monster_fire_bullet", (byte *)monster_fire_bullet}, {"SP_misc_teleporter_dest", (byte *)SP_misc_teleporter_dest}, {"SP_misc_teleporter", (byte *)SP_misc_teleporter}, {"teleporter_touch", (byte *)teleporter_touch}, {"SP_func_clock", (byte *)SP_func_clock}, {"func_clock_use", (byte *)func_clock_use}, {"func_clock_think", (byte *)func_clock_think}, {"func_clock_format_countdown", (byte *)func_clock_format_countdown}, {"func_clock_reset", (byte *)func_clock_reset}, {"SP_target_string", (byte *)SP_target_string}, {"target_string_use", (byte *)target_string_use}, {"SP_target_character", (byte *)SP_target_character}, {"SP_misc_gib_head", (byte *)SP_misc_gib_head}, {"SP_misc_gib_leg", (byte *)SP_misc_gib_leg}, {"SP_misc_gib_arm", (byte *)SP_misc_gib_arm}, {"SP_light_mine2", (byte *)SP_light_mine2}, {"SP_light_mine1", (byte *)SP_light_mine1}, {"SP_misc_satellite_dish", (byte *)SP_misc_satellite_dish}, {"misc_satellite_dish_use", (byte *)misc_satellite_dish_use}, {"misc_satellite_dish_think", (byte *)misc_satellite_dish_think}, {"SP_misc_strogg_ship", (byte *)SP_misc_strogg_ship}, {"misc_strogg_ship_use", (byte *)misc_strogg_ship_use}, {"SP_misc_viper_bomb", (byte *)SP_misc_viper_bomb}, {"misc_viper_bomb_use", (byte *)misc_viper_bomb_use}, {"misc_viper_bomb_prethink", (byte *)misc_viper_bomb_prethink}, {"misc_viper_bomb_touch", (byte *)misc_viper_bomb_touch}, {"SP_misc_bigviper", (byte *)SP_misc_bigviper}, {"SP_misc_viper", (byte *)SP_misc_viper}, {"misc_viper_use", (byte *)misc_viper_use}, {"SP_misc_deadsoldier", (byte *)SP_misc_deadsoldier}, {"misc_deadsoldier_die", (byte *)misc_deadsoldier_die}, {"SP_misc_banner", (byte *)SP_misc_banner}, {"misc_banner_think", (byte *)misc_banner_think}, {"SP_monster_commander_body", (byte *)SP_monster_commander_body}, {"commander_body_drop", (byte *)commander_body_drop}, {"commander_body_use", (byte *)commander_body_use}, {"commander_body_think", (byte *)commander_body_think}, {"SP_misc_easterchick2", (byte *)SP_misc_easterchick2}, {"misc_easterchick2_think", (byte *)misc_easterchick2_think}, {"SP_misc_easterchick", (byte *)SP_misc_easterchick}, {"misc_easterchick_think", (byte *)misc_easterchick_think}, {"SP_misc_eastertank", (byte *)SP_misc_eastertank}, {"misc_eastertank_think", (byte *)misc_eastertank_think}, {"SP_misc_blackhole", (byte *)SP_misc_blackhole}, {"misc_blackhole_transparent", (byte *)misc_blackhole_transparent}, {"misc_blackhole_think", (byte *)misc_blackhole_think}, {"misc_blackhole_use", (byte *)misc_blackhole_use}, {"SP_misc_explobox", (byte *)SP_misc_explobox}, {"barrel_delay", (byte *)barrel_delay}, {"barrel_explode", (byte *)barrel_explode}, {"barrel_touch", (byte *)barrel_touch}, {"SP_func_explosive", (byte *)SP_func_explosive}, {"func_explosive_spawn", (byte *)func_explosive_spawn}, {"func_explosive_use", (byte *)func_explosive_use}, {"func_explosive_explode", (byte *)func_explosive_explode}, {"SP_func_object", (byte *)SP_func_object}, {"func_object_use", (byte *)func_object_use}, {"func_object_release", (byte *)func_object_release}, {"func_object_touch", (byte *)func_object_touch}, {"SP_func_wall", (byte *)SP_func_wall}, {"func_wall_use", (byte *)func_wall_use}, {"SP_light", (byte *)SP_light}, {"light_use", (byte *)light_use}, {"SP_info_notnull", (byte *)SP_info_notnull}, {"SP_info_null", (byte *)SP_info_null}, {"SP_viewthing", (byte *)SP_viewthing}, {"TH_viewthing", (byte *)TH_viewthing}, {"SP_point_combat", (byte *)SP_point_combat}, {"point_combat_touch", (byte *)point_combat_touch}, {"SP_path_corner", (byte *)SP_path_corner}, {"path_corner_touch", (byte *)path_corner_touch}, {"BecomeExplosion2", (byte *)BecomeExplosion2}, {"BecomeExplosion1", (byte *)BecomeExplosion1}, {"ThrowDebris", (byte *)ThrowDebris}, {"debris_die", (byte *)debris_die}, {"ThrowClientHead", (byte *)ThrowClientHead}, {"ThrowHead", (byte *)ThrowHead}, {"ThrowGib", (byte *)ThrowGib}, {"gib_die", (byte *)gib_die}, {"gib_touch", (byte *)gib_touch}, {"gib_think", (byte *)gib_think}, {"ClipGibVelocity", (byte *)ClipGibVelocity}, {"VelocityForDamage", (byte *)VelocityForDamage}, {"SP_func_areaportal", (byte *)SP_func_areaportal}, {"Use_Areaportal", (byte *)Use_Areaportal}, {"G_RunFrame", (byte *)G_RunFrame}, {"ExitLevel", (byte *)ExitLevel}, {"CheckDMRules", (byte *)CheckDMRules}, {"CheckNeedPass", (byte *)CheckNeedPass}, {"EndDMLevel", (byte *)EndDMLevel}, {"CreateTargetChangeLevel", (byte *)CreateTargetChangeLevel}, {"ClientEndServerFrames", (byte *)ClientEndServerFrames}, {"Com_Printf", (byte *)Com_Printf}, {"Sys_Error", (byte *)Sys_Error}, {"GetGameAPI", (byte *)GetGameAPI}, {"ShutdownGame", (byte *)ShutdownGame}, {"SetItemNames", (byte *)SetItemNames}, {"InitItems", (byte *)InitItems}, {"SP_item_health_mega", (byte *)SP_item_health_mega}, {"SP_item_health_large", (byte *)SP_item_health_large}, {"SP_item_health_small", (byte *)SP_item_health_small}, {"SP_item_health", (byte *)SP_item_health}, {"SpawnItem", (byte *)SpawnItem}, {"PrecacheItem", (byte *)PrecacheItem}, {"droptofloor", (byte *)droptofloor}, {"Use_Item", (byte *)Use_Item}, {"Drop_Item", (byte *)Drop_Item}, {"drop_make_touchable", (byte *)drop_make_touchable}, {"drop_temp_touch", (byte *)drop_temp_touch}, {"Touch_Item", (byte *)Touch_Item}, {"Drop_PowerArmor", (byte *)Drop_PowerArmor}, {"Pickup_PowerArmor", (byte *)Pickup_PowerArmor}, {"Use_PowerArmor", (byte *)Use_PowerArmor}, {"PowerArmorType", (byte *)PowerArmorType}, {"Pickup_Armor", (byte *)Pickup_Armor}, {"ArmorIndex", (byte *)ArmorIndex}, {"Pickup_Health", (byte *)Pickup_Health}, {"MegaHealth_think", (byte *)MegaHealth_think}, {"Drop_Ammo", (byte *)Drop_Ammo}, {"Pickup_Ammo", (byte *)Pickup_Ammo}, {"Add_Ammo", (byte *)Add_Ammo}, {"Pickup_Key", (byte *)Pickup_Key}, {"Use_Silencer", (byte *)Use_Silencer}, {"Use_Invulnerability", (byte *)Use_Invulnerability}, {"Use_Envirosuit", (byte *)Use_Envirosuit}, {"Use_Breather", (byte *)Use_Breather}, {"Use_Quad", (byte *)Use_Quad}, {"Pickup_Pack", (byte *)Pickup_Pack}, {"Pickup_Bandolier", (byte *)Pickup_Bandolier}, {"Pickup_AncientHead", (byte *)Pickup_AncientHead}, {"Pickup_Adrenaline", (byte *)Pickup_Adrenaline}, {"Drop_General", (byte *)Drop_General}, {"Pickup_Powerup", (byte *)Pickup_Powerup}, {"SetRespawn", (byte *)SetRespawn}, {"DoRespawn", (byte *)DoRespawn}, {"FindItem", (byte *)FindItem}, {"FindItemByClassname", (byte *)FindItemByClassname}, {"GetItemByIndex", (byte *)GetItemByIndex}, {"SP_func_killbox", (byte *)SP_func_killbox}, {"use_killbox", (byte *)use_killbox}, {"SP_func_door_secret", (byte *)SP_func_door_secret}, {"door_secret_die", (byte *)door_secret_die}, {"door_secret_blocked", (byte *)door_secret_blocked}, {"door_secret_done", (byte *)door_secret_done}, {"door_secret_move6", (byte *)door_secret_move6}, {"door_secret_move5", (byte *)door_secret_move5}, {"door_secret_move4", (byte *)door_secret_move4}, {"door_secret_move3", (byte *)door_secret_move3}, {"door_secret_move2", (byte *)door_secret_move2}, {"door_secret_move1", (byte *)door_secret_move1}, {"door_secret_use", (byte *)door_secret_use}, {"SP_func_conveyor", (byte *)SP_func_conveyor}, {"func_conveyor_use", (byte *)func_conveyor_use}, {"SP_func_timer", (byte *)SP_func_timer}, {"func_timer_use", (byte *)func_timer_use}, {"func_timer_think", (byte *)func_timer_think}, {"SP_trigger_elevator", (byte *)SP_trigger_elevator}, {"trigger_elevator_init", (byte *)trigger_elevator_init}, {"trigger_elevator_use", (byte *)trigger_elevator_use}, {"SP_func_train", (byte *)SP_func_train}, {"train_use", (byte *)train_use}, {"func_train_find", (byte *)func_train_find}, {"train_resume", (byte *)train_resume}, {"train_next", (byte *)train_next}, {"train_wait", (byte *)train_wait}, {"train_blocked", (byte *)train_blocked}, {"SP_func_water", (byte *)SP_func_water}, {"SP_func_door_rotating", (byte *)SP_func_door_rotating}, {"SP_func_door", (byte *)SP_func_door}, {"door_touch", (byte *)door_touch}, {"door_killed", (byte *)door_killed}, {"door_blocked", (byte *)door_blocked}, {"Think_SpawnDoorTrigger", (byte *)Think_SpawnDoorTrigger}, {"Think_CalcMoveSpeed", (byte *)Think_CalcMoveSpeed}, {"Touch_DoorTrigger", (byte *)Touch_DoorTrigger}, {"door_use", (byte *)door_use}, {"door_go_up", (byte *)door_go_up}, {"door_go_down", (byte *)door_go_down}, {"door_hit_bottom", (byte *)door_hit_bottom}, {"door_hit_top", (byte *)door_hit_top}, {"door_use_areaportals", (byte *)door_use_areaportals}, {"SP_func_button", (byte *)SP_func_button}, {"button_killed", (byte *)button_killed}, {"button_touch", (byte *)button_touch}, {"button_use", (byte *)button_use}, {"button_fire", (byte *)button_fire}, {"button_wait", (byte *)button_wait}, {"button_return", (byte *)button_return}, {"button_done", (byte *)button_done}, {"SP_func_rotating", (byte *)SP_func_rotating}, {"rotating_use", (byte *)rotating_use}, {"rotating_touch", (byte *)rotating_touch}, {"rotating_blocked", (byte *)rotating_blocked}, {"SP_func_plat", (byte *)SP_func_plat}, {"plat_spawn_inside_trigger", (byte *)plat_spawn_inside_trigger}, {"Touch_Plat_Center", (byte *)Touch_Plat_Center}, {"Use_Plat", (byte *)Use_Plat}, {"plat_blocked", (byte *)plat_blocked}, {"plat_go_up", (byte *)plat_go_up}, {"plat_go_down", (byte *)plat_go_down}, {"plat_hit_bottom", (byte *)plat_hit_bottom}, {"plat_hit_top", (byte *)plat_hit_top}, {"Think_AccelMove", (byte *)Think_AccelMove}, {"plat_Accelerate", (byte *)plat_Accelerate}, {"plat_CalcAcceleratedMove", (byte *)plat_CalcAcceleratedMove}, {"AngleMove_Calc", (byte *)AngleMove_Calc}, {"AngleMove_Begin", (byte *)AngleMove_Begin}, {"AngleMove_Final", (byte *)AngleMove_Final}, {"AngleMove_Done", (byte *)AngleMove_Done}, {"Move_Calc", (byte *)Move_Calc}, {"Move_Begin", (byte *)Move_Begin}, {"Move_Final", (byte *)Move_Final}, {"Move_Done", (byte *)Move_Done}, {"T_RadiusDamage", (byte *)T_RadiusDamage}, {"T_Damage", (byte *)T_Damage}, {"M_ReactToDamage", (byte *)M_ReactToDamage}, {"CheckArmor", (byte *)CheckArmor}, {"CheckPowerArmor", (byte *)CheckPowerArmor}, {"SpawnDamage", (byte *)SpawnDamage}, {"Killed", (byte *)Killed}, {"CanDamage", (byte *)CanDamage}, {"ClientCommand", (byte *)ClientCommand}, {"Cmd_PlayerList_f", (byte *)Cmd_PlayerList_f}, {"Cmd_Say_f", (byte *)Cmd_Say_f}, {"Cmd_Wave_f", (byte *)Cmd_Wave_f}, {"Cmd_Players_f", (byte *)Cmd_Players_f}, {"PlayerSort", (byte *)PlayerSort}, {"Cmd_PutAway_f", (byte *)Cmd_PutAway_f}, {"Cmd_Kill_f", (byte *)Cmd_Kill_f}, {"Cmd_InvDrop_f", (byte *)Cmd_InvDrop_f}, {"Cmd_WeapLast_f", (byte *)Cmd_WeapLast_f}, {"Cmd_WeapNext_f", (byte *)Cmd_WeapNext_f}, {"Cmd_WeapPrev_f", (byte *)Cmd_WeapPrev_f}, {"Cmd_InvUse_f", (byte *)Cmd_InvUse_f}, {"Cmd_Inven_f", (byte *)Cmd_Inven_f}, {"Cmd_Help_f", (byte *)Cmd_Help_f}, {"Cmd_Score_f", (byte *)Cmd_Score_f}, {"Cmd_Drop_f", (byte *)Cmd_Drop_f}, {"Cmd_Use_f", (byte *)Cmd_Use_f}, {"Cmd_Noclip_f", (byte *)Cmd_Noclip_f}, {"Cmd_Notarget_f", (byte *)Cmd_Notarget_f}, {"Cmd_God_f", (byte *)Cmd_God_f}, {"Cmd_Give_f", (byte *)Cmd_Give_f}, {"ValidateSelectedItem", (byte *)ValidateSelectedItem}, {"SelectPrevItem", (byte *)SelectPrevItem}, {"SelectNextItem", (byte *)SelectNextItem}, {"OnSameTeam", (byte *)OnSameTeam}, {"GetChaseTarget", (byte *)GetChaseTarget}, {"ChasePrev", (byte *)ChasePrev}, {"ChaseNext", (byte *)ChaseNext}, {"UpdateChaseCam", (byte *)UpdateChaseCam}, {"ai_run", (byte *)ai_run}, {"ai_checkattack", (byte *)ai_checkattack}, {"ai_run_slide", (byte *)ai_run_slide}, {"ai_run_missile", (byte *)ai_run_missile}, {"ai_run_melee", (byte *)ai_run_melee}, {"M_CheckAttack", (byte *)M_CheckAttack}, {"FacingIdeal", (byte *)FacingIdeal}, {"FindTarget", (byte *)FindTarget}, {"FoundTarget", (byte *)FoundTarget}, {"HuntTarget", (byte *)HuntTarget}, {"infront", (byte *)infront}, {"visible", (byte *)visible}, {"range", (byte *)range}, {"ai_turn", (byte *)ai_turn}, {"ai_charge", (byte *)ai_charge}, {"ai_walk", (byte *)ai_walk}, {"ai_stand", (byte *)ai_stand}, {"ai_move", (byte *)ai_move}, {"AI_SetSightClient", (byte *)AI_SetSightClient}, {0, 0} yquake2-QUAKE2_5_32/src/game/savegame/tables/gamemmove_decs.h0000644000175000017500000002571712615162034024521 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2011 Knightmare * Copyright (C) 2011 Yamagi Burmeister * * This program is free software; you can 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. * * ======================================================================= * * Prototypes for every mmove_t in the game.so. * * ======================================================================= */ extern mmove_t tank_move_death ; extern mmove_t tank_move_attack_chain ; extern mmove_t tank_move_attack_post_rocket ; extern mmove_t tank_move_attack_fire_rocket ; extern mmove_t tank_move_attack_pre_rocket ; extern mmove_t tank_move_attack_strike ; extern mmove_t tank_move_attack_post_blast ; extern mmove_t tank_move_reattack_blast ; extern mmove_t tank_move_attack_blast ; extern mmove_t tank_move_pain3 ; extern mmove_t tank_move_pain2 ; extern mmove_t tank_move_pain1 ; extern mmove_t tank_move_stop_run ; extern mmove_t tank_move_run ; extern mmove_t tank_move_start_run ; extern mmove_t tank_move_stop_walk ; extern mmove_t tank_move_walk ; extern mmove_t tank_move_start_walk ; extern mmove_t tank_move_stand ; extern mmove_t supertank_move_end_attack1 ; extern mmove_t supertank_move_attack1 ; extern mmove_t supertank_move_attack2 ; extern mmove_t supertank_move_attack3 ; extern mmove_t supertank_move_attack4 ; extern mmove_t supertank_move_backward ; extern mmove_t supertank_move_death ; extern mmove_t supertank_move_pain1 ; extern mmove_t supertank_move_pain2 ; extern mmove_t supertank_move_pain3 ; extern mmove_t supertank_move_turn_left ; extern mmove_t supertank_move_turn_right ; extern mmove_t supertank_move_forward ; extern mmove_t supertank_move_run ; extern mmove_t supertank_move_stand ; extern mmove_t soldier_move_death6 ; extern mmove_t soldier_move_death5 ; extern mmove_t soldier_move_death4 ; extern mmove_t soldier_move_death3 ; extern mmove_t soldier_move_death2 ; extern mmove_t soldier_move_death1 ; extern mmove_t soldier_move_duck ; extern mmove_t soldier_move_attack6 ; extern mmove_t soldier_move_attack4 ; extern mmove_t soldier_move_attack3 ; extern mmove_t soldier_move_attack2 ; extern mmove_t soldier_move_attack1 ; extern mmove_t soldier_move_pain4 ; extern mmove_t soldier_move_pain3 ; extern mmove_t soldier_move_pain2 ; extern mmove_t soldier_move_pain1 ; extern mmove_t soldier_move_run ; extern mmove_t soldier_move_start_run ; extern mmove_t soldier_move_walk2 ; extern mmove_t soldier_move_walk1 ; extern mmove_t soldier_move_stand3 ; extern mmove_t soldier_move_stand1 ; extern mmove_t parasite_move_death ; extern mmove_t parasite_move_break ; extern mmove_t parasite_move_drain ; extern mmove_t parasite_move_pain1 ; extern mmove_t parasite_move_stop_walk ; extern mmove_t parasite_move_start_walk ; extern mmove_t parasite_move_walk ; extern mmove_t parasite_move_stop_run ; extern mmove_t parasite_move_start_run ; extern mmove_t parasite_move_run ; extern mmove_t parasite_move_stand ; extern mmove_t parasite_move_end_fidget ; extern mmove_t parasite_move_fidget ; extern mmove_t parasite_move_start_fidget ; extern mmove_t mutant_move_death2 ; extern mmove_t mutant_move_death1 ; extern mmove_t mutant_move_pain3 ; extern mmove_t mutant_move_pain2 ; extern mmove_t mutant_move_pain1 ; extern mmove_t mutant_move_jump ; extern mmove_t mutant_move_attack ; extern mmove_t mutant_move_run ; extern mmove_t mutant_move_start_walk ; extern mmove_t mutant_move_walk ; extern mmove_t mutant_move_idle ; extern mmove_t mutant_move_stand ; extern mmove_t medic_move_attackCable ; extern mmove_t medic_move_attackBlaster ; extern mmove_t medic_move_attackHyperBlaster ; extern mmove_t medic_move_duck ; extern mmove_t medic_move_death ; extern mmove_t medic_move_pain2 ; extern mmove_t medic_move_pain1 ; extern mmove_t medic_move_run ; extern mmove_t medic_move_walk ; extern mmove_t medic_move_stand ; extern mmove_t insane_move_struggle_cross ; extern mmove_t insane_move_cross ; extern mmove_t insane_move_crawl_death ; extern mmove_t insane_move_crawl_pain ; extern mmove_t insane_move_runcrawl ; extern mmove_t insane_move_crawl ; extern mmove_t insane_move_stand_death ; extern mmove_t insane_move_stand_pain ; extern mmove_t insane_move_run_insane ; extern mmove_t insane_move_walk_insane ; extern mmove_t insane_move_run_normal ; extern mmove_t insane_move_walk_normal ; extern mmove_t insane_move_down ; extern mmove_t insane_move_jumpdown ; extern mmove_t insane_move_downtoup ; extern mmove_t insane_move_uptodown ; extern mmove_t insane_move_stand_insane ; extern mmove_t insane_move_stand_normal ; extern mmove_t infantry_move_attack2 ; extern mmove_t infantry_move_attack1 ; extern mmove_t infantry_move_duck ; extern mmove_t infantry_move_death3 ; extern mmove_t infantry_move_death2 ; extern mmove_t infantry_move_death1 ; extern mmove_t infantry_move_pain2 ; extern mmove_t infantry_move_pain1 ; extern mmove_t infantry_move_run ; extern mmove_t infantry_move_walk ; extern mmove_t infantry_move_fidget ; extern mmove_t infantry_move_stand ; extern mmove_t hover_move_end_attack ; extern mmove_t hover_move_attack1 ; extern mmove_t hover_move_start_attack ; extern mmove_t hover_move_backward ; extern mmove_t hover_move_death1 ; extern mmove_t hover_move_run ; extern mmove_t hover_move_walk ; extern mmove_t hover_move_forward ; extern mmove_t hover_move_land ; extern mmove_t hover_move_pain1 ; extern mmove_t hover_move_pain2 ; extern mmove_t hover_move_pain3 ; extern mmove_t hover_move_takeoff ; extern mmove_t hover_move_stop2 ; extern mmove_t hover_move_stop1 ; extern mmove_t hover_move_stand ; extern mmove_t gunner_move_attack_grenade ; extern mmove_t gunner_move_endfire_chain ; extern mmove_t gunner_move_fire_chain ; extern mmove_t gunner_move_attack_chain ; extern mmove_t gunner_move_duck ; extern mmove_t gunner_move_death ; extern mmove_t gunner_move_pain1 ; extern mmove_t gunner_move_pain2 ; extern mmove_t gunner_move_pain3 ; extern mmove_t gunner_move_runandshoot ; extern mmove_t gunner_move_run ; extern mmove_t gunner_move_walk ; extern mmove_t gunner_move_stand ; extern mmove_t gunner_move_fidget ; extern mmove_t gladiator_move_death ; extern mmove_t gladiator_move_pain_air ; extern mmove_t gladiator_move_pain ; extern mmove_t gladiator_move_attack_gun ; extern mmove_t gladiator_move_attack_melee ; extern mmove_t gladiator_move_run ; extern mmove_t gladiator_move_walk ; extern mmove_t gladiator_move_stand ; extern mmove_t flyer_move_loop_melee ; extern mmove_t flyer_move_end_melee ; extern mmove_t flyer_move_start_melee ; extern mmove_t flyer_move_attack2 ; extern mmove_t flyer_move_bankleft ; extern mmove_t flyer_move_bankright ; extern mmove_t flyer_move_defense ; extern mmove_t flyer_move_pain1 ; extern mmove_t flyer_move_pain2 ; extern mmove_t flyer_move_pain3 ; extern mmove_t flyer_move_rollleft ; extern mmove_t flyer_move_rollright ; extern mmove_t flyer_move_stop ; extern mmove_t flyer_move_start ; extern mmove_t flyer_move_run ; extern mmove_t flyer_move_walk ; extern mmove_t flyer_move_stand ; extern mmove_t floater_move_run ; extern mmove_t floater_move_walk ; extern mmove_t floater_move_pain3 ; extern mmove_t floater_move_pain2 ; extern mmove_t floater_move_pain1 ; extern mmove_t floater_move_death ; extern mmove_t floater_move_attack3 ; extern mmove_t floater_move_attack2 ; extern mmove_t floater_move_attack1 ; extern mmove_t floater_move_activate ; extern mmove_t floater_move_stand2 ; extern mmove_t floater_move_stand1 ; extern mmove_t flipper_move_death ; extern mmove_t flipper_move_attack ; extern mmove_t flipper_move_pain1 ; extern mmove_t flipper_move_pain2 ; extern mmove_t flipper_move_start_run ; extern mmove_t flipper_move_walk ; extern mmove_t flipper_move_run_start ; extern mmove_t flipper_move_run_loop ; extern mmove_t flipper_move_stand ; extern mmove_t chick_move_start_slash ; extern mmove_t chick_move_end_slash ; extern mmove_t chick_move_slash ; extern mmove_t chick_move_end_attack1 ; extern mmove_t chick_move_attack1 ; extern mmove_t chick_move_start_attack1 ; extern mmove_t chick_move_duck ; extern mmove_t chick_move_death1 ; extern mmove_t chick_move_death2 ; extern mmove_t chick_move_pain3 ; extern mmove_t chick_move_pain2 ; extern mmove_t chick_move_pain1 ; extern mmove_t chick_move_walk ; extern mmove_t chick_move_run ; extern mmove_t chick_move_start_run ; extern mmove_t chick_move_stand ; extern mmove_t chick_move_fidget ; extern mmove_t brain_move_run ; extern mmove_t brain_move_attack2 ; extern mmove_t brain_move_attack1 ; extern mmove_t brain_move_death1 ; extern mmove_t brain_move_death2 ; extern mmove_t brain_move_duck ; extern mmove_t brain_move_pain1 ; extern mmove_t brain_move_pain2 ; extern mmove_t brain_move_pain3 ; extern mmove_t brain_move_defense ; extern mmove_t brain_move_walk1 ; extern mmove_t brain_move_idle ; extern mmove_t brain_move_stand ; extern mmove_t makron_move_attack5 ; extern mmove_t makron_move_attack4 ; extern mmove_t makron_move_attack3 ; extern mmove_t makron_move_sight ; extern mmove_t makron_move_death3 ; extern mmove_t makron_move_death2 ; extern mmove_t makron_move_pain4 ; extern mmove_t makron_move_pain5 ; extern mmove_t makron_move_pain6 ; extern mmove_t makron_move_walk ; extern mmove_t makron_move_run ; extern mmove_t makron_move_stand ; extern mmove_t jorg_move_end_attack1 ; extern mmove_t jorg_move_attack1 ; extern mmove_t jorg_move_start_attack1 ; extern mmove_t jorg_move_attack2 ; extern mmove_t jorg_move_death ; extern mmove_t jorg_move_pain1 ; extern mmove_t jorg_move_pain2 ; extern mmove_t jorg_move_pain3 ; extern mmove_t jorg_move_end_walk ; extern mmove_t jorg_move_walk ; extern mmove_t jorg_move_start_walk ; extern mmove_t jorg_move_run ; extern mmove_t jorg_move_stand ; extern mmove_t boss2_move_death ; extern mmove_t boss2_move_pain_light ; extern mmove_t boss2_move_pain_heavy ; extern mmove_t boss2_move_attack_rocket ; extern mmove_t boss2_move_attack_post_mg ; extern mmove_t boss2_move_attack_mg ; extern mmove_t boss2_move_attack_pre_mg ; extern mmove_t boss2_move_run ; extern mmove_t boss2_move_walk ; extern mmove_t boss2_move_fidget ; extern mmove_t boss2_move_stand ; extern mmove_t berserk_move_death2 ; extern mmove_t berserk_move_death1 ; extern mmove_t berserk_move_pain2 ; extern mmove_t berserk_move_pain1 ; extern mmove_t berserk_move_attack_strike ; extern mmove_t berserk_move_attack_club ; extern mmove_t berserk_move_attack_spike ; extern mmove_t berserk_move_run1 ; extern mmove_t berserk_move_walk ; extern mmove_t berserk_move_stand_fidget ; extern mmove_t berserk_move_stand ; yquake2-QUAKE2_5_32/src/game/savegame/tables/fields.h0000644000175000017500000001126412615162034023004 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2011 Yamagi Burmeister * * This program is free software; you can 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. * * ======================================================================= * * Game fields to be saved. * * ======================================================================= */ {"classname", FOFS(classname), F_LSTRING}, {"model", FOFS(model), F_LSTRING}, {"spawnflags", FOFS(spawnflags), F_INT}, {"speed", FOFS(speed), F_FLOAT}, {"accel", FOFS(accel), F_FLOAT}, {"decel", FOFS(decel), F_FLOAT}, {"target", FOFS(target), F_LSTRING}, {"targetname", FOFS(targetname), F_LSTRING}, {"pathtarget", FOFS(pathtarget), F_LSTRING}, {"deathtarget", FOFS(deathtarget), F_LSTRING}, {"killtarget", FOFS(killtarget), F_LSTRING}, {"combattarget", FOFS(combattarget), F_LSTRING}, {"message", FOFS(message), F_LSTRING}, {"team", FOFS(team), F_LSTRING}, {"wait", FOFS(wait), F_FLOAT}, {"delay", FOFS(delay), F_FLOAT}, {"random", FOFS(random), F_FLOAT}, {"move_origin", FOFS(move_origin), F_VECTOR}, {"move_angles", FOFS(move_angles), F_VECTOR}, {"style", FOFS(style), F_INT}, {"count", FOFS(count), F_INT}, {"health", FOFS(health), F_INT}, {"sounds", FOFS(sounds), F_INT}, {"light", 0, F_IGNORE}, {"dmg", FOFS(dmg), F_INT}, {"mass", FOFS(mass), F_INT}, {"volume", FOFS(volume), F_FLOAT}, {"attenuation", FOFS(attenuation), F_FLOAT}, {"map", FOFS(map), F_LSTRING}, {"origin", FOFS(s.origin), F_VECTOR}, {"angles", FOFS(s.angles), F_VECTOR}, {"angle", FOFS(s.angles), F_ANGLEHACK}, {"goalentity", FOFS(goalentity), F_EDICT, FFL_NOSPAWN}, {"movetarget", FOFS(movetarget), F_EDICT, FFL_NOSPAWN}, {"enemy", FOFS(enemy), F_EDICT, FFL_NOSPAWN}, {"oldenemy", FOFS(oldenemy), F_EDICT, FFL_NOSPAWN}, {"activator", FOFS(activator), F_EDICT, FFL_NOSPAWN}, {"groundentity", FOFS(groundentity), F_EDICT, FFL_NOSPAWN}, {"teamchain", FOFS(teamchain), F_EDICT, FFL_NOSPAWN}, {"teammaster", FOFS(teammaster), F_EDICT, FFL_NOSPAWN}, {"owner", FOFS(owner), F_EDICT, FFL_NOSPAWN}, {"mynoise", FOFS(mynoise), F_EDICT, FFL_NOSPAWN}, {"mynoise2", FOFS(mynoise2), F_EDICT, FFL_NOSPAWN}, {"target_ent", FOFS(target_ent), F_EDICT, FFL_NOSPAWN}, {"chain", FOFS(chain), F_EDICT, FFL_NOSPAWN}, {"prethink", FOFS(prethink), F_FUNCTION, FFL_NOSPAWN}, {"think", FOFS(think), F_FUNCTION, FFL_NOSPAWN}, {"blocked", FOFS(blocked), F_FUNCTION, FFL_NOSPAWN}, {"touch", FOFS(touch), F_FUNCTION, FFL_NOSPAWN}, {"use", FOFS(use), F_FUNCTION, FFL_NOSPAWN}, {"pain", FOFS(pain), F_FUNCTION, FFL_NOSPAWN}, {"die", FOFS(die), F_FUNCTION, FFL_NOSPAWN}, {"stand", FOFS(monsterinfo.stand), F_FUNCTION, FFL_NOSPAWN}, {"idle", FOFS(monsterinfo.idle), F_FUNCTION, FFL_NOSPAWN}, {"search", FOFS(monsterinfo.search), F_FUNCTION, FFL_NOSPAWN}, {"walk", FOFS(monsterinfo.walk), F_FUNCTION, FFL_NOSPAWN}, {"run", FOFS(monsterinfo.run), F_FUNCTION, FFL_NOSPAWN}, {"dodge", FOFS(monsterinfo.dodge), F_FUNCTION, FFL_NOSPAWN}, {"attack", FOFS(monsterinfo.attack), F_FUNCTION, FFL_NOSPAWN}, {"melee", FOFS(monsterinfo.melee), F_FUNCTION, FFL_NOSPAWN}, {"sight", FOFS(monsterinfo.sight), F_FUNCTION, FFL_NOSPAWN}, {"checkattack", FOFS(monsterinfo.checkattack), F_FUNCTION, FFL_NOSPAWN}, {"currentmove", FOFS(monsterinfo.currentmove), F_MMOVE, FFL_NOSPAWN}, {"endfunc", FOFS(moveinfo.endfunc), F_FUNCTION, FFL_NOSPAWN}, {"lip", STOFS(lip), F_INT, FFL_SPAWNTEMP}, {"distance", STOFS(distance), F_INT, FFL_SPAWNTEMP}, {"height", STOFS(height), F_INT, FFL_SPAWNTEMP}, {"noise", STOFS(noise), F_LSTRING, FFL_SPAWNTEMP}, {"pausetime", STOFS(pausetime), F_FLOAT, FFL_SPAWNTEMP}, {"item", STOFS(item), F_LSTRING, FFL_SPAWNTEMP}, {"item", FOFS(item), F_ITEM}, {"gravity", STOFS(gravity), F_LSTRING, FFL_SPAWNTEMP}, {"sky", STOFS(sky), F_LSTRING, FFL_SPAWNTEMP}, {"skyrotate", STOFS(skyrotate), F_FLOAT, FFL_SPAWNTEMP}, {"skyaxis", STOFS(skyaxis), F_VECTOR, FFL_SPAWNTEMP}, {"minyaw", STOFS(minyaw), F_FLOAT, FFL_SPAWNTEMP}, {"maxyaw", STOFS(maxyaw), F_FLOAT, FFL_SPAWNTEMP}, {"minpitch", STOFS(minpitch), F_FLOAT, FFL_SPAWNTEMP}, {"maxpitch", STOFS(maxpitch), F_FLOAT, FFL_SPAWNTEMP}, {"nextmap", STOFS(nextmap), F_LSTRING, FFL_SPAWNTEMP}, {0, 0, 0, 0} yquake2-QUAKE2_5_32/src/game/savegame/tables/gamemmove_list.h0000644000175000017500000003314112615162034024544 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2011 Yamagi Burmeister * * This program is free software; you can 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. * * ======================================================================= * * Pointers to every mmove_t in the game.so. * * ======================================================================= */ {"tank_move_death", &tank_move_death}, {"tank_move_attack_chain", &tank_move_attack_chain}, {"tank_move_attack_post_rocket", &tank_move_attack_post_rocket}, {"tank_move_attack_fire_rocket", &tank_move_attack_fire_rocket}, {"tank_move_attack_pre_rocket", &tank_move_attack_pre_rocket}, {"tank_move_attack_strike", &tank_move_attack_strike}, {"tank_move_attack_post_blast", &tank_move_attack_post_blast}, {"tank_move_reattack_blast", &tank_move_reattack_blast}, {"tank_move_attack_blast", &tank_move_attack_blast}, {"tank_move_pain3", &tank_move_pain3}, {"tank_move_pain2", &tank_move_pain2}, {"tank_move_pain1", &tank_move_pain1}, {"tank_move_stop_run", &tank_move_stop_run}, {"tank_move_run", &tank_move_run}, {"tank_move_start_run", &tank_move_start_run}, {"tank_move_stop_walk", &tank_move_stop_walk}, {"tank_move_walk", &tank_move_walk}, {"tank_move_start_walk", &tank_move_start_walk}, {"tank_move_stand", &tank_move_stand}, {"supertank_move_end_attack1", &supertank_move_end_attack1}, {"supertank_move_attack1", &supertank_move_attack1}, {"supertank_move_attack2", &supertank_move_attack2}, {"supertank_move_attack3", &supertank_move_attack3}, {"supertank_move_attack4", &supertank_move_attack4}, {"supertank_move_backward", &supertank_move_backward}, {"supertank_move_death", &supertank_move_death}, {"supertank_move_pain1", &supertank_move_pain1}, {"supertank_move_pain2", &supertank_move_pain2}, {"supertank_move_pain3", &supertank_move_pain3}, {"supertank_move_turn_left", &supertank_move_turn_left}, {"supertank_move_turn_right", &supertank_move_turn_right}, {"supertank_move_forward", &supertank_move_forward}, {"supertank_move_run", &supertank_move_run}, {"supertank_move_stand", &supertank_move_stand}, {"soldier_move_death6", &soldier_move_death6}, {"soldier_move_death5", &soldier_move_death5}, {"soldier_move_death4", &soldier_move_death4}, {"soldier_move_death3", &soldier_move_death3}, {"soldier_move_death2", &soldier_move_death2}, {"soldier_move_death1", &soldier_move_death1}, {"soldier_move_duck", &soldier_move_duck}, {"soldier_move_attack6", &soldier_move_attack6}, {"soldier_move_attack4", &soldier_move_attack4}, {"soldier_move_attack3", &soldier_move_attack3}, {"soldier_move_attack2", &soldier_move_attack2}, {"soldier_move_attack1", &soldier_move_attack1}, {"soldier_move_pain4", &soldier_move_pain4}, {"soldier_move_pain3", &soldier_move_pain3}, {"soldier_move_pain2", &soldier_move_pain2}, {"soldier_move_pain1", &soldier_move_pain1}, {"soldier_move_run", &soldier_move_run}, {"soldier_move_start_run", &soldier_move_start_run}, {"soldier_move_walk2", &soldier_move_walk2}, {"soldier_move_walk1", &soldier_move_walk1}, {"soldier_move_stand3", &soldier_move_stand3}, {"soldier_move_stand1", &soldier_move_stand1}, {"parasite_move_death", ¶site_move_death}, {"parasite_move_break", ¶site_move_break}, {"parasite_move_drain", ¶site_move_drain}, {"parasite_move_pain1", ¶site_move_pain1}, {"parasite_move_stop_walk", ¶site_move_stop_walk}, {"parasite_move_start_walk", ¶site_move_start_walk}, {"parasite_move_walk", ¶site_move_walk}, {"parasite_move_stop_run", ¶site_move_stop_run}, {"parasite_move_start_run", ¶site_move_start_run}, {"parasite_move_run", ¶site_move_run}, {"parasite_move_stand", ¶site_move_stand}, {"parasite_move_end_fidget", ¶site_move_end_fidget}, {"parasite_move_fidget", ¶site_move_fidget}, {"parasite_move_start_fidget", ¶site_move_start_fidget}, {"mutant_move_death2", &mutant_move_death2}, {"mutant_move_death1", &mutant_move_death1}, {"mutant_move_pain3", &mutant_move_pain3}, {"mutant_move_pain2", &mutant_move_pain2}, {"mutant_move_pain1", &mutant_move_pain1}, {"mutant_move_jump", &mutant_move_jump}, {"mutant_move_attack", &mutant_move_attack}, {"mutant_move_run", &mutant_move_run}, {"mutant_move_start_walk", &mutant_move_start_walk}, {"mutant_move_walk", &mutant_move_walk}, {"mutant_move_idle", &mutant_move_idle}, {"mutant_move_stand", &mutant_move_stand}, {"medic_move_attackCable", &medic_move_attackCable}, {"medic_move_attackBlaster", &medic_move_attackBlaster}, {"medic_move_attackHyperBlaster", &medic_move_attackHyperBlaster}, {"medic_move_duck", &medic_move_duck}, {"medic_move_death", &medic_move_death}, {"medic_move_pain2", &medic_move_pain2}, {"medic_move_pain1", &medic_move_pain1}, {"medic_move_run", &medic_move_run}, {"medic_move_walk", &medic_move_walk}, {"medic_move_stand", &medic_move_stand}, {"insane_move_struggle_cross", &insane_move_struggle_cross}, {"insane_move_cross", &insane_move_cross}, {"insane_move_crawl_death", &insane_move_crawl_death}, {"insane_move_crawl_pain", &insane_move_crawl_pain}, {"insane_move_runcrawl", &insane_move_runcrawl}, {"insane_move_crawl", &insane_move_crawl}, {"insane_move_stand_death", &insane_move_stand_death}, {"insane_move_stand_pain", &insane_move_stand_pain}, {"insane_move_run_insane", &insane_move_run_insane}, {"insane_move_walk_insane", &insane_move_walk_insane}, {"insane_move_run_normal", &insane_move_run_normal}, {"insane_move_walk_normal", &insane_move_walk_normal}, {"insane_move_down", &insane_move_down}, {"insane_move_jumpdown", &insane_move_jumpdown}, {"insane_move_downtoup", &insane_move_downtoup}, {"insane_move_uptodown", &insane_move_uptodown}, {"insane_move_stand_insane", &insane_move_stand_insane}, {"insane_move_stand_normal", &insane_move_stand_normal}, {"infantry_move_attack2", &infantry_move_attack2}, {"infantry_move_attack1", &infantry_move_attack1}, {"infantry_move_duck", &infantry_move_duck}, {"infantry_move_death3", &infantry_move_death3}, {"infantry_move_death2", &infantry_move_death2}, {"infantry_move_death1", &infantry_move_death1}, {"infantry_move_pain2", &infantry_move_pain2}, {"infantry_move_pain1", &infantry_move_pain1}, {"infantry_move_run", &infantry_move_run}, {"infantry_move_walk", &infantry_move_walk}, {"infantry_move_fidget", &infantry_move_fidget}, {"infantry_move_stand", &infantry_move_stand}, {"hover_move_end_attack", &hover_move_end_attack}, {"hover_move_attack1", &hover_move_attack1}, {"hover_move_start_attack", &hover_move_start_attack}, {"hover_move_backward", &hover_move_backward}, {"hover_move_death1", &hover_move_death1}, {"hover_move_run", &hover_move_run}, {"hover_move_walk", &hover_move_walk}, {"hover_move_forward", &hover_move_forward}, {"hover_move_land", &hover_move_land}, {"hover_move_pain1", &hover_move_pain1}, {"hover_move_pain2", &hover_move_pain2}, {"hover_move_pain3", &hover_move_pain3}, {"hover_move_takeoff", &hover_move_takeoff}, {"hover_move_stop2", &hover_move_stop2}, {"hover_move_stop1", &hover_move_stop1}, {"hover_move_stand", &hover_move_stand}, {"gunner_move_attack_grenade", &gunner_move_attack_grenade}, {"gunner_move_endfire_chain", &gunner_move_endfire_chain}, {"gunner_move_fire_chain", &gunner_move_fire_chain}, {"gunner_move_attack_chain", &gunner_move_attack_chain}, {"gunner_move_duck", &gunner_move_duck}, {"gunner_move_death", &gunner_move_death}, {"gunner_move_pain1", &gunner_move_pain1}, {"gunner_move_pain2", &gunner_move_pain2}, {"gunner_move_pain3", &gunner_move_pain3}, {"gunner_move_runandshoot", &gunner_move_runandshoot}, {"gunner_move_run", &gunner_move_run}, {"gunner_move_walk", &gunner_move_walk}, {"gunner_move_stand", &gunner_move_stand}, {"gunner_move_fidget", &gunner_move_fidget}, {"gladiator_move_death", &gladiator_move_death}, {"gladiator_move_pain_air", &gladiator_move_pain_air}, {"gladiator_move_pain", &gladiator_move_pain}, {"gladiator_move_attack_gun", &gladiator_move_attack_gun}, {"gladiator_move_attack_melee", &gladiator_move_attack_melee}, {"gladiator_move_run", &gladiator_move_run}, {"gladiator_move_walk", &gladiator_move_walk}, {"gladiator_move_stand", &gladiator_move_stand}, {"flyer_move_loop_melee", &flyer_move_loop_melee}, {"flyer_move_end_melee", &flyer_move_end_melee}, {"flyer_move_start_melee", &flyer_move_start_melee}, {"flyer_move_attack2", &flyer_move_attack2}, {"flyer_move_bankleft", &flyer_move_bankleft}, {"flyer_move_bankright", &flyer_move_bankright}, {"flyer_move_defense", &flyer_move_defense}, {"flyer_move_pain1", &flyer_move_pain1}, {"flyer_move_pain2", &flyer_move_pain2}, {"flyer_move_pain3", &flyer_move_pain3}, {"flyer_move_rollleft", &flyer_move_rollleft}, {"flyer_move_rollright", &flyer_move_rollright}, {"flyer_move_stop", &flyer_move_stop}, {"flyer_move_start", &flyer_move_start}, {"flyer_move_run", &flyer_move_run}, {"flyer_move_walk", &flyer_move_walk}, {"flyer_move_stand", &flyer_move_stand}, {"floater_move_run", &floater_move_run}, {"floater_move_walk", &floater_move_walk}, {"floater_move_pain3", &floater_move_pain3}, {"floater_move_pain2", &floater_move_pain2}, {"floater_move_pain1", &floater_move_pain1}, {"floater_move_death", &floater_move_death}, {"floater_move_attack3", &floater_move_attack3}, {"floater_move_attack2", &floater_move_attack2}, {"floater_move_attack1", &floater_move_attack1}, {"floater_move_activate", &floater_move_activate}, {"floater_move_stand2", &floater_move_stand2}, {"floater_move_stand1", &floater_move_stand1}, {"flipper_move_death", &flipper_move_death}, {"flipper_move_attack", &flipper_move_attack}, {"flipper_move_pain1", &flipper_move_pain1}, {"flipper_move_pain2", &flipper_move_pain2}, {"flipper_move_start_run", &flipper_move_start_run}, {"flipper_move_walk", &flipper_move_walk}, {"flipper_move_run_start", &flipper_move_run_start}, {"flipper_move_run_loop", &flipper_move_run_loop}, {"flipper_move_stand", &flipper_move_stand}, {"chick_move_start_slash", &chick_move_start_slash}, {"chick_move_end_slash", &chick_move_end_slash}, {"chick_move_slash", &chick_move_slash}, {"chick_move_end_attack1", &chick_move_end_attack1}, {"chick_move_attack1", &chick_move_attack1}, {"chick_move_start_attack1", &chick_move_start_attack1}, {"chick_move_duck", &chick_move_duck}, {"chick_move_death1", &chick_move_death1}, {"chick_move_death2", &chick_move_death2}, {"chick_move_pain3", &chick_move_pain3}, {"chick_move_pain2", &chick_move_pain2}, {"chick_move_pain1", &chick_move_pain1}, {"chick_move_walk", &chick_move_walk}, {"chick_move_run", &chick_move_run}, {"chick_move_start_run", &chick_move_start_run}, {"chick_move_stand", &chick_move_stand}, {"chick_move_fidget", &chick_move_fidget}, {"brain_move_run", &brain_move_run}, {"brain_move_attack2", &brain_move_attack2}, {"brain_move_attack1", &brain_move_attack1}, {"brain_move_death1", &brain_move_death1}, {"brain_move_death2", &brain_move_death2}, {"brain_move_duck", &brain_move_duck}, {"brain_move_pain1", &brain_move_pain1}, {"brain_move_pain2", &brain_move_pain2}, {"brain_move_pain3", &brain_move_pain3}, {"brain_move_defense", &brain_move_defense}, {"brain_move_walk1", &brain_move_walk1}, {"brain_move_idle", &brain_move_idle}, {"brain_move_stand", &brain_move_stand}, {"makron_move_attack5", &makron_move_attack5}, {"makron_move_attack4", &makron_move_attack4}, {"makron_move_attack3", &makron_move_attack3}, {"makron_move_sight", &makron_move_sight}, {"makron_move_death3", &makron_move_death3}, {"makron_move_death2", &makron_move_death2}, {"makron_move_pain4", &makron_move_pain4}, {"makron_move_pain5", &makron_move_pain5}, {"makron_move_pain6", &makron_move_pain6}, {"makron_move_walk", &makron_move_walk}, {"makron_move_run", &makron_move_run}, {"makron_move_stand", &makron_move_stand}, {"jorg_move_end_attack1", &jorg_move_end_attack1}, {"jorg_move_attack1", &jorg_move_attack1}, {"jorg_move_start_attack1", &jorg_move_start_attack1}, {"jorg_move_attack2", &jorg_move_attack2}, {"jorg_move_death", &jorg_move_death}, {"jorg_move_pain1", &jorg_move_pain1}, {"jorg_move_pain2", &jorg_move_pain2}, {"jorg_move_pain3", &jorg_move_pain3}, {"jorg_move_end_walk", &jorg_move_end_walk}, {"jorg_move_walk", &jorg_move_walk}, {"jorg_move_start_walk", &jorg_move_start_walk}, {"jorg_move_run", &jorg_move_run}, {"jorg_move_stand", &jorg_move_stand}, {"boss2_move_death", &boss2_move_death}, {"boss2_move_pain_light", &boss2_move_pain_light}, {"boss2_move_pain_heavy", &boss2_move_pain_heavy}, {"boss2_move_attack_rocket", &boss2_move_attack_rocket}, {"boss2_move_attack_post_mg", &boss2_move_attack_post_mg}, {"boss2_move_attack_mg", &boss2_move_attack_mg}, {"boss2_move_attack_pre_mg", &boss2_move_attack_pre_mg}, {"boss2_move_run", &boss2_move_run}, {"boss2_move_walk", &boss2_move_walk}, {"boss2_move_fidget", &boss2_move_fidget}, {"boss2_move_stand", &boss2_move_stand}, {"berserk_move_death2", &berserk_move_death2}, {"berserk_move_death1", &berserk_move_death1}, {"berserk_move_pain2", &berserk_move_pain2}, {"berserk_move_pain1", &berserk_move_pain1}, {"berserk_move_attack_strike", &berserk_move_attack_strike}, {"berserk_move_attack_club", &berserk_move_attack_club}, {"berserk_move_attack_spike", &berserk_move_attack_spike}, {"berserk_move_run1", &berserk_move_run1}, {"berserk_move_walk", &berserk_move_walk}, {"berserk_move_stand_fidget", &berserk_move_stand_fidget}, {"berserk_move_stand", &berserk_move_stand}, {0, 0} yquake2-QUAKE2_5_32/src/game/savegame/savegame.c0000644000175000017500000005246312615162034022055 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2011 Knightmare * Copyright (C) 2011 Yamagi Burmeister * * This program is free software; you can 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 savegame system. * * ======================================================================= */ /* * This is the Quake 2 savegame system, fixed by Yamagi * based on an idea by Knightmare of kmquake2. This major * rewrite of the original g_save.c is much more robust * and portable since it doesn't use any function pointers. * * Inner workings: * When the game is saved all function pointers are * translated into human readable function definition strings. * The same way all mmove_t pointers are translated. This * human readable strings are then written into the file. * At game load the human readable strings are retranslated * into the actual function pointers and struct pointers. The * pointers are generated at each compilation / start of the * client, thus the pointers are always correct. * * Limitations: * While savegames survive recompilations of the game source * and bigger changes in the source, there are some limitation * which a nearly impossible to fix without a object orientated * rewrite of the game. * - If functions or mmove_t structs that a referencenced * inside savegames are added or removed (e.g. the files * in tables/ are altered) the load functions cannot * reconnect all pointers and thus not restore the game. * - If the operating system is changed internal structures * may change in an unrepairable way. * - If the architecture is changed pointer length and * other internal datastructures change in an * incompatible way. * - If the edict_t struct is changed, savegames * will break. * This is not so bad as it looks since functions and * struct won't be added and edict_t won't be changed * if no big, sweeping changes are done. The operating * system and architecture are in the hands of the user. */ #include "../header/local.h" /* * When ever the savegame version * is changed, q2 will refuse to * load older savegames. This * should be bumped if the files * in tables/ are changed, otherwise * strange things may happen. */ #define SAVEGAMEVER "YQ2-1" /* * This macros are used to * prohibit loading of savegames * created on other systems or * architectures. This will * crash q2 in spectacular * ways */ #if defined(__APPLE__) #define OS "MacOS X" #elif defined(__FreeBSD__) #define OS "FreeBSD" #elif defined(__OpenBSD__) #define OS "OpenBSD" #elif defined(__linux__) #define OS "Linux" #elif defined(_WIN32) #define OS "Windows" #else #define OS "Unknown" #endif #if defined(__i386__) #define ARCH "i386" #elif defined(__x86_64__) #define ARCH "amd64" #elif defined(__sparc__) #define ARCH "sparc64" #elif defined(__ia64__) #define ARCH "ia64" #else #define ARCH "unknown" #endif /* * Connects a human readable * function signature with * the corresponding pointer */ typedef struct { char *funcStr; byte *funcPtr; } functionList_t; /* * Connects a human readable * mmove_t string with the * corresponding pointer * */ typedef struct { char *mmoveStr; mmove_t *mmovePtr; } mmoveList_t; /* ========================================================= */ /* * Prototypes for forward * declaration for all game * functions. */ #include "tables/gamefunc_decs.h" /* * List with function pointer * to each of the functions * prototyped above. */ functionList_t functionList[] = { #include "tables/gamefunc_list.h" }; /* * Prototypes for forward * declaration for all game * mmove_t functions. */ #include "tables/gamemmove_decs.h" /* * List with pointers to * each of the mmove_t * functions prototyped * above. */ mmoveList_t mmoveList[] = { #include "tables/gamemmove_list.h" }; /* * Fields to be saved */ field_t fields[] = { #include "tables/fields.h" }; /* * Level fields to * be saved */ field_t levelfields[] = { #include "tables/levelfields.h" }; /* * Client fields to * be saved */ field_t clientfields[] = { #include "tables/clientfields.h" }; /* ========================================================= */ /* * This will be called when the dll is first loaded, * which only happens when a new game is started or * a save game is loaded. */ void InitGame(void) { gi.dprintf("Game is starting up.\n"); gi.dprintf("Game is %s built on %s.\n", GAMEVERSION, __DATE__); gun_x = gi.cvar("gun_x", "0", 0); gun_y = gi.cvar("gun_y", "0", 0); gun_z = gi.cvar("gun_z", "0", 0); sv_rollspeed = gi.cvar("sv_rollspeed", "200", 0); sv_rollangle = gi.cvar("sv_rollangle", "2", 0); sv_maxvelocity = gi.cvar("sv_maxvelocity", "2000", 0); sv_gravity = gi.cvar("sv_gravity", "800", 0); /* noset vars */ dedicated = gi.cvar("dedicated", "0", CVAR_NOSET); /* latched vars */ sv_cheats = gi.cvar("cheats", "0", CVAR_SERVERINFO | CVAR_LATCH); gi.cvar("gamename", GAMEVERSION, CVAR_SERVERINFO | CVAR_LATCH); gi.cvar("gamedate", __DATE__, CVAR_SERVERINFO | CVAR_LATCH); maxclients = gi.cvar("maxclients", "4", CVAR_SERVERINFO | CVAR_LATCH); maxspectators = gi.cvar("maxspectators", "4", CVAR_SERVERINFO); deathmatch = gi.cvar("deathmatch", "0", CVAR_LATCH); coop = gi.cvar("coop", "0", CVAR_LATCH); skill = gi.cvar("skill", "1", CVAR_LATCH); maxentities = gi.cvar("maxentities", "1024", CVAR_LATCH); /* change anytime vars */ dmflags = gi.cvar("dmflags", "0", CVAR_SERVERINFO); fraglimit = gi.cvar("fraglimit", "0", CVAR_SERVERINFO); timelimit = gi.cvar("timelimit", "0", CVAR_SERVERINFO); password = gi.cvar("password", "", CVAR_USERINFO); spectator_password = gi.cvar("spectator_password", "", CVAR_USERINFO); needpass = gi.cvar("needpass", "0", CVAR_SERVERINFO); filterban = gi.cvar("filterban", "1", 0); g_select_empty = gi.cvar("g_select_empty", "0", CVAR_ARCHIVE); run_pitch = gi.cvar("run_pitch", "0.002", 0); run_roll = gi.cvar("run_roll", "0.005", 0); bob_up = gi.cvar("bob_up", "0.005", 0); bob_pitch = gi.cvar("bob_pitch", "0.002", 0); bob_roll = gi.cvar("bob_roll", "0.002", 0); /* flood control */ flood_msgs = gi.cvar("flood_msgs", "4", 0); flood_persecond = gi.cvar("flood_persecond", "4", 0); flood_waitdelay = gi.cvar("flood_waitdelay", "10", 0); /* dm map list */ sv_maplist = gi.cvar("sv_maplist", "", 0); /* items */ InitItems(); game.helpmessage1[0] = 0; game.helpmessage2[0] = 0; /* initialize all entities for this game */ game.maxentities = maxentities->value; g_edicts = gi.TagMalloc(game.maxentities * sizeof(g_edicts[0]), TAG_GAME); globals.edicts = g_edicts; globals.max_edicts = game.maxentities; /* initialize all clients for this game */ game.maxclients = maxclients->value; game.clients = gi.TagMalloc(game.maxclients * sizeof(game.clients[0]), TAG_GAME); globals.num_edicts = game.maxclients + 1; } /* ========================================================= */ /* * Helper function to get * the human readable function * definition by an address. * Called by WriteField1 and * WriteField2. */ functionList_t * GetFunctionByAddress(byte *adr) { int i; for (i = 0; functionList[i].funcStr; i++) { if (functionList[i].funcPtr == adr) { return &functionList[i]; } } return NULL; } /* * Helper function to get the * pointer to a function by * it's human readable name. * Called by WriteField1 and * WriteField2. */ byte * FindFunctionByName(char *name) { int i; for (i = 0; functionList[i].funcStr; i++) { if (!strcmp(name, functionList[i].funcStr)) { return functionList[i].funcPtr; } } return NULL; } /* * Helper function to get the * human readable definition of * a mmove_t struct by a pointer. */ mmoveList_t * GetMmoveByAddress(mmove_t *adr) { int i; for (i = 0; mmoveList[i].mmoveStr; i++) { if (mmoveList[i].mmovePtr == adr) { return &mmoveList[i]; } } return NULL; } /* * Helper function to get the * pointer to a mmove_t struct * by a human readable definition. */ mmove_t * FindMmoveByName(char *name) { int i; for (i = 0; mmoveList[i].mmoveStr; i++) { if (!strcmp(name, mmoveList[i].mmoveStr)) { return mmoveList[i].mmovePtr; } } return NULL; } /* ========================================================= */ /* * The following two functions are * doing the dirty work to write the * data generated by the functions * below this block into files. */ void WriteField1(FILE *f, field_t *field, byte *base) { void *p; int len; int index; functionList_t *func; mmoveList_t *mmove; if (field->flags & FFL_SPAWNTEMP) { return; } p = (void *)(base + field->ofs); switch (field->type) { case F_INT: case F_FLOAT: case F_ANGLEHACK: case F_VECTOR: case F_IGNORE: break; case F_LSTRING: case F_GSTRING: if (*(char **)p) { len = strlen(*(char **)p) + 1; } else { len = 0; } *(int *)p = len; break; case F_EDICT: if (*(edict_t **)p == NULL) { index = -1; } else { index = *(edict_t **)p - g_edicts; } *(int *)p = index; break; case F_CLIENT: if (*(gclient_t **)p == NULL) { index = -1; } else { index = *(gclient_t **)p - game.clients; } *(int *)p = index; break; case F_ITEM: if (*(edict_t **)p == NULL) { index = -1; } else { index = *(gitem_t **)p - itemlist; } *(int *)p = index; break; case F_FUNCTION: if (*(byte **)p == NULL) { len = 0; } else { func = GetFunctionByAddress (*(byte **)p); if (!func) { gi.error ("WriteField1: function not in list, can't save game"); } len = strlen(func->funcStr)+1; } *(int *)p = len; break; case F_MMOVE: if (*(byte **)p == NULL) { len = 0; } else { mmove = GetMmoveByAddress (*(mmove_t **)p); if (!mmove) { gi.error ("WriteField1: mmove not in list, can't save game"); } len = strlen(mmove->mmoveStr)+1; } *(int *)p = len; break; default: gi.error("WriteEdict: unknown field type"); } } void WriteField2(FILE *f, field_t *field, byte *base) { int len; void *p; functionList_t *func; mmoveList_t *mmove; if (field->flags & FFL_SPAWNTEMP) { return; } p = (void *)(base + field->ofs); switch (field->type) { case F_LSTRING: if (*(char **)p) { len = strlen(*(char **)p) + 1; fwrite(*(char **)p, len, 1, f); } break; case F_FUNCTION: if (*(byte **)p) { func = GetFunctionByAddress (*(byte **)p); if (!func) { gi.error ("WriteField2: function not in list, can't save game"); } len = strlen(func->funcStr)+1; fwrite (func->funcStr, len, 1, f); } break; case F_MMOVE: if (*(byte **)p) { mmove = GetMmoveByAddress (*(mmove_t **)p); if (!mmove) { gi.error ("WriteField2: mmove not in list, can't save game"); } len = strlen(mmove->mmoveStr)+1; fwrite (mmove->mmoveStr, len, 1, f); } break; default: break; } } /* ========================================================= */ /* * This function does the dirty * work to read the data from a * file. The processing of the * data is done in the functions * below */ void ReadField(FILE *f, field_t *field, byte *base) { void *p; int len; int index; char funcStr[2048]; if (field->flags & FFL_SPAWNTEMP) { return; } p = (void *)(base + field->ofs); switch (field->type) { case F_INT: case F_FLOAT: case F_ANGLEHACK: case F_VECTOR: case F_IGNORE: break; case F_LSTRING: len = *(int *)p; if (!len) { *(char **)p = NULL; } else { *(char **)p = gi.TagMalloc(32 + len, TAG_LEVEL); fread(*(char **)p, len, 1, f); } break; case F_EDICT: index = *(int *)p; if (index == -1) { *(edict_t **)p = NULL; } else { *(edict_t **)p = &g_edicts[index]; } break; case F_CLIENT: index = *(int *)p; if (index == -1) { *(gclient_t **)p = NULL; } else { *(gclient_t **)p = &game.clients[index]; } break; case F_ITEM: index = *(int *)p; if (index == -1) { *(gitem_t **)p = NULL; } else { *(gitem_t **)p = &itemlist[index]; } break; case F_FUNCTION: len = *(int *)p; if (!len) { *(byte **)p = NULL; } else { if (len > sizeof(funcStr)) { gi.error ("ReadField: function name is longer than buffer (%i chars)", (int)sizeof(funcStr)); } fread (funcStr, len, 1, f); if ( !(*(byte **)p = FindFunctionByName (funcStr)) ) { gi.error ("ReadField: function %s not found in table, can't load game", funcStr); } } break; case F_MMOVE: len = *(int *)p; if (!len) { *(byte **)p = NULL; } else { if (len > sizeof(funcStr)) { gi.error ("ReadField: mmove name is longer than buffer (%i chars)", (int)sizeof(funcStr)); } fread (funcStr, len, 1, f); if ( !(*(mmove_t **)p = FindMmoveByName (funcStr)) ) { gi.error ("ReadField: mmove %s not found in table, can't load game", funcStr); } } break; default: gi.error("ReadEdict: unknown field type"); } } /* ========================================================= */ /* * Write the client struct into a file. */ void WriteClient(FILE *f, gclient_t *client) { field_t *field; gclient_t temp; /* all of the ints, floats, and vectors stay as they are */ temp = *client; /* change the pointers to indexes */ for (field = clientfields; field->name; field++) { WriteField1(f, field, (byte *)&temp); } /* write the block */ fwrite(&temp, sizeof(temp), 1, f); /* now write any allocated data following the edict */ for (field = clientfields; field->name; field++) { WriteField2(f, field, (byte *)client); } } /* * Read the client struct from a file */ void ReadClient(FILE *f, gclient_t *client) { field_t *field; fread(client, sizeof(*client), 1, f); for (field = clientfields; field->name; field++) { ReadField(f, field, (byte *)client); } } /* ========================================================= */ /* * Writes the game struct into * a file. This is called when * ever the games goes to e new * level or the user saves the * game. Saved informations are: * - cross level data * - client states * - help computer info */ void WriteGame(const char *filename, qboolean autosave) { FILE *f; int i; char str_ver[32]; char str_game[32]; char str_os[32]; char str_arch[32]; if (!autosave) { SaveClientData(); } f = fopen(filename, "wb"); if (!f) { gi.error("Couldn't open %s", filename); } /* Savegame identification */ memset(str_ver, 0, sizeof(str_ver)); memset(str_game, 0, sizeof(str_game)); memset(str_os, 0, sizeof(str_os)); memset(str_arch, 0, sizeof(str_arch)); Q_strlcpy(str_ver, SAVEGAMEVER, sizeof(str_ver)); Q_strlcpy(str_game, GAMEVERSION, sizeof(str_game)); Q_strlcpy(str_os, OS, sizeof(str_os)); Q_strlcpy(str_arch, ARCH, sizeof(str_arch)); fwrite(str_ver, sizeof(str_ver), 1, f); fwrite(str_game, sizeof(str_game), 1, f); fwrite(str_os, sizeof(str_os), 1, f); fwrite(str_arch, sizeof(str_arch), 1, f); game.autosaved = autosave; fwrite(&game, sizeof(game), 1, f); game.autosaved = false; for (i = 0; i < game.maxclients; i++) { WriteClient(f, &game.clients[i]); } fclose(f); } /* * Read the game structs from * a file. Called when ever a * savegames is loaded. */ void ReadGame(const char *filename) { FILE *f; int i; char str_ver[32]; char str_game[32]; char str_os[32]; char str_arch[32]; gi.FreeTags(TAG_GAME); f = fopen(filename, "rb"); if (!f) { gi.error("Couldn't open %s", filename); } /* Sanity checks */ fread(str_ver, sizeof(str_ver), 1, f); fread(str_game, sizeof(str_game), 1, f); fread(str_os, sizeof(str_os), 1, f); fread(str_arch, sizeof(str_arch), 1, f); if (strcmp(str_ver, SAVEGAMEVER)) { fclose(f); gi.error("Savegame from an incompatible version.\n"); } else if (strcmp(str_game, GAMEVERSION)) { fclose(f); gi.error("Savegame from an other game.so.\n"); } else if (strcmp(str_os, OS)) { fclose(f); gi.error("Savegame from an other os.\n"); } else if (strcmp(str_arch, ARCH)) { fclose(f); gi.error("Savegame from an other architecure.\n"); } g_edicts = gi.TagMalloc(game.maxentities * sizeof(g_edicts[0]), TAG_GAME); globals.edicts = g_edicts; fread(&game, sizeof(game), 1, f); game.clients = gi.TagMalloc(game.maxclients * sizeof(game.clients[0]), TAG_GAME); for (i = 0; i < game.maxclients; i++) { ReadClient(f, &game.clients[i]); } fclose(f); } /* ========================================================== */ /* * Helper function to write the * edict into a file. Called by * WriteLevel. */ void WriteEdict(FILE *f, edict_t *ent) { field_t *field; edict_t temp; /* all of the ints, floats, and vectors stay as they are */ temp = *ent; /* change the pointers to lengths or indexes */ for (field = fields; field->name; field++) { WriteField1(f, field, (byte *)&temp); } /* write the block */ fwrite(&temp, sizeof(temp), 1, f); /* now write any allocated data following the edict */ for (field = fields; field->name; field++) { WriteField2(f, field, (byte *)ent); } } /* * Helper function to write the * level local data into a file. * Called by WriteLevel. */ void WriteLevelLocals(FILE *f) { field_t *field; level_locals_t temp; /* all of the ints, floats, and vectors stay as they are */ temp = level; /* change the pointers to lengths or indexes */ for (field = levelfields; field->name; field++) { WriteField1(f, field, (byte *)&temp); } /* write the block */ fwrite(&temp, sizeof(temp), 1, f); /* now write any allocated data following the edict */ for (field = levelfields; field->name; field++) { WriteField2(f, field, (byte *)&level); } } /* * Writes the current level * into a file. */ void WriteLevel(const char *filename) { int i; edict_t *ent; FILE *f; f = fopen(filename, "wb"); if (!f) { gi.error("Couldn't open %s", filename); } /* write out edict size for checking */ i = sizeof(edict_t); fwrite(&i, sizeof(i), 1, f); /* write out level_locals_t */ WriteLevelLocals(f); /* write out all the entities */ for (i = 0; i < globals.num_edicts; i++) { ent = &g_edicts[i]; if (!ent->inuse) { continue; } fwrite(&i, sizeof(i), 1, f); WriteEdict(f, ent); } i = -1; fwrite(&i, sizeof(i), 1, f); fclose(f); } /* ========================================================== */ /* * A helper function to * read the edict back * into the memory. Called * by ReadLevel. */ void ReadEdict(FILE *f, edict_t *ent) { field_t *field; fread(ent, sizeof(*ent), 1, f); for (field = fields; field->name; field++) { ReadField(f, field, (byte *)ent); } } /* * A helper function to * read the level local * data from a file. * Called by ReadLevel. */ void ReadLevelLocals(FILE *f) { field_t *field; fread(&level, sizeof(level), 1, f); for (field = levelfields; field->name; field++) { ReadField(f, field, (byte *)&level); } } /* * Reads a level back into the memory. * SpawnEntities were already called * in the same way when the level was * saved. All world links were cleared * before this function was called. When * this function is called, no clients * are connected to the server. */ void ReadLevel(const char *filename) { int entnum; FILE *f; int i; edict_t *ent; f = fopen(filename, "rb"); if (!f) { gi.error("Couldn't open %s", filename); } /* free any dynamic memory allocated by loading the level base state */ gi.FreeTags(TAG_LEVEL); /* wipe all the entities */ memset(g_edicts, 0, game.maxentities * sizeof(g_edicts[0])); globals.num_edicts = maxclients->value + 1; /* check edict size */ fread(&i, sizeof(i), 1, f); if (i != sizeof(edict_t)) { fclose(f); gi.error("ReadLevel: mismatched edict size"); } /* load the level locals */ ReadLevelLocals(f); /* load all the entities */ while (1) { if (fread(&entnum, sizeof(entnum), 1, f) != 1) { fclose(f); gi.error("ReadLevel: failed to read entnum"); } if (entnum == -1) { break; } if (entnum >= globals.num_edicts) { globals.num_edicts = entnum + 1; } ent = &g_edicts[entnum]; ReadEdict(f, ent); /* let the server rebuild world links for this ent */ memset(&ent->area, 0, sizeof(ent->area)); gi.linkentity(ent); } fclose(f); /* mark all clients as unconnected */ for (i = 0; i < maxclients->value; i++) { ent = &g_edicts[i + 1]; ent->client = game.clients + i; ent->client->pers.connected = false; } /* do any load time things at this point */ for (i = 0; i < globals.num_edicts; i++) { ent = &g_edicts[i]; if (!ent->inuse) { continue; } /* fire any cross-level triggers */ if (ent->classname) { if (strcmp(ent->classname, "target_crosslevel_target") == 0) { ent->nextthink = level.time + ent->delay; } } } } yquake2-QUAKE2_5_32/src/game/g_trigger.c0000644000175000017500000003465312615162034020447 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Trigger. * * ======================================================================= */ #include "header/local.h" #define PUSH_ONCE 1 static int windsound; void InitTrigger(edict_t *self) { if (!self) { return; } if (!VectorCompare(self->s.angles, vec3_origin)) { G_SetMovedir(self->s.angles, self->movedir); } self->solid = SOLID_TRIGGER; self->movetype = MOVETYPE_NONE; gi.setmodel(self, self->model); self->svflags = SVF_NOCLIENT; } /* * The wait time has passed, so * set back up for another activation */ void multi_wait(edict_t *ent) { if (!ent) { return; } ent->nextthink = 0; } /* * The trigger was just activated * ent->activator should be set to * the activator so it can be held * through a delay so wait for the * delay time before firing */ void multi_trigger(edict_t *ent) { if (!ent) { return; } if (ent->nextthink) { return; /* already been triggered */ } G_UseTargets(ent, ent->activator); if (ent->wait > 0) { ent->think = multi_wait; ent->nextthink = level.time + ent->wait; } else { /* we can't just remove (self) here, because this is a touch function called while looping through area links... */ ent->touch = NULL; ent->nextthink = level.time + FRAMETIME; ent->think = G_FreeEdict; } } void Use_Multi(edict_t *ent, edict_t *other /* unused */, edict_t *activator) { if (!ent || !activator) { return; } ent->activator = activator; multi_trigger(ent); } void Touch_Multi(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { if (!self || !other) { return; } if (other->client) { if (self->spawnflags & 2) { return; } } else if (other->svflags & SVF_MONSTER) { if (!(self->spawnflags & 1)) { return; } } else { return; } if (!VectorCompare(self->movedir, vec3_origin)) { vec3_t forward; AngleVectors(other->s.angles, forward, NULL, NULL); if (_DotProduct(forward, self->movedir) < 0) { return; } } self->activator = other; multi_trigger(self); } /* * QUAKED trigger_multiple (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED * Variable sized repeatable trigger. Must be targeted at one or more * entities. If "delay" is set, the trigger waits some time after * activating before firing. * * "wait" : Seconds between triggerings. (.2 default) * * sounds * 1) secret * 2) beep beep * 3) large switch * 4) * * set "message" to text string */ void trigger_enable(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!self) { return; } self->solid = SOLID_TRIGGER; self->use = Use_Multi; gi.linkentity(self); } void SP_trigger_multiple(edict_t *ent) { if (!ent) { return; } if (ent->sounds == 1) { ent->noise_index = gi.soundindex("misc/secret.wav"); } else if (ent->sounds == 2) { ent->noise_index = gi.soundindex("misc/talk.wav"); } else if (ent->sounds == 3) { ent->noise_index = gi.soundindex ("misc/trigger1.wav"); } if (!ent->wait) { ent->wait = 0.2; } ent->touch = Touch_Multi; ent->movetype = MOVETYPE_NONE; ent->svflags |= SVF_NOCLIENT; if (ent->spawnflags & 4) { ent->solid = SOLID_NOT; ent->use = trigger_enable; } else { ent->solid = SOLID_TRIGGER; ent->use = Use_Multi; } if (!VectorCompare(ent->s.angles, vec3_origin)) { G_SetMovedir(ent->s.angles, ent->movedir); } gi.setmodel(ent, ent->model); gi.linkentity(ent); } /* * QUAKED trigger_once (.5 .5 .5) ? x x TRIGGERED * Triggers once, then removes itself. * * You must set the key "target" to the name of another * object in the level that has a matching "targetname". * * If TRIGGERED, this trigger must be triggered before it is live. * * sounds * 1) secret * 2) beep beep * 3) large switch * * "message" string to be displayed when triggered */ void SP_trigger_once(edict_t *ent) { if (!ent) { return; } /* make old maps work because I messed up on flag assignments here triggered was on bit 1 when it should have been on bit 4 */ if (ent->spawnflags & 1) { vec3_t v; VectorMA(ent->mins, 0.5, ent->size, v); ent->spawnflags &= ~1; ent->spawnflags |= 4; gi.dprintf("fixed TRIGGERED flag on %s at %s\n", ent->classname, vtos(v)); } ent->wait = -1; SP_trigger_multiple(ent); } /* * QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8) * This fixed size trigger cannot be touched, * it can only be fired by other events. */ void trigger_relay_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) { if (!self || !activator) { return; } G_UseTargets(self, activator); } void SP_trigger_relay(edict_t *self) { if (!self) { return; } self->use = trigger_relay_use; } /* * QUAKED trigger_key (.5 .5 .5) (-8 -8 -8) (8 8 8) * A relay trigger that only fires it's targets if player * has the proper key. Use "item" to specify the required key, * for example "key_data_cd" */ void trigger_key_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) { int index; if (!self || !activator) { return; } if (!self->item) { return; } if (!activator->client) { return; } index = ITEM_INDEX(self->item); if (!activator->client->pers.inventory[index]) { if (level.time < self->touch_debounce_time) { return; } self->touch_debounce_time = level.time + 5.0; gi.centerprintf(activator, "You need the %s", self->item->pickup_name); gi.sound(activator, CHAN_AUTO, gi.soundindex( "misc/keytry.wav"), 1, ATTN_NORM, 0); return; } gi.sound(activator, CHAN_AUTO, gi.soundindex( "misc/keyuse.wav"), 1, ATTN_NORM, 0); if (coop->value) { int player; edict_t *ent; if (strcmp(self->item->classname, "key_power_cube") == 0) { int cube; for (cube = 0; cube < 8; cube++) { if (activator->client->pers.power_cubes & (1 << cube)) { break; } } for (player = 1; player <= game.maxclients; player++) { ent = &g_edicts[player]; if (!ent->inuse) { continue; } if (!ent->client) { continue; } if (ent->client->pers.power_cubes & (1 << cube)) { ent->client->pers.inventory[index]--; ent->client->pers.power_cubes &= ~(1 << cube); } } } else { for (player = 1; player <= game.maxclients; player++) { ent = &g_edicts[player]; if (!ent->inuse) { continue; } if (!ent->client) { continue; } ent->client->pers.inventory[index] = 0; } } } else { activator->client->pers.inventory[index]--; } G_UseTargets(self, activator); self->use = NULL; } void SP_trigger_key(edict_t *self) { if (!self) { return; } if (!st.item) { gi.dprintf("no key item for trigger_key at %s\n", vtos(self->s.origin)); return; } self->item = FindItemByClassname(st.item); if (!self->item) { gi.dprintf("item %s not found for trigger_key at %s\n", st.item, vtos(self->s.origin)); return; } if (!self->target) { gi.dprintf("%s at %s has no target\n", self->classname, vtos(self->s.origin)); return; } gi.soundindex("misc/keytry.wav"); gi.soundindex("misc/keyuse.wav"); self->use = trigger_key_use; } /* * QUAKED trigger_counter (.5 .5 .5) ? nomessage * Acts as an intermediary for an action that takes multiple inputs. * * If nomessage is not set, it will print "1 more.. " etc when * triggered and "sequence complete" when finished. * * After the counter has been triggered "count" times (default 2), * it will fire all of it's targets and remove itself. */ void trigger_counter_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) { if (!self || !activator) { return; } if (self->count == 0) { return; } self->count--; if (self->count) { if (!(self->spawnflags & 1)) { gi.centerprintf(activator, "%i more to go...", self->count); gi.sound(activator, CHAN_AUTO, gi.soundindex( "misc/talk1.wav"), 1, ATTN_NORM, 0); } return; } if (!(self->spawnflags & 1)) { gi.centerprintf(activator, "Sequence completed!"); gi.sound(activator, CHAN_AUTO, gi.soundindex( "misc/talk1.wav"), 1, ATTN_NORM, 0); } self->activator = activator; multi_trigger(self); } void SP_trigger_counter(edict_t *self) { if (!self) { return; } self->wait = -1; if (!self->count) { self->count = 2; } self->use = trigger_counter_use; } /* * QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8) * This trigger will always fire. It is activated by the world. */ void SP_trigger_always(edict_t *ent) { if (!ent) { return; } /* we must have some delay to make sure our use targets are present */ if (ent->delay < 0.2) { ent->delay = 0.2; } G_UseTargets(ent, ent); } void trigger_push_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { if (!self || !other) { return; } if (strcmp(other->classname, "grenade") == 0) { VectorScale(self->movedir, self->speed * 10, other->velocity); } else if (other->health > 0) { VectorScale(self->movedir, self->speed * 10, other->velocity); if (other->client) { /* don't take falling damage immediately from this */ VectorCopy(other->velocity, other->client->oldvelocity); if (other->fly_sound_debounce_time < level.time) { other->fly_sound_debounce_time = level.time + 1.5; gi.sound(other, CHAN_AUTO, windsound, 1, ATTN_NORM, 0); } } } if (self->spawnflags & PUSH_ONCE) { G_FreeEdict(self); } } /* * QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE * Pushes the player * * "speed" defaults to 1000 */ void SP_trigger_push(edict_t *self) { if (!self) { return; } InitTrigger(self); windsound = gi.soundindex("misc/windfly.wav"); self->touch = trigger_push_touch; if (!self->speed) { self->speed = 1000; } gi.linkentity(self); } /* * QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW * Any entity that touches this will be hurt. * * It does dmg points of damage each server frame * * SILENT supresses playing the sound * SLOW changes the damage rate to once per second * NO_PROTECTION *nothing* stops the damage * * "dmg" default 5 (whole numbers only) */ void hurt_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { int dflags; if (!self || !other) { return; } if (!other->takedamage) { return; } if (self->timestamp > level.time) { return; } if (self->spawnflags & 16) { self->timestamp = level.time + 1; } else { self->timestamp = level.time + FRAMETIME; } if (!(self->spawnflags & 4)) { if ((level.framenum % 10) == 0) { gi.sound(other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM, 0); } } if (self->spawnflags & 8) { dflags = DAMAGE_NO_PROTECTION; } else { dflags = 0; } T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, self->dmg, dflags, MOD_TRIGGER_HURT); } void hurt_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!self) { return; } if (self->solid == SOLID_NOT) { int i, num; edict_t *touch[MAX_EDICTS], *hurtme; self->solid = SOLID_TRIGGER; num = gi.BoxEdicts (self->absmin, self->absmax, touch, MAX_EDICTS, AREA_SOLID); /* Check for idle monsters in trigger hurt */ for (i = 0 ; i < num ; i++) { hurtme = touch[i]; hurt_touch (self, hurtme, NULL, NULL); } } else { self->solid = SOLID_NOT; } gi.linkentity(self); if (!(self->spawnflags & 2)) { self->use = NULL; } } void SP_trigger_hurt(edict_t *self) { if (!self) { return; } InitTrigger(self); self->noise_index = gi.soundindex("world/electro.wav"); self->touch = hurt_touch; if (!self->dmg) { self->dmg = 5; } if (self->spawnflags & 1) { self->solid = SOLID_NOT; } else { self->solid = SOLID_TRIGGER; } if (self->spawnflags & 2) { self->use = hurt_use; } gi.linkentity(self); } /* * QUAKED trigger_gravity (.5 .5 .5) ? * Changes the touching entites gravity to * the value of "gravity". 1.0 is standard * gravity for the level. */ void trigger_gravity_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { if (!self || !other) { return; } other->gravity = self->gravity; } void SP_trigger_gravity(edict_t *self) { if (!self) { return; } if (st.gravity == 0) { gi.dprintf("trigger_gravity without gravity set at %s\n", vtos(self->s.origin)); G_FreeEdict(self); return; } InitTrigger(self); self->gravity = (int)strtol(st.gravity, (char **)NULL, 10); self->touch = trigger_gravity_touch; } /* * QUAKED trigger_monsterjump (.5 .5 .5) ? * Walking monsters that touch this will jump in the direction of the trigger's angle * * "speed" default to 200, the speed thrown forward * "height" default to 200, the speed thrown upwards */ void trigger_monsterjump_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { if (!self || !other) { return; } if (other->flags & (FL_FLY | FL_SWIM)) { return; } if (other->svflags & SVF_DEADMONSTER) { return; } if (!(other->svflags & SVF_MONSTER)) { return; } /* set XY even if not on ground, so the jump will clear lips */ other->velocity[0] = self->movedir[0] * self->speed; other->velocity[1] = self->movedir[1] * self->speed; if (!other->groundentity) { return; } other->groundentity = NULL; other->velocity[2] = self->movedir[2]; } void SP_trigger_monsterjump(edict_t *self) { if (!self) { return; } if (!self->speed) { self->speed = 200; } if (!st.height) { st.height = 200; } if (self->s.angles[YAW] == 0) { self->s.angles[YAW] = 360; } InitTrigger(self); self->touch = trigger_monsterjump_touch; self->movedir[2] = st.height; } yquake2-QUAKE2_5_32/src/game/g_weapon.c0000644000175000017500000006113512615162034020270 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Weapon support functions. * * ======================================================================= */ #include "header/local.h" /* * This is a support routine used when a client is firing * a non-instant attack weapon. It checks to see if a * monster's dodge function should be called. */ void check_dodge(edict_t *self, vec3_t start, vec3_t dir, int speed) { vec3_t end; vec3_t v; trace_t tr; float eta; if (!self) { return; } /* easy mode only ducks one quarter the time */ if (skill->value == 0) { if (random() > 0.25) { return; } } VectorMA(start, 8192, dir, end); tr = gi.trace(start, NULL, NULL, end, self, MASK_SHOT); if ((tr.ent) && (tr.ent->svflags & SVF_MONSTER) && (tr.ent->health > 0) && (tr.ent->monsterinfo.dodge) && infront(tr.ent, self)) { VectorSubtract(tr.endpos, start, v); eta = (VectorLength(v) - tr.ent->maxs[0]) / speed; tr.ent->monsterinfo.dodge(tr.ent, self, eta); } } /* * Used for all impact (hit/punch/slash) attacks */ qboolean fire_hit(edict_t *self, vec3_t aim, int damage, int kick) { trace_t tr; vec3_t forward, right, up; vec3_t v; vec3_t point; float range; vec3_t dir; if (!self) { return false; } /* Lazarus: Paranoia check */ if (!self->enemy) { return false; } /* see if enemy is in range */ VectorSubtract(self->enemy->s.origin, self->s.origin, dir); range = VectorLength(dir); if (range > aim[0]) { return false; } if ((aim[1] > self->mins[0]) && (aim[1] < self->maxs[0])) { /* the hit is straight on so back the range up to the edge of their bbox */ range -= self->enemy->maxs[0]; } else { /* this is a side hit so adjust the "right" value out to the edge of their bbox */ if (aim[1] < 0) { aim[1] = self->enemy->mins[0]; } else { aim[1] = self->enemy->maxs[0]; } } VectorMA(self->s.origin, range, dir, point); tr = gi.trace(self->s.origin, NULL, NULL, point, self, MASK_SHOT); if (tr.fraction < 1) { if (!tr.ent->takedamage) { return false; } /* if it will hit any client/monster then hit the one we wanted to hit */ if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client)) { tr.ent = self->enemy; } } AngleVectors(self->s.angles, forward, right, up); VectorMA(self->s.origin, range, forward, point); VectorMA(point, aim[1], right, point); VectorMA(point, aim[2], up, point); VectorSubtract(point, self->enemy->s.origin, dir); /* do the damage */ T_Damage(tr.ent, self, self, dir, point, vec3_origin, damage, kick / 2, DAMAGE_NO_KNOCKBACK, MOD_HIT); if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) { return false; } /* do our special form of knockback here */ VectorMA(self->enemy->absmin, 0.5, self->enemy->size, v); VectorSubtract(v, point, v); VectorNormalize(v); VectorMA(self->enemy->velocity, kick, v, self->enemy->velocity); if (self->enemy->velocity[2] > 0) { self->enemy->groundentity = NULL; } return true; } /* * This is an internal support routine * used for bullet/pellet based weapons. */ void fire_lead(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod) { trace_t tr; vec3_t dir; vec3_t forward, right, up; vec3_t end; float r; float u; vec3_t water_start; qboolean water = false; int content_mask = MASK_SHOT | MASK_WATER; if (!self) { return; } tr = gi.trace(self->s.origin, NULL, NULL, start, self, MASK_SHOT); if (!(tr.fraction < 1.0)) { vectoangles(aimdir, dir); AngleVectors(dir, forward, right, up); r = crandom() * hspread; u = crandom() * vspread; VectorMA(start, 8192, forward, end); VectorMA(end, r, right, end); VectorMA(end, u, up, end); if (gi.pointcontents(start) & MASK_WATER) { water = true; VectorCopy(start, water_start); content_mask &= ~MASK_WATER; } tr = gi.trace(start, NULL, NULL, end, self, content_mask); /* see if we hit water */ if (tr.contents & MASK_WATER) { int color; water = true; VectorCopy(tr.endpos, water_start); if (!VectorCompare(start, tr.endpos)) { if (tr.contents & CONTENTS_WATER) { if (strcmp(tr.surface->name, "*brwater") == 0) { color = SPLASH_BROWN_WATER; } else { color = SPLASH_BLUE_WATER; } } else if (tr.contents & CONTENTS_SLIME) { color = SPLASH_SLIME; } else if (tr.contents & CONTENTS_LAVA) { color = SPLASH_LAVA; } else { color = SPLASH_UNKNOWN; } if (color != SPLASH_UNKNOWN) { gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_SPLASH); gi.WriteByte(8); gi.WritePosition(tr.endpos); gi.WriteDir(tr.plane.normal); gi.WriteByte(color); gi.multicast(tr.endpos, MULTICAST_PVS); } /* change bullet's course when it enters water */ VectorSubtract(end, start, dir); vectoangles(dir, dir); AngleVectors(dir, forward, right, up); r = crandom() * hspread * 2; u = crandom() * vspread * 2; VectorMA(water_start, 8192, forward, end); VectorMA(end, r, right, end); VectorMA(end, u, up, end); } /* re-trace ignoring water this time */ tr = gi.trace(water_start, NULL, NULL, end, self, MASK_SHOT); } } /* send gun puff / flash */ if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) { if (tr.fraction < 1.0) { if (tr.ent->takedamage) { T_Damage(tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod); } else { if (strncmp(tr.surface->name, "sky", 3) != 0) { gi.WriteByte(svc_temp_entity); gi.WriteByte(te_impact); gi.WritePosition(tr.endpos); gi.WriteDir(tr.plane.normal); gi.multicast(tr.endpos, MULTICAST_PVS); if (self->client) { PlayerNoise(self, tr.endpos, PNOISE_IMPACT); } } } } } /* if went through water, determine where the end and make a bubble trail */ if (water) { vec3_t pos; VectorSubtract(tr.endpos, water_start, dir); VectorNormalize(dir); VectorMA(tr.endpos, -2, dir, pos); if (gi.pointcontents(pos) & MASK_WATER) { VectorCopy(pos, tr.endpos); } else { tr = gi.trace(pos, NULL, NULL, water_start, tr.ent, MASK_WATER); } VectorAdd(water_start, tr.endpos, pos); VectorScale(pos, 0.5, pos); gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_BUBBLETRAIL); gi.WritePosition(water_start); gi.WritePosition(tr.endpos); gi.multicast(pos, MULTICAST_PVS); } } /* * Fires a single round. Used for machinegun and * chaingun. Would be fine for pistols, rifles, etc.... */ void fire_bullet(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod) { if (!self) { return; } fire_lead(self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod); } /* * Shoots shotgun pellets. Used * by shotgun and super shotgun. */ void fire_shotgun(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int mod) { int i; if (!self) { return; } for (i = 0; i < count; i++) { fire_lead(self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod); } } /* * Fires a single blaster bolt. * Used by the blaster and hyper blaster. */ void blaster_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { int mod; if (!self || !other) /* plane and surf can be NULL */ { G_FreeEdict(self); return; } if (other == self->owner) { return; } if (surf && (surf->flags & SURF_SKY)) { G_FreeEdict(self); return; } if (self->owner && self->owner->client) { PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); } if (other->takedamage) { if (self->spawnflags & 1) { mod = MOD_HYPERBLASTER; } else { mod = MOD_BLASTER; } if (plane) { T_Damage(other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, mod); } else { T_Damage(other, self, self->owner, self->velocity, self->s.origin, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, mod); } } else { gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_BLASTER); gi.WritePosition(self->s.origin); if (!plane) { gi.WriteDir(vec3_origin); } else { gi.WriteDir(plane->normal); } gi.multicast(self->s.origin, MULTICAST_PVS); } G_FreeEdict(self); } void fire_blaster(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper) { edict_t *bolt; trace_t tr; if (!self) { return; } VectorNormalize(dir); bolt = G_Spawn(); bolt->svflags = SVF_DEADMONSTER; /* yes, I know it looks weird that projectiles are deadmonsters what this means is that when prediction is used against the object (blaster/hyperblaster shots), the player won't be solid clipped against the object. Right now trying to run into a firing hyperblaster is very jerky since you are predicted 'against' the shots. */ VectorCopy(start, bolt->s.origin); VectorCopy(start, bolt->s.old_origin); vectoangles(dir, bolt->s.angles); VectorScale(dir, speed, bolt->velocity); bolt->movetype = MOVETYPE_FLYMISSILE; bolt->clipmask = MASK_SHOT; bolt->solid = SOLID_BBOX; bolt->s.effects |= effect; bolt->s.renderfx |= RF_NOSHADOW; VectorClear(bolt->mins); VectorClear(bolt->maxs); bolt->s.modelindex = gi.modelindex("models/objects/laser/tris.md2"); bolt->s.sound = gi.soundindex("misc/lasfly.wav"); bolt->owner = self; bolt->touch = blaster_touch; bolt->nextthink = level.time + 2; bolt->think = G_FreeEdict; bolt->dmg = damage; bolt->classname = "bolt"; if (hyper) { bolt->spawnflags = 1; } gi.linkentity(bolt); if (self->client) { check_dodge(self, bolt->s.origin, dir, speed); } tr = gi.trace(self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); if (tr.fraction < 1.0) { VectorMA(bolt->s.origin, -10, dir, bolt->s.origin); bolt->touch(bolt, tr.ent, NULL, NULL); } } void Grenade_Explode(edict_t *ent) { vec3_t origin; int mod; if (!ent) { return; } if (ent->owner && ent->owner->client) { PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); } if (ent->enemy) { float points; vec3_t v; vec3_t dir; VectorAdd(ent->enemy->mins, ent->enemy->maxs, v); VectorMA(ent->enemy->s.origin, 0.5, v, v); VectorSubtract(ent->s.origin, v, v); points = ent->dmg - 0.5 * VectorLength(v); VectorSubtract(ent->enemy->s.origin, ent->s.origin, dir); if (ent->spawnflags & 1) { mod = MOD_HANDGRENADE; } else { mod = MOD_GRENADE; } T_Damage(ent->enemy, ent, ent->owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); } if (ent->spawnflags & 2) { mod = MOD_HELD_GRENADE; } else if (ent->spawnflags & 1) { mod = MOD_HG_SPLASH; } else { mod = MOD_G_SPLASH; } T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod); VectorMA(ent->s.origin, -0.02, ent->velocity, origin); gi.WriteByte(svc_temp_entity); if (ent->waterlevel) { if (ent->groundentity) { gi.WriteByte(TE_GRENADE_EXPLOSION_WATER); } else { gi.WriteByte(TE_ROCKET_EXPLOSION_WATER); } } else { if (ent->groundentity) { gi.WriteByte(TE_GRENADE_EXPLOSION); } else { gi.WriteByte(TE_ROCKET_EXPLOSION); } } gi.WritePosition(origin); gi.multicast(ent->s.origin, MULTICAST_PHS); G_FreeEdict(ent); } void Grenade_Touch(edict_t *ent, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf) { if (!ent || !other) /* plane is unused, surf can be NULL */ { G_FreeEdict(ent); return; } if (other == ent->owner) { return; } if (surf && (surf->flags & SURF_SKY)) { G_FreeEdict(ent); return; } if (!other->takedamage) { if (ent->spawnflags & 1) { if (random() > 0.5) { gi.sound(ent, CHAN_VOICE, gi.soundindex( "weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0); } else { gi.sound(ent, CHAN_VOICE, gi.soundindex( "weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0); } } else { gi.sound(ent, CHAN_VOICE, gi.soundindex( "weapons/grenlb1b.wav"), 1, ATTN_NORM, 0); } return; } ent->enemy = other; Grenade_Explode(ent); } void fire_grenade(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius) { edict_t *grenade; vec3_t dir; vec3_t forward, right, up; if (!self) { return; } vectoangles(aimdir, dir); AngleVectors(dir, forward, right, up); grenade = G_Spawn(); VectorCopy(start, grenade->s.origin); VectorScale(aimdir, speed, grenade->velocity); VectorMA(grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); VectorMA(grenade->velocity, crandom() * 10.0, right, grenade->velocity); VectorSet(grenade->avelocity, 300, 300, 300); grenade->movetype = MOVETYPE_BOUNCE; grenade->clipmask = MASK_SHOT; grenade->solid = SOLID_BBOX; grenade->s.effects |= EF_GRENADE; VectorClear(grenade->mins); VectorClear(grenade->maxs); grenade->s.modelindex = gi.modelindex("models/objects/grenade/tris.md2"); grenade->owner = self; grenade->touch = Grenade_Touch; grenade->nextthink = level.time + timer; grenade->think = Grenade_Explode; grenade->dmg = damage; grenade->dmg_radius = damage_radius; grenade->classname = "grenade"; gi.linkentity(grenade); } void fire_grenade2(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held) { edict_t *grenade; vec3_t dir; vec3_t forward, right, up; if (!self) { return; } vectoangles(aimdir, dir); AngleVectors(dir, forward, right, up); grenade = G_Spawn(); VectorCopy(start, grenade->s.origin); VectorScale(aimdir, speed, grenade->velocity); VectorMA(grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); VectorMA(grenade->velocity, crandom() * 10.0, right, grenade->velocity); VectorSet(grenade->avelocity, 300, 300, 300); grenade->movetype = MOVETYPE_BOUNCE; grenade->clipmask = MASK_SHOT; grenade->solid = SOLID_BBOX; grenade->s.effects |= EF_GRENADE; VectorClear(grenade->mins); VectorClear(grenade->maxs); grenade->s.modelindex = gi.modelindex("models/objects/grenade2/tris.md2"); grenade->owner = self; grenade->touch = Grenade_Touch; grenade->nextthink = level.time + timer; grenade->think = Grenade_Explode; grenade->dmg = damage; grenade->dmg_radius = damage_radius; grenade->classname = "hgrenade"; if (held) { grenade->spawnflags = 3; } else { grenade->spawnflags = 1; } grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav"); if (timer <= 0.0) { Grenade_Explode(grenade); } else { gi.sound(self, CHAN_WEAPON, gi.soundindex( "weapons/hgrent1a.wav"), 1, ATTN_NORM, 0); gi.linkentity(grenade); } } void rocket_touch(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) { vec3_t origin; int n; if (!ent || !other) /* plane and surf can be NULL */ { G_FreeEdict(ent); return; } if (other == ent->owner) { return; } if (surf && (surf->flags & SURF_SKY)) { G_FreeEdict(ent); return; } if (ent->owner && ent->owner->client) { PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT); } /* calculate position for the explosion entity */ VectorMA(ent->s.origin, -0.02, ent->velocity, origin); if (other->takedamage) { if (plane) { T_Damage(other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET); } else { T_Damage(other, ent, ent->owner, ent->velocity, ent->s.origin, vec3_origin, ent->dmg, 0, 0, MOD_ROCKET); } } else { /* don't throw any debris in net games */ if (!deathmatch->value && !coop->value) { if ((surf) && !(surf->flags & (SURF_WARP | SURF_TRANS33 | SURF_TRANS66 | SURF_FLOWING))) { n = randk() % 5; while (n--) { ThrowDebris(ent, "models/objects/debris2/tris.md2", 2, ent->s.origin); } } } } T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH); gi.WriteByte(svc_temp_entity); if (ent->waterlevel) { gi.WriteByte(TE_ROCKET_EXPLOSION_WATER); } else { gi.WriteByte(TE_ROCKET_EXPLOSION); } gi.WritePosition(origin); gi.multicast(ent->s.origin, MULTICAST_PHS); G_FreeEdict(ent); } void fire_rocket(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage) { edict_t *rocket; if (!self) { return; } rocket = G_Spawn(); VectorCopy(start, rocket->s.origin); VectorCopy(dir, rocket->movedir); vectoangles(dir, rocket->s.angles); VectorScale(dir, speed, rocket->velocity); rocket->movetype = MOVETYPE_FLYMISSILE; rocket->clipmask = MASK_SHOT; rocket->solid = SOLID_BBOX; rocket->s.effects |= EF_ROCKET; VectorClear(rocket->mins); VectorClear(rocket->maxs); rocket->s.modelindex = gi.modelindex("models/objects/rocket/tris.md2"); rocket->owner = self; rocket->touch = rocket_touch; rocket->nextthink = level.time + 8000 / speed; rocket->think = G_FreeEdict; rocket->dmg = damage; rocket->radius_dmg = radius_damage; rocket->dmg_radius = damage_radius; rocket->s.sound = gi.soundindex("weapons/rockfly.wav"); rocket->classname = "rocket"; if (self->client) { check_dodge(self, rocket->s.origin, dir, speed); } gi.linkentity(rocket); } void fire_rail(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick) { vec3_t from; vec3_t end; trace_t tr; edict_t *ignore; int mask; qboolean water; if (!self) { return; } VectorMA(start, 8192, aimdir, end); VectorCopy(start, from); ignore = self; water = false; mask = MASK_SHOT | CONTENTS_SLIME | CONTENTS_LAVA; while (ignore) { tr = gi.trace(from, NULL, NULL, end, ignore, mask); if (tr.contents & (CONTENTS_SLIME | CONTENTS_LAVA)) { mask &= ~(CONTENTS_SLIME | CONTENTS_LAVA); water = true; } else { if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client) || (tr.ent->solid == SOLID_BBOX)) { ignore = tr.ent; } else { ignore = NULL; } if ((tr.ent != self) && (tr.ent->takedamage)) { T_Damage(tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN); } else { ignore = NULL; } } VectorCopy(tr.endpos, from); } /* send gun puff / flash */ gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_RAILTRAIL); gi.WritePosition(start); gi.WritePosition(tr.endpos); gi.multicast(self->s.origin, MULTICAST_PHS); if (water) { gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_RAILTRAIL); gi.WritePosition(start); gi.WritePosition(tr.endpos); gi.multicast(tr.endpos, MULTICAST_PHS); } if (self->client) { PlayerNoise(self, tr.endpos, PNOISE_IMPACT); } } void bfg_explode(edict_t *self) { edict_t *ent; float points; vec3_t v; float dist; if (!self) { return; } if (self->s.frame == 0) { /* the BFG effect */ ent = NULL; while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL) { if (!ent->takedamage) { continue; } if (ent == self->owner) { continue; } if (!CanDamage(ent, self)) { continue; } if (!CanDamage(ent, self->owner)) { continue; } VectorAdd(ent->mins, ent->maxs, v); VectorMA(ent->s.origin, 0.5, v, v); VectorSubtract(self->s.origin, v, v); dist = VectorLength(v); points = self->radius_dmg * (1.0 - sqrt(dist / self->dmg_radius)); if (ent == self->owner) { points = points * 0.5; } gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_BFG_EXPLOSION); gi.WritePosition(ent->s.origin); gi.multicast(ent->s.origin, MULTICAST_PHS); T_Damage(ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT); } } self->nextthink = level.time + FRAMETIME; self->s.frame++; if (self->s.frame == 5) { self->think = G_FreeEdict; } } void bfg_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { if (!self || !other) /* plane and surf can be NULL */ { G_FreeEdict(self); return; } if (other == self->owner) { return; } if (surf && (surf->flags & SURF_SKY)) { G_FreeEdict(self); return; } if (self->owner && self->owner->client) { PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT); } /* core explosion - prevents firing it into the wall/floor */ if (other->takedamage) { if (plane) { T_Damage(other, self, self->owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST); } else { T_Damage(other, self, self->owner, self->velocity, self->s.origin, vec3_origin, 200, 0, 0, MOD_BFG_BLAST); } } T_RadiusDamage(self, self->owner, 200, other, 100, MOD_BFG_BLAST); gi.sound(self, CHAN_VOICE, gi.soundindex( "weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0); self->solid = SOLID_NOT; self->touch = NULL; VectorMA(self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin); VectorClear(self->velocity); self->s.modelindex = gi.modelindex("sprites/s_bfg3.sp2"); self->s.frame = 0; self->s.sound = 0; self->s.effects &= ~EF_ANIM_ALLFAST; self->think = bfg_explode; self->nextthink = level.time + FRAMETIME; self->enemy = other; gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_BFG_BIGEXPLOSION); gi.WritePosition(self->s.origin); gi.multicast(self->s.origin, MULTICAST_PVS); } void bfg_think(edict_t *self) { edict_t *ent; edict_t *ignore; vec3_t point; vec3_t dir; vec3_t start; vec3_t end; int dmg; trace_t tr; if (!self) { return; } if (deathmatch->value) { dmg = 5; } else { dmg = 10; } ent = NULL; while ((ent = findradius(ent, self->s.origin, 256)) != NULL) { if (ent == self) { continue; } if (ent == self->owner) { continue; } if (!ent->takedamage) { continue; } if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0)) { continue; } VectorMA(ent->absmin, 0.5, ent->size, point); VectorSubtract(point, self->s.origin, dir); VectorNormalize(dir); ignore = self; VectorCopy(self->s.origin, start); VectorMA(start, 2048, dir, end); while (1) { tr = gi.trace(start, NULL, NULL, end, ignore, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_DEADMONSTER); if (!tr.ent) { break; } /* hurt it if we can */ if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner)) { T_Damage(tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER); } /* if we hit something that's not a monster or player we're done */ if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) { gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_LASER_SPARKS); gi.WriteByte(4); gi.WritePosition(tr.endpos); gi.WriteDir(tr.plane.normal); gi.WriteByte(self->s.skinnum); gi.multicast(tr.endpos, MULTICAST_PVS); break; } ignore = tr.ent; VectorCopy(tr.endpos, start); } gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_BFG_LASER); gi.WritePosition(self->s.origin); gi.WritePosition(tr.endpos); gi.multicast(self->s.origin, MULTICAST_PHS); } self->nextthink = level.time + FRAMETIME; } void fire_bfg(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius) { edict_t *bfg; if (!self) { return; } bfg = G_Spawn(); VectorCopy(start, bfg->s.origin); VectorCopy(dir, bfg->movedir); vectoangles(dir, bfg->s.angles); VectorScale(dir, speed, bfg->velocity); bfg->movetype = MOVETYPE_FLYMISSILE; bfg->clipmask = MASK_SHOT; bfg->solid = SOLID_BBOX; bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST; VectorClear(bfg->mins); VectorClear(bfg->maxs); bfg->s.modelindex = gi.modelindex("sprites/s_bfg1.sp2"); bfg->owner = self; bfg->touch = bfg_touch; bfg->nextthink = level.time + 8000 / speed; bfg->think = G_FreeEdict; bfg->radius_dmg = damage; bfg->dmg_radius = damage_radius; bfg->classname = "bfg blast"; bfg->s.sound = gi.soundindex("weapons/bfg__l1a.wav"); bfg->think = bfg_think; bfg->nextthink = level.time + FRAMETIME; bfg->teammaster = bfg; bfg->teamchain = NULL; if (self->client) { check_dodge(self, bfg->s.origin, dir, speed); } gi.linkentity(bfg); } yquake2-QUAKE2_5_32/src/game/g_spawn.c0000644000175000017500000005636212615162034020135 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Item spawning. * * ======================================================================= */ #include "header/local.h" typedef struct { char *name; void (*spawn)(edict_t *ent); } spawn_t; void SP_item_health(edict_t *self); void SP_item_health_small(edict_t *self); void SP_item_health_large(edict_t *self); void SP_item_health_mega(edict_t *self); void SP_info_player_start(edict_t *ent); void SP_info_player_deathmatch(edict_t *ent); void SP_info_player_coop(edict_t *ent); void SP_info_player_intermission(edict_t *ent); void SP_func_plat(edict_t *ent); void SP_func_rotating(edict_t *ent); void SP_func_button(edict_t *ent); void SP_func_door(edict_t *ent); void SP_func_door_secret(edict_t *ent); void SP_func_door_rotating(edict_t *ent); void SP_func_water(edict_t *ent); void SP_func_train(edict_t *ent); void SP_func_conveyor(edict_t *self); void SP_func_wall(edict_t *self); void SP_func_object(edict_t *self); void SP_func_explosive(edict_t *self); void SP_func_timer(edict_t *self); void SP_func_areaportal(edict_t *ent); void SP_func_clock(edict_t *ent); void SP_func_killbox(edict_t *ent); void SP_trigger_always(edict_t *ent); void SP_trigger_once(edict_t *ent); void SP_trigger_multiple(edict_t *ent); void SP_trigger_relay(edict_t *ent); void SP_trigger_push(edict_t *ent); void SP_trigger_hurt(edict_t *ent); void SP_trigger_key(edict_t *ent); void SP_trigger_counter(edict_t *ent); void SP_trigger_elevator(edict_t *ent); void SP_trigger_gravity(edict_t *ent); void SP_trigger_monsterjump(edict_t *ent); void SP_target_temp_entity(edict_t *ent); void SP_target_speaker(edict_t *ent); void SP_target_explosion(edict_t *ent); void SP_target_changelevel(edict_t *ent); void SP_target_secret(edict_t *ent); void SP_target_goal(edict_t *ent); void SP_target_splash(edict_t *ent); void SP_target_spawner(edict_t *ent); void SP_target_blaster(edict_t *ent); void SP_target_crosslevel_trigger(edict_t *ent); void SP_target_crosslevel_target(edict_t *ent); void SP_target_laser(edict_t *self); void SP_target_help(edict_t *ent); void SP_target_lightramp(edict_t *self); void SP_target_earthquake(edict_t *ent); void SP_target_character(edict_t *ent); void SP_target_string(edict_t *ent); void SP_worldspawn(edict_t *ent); void SP_viewthing(edict_t *ent); void SP_light(edict_t *self); void SP_light_mine1(edict_t *ent); void SP_light_mine2(edict_t *ent); void SP_info_null(edict_t *self); void SP_info_notnull(edict_t *self); void SP_path_corner(edict_t *self); void SP_point_combat(edict_t *self); void SP_misc_explobox(edict_t *self); void SP_misc_banner(edict_t *self); void SP_misc_satellite_dish(edict_t *self); void SP_misc_gib_arm(edict_t *self); void SP_misc_gib_leg(edict_t *self); void SP_misc_gib_head(edict_t *self); void SP_misc_insane(edict_t *self); void SP_misc_deadsoldier(edict_t *self); void SP_misc_viper(edict_t *self); void SP_misc_viper_bomb(edict_t *self); void SP_misc_bigviper(edict_t *self); void SP_misc_strogg_ship(edict_t *self); void SP_misc_teleporter(edict_t *self); void SP_misc_teleporter_dest(edict_t *self); void SP_misc_blackhole(edict_t *self); void SP_misc_eastertank(edict_t *self); void SP_misc_easterchick(edict_t *self); void SP_misc_easterchick2(edict_t *self); void SP_monster_berserk(edict_t *self); void SP_monster_gladiator(edict_t *self); void SP_monster_gunner(edict_t *self); void SP_monster_infantry(edict_t *self); void SP_monster_soldier_light(edict_t *self); void SP_monster_soldier(edict_t *self); void SP_monster_soldier_ss(edict_t *self); void SP_monster_tank(edict_t *self); void SP_monster_medic(edict_t *self); void SP_monster_flipper(edict_t *self); void SP_monster_chick(edict_t *self); void SP_monster_parasite(edict_t *self); void SP_monster_flyer(edict_t *self); void SP_monster_brain(edict_t *self); void SP_monster_floater(edict_t *self); void SP_monster_hover(edict_t *self); void SP_monster_mutant(edict_t *self); void SP_monster_supertank(edict_t *self); void SP_monster_boss2(edict_t *self); void SP_monster_jorg(edict_t *self); void SP_monster_boss3_stand(edict_t *self); void SP_monster_commander_body(edict_t *self); void SP_turret_breach(edict_t *self); void SP_turret_base(edict_t *self); void SP_turret_driver(edict_t *self); spawn_t spawns[] = { {"item_health", SP_item_health}, {"item_health_small", SP_item_health_small}, {"item_health_large", SP_item_health_large}, {"item_health_mega", SP_item_health_mega}, {"info_player_start", SP_info_player_start}, {"info_player_deathmatch", SP_info_player_deathmatch}, {"info_player_coop", SP_info_player_coop}, {"info_player_intermission", SP_info_player_intermission}, {"func_plat", SP_func_plat}, {"func_button", SP_func_button}, {"func_door", SP_func_door}, {"func_door_secret", SP_func_door_secret}, {"func_door_rotating", SP_func_door_rotating}, {"func_rotating", SP_func_rotating}, {"func_train", SP_func_train}, {"func_water", SP_func_water}, {"func_conveyor", SP_func_conveyor}, {"func_areaportal", SP_func_areaportal}, {"func_clock", SP_func_clock}, {"func_wall", SP_func_wall}, {"func_object", SP_func_object}, {"func_timer", SP_func_timer}, {"func_explosive", SP_func_explosive}, {"func_killbox", SP_func_killbox}, {"trigger_always", SP_trigger_always}, {"trigger_once", SP_trigger_once}, {"trigger_multiple", SP_trigger_multiple}, {"trigger_relay", SP_trigger_relay}, {"trigger_push", SP_trigger_push}, {"trigger_hurt", SP_trigger_hurt}, {"trigger_key", SP_trigger_key}, {"trigger_counter", SP_trigger_counter}, {"trigger_elevator", SP_trigger_elevator}, {"trigger_gravity", SP_trigger_gravity}, {"trigger_monsterjump", SP_trigger_monsterjump}, {"target_temp_entity", SP_target_temp_entity}, {"target_speaker", SP_target_speaker}, {"target_explosion", SP_target_explosion}, {"target_changelevel", SP_target_changelevel}, {"target_secret", SP_target_secret}, {"target_goal", SP_target_goal}, {"target_splash", SP_target_splash}, {"target_spawner", SP_target_spawner}, {"target_blaster", SP_target_blaster}, {"target_crosslevel_trigger", SP_target_crosslevel_trigger}, {"target_crosslevel_target", SP_target_crosslevel_target}, {"target_laser", SP_target_laser}, {"target_help", SP_target_help}, {"target_lightramp", SP_target_lightramp}, {"target_earthquake", SP_target_earthquake}, {"target_character", SP_target_character}, {"target_string", SP_target_string}, {"worldspawn", SP_worldspawn}, {"viewthing", SP_viewthing}, {"light", SP_light}, {"light_mine1", SP_light_mine1}, {"light_mine2", SP_light_mine2}, {"info_null", SP_info_null}, {"func_group", SP_info_null}, {"info_notnull", SP_info_notnull}, {"path_corner", SP_path_corner}, {"point_combat", SP_point_combat}, {"misc_explobox", SP_misc_explobox}, {"misc_banner", SP_misc_banner}, {"misc_satellite_dish", SP_misc_satellite_dish}, {"misc_gib_arm", SP_misc_gib_arm}, {"misc_gib_leg", SP_misc_gib_leg}, {"misc_gib_head", SP_misc_gib_head}, {"misc_insane", SP_misc_insane}, {"misc_deadsoldier", SP_misc_deadsoldier}, {"misc_viper", SP_misc_viper}, {"misc_viper_bomb", SP_misc_viper_bomb}, {"misc_bigviper", SP_misc_bigviper}, {"misc_strogg_ship", SP_misc_strogg_ship}, {"misc_teleporter", SP_misc_teleporter}, {"misc_teleporter_dest", SP_misc_teleporter_dest}, {"misc_blackhole", SP_misc_blackhole}, {"misc_eastertank", SP_misc_eastertank}, {"misc_easterchick", SP_misc_easterchick}, {"misc_easterchick2", SP_misc_easterchick2}, {"monster_berserk", SP_monster_berserk}, {"monster_gladiator", SP_monster_gladiator}, {"monster_gunner", SP_monster_gunner}, {"monster_infantry", SP_monster_infantry}, {"monster_soldier_light", SP_monster_soldier_light}, {"monster_soldier", SP_monster_soldier}, {"monster_soldier_ss", SP_monster_soldier_ss}, {"monster_tank", SP_monster_tank}, {"monster_tank_commander", SP_monster_tank}, {"monster_medic", SP_monster_medic}, {"monster_flipper", SP_monster_flipper}, {"monster_chick", SP_monster_chick}, {"monster_parasite", SP_monster_parasite}, {"monster_flyer", SP_monster_flyer}, {"monster_brain", SP_monster_brain}, {"monster_floater", SP_monster_floater}, {"monster_hover", SP_monster_hover}, {"monster_mutant", SP_monster_mutant}, {"monster_supertank", SP_monster_supertank}, {"monster_boss2", SP_monster_boss2}, {"monster_boss3_stand", SP_monster_boss3_stand}, {"monster_jorg", SP_monster_jorg}, {"monster_commander_body", SP_monster_commander_body}, {"turret_breach", SP_turret_breach}, {"turret_base", SP_turret_base}, {"turret_driver", SP_turret_driver}, {NULL, NULL} }; /* * Finds the spawn function for * the entity and calls it */ void ED_CallSpawn(edict_t *ent) { spawn_t *s; gitem_t *item; int i; if (!ent) { return; } if (!ent->classname) { gi.dprintf("ED_CallSpawn: NULL classname\n"); G_FreeEdict(ent); return; } /* check item spawn functions */ for (i = 0, item = itemlist; i < game.num_items; i++, item++) { if (!item->classname) { continue; } if (!strcmp(item->classname, ent->classname)) { /* found it */ SpawnItem(ent, item); return; } } /* check normal spawn functions */ for (s = spawns; s->name; s++) { if (!strcmp(s->name, ent->classname)) { /* found it */ s->spawn(ent); return; } } gi.dprintf("%s doesn't have a spawn function\n", ent->classname); } char * ED_NewString(const char *string) { char *newb, *new_p; int i, l; l = strlen(string) + 1; newb = gi.TagMalloc(l, TAG_LEVEL); new_p = newb; 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 newb; } /* * Takes a key/value pair and sets * the binary values in an edict */ void ED_ParseField(const char *key, const char *value, edict_t *ent) { field_t *f; byte *b; float v; vec3_t vec; if (!key || !value) { return; } for (f = fields; f->name; f++) { if (!(f->flags & FFL_NOSPAWN) && !Q_stricmp(f->name, key)) { /* found it */ if (f->flags & FFL_SPAWNTEMP) { b = (byte *)&st; } else { b = (byte *)ent; } switch (f->type) { case F_LSTRING: *(char **)(b + f->ofs) = ED_NewString(value); break; case F_VECTOR: sscanf(value, "%f %f %f", &vec[0], &vec[1], &vec[2]); ((float *)(b + f->ofs))[0] = vec[0]; ((float *)(b + f->ofs))[1] = vec[1]; ((float *)(b + f->ofs))[2] = vec[2]; break; case F_INT: *(int *)(b + f->ofs) = (int)strtol(value, (char **)NULL, 10); break; case F_FLOAT: *(float *)(b + f->ofs) = strtod(value, (char **)NULL); break; case F_ANGLEHACK: v = strtod(value, (char **)NULL); ((float *)(b + f->ofs))[0] = 0; ((float *)(b + f->ofs))[1] = v; ((float *)(b + f->ofs))[2] = 0; break; case F_IGNORE: break; default: break; } return; } } gi.dprintf("%s is not a field\n", key); } /* * Parses an edict out of the given string, * returning the new position ed should be * a properly initialized empty edict. */ char * ED_ParseEdict(char *data, edict_t *ent) { qboolean init; char keyname[256]; const char *com_token; init = false; memset(&st, 0, sizeof(st)); /* go through all the dictionary pairs */ while (1) { /* parse key */ com_token = COM_Parse(&data); if (com_token[0] == '}') { break; } if (!data) { gi.error("ED_ParseEntity: EOF without closing brace"); } Q_strlcpy(keyname, com_token, sizeof(keyname)); /* parse value */ com_token = COM_Parse(&data); if (!data) { gi.error("ED_ParseEntity: EOF without closing brace"); } if (com_token[0] == '}') { gi.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; } ED_ParseField(keyname, com_token, ent); } if (!init) { memset(ent, 0, sizeof(*ent)); } return data; } /* * Chain together all entities with a matching team field. * * All but the first will have the FL_TEAMSLAVE flag set. * All but the last will have the teamchain field set to the next one */ void G_FindTeams(void) { edict_t *e, *e2, *chain; int i, j; int c, c2; c = 0; c2 = 0; for (i = 1, e = g_edicts + i; i < globals.num_edicts; i++, e++) { if (!e->inuse) { continue; } if (!e->team) { continue; } if (e->flags & FL_TEAMSLAVE) { continue; } chain = e; e->teammaster = e; c++; c2++; for (j = i + 1, e2 = e + 1; j < globals.num_edicts; j++, e2++) { if (!e2->inuse) { continue; } if (!e2->team) { continue; } if (e2->flags & FL_TEAMSLAVE) { continue; } if (!strcmp(e->team, e2->team)) { c2++; chain->teamchain = e2; e2->teammaster = e; chain = e2; e2->flags |= FL_TEAMSLAVE; } } } gi.dprintf("%i teams with %i entities.\n", c, c2); } /* * Creates a server's entity / program execution context by * parsing textual entity definitions out of an ent file. */ void SpawnEntities(const char *mapname, char *entities, const char *spawnpoint) { edict_t *ent; int inhibit; const char *com_token; int i; float skill_level; if (!mapname || !entities || !spawnpoint) { return; } skill_level = floor(skill->value); if (skill_level < 0) { skill_level = 0; } if (skill_level > 3) { skill_level = 3; } if (skill->value != skill_level) { gi.cvar_forceset("skill", va("%f", skill_level)); } SaveClientData(); gi.FreeTags(TAG_LEVEL); memset(&level, 0, sizeof(level)); memset(g_edicts, 0, game.maxentities * sizeof(g_edicts[0])); Q_strlcpy(level.mapname, mapname, sizeof(level.mapname)); Q_strlcpy(game.spawnpoint, spawnpoint, sizeof(game.spawnpoint)); /* set client fields on player ents */ for (i = 0; i < game.maxclients; i++) { g_edicts[i + 1].client = game.clients + i; } ent = NULL; inhibit = 0; /* parse ents */ while (1) { /* parse the opening brace */ com_token = COM_Parse(&entities); if (!entities) { break; } if (com_token[0] != '{') { gi.error("ED_LoadFromFile: found %s when expecting {", com_token); } if (!ent) { ent = g_edicts; } else { ent = G_Spawn(); } entities = ED_ParseEdict(entities, ent); /* yet another map hack */ if (!Q_stricmp(level.mapname, "command") && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27")) { ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; } /* remove things (except the world) from different skill levels or deathmatch */ if (ent != g_edicts) { if (deathmatch->value) { if (ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH) { G_FreeEdict(ent); inhibit++; continue; } } else { if (((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (((skill->value == 2) || (skill->value == 3)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ) { G_FreeEdict(ent); inhibit++; continue; } } ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY | SPAWNFLAG_NOT_MEDIUM | SPAWNFLAG_NOT_HARD | SPAWNFLAG_NOT_COOP | SPAWNFLAG_NOT_DEATHMATCH); } ED_CallSpawn(ent); } gi.dprintf("%i entities inhibited.\n", inhibit); G_FindTeams(); PlayerTrail_Init(); } /* =================================================================== */ char *single_statusbar = "yb -24 " /* health */ "xv 0 " "hnum " "xv 50 " "pic 0 " /* ammo */ "if 2 " " xv 100 " " anum " " xv 150 " " pic 2 " "endif " /* armor */ "if 4 " " xv 200 " " rnum " " xv 250 " " pic 4 " "endif " /* selected item */ "if 6 " " xv 296 " " pic 6 " "endif " "yb -50 " /* picked up item */ "if 7 " " xv 0 " " pic 7 " " xv 26 " " yb -42 " " stat_string 8 " " yb -50 " "endif " /* timer */ "if 9 " " xv 262 " " num 2 10 " " xv 296 " " pic 9 " "endif " /* help / weapon icon */ "if 11 " " xv 148 " " pic 11 " "endif " ; char *dm_statusbar = "yb -24 " /* health */ "xv 0 " "hnum " "xv 50 " "pic 0 " /* ammo */ "if 2 " " xv 100 " " anum " " xv 150 " " pic 2 " "endif " /* armor */ "if 4 " " xv 200 " " rnum " " xv 250 " " pic 4 " "endif " /* selected item */ "if 6 " " xv 296 " " pic 6 " "endif " "yb -50 " /* picked up item */ "if 7 " " xv 0 " " pic 7 " " xv 26 " " yb -42 " " stat_string 8 " " yb -50 " "endif " /* timer */ "if 9 " " xv 246 " " num 2 10 " " xv 296 " " pic 9 " "endif " /* help / weapon icon */ "if 11 " " xv 148 " " pic 11 " "endif " /* frags */ "xr -50 " "yt 2 " "num 3 14 " /* spectator */ "if 17 " "xv 0 " "yb -58 " "string2 \"SPECTATOR MODE\" " "endif " /* chase camera */ "if 16 " "xv 0 " "yb -68 " "string \"Chasing\" " "xv 64 " "stat_string 16 " "endif " ; /*QUAKED worldspawn (0 0 0) ? * * Only used for the world. * "sky" environment map name * "skyaxis" vector axis for rotating sky * "skyrotate" speed of rotation in degrees/second * "sounds" music cd track number * "gravity" 800 is default gravity * "message" text to print at user logon */ void SP_worldspawn(edict_t *ent) { if (!ent) { return; } ent->movetype = MOVETYPE_PUSH; ent->solid = SOLID_BSP; ent->inuse = true; /* since the world doesn't use G_Spawn() */ ent->s.modelindex = 1; /* world model is always index 1 */ /* --------------- */ /* reserve some spots for dead player bodies for coop / deathmatch */ InitBodyQue(); /* set configstrings for items */ SetItemNames(); if (st.nextmap) { strcpy(level.nextmap, st.nextmap); } /* make some data visible to the server */ if (ent->message && ent->message[0]) { gi.configstring(CS_NAME, ent->message); Q_strlcpy(level.level_name, ent->message, sizeof(level.level_name)); } else { Q_strlcpy(level.level_name, level.mapname, sizeof(level.level_name)); } if (st.sky && st.sky[0]) { gi.configstring(CS_SKY, st.sky); } else { gi.configstring(CS_SKY, "unit1_"); } gi.configstring(CS_SKYROTATE, va("%f", st.skyrotate)); gi.configstring(CS_SKYAXIS, va("%f %f %f", st.skyaxis[0], st.skyaxis[1], st.skyaxis[2])); gi.configstring(CS_CDTRACK, va("%i", ent->sounds)); gi.configstring(CS_MAXCLIENTS, va("%i", (int)(maxclients->value))); /* status bar program */ if (deathmatch->value) { gi.configstring(CS_STATUSBAR, dm_statusbar); } else { gi.configstring(CS_STATUSBAR, single_statusbar); } /* --------------- */ /* help icon for statusbar */ gi.imageindex("i_help"); level.pic_health = gi.imageindex("i_health"); gi.imageindex("help"); gi.imageindex("field_3"); if (!st.gravity) { gi.cvar_set("sv_gravity", "800"); } else { gi.cvar_set("sv_gravity", st.gravity); } snd_fry = gi.soundindex("player/fry.wav"); /* standing in lava / slime */ PrecacheItem(FindItem("Blaster")); gi.soundindex("player/lava1.wav"); gi.soundindex("player/lava2.wav"); gi.soundindex("misc/pc_up.wav"); gi.soundindex("misc/talk1.wav"); gi.soundindex("misc/udeath.wav"); /* gibs */ gi.soundindex("items/respawn1.wav"); /* sexed sounds */ gi.soundindex("*death1.wav"); gi.soundindex("*death2.wav"); gi.soundindex("*death3.wav"); gi.soundindex("*death4.wav"); gi.soundindex("*fall1.wav"); gi.soundindex("*fall2.wav"); gi.soundindex("*gurp1.wav"); /* drowning damage */ gi.soundindex("*gurp2.wav"); gi.soundindex("*jump1.wav"); /* player jump */ gi.soundindex("*pain25_1.wav"); gi.soundindex("*pain25_2.wav"); gi.soundindex("*pain50_1.wav"); gi.soundindex("*pain50_2.wav"); gi.soundindex("*pain75_1.wav"); gi.soundindex("*pain75_2.wav"); gi.soundindex("*pain100_1.wav"); gi.soundindex("*pain100_2.wav"); /* sexed models you can add more, max 15 THIS ORDER MUST MATCH THE DEFINES IN g_local.h */ gi.modelindex("#w_blaster.md2"); gi.modelindex("#w_shotgun.md2"); gi.modelindex("#w_sshotgun.md2"); gi.modelindex("#w_machinegun.md2"); gi.modelindex("#w_chaingun.md2"); gi.modelindex("#a_grenades.md2"); gi.modelindex("#w_glauncher.md2"); gi.modelindex("#w_rlauncher.md2"); gi.modelindex("#w_hyperblaster.md2"); gi.modelindex("#w_railgun.md2"); gi.modelindex("#w_bfg.md2"); /* ------------------- */ gi.soundindex("player/gasp1.wav"); /* gasping for air */ gi.soundindex("player/gasp2.wav"); /* head breaking surface, not gasping */ gi.soundindex("player/watr_in.wav"); /* feet hitting water */ gi.soundindex("player/watr_out.wav"); /* feet leaving water */ gi.soundindex("player/watr_un.wav"); /* head going underwater */ gi.soundindex("player/u_breath1.wav"); gi.soundindex("player/u_breath2.wav"); gi.soundindex("items/pkup.wav"); /* bonus item pickup */ gi.soundindex("world/land.wav"); /* landing thud */ gi.soundindex("misc/h2ohit1.wav"); /* landing splash */ gi.soundindex("items/damage.wav"); gi.soundindex("items/protect.wav"); gi.soundindex("items/protect4.wav"); gi.soundindex("weapons/noammo.wav"); gi.soundindex("infantry/inflies1.wav"); sm_meat_index = gi.modelindex("models/objects/gibs/sm_meat/tris.md2"); gi.modelindex("models/objects/gibs/arm/tris.md2"); gi.modelindex("models/objects/gibs/bone/tris.md2"); gi.modelindex("models/objects/gibs/bone2/tris.md2"); gi.modelindex("models/objects/gibs/chest/tris.md2"); gi.modelindex("models/objects/gibs/skull/tris.md2"); gi.modelindex("models/objects/gibs/head2/tris.md2"); /* Setup light animation tables. 'a' is total darkness, 'z' is doublebright. */ /* 0 normal */ gi.configstring(CS_LIGHTS + 0, "m"); /* 1 FLICKER (first variety) */ gi.configstring(CS_LIGHTS + 1, "mmnmmommommnonmmonqnmmo"); /* 2 SLOW STRONG PULSE */ gi.configstring(CS_LIGHTS + 2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba"); /* 3 CANDLE (first variety) */ gi.configstring(CS_LIGHTS + 3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg"); /* 4 FAST STROBE */ gi.configstring(CS_LIGHTS + 4, "mamamamamama"); /* 5 GENTLE PULSE 1 */ gi.configstring(CS_LIGHTS + 5, "jklmnopqrstuvwxyzyxwvutsrqponmlkj"); /* 6 FLICKER (second variety) */ gi.configstring(CS_LIGHTS + 6, "nmonqnmomnmomomno"); /* 7 CANDLE (second variety) */ gi.configstring(CS_LIGHTS + 7, "mmmaaaabcdefgmmmmaaaammmaamm"); /* 8 CANDLE (third variety) */ gi.configstring(CS_LIGHTS + 8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa"); /* 9 SLOW STROBE (fourth variety) */ gi.configstring(CS_LIGHTS + 9, "aaaaaaaazzzzzzzz"); /* 10 FLUORESCENT FLICKER */ gi.configstring(CS_LIGHTS + 10, "mmamammmmammamamaaamammma"); /* 11 SLOW PULSE NOT FADE TO BLACK */ gi.configstring(CS_LIGHTS + 11, "abcdefghijklmnopqrrqponmlkjihgfedcba"); /* styles 32-62 are assigned by the light program for switchable lights */ /* 63 testing */ gi.configstring(CS_LIGHTS + 63, "a"); } yquake2-QUAKE2_5_32/src/game/player/0000755000175000017500000000000012615162034017613 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/player/weapon.c0000644000175000017500000010500212615162034021246 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Player weapons. * * ======================================================================= */ #include "../header/local.h" #include "../monster/misc/player.h" #define FRAME_FIRE_FIRST (FRAME_ACTIVATE_LAST + 1) #define FRAME_IDLE_FIRST (FRAME_FIRE_LAST + 1) #define FRAME_DEACTIVATE_FIRST (FRAME_IDLE_LAST + 1) #define GRENADE_TIMER 3.0 #define GRENADE_MINSPEED 400 #define GRENADE_MAXSPEED 800 static qboolean is_quad; static byte is_silenced; void weapon_grenade_fire(edict_t *ent, qboolean held); void P_ProjectSource(gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result) { vec3_t _distance; if (!client) { return; } VectorCopy(distance, _distance); if (client->pers.hand == LEFT_HANDED) { _distance[1] *= -1; } else if (client->pers.hand == CENTER_HANDED) { _distance[1] = 0; } G_ProjectSource(point, _distance, forward, right, result); } /* * Each player can have two noise objects associated with it: * a personal noise (jumping, pain, weapon firing), and a weapon * target noise (bullet wall impacts) * * Monsters that don't directly see the player can move * to a noise in hopes of seeing the player from there. */ void PlayerNoise(edict_t *who, vec3_t where, int type) { edict_t *noise; if (!who) { return; } if (type == PNOISE_WEAPON) { if (who->client->silencer_shots) { who->client->silencer_shots--; return; } } if (deathmatch->value) { return; } if (who->flags & FL_NOTARGET) { return; } if (!who->mynoise) { noise = G_Spawn(); noise->classname = "player_noise"; VectorSet(noise->mins, -8, -8, -8); VectorSet(noise->maxs, 8, 8, 8); noise->owner = who; noise->svflags = SVF_NOCLIENT; who->mynoise = noise; noise = G_Spawn(); noise->classname = "player_noise"; VectorSet(noise->mins, -8, -8, -8); VectorSet(noise->maxs, 8, 8, 8); noise->owner = who; noise->svflags = SVF_NOCLIENT; who->mynoise2 = noise; } if ((type == PNOISE_SELF) || (type == PNOISE_WEAPON)) { noise = who->mynoise; level.sound_entity = noise; level.sound_entity_framenum = level.framenum; } else { noise = who->mynoise2; level.sound2_entity = noise; level.sound2_entity_framenum = level.framenum; } VectorCopy(where, noise->s.origin); VectorSubtract(where, noise->maxs, noise->absmin); VectorAdd(where, noise->maxs, noise->absmax); noise->teleport_time = level.time; gi.linkentity(noise); } qboolean Pickup_Weapon(edict_t *ent, edict_t *other) { int index; gitem_t *ammo; if (!ent || !other) { return false; } index = ITEM_INDEX(ent->item); if ((((int)(dmflags->value) & DF_WEAPONS_STAY) || coop->value) && other->client->pers.inventory[index]) { if (!(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM))) { return false; /* leave the weapon for others to pickup */ } } other->client->pers.inventory[index]++; if (!(ent->spawnflags & DROPPED_ITEM)) { /* give them some ammo with it */ ammo = FindItem(ent->item->ammo); if ((int)dmflags->value & DF_INFINITE_AMMO) { Add_Ammo(other, ammo, 1000); } else { Add_Ammo(other, ammo, ammo->quantity); } if (!(ent->spawnflags & DROPPED_PLAYER_ITEM)) { if (deathmatch->value) { if ((int)(dmflags->value) & DF_WEAPONS_STAY) { ent->flags |= FL_RESPAWN; } else { SetRespawn(ent, 30); } } if (coop->value) { ent->flags |= FL_RESPAWN; } } } if ((other->client->pers.weapon != ent->item) && (other->client->pers.inventory[index] == 1) && (!deathmatch->value || (other->client->pers.weapon == FindItem("blaster")))) { other->client->newweapon = ent->item; } return true; } /* * The old weapon has been dropped all * the way, so make the new one current */ void ChangeWeapon(edict_t *ent) { int i; if (!ent) { return; } if (ent->client->grenade_time) { ent->client->grenade_time = level.time; ent->client->weapon_sound = 0; weapon_grenade_fire(ent, false); ent->client->grenade_time = 0; } ent->client->pers.lastweapon = ent->client->pers.weapon; ent->client->pers.weapon = ent->client->newweapon; ent->client->newweapon = NULL; ent->client->machinegun_shots = 0; /* set visible model */ if (ent->s.modelindex == 255) { if (ent->client->pers.weapon) { i = ((ent->client->pers.weapon->weapmodel & 0xff) << 8); } else { i = 0; } ent->s.skinnum = (ent - g_edicts - 1) | i; } if (ent->client->pers.weapon && ent->client->pers.weapon->ammo) { ent->client->ammo_index = ITEM_INDEX(FindItem(ent->client->pers.weapon->ammo)); } else { ent->client->ammo_index = 0; } if (!ent->client->pers.weapon) { /* dead */ ent->client->ps.gunindex = 0; return; } ent->client->weaponstate = WEAPON_ACTIVATING; ent->client->ps.gunframe = 0; ent->client->ps.gunindex = gi.modelindex( ent->client->pers.weapon->view_model); ent->client->anim_priority = ANIM_PAIN; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { ent->s.frame = FRAME_crpain1; ent->client->anim_end = FRAME_crpain4; } else { ent->s.frame = FRAME_pain301; ent->client->anim_end = FRAME_pain304; } } void NoAmmoWeaponChange(edict_t *ent) { if (ent->client->pers.inventory[ITEM_INDEX(FindItem("slugs"))] && ent->client->pers.inventory[ITEM_INDEX(FindItem("railgun"))]) { ent->client->newweapon = FindItem("railgun"); return; } if (ent->client->pers.inventory[ITEM_INDEX(FindItem("cells"))] && ent->client->pers.inventory[ITEM_INDEX(FindItem("hyperblaster"))]) { ent->client->newweapon = FindItem("hyperblaster"); return; } if (ent->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))] && ent->client->pers.inventory[ITEM_INDEX(FindItem("chaingun"))]) { ent->client->newweapon = FindItem("chaingun"); return; } if (ent->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))] && ent->client->pers.inventory[ITEM_INDEX(FindItem("machinegun"))]) { ent->client->newweapon = FindItem("machinegun"); return; } if ((ent->client->pers.inventory[ITEM_INDEX(FindItem("shells"))] > 1) && ent->client->pers.inventory[ITEM_INDEX(FindItem("super shotgun"))]) { ent->client->newweapon = FindItem("super shotgun"); return; } if (ent->client->pers.inventory[ITEM_INDEX(FindItem("shells"))] && ent->client->pers.inventory[ITEM_INDEX(FindItem("shotgun"))]) { ent->client->newweapon = FindItem("shotgun"); return; } ent->client->newweapon = FindItem("blaster"); } /* * Called by ClientBeginServerFrame and ClientThink */ void Think_Weapon(edict_t *ent) { if (!ent) { return; } /* if just died, put the weapon away */ if (ent->health < 1) { ent->client->newweapon = NULL; ChangeWeapon(ent); } /* call active weapon think routine */ if (ent->client->pers.weapon && ent->client->pers.weapon->weaponthink) { is_quad = (ent->client->quad_framenum > level.framenum); if (ent->client->silencer_shots) { is_silenced = MZ_SILENCED; } else { is_silenced = 0; } ent->client->pers.weapon->weaponthink(ent); } } /* * Make the weapon ready if there is ammo */ void Use_Weapon(edict_t *ent, gitem_t *item) { int ammo_index; gitem_t *ammo_item; if (!ent || !item) { return; } /* see if we're already using it */ if (item == ent->client->pers.weapon) { return; } if (item->ammo && !g_select_empty->value && !(item->flags & IT_AMMO)) { ammo_item = FindItem(item->ammo); ammo_index = ITEM_INDEX(ammo_item); if (!ent->client->pers.inventory[ammo_index]) { gi.cprintf(ent, PRINT_HIGH, "No %s for %s.\n", ammo_item->pickup_name, item->pickup_name); return; } if (ent->client->pers.inventory[ammo_index] < item->quantity) { gi.cprintf(ent, PRINT_HIGH, "Not enough %s for %s.\n", ammo_item->pickup_name, item->pickup_name); return; } } /* change to this weapon when down */ ent->client->newweapon = item; } void Drop_Weapon(edict_t *ent, gitem_t *item) { int index; if (!ent || !item) { return; } if ((int)(dmflags->value) & DF_WEAPONS_STAY) { return; } index = ITEM_INDEX(item); /* see if we're already using it */ if (((item == ent->client->pers.weapon) || (item == ent->client->newweapon)) && (ent->client->pers.inventory[index] == 1)) { gi.cprintf(ent, PRINT_HIGH, "Can't drop current weapon\n"); return; } Drop_Item(ent, item); ent->client->pers.inventory[index]--; } /* * A generic function to handle * the basics of weapon thinking */ void Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int FRAME_DEACTIVATE_LAST, int *pause_frames, int *fire_frames, void (*fire)(edict_t *ent)) { int n; if (!ent || !fire_frames || !fire) { return; } if (ent->deadflag || (ent->s.modelindex != 255)) /* VWep animations screw up corpses */ { return; } if (ent->client->weaponstate == WEAPON_DROPPING) { if (ent->client->ps.gunframe == FRAME_DEACTIVATE_LAST) { ChangeWeapon(ent); return; } else if ((FRAME_DEACTIVATE_LAST - ent->client->ps.gunframe) == 4) { ent->client->anim_priority = ANIM_REVERSE; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { ent->s.frame = FRAME_crpain4 + 1; ent->client->anim_end = FRAME_crpain1; } else { ent->s.frame = FRAME_pain304 + 1; ent->client->anim_end = FRAME_pain301; } } ent->client->ps.gunframe++; return; } if (ent->client->weaponstate == WEAPON_ACTIVATING) { if (ent->client->ps.gunframe == FRAME_ACTIVATE_LAST) { ent->client->weaponstate = WEAPON_READY; ent->client->ps.gunframe = FRAME_IDLE_FIRST; return; } ent->client->ps.gunframe++; return; } if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING)) { ent->client->weaponstate = WEAPON_DROPPING; ent->client->ps.gunframe = FRAME_DEACTIVATE_FIRST; if ((FRAME_DEACTIVATE_LAST - FRAME_DEACTIVATE_FIRST) < 4) { ent->client->anim_priority = ANIM_REVERSE; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { ent->s.frame = FRAME_crpain4 + 1; ent->client->anim_end = FRAME_crpain1; } else { ent->s.frame = FRAME_pain304 + 1; ent->client->anim_end = FRAME_pain301; } } return; } if (ent->client->weaponstate == WEAPON_READY) { if (((ent->client->latched_buttons | ent->client->buttons) & BUTTON_ATTACK)) { ent->client->latched_buttons &= ~BUTTON_ATTACK; if ((!ent->client->ammo_index) || (ent->client->pers.inventory[ent->client->ammo_index] >= ent->client->pers.weapon->quantity)) { ent->client->ps.gunframe = FRAME_FIRE_FIRST; ent->client->weaponstate = WEAPON_FIRING; /* start the animation */ ent->client->anim_priority = ANIM_ATTACK; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { ent->s.frame = FRAME_crattak1 - 1; ent->client->anim_end = FRAME_crattak9; } else { ent->s.frame = FRAME_attack1 - 1; ent->client->anim_end = FRAME_attack8; } } else { if (level.time >= ent->pain_debounce_time) { gi.sound(ent, CHAN_VOICE, gi.soundindex( "weapons/noammo.wav"), 1, ATTN_NORM, 0); ent->pain_debounce_time = level.time + 1; } NoAmmoWeaponChange(ent); } } else { if (ent->client->ps.gunframe == FRAME_IDLE_LAST) { ent->client->ps.gunframe = FRAME_IDLE_FIRST; return; } if (pause_frames) { for (n = 0; pause_frames[n]; n++) { if (ent->client->ps.gunframe == pause_frames[n]) { if (randk() & 15) { return; } } } } ent->client->ps.gunframe++; return; } } if (ent->client->weaponstate == WEAPON_FIRING) { for (n = 0; fire_frames[n]; n++) { if (ent->client->ps.gunframe == fire_frames[n]) { if (ent->client->quad_framenum > level.framenum) { gi.sound(ent, CHAN_ITEM, gi.soundindex( "items/damage3.wav"), 1, ATTN_NORM, 0); } fire(ent); break; } } if (!fire_frames[n]) { ent->client->ps.gunframe++; } if (ent->client->ps.gunframe == FRAME_IDLE_FIRST + 1) { ent->client->weaponstate = WEAPON_READY; } } } /* ====================================================================== */ /* GRENADE */ void weapon_grenade_fire(edict_t *ent, qboolean held) { vec3_t offset; vec3_t forward, right; vec3_t start; int damage = 125; float timer; int speed; float radius; if (!ent) { return; } radius = damage + 40; if (is_quad) { damage *= 4; } VectorSet(offset, 8, 8, ent->viewheight - 8); AngleVectors(ent->client->v_angle, forward, right, NULL); P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start); timer = ent->client->grenade_time - level.time; speed = GRENADE_MINSPEED + (GRENADE_TIMER - timer) * ((GRENADE_MAXSPEED - GRENADE_MINSPEED) / GRENADE_TIMER); fire_grenade2(ent, start, forward, damage, speed, timer, radius, held); if (!((int)dmflags->value & DF_INFINITE_AMMO)) { ent->client->pers.inventory[ent->client->ammo_index]--; } ent->client->grenade_time = level.time + 1.0; if (ent->deadflag || (ent->s.modelindex != 255)) /* VWep animations screw up corpses */ { return; } if (ent->health <= 0) { return; } if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { ent->client->anim_priority = ANIM_ATTACK; ent->s.frame = FRAME_crattak1 - 1; ent->client->anim_end = FRAME_crattak3; } else { ent->client->anim_priority = ANIM_REVERSE; ent->s.frame = FRAME_wave08; ent->client->anim_end = FRAME_wave01; } } void Weapon_Grenade(edict_t *ent) { if (!ent) { return; } if ((ent->client->newweapon) && (ent->client->weaponstate == WEAPON_READY)) { ChangeWeapon(ent); return; } if (ent->client->weaponstate == WEAPON_ACTIVATING) { ent->client->weaponstate = WEAPON_READY; ent->client->ps.gunframe = 16; return; } if (ent->client->weaponstate == WEAPON_READY) { if (((ent->client->latched_buttons | ent->client->buttons) & BUTTON_ATTACK)) { ent->client->latched_buttons &= ~BUTTON_ATTACK; if (ent->client->pers.inventory[ent->client->ammo_index]) { ent->client->ps.gunframe = 1; ent->client->weaponstate = WEAPON_FIRING; ent->client->grenade_time = 0; } else { if (level.time >= ent->pain_debounce_time) { gi.sound(ent, CHAN_VOICE, gi.soundindex( "weapons/noammo.wav"), 1, ATTN_NORM, 0); ent->pain_debounce_time = level.time + 1; } NoAmmoWeaponChange(ent); } return; } if ((ent->client->ps.gunframe == 29) || (ent->client->ps.gunframe == 34) || (ent->client->ps.gunframe == 39) || (ent->client->ps.gunframe == 48)) { if (randk() & 15) { return; } } if (++ent->client->ps.gunframe > 48) { ent->client->ps.gunframe = 16; } return; } if (ent->client->weaponstate == WEAPON_FIRING) { if (ent->client->ps.gunframe == 5) { gi.sound(ent, CHAN_WEAPON, gi.soundindex( "weapons/hgrena1b.wav"), 1, ATTN_NORM, 0); } if (ent->client->ps.gunframe == 11) { if (!ent->client->grenade_time) { ent->client->grenade_time = level.time + GRENADE_TIMER + 0.2; ent->client->weapon_sound = gi.soundindex( "weapons/hgrenc1b.wav"); } /* they waited too long, detonate it in their hand */ if (!ent->client->grenade_blew_up && (level.time >= ent->client->grenade_time)) { ent->client->weapon_sound = 0; weapon_grenade_fire(ent, true); ent->client->grenade_blew_up = true; } if (ent->client->buttons & BUTTON_ATTACK) { return; } if (ent->client->grenade_blew_up) { if (level.time >= ent->client->grenade_time) { ent->client->ps.gunframe = 15; ent->client->grenade_blew_up = false; } else { return; } } } if (ent->client->ps.gunframe == 12) { ent->client->weapon_sound = 0; weapon_grenade_fire(ent, false); } if ((ent->client->ps.gunframe == 15) && (level.time < ent->client->grenade_time)) { return; } ent->client->ps.gunframe++; if (ent->client->ps.gunframe == 16) { ent->client->grenade_time = 0; ent->client->weaponstate = WEAPON_READY; } } } /* ====================================================================== */ /* GRENADE LAUNCHER */ void weapon_grenadelauncher_fire(edict_t *ent) { vec3_t offset; vec3_t forward, right; vec3_t start; int damage = 120; float radius; if (!ent) { return; } radius = damage + 40; if (is_quad) { damage *= 4; } VectorSet(offset, 8, 8, ent->viewheight - 8); AngleVectors(ent->client->v_angle, forward, right, NULL); P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start); VectorScale(forward, -2, ent->client->kick_origin); ent->client->kick_angles[0] = -1; fire_grenade(ent, start, forward, damage, 600, 2.5, radius); gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); gi.WriteByte(MZ_GRENADE | is_silenced); gi.multicast(ent->s.origin, MULTICAST_PVS); ent->client->ps.gunframe++; PlayerNoise(ent, start, PNOISE_WEAPON); if (!((int)dmflags->value & DF_INFINITE_AMMO)) { ent->client->pers.inventory[ent->client->ammo_index]--; } } void Weapon_GrenadeLauncher(edict_t *ent) { static int pause_frames[] = {34, 51, 59, 0}; static int fire_frames[] = {6, 0}; Weapon_Generic(ent, 5, 16, 59, 64, pause_frames, fire_frames, weapon_grenadelauncher_fire); } /* ====================================================================== */ /* ROCKET */ void Weapon_RocketLauncher_Fire(edict_t *ent) { vec3_t offset, start; vec3_t forward, right; int damage; float damage_radius; int radius_damage; if (!ent) { return; } damage = 100 + (int)(random() * 20.0); radius_damage = 120; damage_radius = 120; if (is_quad) { damage *= 4; radius_damage *= 4; } AngleVectors(ent->client->v_angle, forward, right, NULL); VectorScale(forward, -2, ent->client->kick_origin); ent->client->kick_angles[0] = -1; VectorSet(offset, 8, 8, ent->viewheight - 8); P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start); fire_rocket(ent, start, forward, damage, 650, damage_radius, radius_damage); /* send muzzle flash */ gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); gi.WriteByte(MZ_ROCKET | is_silenced); gi.multicast(ent->s.origin, MULTICAST_PVS); ent->client->ps.gunframe++; PlayerNoise(ent, start, PNOISE_WEAPON); if (!((int)dmflags->value & DF_INFINITE_AMMO)) { ent->client->pers.inventory[ent->client->ammo_index]--; } } void Weapon_RocketLauncher(edict_t *ent) { static int pause_frames[] = {25, 33, 42, 50, 0}; static int fire_frames[] = {5, 0}; Weapon_Generic(ent, 4, 12, 50, 54, pause_frames, fire_frames, Weapon_RocketLauncher_Fire); } /* ====================================================================== */ /* BLASTER / HYPERBLASTER */ void Blaster_Fire(edict_t *ent, vec3_t g_offset, int damage, qboolean hyper, int effect) { vec3_t forward, right; vec3_t start; vec3_t offset; if (!ent) { return; } if (is_quad) { damage *= 4; } AngleVectors(ent->client->v_angle, forward, right, NULL); VectorSet(offset, 24, 8, ent->viewheight - 8); VectorAdd(offset, g_offset, offset); P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start); VectorScale(forward, -2, ent->client->kick_origin); ent->client->kick_angles[0] = -1; fire_blaster(ent, start, forward, damage, 1000, effect, hyper); /* send muzzle flash */ gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); if (hyper) { gi.WriteByte(MZ_HYPERBLASTER | is_silenced); } else { gi.WriteByte(MZ_BLASTER | is_silenced); } gi.multicast(ent->s.origin, MULTICAST_PVS); PlayerNoise(ent, start, PNOISE_WEAPON); } void Weapon_Blaster_Fire(edict_t *ent) { int damage; if (!ent) { return; } if (deathmatch->value) { damage = 15; } else { damage = 10; } Blaster_Fire(ent, vec3_origin, damage, false, EF_BLASTER); ent->client->ps.gunframe++; } void Weapon_Blaster(edict_t *ent) { static int pause_frames[] = {19, 32, 0}; static int fire_frames[] = {5, 0}; if (!ent) { return; } Weapon_Generic(ent, 4, 8, 52, 55, pause_frames, fire_frames, Weapon_Blaster_Fire); } void Weapon_HyperBlaster_Fire(edict_t *ent) { float rotation; vec3_t offset; int effect; int damage; if (!ent) { return; } ent->client->weapon_sound = gi.soundindex("weapons/hyprbl1a.wav"); if (!(ent->client->buttons & BUTTON_ATTACK)) { ent->client->ps.gunframe++; } else { if (!ent->client->pers.inventory[ent->client->ammo_index]) { if (level.time >= ent->pain_debounce_time) { gi.sound(ent, CHAN_VOICE, gi.soundindex( "weapons/noammo.wav"), 1, ATTN_NORM, 0); ent->pain_debounce_time = level.time + 1; } NoAmmoWeaponChange(ent); } else { rotation = (ent->client->ps.gunframe - 5) * 2 * M_PI / 6; offset[0] = -4 * sin(rotation); offset[1] = 0; offset[2] = 4 * cos(rotation); if ((ent->client->ps.gunframe == 6) || (ent->client->ps.gunframe == 9)) { effect = EF_HYPERBLASTER; } else { effect = 0; } if (deathmatch->value) { damage = 15; } else { damage = 20; } Blaster_Fire(ent, offset, damage, true, effect); if (!((int)dmflags->value & DF_INFINITE_AMMO)) { ent->client->pers.inventory[ent->client->ammo_index]--; } ent->client->anim_priority = ANIM_ATTACK; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { ent->s.frame = FRAME_crattak1 - 1; ent->client->anim_end = FRAME_crattak9; } else { ent->s.frame = FRAME_attack1 - 1; ent->client->anim_end = FRAME_attack8; } } ent->client->ps.gunframe++; if ((ent->client->ps.gunframe == 12) && ent->client->pers.inventory[ent->client->ammo_index]) { ent->client->ps.gunframe = 6; } } if (ent->client->ps.gunframe == 12) { gi.sound(ent, CHAN_AUTO, gi.soundindex( "weapons/hyprbd1a.wav"), 1, ATTN_NORM, 0); ent->client->weapon_sound = 0; } } void Weapon_HyperBlaster(edict_t *ent) { static int pause_frames[] = {0}; static int fire_frames[] = {6, 7, 8, 9, 10, 11, 0}; if (!ent) { return; } Weapon_Generic(ent, 5, 20, 49, 53, pause_frames, fire_frames, Weapon_HyperBlaster_Fire); } /* ====================================================================== */ /* MACHINEGUN / CHAINGUN */ void Machinegun_Fire(edict_t *ent) { int i; vec3_t start; vec3_t forward, right; vec3_t angles; int damage = 8; int kick = 2; vec3_t offset; if (!ent) { return; } if (!(ent->client->buttons & BUTTON_ATTACK)) { ent->client->machinegun_shots = 0; ent->client->ps.gunframe++; return; } if (ent->client->ps.gunframe == 5) { ent->client->ps.gunframe = 4; } else { ent->client->ps.gunframe = 5; } if (ent->client->pers.inventory[ent->client->ammo_index] < 1) { ent->client->ps.gunframe = 6; if (level.time >= ent->pain_debounce_time) { gi.sound(ent, CHAN_VOICE, gi.soundindex( "weapons/noammo.wav"), 1, ATTN_NORM, 0); ent->pain_debounce_time = level.time + 1; } NoAmmoWeaponChange(ent); return; } if (is_quad) { damage *= 4; kick *= 4; } for (i = 1; i < 3; i++) { ent->client->kick_origin[i] = crandom() * 0.35; ent->client->kick_angles[i] = crandom() * 0.7; } ent->client->kick_origin[0] = crandom() * 0.35; ent->client->kick_angles[0] = ent->client->machinegun_shots * -1.5; /* raise the gun as it is firing */ if (!deathmatch->value) { ent->client->machinegun_shots++; if (ent->client->machinegun_shots > 9) { ent->client->machinegun_shots = 9; } } /* get start / end positions */ VectorAdd(ent->client->v_angle, ent->client->kick_angles, angles); AngleVectors(angles, forward, right, NULL); VectorSet(offset, 0, 8, ent->viewheight - 8); P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start); fire_bullet(ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_MACHINEGUN); gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); gi.WriteByte(MZ_MACHINEGUN | is_silenced); gi.multicast(ent->s.origin, MULTICAST_PVS); PlayerNoise(ent, start, PNOISE_WEAPON); if (!((int)dmflags->value & DF_INFINITE_AMMO)) { ent->client->pers.inventory[ent->client->ammo_index]--; } ent->client->anim_priority = ANIM_ATTACK; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { ent->s.frame = FRAME_crattak1 - (int)(random() + 0.25); ent->client->anim_end = FRAME_crattak9; } else { ent->s.frame = FRAME_attack1 - (int)(random() + 0.25); ent->client->anim_end = FRAME_attack8; } } void Weapon_Machinegun(edict_t *ent) { static int pause_frames[] = {23, 45, 0}; static int fire_frames[] = {4, 5, 0}; if (!ent) { return; } Weapon_Generic(ent, 3, 5, 45, 49, pause_frames, fire_frames, Machinegun_Fire); } void Chaingun_Fire(edict_t *ent) { int i; int shots; vec3_t start; vec3_t forward, right, up; float r, u; vec3_t offset; int damage; int kick = 2; if (!ent) { return; } if (deathmatch->value) { damage = 6; } else { damage = 8; } if (ent->client->ps.gunframe == 5) { gi.sound(ent, CHAN_AUTO, gi.soundindex( "weapons/chngnu1a.wav"), 1, ATTN_IDLE, 0); } if ((ent->client->ps.gunframe == 14) && !(ent->client->buttons & BUTTON_ATTACK)) { ent->client->ps.gunframe = 32; ent->client->weapon_sound = 0; return; } else if ((ent->client->ps.gunframe == 21) && (ent->client->buttons & BUTTON_ATTACK) && ent->client->pers.inventory[ent->client->ammo_index]) { ent->client->ps.gunframe = 15; } else { ent->client->ps.gunframe++; } if (ent->client->ps.gunframe == 22) { ent->client->weapon_sound = 0; gi.sound(ent, CHAN_AUTO, gi.soundindex( "weapons/chngnd1a.wav"), 1, ATTN_IDLE, 0); } else { ent->client->weapon_sound = gi.soundindex("weapons/chngnl1a.wav"); } ent->client->anim_priority = ANIM_ATTACK; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { ent->s.frame = FRAME_crattak1 - (ent->client->ps.gunframe & 1); ent->client->anim_end = FRAME_crattak9; } else { ent->s.frame = FRAME_attack1 - (ent->client->ps.gunframe & 1); ent->client->anim_end = FRAME_attack8; } if (ent->client->ps.gunframe <= 9) { shots = 1; } else if (ent->client->ps.gunframe <= 14) { if (ent->client->buttons & BUTTON_ATTACK) { shots = 2; } else { shots = 1; } } else { shots = 3; } if (ent->client->pers.inventory[ent->client->ammo_index] < shots) { shots = ent->client->pers.inventory[ent->client->ammo_index]; } if (!shots) { if (level.time >= ent->pain_debounce_time) { gi.sound(ent, CHAN_VOICE, gi.soundindex( "weapons/noammo.wav"), 1, ATTN_NORM, 0); ent->pain_debounce_time = level.time + 1; } NoAmmoWeaponChange(ent); return; } if (is_quad) { damage *= 4; kick *= 4; } for (i = 0; i < 3; i++) { ent->client->kick_origin[i] = crandom() * 0.35; ent->client->kick_angles[i] = crandom() * 0.7; } for (i = 0; i < shots; i++) { /* get start / end positions */ AngleVectors(ent->client->v_angle, forward, right, up); r = 7 + crandom() * 4; u = crandom() * 4; VectorSet(offset, 0, r, u + ent->viewheight - 8); P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start); fire_bullet(ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_CHAINGUN); } /* send muzzle flash */ gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); gi.WriteByte((MZ_CHAINGUN1 + shots - 1) | is_silenced); gi.multicast(ent->s.origin, MULTICAST_PVS); PlayerNoise(ent, start, PNOISE_WEAPON); if (!((int)dmflags->value & DF_INFINITE_AMMO)) { ent->client->pers.inventory[ent->client->ammo_index] -= shots; } } void Weapon_Chaingun(edict_t *ent) { static int pause_frames[] = {38, 43, 51, 61, 0}; static int fire_frames[] = {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0}; if (!ent) { return; } Weapon_Generic(ent, 4, 31, 61, 64, pause_frames, fire_frames, Chaingun_Fire); } /* ====================================================================== */ /* SHOTGUN / SUPERSHOTGUN */ void weapon_shotgun_fire(edict_t *ent) { vec3_t start; vec3_t forward, right; vec3_t offset; int damage = 4; int kick = 8; if (!ent) { return; } if (ent->client->ps.gunframe == 9) { ent->client->ps.gunframe++; return; } AngleVectors(ent->client->v_angle, forward, right, NULL); VectorScale(forward, -2, ent->client->kick_origin); ent->client->kick_angles[0] = -2; VectorSet(offset, 0, 8, ent->viewheight - 8); P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start); if (is_quad) { damage *= 4; kick *= 4; } if (deathmatch->value) { fire_shotgun(ent, start, forward, damage, kick, 500, 500, DEFAULT_DEATHMATCH_SHOTGUN_COUNT, MOD_SHOTGUN); } else { fire_shotgun(ent, start, forward, damage, kick, 500, 500, DEFAULT_SHOTGUN_COUNT, MOD_SHOTGUN); } /* send muzzle flash */ gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); gi.WriteByte(MZ_SHOTGUN | is_silenced); gi.multicast(ent->s.origin, MULTICAST_PVS); ent->client->ps.gunframe++; PlayerNoise(ent, start, PNOISE_WEAPON); if (!((int)dmflags->value & DF_INFINITE_AMMO)) { ent->client->pers.inventory[ent->client->ammo_index]--; } } void Weapon_Shotgun(edict_t *ent) { static int pause_frames[] = {22, 28, 34, 0}; static int fire_frames[] = {8, 9, 0}; if (!ent) { return; } Weapon_Generic(ent, 7, 18, 36, 39, pause_frames, fire_frames, weapon_shotgun_fire); } void weapon_supershotgun_fire(edict_t *ent) { vec3_t start; vec3_t forward, right; vec3_t offset; vec3_t v; int damage = 6; int kick = 12; if (!ent) { return; } AngleVectors(ent->client->v_angle, forward, right, NULL); VectorScale(forward, -2, ent->client->kick_origin); ent->client->kick_angles[0] = -2; VectorSet(offset, 0, 8, ent->viewheight - 8); P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start); if (is_quad) { damage *= 4; kick *= 4; } v[PITCH] = ent->client->v_angle[PITCH]; v[YAW] = ent->client->v_angle[YAW] - 5; v[ROLL] = ent->client->v_angle[ROLL]; AngleVectors(v, forward, NULL, NULL); fire_shotgun(ent, start, forward, damage, kick, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT / 2, MOD_SSHOTGUN); v[YAW] = ent->client->v_angle[YAW] + 5; AngleVectors(v, forward, NULL, NULL); fire_shotgun(ent, start, forward, damage, kick, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT / 2, MOD_SSHOTGUN); /* send muzzle flash */ gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); gi.WriteByte(MZ_SSHOTGUN | is_silenced); gi.multicast(ent->s.origin, MULTICAST_PVS); ent->client->ps.gunframe++; PlayerNoise(ent, start, PNOISE_WEAPON); if (!((int)dmflags->value & DF_INFINITE_AMMO)) { ent->client->pers.inventory[ent->client->ammo_index] -= 2; } } void Weapon_SuperShotgun(edict_t *ent) { static int pause_frames[] = {29, 42, 57, 0}; static int fire_frames[] = {7, 0}; if (!ent) { return; } Weapon_Generic(ent, 6, 17, 57, 61, pause_frames, fire_frames, weapon_supershotgun_fire); } /* ====================================================================== */ /* RAILGUN */ void weapon_railgun_fire(edict_t *ent) { vec3_t start; vec3_t forward, right; vec3_t offset; int damage; int kick; if (!ent) { return; } if (deathmatch->value) { /* normal damage is too extreme in dm */ damage = 100; kick = 200; } else { damage = 150; kick = 250; } if (is_quad) { damage *= 4; kick *= 4; } AngleVectors(ent->client->v_angle, forward, right, NULL); VectorScale(forward, -3, ent->client->kick_origin); ent->client->kick_angles[0] = -3; VectorSet(offset, 0, 7, ent->viewheight - 8); P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start); fire_rail(ent, start, forward, damage, kick); /* send muzzle flash */ gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); gi.WriteByte(MZ_RAILGUN | is_silenced); gi.multicast(ent->s.origin, MULTICAST_PVS); ent->client->ps.gunframe++; PlayerNoise(ent, start, PNOISE_WEAPON); if (!((int)dmflags->value & DF_INFINITE_AMMO)) { ent->client->pers.inventory[ent->client->ammo_index]--; } } void Weapon_Railgun(edict_t *ent) { static int pause_frames[] = {56, 0}; static int fire_frames[] = {4, 0}; if (!ent) { return; } Weapon_Generic(ent, 3, 18, 56, 61, pause_frames, fire_frames, weapon_railgun_fire); } /* ====================================================================== */ /* BFG10K */ void weapon_bfg_fire(edict_t *ent) { vec3_t offset, start; vec3_t forward, right; int damage; float damage_radius = 1000; if (!ent) { return; } if (deathmatch->value) { damage = 200; } else { damage = 500; } if (ent->client->ps.gunframe == 9) { /* send muzzle flash */ gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); gi.WriteByte(MZ_BFG | is_silenced); gi.multicast(ent->s.origin, MULTICAST_PVS); ent->client->ps.gunframe++; PlayerNoise(ent, start, PNOISE_WEAPON); return; } /* cells can go down during windup (from power armor hits), so check again and abort firing if we don't have enough now */ if (ent->client->pers.inventory[ent->client->ammo_index] < 50) { ent->client->ps.gunframe++; return; } if (is_quad) { damage *= 4; } AngleVectors(ent->client->v_angle, forward, right, NULL); VectorScale(forward, -2, ent->client->kick_origin); /* make a big pitch kick with an inverse fall */ ent->client->v_dmg_pitch = -40; ent->client->v_dmg_roll = crandom() * 8; ent->client->v_dmg_time = level.time + DAMAGE_TIME; VectorSet(offset, 8, 8, ent->viewheight - 8); P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start); fire_bfg(ent, start, forward, damage, 400, damage_radius); ent->client->ps.gunframe++; PlayerNoise(ent, start, PNOISE_WEAPON); if (!((int)dmflags->value & DF_INFINITE_AMMO)) { ent->client->pers.inventory[ent->client->ammo_index] -= 50; } } void Weapon_BFG(edict_t *ent) { static int pause_frames[] = {39, 45, 50, 55, 0}; static int fire_frames[] = {9, 17, 0}; if (!ent) { return; } Weapon_Generic(ent, 8, 32, 55, 58, pause_frames, fire_frames, weapon_bfg_fire); } yquake2-QUAKE2_5_32/src/game/player/client.c0000644000175000017500000014101112615162034021233 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Interface between client <-> game and client calculations. * * ======================================================================= */ #include "../header/local.h" #include "../monster/misc/player.h" void ClientUserinfoChanged(edict_t *ent, char *userinfo); void SP_misc_teleporter_dest(edict_t *ent); void Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf); /* * The ugly as hell coop spawnpoint fixup function. * While coop was planed by id, it wasn't part of * the initial release and added later with patch * to version 2.00. The spawnpoints in some maps * were SNAFU, some have wrong targets and some * no name at all. Fix this by matching the coop * spawnpoint target names to the nearest named * single player spot. */ void SP_FixCoopSpots(edict_t *self) { edict_t *spot; vec3_t d; if (!self) { return; } spot = NULL; while (1) { spot = G_Find(spot, FOFS(classname), "info_player_start"); if (!spot) { return; } if (!spot->targetname) { continue; } VectorSubtract(self->s.origin, spot->s.origin, d); if (VectorLength(d) < 550) { if ((!self->targetname) || (Q_stricmp(self->targetname, spot->targetname) != 0)) { self->targetname = spot->targetname; } return; } } } /* * Some maps have no coop spawnpoints at * all. Add these by injecting entities * into the map where they should have * been */ void SP_CreateCoopSpots(edict_t *self) { edict_t *spot; if (!self) { return; } if (Q_stricmp(level.mapname, "security") == 0) { spot = G_Spawn(); spot->classname = "info_player_coop"; spot->s.origin[0] = 188 - 64; spot->s.origin[1] = -164; spot->s.origin[2] = 80; spot->targetname = "jail3"; spot->s.angles[1] = 90; spot = G_Spawn(); spot->classname = "info_player_coop"; spot->s.origin[0] = 188 + 64; spot->s.origin[1] = -164; spot->s.origin[2] = 80; spot->targetname = "jail3"; spot->s.angles[1] = 90; spot = G_Spawn(); spot->classname = "info_player_coop"; spot->s.origin[0] = 188 + 128; spot->s.origin[1] = -164; spot->s.origin[2] = 80; spot->targetname = "jail3"; spot->s.angles[1] = 90; return; } } /* * Some maps have no unnamed (e.g. generic) * info_player_start. This is no problem in * normal gameplay, but if the map is loaded * via console there is a huge chance that * the player will spawn in the wrong point. * Therefore create an unnamed info_player_start * at the correct point. */ void SP_CreateUnnamedSpawn(edict_t *self) { edict_t *spot = G_Spawn(); if (!self) { return; } /* mine1 */ if (Q_stricmp(level.mapname, "mine1") == 0) { if (Q_stricmp(self->targetname, "mintro") == 0) { spot->classname = self->classname; spot->s.origin[0] = self->s.origin[0]; spot->s.origin[1] = self->s.origin[1]; spot->s.origin[2] = self->s.origin[2]; spot->s.angles[1] = self->s.angles[1]; spot->targetname = NULL; return; } } /* mine2 */ if (Q_stricmp(level.mapname, "mine2") == 0) { if (Q_stricmp(self->targetname, "mine1") == 0) { spot->classname = self->classname; spot->s.origin[0] = self->s.origin[0]; spot->s.origin[1] = self->s.origin[1]; spot->s.origin[2] = self->s.origin[2]; spot->s.angles[1] = self->s.angles[1]; spot->targetname = NULL; return; } } /* mine3 */ if (Q_stricmp(level.mapname, "mine3") == 0) { if (Q_stricmp(self->targetname, "mine2a") == 0) { spot->classname = self->classname; spot->s.origin[0] = self->s.origin[0]; spot->s.origin[1] = self->s.origin[1]; spot->s.origin[2] = self->s.origin[2]; spot->s.angles[1] = self->s.angles[1]; spot->targetname = NULL; return; } } /* mine4 */ if (Q_stricmp(level.mapname, "mine4") == 0) { if (Q_stricmp(self->targetname, "mine3") == 0) { spot->classname = self->classname; spot->s.origin[0] = self->s.origin[0]; spot->s.origin[1] = self->s.origin[1]; spot->s.origin[2] = self->s.origin[2]; spot->s.angles[1] = self->s.angles[1]; spot->targetname = NULL; return; } } /* power2 */ if (Q_stricmp(level.mapname, "power2") == 0) { if (Q_stricmp(self->targetname, "power1") == 0) { spot->classname = self->classname; spot->s.origin[0] = self->s.origin[0]; spot->s.origin[1] = self->s.origin[1]; spot->s.origin[2] = self->s.origin[2]; spot->s.angles[1] = self->s.angles[1]; spot->targetname = NULL; return; } } /* waste1 */ if (Q_stricmp(level.mapname, "waste1") == 0) { if (Q_stricmp(self->targetname, "power2") == 0) { spot->classname = self->classname; spot->s.origin[0] = self->s.origin[0]; spot->s.origin[1] = self->s.origin[1]; spot->s.origin[2] = self->s.origin[2]; spot->s.angles[1] = self->s.angles[1]; spot->targetname = NULL; return; } } /* waste2 */ if (Q_stricmp(level.mapname, "waste2") == 0) { if (Q_stricmp(self->targetname, "waste1") == 0) { spot->classname = self->classname; spot->s.origin[0] = self->s.origin[0]; spot->s.origin[1] = self->s.origin[1]; spot->s.origin[2] = self->s.origin[2]; spot->s.angles[1] = self->s.angles[1]; spot->targetname = NULL; return; } } /* waste3 */ if (Q_stricmp(level.mapname, "waste3") == 0) { if (Q_stricmp(self->targetname, "waste2") == 0) { spot->classname = self->classname; spot->s.origin[0] = self->s.origin[0]; spot->s.origin[1] = self->s.origin[1]; spot->s.origin[2] = self->s.origin[2]; spot->s.angles[1] = self->s.angles[1]; spot->targetname = NULL; return; } } /* city3 */ if (Q_stricmp(level.mapname, "city2") == 0) { if (Q_stricmp(self->targetname, "city2NL") == 0) { spot->classname = self->classname; spot->s.origin[0] = self->s.origin[0]; spot->s.origin[1] = self->s.origin[1]; spot->s.origin[2] = self->s.origin[2]; spot->s.angles[1] = self->s.angles[1]; spot->targetname = NULL; return; } } } /* * QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) * The normal starting point for a level. */ void SP_info_player_start(edict_t *self) { if (!self) { return; } /* Call function to hack unnamed spawn points */ self->think = SP_CreateUnnamedSpawn; self->nextthink = level.time + FRAMETIME; if (!coop->value) { return; } if (Q_stricmp(level.mapname, "security") == 0) { /* invoke one of our gross, ugly, disgusting hacks */ self->think = SP_CreateCoopSpots; self->nextthink = level.time + FRAMETIME; } } /* * QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) * potential spawning position for deathmatch games */ void SP_info_player_deathmatch(edict_t *self) { if (!self) { return; } if (!deathmatch->value) { G_FreeEdict(self); return; } SP_misc_teleporter_dest(self); } /* * QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 32) * potential spawning position for coop games */ void SP_info_player_coop(edict_t *self) { if (!self) { return; } if (!coop->value) { G_FreeEdict(self); return; } if ((Q_stricmp(level.mapname, "jail2") == 0) || (Q_stricmp(level.mapname, "jail4") == 0) || (Q_stricmp(level.mapname, "mintro") == 0) || (Q_stricmp(level.mapname, "mine1") == 0) || (Q_stricmp(level.mapname, "mine2") == 0) || (Q_stricmp(level.mapname, "mine3") == 0) || (Q_stricmp(level.mapname, "mine4") == 0) || (Q_stricmp(level.mapname, "lab") == 0) || (Q_stricmp(level.mapname, "boss1") == 0) || (Q_stricmp(level.mapname, "fact1") == 0) || (Q_stricmp(level.mapname, "fact3") == 0) || (Q_stricmp(level.mapname, "waste1") == 0) || /* really? */ (Q_stricmp(level.mapname, "biggun") == 0) || (Q_stricmp(level.mapname, "space") == 0) || (Q_stricmp(level.mapname, "command") == 0) || (Q_stricmp(level.mapname, "power2") == 0) || (Q_stricmp(level.mapname, "strike") == 0) || (Q_stricmp(level.mapname, "city2") == 0)) { /* invoke one of our gross, ugly, disgusting hacks */ self->think = SP_FixCoopSpots; self->nextthink = level.time + FRAMETIME; } } /* * QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32) * The deathmatch intermission point will be at one of these * Use 'angles' instead of 'angle', so you can set pitch or * roll as well as yaw. 'pitch yaw roll' */ void SP_info_player_intermission(void) { /* Thus function cannot be removed * since the info_player_intermission * needs a callback function. Like * every entity. */ } /* ======================================================================= */ void player_pain(edict_t *self /* unused */, edict_t *other /* unused */, float kick /* unused */, int damage /* unused */) { /* Player pain is handled at the end * of the frame in P_DamageFeedback. * This function is still here since * the player is an entity and needs * a pain callback */ } qboolean IsFemale(edict_t *ent) { char *info; if (!ent) { return false; } if (!ent->client) { return false; } info = Info_ValueForKey(ent->client->pers.userinfo, "gender"); if (strstr(info, "crakhor")) { return true; } if ((info[0] == 'f') || (info[0] == 'F')) { return true; } return false; } qboolean IsNeutral(edict_t *ent) { char *info; if (!ent) { return false; } if (!ent->client) { return false; } info = Info_ValueForKey(ent->client->pers.userinfo, "gender"); if (strstr(info, "crakhor")) { return false; } if ((info[0] != 'f') && (info[0] != 'F') && (info[0] != 'm') && (info[0] != 'M')) { return true; } return false; } void ClientObituary(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker) { int mod; char *message; char *message2; qboolean ff; if (!self || !inflictor) { return; } if (coop->value && attacker->client) { meansOfDeath |= MOD_FRIENDLY_FIRE; } if (deathmatch->value || coop->value) { ff = meansOfDeath & MOD_FRIENDLY_FIRE; mod = meansOfDeath & ~MOD_FRIENDLY_FIRE; message = NULL; message2 = ""; switch (mod) { case MOD_SUICIDE: message = "suicides"; break; case MOD_FALLING: message = "cratered"; break; case MOD_CRUSH: message = "was squished"; break; case MOD_WATER: message = "sank like a rock"; break; case MOD_SLIME: message = "melted"; break; case MOD_LAVA: message = "does a back flip into the lava"; break; case MOD_EXPLOSIVE: case MOD_BARREL: message = "blew up"; break; case MOD_EXIT: message = "found a way out"; break; case MOD_TARGET_LASER: message = "saw the light"; break; case MOD_TARGET_BLASTER: message = "got blasted"; break; case MOD_BOMB: case MOD_SPLASH: case MOD_TRIGGER_HURT: message = "was in the wrong place"; break; } if (attacker == self) { switch (mod) { case MOD_HELD_GRENADE: message = "tried to put the pin back in"; break; case MOD_HG_SPLASH: case MOD_G_SPLASH: if (IsNeutral(self)) { message = "tripped on its own grenade"; } else if (IsFemale(self)) { message = "tripped on her own grenade"; } else { message = "tripped on his own grenade"; } break; case MOD_R_SPLASH: if (IsNeutral(self)) { message = "blew itself up"; } else if (IsFemale(self)) { message = "blew herself up"; } else { message = "blew himself up"; } break; case MOD_BFG_BLAST: message = "should have used a smaller gun"; break; default: if (IsNeutral(self)) { message = "killed itself"; } else if (IsFemale(self)) { message = "killed herself"; } else { message = "killed himself"; } break; } } if (message) { gi.bprintf(PRINT_MEDIUM, "%s %s.\n", self->client->pers.netname, message); if (deathmatch->value) { self->client->resp.score--; } self->enemy = NULL; return; } self->enemy = attacker; if (attacker && attacker->client) { switch (mod) { case MOD_BLASTER: message = "was blasted by"; break; case MOD_SHOTGUN: message = "was gunned down by"; break; case MOD_SSHOTGUN: message = "was blown away by"; message2 = "'s super shotgun"; break; case MOD_MACHINEGUN: message = "was machinegunned by"; break; case MOD_CHAINGUN: message = "was cut in half by"; message2 = "'s chaingun"; break; case MOD_GRENADE: message = "was popped by"; message2 = "'s grenade"; break; case MOD_G_SPLASH: message = "was shredded by"; message2 = "'s shrapnel"; break; case MOD_ROCKET: message = "ate"; message2 = "'s rocket"; break; case MOD_R_SPLASH: message = "almost dodged"; message2 = "'s rocket"; break; case MOD_HYPERBLASTER: message = "was melted by"; message2 = "'s hyperblaster"; break; case MOD_RAILGUN: message = "was railed by"; break; case MOD_BFG_LASER: message = "saw the pretty lights from"; message2 = "'s BFG"; break; case MOD_BFG_BLAST: message = "was disintegrated by"; message2 = "'s BFG blast"; break; case MOD_BFG_EFFECT: message = "couldn't hide from"; message2 = "'s BFG"; break; case MOD_HANDGRENADE: message = "caught"; message2 = "'s handgrenade"; break; case MOD_HG_SPLASH: message = "didn't see"; message2 = "'s handgrenade"; break; case MOD_HELD_GRENADE: message = "feels"; message2 = "'s pain"; break; case MOD_TELEFRAG: message = "tried to invade"; message2 = "'s personal space"; break; } if (message) { gi.bprintf(PRINT_MEDIUM, "%s %s %s%s\n", self->client->pers.netname, message, attacker->client->pers.netname, message2); if (deathmatch->value) { if (ff) { attacker->client->resp.score--; } else { attacker->client->resp.score++; } } return; } } } gi.bprintf(PRINT_MEDIUM, "%s died.\n", self->client->pers.netname); if (deathmatch->value) { self->client->resp.score--; } } void TossClientWeapon(edict_t *self) { gitem_t *item; edict_t *drop; qboolean quad; float spread; if (!self) { return; } if (!deathmatch->value) { return; } item = self->client->pers.weapon; if (!self->client->pers.inventory[self->client->ammo_index]) { item = NULL; } if (item && (strcmp(item->pickup_name, "Blaster") == 0)) { item = NULL; } if (!((int)(dmflags->value) & DF_QUAD_DROP)) { quad = false; } else { quad = (self->client->quad_framenum > (level.framenum + 10)); } if (item && quad) { spread = 22.5; } else { spread = 0.0; } if (item) { self->client->v_angle[YAW] -= spread; drop = Drop_Item(self, item); self->client->v_angle[YAW] += spread; drop->spawnflags = DROPPED_PLAYER_ITEM; } if (quad) { self->client->v_angle[YAW] += spread; drop = Drop_Item(self, FindItemByClassname("item_quad")); self->client->v_angle[YAW] -= spread; drop->spawnflags |= DROPPED_PLAYER_ITEM; drop->touch = Touch_Item; drop->nextthink = level.time + (self->client->quad_framenum - level.framenum) * FRAMETIME; drop->think = G_FreeEdict; } } void LookAtKiller(edict_t *self, edict_t *inflictor, edict_t *attacker) { vec3_t dir; if (!self || !inflictor || !attacker) { return; } if (attacker && (attacker != world) && (attacker != self)) { VectorSubtract(attacker->s.origin, self->s.origin, dir); } else if (inflictor && (inflictor != world) && (inflictor != self)) { VectorSubtract(inflictor->s.origin, self->s.origin, dir); } else { self->client->killer_yaw = self->s.angles[YAW]; return; } if (dir[0]) { self->client->killer_yaw = 180 / M_PI *atan2(dir[1], dir[0]); } else { self->client->killer_yaw = 0; if (dir[1] > 0) { self->client->killer_yaw = 90; } else if (dir[1] < 0) { self->client->killer_yaw = -90; } } if (self->client->killer_yaw < 0) { self->client->killer_yaw += 360; } } void player_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point /* unused */) { int n; if (!self || !inflictor || !attacker) { return; } VectorClear(self->avelocity); self->takedamage = DAMAGE_YES; self->movetype = MOVETYPE_TOSS; self->s.modelindex2 = 0; /* remove linked weapon model */ self->s.angles[0] = 0; self->s.angles[2] = 0; self->s.sound = 0; self->client->weapon_sound = 0; self->maxs[2] = -8; self->svflags |= SVF_DEADMONSTER; if (!self->deadflag) { self->client->respawn_time = level.time + 1.0; LookAtKiller(self, inflictor, attacker); self->client->ps.pmove.pm_type = PM_DEAD; ClientObituary(self, inflictor, attacker); TossClientWeapon(self); if (deathmatch->value) { Cmd_Help_f(self); /* show scores */ } /* clear inventory: this is kind of ugly, but it's how we want to handle keys in coop */ for (n = 0; n < game.num_items; n++) { if (coop->value && itemlist[n].flags & IT_KEY) { self->client->resp.coop_respawn.inventory[n] = self->client->pers.inventory[n]; } self->client->pers.inventory[n] = 0; } } /* remove powerups */ self->client->quad_framenum = 0; self->client->invincible_framenum = 0; self->client->breather_framenum = 0; self->client->enviro_framenum = 0; self->flags &= ~FL_POWER_ARMOR; if (self->health < -40) { /* gib */ gi.sound(self, CHAN_BODY, gi.soundindex( "misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowClientHead(self, damage); self->takedamage = DAMAGE_NO; } else { /* normal death */ if (!self->deadflag) { static int i; i = (i + 1) % 3; /* start a death animation */ self->client->anim_priority = ANIM_DEATH; if (self->client->ps.pmove.pm_flags & PMF_DUCKED) { self->s.frame = FRAME_crdeath1 - 1; self->client->anim_end = FRAME_crdeath5; } else { switch (i) { case 0: self->s.frame = FRAME_death101 - 1; self->client->anim_end = FRAME_death106; break; case 1: self->s.frame = FRAME_death201 - 1; self->client->anim_end = FRAME_death206; break; case 2: self->s.frame = FRAME_death301 - 1; self->client->anim_end = FRAME_death308; break; } } gi.sound(self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (randk() % 4) + 1)), 1, ATTN_NORM, 0); } } self->deadflag = DEAD_DEAD; gi.linkentity(self); } /* ======================================================================= */ /* * This is only called when the game first * initializes in single player, but is called * after each death and level change in deathmatch */ void InitClientPersistant(gclient_t *client) { gitem_t *item; if (!client) { return; } memset(&client->pers, 0, sizeof(client->pers)); item = FindItem("Blaster"); client->pers.selected_item = ITEM_INDEX(item); client->pers.inventory[client->pers.selected_item] = 1; client->pers.weapon = item; client->pers.health = 100; client->pers.max_health = 100; client->pers.max_bullets = 200; client->pers.max_shells = 100; client->pers.max_rockets = 50; client->pers.max_grenades = 50; client->pers.max_cells = 200; client->pers.max_slugs = 50; client->pers.connected = true; } void InitClientResp(gclient_t *client) { if (!client) { return; } memset(&client->resp, 0, sizeof(client->resp)); client->resp.enterframe = level.framenum; client->resp.coop_respawn = client->pers; } /* * Some information that should be persistant, like health, * is still stored in the edict structure, so it needs to * be mirrored out to the client structure before all the * edicts are wiped. */ void SaveClientData(void) { int i; edict_t *ent; for (i = 0; i < game.maxclients; i++) { ent = &g_edicts[1 + i]; if (!ent->inuse) { continue; } game.clients[i].pers.health = ent->health; game.clients[i].pers.max_health = ent->max_health; game.clients[i].pers.savedFlags = (ent->flags & (FL_GODMODE | FL_NOTARGET | FL_POWER_ARMOR)); if (coop->value) { game.clients[i].pers.score = ent->client->resp.score; } } } void FetchClientEntData(edict_t *ent) { if (!ent) { return; } ent->health = ent->client->pers.health; ent->max_health = ent->client->pers.max_health; ent->flags |= ent->client->pers.savedFlags; if (coop->value) { ent->client->resp.score = ent->client->pers.score; } } /* ======================================================================= */ /* * Returns the distance to the * nearest player from the given spot */ float PlayersRangeFromSpot(edict_t *spot) { edict_t *player; float bestplayerdistance; vec3_t v; int n; float playerdistance; if (!spot) { return 0; } bestplayerdistance = 9999999; for (n = 1; n <= maxclients->value; n++) { player = &g_edicts[n]; if (!player->inuse) { continue; } if (player->health <= 0) { continue; } VectorSubtract(spot->s.origin, player->s.origin, v); playerdistance = VectorLength(v); if (playerdistance < bestplayerdistance) { bestplayerdistance = playerdistance; } } return bestplayerdistance; } /* * go to a random point, but NOT the two * points closest to other players */ edict_t * SelectRandomDeathmatchSpawnPoint(void) { edict_t *spot, *spot1, *spot2; int count = 0; int selection; float range, range1, range2; spot = NULL; range1 = range2 = 99999; spot1 = spot2 = NULL; while ((spot = G_Find(spot, FOFS(classname), "info_player_deathmatch")) != NULL) { count++; range = PlayersRangeFromSpot(spot); if (range < range1) { range1 = range; spot1 = spot; } else if (range < range2) { range2 = range; spot2 = spot; } } if (!count) { return NULL; } if (count <= 2) { spot1 = spot2 = NULL; } else { if (spot1) { count--; } if (spot2) { count--; } } selection = randk() % count; spot = NULL; do { spot = G_Find(spot, FOFS(classname), "info_player_deathmatch"); if ((spot == spot1) || (spot == spot2)) { selection++; } } while (selection--); return spot; } edict_t * SelectFarthestDeathmatchSpawnPoint(void) { edict_t *bestspot; float bestdistance, bestplayerdistance; edict_t *spot; spot = NULL; bestspot = NULL; bestdistance = 0; while ((spot = G_Find(spot, FOFS(classname), "info_player_deathmatch")) != NULL) { bestplayerdistance = PlayersRangeFromSpot(spot); if (bestplayerdistance > bestdistance) { bestspot = spot; bestdistance = bestplayerdistance; } } if (bestspot) { return bestspot; } /* if there is a player just spawned on each and every start spot/ we have no choice to turn one into a telefrag meltdown */ spot = G_Find(NULL, FOFS(classname), "info_player_deathmatch"); return spot; } edict_t * SelectDeathmatchSpawnPoint(void) { if ((int)(dmflags->value) & DF_SPAWN_FARTHEST) { return SelectFarthestDeathmatchSpawnPoint(); } else { return SelectRandomDeathmatchSpawnPoint(); } } edict_t * SelectCoopSpawnPoint(edict_t *ent) { int index; edict_t *spot = NULL; char *target; if (!ent) { return NULL; } index = ent->client - game.clients; /* player 0 starts in normal player spawn point */ if (!index) { return NULL; } spot = NULL; /* assume there are four coop spots at each spawnpoint */ while (1) { spot = G_Find(spot, FOFS(classname), "info_player_coop"); if (!spot) { return NULL; /* we didn't have enough... */ } target = spot->targetname; if (!target) { target = ""; } if (Q_stricmp(game.spawnpoint, target) == 0) { /* this is a coop spawn point for one of the clients here */ index--; if (!index) { return spot; /* this is it */ } } } return spot; } /* * Chooses a player start, deathmatch start, coop start, etc */ void SelectSpawnPoint(edict_t *ent, vec3_t origin, vec3_t angles) { edict_t *spot = NULL; edict_t *coopspot = NULL; int index; int counter = 0; vec3_t d; if (!ent) { return; } if (deathmatch->value) { spot = SelectDeathmatchSpawnPoint(); } else if (coop->value) { spot = SelectCoopSpawnPoint(ent); } /* find a single player start spot */ if (!spot) { while ((spot = G_Find(spot, FOFS(classname), "info_player_start")) != NULL) { if (!game.spawnpoint[0] && !spot->targetname) { break; } if (!game.spawnpoint[0] || !spot->targetname) { continue; } if (Q_stricmp(game.spawnpoint, spot->targetname) == 0) { break; } } if (!spot) { if (!game.spawnpoint[0]) { /* there wasn't a spawnpoint without a target, so use any */ spot = G_Find(spot, FOFS(classname), "info_player_start"); } if (!spot) { gi.error("Couldn't find spawn point %s\n", game.spawnpoint); } } } /* If we are in coop and we didn't find a coop spawnpoint due to map bugs (not correctly connected or the map was loaded via console and thus no previously map is known to the client) use one in 550 units radius. */ if (coop->value) { index = ent->client - game.clients; if (Q_stricmp(spot->classname, "info_player_start") == 0 && index != 0) { while(counter < 3) { coopspot = G_Find(coopspot, FOFS(classname), "info_player_coop"); if (!coopspot) { break; } VectorSubtract(coopspot->s.origin, spot->s.origin, d); if ((VectorLength(d) < 550)) { if (index == counter) { spot = coopspot; break; } else { counter++; } } } } } VectorCopy(spot->s.origin, origin); origin[2] += 9; VectorCopy(spot->s.angles, angles); } /* ====================================================================== */ void InitBodyQue(void) { if (deathmatch->value || coop->value) { int i; edict_t *ent; level.body_que = 0; for (i = 0; i < BODY_QUEUE_SIZE; i++) { ent = G_Spawn(); ent->classname = "bodyque"; } } } void body_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage, vec3_t point /* unused */) { int n; if (!self) { return; } if (self->health < -40) { gi.sound(self, CHAN_BODY, gi.soundindex( "misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } self->s.origin[2] -= 48; ThrowClientHead(self, damage); self->takedamage = DAMAGE_NO; } } void CopyToBodyQue(edict_t *ent) { edict_t *body; if (!ent) { return; } /* grab a body que and cycle to the next one */ body = &g_edicts[(int)maxclients->value + level.body_que + 1]; level.body_que = (level.body_que + 1) % BODY_QUEUE_SIZE; gi.unlinkentity(ent); gi.unlinkentity(body); body->s = ent->s; body->s.number = body - g_edicts; body->svflags = ent->svflags; VectorCopy(ent->mins, body->mins); VectorCopy(ent->maxs, body->maxs); VectorCopy(ent->absmin, body->absmin); VectorCopy(ent->absmax, body->absmax); VectorCopy(ent->size, body->size); body->solid = ent->solid; body->clipmask = ent->clipmask; body->owner = ent->owner; body->movetype = ent->movetype; body->die = body_die; body->takedamage = DAMAGE_YES; gi.linkentity(body); } void respawn(edict_t *self) { if (!self) { return; } if (deathmatch->value || coop->value) { /* spectator's don't leave bodies */ if (self->movetype != MOVETYPE_NOCLIP) { CopyToBodyQue(self); } self->svflags &= ~SVF_NOCLIENT; PutClientInServer(self); /* add a teleportation effect */ self->s.event = EV_PLAYER_TELEPORT; /* hold in place briefly */ self->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; self->client->ps.pmove.pm_time = 14; self->client->respawn_time = level.time; return; } /* restart the entire server */ gi.AddCommandString("menu_loadgame\n"); } /* * only called when pers.spectator changes * note that resp.spectator should be the * opposite of pers.spectator here */ void spectator_respawn(edict_t *ent) { int i, numspec; if (!ent) { return; } /* if the user wants to become a spectator, make sure he doesn't exceed max_spectators */ if (ent->client->pers.spectator) { char *value = Info_ValueForKey(ent->client->pers.userinfo, "spectator"); if (*spectator_password->string && strcmp(spectator_password->string, "none") && strcmp(spectator_password->string, value)) { gi.cprintf(ent, PRINT_HIGH, "Spectator password incorrect.\n"); ent->client->pers.spectator = false; gi.WriteByte(svc_stufftext); gi.WriteString("spectator 0\n"); gi.unicast(ent, true); return; } /* count spectators */ for (i = 1, numspec = 0; i <= maxclients->value; i++) { if (g_edicts[i].inuse && g_edicts[i].client->pers.spectator) { numspec++; } } if (numspec >= maxspectators->value) { gi.cprintf(ent, PRINT_HIGH, "Server spectator limit is full."); ent->client->pers.spectator = false; /* reset his spectator var */ gi.WriteByte(svc_stufftext); gi.WriteString("spectator 0\n"); gi.unicast(ent, true); return; } } else { /* he was a spectator and wants to join the game he must have the right password */ char *value = Info_ValueForKey(ent->client->pers.userinfo, "password"); if (*password->string && strcmp(password->string, "none") && strcmp(password->string, value)) { gi.cprintf(ent, PRINT_HIGH, "Password incorrect.\n"); ent->client->pers.spectator = true; gi.WriteByte(svc_stufftext); gi.WriteString("spectator 1\n"); gi.unicast(ent, true); return; } } /* clear client on respawn */ ent->client->resp.score = ent->client->pers.score = 0; ent->svflags &= ~SVF_NOCLIENT; PutClientInServer(ent); /* add a teleportation effect */ if (!ent->client->pers.spectator) { /* send effect */ gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); gi.WriteByte(MZ_LOGIN); gi.multicast(ent->s.origin, MULTICAST_PVS); /* hold in place briefly */ ent->client->ps.pmove.pm_flags = PMF_TIME_TELEPORT; ent->client->ps.pmove.pm_time = 14; } ent->client->respawn_time = level.time; if (ent->client->pers.spectator) { gi.bprintf(PRINT_HIGH, "%s has moved to the sidelines\n", ent->client->pers.netname); } else { gi.bprintf(PRINT_HIGH, "%s joined the game\n", ent->client->pers.netname); } } /* ============================================================== */ /* * Called when a player connects to * a server or respawns in a deathmatch. */ void PutClientInServer(edict_t *ent) { char userinfo[MAX_INFO_STRING]; if (!ent) { return; } vec3_t mins = {-16, -16, -24}; vec3_t maxs = {16, 16, 32}; int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; client_persistant_t saved; client_respawn_t resp; /* find a spawn point do it before setting health back up, so farthest ranging doesn't count this client */ SelectSpawnPoint(ent, spawn_origin, spawn_angles); index = ent - g_edicts - 1; client = ent->client; /* deathmatch wipes most client data every spawn */ if (deathmatch->value) { resp = client->resp; memcpy(userinfo, client->pers.userinfo, sizeof(userinfo)); InitClientPersistant(client); ClientUserinfoChanged(ent, userinfo); } else if (coop->value) { resp = client->resp; memcpy(userinfo, client->pers.userinfo, sizeof(userinfo)); resp.coop_respawn.game_helpchanged = client->pers.game_helpchanged; resp.coop_respawn.helpchanged = client->pers.helpchanged; client->pers = resp.coop_respawn; ClientUserinfoChanged(ent, userinfo); if (resp.score > client->pers.score) { client->pers.score = resp.score; } } else { memset(&resp, 0, sizeof(resp)); } memcpy(userinfo, client->pers.userinfo, sizeof(userinfo)); ClientUserinfoChanged(ent, userinfo); /* clear everything but the persistant data */ saved = client->pers; memset(client, 0, sizeof(*client)); client->pers = saved; if (client->pers.health <= 0) { InitClientPersistant(client); } client->resp = resp; /* copy some data from the client to the entity */ FetchClientEntData(ent); /* clear entity values */ ent->groundentity = NULL; ent->client = &game.clients[index]; ent->takedamage = DAMAGE_AIM; ent->movetype = MOVETYPE_WALK; ent->viewheight = 22; ent->inuse = true; ent->classname = "player"; ent->mass = 200; ent->solid = SOLID_BBOX; ent->deadflag = DEAD_NO; ent->air_finished = level.time + 12; ent->clipmask = MASK_PLAYERSOLID; ent->model = "players/male/tris.md2"; ent->pain = player_pain; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags &= ~FL_NO_KNOCKBACK; ent->svflags = 0; VectorCopy(mins, ent->mins); VectorCopy(maxs, ent->maxs); VectorClear(ent->velocity); /* clear playerstate values */ memset(&ent->client->ps, 0, sizeof(client->ps)); client->ps.pmove.origin[0] = spawn_origin[0] * 8; client->ps.pmove.origin[1] = spawn_origin[1] * 8; client->ps.pmove.origin[2] = spawn_origin[2] * 8; if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) { client->ps.fov = 90; } else { client->ps.fov = (int)strtol(Info_ValueForKey(client->pers.userinfo, "fov"), (char **)NULL, 10); if (client->ps.fov < 1) { client->ps.fov = 90; } else if (client->ps.fov > 160) { client->ps.fov = 160; } } client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model); /* clear entity state values */ ent->s.effects = 0; ent->s.modelindex = 255; /* will use the skin specified model */ ent->s.modelindex2 = 255; /* custom gun model */ /* sknum is player num and weapon number weapon number will be added in changeweapon */ ent->s.skinnum = ent - g_edicts - 1; ent->s.frame = 0; VectorCopy(spawn_origin, ent->s.origin); ent->s.origin[2] += 1; /* make sure off ground */ VectorCopy(ent->s.origin, ent->s.old_origin); /* set the delta angle */ for (i = 0; i < 3; i++) { client->ps.pmove.delta_angles[i] = ANGLE2SHORT( spawn_angles[i] - client->resp.cmd_angles[i]); } ent->s.angles[PITCH] = 0; ent->s.angles[YAW] = spawn_angles[YAW]; ent->s.angles[ROLL] = 0; VectorCopy(ent->s.angles, client->ps.viewangles); VectorCopy(ent->s.angles, client->v_angle); /* spawn a spectator */ if (client->pers.spectator) { client->chase_target = NULL; client->resp.spectator = true; ent->movetype = MOVETYPE_NOCLIP; ent->solid = SOLID_NOT; ent->svflags |= SVF_NOCLIENT; ent->client->ps.gunindex = 0; gi.linkentity(ent); return; } else { client->resp.spectator = false; } if (!KillBox(ent)) { /* could't spawn in? */ } gi.linkentity(ent); /* force the current weapon up */ client->newweapon = client->pers.weapon; ChangeWeapon(ent); } /* * A client has just connected to the server in * deathmatch mode, so clear everything out before * starting them. */ void ClientBeginDeathmatch(edict_t *ent) { if (!ent) { return; } G_InitEdict(ent); InitClientResp(ent->client); /* locate ent at a spawn point */ PutClientInServer(ent); if (level.intermissiontime) { MoveClientToIntermission(ent); } else { /* send effect */ gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); gi.WriteByte(MZ_LOGIN); gi.multicast(ent->s.origin, MULTICAST_PVS); } gi.bprintf(PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); /* make sure all view stuff is valid */ ClientEndServerFrame(ent); } /* * called when a client has finished connecting, and is ready * to be placed into the game. This will happen every level load. */ void ClientBegin(edict_t *ent) { int i; if (!ent) { return; } ent->client = game.clients + (ent - g_edicts - 1); if (deathmatch->value) { ClientBeginDeathmatch(ent); return; } /* if there is already a body waiting for us (a loadgame), just take it, otherwise spawn one from scratch */ if (ent->inuse == true) { /* the client has cleared the client side viewangles upon connecting to the server, which is different than the state when the game is saved, so we need to compensate with deltaangles */ for (i = 0; i < 3; i++) { ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT( ent->client->ps.viewangles[i]); } } else { /* a spawn point will completely reinitialize the entity except for the persistant data that was initialized at ClientConnect() time */ G_InitEdict(ent); ent->classname = "player"; InitClientResp(ent->client); PutClientInServer(ent); } if (level.intermissiontime) { MoveClientToIntermission(ent); } else { /* send effect if in a multiplayer game */ if (game.maxclients > 1) { gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); gi.WriteByte(MZ_LOGIN); gi.multicast(ent->s.origin, MULTICAST_PVS); gi.bprintf(PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname); } } /* make sure all view stuff is valid */ ClientEndServerFrame(ent); } /* * Called whenever the player updates a userinfo variable. * The game can override any of the settings in place * (forcing skins or names, etc) before copying it off. */ void ClientUserinfoChanged(edict_t *ent, char *userinfo) { char *s; int playernum; if (!ent || !userinfo) { return; } /* check for malformed or illegal info strings */ if (!Info_Validate(userinfo)) { strcpy(userinfo, "\\name\\badinfo\\skin\\male/grunt"); } /* set name */ s = Info_ValueForKey(userinfo, "name"); Q_strlcpy(ent->client->pers.netname, s, sizeof(ent->client->pers.netname)); /* set spectator */ s = Info_ValueForKey(userinfo, "spectator"); /* spectators are only supported in deathmatch */ if (deathmatch->value && *s && strcmp(s, "0")) { ent->client->pers.spectator = true; } else { ent->client->pers.spectator = false; } /* set skin */ s = Info_ValueForKey(userinfo, "skin"); playernum = ent - g_edicts - 1; /* combine name and skin into a configstring */ gi.configstring(CS_PLAYERSKINS + playernum, va("%s\\%s", ent->client->pers.netname, s)); /* fov */ if (deathmatch->value && ((int)dmflags->value & DF_FIXED_FOV)) { ent->client->ps.fov = 90; } else { ent->client->ps.fov = (int)strtol(Info_ValueForKey(userinfo, "fov"), (char **)NULL, 10); if (ent->client->ps.fov < 1) { ent->client->ps.fov = 90; } else if (ent->client->ps.fov > 160) { ent->client->ps.fov = 160; } } /* handedness */ s = Info_ValueForKey(userinfo, "hand"); if (strlen(s)) { ent->client->pers.hand = (int)strtol(s, (char **)NULL, 10); } /* save off the userinfo in case we want to check something later */ Q_strlcpy(ent->client->pers.userinfo, userinfo, sizeof(ent->client->pers.userinfo)); } /* * Called when a player begins connecting to the server. * The game can refuse entrance to a client by returning false. * If the client is allowed, the connection process will continue * and eventually get to ClientBegin(). Changing levels will NOT * cause this to be called again, but loadgames will. */ qboolean ClientConnect(edict_t *ent, char *userinfo) { char *value; if (!ent || !userinfo) { return false; } /* check to see if they are on the banned IP list */ value = Info_ValueForKey(userinfo, "ip"); if (SV_FilterPacket(value)) { Info_SetValueForKey(userinfo, "rejmsg", "Banned."); return false; } /* check for a spectator */ value = Info_ValueForKey(userinfo, "spectator"); if (deathmatch->value && *value && strcmp(value, "0")) { int i, numspec; if (*spectator_password->string && strcmp(spectator_password->string, "none") && strcmp(spectator_password->string, value)) { Info_SetValueForKey(userinfo, "rejmsg", "Spectator password required or incorrect."); return false; } /* count spectators */ for (i = numspec = 0; i < maxclients->value; i++) { if (g_edicts[i + 1].inuse && g_edicts[i + 1].client->pers.spectator) { numspec++; } } if (numspec >= maxspectators->value) { Info_SetValueForKey(userinfo, "rejmsg", "Server spectator limit is full."); return false; } } else { /* check for a password */ value = Info_ValueForKey(userinfo, "password"); if (*password->string && strcmp(password->string, "none") && strcmp(password->string, value)) { Info_SetValueForKey(userinfo, "rejmsg", "Password required or incorrect."); return false; } } /* they can connect */ ent->client = game.clients + (ent - g_edicts - 1); /* if there is already a body waiting for us (a loadgame), just take it, otherwise spawn one from scratch */ if (ent->inuse == false) { /* clear the respawning variables */ InitClientResp(ent->client); if (!game.autosaved || !ent->client->pers.weapon) { InitClientPersistant(ent->client); } } ClientUserinfoChanged(ent, userinfo); if (game.maxclients > 1) { gi.dprintf("%s connected\n", ent->client->pers.netname); } ent->svflags = 0; /* make sure we start with known default */ ent->client->pers.connected = true; return true; } /* * Called when a player drops from the server. * Will not be called between levels. */ void ClientDisconnect(edict_t *ent) { int playernum; if (!ent) { return; } if (!ent->client) { return; } gi.bprintf(PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname); /* send effect */ gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); gi.WriteByte(MZ_LOGOUT); gi.multicast(ent->s.origin, MULTICAST_PVS); gi.unlinkentity(ent); ent->s.modelindex = 0; ent->solid = SOLID_NOT; ent->inuse = false; ent->classname = "disconnected"; ent->client->pers.connected = false; playernum = ent - g_edicts - 1; gi.configstring(CS_PLAYERSKINS + playernum, ""); } /* ============================================================== */ edict_t *pm_passent; /* * pmove doesn't need to know * about passent and contentmask */ trace_t PM_trace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end) { if (pm_passent->health > 0) { return gi.trace(start, mins, maxs, end, pm_passent, MASK_PLAYERSOLID); } else { return gi.trace(start, mins, maxs, end, pm_passent, MASK_DEADSOLID); } } unsigned CheckBlock(void *b, int c) { int v, i; if (!b) { return 0; } v = 0; for (i = 0; i < c; i++) { v += ((byte *)b)[i]; } return v; } void PrintPmove(pmove_t *pm) { unsigned c1, c2; if (!pm) { return; } c1 = CheckBlock(&pm->s, sizeof(pm->s)); c2 = CheckBlock(&pm->cmd, sizeof(pm->cmd)); gi.dprintf("sv %3i:%i %i\n", pm->cmd.impulse, c1, c2); } /* * This will be called once for each client frame, which will * usually be a couple times for each server frame. */ void ClientThink(edict_t *ent, usercmd_t *ucmd) { gclient_t *client; edict_t *other; int i, j; pmove_t pm; if (!ent || !ucmd) { return; } level.current_entity = ent; client = ent->client; if (level.intermissiontime) { client->ps.pmove.pm_type = PM_FREEZE; /* can exit intermission after five seconds */ if ((level.time > level.intermissiontime + 5.0) && (ucmd->buttons & BUTTON_ANY)) { level.exitintermission = true; } return; } pm_passent = ent; if (ent->client->chase_target) { client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); } else { /* set up for pmove */ memset(&pm, 0, sizeof(pm)); if (ent->movetype == MOVETYPE_NOCLIP) { client->ps.pmove.pm_type = PM_SPECTATOR; } else if (ent->s.modelindex != 255) { client->ps.pmove.pm_type = PM_GIB; } else if (ent->deadflag) { client->ps.pmove.pm_type = PM_DEAD; } else { client->ps.pmove.pm_type = PM_NORMAL; } client->ps.pmove.gravity = sv_gravity->value; pm.s = client->ps.pmove; for (i = 0; i < 3; i++) { pm.s.origin[i] = ent->s.origin[i] * 8; /* save to an int first, in case the short overflows * so we get defined behavior (at least with -fwrapv) */ int tmpVel = ent->velocity[i] * 8; pm.s.velocity[i] = tmpVel; } if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s))) { pm.snapinitial = true; } pm.cmd = *ucmd; pm.trace = PM_trace; /* adds default parms */ pm.pointcontents = gi.pointcontents; /* perform a pmove */ gi.Pmove(&pm); /* save results of pmove */ client->ps.pmove = pm.s; client->old_pmove = pm.s; for (i = 0; i < 3; i++) { ent->s.origin[i] = pm.s.origin[i] * 0.125; ent->velocity[i] = pm.s.velocity[i] * 0.125; } VectorCopy(pm.mins, ent->mins); VectorCopy(pm.maxs, ent->maxs); client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]); client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]); client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]); if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0)) { gi.sound(ent, CHAN_VOICE, gi.soundindex( "*jump1.wav"), 1, ATTN_NORM, 0); PlayerNoise(ent, ent->s.origin, PNOISE_SELF); } ent->viewheight = pm.viewheight; ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; ent->groundentity = pm.groundentity; if (pm.groundentity) { ent->groundentity_linkcount = pm.groundentity->linkcount; } if (ent->deadflag) { client->ps.viewangles[ROLL] = 40; client->ps.viewangles[PITCH] = -15; client->ps.viewangles[YAW] = client->killer_yaw; } else { VectorCopy(pm.viewangles, client->v_angle); VectorCopy(pm.viewangles, client->ps.viewangles); } gi.linkentity(ent); if (ent->movetype != MOVETYPE_NOCLIP) { G_TouchTriggers(ent); } /* touch other objects */ for (i = 0; i < pm.numtouch; i++) { other = pm.touchents[i]; for (j = 0; j < i; j++) { if (pm.touchents[j] == other) { break; } } if (j != i) { continue; /* duplicated */ } if (!other->touch) { continue; } other->touch(other, ent, NULL, NULL); } } client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; client->latched_buttons |= client->buttons & ~client->oldbuttons; /* save light level the player is standing on for monster sighting AI */ ent->light_level = ucmd->lightlevel; /* fire weapon from final position if needed */ if (client->latched_buttons & BUTTON_ATTACK) { if (client->resp.spectator) { client->latched_buttons = 0; if (client->chase_target) { client->chase_target = NULL; client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; } else { GetChaseTarget(ent); } } else if (!client->weapon_thunk) { client->weapon_thunk = true; Think_Weapon(ent); } } if (client->resp.spectator) { if (ucmd->upmove >= 10) { if (!(client->ps.pmove.pm_flags & PMF_JUMP_HELD)) { client->ps.pmove.pm_flags |= PMF_JUMP_HELD; if (client->chase_target) { ChaseNext(ent); } else { GetChaseTarget(ent); } } } else { client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD; } } /* update chase cam if being followed */ for (i = 1; i <= maxclients->value; i++) { other = g_edicts + i; if (other->inuse && (other->client->chase_target == ent)) { UpdateChaseCam(other); } } } /* * This will be called once for each server * frame, before running any other entities * in the world. */ void ClientBeginServerFrame(edict_t *ent) { gclient_t *client; int buttonMask; if (!ent) { return; } if (level.intermissiontime) { return; } client = ent->client; if (deathmatch->value && (client->pers.spectator != client->resp.spectator) && ((level.time - client->respawn_time) >= 5)) { spectator_respawn(ent); return; } /* run weapon animations if it hasn't been done by a ucmd_t */ if (!client->weapon_thunk && !client->resp.spectator) { Think_Weapon(ent); } else { client->weapon_thunk = false; } if (ent->deadflag) { /* wait for any button just going down */ if (level.time > client->respawn_time) { /* in deathmatch, only wait for attack button */ if (deathmatch->value) { buttonMask = BUTTON_ATTACK; } else { buttonMask = -1; } if ((client->latched_buttons & buttonMask) || (deathmatch->value && ((int)dmflags->value & DF_FORCE_RESPAWN))) { respawn(ent); client->latched_buttons = 0; } } return; } /* add player trail so monsters can follow */ if (!deathmatch->value) { if (!visible(ent, PlayerTrail_LastSpot())) { PlayerTrail_Add(ent->s.old_origin); } } client->latched_buttons = 0; } yquake2-QUAKE2_5_32/src/game/player/view.c0000644000175000017500000006642412615162034020745 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 "camera" through which the player looks into the game. * * ======================================================================= */ #include "../header/local.h" #include "../monster/misc/player.h" static edict_t *current_player; static gclient_t *current_client; static vec3_t forward, right, up; float xyspeed; float bobmove; int bobcycle; /* odd cycles are right foot going forward */ float bobfracsin; /* sin(bobfrac*M_PI) */ float SV_CalcRoll(vec3_t angles, vec3_t velocity) { float sign; float side; float value; side = DotProduct(velocity, right); sign = side < 0 ? -1 : 1; side = fabs(side); value = sv_rollangle->value; if (side < sv_rollspeed->value) { side = side * value / sv_rollspeed->value; } else { side = value; } return side * sign; } /* * Handles color blends and view kicks */ void P_DamageFeedback(edict_t *player) { gclient_t *client; float side; float realcount, count, kick; vec3_t v; int r, l; static vec3_t power_color = {0.0, 1.0, 0.0}; static vec3_t acolor = {1.0, 1.0, 1.0}; static vec3_t bcolor = {1.0, 0.0, 0.0}; if (!player) { return; } client = player->client; /* flash the backgrounds behind the status numbers */ client->ps.stats[STAT_FLASHES] = 0; if (client->damage_blood) { client->ps.stats[STAT_FLASHES] |= 1; } if (client->damage_armor && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum)) { client->ps.stats[STAT_FLASHES] |= 2; } /* total points of damage shot at the player this frame */ count = (client->damage_blood + client->damage_armor + client->damage_parmor); if (count == 0) { return; /* didn't take any damage */ } /* start a pain animation if still in the player model */ if ((client->anim_priority < ANIM_PAIN) && (player->s.modelindex == 255)) { static int i; client->anim_priority = ANIM_PAIN; if (client->ps.pmove.pm_flags & PMF_DUCKED) { player->s.frame = FRAME_crpain1 - 1; client->anim_end = FRAME_crpain4; } else { i = (i + 1) % 3; switch (i) { case 0: player->s.frame = FRAME_pain101 - 1; client->anim_end = FRAME_pain104; break; case 1: player->s.frame = FRAME_pain201 - 1; client->anim_end = FRAME_pain204; break; case 2: player->s.frame = FRAME_pain301 - 1; client->anim_end = FRAME_pain304; break; } } } realcount = count; if (count < 10) { count = 10; /* always make a visible effect */ } /* play an apropriate pain sound */ if ((level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) && (client->invincible_framenum <= level.framenum)) { r = 1 + (randk() & 1); player->pain_debounce_time = level.time + 0.7; if (player->health < 25) { l = 25; } else if (player->health < 50) { l = 50; } else if (player->health < 75) { l = 75; } else { l = 100; } gi.sound(player, CHAN_VOICE, gi.soundindex(va("*pain%i_%i.wav", l, r)), 1, ATTN_NORM, 0); } /* the total alpha of the blend is always proportional to count */ if (client->damage_alpha < 0) { client->damage_alpha = 0; } client->damage_alpha += count * 0.01; if (client->damage_alpha < 0.2) { client->damage_alpha = 0.2; } if (client->damage_alpha > 0.6) { client->damage_alpha = 0.6; /* don't go too saturated */ } /* the color of the blend will vary based on how much was absorbed by different armors */ VectorClear(v); if (client->damage_parmor) { VectorMA(v, (float)client->damage_parmor / realcount, power_color, v); } if (client->damage_armor) { VectorMA(v, (float)client->damage_armor / realcount, acolor, v); } if (client->damage_blood) { VectorMA(v, (float)client->damage_blood / realcount, bcolor, v); } VectorCopy(v, client->damage_blend); /* calculate view angle kicks */ kick = abs(client->damage_knockback); if (kick && (player->health > 0)) /* kick of 0 means no view adjust at all */ { kick = kick * 100 / player->health; if (kick < count * 0.5) { kick = count * 0.5; } if (kick > 50) { kick = 50; } VectorSubtract(client->damage_from, player->s.origin, v); VectorNormalize(v); side = DotProduct(v, right); client->v_dmg_roll = kick * side * 0.3; side = -DotProduct(v, forward); client->v_dmg_pitch = kick * side * 0.3; client->v_dmg_time = level.time + DAMAGE_TIME; } /* clear totals */ client->damage_blood = 0; client->damage_armor = 0; client->damage_parmor = 0; client->damage_knockback = 0; } /* * fall from 128: 400 = 160000 * fall from 256: 580 = 336400 * fall from 384: 720 = 518400 * fall from 512: 800 = 640000 * fall from 640: 960 = * * damage = deltavelocity*deltavelocity * 0.0001 */ void SV_CalcViewOffset(edict_t *ent) { float *angles; float bob; float ratio; float delta; vec3_t v; /* base angles */ angles = ent->client->ps.kick_angles; /* if dead, fix the angle and don't add any kick */ if (ent->deadflag) { VectorClear(angles); ent->client->ps.viewangles[ROLL] = 40; ent->client->ps.viewangles[PITCH] = -15; ent->client->ps.viewangles[YAW] = ent->client->killer_yaw; } else { /* add angles based on weapon kick */ VectorCopy(ent->client->kick_angles, angles); /* add angles based on damage kick */ ratio = (ent->client->v_dmg_time - level.time) / DAMAGE_TIME; if (ratio < 0) { ratio = 0; ent->client->v_dmg_pitch = 0; ent->client->v_dmg_roll = 0; } angles[PITCH] += ratio * ent->client->v_dmg_pitch; angles[ROLL] += ratio * ent->client->v_dmg_roll; /* add pitch based on fall kick */ ratio = (ent->client->fall_time - level.time) / FALL_TIME; if (ratio < 0) { ratio = 0; } angles[PITCH] += ratio * ent->client->fall_value; /* add angles based on velocity */ delta = DotProduct(ent->velocity, forward); angles[PITCH] += delta * run_pitch->value; delta = DotProduct(ent->velocity, right); angles[ROLL] += delta * run_roll->value; /* add angles based on bob */ delta = bobfracsin * bob_pitch->value * xyspeed; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { delta *= 6; /* crouching */ } angles[PITCH] += delta; delta = bobfracsin * bob_roll->value * xyspeed; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { delta *= 6; /* crouching */ } if (bobcycle & 1) { delta = -delta; } angles[ROLL] += delta; } /* base origin */ VectorClear(v); /* add view height */ v[2] += ent->viewheight; /* add fall height */ ratio = (ent->client->fall_time - level.time) / FALL_TIME; if (ratio < 0) { ratio = 0; } v[2] -= ratio * ent->client->fall_value * 0.4; /* add bob height */ bob = bobfracsin * xyspeed * bob_up->value; if (bob > 6) { bob = 6; } v[2] += bob; /* add kick offset */ VectorAdd(v, ent->client->kick_origin, v); /* absolutely bound offsets so the view can never be outside the player box */ if (v[0] < -14) { v[0] = -14; } else if (v[0] > 14) { v[0] = 14; } if (v[1] < -14) { v[1] = -14; } else if (v[1] > 14) { v[1] = 14; } if (v[2] < -22) { v[2] = -22; } else if (v[2] > 30) { v[2] = 30; } VectorCopy(v, ent->client->ps.viewoffset); } void SV_CalcGunOffset(edict_t *ent) { int i; float delta; if (!ent) { return; } /* gun angles from bobbing */ ent->client->ps.gunangles[ROLL] = xyspeed * bobfracsin * 0.005; ent->client->ps.gunangles[YAW] = xyspeed * bobfracsin * 0.01; if (bobcycle & 1) { ent->client->ps.gunangles[ROLL] = -ent->client->ps.gunangles[ROLL]; ent->client->ps.gunangles[YAW] = -ent->client->ps.gunangles[YAW]; } ent->client->ps.gunangles[PITCH] = xyspeed * bobfracsin * 0.005; /* gun angles from delta movement */ for (i = 0; i < 3; i++) { delta = ent->client->oldviewangles[i] - ent->client->ps.viewangles[i]; if (delta > 180) { delta -= 360; } if (delta < -180) { delta += 360; } if (delta > 45) { delta = 45; } if (delta < -45) { delta = -45; } if (i == YAW) { ent->client->ps.gunangles[ROLL] += 0.1 * delta; } ent->client->ps.gunangles[i] += 0.2 * delta; } /* gun height */ VectorClear(ent->client->ps.gunoffset); /* gun_x / gun_y / gun_z are development tools */ for (i = 0; i < 3; i++) { ent->client->ps.gunoffset[i] += forward[i] * (gun_y->value); ent->client->ps.gunoffset[i] += right[i] * gun_x->value; ent->client->ps.gunoffset[i] += up[i] * (-gun_z->value); } } void SV_AddBlend(float r, float g, float b, float a, float *v_blend) { float a2, a3; if (!v_blend) { return; } if (a <= 0) { return; } a2 = v_blend[3] + (1 - v_blend[3]) * a; /* new total alpha */ a3 = v_blend[3] / a2; /* fraction of color from old */ v_blend[0] = v_blend[0] * a3 + r * (1 - a3); v_blend[1] = v_blend[1] * a3 + g * (1 - a3); v_blend[2] = v_blend[2] * a3 + b * (1 - a3); v_blend[3] = a2; } void SV_CalcBlend(edict_t *ent) { int contents; vec3_t vieworg; int remaining; if (!ent) { return; } ent->client->ps.blend[0] = ent->client->ps.blend[1] = ent->client->ps.blend[2] = ent->client->ps.blend[3] = 0; /* add for contents */ VectorAdd(ent->s.origin, ent->client->ps.viewoffset, vieworg); contents = gi.pointcontents(vieworg); if (contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) { ent->client->ps.rdflags |= RDF_UNDERWATER; } else { ent->client->ps.rdflags &= ~RDF_UNDERWATER; } if (contents & (CONTENTS_SOLID | CONTENTS_LAVA)) { SV_AddBlend(1.0, 0.3, 0.0, 0.6, ent->client->ps.blend); } else if (contents & CONTENTS_SLIME) { SV_AddBlend(0.0, 0.1, 0.05, 0.6, ent->client->ps.blend); } else if (contents & CONTENTS_WATER) { SV_AddBlend(0.5, 0.3, 0.2, 0.4, ent->client->ps.blend); } /* add for powerups */ if (ent->client->quad_framenum > level.framenum) { remaining = ent->client->quad_framenum - level.framenum; if (remaining == 30) /* beginning to fade */ { gi.sound(ent, CHAN_ITEM, gi.soundindex( "items/damage2.wav"), 1, ATTN_NORM, 0); } if ((remaining > 30) || (remaining & 4)) { SV_AddBlend(0, 0, 1, 0.08, ent->client->ps.blend); } } else if (ent->client->invincible_framenum > level.framenum) { remaining = ent->client->invincible_framenum - level.framenum; if (remaining == 30) /* beginning to fade */ { gi.sound(ent, CHAN_ITEM, gi.soundindex( "items/protect2.wav"), 1, ATTN_NORM, 0); } if ((remaining > 30) || (remaining & 4)) { SV_AddBlend(1, 1, 0, 0.08, ent->client->ps.blend); } } else if (ent->client->enviro_framenum > level.framenum) { remaining = ent->client->enviro_framenum - level.framenum; if (remaining == 30) /* beginning to fade */ { gi.sound(ent, CHAN_ITEM, gi.soundindex( "items/airout.wav"), 1, ATTN_NORM, 0); } if ((remaining > 30) || (remaining & 4)) { SV_AddBlend(0, 1, 0, 0.08, ent->client->ps.blend); } } else if (ent->client->breather_framenum > level.framenum) { remaining = ent->client->breather_framenum - level.framenum; if (remaining == 30) /* beginning to fade */ { gi.sound(ent, CHAN_ITEM, gi.soundindex( "items/airout.wav"), 1, ATTN_NORM, 0); } if ((remaining > 30) || (remaining & 4)) { SV_AddBlend(0.4, 1, 0.4, 0.04, ent->client->ps.blend); } } /* add for damage */ if (ent->client->damage_alpha > 0) { SV_AddBlend(ent->client->damage_blend[0], ent->client->damage_blend[1], ent->client->damage_blend[2], ent->client->damage_alpha, ent->client->ps.blend); } if (ent->client->bonus_alpha > 0) { SV_AddBlend(0.85, 0.7, 0.3, ent->client->bonus_alpha, ent->client->ps.blend); } /* drop the damage value */ ent->client->damage_alpha -= 0.06; if (ent->client->damage_alpha < 0) { ent->client->damage_alpha = 0; } /* drop the bonus value */ ent->client->bonus_alpha -= 0.1; if (ent->client->bonus_alpha < 0) { ent->client->bonus_alpha = 0; } } void P_FallingDamage(edict_t *ent) { float delta; int damage; vec3_t dir; if (!ent) { return; } if (ent->s.modelindex != 255) { return; /* not in the player model */ } if (ent->movetype == MOVETYPE_NOCLIP) { return; } if ((ent->client->oldvelocity[2] < 0) && (ent->velocity[2] > ent->client->oldvelocity[2]) && (!ent->groundentity)) { delta = ent->client->oldvelocity[2]; } else { if (!ent->groundentity) { return; } delta = ent->velocity[2] - ent->client->oldvelocity[2]; } delta = delta * delta * 0.0001; /* never take falling damage if completely underwater */ if (ent->waterlevel == 3) { return; } if (ent->waterlevel == 2) { delta *= 0.25; } if (ent->waterlevel == 1) { delta *= 0.5; } if (delta < 1) { return; } if (delta < 15) { ent->s.event = EV_FOOTSTEP; return; } ent->client->fall_value = delta * 0.5; if (ent->client->fall_value > 40) { ent->client->fall_value = 40; } ent->client->fall_time = level.time + FALL_TIME; if (delta > 30) { if (ent->health > 0) { if (delta >= 55) { ent->s.event = EV_FALLFAR; } else { ent->s.event = EV_FALL; } } ent->pain_debounce_time = level.time; /* no normal pain sound */ damage = (delta - 30) / 2; if (damage < 1) { damage = 1; } VectorSet(dir, 0, 0, 1); if (!deathmatch->value || !((int)dmflags->value & DF_NO_FALLING)) { T_Damage(ent, world, world, dir, ent->s.origin, vec3_origin, damage, 0, 0, MOD_FALLING); } } else { ent->s.event = EV_FALLSHORT; return; } } void P_WorldEffects(void) { qboolean breather; qboolean envirosuit; int waterlevel, old_waterlevel; if (current_player->movetype == MOVETYPE_NOCLIP) { current_player->air_finished = level.time + 12; /* don't need air */ return; } waterlevel = current_player->waterlevel; old_waterlevel = current_client->old_waterlevel; current_client->old_waterlevel = waterlevel; breather = current_client->breather_framenum > level.framenum; envirosuit = current_client->enviro_framenum > level.framenum; /* if just entered a water volume, play a sound */ if (!old_waterlevel && waterlevel) { PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); if (current_player->watertype & CONTENTS_LAVA) { gi.sound(current_player, CHAN_BODY, gi.soundindex("player/lava_in.wav"), 1, ATTN_NORM, 0); } else if (current_player->watertype & CONTENTS_SLIME) { gi.sound(current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); } else if (current_player->watertype & CONTENTS_WATER) { gi.sound(current_player, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0); } current_player->flags |= FL_INWATER; /* clear damage_debounce, so the pain sound will play immediately */ current_player->damage_debounce_time = level.time - 1; } /* if just completely exited a water volume, play a sound */ if (old_waterlevel && !waterlevel) { PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); gi.sound(current_player, CHAN_BODY, gi.soundindex( "player/watr_out.wav"), 1, ATTN_NORM, 0); current_player->flags &= ~FL_INWATER; } /* check for head just going under water */ if ((old_waterlevel != 3) && (waterlevel == 3)) { gi.sound(current_player, CHAN_BODY, gi.soundindex( "player/watr_un.wav"), 1, ATTN_NORM, 0); } /* check for head just coming out of water */ if ((old_waterlevel == 3) && (waterlevel != 3)) { if (current_player->air_finished < level.time) { /* gasp for air */ gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/gasp1.wav"), 1, ATTN_NORM, 0); PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); } else if (current_player->air_finished < level.time + 11) { /* just break surface */ gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/gasp2.wav"), 1, ATTN_NORM, 0); } } /* check for drowning */ if (waterlevel == 3) { /* breather or envirosuit give air */ if (breather || envirosuit) { current_player->air_finished = level.time + 10; if (((int)(current_client->breather_framenum - level.framenum) % 25) == 0) { if (!current_client->breather_sound) { gi.sound(current_player, CHAN_AUTO, gi.soundindex("player/u_breath1.wav"), 1, ATTN_NORM, 0); } else { gi.sound(current_player, CHAN_AUTO, gi.soundindex("player/u_breath2.wav"), 1, ATTN_NORM, 0); } current_client->breather_sound ^= 1; PlayerNoise(current_player, current_player->s.origin, PNOISE_SELF); } } /* if out of air, start drowning */ if (current_player->air_finished < level.time) { /* drown! */ if ((current_player->client->next_drown_time < level.time) && (current_player->health > 0)) { current_player->client->next_drown_time = level.time + 1; /* take more damage the longer underwater */ current_player->dmg += 2; if (current_player->dmg > 15) { current_player->dmg = 15; } /* play a gurp sound instead of a normal pain sound */ if (current_player->health <= current_player->dmg) { gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/drown1.wav"), 1, ATTN_NORM, 0); } else if (randk() & 1) { gi.sound(current_player, CHAN_VOICE, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0); } else { gi.sound(current_player, CHAN_VOICE, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0); } current_player->pain_debounce_time = level.time; T_Damage(current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, current_player->dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); } } } else { current_player->air_finished = level.time + 12; current_player->dmg = 2; } /* check for sizzle damage */ if (waterlevel && (current_player->watertype & (CONTENTS_LAVA | CONTENTS_SLIME))) { if (current_player->watertype & CONTENTS_LAVA) { if ((current_player->health > 0) && (current_player->pain_debounce_time <= level.time) && (current_client->invincible_framenum < level.framenum)) { if (randk() & 1) { gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/burn1.wav"), 1, ATTN_NORM, 0); } else { gi.sound(current_player, CHAN_VOICE, gi.soundindex("player/burn2.wav"), 1, ATTN_NORM, 0); } current_player->pain_debounce_time = level.time + 1; } if (envirosuit) /* take 1/3 damage with envirosuit */ { T_Damage(current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1 * waterlevel, 0, 0, MOD_LAVA); } else { T_Damage(current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 3 * waterlevel, 0, 0, MOD_LAVA); } } if (current_player->watertype & CONTENTS_SLIME) { if (!envirosuit) { /* no damage from slime with envirosuit */ T_Damage(current_player, world, world, vec3_origin, current_player->s.origin, vec3_origin, 1 * waterlevel, 0, 0, MOD_SLIME); } } } } void G_SetClientEffects(edict_t *ent) { int pa_type; int remaining; if (!ent) { return; } ent->s.effects = 0; ent->s.renderfx = RF_IR_VISIBLE; if ((ent->health <= 0) || level.intermissiontime) { return; } if (ent->powerarmor_time > level.time) { pa_type = PowerArmorType(ent); if (pa_type == POWER_ARMOR_SCREEN) { ent->s.effects |= EF_POWERSCREEN; } else if (pa_type == POWER_ARMOR_SHIELD) { ent->s.effects |= EF_COLOR_SHELL; ent->s.renderfx |= RF_SHELL_GREEN; } } if (ent->client->quad_framenum > level.framenum) { remaining = ent->client->quad_framenum - level.framenum; if ((remaining > 30) || (remaining & 4)) { ent->s.effects |= EF_QUAD; } } if (ent->client->invincible_framenum > level.framenum) { remaining = ent->client->invincible_framenum - level.framenum; if ((remaining > 30) || (remaining & 4)) { ent->s.effects |= EF_PENT; } } /* show cheaters */ if (ent->flags & FL_GODMODE) { ent->s.effects |= EF_COLOR_SHELL; ent->s.renderfx |= (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE); } } void G_SetClientEvent(edict_t *ent) { if (!ent) { return; } if (ent->s.event) { return; } if (ent->groundentity && (xyspeed > 225)) { if ((int)(current_client->bobtime + bobmove) != bobcycle) { ent->s.event = EV_FOOTSTEP; } } } void G_SetClientSound(edict_t *ent) { char *weap; if (!ent) { return; } if (ent->client->pers.game_helpchanged != game.helpchanged) { ent->client->pers.game_helpchanged = game.helpchanged; ent->client->pers.helpchanged = 1; } /* help beep (no more than three times) */ if (ent->client->pers.helpchanged && (ent->client->pers.helpchanged <= 3) && !(level.framenum & 63)) { ent->client->pers.helpchanged++; gi.sound(ent, CHAN_VOICE, gi.soundindex( "misc/pc_up.wav"), 1, ATTN_STATIC, 0); } if (ent->client->pers.weapon) { weap = ent->client->pers.weapon->classname; } else { weap = ""; } if (ent->waterlevel && (ent->watertype & (CONTENTS_LAVA | CONTENTS_SLIME))) { ent->s.sound = snd_fry; } else if (strcmp(weap, "weapon_railgun") == 0) { ent->s.sound = gi.soundindex("weapons/rg_hum.wav"); } else if (strcmp(weap, "weapon_bfg") == 0) { ent->s.sound = gi.soundindex("weapons/bfg_hum.wav"); } else if (ent->client->weapon_sound) { ent->s.sound = ent->client->weapon_sound; } else { ent->s.sound = 0; } } void G_SetClientFrame(edict_t *ent) { gclient_t *client; qboolean duck, run; if (!ent) { return; } if (ent->s.modelindex != 255) { return; /* not in the player model */ } client = ent->client; if (client->ps.pmove.pm_flags & PMF_DUCKED) { duck = true; } else { duck = false; } if (xyspeed) { run = true; } else { run = false; } /* check for stand/duck and stop/go transitions */ if ((duck != client->anim_duck) && (client->anim_priority < ANIM_DEATH)) { goto newanim; } if ((run != client->anim_run) && (client->anim_priority == ANIM_BASIC)) { goto newanim; } if (!ent->groundentity && (client->anim_priority <= ANIM_WAVE)) { goto newanim; } if (client->anim_priority == ANIM_REVERSE) { if (ent->s.frame > client->anim_end) { ent->s.frame--; return; } } else if (ent->s.frame < client->anim_end) { /* continue an animation */ ent->s.frame++; return; } if (client->anim_priority == ANIM_DEATH) { return; /* stay there */ } if (client->anim_priority == ANIM_JUMP) { if (!ent->groundentity) { return; /* stay there */ } ent->client->anim_priority = ANIM_WAVE; ent->s.frame = FRAME_jump3; ent->client->anim_end = FRAME_jump6; return; } newanim: /* return to either a running or standing frame */ client->anim_priority = ANIM_BASIC; client->anim_duck = duck; client->anim_run = run; if (!ent->groundentity) { client->anim_priority = ANIM_JUMP; if (ent->s.frame != FRAME_jump2) { ent->s.frame = FRAME_jump1; } client->anim_end = FRAME_jump2; } else if (run) { /* running */ if (duck) { ent->s.frame = FRAME_crwalk1; client->anim_end = FRAME_crwalk6; } else { ent->s.frame = FRAME_run1; client->anim_end = FRAME_run6; } } else { /* standing */ if (duck) { ent->s.frame = FRAME_crstnd01; client->anim_end = FRAME_crstnd19; } else { ent->s.frame = FRAME_stand01; client->anim_end = FRAME_stand40; } } } /* * Called for each player at the end of * the server frame and right after spawning */ void ClientEndServerFrame(edict_t *ent) { float bobtime; int i; if (!ent) { return; } current_player = ent; current_client = ent->client; /* If the origin or velocity have changed since ClientThink(), update the pmove values. This will happen when the client is pushed by a bmodel or kicked by an explosion. If it wasn't updated here, the view position would lag a frame behind the body position when pushed -- "sinking into plats" */ for (i = 0; i < 3; i++) { current_client->ps.pmove.origin[i] = ent->s.origin[i] * 8.0; current_client->ps.pmove.velocity[i] = ent->velocity[i] * 8.0; } /* If the end of unit layout is displayed, don't give the player any normal movement attributes */ if (level.intermissiontime) { current_client->ps.blend[3] = 0; current_client->ps.fov = 90; G_SetStats(ent); return; } AngleVectors(ent->client->v_angle, forward, right, up); /* burn from lava, etc */ P_WorldEffects(); /* set model angles from view angles so other things in the world can tell which direction you are looking */ if (ent->client->v_angle[PITCH] > 180) { ent->s.angles[PITCH] = (-360 + ent->client->v_angle[PITCH]) / 3; } else { ent->s.angles[PITCH] = ent->client->v_angle[PITCH] / 3; } ent->s.angles[YAW] = ent->client->v_angle[YAW]; ent->s.angles[ROLL] = 0; ent->s.angles[ROLL] = SV_CalcRoll(ent->s.angles, ent->velocity) * 4; /* calculate speed and cycle to be used for all cyclic walking effects */ xyspeed = sqrt( ent->velocity[0] * ent->velocity[0] + ent->velocity[1] * ent->velocity[1]); if (xyspeed < 5) { bobmove = 0; current_client->bobtime = 0; /* start at beginning of cycle again */ } else if (ent->groundentity) { /* so bobbing only cycles when on ground */ if (xyspeed > 210) { bobmove = 0.25; } else if (xyspeed > 100) { bobmove = 0.125; } else { bobmove = 0.0625; } } bobtime = (current_client->bobtime += bobmove); if (current_client->ps.pmove.pm_flags & PMF_DUCKED) { bobtime *= 4; } bobcycle = (int)bobtime; bobfracsin = fabs(sin(bobtime * M_PI)); /* detect hitting the floor */ P_FallingDamage(ent); /* apply all the damage taken this frame */ P_DamageFeedback(ent); /* determine the view offsets */ SV_CalcViewOffset(ent); /* determine the gun offsets */ SV_CalcGunOffset(ent); /* determine the full screen color blend must be after viewoffset, so eye contents can be accurately determined */ SV_CalcBlend(ent); /* chase cam stuff */ if (ent->client->resp.spectator) { G_SetSpectatorStats(ent); } else { G_SetStats(ent); } G_CheckChaseStats(ent); G_SetClientEvent(ent); G_SetClientEffects(ent); G_SetClientSound(ent); G_SetClientFrame(ent); VectorCopy(ent->velocity, ent->client->oldvelocity); VectorCopy(ent->client->ps.viewangles, ent->client->oldviewangles); /* clear weapon kicks */ VectorClear(ent->client->kick_origin); VectorClear(ent->client->kick_angles); if (!(level.framenum & 31)) { /* if the scoreboard is up, update it */ if (ent->client->showscores) { DeathmatchScoreboardMessage(ent, ent->enemy); gi.unicast(ent, false); } /* if the help computer is up, update it */ if (ent->client->showhelp) { ent->client->pers.helpchanged = 0; HelpComputerMessage(ent); gi.unicast(ent, false); } } /* if the inventory is up, update it */ if (ent->client->showinventory) { InventoryMessage(ent); gi.unicast(ent, false); } } yquake2-QUAKE2_5_32/src/game/player/trail.c0000644000175000017500000000576012615162034021102 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 player trail, used by monsters to locate the player. * * ======================================================================= */ #include "../header/local.h" /* * This is a circular list containing the a list of points of where * the player has been recently. It is used by monsters for pursuit. * * .origin the spot * .owner forward link * .aiment backward link */ #define TRAIL_LENGTH 8 #define NEXT(n) (((n) + 1) & (TRAIL_LENGTH - 1)) #define PREV(n) (((n) - 1) & (TRAIL_LENGTH - 1)) edict_t *trail[TRAIL_LENGTH]; int trail_head; qboolean trail_active = false; void PlayerTrail_Init(void) { int n; if (deathmatch->value) { return; } for (n = 0; n < TRAIL_LENGTH; n++) { trail[n] = G_Spawn(); trail[n]->classname = "player_trail"; } trail_head = 0; trail_active = true; } void PlayerTrail_Add(vec3_t spot) { vec3_t temp; if (!trail_active) { return; } VectorCopy(spot, trail[trail_head]->s.origin); trail[trail_head]->timestamp = level.time; VectorSubtract(spot, trail[PREV(trail_head)]->s.origin, temp); trail[trail_head]->s.angles[1] = vectoyaw(temp); trail_head = NEXT(trail_head); } void PlayerTrail_New(vec3_t spot) { if (!trail_active) { return; } PlayerTrail_Init(); PlayerTrail_Add(spot); } edict_t * PlayerTrail_PickFirst(edict_t *self) { int marker; int n; if (!self) { return NULL; } if (!trail_active) { return NULL; } for (marker = trail_head, n = TRAIL_LENGTH; n; n--) { if (trail[marker]->timestamp <= self->monsterinfo.trail_time) { marker = NEXT(marker); } else { break; } } if (visible(self, trail[marker])) { return trail[marker]; } if (visible(self, trail[PREV(marker)])) { return trail[PREV(marker)]; } return trail[marker]; } edict_t * PlayerTrail_PickNext(edict_t *self) { int marker; int n; if (!self) { return NULL; } if (!trail_active) { return NULL; } for (marker = trail_head, n = TRAIL_LENGTH; n; n--) { if (trail[marker]->timestamp <= self->monsterinfo.trail_time) { marker = NEXT(marker); } else { break; } } return trail[marker]; } edict_t * PlayerTrail_LastSpot(void) { return trail[PREV(trail_head)]; } yquake2-QUAKE2_5_32/src/game/player/hud.c0000644000175000017500000003151212615162034020541 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * HUD, deathmatch scoreboard, help computer and intermission stuff. * * ======================================================================= */ #include "../header/local.h" void MoveClientToIntermission(edict_t *ent) { if (!ent) { return; } if (deathmatch->value || coop->value) { ent->client->showscores = true; } VectorCopy(level.intermission_origin, ent->s.origin); ent->client->ps.pmove.origin[0] = level.intermission_origin[0] * 8; ent->client->ps.pmove.origin[1] = level.intermission_origin[1] * 8; ent->client->ps.pmove.origin[2] = level.intermission_origin[2] * 8; VectorCopy(level.intermission_angle, ent->client->ps.viewangles); ent->client->ps.pmove.pm_type = PM_FREEZE; ent->client->ps.gunindex = 0; ent->client->ps.blend[3] = 0; ent->client->ps.rdflags &= ~RDF_UNDERWATER; /* clean up powerup info */ ent->client->quad_framenum = 0; ent->client->invincible_framenum = 0; ent->client->breather_framenum = 0; ent->client->enviro_framenum = 0; ent->client->grenade_blew_up = false; ent->client->grenade_time = 0; ent->viewheight = 0; ent->s.modelindex = 0; ent->s.modelindex2 = 0; ent->s.modelindex3 = 0; ent->s.modelindex = 0; ent->s.effects = 0; ent->s.sound = 0; ent->solid = SOLID_NOT; /* add the layout */ if (deathmatch->value || coop->value) { DeathmatchScoreboardMessage(ent, NULL); gi.unicast(ent, true); } } void BeginIntermission(edict_t *targ) { int i, n; edict_t *ent, *client; if (!targ) { return; } if (level.intermissiontime) { return; /* already activated */ } game.autosaved = false; /* respawn any dead clients */ for (i = 0; i < maxclients->value; i++) { client = g_edicts + 1 + i; if (!client->inuse) { continue; } if (client->health <= 0) { respawn(client); } } level.intermissiontime = level.time; level.changemap = targ->map; if (strstr(level.changemap, "*")) { if (coop->value) { for (i = 0; i < maxclients->value; i++) { client = g_edicts + 1 + i; if (!client->inuse) { continue; } /* strip players of all keys between units */ for (n = 0; n < MAX_ITEMS; n++) { if (itemlist[n].flags & IT_KEY) { client->client->pers.inventory[n] = 0; } } } } } else { if (!deathmatch->value) { level.exitintermission = 1; /* go immediately to the next level */ return; } } level.exitintermission = 0; /* find an intermission spot */ ent = G_Find(NULL, FOFS(classname), "info_player_intermission"); if (!ent) { /* the map creator forgot to put in an intermission point... */ ent = G_Find(NULL, FOFS(classname), "info_player_start"); if (!ent) { ent = G_Find(NULL, FOFS(classname), "info_player_deathmatch"); } } else { /* chose one of four spots */ i = randk() & 3; while (i--) { ent = G_Find(ent, FOFS(classname), "info_player_intermission"); if (!ent) /* wrap around the list */ { ent = G_Find(ent, FOFS(classname), "info_player_intermission"); } } } VectorCopy(ent->s.origin, level.intermission_origin); VectorCopy(ent->s.angles, level.intermission_angle); /* In fact1 the intermission collides with an area portal, resulting in clutterings */ if (!Q_stricmp(level.mapname, "fact1")) { level.intermission_origin[0] = 1037.0; level.intermission_origin[1] = 1100.0; level.intermission_origin[2] = 222.0; } /* move all clients to the intermission point */ for (i = 0; i < maxclients->value; i++) { client = g_edicts + 1 + i; if (!client->inuse) { continue; } MoveClientToIntermission(client); } } void DeathmatchScoreboardMessage(edict_t *ent, edict_t *killer) { char entry[1024]; char string[1400]; int stringlength; int i, j, k; int sorted[MAX_CLIENTS]; int sortedscores[MAX_CLIENTS]; int score, total; int x, y; gclient_t *cl; edict_t *cl_ent; char *tag; if (!ent) /* killer can be NULL */ { return; } /* sort the clients by score */ total = 0; for (i = 0; i < game.maxclients; i++) { cl_ent = g_edicts + 1 + i; if (!cl_ent->inuse || game.clients[i].resp.spectator) { continue; } score = game.clients[i].resp.score; for (j = 0; j < total; j++) { if (score > sortedscores[j]) { break; } } for (k = total; k > j; k--) { sorted[k] = sorted[k - 1]; sortedscores[k] = sortedscores[k - 1]; } sorted[j] = i; sortedscores[j] = score; total++; } /* print level name and exit rules */ string[0] = 0; stringlength = strlen(string); /* add the clients in sorted order */ if (total > 12) { total = 12; } for (i = 0; i < total; i++) { cl = &game.clients[sorted[i]]; cl_ent = g_edicts + 1 + sorted[i]; x = (i >= 6) ? 160 : 0; y = 32 + 32 * (i % 6); /* add a dogtag */ if (cl_ent == ent) { tag = "tag1"; } else if (cl_ent == killer) { tag = "tag2"; } else { tag = NULL; } if (tag) { Com_sprintf(entry, sizeof(entry), "xv %i yv %i picn %s ", x + 32, y, tag); j = strlen(entry); if (stringlength + j > 1024) { break; } strcpy(string + stringlength, entry); stringlength += j; } /* send the layout */ Com_sprintf(entry, sizeof(entry), "client %i %i %i %i %i %i ", x, y, sorted[i], cl->resp.score, cl->ping, (level.framenum - cl->resp.enterframe) / 600); j = strlen(entry); if (stringlength + j > 1024) { break; } strcpy(string + stringlength, entry); stringlength += j; } gi.WriteByte(svc_layout); gi.WriteString(string); } void HelpComputerMessage(edict_t *ent) { char string[1024]; char *sk; if (!ent) { return; } if (skill->value == 0) { sk = "easy"; } else if (skill->value == 1) { sk = "medium"; } else if (skill->value == 2) { sk = "hard"; } else { sk = "hard+"; } /* send the layout */ Com_sprintf(string, sizeof(string), "xv 32 yv 8 picn help " /* background */ "xv 202 yv 12 string2 \"%s\" " /* skill */ "xv 0 yv 24 cstring2 \"%s\" " /* level name */ "xv 0 yv 54 cstring2 \"%s\" " /* help 1 */ "xv 0 yv 110 cstring2 \"%s\" " /* help 2 */ "xv 50 yv 164 string2 \" kills goals secrets\" " "xv 50 yv 172 string2 \"%3i/%3i %i/%i %i/%i\" ", sk, level.level_name, game.helpmessage1, game.helpmessage2, level.killed_monsters, level.total_monsters, level.found_goals, level.total_goals, level.found_secrets, level.total_secrets); gi.WriteByte(svc_layout); gi.WriteString(string); } void InventoryMessage(edict_t *ent) { int i; if (!ent) { return; } gi.WriteByte(svc_inventory); for (i = 0; i < MAX_ITEMS; i++) { gi.WriteShort(ent->client->pers.inventory[i]); } } /* ======================================================================= */ void G_SetStats(edict_t *ent) { gitem_t *item; int index, cells = 0; int power_armor_type; if (!ent) { return; } /* health */ ent->client->ps.stats[STAT_HEALTH_ICON] = level.pic_health; ent->client->ps.stats[STAT_HEALTH] = ent->health; /* ammo */ if (!ent->client->ammo_index) { ent->client->ps.stats[STAT_AMMO_ICON] = 0; ent->client->ps.stats[STAT_AMMO] = 0; } else { item = &itemlist[ent->client->ammo_index]; ent->client->ps.stats[STAT_AMMO_ICON] = gi.imageindex(item->icon); ent->client->ps.stats[STAT_AMMO] = ent->client->pers.inventory[ent->client->ammo_index]; } /* armor */ power_armor_type = PowerArmorType(ent); if (power_armor_type) { cells = ent->client->pers.inventory[ITEM_INDEX(FindItem("cells"))]; if (cells == 0) { /* ran out of cells for power armor */ ent->flags &= ~FL_POWER_ARMOR; gi.sound(ent, CHAN_ITEM, gi.soundindex( "misc/power2.wav"), 1, ATTN_NORM, 0); power_armor_type = 0; } } index = ArmorIndex(ent); if (power_armor_type && (!index || (level.framenum & 8))) { /* flash between power armor and other armor icon */ ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex("i_powershield"); ent->client->ps.stats[STAT_ARMOR] = cells; } else if (index) { item = GetItemByIndex(index); ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex(item->icon); ent->client->ps.stats[STAT_ARMOR] = ent->client->pers.inventory[index]; } else { ent->client->ps.stats[STAT_ARMOR_ICON] = 0; ent->client->ps.stats[STAT_ARMOR] = 0; } /* pickup message */ if (level.time > ent->client->pickup_msg_time) { ent->client->ps.stats[STAT_PICKUP_ICON] = 0; ent->client->ps.stats[STAT_PICKUP_STRING] = 0; } /* timers */ if (ent->client->quad_framenum > level.framenum) { ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex("p_quad"); ent->client->ps.stats[STAT_TIMER] = (ent->client->quad_framenum - level.framenum) / 10; } else if (ent->client->invincible_framenum > level.framenum) { ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex( "p_invulnerability"); ent->client->ps.stats[STAT_TIMER] = (ent->client->invincible_framenum - level.framenum) / 10; } else if (ent->client->enviro_framenum > level.framenum) { ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex("p_envirosuit"); ent->client->ps.stats[STAT_TIMER] = (ent->client->enviro_framenum - level.framenum) / 10; } else if (ent->client->breather_framenum > level.framenum) { ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex("p_rebreather"); ent->client->ps.stats[STAT_TIMER] = (ent->client->breather_framenum - level.framenum) / 10; } else { ent->client->ps.stats[STAT_TIMER_ICON] = 0; ent->client->ps.stats[STAT_TIMER] = 0; } /* selected item */ if (ent->client->pers.selected_item == -1) { ent->client->ps.stats[STAT_SELECTED_ICON] = 0; } else { ent->client->ps.stats[STAT_SELECTED_ICON] = gi.imageindex(itemlist[ent->client->pers.selected_item].icon); } ent->client->ps.stats[STAT_SELECTED_ITEM] = ent->client->pers.selected_item; /* layouts */ ent->client->ps.stats[STAT_LAYOUTS] = 0; if (deathmatch->value) { if ((ent->client->pers.health <= 0) || level.intermissiontime || ent->client->showscores) { ent->client->ps.stats[STAT_LAYOUTS] |= 1; } if (ent->client->showinventory && (ent->client->pers.health > 0)) { ent->client->ps.stats[STAT_LAYOUTS] |= 2; } } else { if (ent->client->showscores || ent->client->showhelp) { ent->client->ps.stats[STAT_LAYOUTS] |= 1; } if (ent->client->showinventory && (ent->client->pers.health > 0)) { ent->client->ps.stats[STAT_LAYOUTS] |= 2; } } /* frags */ ent->client->ps.stats[STAT_FRAGS] = ent->client->resp.score; /* help icon / current weapon if not shown */ if (ent->client->pers.helpchanged && (level.framenum & 8)) { ent->client->ps.stats[STAT_HELPICON] = gi.imageindex("i_help"); } else if (((ent->client->pers.hand == CENTER_HANDED) || (ent->client->ps.fov > 91)) && ent->client->pers.weapon) { cvar_t *gun; gun = gi.cvar("cl_gun", "2", 0); if (gun->value != 2) { ent->client->ps.stats[STAT_HELPICON] = gi.imageindex( ent->client->pers.weapon->icon); } else { ent->client->ps.stats[STAT_HELPICON] = 0; } } else { ent->client->ps.stats[STAT_HELPICON] = 0; } ent->client->ps.stats[STAT_SPECTATOR] = 0; } void G_CheckChaseStats(edict_t *ent) { int i; gclient_t *cl; if (!ent) { return; } for (i = 1; i <= maxclients->value; i++) { cl = g_edicts[i].client; if (!g_edicts[i].inuse || (cl->chase_target != ent)) { continue; } memcpy(cl->ps.stats, ent->client->ps.stats, sizeof(cl->ps.stats)); G_SetSpectatorStats(g_edicts + i); } } void G_SetSpectatorStats(edict_t *ent) { if (!ent) { return; } gclient_t *cl = ent->client; if (!cl->chase_target) { G_SetStats(ent); } cl->ps.stats[STAT_SPECTATOR] = 1; /* layouts are independant in spectator */ cl->ps.stats[STAT_LAYOUTS] = 0; if ((cl->pers.health <= 0) || level.intermissiontime || cl->showscores) { cl->ps.stats[STAT_LAYOUTS] |= 1; } if (cl->showinventory && (cl->pers.health > 0)) { cl->ps.stats[STAT_LAYOUTS] |= 2; } if (cl->chase_target && cl->chase_target->inuse) { cl->ps.stats[STAT_CHASE] = CS_PLAYERSKINS + (cl->chase_target - g_edicts) - 1; } else { cl->ps.stats[STAT_CHASE] = 0; } } yquake2-QUAKE2_5_32/src/game/g_phys.c0000644000175000017500000005570212615162034017765 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Quake IIs legendary physic engine. * * ======================================================================= */ #include "header/local.h" #define STOP_EPSILON 0.1 #define MAX_CLIP_PLANES 5 #define STOPSPEED 100 #define FRICTION 6 #define WATERFRICTION 1 /* * 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. */ edict_t * SV_TestEntityPosition(edict_t *ent) { if (!ent) { return NULL; } trace_t trace; int mask; if (ent->clipmask) { mask = ent->clipmask; } else { mask = MASK_SOLID; } trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, ent->s.origin, ent, mask); if (trace.startsolid) { if ((ent->svflags & SVF_DEADMONSTER) && (trace.ent->client || (trace.ent->svflags & SVF_MONSTER))) { return NULL; } return g_edicts; } return NULL; } void SV_CheckVelocity(edict_t *ent) { if (!ent) { return; } if (VectorLength(ent->velocity) > sv_maxvelocity->value) { VectorNormalize(ent->velocity); VectorScale(ent->velocity, sv_maxvelocity->value, ent->velocity); } } /* * Runs thinking code for * this frame if necessary */ qboolean SV_RunThink(edict_t *ent) { float thinktime; if (!ent) { return false; } thinktime = ent->nextthink; if (thinktime <= 0) { return true; } if (thinktime > level.time + 0.001) { return true; } ent->nextthink = 0; if (!ent->think) { gi.error("NULL ent->think"); } ent->think(ent); return false; } /* * Two entities have touched, so * run their touch functions */ void SV_Impact(edict_t *e1, trace_t *trace) { edict_t *e2; if (!e1 || !trace) { return; } e2 = trace->ent; if (e1->touch && (e1->solid != SOLID_NOT)) { e1->touch(e1, e2, &trace->plane, trace->surface); } if (e2->touch && (e2->solid != SOLID_NOT)) { e2->touch(e2, e1, NULL, NULL); } } /* * Slide off of the impacting object * returns the blocked flags (1 = floor, * 2 = step / wall) */ 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; } /* * 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 */ int SV_FlyMove(edict_t *ent, float time, int mask) { edict_t *hit; 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; if (!ent) { return 0; } numbumps = 4; blocked = 0; VectorCopy(ent->velocity, original_velocity); VectorCopy(ent->velocity, primal_velocity); numplanes = 0; time_left = time; ent->groundentity = NULL; for (bumpcount = 0; bumpcount < numbumps; bumpcount++) { for (i = 0; i < 3; i++) { end[i] = ent->s.origin[i] + time_left * ent->velocity[i]; } trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, end, ent, mask); if (trace.allsolid) { /* entity is trapped in another solid */ VectorCopy(vec3_origin, ent->velocity); return 3; } if (trace.fraction > 0) { /* actually covered some distance */ VectorCopy(trace.endpos, ent->s.origin); VectorCopy(ent->velocity, original_velocity); numplanes = 0; } if (trace.fraction == 1) { break; /* moved the entire distance */ } hit = trace.ent; if (trace.plane.normal[2] > 0.7) { blocked |= 1; /* floor */ if (hit->solid == SOLID_BSP) { ent->groundentity = hit; ent->groundentity_linkcount = hit->linkcount; } } if (!trace.plane.normal[2]) { blocked |= 2; /* step */ } /* run the impact function */ SV_Impact(ent, &trace); if (!ent->inuse) { 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->velocity); return 3; } VectorCopy(trace.plane.normal, planes[numplanes]); numplanes++; /* modify original_velocity so it parallels all of the clip planes */ for (i = 0; i < numplanes; i++) { ClipVelocity(original_velocity, planes[i], new_velocity, 1); for (j = 0; j < numplanes; j++) { if ((j != i) && !VectorCompare(planes[i], planes[j])) { if (DotProduct(new_velocity, planes[j]) < 0) { break; /* not ok */ } } } if (j == numplanes) { break; } } if (i != numplanes) { /* go along this plane */ VectorCopy(new_velocity, ent->velocity); } else { /* go along the crease */ if (numplanes != 2) { VectorCopy(vec3_origin, ent->velocity); return 7; } CrossProduct(planes[0], planes[1], dir); d = DotProduct(dir, ent->velocity); VectorScale(dir, d, ent->velocity); } /* if original velocity is against the original velocity, stop dead to avoid tiny occilations in sloping corners */ if (DotProduct(ent->velocity, primal_velocity) <= 0) { VectorCopy(vec3_origin, ent->velocity); return blocked; } } return blocked; } void SV_AddGravity(edict_t *ent) { if (!ent) { return; } ent->velocity[2] -= ent->gravity * sv_gravity->value * FRAMETIME; } /* * Returns the actual bounding box of a bmodel. * This is a big improvement over what q2 normally * does with rotating bmodels - q2 sets absmin, * absmax to a cube that will completely contain * the bmodel at *any* rotation on *any* axis, whether * the bmodel can actually rotate to that angle or not. * This leads to a lot of false block tests in SV_Push * if another bmodel is in the vicinity. */ void RealBoundingBox(edict_t *ent, vec3_t mins, vec3_t maxs) { vec3_t forward, left, up, f1, l1, u1; vec3_t p[8]; int i, j, k, j2, k4; for (k = 0; k < 2; k++) { k4 = k * 4; if (k) { p[k4][2] = ent->maxs[2]; } else { p[k4][2] = ent->mins[2]; } p[k4 + 1][2] = p[k4][2]; p[k4 + 2][2] = p[k4][2]; p[k4 + 3][2] = p[k4][2]; for (j = 0; j < 2; j++) { j2 = j * 2; if (j) { p[j2 + k4][1] = ent->maxs[1]; } else { p[j2 + k4][1] = ent->mins[1]; } p[j2 + k4 + 1][1] = p[j2 + k4][1]; for (i = 0; i < 2; i++) { if (i) { p[i + j2 + k4][0] = ent->maxs[0]; } else { p[i + j2 + k4][0] = ent->mins[0]; } } } } AngleVectors(ent->s.angles, forward, left, up); for (i = 0; i < 8; i++) { VectorScale(forward, p[i][0], f1); VectorScale(left, -p[i][1], l1); VectorScale(up, p[i][2], u1); VectorAdd(ent->s.origin, f1, p[i]); VectorAdd(p[i], l1, p[i]); VectorAdd(p[i], u1, p[i]); } VectorCopy(p[0], mins); VectorCopy(p[0], maxs); for (i = 1; i < 8; i++) { if (mins[0] > p[i][0]) { mins[0] = p[i][0]; } if (mins[1] > p[i][1]) { mins[1] = p[i][1]; } if (mins[2] > p[i][2]) { mins[2] = p[i][2]; } if (maxs[0] < p[i][0]) { maxs[0] = p[i][0]; } if (maxs[1] < p[i][1]) { maxs[1] = p[i][1]; } if (maxs[2] < p[i][2]) { maxs[2] = p[i][2]; } } } /* ================================================================== */ /* PUSHMOVE */ /* * Does not change the entities velocity at all */ trace_t SV_PushEntity(edict_t *ent, vec3_t push) { trace_t trace; vec3_t start; vec3_t end; int mask; VectorCopy(ent->s.origin, start); VectorAdd(start, push, end); retry: if (ent->clipmask) { mask = ent->clipmask; } else { mask = MASK_SOLID; } trace = gi.trace(start, ent->mins, ent->maxs, end, ent, mask); if (trace.startsolid || trace.allsolid) { mask ^= CONTENTS_DEADMONSTER; trace = gi.trace (start, ent->mins, ent->maxs, end, ent, mask); } VectorCopy(trace.endpos, ent->s.origin); gi.linkentity(ent); if (trace.fraction != 1.0) { SV_Impact(ent, &trace); /* if the pushed entity went away and the pusher is still there */ if (!trace.ent->inuse && ent->inuse) { /* move the pusher back and try again */ VectorCopy(start, ent->s.origin); gi.linkentity(ent); goto retry; } } if (ent->inuse) { G_TouchTriggers(ent); } return trace; } typedef struct { edict_t *ent; vec3_t origin; vec3_t angles; float deltayaw; } pushed_t; pushed_t pushed[MAX_EDICTS], *pushed_p; edict_t *obstacle; /* * Objects need to be moved back on a failed push, * otherwise riders would continue to slide. */ qboolean SV_Push(edict_t *pusher, vec3_t move, vec3_t amove) { int i, e; edict_t *check, *block; pushed_t *p; vec3_t org, org2, move2, forward, right, up; vec3_t realmins, realmaxs; if (!pusher) { return false; } /* clamp the move to 1/8 units, so the position will be accurate for client side prediction */ for (i = 0; i < 3; i++) { float temp; temp = move[i] * 8.0; if (temp > 0.0) { temp += 0.5; } else { temp -= 0.5; } move[i] = 0.125 * (int)temp; } /* we need this for pushing things later */ VectorSubtract(vec3_origin, amove, org); AngleVectors(org, forward, right, up); /* save the pusher's original position */ pushed_p->ent = pusher; VectorCopy(pusher->s.origin, pushed_p->origin); VectorCopy(pusher->s.angles, pushed_p->angles); if (pusher->client) { pushed_p->deltayaw = pusher->client->ps.pmove.delta_angles[YAW]; } pushed_p++; /* move the pusher to it's final position */ VectorAdd(pusher->s.origin, move, pusher->s.origin); VectorAdd(pusher->s.angles, amove, pusher->s.angles); gi.linkentity(pusher); /* Create a real bounding box for rotating brush models. */ RealBoundingBox(pusher,realmins,realmaxs); /* see if any solid entities are inside the final position */ check = g_edicts + 1; for (e = 1; e < globals.num_edicts; e++, check++) { if (!check->inuse) { continue; } if ((check->movetype == MOVETYPE_PUSH) || (check->movetype == MOVETYPE_STOP) || (check->movetype == MOVETYPE_NONE) || (check->movetype == MOVETYPE_NOCLIP)) { continue; } if (!check->area.prev) { continue; /* not linked in anywhere */ } /* if the entity is standing on the pusher, it will definitely be moved */ if (check->groundentity != pusher) { /* see if the ent needs to be tested */ if ((check->absmin[0] >= realmaxs[0]) || (check->absmin[1] >= realmaxs[1]) || (check->absmin[2] >= realmaxs[2]) || (check->absmax[0] <= realmins[0]) || (check->absmax[1] <= realmins[1]) || (check->absmax[2] <= realmins[2])) { continue; } /* see if the ent's bbox is inside the pusher's final position */ if (!SV_TestEntityPosition(check)) { continue; } } if ((pusher->movetype == MOVETYPE_PUSH) || (check->groundentity == pusher)) { /* move this entity */ pushed_p->ent = check; VectorCopy(check->s.origin, pushed_p->origin); VectorCopy(check->s.angles, pushed_p->angles); pushed_p++; /* try moving the contacted entity */ VectorAdd(check->s.origin, move, check->s.origin); if (check->client) { check->client->ps.pmove.delta_angles[YAW] += amove[YAW]; } /* figure movement due to the pusher's amove */ VectorSubtract(check->s.origin, pusher->s.origin, org); org2[0] = DotProduct(org, forward); org2[1] = -DotProduct(org, right); org2[2] = DotProduct(org, up); VectorSubtract(org2, org, move2); VectorAdd(check->s.origin, move2, check->s.origin); /* may have pushed them off an edge */ if (check->groundentity != pusher) { check->groundentity = NULL; } block = SV_TestEntityPosition(check); if (!block) { /* pushed ok */ gi.linkentity(check); continue; } /* if it is ok to leave in the old position, do it this is only relevent for riding entities, not pushed */ VectorSubtract(check->s.origin, move, check->s.origin); block = SV_TestEntityPosition(check); if (!block) { pushed_p--; continue; } } /* save off the obstacle so we can call the block function */ obstacle = check; /* move back any entities we already moved go backwards, so if the same entity was pushed twice, it goes back to the original position */ for (p = pushed_p - 1; p >= pushed; p--) { VectorCopy(p->origin, p->ent->s.origin); VectorCopy(p->angles, p->ent->s.angles); if (p->ent->client) { p->ent->client->ps.pmove.delta_angles[YAW] = p->deltayaw; } gi.linkentity(p->ent); } return false; } /* see if anything we moved has touched a trigger */ for (p = pushed_p - 1; p >= pushed; p--) { G_TouchTriggers(p->ent); } return true; } /* * Bmodel objects don't interact with each * other, but push all box objects */ void SV_Physics_Pusher(edict_t *ent) { vec3_t move, amove; edict_t *part, *mv; if (!ent) { return; } /* if not a team captain, so movement will be handled elsewhere */ if (ent->flags & FL_TEAMSLAVE) { return; } /* make sure all team slaves can move before commiting any moves or calling any think functions if the move is blocked, all moved objects will be backed out */ pushed_p = pushed; for (part = ent; part; part = part->teamchain) { if (part->velocity[0] || part->velocity[1] || part->velocity[2] || part->avelocity[0] || part->avelocity[1] || part->avelocity[2]) { /* object is moving */ VectorScale(part->velocity, FRAMETIME, move); VectorScale(part->avelocity, FRAMETIME, amove); if (!SV_Push(part, move, amove)) { break; /* move was blocked */ } } } if (pushed_p > &pushed[MAX_EDICTS -1 ]) { gi.error("pushed_p > &pushed[MAX_EDICTS - 1], memory corrupted"); } if (part) { /* the move failed, bump all nextthink times and back out moves */ for (mv = ent; mv; mv = mv->teamchain) { if (mv->nextthink > 0) { mv->nextthink += FRAMETIME; } } /* if the pusher has a "blocked" function, call it otherwise, just stay in place until the obstacle is gone */ if (part->blocked) { part->blocked(part, obstacle); } } else { /* the move succeeded, so call all think functions */ for (part = ent; part; part = part->teamchain) { SV_RunThink(part); } } } /* ================================================================== */ /* * Non moving objects can only think */ void SV_Physics_None(edict_t *ent) { if (!ent) { return; } /* regular thinking */ SV_RunThink(ent); } /* * A moving object that doesn't obey physics */ void SV_Physics_Noclip(edict_t *ent) { if (!ent) { return; } /* regular thinking */ if (!SV_RunThink(ent)) { return; } VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); VectorMA(ent->s.origin, FRAMETIME, ent->velocity, ent->s.origin); gi.linkentity(ent); } /* ================================================================== */ /* TOSS / BOUNCE */ /* * Toss, bounce, and fly movement. * When onground, do nothing. */ void SV_Physics_Toss(edict_t *ent) { trace_t trace; vec3_t move; float backoff; edict_t *slave; qboolean wasinwater; qboolean isinwater; vec3_t old_origin; if (!ent) { return; } /* regular thinking */ SV_RunThink(ent); /* if not a team captain, so movement will be handled elsewhere */ if (ent->flags & FL_TEAMSLAVE) { return; } if (ent->velocity[2] > 0) { ent->groundentity = NULL; } /* check for the groundentity going away */ if (ent->groundentity) { if (!ent->groundentity->inuse) { ent->groundentity = NULL; } } /* if onground, return without moving */ if (ent->groundentity) { return; } VectorCopy(ent->s.origin, old_origin); SV_CheckVelocity(ent); /* add gravity */ if ((ent->movetype != MOVETYPE_FLY) && (ent->movetype != MOVETYPE_FLYMISSILE)) { SV_AddGravity(ent); } /* move angles */ VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); /* move origin */ VectorScale(ent->velocity, FRAMETIME, move); trace = SV_PushEntity(ent, move); if (!ent->inuse) { return; } if (trace.fraction < 1) { if (ent->movetype == MOVETYPE_BOUNCE) { backoff = 1.5; } else { backoff = 1; } ClipVelocity(ent->velocity, trace.plane.normal, ent->velocity, backoff); /* stop if on ground */ if (trace.plane.normal[2] > 0.7) { if ((ent->velocity[2] < 60) || (ent->movetype != MOVETYPE_BOUNCE)) { ent->groundentity = trace.ent; ent->groundentity_linkcount = trace.ent->linkcount; VectorCopy(vec3_origin, ent->velocity); VectorCopy(vec3_origin, ent->avelocity); } } } /* check for water transition */ wasinwater = (ent->watertype & MASK_WATER); ent->watertype = gi.pointcontents(ent->s.origin); isinwater = ent->watertype & MASK_WATER; if (isinwater) { ent->waterlevel = 1; } else { ent->waterlevel = 0; } if (!wasinwater && isinwater) { gi.positioned_sound(old_origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); } else if (wasinwater && !isinwater) { gi.positioned_sound(ent->s.origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0); } /* move teamslaves */ for (slave = ent->teamchain; slave; slave = slave->teamchain) { VectorCopy(ent->s.origin, slave->s.origin); gi.linkentity(slave); } } /* ================================================================== */ /* STEPPING MOVEMENT */ /* * 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_AddRotationalFriction(edict_t *ent) { int n; float adjustment; if (!ent) { return; } VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles); adjustment = FRAMETIME * STOPSPEED * FRICTION; for (n = 0; n < 3; n++) { if (ent->avelocity[n] > 0) { ent->avelocity[n] -= adjustment; if (ent->avelocity[n] < 0) { ent->avelocity[n] = 0; } } else { ent->avelocity[n] += adjustment; if (ent->avelocity[n] > 0) { ent->avelocity[n] = 0; } } } } void SV_Physics_Step(edict_t *ent) { qboolean wasonground; qboolean hitsound = false; float *vel; float speed, newspeed, control; float friction; edict_t *groundentity; int mask; if (!ent) { return; } /* airborn monsters should always check for ground */ if (!ent->groundentity) { M_CheckGround(ent); } groundentity = ent->groundentity; SV_CheckVelocity(ent); if (groundentity) { wasonground = true; } else { wasonground = false; } if (ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2]) { SV_AddRotationalFriction(ent); } /* add gravity except: flying monsters swimming monsters who are in the water */ if (!wasonground) { if (!(ent->flags & FL_FLY)) { if (!((ent->flags & FL_SWIM) && (ent->waterlevel > 2))) { if (ent->velocity[2] < sv_gravity->value * -0.1) { hitsound = true; } if (ent->waterlevel == 0) { SV_AddGravity(ent); } } } } /* friction for flying monsters that have been given vertical velocity */ if ((ent->flags & FL_FLY) && (ent->velocity[2] != 0)) { speed = fabs(ent->velocity[2]); control = speed < STOPSPEED ? STOPSPEED : speed; friction = FRICTION / 3; newspeed = speed - (FRAMETIME * control * friction); if (newspeed < 0) { newspeed = 0; } newspeed /= speed; ent->velocity[2] *= newspeed; } /* friction for flying monsters that have been given vertical velocity */ if ((ent->flags & FL_SWIM) && (ent->velocity[2] != 0)) { speed = fabs(ent->velocity[2]); control = speed < STOPSPEED ? STOPSPEED : speed; newspeed = speed - (FRAMETIME * control * WATERFRICTION * ent->waterlevel); if (newspeed < 0) { newspeed = 0; } newspeed /= speed; ent->velocity[2] *= newspeed; } if (ent->velocity[2] || ent->velocity[1] || ent->velocity[0]) { /* apply friction: let dead monsters who aren't completely onground slide */ if ((wasonground) || (ent->flags & (FL_SWIM | FL_FLY))) { if (!((ent->health <= 0.0) && !M_CheckBottom(ent))) { vel = ent->velocity; speed = sqrt(vel[0] * vel[0] + vel[1] * vel[1]); if (speed) { friction = FRICTION; control = speed < STOPSPEED ? STOPSPEED : speed; newspeed = speed - FRAMETIME * control * friction; if (newspeed < 0) { newspeed = 0; } newspeed /= speed; vel[0] *= newspeed; vel[1] *= newspeed; } } } if (ent->svflags & SVF_MONSTER) { mask = MASK_MONSTERSOLID; } else { mask = MASK_SOLID; } SV_FlyMove(ent, FRAMETIME, mask); gi.linkentity(ent); G_TouchTriggers(ent); if (!ent->inuse) { return; } if (ent->groundentity) { if (!wasonground) { if (hitsound) { gi.sound(ent, 0, gi.soundindex("world/land.wav"), 1, 1, 0); } } } } /* regular thinking */ SV_RunThink(ent); } /* ================================================================== */ void G_RunEntity(edict_t *ent) { if (!ent) { return; } if (ent->prethink) { ent->prethink(ent); } switch ((int)ent->movetype) { case MOVETYPE_PUSH: case MOVETYPE_STOP: SV_Physics_Pusher(ent); break; case MOVETYPE_NONE: SV_Physics_None(ent); break; case MOVETYPE_NOCLIP: SV_Physics_Noclip(ent); break; case MOVETYPE_STEP: SV_Physics_Step(ent); break; case MOVETYPE_TOSS: case MOVETYPE_BOUNCE: case MOVETYPE_FLY: case MOVETYPE_FLYMISSILE: SV_Physics_Toss(ent); break; default: gi.error("SV_Physics: bad movetype %i", (int)ent->movetype); } } yquake2-QUAKE2_5_32/src/game/g_items.c0000644000175000017500000013422412615162034020120 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Item handling and item definitions. * * ======================================================================= */ #include "header/local.h" #define HEALTH_IGNORE_MAX 1 #define HEALTH_TIMED 2 qboolean Pickup_Weapon(edict_t *ent, edict_t *other); void Use_Weapon(edict_t *ent, gitem_t *inv); void Drop_Weapon(edict_t *ent, gitem_t *inv); void Weapon_Blaster(edict_t *ent); void Weapon_Shotgun(edict_t *ent); void Weapon_SuperShotgun(edict_t *ent); void Weapon_Machinegun(edict_t *ent); void Weapon_Chaingun(edict_t *ent); void Weapon_HyperBlaster(edict_t *ent); void Weapon_RocketLauncher(edict_t *ent); void Weapon_Grenade(edict_t *ent); void Weapon_GrenadeLauncher(edict_t *ent); void Weapon_Railgun(edict_t *ent); void Weapon_BFG(edict_t *ent); gitem_armor_t jacketarmor_info = {25, 50, .30, .00, ARMOR_JACKET}; gitem_armor_t combatarmor_info = {50, 100, .60, .30, ARMOR_COMBAT}; gitem_armor_t bodyarmor_info = {100, 200, .80, .60, ARMOR_BODY}; int jacket_armor_index; int combat_armor_index; int body_armor_index; static int power_screen_index; static int power_shield_index; void Use_Quad(edict_t *ent, gitem_t *item); static int quad_drop_timeout_hack; /* ====================================================================== */ gitem_t * GetItemByIndex(int index) { if ((index == 0) || (index >= game.num_items)) { return NULL; } return &itemlist[index]; } gitem_t * FindItemByClassname(char *classname) { int i; gitem_t *it; if (!classname) { return NULL; } it = itemlist; for (i = 0; i < game.num_items; i++, it++) { if (!it->classname) { continue; } if (!Q_stricmp(it->classname, classname)) { return it; } } return NULL; } gitem_t * FindItem(char *pickup_name) { int i; gitem_t *it; if (!pickup_name) { return NULL; } it = itemlist; for (i = 0; i < game.num_items; i++, it++) { if (!it->pickup_name) { continue; } if (!Q_stricmp(it->pickup_name, pickup_name)) { return it; } } return NULL; } /* ====================================================================== */ void DoRespawn(edict_t *ent) { if (!ent) { return; } if (ent->team) { edict_t *master; int count; int choice; master = ent->teammaster; for (count = 0, ent = master; ent; ent = ent->chain, count++) { } choice = count ? randk() % count : 0; for (count = 0, ent = master; count < choice; ent = ent->chain, count++) { } } ent->svflags &= ~SVF_NOCLIENT; ent->solid = SOLID_TRIGGER; gi.linkentity(ent); /* send an effect */ ent->s.event = EV_ITEM_RESPAWN; } void SetRespawn(edict_t *ent, float delay) { if (!ent) { return; } ent->flags |= FL_RESPAWN; ent->svflags |= SVF_NOCLIENT; ent->solid = SOLID_NOT; ent->nextthink = level.time + delay; ent->think = DoRespawn; gi.linkentity(ent); } /* ====================================================================== */ qboolean Pickup_Powerup(edict_t *ent, edict_t *other) { int quantity; if (!ent || !other) { return false; } quantity = other->client->pers.inventory[ITEM_INDEX(ent->item)]; if (((skill->value == 1) && (quantity >= 2)) || ((skill->value >= 2) && (quantity >= 1))) { return false; } if ((coop->value) && (ent->item->flags & IT_STAY_COOP) && (quantity > 0)) { return false; } other->client->pers.inventory[ITEM_INDEX(ent->item)]++; if (deathmatch->value) { if (!(ent->spawnflags & DROPPED_ITEM)) { SetRespawn(ent, ent->item->quantity); } if (((int)dmflags->value & DF_INSTANT_ITEMS) || ((ent->item->use == Use_Quad) && (ent->spawnflags & DROPPED_PLAYER_ITEM))) { if ((ent->item->use == Use_Quad) && (ent->spawnflags & DROPPED_PLAYER_ITEM)) { quad_drop_timeout_hack = (ent->nextthink - level.time) / FRAMETIME; } ent->item->use(other, ent->item); } } return true; } void Drop_General(edict_t *ent, gitem_t *item) { Drop_Item(ent, item); ent->client->pers.inventory[ITEM_INDEX(item)]--; ValidateSelectedItem(ent); } /* ====================================================================== */ qboolean Pickup_Adrenaline(edict_t *ent, edict_t *other) { if (!ent || !other) { return false; } if (!deathmatch->value) { other->max_health += 1; } if (other->health < other->max_health) { other->health = other->max_health; } if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) { SetRespawn(ent, ent->item->quantity); } return true; } qboolean Pickup_AncientHead(edict_t *ent, edict_t *other) { if (!ent || !other) { return false; } other->max_health += 2; if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) { SetRespawn(ent, ent->item->quantity); } return true; } qboolean Pickup_Bandolier(edict_t *ent, edict_t *other) { gitem_t *item; int index; if (!ent || !other) { return false; } if (other->client->pers.max_bullets < 250) { other->client->pers.max_bullets = 250; } if (other->client->pers.max_shells < 150) { other->client->pers.max_shells = 150; } if (other->client->pers.max_cells < 250) { other->client->pers.max_cells = 250; } if (other->client->pers.max_slugs < 75) { other->client->pers.max_slugs = 75; } item = FindItem("Bullets"); if (item) { index = ITEM_INDEX(item); other->client->pers.inventory[index] += item->quantity; if (other->client->pers.inventory[index] > other->client->pers.max_bullets) { other->client->pers.inventory[index] = other->client->pers.max_bullets; } } item = FindItem("Shells"); if (item) { index = ITEM_INDEX(item); other->client->pers.inventory[index] += item->quantity; if (other->client->pers.inventory[index] > other->client->pers.max_shells) { other->client->pers.inventory[index] = other->client->pers.max_shells; } } if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) { SetRespawn(ent, ent->item->quantity); } return true; } qboolean Pickup_Pack(edict_t *ent, edict_t *other) { gitem_t *item; int index; if (!ent || !other) { return false; } if (other->client->pers.max_bullets < 300) { other->client->pers.max_bullets = 300; } if (other->client->pers.max_shells < 200) { other->client->pers.max_shells = 200; } if (other->client->pers.max_rockets < 100) { other->client->pers.max_rockets = 100; } if (other->client->pers.max_grenades < 100) { other->client->pers.max_grenades = 100; } if (other->client->pers.max_cells < 300) { other->client->pers.max_cells = 300; } if (other->client->pers.max_slugs < 100) { other->client->pers.max_slugs = 100; } item = FindItem("Bullets"); if (item) { index = ITEM_INDEX(item); other->client->pers.inventory[index] += item->quantity; if (other->client->pers.inventory[index] > other->client->pers.max_bullets) { other->client->pers.inventory[index] = other->client->pers.max_bullets; } } item = FindItem("Shells"); if (item) { index = ITEM_INDEX(item); other->client->pers.inventory[index] += item->quantity; if (other->client->pers.inventory[index] > other->client->pers.max_shells) { other->client->pers.inventory[index] = other->client->pers.max_shells; } } item = FindItem("Cells"); if (item) { index = ITEM_INDEX(item); other->client->pers.inventory[index] += item->quantity; if (other->client->pers.inventory[index] > other->client->pers.max_cells) { other->client->pers.inventory[index] = other->client->pers.max_cells; } } item = FindItem("Grenades"); if (item) { index = ITEM_INDEX(item); other->client->pers.inventory[index] += item->quantity; if (other->client->pers.inventory[index] > other->client->pers.max_grenades) { other->client->pers.inventory[index] = other->client->pers.max_grenades; } } item = FindItem("Rockets"); if (item) { index = ITEM_INDEX(item); other->client->pers.inventory[index] += item->quantity; if (other->client->pers.inventory[index] > other->client->pers.max_rockets) { other->client->pers.inventory[index] = other->client->pers.max_rockets; } } item = FindItem("Slugs"); if (item) { index = ITEM_INDEX(item); other->client->pers.inventory[index] += item->quantity; if (other->client->pers.inventory[index] > other->client->pers.max_slugs) { other->client->pers.inventory[index] = other->client->pers.max_slugs; } } if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) { SetRespawn(ent, ent->item->quantity); } return true; } /* ====================================================================== */ void Use_Quad(edict_t *ent, gitem_t *item) { int timeout; if (!ent || !item) { return; } ent->client->pers.inventory[ITEM_INDEX(item)]--; ValidateSelectedItem(ent); if (quad_drop_timeout_hack) { timeout = quad_drop_timeout_hack; quad_drop_timeout_hack = 0; } else { timeout = 300; } if (ent->client->quad_framenum > level.framenum) { ent->client->quad_framenum += timeout; } else { ent->client->quad_framenum = level.framenum + timeout; } gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage.wav"), 1, ATTN_NORM, 0); } /* ====================================================================== */ void Use_Breather(edict_t *ent, gitem_t *item) { if (!ent || !item) { return; } ent->client->pers.inventory[ITEM_INDEX(item)]--; ValidateSelectedItem(ent); if (ent->client->breather_framenum > level.framenum) { ent->client->breather_framenum += 300; } else { ent->client->breather_framenum = level.framenum + 300; } } /* ====================================================================== */ void Use_Envirosuit(edict_t *ent, gitem_t *item) { if (!ent || !item) { return; } ent->client->pers.inventory[ITEM_INDEX(item)]--; ValidateSelectedItem(ent); if (ent->client->enviro_framenum > level.framenum) { ent->client->enviro_framenum += 300; } else { ent->client->enviro_framenum = level.framenum + 300; } } /* ====================================================================== */ void Use_Invulnerability(edict_t *ent, gitem_t *item) { if (!ent || !item) { return; } ent->client->pers.inventory[ITEM_INDEX(item)]--; ValidateSelectedItem(ent); if (ent->client->invincible_framenum > level.framenum) { ent->client->invincible_framenum += 300; } else { ent->client->invincible_framenum = level.framenum + 300; } gi.sound(ent, CHAN_ITEM, gi.soundindex( "items/protect.wav"), 1, ATTN_NORM, 0); } /* ====================================================================== */ void Use_Silencer(edict_t *ent, gitem_t *item) { if (!ent || !item) { return; } ent->client->pers.inventory[ITEM_INDEX(item)]--; ValidateSelectedItem(ent); ent->client->silencer_shots += 30; } /* ====================================================================== */ qboolean Pickup_Key(edict_t *ent, edict_t *other) { if (!ent || !other) { return false; } if (coop->value) { if (strcmp(ent->classname, "key_power_cube") == 0) { if (other->client->pers.power_cubes & ((ent->spawnflags & 0x0000ff00) >> 8)) { return false; } other->client->pers.inventory[ITEM_INDEX(ent->item)]++; other->client->pers.power_cubes |= ((ent->spawnflags & 0x0000ff00) >> 8); } else { if (other->client->pers.inventory[ITEM_INDEX(ent->item)]) { return false; } other->client->pers.inventory[ITEM_INDEX(ent->item)] = 1; } return true; } other->client->pers.inventory[ITEM_INDEX(ent->item)]++; return true; } /* ====================================================================== */ qboolean Add_Ammo(edict_t *ent, gitem_t *item, int count) { int index; int max; if (!ent || !item) { return false; } if (!ent->client) { return false; } if (item->tag == AMMO_BULLETS) { max = ent->client->pers.max_bullets; } else if (item->tag == AMMO_SHELLS) { max = ent->client->pers.max_shells; } else if (item->tag == AMMO_ROCKETS) { max = ent->client->pers.max_rockets; } else if (item->tag == AMMO_GRENADES) { max = ent->client->pers.max_grenades; } else if (item->tag == AMMO_CELLS) { max = ent->client->pers.max_cells; } else if (item->tag == AMMO_SLUGS) { max = ent->client->pers.max_slugs; } else { return false; } index = ITEM_INDEX(item); if (ent->client->pers.inventory[index] == max) { return false; } ent->client->pers.inventory[index] += count; if (ent->client->pers.inventory[index] > max) { ent->client->pers.inventory[index] = max; } return true; } qboolean Pickup_Ammo(edict_t *ent, edict_t *other) { int oldcount; int count; qboolean weapon; if (!ent || !other) { return false; } weapon = (ent->item->flags & IT_WEAPON); if ((weapon) && ((int)dmflags->value & DF_INFINITE_AMMO)) { count = 1000; } else if (ent->count) { count = ent->count; } else { count = ent->item->quantity; } oldcount = other->client->pers.inventory[ITEM_INDEX(ent->item)]; if (!Add_Ammo(other, ent->item, count)) { return false; } if (weapon && !oldcount) { if ((other->client->pers.weapon != ent->item) && (!deathmatch->value || (other->client->pers.weapon == FindItem("blaster")))) { other->client->newweapon = ent->item; } } if (!(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM)) && (deathmatch->value)) { SetRespawn(ent, 30); } return true; } void Drop_Ammo(edict_t *ent, gitem_t *item) { edict_t *dropped; int index; if (!ent || !item) { return; } index = ITEM_INDEX(item); dropped = Drop_Item(ent, item); if (ent->client->pers.inventory[index] >= item->quantity) { dropped->count = item->quantity; } else { dropped->count = ent->client->pers.inventory[index]; } if (ent->client->pers.weapon && (ent->client->pers.weapon->tag == AMMO_GRENADES) && (item->tag == AMMO_GRENADES) && (ent->client->pers.inventory[index] - dropped->count <= 0)) { gi.cprintf(ent, PRINT_HIGH, "Can't drop current weapon\n"); G_FreeEdict(dropped); return; } ent->client->pers.inventory[index] -= dropped->count; ValidateSelectedItem(ent); } /* ====================================================================== */ void MegaHealth_think(edict_t *self) { if (!self) { return; } if (self->owner->health > self->owner->max_health) { self->nextthink = level.time + 1; self->owner->health -= 1; return; } if (!(self->spawnflags & DROPPED_ITEM) && (deathmatch->value)) { SetRespawn(self, 20); } else { G_FreeEdict(self); } } qboolean Pickup_Health(edict_t *ent, edict_t *other) { if (!ent || !other) { return false; } if (!(ent->style & HEALTH_IGNORE_MAX)) { if (other->health >= other->max_health) { return false; } } other->health += ent->count; if (!(ent->style & HEALTH_IGNORE_MAX)) { if (other->health > other->max_health) { other->health = other->max_health; } } if (ent->style & HEALTH_TIMED) { ent->think = MegaHealth_think; ent->nextthink = level.time + 5; ent->owner = other; ent->flags |= FL_RESPAWN; ent->svflags |= SVF_NOCLIENT; ent->solid = SOLID_NOT; } else { if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) { SetRespawn(ent, 30); } } return true; } /* ====================================================================== */ int ArmorIndex(edict_t *ent) { if (!ent) { return 0; } if (!ent->client) { return 0; } if (ent->client->pers.inventory[jacket_armor_index] > 0) { return jacket_armor_index; } if (ent->client->pers.inventory[combat_armor_index] > 0) { return combat_armor_index; } if (ent->client->pers.inventory[body_armor_index] > 0) { return body_armor_index; } return 0; } qboolean Pickup_Armor(edict_t *ent, edict_t *other) { int old_armor_index; gitem_armor_t *oldinfo; gitem_armor_t *newinfo; int newcount; float salvage; int salvagecount; if (!ent || !other) { return false; } /* get info on new armor */ newinfo = (gitem_armor_t *)ent->item->info; old_armor_index = ArmorIndex(other); /* handle armor shards specially */ if (ent->item->tag == ARMOR_SHARD) { if (!old_armor_index) { other->client->pers.inventory[jacket_armor_index] = 2; } else { other->client->pers.inventory[old_armor_index] += 2; } } else if (!old_armor_index) /* if player has no armor, just use it */ { other->client->pers.inventory[ITEM_INDEX(ent->item)] = newinfo->base_count; } else /* use the better armor */ { /* get info on old armor */ if (old_armor_index == jacket_armor_index) { oldinfo = &jacketarmor_info; } else if (old_armor_index == combat_armor_index) { oldinfo = &combatarmor_info; } else { oldinfo = &bodyarmor_info; } if (newinfo->normal_protection > oldinfo->normal_protection) { /* calc new armor values */ salvage = oldinfo->normal_protection / newinfo->normal_protection; salvagecount = salvage * other->client->pers.inventory[old_armor_index]; newcount = newinfo->base_count + salvagecount; if (newcount > newinfo->max_count) { newcount = newinfo->max_count; } /* zero count of old armor so it goes away */ other->client->pers.inventory[old_armor_index] = 0; /* change armor to new item with computed value */ other->client->pers.inventory[ITEM_INDEX(ent->item)] = newcount; } else { /* calc new armor values */ salvage = newinfo->normal_protection / oldinfo->normal_protection; salvagecount = salvage * newinfo->base_count; newcount = other->client->pers.inventory[old_armor_index] + salvagecount; if (newcount > oldinfo->max_count) { newcount = oldinfo->max_count; } /* if we're already maxed out then we don't need the new armor */ if (other->client->pers.inventory[old_armor_index] >= newcount) { return false; } /* update current armor value */ other->client->pers.inventory[old_armor_index] = newcount; } } if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value)) { SetRespawn(ent, 20); } return true; } /* ====================================================================== */ int PowerArmorType(edict_t *ent) { if (!ent) { return POWER_ARMOR_NONE; } if (!ent->client) { return POWER_ARMOR_NONE; } if (!(ent->flags & FL_POWER_ARMOR)) { return POWER_ARMOR_NONE; } if (ent->client->pers.inventory[power_shield_index] > 0) { return POWER_ARMOR_SHIELD; } if (ent->client->pers.inventory[power_screen_index] > 0) { return POWER_ARMOR_SCREEN; } return POWER_ARMOR_NONE; } void Use_PowerArmor(edict_t *ent, gitem_t *item) { int index; if (!ent || !item) { return; } if (ent->flags & FL_POWER_ARMOR) { ent->flags &= ~FL_POWER_ARMOR; gi.sound(ent, CHAN_AUTO, gi.soundindex( "misc/power2.wav"), 1, ATTN_NORM, 0); } else { index = ITEM_INDEX(FindItem("cells")); if (!ent->client->pers.inventory[index]) { gi.cprintf(ent, PRINT_HIGH, "No cells for power armor.\n"); return; } ent->flags |= FL_POWER_ARMOR; gi.sound(ent, CHAN_AUTO, gi.soundindex( "misc/power1.wav"), 1, ATTN_NORM, 0); } } qboolean Pickup_PowerArmor(edict_t *ent, edict_t *other) { int quantity; if (!ent || !other) { return false; } quantity = other->client->pers.inventory[ITEM_INDEX(ent->item)]; other->client->pers.inventory[ITEM_INDEX(ent->item)]++; if (deathmatch->value) { if (!(ent->spawnflags & DROPPED_ITEM)) { SetRespawn(ent, ent->item->quantity); } /* auto-use for DM only if we didn't already have one */ if (!quantity) { ent->item->use(other, ent->item); } } return true; } void Drop_PowerArmor(edict_t *ent, gitem_t *item) { if (!ent || !item) { return; } if ((ent->flags & FL_POWER_ARMOR) && (ent->client->pers.inventory[ITEM_INDEX(item)] == 1)) { Use_PowerArmor(ent, item); } Drop_General(ent, item); } /* ====================================================================== */ void Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { qboolean taken; if (!ent || !other) { return; } if (!other->client) { return; } if (other->health < 1) { return; /* dead people can't pickup */ } if (!ent->item->pickup) { return; /* not a grabbable item? */ } taken = ent->item->pickup(ent, other); if (taken) { /* flash the screen */ other->client->bonus_alpha = 0.25; /* show icon and name on status bar */ other->client->ps.stats[STAT_PICKUP_ICON] = gi.imageindex( ent->item->icon); other->client->ps.stats[STAT_PICKUP_STRING] = CS_ITEMS + ITEM_INDEX( ent->item); other->client->pickup_msg_time = level.time + 3.0; /* change selected item */ if (ent->item->use) { other->client->pers.selected_item = other->client->ps.stats[STAT_SELECTED_ITEM] = ITEM_INDEX( ent->item); } if (ent->item->pickup == Pickup_Health) { if (ent->count == 2) { gi.sound(other, CHAN_ITEM, gi.soundindex( "items/s_health.wav"), 1, ATTN_NORM, 0); } else if (ent->count == 10) { gi.sound(other, CHAN_ITEM, gi.soundindex( "items/n_health.wav"), 1, ATTN_NORM, 0); } else if (ent->count == 25) { gi.sound(other, CHAN_ITEM, gi.soundindex( "items/l_health.wav"), 1, ATTN_NORM, 0); } else /* (ent->count == 100) */ { gi.sound(other, CHAN_ITEM, gi.soundindex( "items/m_health.wav"), 1, ATTN_NORM, 0); } } else if (ent->item->pickup_sound) { gi.sound(other, CHAN_ITEM, gi.soundindex( ent->item->pickup_sound), 1, ATTN_NORM, 0); } } if (!(ent->spawnflags & ITEM_TARGETS_USED)) { G_UseTargets(ent, other); ent->spawnflags |= ITEM_TARGETS_USED; } if (!taken) { return; } if (!((coop->value) && (ent->item->flags & IT_STAY_COOP)) || (ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM))) { if (ent->flags & FL_RESPAWN) { ent->flags &= ~FL_RESPAWN; } else { G_FreeEdict(ent); } } } /* ====================================================================== */ void drop_temp_touch(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) { if (!ent || !other) { return; } if (other == ent->owner) { return; } /* plane and surf are unused in Touch_Item but since the function is part of the game <-> client interface dropping them is too much pain. */ Touch_Item(ent, other, plane, surf); } void drop_make_touchable(edict_t *ent) { if (!ent) { return; } ent->touch = Touch_Item; if (deathmatch->value) { ent->nextthink = level.time + 29; ent->think = G_FreeEdict; } } edict_t * Drop_Item(edict_t *ent, gitem_t *item) { edict_t *dropped; vec3_t forward, right; vec3_t offset; if (!ent || !item) { return NULL; } dropped = G_Spawn(); dropped->classname = item->classname; dropped->item = item; dropped->spawnflags = DROPPED_ITEM; dropped->s.effects = item->world_model_flags; dropped->s.renderfx = RF_GLOW; if (randk() > 0.5) { dropped->s.angles[1] += randk()*45; } else { dropped->s.angles[1] -= randk()*45; } VectorSet (dropped->mins, -16, -16, -16); VectorSet (dropped->maxs, 16, 16, 16); gi.setmodel(dropped, dropped->item->world_model); dropped->solid = SOLID_TRIGGER; dropped->movetype = MOVETYPE_TOSS; dropped->touch = drop_temp_touch; dropped->owner = ent; if (ent->client) { trace_t trace; AngleVectors(ent->client->v_angle, forward, right, NULL); VectorSet(offset, 24, 0, -16); G_ProjectSource(ent->s.origin, offset, forward, right, dropped->s.origin); trace = gi.trace(ent->s.origin, dropped->mins, dropped->maxs, dropped->s.origin, ent, CONTENTS_SOLID); VectorCopy(trace.endpos, dropped->s.origin); } else { AngleVectors(ent->s.angles, forward, right, NULL); VectorCopy(ent->s.origin, dropped->s.origin); } VectorScale(forward, 100, dropped->velocity); dropped->velocity[2] = 300; dropped->think = drop_make_touchable; dropped->nextthink = level.time + 1; gi.linkentity(dropped); return dropped; } void Use_Item(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!ent) { return; } ent->svflags &= ~SVF_NOCLIENT; ent->use = NULL; if (ent->spawnflags & ITEM_NO_TOUCH) { ent->solid = SOLID_BBOX; ent->touch = NULL; } else { ent->solid = SOLID_TRIGGER; ent->touch = Touch_Item; } gi.linkentity(ent); } /* ====================================================================== */ void droptofloor(edict_t *ent) { trace_t tr; vec3_t dest; float *v; if (!ent) { return; } v = tv(-15, -15, -15); VectorCopy(v, ent->mins); v = tv(15, 15, 15); VectorCopy(v, ent->maxs); if (ent->model) { gi.setmodel(ent, ent->model); } else { gi.setmodel(ent, ent->item->world_model); } ent->solid = SOLID_TRIGGER; ent->movetype = MOVETYPE_TOSS; ent->touch = Touch_Item; v = tv(0, 0, -128); VectorAdd(ent->s.origin, v, dest); tr = gi.trace(ent->s.origin, ent->mins, ent->maxs, dest, ent, MASK_SOLID); if (tr.startsolid) { gi.dprintf("droptofloor: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin)); G_FreeEdict(ent); return; } VectorCopy(tr.endpos, ent->s.origin); if (ent->team) { ent->flags &= ~FL_TEAMSLAVE; ent->chain = ent->teamchain; ent->teamchain = NULL; ent->svflags |= SVF_NOCLIENT; ent->solid = SOLID_NOT; if (ent == ent->teammaster) { ent->nextthink = level.time + FRAMETIME; ent->think = DoRespawn; } } if (ent->spawnflags & ITEM_NO_TOUCH) { ent->solid = SOLID_BBOX; ent->touch = NULL; ent->s.effects &= ~EF_ROTATE; ent->s.renderfx &= ~RF_GLOW; } if (ent->spawnflags & ITEM_TRIGGER_SPAWN) { ent->svflags |= SVF_NOCLIENT; ent->solid = SOLID_NOT; ent->use = Use_Item; } gi.linkentity(ent); } /* * Precaches all data needed for a given item. * This will be called for each item spawned in a level, * and for each item in each client's inventory. */ void PrecacheItem(gitem_t *it) { char *s, *start; char data[MAX_QPATH]; int len; gitem_t *ammo; if (!it) { return; } if (it->pickup_sound) { gi.soundindex(it->pickup_sound); } if (it->world_model) { gi.modelindex(it->world_model); } if (it->view_model) { gi.modelindex(it->view_model); } if (it->icon) { gi.imageindex(it->icon); } /* parse everything for its ammo */ if (it->ammo && it->ammo[0]) { ammo = FindItem(it->ammo); if (ammo != it) { PrecacheItem(ammo); } } /* parse the space seperated precache string for other items */ s = it->precaches; if (!s || !s[0]) { return; } while (*s) { start = s; while (*s && *s != ' ') { s++; } len = s - start; if ((len >= MAX_QPATH) || (len < 5)) { gi.error("PrecacheItem: %s has bad precache string", it->classname); } memcpy(data, start, len); data[len] = 0; if (*s) { s++; } /* determine type based on extension */ if (!strcmp(data + len - 3, "md2")) { gi.modelindex(data); } else if (!strcmp(data + len - 3, "sp2")) { gi.modelindex(data); } else if (!strcmp(data + len - 3, "wav")) { gi.soundindex(data); } if (!strcmp(data + len - 3, "pcx")) { gi.imageindex(data); } } } /* * ============ * Sets the clipping size and * plants the object on the floor. * * Items can't be immediately dropped * to floor, because they might be on * an entity that hasn't spawned yet. * ============ */ void SpawnItem(edict_t *ent, gitem_t *item) { if (!ent || !item) { return; } PrecacheItem(item); if (ent->spawnflags) { if (strcmp(ent->classname, "key_power_cube") != 0) { ent->spawnflags = 0; gi.dprintf("%s at %s has invalid spawnflags set\n", ent->classname, vtos(ent->s.origin)); } } /* some items will be prevented in deathmatch */ if (deathmatch->value) { if ((int)dmflags->value & DF_NO_ARMOR) { if ((item->pickup == Pickup_Armor) || (item->pickup == Pickup_PowerArmor)) { G_FreeEdict(ent); return; } } if ((int)dmflags->value & DF_NO_ITEMS) { if (item->pickup == Pickup_Powerup) { G_FreeEdict(ent); return; } } if ((int)dmflags->value & DF_NO_HEALTH) { if ((item->pickup == Pickup_Health) || (item->pickup == Pickup_Adrenaline) || (item->pickup == Pickup_AncientHead)) { G_FreeEdict(ent); return; } } if ((int)dmflags->value & DF_INFINITE_AMMO) { if ((item->flags == IT_AMMO) || (strcmp(ent->classname, "weapon_bfg") == 0)) { G_FreeEdict(ent); return; } } } if (coop->value && (strcmp(ent->classname, "key_power_cube") == 0)) { ent->spawnflags |= (1 << (8 + level.power_cubes)); level.power_cubes++; } /* don't let them drop items that stay in a coop game */ if ((coop->value) && (item->flags & IT_STAY_COOP)) { item->drop = NULL; } ent->item = item; ent->nextthink = level.time + 2 * FRAMETIME; /* items start after other solids */ ent->think = droptofloor; ent->s.effects = item->world_model_flags; ent->s.renderfx = RF_GLOW; if (ent->model) { gi.modelindex(ent->model); } } /* ====================================================================== */ gitem_t itemlist[] = { { NULL }, /* leave index 0 alone */ /* QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "item_armor_body", Pickup_Armor, NULL, NULL, NULL, "misc/ar1_pkup.wav", "models/items/armor/body/tris.md2", EF_ROTATE, NULL, "i_bodyarmor", "Body Armor", 3, 0, NULL, IT_ARMOR, 0, &bodyarmor_info, ARMOR_BODY, "" }, /* QUAKED item_armor_combat (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "item_armor_combat", Pickup_Armor, NULL, NULL, NULL, "misc/ar1_pkup.wav", "models/items/armor/combat/tris.md2", EF_ROTATE, NULL, "i_combatarmor", "Combat Armor", 3, 0, NULL, IT_ARMOR, 0, &combatarmor_info, ARMOR_COMBAT, "" }, /* QUAKED item_armor_jacket (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "item_armor_jacket", Pickup_Armor, NULL, NULL, NULL, "misc/ar1_pkup.wav", "models/items/armor/jacket/tris.md2", EF_ROTATE, NULL, "i_jacketarmor", "Jacket Armor", 3, 0, NULL, IT_ARMOR, 0, &jacketarmor_info, ARMOR_JACKET, "" }, /* QUAKED item_armor_shard (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "item_armor_shard", Pickup_Armor, NULL, NULL, NULL, "misc/ar2_pkup.wav", "models/items/armor/shard/tris.md2", EF_ROTATE, NULL, "i_jacketarmor", "Armor Shard", 3, 0, NULL, IT_ARMOR, 0, NULL, ARMOR_SHARD, "" }, /* QUAKED item_power_screen (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "item_power_screen", Pickup_PowerArmor, Use_PowerArmor, Drop_PowerArmor, NULL, "misc/ar3_pkup.wav", "models/items/armor/screen/tris.md2", EF_ROTATE, NULL, "i_powerscreen", "Power Screen", 0, 60, NULL, IT_ARMOR, 0, NULL, 0, "" }, /* QUAKED item_power_shield (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "item_power_shield", Pickup_PowerArmor, Use_PowerArmor, Drop_PowerArmor, NULL, "misc/ar3_pkup.wav", "models/items/armor/shield/tris.md2", EF_ROTATE, NULL, "i_powershield", "Power Shield", 0, 60, NULL, IT_ARMOR, 0, NULL, 0, "misc/power2.wav misc/power1.wav" }, /* weapon_blaster (.3 .3 1) (-16 -16 -16) (16 16 16) always owned, never in the world */ { "weapon_blaster", NULL, Use_Weapon, NULL, Weapon_Blaster, "misc/w_pkup.wav", NULL, 0, "models/weapons/v_blast/tris.md2", "w_blaster", "Blaster", 0, 0, NULL, IT_WEAPON | IT_STAY_COOP, WEAP_BLASTER, NULL, 0, "weapons/blastf1a.wav misc/lasfly.wav" }, /* QUAKED weapon_shotgun (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "weapon_shotgun", Pickup_Weapon, Use_Weapon, Drop_Weapon, Weapon_Shotgun, "misc/w_pkup.wav", "models/weapons/g_shotg/tris.md2", EF_ROTATE, "models/weapons/v_shotg/tris.md2", "w_shotgun", "Shotgun", 0, 1, "Shells", IT_WEAPON | IT_STAY_COOP, WEAP_SHOTGUN, NULL, 0, "weapons/shotgf1b.wav weapons/shotgr1b.wav" }, /* QUAKED weapon_supershotgun (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "weapon_supershotgun", Pickup_Weapon, Use_Weapon, Drop_Weapon, Weapon_SuperShotgun, "misc/w_pkup.wav", "models/weapons/g_shotg2/tris.md2", EF_ROTATE, "models/weapons/v_shotg2/tris.md2", "w_sshotgun", "Super Shotgun", 0, 2, "Shells", IT_WEAPON | IT_STAY_COOP, WEAP_SUPERSHOTGUN, NULL, 0, "weapons/sshotf1b.wav" }, /* QUAKED weapon_machinegun (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "weapon_machinegun", Pickup_Weapon, Use_Weapon, Drop_Weapon, Weapon_Machinegun, "misc/w_pkup.wav", "models/weapons/g_machn/tris.md2", EF_ROTATE, "models/weapons/v_machn/tris.md2", "w_machinegun", "Machinegun", 0, 1, "Bullets", IT_WEAPON | IT_STAY_COOP, WEAP_MACHINEGUN, NULL, 0, "weapons/machgf1b.wav weapons/machgf2b.wav weapons/machgf3b.wav weapons/machgf4b.wav weapons/machgf5b.wav" }, /* QUAKED weapon_chaingun (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "weapon_chaingun", Pickup_Weapon, Use_Weapon, Drop_Weapon, Weapon_Chaingun, "misc/w_pkup.wav", "models/weapons/g_chain/tris.md2", EF_ROTATE, "models/weapons/v_chain/tris.md2", "w_chaingun", "Chaingun", 0, 1, "Bullets", IT_WEAPON | IT_STAY_COOP, WEAP_CHAINGUN, NULL, 0, "weapons/chngnu1a.wav weapons/chngnl1a.wav weapons/machgf3b.wav` weapons/chngnd1a.wav" }, /* QUAKED ammo_grenades (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "ammo_grenades", Pickup_Ammo, Use_Weapon, Drop_Ammo, Weapon_Grenade, "misc/am_pkup.wav", "models/items/ammo/grenades/medium/tris.md2", 0, "models/weapons/v_handgr/tris.md2", "a_grenades", "Grenades", 3, 5, "grenades", IT_AMMO | IT_WEAPON, WEAP_GRENADES, NULL, AMMO_GRENADES, "weapons/hgrent1a.wav weapons/hgrena1b.wav weapons/hgrenc1b.wav weapons/hgrenb1a.wav weapons/hgrenb2a.wav " }, /* QUAKED weapon_grenadelauncher (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "weapon_grenadelauncher", Pickup_Weapon, Use_Weapon, Drop_Weapon, Weapon_GrenadeLauncher, "misc/w_pkup.wav", "models/weapons/g_launch/tris.md2", EF_ROTATE, "models/weapons/v_launch/tris.md2", "w_glauncher", "Grenade Launcher", 0, 1, "Grenades", IT_WEAPON | IT_STAY_COOP, WEAP_GRENADELAUNCHER, NULL, 0, "models/objects/grenade/tris.md2 weapons/grenlf1a.wav weapons/grenlr1b.wav weapons/grenlb1b.wav" }, /* QUAKED weapon_rocketlauncher (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "weapon_rocketlauncher", Pickup_Weapon, Use_Weapon, Drop_Weapon, Weapon_RocketLauncher, "misc/w_pkup.wav", "models/weapons/g_rocket/tris.md2", EF_ROTATE, "models/weapons/v_rocket/tris.md2", "w_rlauncher", "Rocket Launcher", 0, 1, "Rockets", IT_WEAPON | IT_STAY_COOP, WEAP_ROCKETLAUNCHER, NULL, 0, "models/objects/rocket/tris.md2 weapons/rockfly.wav weapons/rocklf1a.wav weapons/rocklr1b.wav models/objects/debris2/tris.md2" }, /* QUAKED weapon_hyperblaster (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "weapon_hyperblaster", Pickup_Weapon, Use_Weapon, Drop_Weapon, Weapon_HyperBlaster, "misc/w_pkup.wav", "models/weapons/g_hyperb/tris.md2", EF_ROTATE, "models/weapons/v_hyperb/tris.md2", "w_hyperblaster", "HyperBlaster", 0, 1, "Cells", IT_WEAPON | IT_STAY_COOP, WEAP_HYPERBLASTER, NULL, 0, "weapons/hyprbu1a.wav weapons/hyprbl1a.wav weapons/hyprbf1a.wav weapons/hyprbd1a.wav misc/lasfly.wav" }, /* QUAKED weapon_railgun (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "weapon_railgun", Pickup_Weapon, Use_Weapon, Drop_Weapon, Weapon_Railgun, "misc/w_pkup.wav", "models/weapons/g_rail/tris.md2", EF_ROTATE, "models/weapons/v_rail/tris.md2", "w_railgun", "Railgun", 0, 1, "Slugs", IT_WEAPON | IT_STAY_COOP, WEAP_RAILGUN, NULL, 0, "weapons/rg_hum.wav" }, /* QUAKED weapon_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "weapon_bfg", Pickup_Weapon, Use_Weapon, Drop_Weapon, Weapon_BFG, "misc/w_pkup.wav", "models/weapons/g_bfg/tris.md2", EF_ROTATE, "models/weapons/v_bfg/tris.md2", "w_bfg", "BFG10K", 0, 50, "Cells", IT_WEAPON | IT_STAY_COOP, WEAP_BFG, NULL, 0, "sprites/s_bfg1.sp2 sprites/s_bfg2.sp2 sprites/s_bfg3.sp2 weapons/bfg__f1y.wav weapons/bfg__l1a.wav weapons/bfg__x1b.wav weapons/bfg_hum.wav" }, /* QUAKED ammo_shells (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "ammo_shells", Pickup_Ammo, NULL, Drop_Ammo, NULL, "misc/am_pkup.wav", "models/items/ammo/shells/medium/tris.md2", 0, NULL, "a_shells", "Shells", 3, 10, NULL, IT_AMMO, 0, NULL, AMMO_SHELLS, "" }, /* QUAKED ammo_bullets (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "ammo_bullets", Pickup_Ammo, NULL, Drop_Ammo, NULL, "misc/am_pkup.wav", "models/items/ammo/bullets/medium/tris.md2", 0, NULL, "a_bullets", "Bullets", 3, 50, NULL, IT_AMMO, 0, NULL, AMMO_BULLETS, "" }, /* QUAKED ammo_cells (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "ammo_cells", Pickup_Ammo, NULL, Drop_Ammo, NULL, "misc/am_pkup.wav", "models/items/ammo/cells/medium/tris.md2", 0, NULL, "a_cells", "Cells", 3, 50, NULL, IT_AMMO, 0, NULL, AMMO_CELLS, "" }, /* QUAKED ammo_rockets (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "ammo_rockets", Pickup_Ammo, NULL, Drop_Ammo, NULL, "misc/am_pkup.wav", "models/items/ammo/rockets/medium/tris.md2", 0, NULL, "a_rockets", "Rockets", 3, 5, NULL, IT_AMMO, 0, NULL, AMMO_ROCKETS, "" }, /* QUAKED ammo_slugs (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "ammo_slugs", Pickup_Ammo, NULL, Drop_Ammo, NULL, "misc/am_pkup.wav", "models/items/ammo/slugs/medium/tris.md2", 0, NULL, "a_slugs", "Slugs", 3, 10, NULL, IT_AMMO, 0, NULL, AMMO_SLUGS, "" }, /* QUAKED item_quad (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "item_quad", Pickup_Powerup, Use_Quad, Drop_General, NULL, "items/pkup.wav", "models/items/quaddama/tris.md2", EF_ROTATE, NULL, "p_quad", "Quad Damage", 2, 60, NULL, IT_POWERUP, 0, NULL, 0, "items/damage.wav items/damage2.wav items/damage3.wav" }, /* QUAKED item_invulnerability (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "item_invulnerability", Pickup_Powerup, Use_Invulnerability, Drop_General, NULL, "items/pkup.wav", "models/items/invulner/tris.md2", EF_ROTATE, NULL, "p_invulnerability", "Invulnerability", 2, 300, NULL, IT_POWERUP, 0, NULL, 0, "items/protect.wav items/protect2.wav items/protect4.wav" }, /* QUAKED item_silencer (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "item_silencer", Pickup_Powerup, Use_Silencer, Drop_General, NULL, "items/pkup.wav", "models/items/silencer/tris.md2", EF_ROTATE, NULL, "p_silencer", "Silencer", 2, 60, NULL, IT_POWERUP, 0, NULL, 0, "" }, /* QUAKED item_breather (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "item_breather", Pickup_Powerup, Use_Breather, Drop_General, NULL, "items/pkup.wav", "models/items/breather/tris.md2", EF_ROTATE, NULL, "p_rebreather", "Rebreather", 2, 60, NULL, IT_STAY_COOP | IT_POWERUP, 0, NULL, 0, "items/airout.wav" }, /* QUAKED item_enviro (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "item_enviro", Pickup_Powerup, Use_Envirosuit, Drop_General, NULL, "items/pkup.wav", "models/items/enviro/tris.md2", EF_ROTATE, NULL, "p_envirosuit", "Environment Suit", 2, 60, NULL, IT_STAY_COOP | IT_POWERUP, 0, NULL, 0, "items/airout.wav" }, /* QUAKED item_ancient_head (.3 .3 1) (-16 -16 -16) (16 16 16) Special item that gives +2 to maximum health */ { "item_ancient_head", Pickup_AncientHead, NULL, NULL, NULL, "items/pkup.wav", "models/items/c_head/tris.md2", EF_ROTATE, NULL, "i_fixme", "Ancient Head", 2, 60, NULL, 0, 0, NULL, 0, "" }, /* QUAKED item_adrenaline (.3 .3 1) (-16 -16 -16) (16 16 16) gives +1 to maximum health */ { "item_adrenaline", Pickup_Adrenaline, NULL, NULL, NULL, "items/pkup.wav", "models/items/adrenal/tris.md2", EF_ROTATE, NULL, "p_adrenaline", "Adrenaline", 2, 60, NULL, 0, 0, NULL, 0, "" }, /* QUAKED item_bandolier (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "item_bandolier", Pickup_Bandolier, NULL, NULL, NULL, "items/pkup.wav", "models/items/band/tris.md2", EF_ROTATE, NULL, "p_bandolier", "Bandolier", 2, 60, NULL, 0, 0, NULL, 0, "" }, /* QUAKED item_pack (.3 .3 1) (-16 -16 -16) (16 16 16) */ { "item_pack", Pickup_Pack, NULL, NULL, NULL, "items/pkup.wav", "models/items/pack/tris.md2", EF_ROTATE, NULL, "i_pack", "Ammo Pack", 2, 180, NULL, 0, 0, NULL, 0, "" }, /* QUAKED key_data_cd (0 .5 .8) (-16 -16 -16) (16 16 16) key for computer centers */ { "key_data_cd", Pickup_Key, NULL, Drop_General, NULL, "items/pkup.wav", "models/items/keys/data_cd/tris.md2", EF_ROTATE, NULL, "k_datacd", "Data CD", 2, 0, NULL, IT_STAY_COOP | IT_KEY, 0, NULL, 0, "" }, /* QUAKED key_power_cube (0 .5 .8) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN NO_TOUCH warehouse circuits */ { "key_power_cube", Pickup_Key, NULL, Drop_General, NULL, "items/pkup.wav", "models/items/keys/power/tris.md2", EF_ROTATE, NULL, "k_powercube", "Power Cube", 2, 0, NULL, IT_STAY_COOP | IT_KEY, 0, NULL, 0, "" }, /* QUAKED key_pyramid (0 .5 .8) (-16 -16 -16) (16 16 16) key for the entrance of jail3 */ { "key_pyramid", Pickup_Key, NULL, Drop_General, NULL, "items/pkup.wav", "models/items/keys/pyramid/tris.md2", EF_ROTATE, NULL, "k_pyramid", "Pyramid Key", 2, 0, NULL, IT_STAY_COOP | IT_KEY, 0, NULL, 0, "" }, /* QUAKED key_data_spinner (0 .5 .8) (-16 -16 -16) (16 16 16) key for the city computer */ { "key_data_spinner", Pickup_Key, NULL, Drop_General, NULL, "items/pkup.wav", "models/items/keys/spinner/tris.md2", EF_ROTATE, NULL, "k_dataspin", "Data Spinner", 2, 0, NULL, IT_STAY_COOP | IT_KEY, 0, NULL, 0, "" }, /* QUAKED key_pass (0 .5 .8) (-16 -16 -16) (16 16 16) security pass for the security level */ { "key_pass", Pickup_Key, NULL, Drop_General, NULL, "items/pkup.wav", "models/items/keys/pass/tris.md2", EF_ROTATE, NULL, "k_security", "Security Pass", 2, 0, NULL, IT_STAY_COOP | IT_KEY, 0, NULL, 0, "" }, /* QUAKED key_blue_key (0 .5 .8) (-16 -16 -16) (16 16 16) normal door key - blue */ { "key_blue_key", Pickup_Key, NULL, Drop_General, NULL, "items/pkup.wav", "models/items/keys/key/tris.md2", EF_ROTATE, NULL, "k_bluekey", "Blue Key", 2, 0, NULL, IT_STAY_COOP | IT_KEY, 0, NULL, 0, "" }, /* QUAKED key_red_key (0 .5 .8) (-16 -16 -16) (16 16 16) normal door key - red */ { "key_red_key", Pickup_Key, NULL, Drop_General, NULL, "items/pkup.wav", "models/items/keys/red_key/tris.md2", EF_ROTATE, NULL, "k_redkey", "Red Key", 2, 0, NULL, IT_STAY_COOP | IT_KEY, 0, NULL, 0, "" }, /* QUAKED key_commander_head (0 .5 .8) (-16 -16 -16) (16 16 16) tank commander's head */ { "key_commander_head", Pickup_Key, NULL, Drop_General, NULL, "items/pkup.wav", "models/monsters/commandr/head/tris.md2", EF_GIB, NULL, "k_comhead", "Commander's Head", 2, 0, NULL, IT_STAY_COOP | IT_KEY, 0, NULL, 0, "" }, /* QUAKED key_airstrike_target (0 .5 .8) (-16 -16 -16) (16 16 16) */ { "key_airstrike_target", Pickup_Key, NULL, Drop_General, NULL, "items/pkup.wav", "models/items/keys/target/tris.md2", EF_ROTATE, NULL, "i_airstrike", "Airstrike Marker", 2, 0, NULL, IT_STAY_COOP | IT_KEY, 0, NULL, 0, "" }, { NULL, Pickup_Health, NULL, NULL, NULL, "items/pkup.wav", NULL, 0, NULL, "i_health", "Health", 3, 0, NULL, 0, 0, NULL, 0, "items/s_health.wav items/n_health.wav items/l_health.wav items/m_health.wav" }, /* end of list marker */ {NULL} }; /* * QUAKED item_health (.3 .3 1) (-16 -16 -16) (16 16 16) */ void SP_item_health(edict_t *self) { if (!self) { return; } if (deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH)) { G_FreeEdict(self); return; } self->model = "models/items/healing/medium/tris.md2"; self->count = 10; SpawnItem(self, FindItem("Health")); gi.soundindex("items/n_health.wav"); } /* * QUAKED item_health_small (.3 .3 1) (-16 -16 -16) (16 16 16) */ void SP_item_health_small(edict_t *self) { if (!self) { return; } if (deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH)) { G_FreeEdict(self); return; } self->model = "models/items/healing/stimpack/tris.md2"; self->count = 2; SpawnItem(self, FindItem("Health")); self->style = HEALTH_IGNORE_MAX; gi.soundindex("items/s_health.wav"); } /* * QUAKED item_health_large (.3 .3 1) (-16 -16 -16) (16 16 16) */ void SP_item_health_large(edict_t *self) { if (!self) { return; } if (deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH)) { G_FreeEdict(self); return; } self->model = "models/items/healing/large/tris.md2"; self->count = 25; SpawnItem(self, FindItem("Health")); gi.soundindex("items/l_health.wav"); } /* * QUAKED item_health_mega (.3 .3 1) (-16 -16 -16) (16 16 16) */ void SP_item_health_mega(edict_t *self) { if (!self) { return; } if (deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH)) { G_FreeEdict(self); return; } self->model = "models/items/mega_h/tris.md2"; self->count = 100; SpawnItem(self, FindItem("Health")); gi.soundindex("items/m_health.wav"); self->style = HEALTH_IGNORE_MAX | HEALTH_TIMED; } void InitItems(void) { game.num_items = sizeof(itemlist) / sizeof(itemlist[0]) - 1; } /* * Called by worldspawn */ void SetItemNames(void) { int i; gitem_t *it; for (i = 0; i < game.num_items; i++) { it = &itemlist[i]; gi.configstring(CS_ITEMS + i, it->pickup_name); } jacket_armor_index = ITEM_INDEX(FindItem("Jacket Armor")); combat_armor_index = ITEM_INDEX(FindItem("Combat Armor")); body_armor_index = ITEM_INDEX(FindItem("Body Armor")); power_screen_index = ITEM_INDEX(FindItem("Power Screen")); power_shield_index = ITEM_INDEX(FindItem("Power Shield")); } yquake2-QUAKE2_5_32/src/game/g_utils.c0000644000175000017500000002666012615162034020143 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Misc. utility functions for the game logic. * * ======================================================================= */ #include "header/local.h" #define MAXCHOICES 8 void G_ProjectSource(vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result) { result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1]; result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1]; result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + distance[2]; } /* * Searches all active entities for the next * one that holds the matching string at fieldofs * (use the FOFS() macro) in the structure. * * Searches beginning at the edict after from, or * the beginning. If NULL, NULL will be returned * if the end of the list is reached. */ edict_t * G_Find(edict_t *from, int fieldofs, char *match) { char *s; if (!from) { from = g_edicts; } else { from++; } if (!match) { return NULL; } for ( ; from < &g_edicts[globals.num_edicts]; from++) { if (!from->inuse) { continue; } s = *(char **)((byte *)from + fieldofs); if (!s) { continue; } if (!Q_stricmp(s, match)) { return from; } } return NULL; } /* * Returns entities that have origins * within a spherical area */ edict_t * findradius(edict_t *from, vec3_t org, float rad) { vec3_t eorg; int j; if (!from) { from = g_edicts; } else { from++; } for ( ; from < &g_edicts[globals.num_edicts]; from++) { if (!from->inuse) { continue; } if (from->solid == SOLID_NOT) { continue; } for (j = 0; j < 3; j++) { eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j]) * 0.5); } if (VectorLength(eorg) > rad) { continue; } return from; } return NULL; } /* * Searches all active entities for * the next one that holds the matching * string at fieldofs (use the FOFS() macro) * in the structure. * * Searches beginning at the edict after from, * or the beginning. If NULL, NULL will be * returned if the end of the list is reached. */ edict_t * G_PickTarget(char *targetname) { edict_t *ent = NULL; int num_choices = 0; edict_t *choice[MAXCHOICES]; if (!targetname) { gi.dprintf("G_PickTarget called with NULL targetname\n"); return NULL; } while (1) { ent = G_Find(ent, FOFS(targetname), targetname); if (!ent) { break; } choice[num_choices++] = ent; if (num_choices == MAXCHOICES) { break; } } if (!num_choices) { gi.dprintf("G_PickTarget: target %s not found\n", targetname); return NULL; } return choice[randk() % num_choices]; } void Think_Delay(edict_t *ent) { if (!ent) { return; } G_UseTargets(ent, ent->activator); G_FreeEdict(ent); } /* * The global "activator" should be set to * the entity that initiated the firing. * * If self.delay is set, a DelayedUse entity * will be created that will actually do the * SUB_UseTargets after that many seconds have passed. * * Centerprints any self.message to the activator. * * Search for (string)targetname in all entities that * match (string)self.target and call their .use function */ void G_UseTargets(edict_t *ent, edict_t *activator) { edict_t *t; if (!ent || !activator) { return; } /* check for a delay */ if (ent->delay) { /* create a temp object to fire at a later time */ t = G_Spawn(); t->classname = "DelayedUse"; t->nextthink = level.time + ent->delay; t->think = Think_Delay; t->activator = activator; if (!activator) { gi.dprintf("Think_Delay with no activator\n"); } t->message = ent->message; t->target = ent->target; t->killtarget = ent->killtarget; return; } /* print the message */ if ((ent->message) && !(activator->svflags & SVF_MONSTER)) { gi.centerprintf(activator, "%s", ent->message); if (ent->noise_index) { gi.sound(activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0); } else { gi.sound(activator, CHAN_AUTO, gi.soundindex( "misc/talk1.wav"), 1, ATTN_NORM, 0); } } /* kill killtargets */ if (ent->killtarget) { t = NULL; while ((t = G_Find(t, FOFS(targetname), ent->killtarget))) { /* decrement secret count if target_secret is removed */ if (!Q_stricmp(t->classname,"target_secret")) { level.total_secrets--; } /* same deal with target_goal, but also turn off CD music if applicable */ else if (!Q_stricmp(t->classname,"target_goal")) { level.total_goals--; if (level.found_goals >= level.total_goals) { gi.configstring (CS_CDTRACK, "0"); } } G_FreeEdict(t); if (!ent->inuse) { gi.dprintf("entity was removed while using killtargets\n"); return; } } } /* fire targets */ if (ent->target) { t = NULL; while ((t = G_Find(t, FOFS(targetname), ent->target))) { /* doors fire area portals in a specific way */ if (!Q_stricmp(t->classname, "func_areaportal") && (!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating"))) { continue; } if (t == ent) { gi.dprintf("WARNING: Entity used itself.\n"); } else { if (t->use) { t->use(t, ent, activator); } } if (!ent->inuse) { gi.dprintf("entity was removed while using targets\n"); return; } } } } /* * This is just a convenience function * for making temporary vectors for function calls */ float * tv(float x, float y, float z) { static int index; static vec3_t vecs[8]; float *v; /* use an array so that multiple tempvectors won't collide for a while */ v = vecs[index]; index = (index + 1) & 7; v[0] = x; v[1] = y; v[2] = z; return v; } /* * This is just a convenience function * for printing vectors */ char * vtos(vec3_t v) { static int index; static char str[8][32]; char *s; /* use an array so that multiple vtos won't collide */ s = str[index]; index = (index + 1) & 7; Com_sprintf(s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]); return s; } vec3_t VEC_UP = {0, -1, 0}; vec3_t MOVEDIR_UP = {0, 0, 1}; vec3_t VEC_DOWN = {0, -2, 0}; vec3_t MOVEDIR_DOWN = {0, 0, -1}; void G_SetMovedir(vec3_t angles, vec3_t movedir) { if (VectorCompare(angles, VEC_UP)) { VectorCopy(MOVEDIR_UP, movedir); } else if (VectorCompare(angles, VEC_DOWN)) { VectorCopy(MOVEDIR_DOWN, movedir); } else { AngleVectors(angles, movedir, NULL, NULL); } VectorClear(angles); } float vectoyaw(vec3_t vec) { float yaw; if (vec[PITCH] == 0) { yaw = 0; if (vec[YAW] > 0) { yaw = 90; } else if (vec[YAW] < 0) { yaw = -90; } } else { yaw = (int)(atan2(vec[YAW], vec[PITCH]) * 180 / M_PI); if (yaw < 0) { yaw += 360; } } return yaw; } void vectoangles(vec3_t value1, vec3_t angles) { float forward; float yaw, pitch; if ((value1[1] == 0) && (value1[0] == 0)) { yaw = 0; if (value1[2] > 0) { pitch = 90; } else { pitch = 270; } } else { if (value1[0]) { yaw = (int)(atan2(value1[1], value1[0]) * 180 / M_PI); } else if (value1[1] > 0) { yaw = 90; } else { yaw = -90; } if (yaw < 0) { yaw += 360; } forward = sqrt(value1[0] * value1[0] + value1[1] * value1[1]); pitch = (int)(atan2(value1[2], forward) * 180 / M_PI); if (pitch < 0) { pitch += 360; } } angles[PITCH] = -pitch; angles[YAW] = yaw; angles[ROLL] = 0; } char * G_CopyString(char *in) { char *out; out = gi.TagMalloc(strlen(in) + 1, TAG_LEVEL); strcpy(out, in); return out; } void G_InitEdict(edict_t *e) { e->inuse = true; e->classname = "noclass"; e->gravity = 1.0; e->s.number = e - g_edicts; } /* * 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 * G_Spawn(void) { int i; edict_t *e; e = &g_edicts[(int)maxclients->value + 1]; for (i = maxclients->value + 1; i < globals.num_edicts; i++, e++) { /* the first couple seconds of server time can involve a lot of freeing and allocating, so relax the replacement policy */ if (!e->inuse && ((e->freetime < 2) || (level.time - e->freetime > 0.5))) { G_InitEdict(e); return e; } } if (i == game.maxentities) { gi.error("ED_Alloc: no free edicts"); } globals.num_edicts++; G_InitEdict(e); return e; } /* * Marks the edict as free */ void G_FreeEdict(edict_t *ed) { gi.unlinkentity(ed); /* unlink from world */ if (deathmatch->value || coop->value) { if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE)) { return; } } else { if ((ed - g_edicts) <= maxclients->value) { return; } } memset(ed, 0, sizeof(*ed)); ed->classname = "freed"; ed->freetime = level.time; ed->inuse = false; } void G_TouchTriggers(edict_t *ent) { int i, num; edict_t *touch[MAX_EDICTS], *hit; if (!ent) { return; } /* dead things don't activate triggers! */ if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0)) { return; } num = gi.BoxEdicts(ent->absmin, ent->absmax, touch, MAX_EDICTS, AREA_TRIGGERS); /* be careful, it is possible to have an entity in this list removed before we get to it (killtriggered) */ for (i = 0; i < num; i++) { hit = touch[i]; if (!hit->inuse) { continue; } if (!hit->touch) { continue; } hit->touch(hit, ent, NULL, NULL); } } /* * Call after linking a new trigger * in during gameplay to force all * entities it covers to immediately * touch it */ void G_TouchSolids(edict_t *ent) { int i, num; edict_t *touch[MAX_EDICTS], *hit; if (!ent) { return; } num = gi.BoxEdicts(ent->absmin, ent->absmax, touch, MAX_EDICTS, AREA_SOLID); /* be careful, it is possible to have an entity in this list removed before we get to it (killtriggered) */ for (i = 0; i < num; i++) { hit = touch[i]; if (!hit->inuse) { continue; } if (ent->touch) { ent->touch(hit, ent, NULL, NULL); } if (!ent->inuse) { break; } } } /* * Kills all entities that would touch the * proposed new positioning of ent. Ent s * hould be unlinked before calling this! */ qboolean KillBox(edict_t *ent) { trace_t tr; if (!ent) { return false; } while (1) { tr = gi.trace(ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID); if (!tr.ent) { break; } /* nail it */ T_Damage(tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG); /* if we didn't kill it, fail */ if (tr.ent->solid) { return false; } } return true; /* all clear */ } yquake2-QUAKE2_5_32/src/game/g_cmds.c0000644000175000017500000004701512615162034017726 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Game command processing. * * ======================================================================= */ #include "header/local.h" #include "monster/misc/player.h" static char * ClientTeam(edict_t *ent, char* value) { char *p; value[0] = 0; if (!ent) { return value; } if (!ent->client) { return value; } strcpy(value, Info_ValueForKey(ent->client->pers.userinfo, "skin")); p = strchr(value, '/'); if (!p) { return value; } if ((int)(dmflags->value) & DF_MODELTEAMS) { *p = 0; return value; } return ++p; } qboolean OnSameTeam(edict_t *ent1, edict_t *ent2) { char ent1Team[512]; char ent2Team[512]; if (!ent1 || !ent2) { return false; } if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) { return false; } ClientTeam(ent1, ent1Team); ClientTeam(ent2, ent2Team); if (ent1Team[0] != '\0' && strcmp(ent1Team, ent2Team) == 0) { return true; } return false; } void SelectNextItem(edict_t *ent, int itflags) { gclient_t *cl; int i, index; gitem_t *it; if (!ent) { return; } cl = ent->client; if (cl->chase_target) { ChaseNext(ent); return; } /* scan for the next valid one */ for (i = 1; i <= MAX_ITEMS; i++) { index = (cl->pers.selected_item + i) % MAX_ITEMS; if (!cl->pers.inventory[index]) { continue; } it = &itemlist[index]; if (!it->use) { continue; } if (!(it->flags & itflags)) { continue; } cl->pers.selected_item = index; return; } cl->pers.selected_item = -1; } void SelectPrevItem(edict_t *ent, int itflags) { gclient_t *cl; int i, index; gitem_t *it; if (!ent) { return; } cl = ent->client; if (cl->chase_target) { ChasePrev(ent); return; } /* scan for the next valid one */ for (i = 1; i <= MAX_ITEMS; i++) { index = (cl->pers.selected_item + MAX_ITEMS - i) % MAX_ITEMS; if (!cl->pers.inventory[index]) { continue; } it = &itemlist[index]; if (!it->use) { continue; } if (!(it->flags & itflags)) { continue; } cl->pers.selected_item = index; return; } cl->pers.selected_item = -1; } void ValidateSelectedItem(edict_t *ent) { gclient_t *cl; if (!ent) { return; } cl = ent->client; if (cl->pers.inventory[cl->pers.selected_item]) { return; /* valid */ } SelectNextItem(ent, -1); } /* ================================================================================= */ /* * Give items to a client */ void Cmd_Give_f(edict_t *ent) { char *name; gitem_t *it; int index; int i; qboolean give_all; edict_t *it_ent; if (!ent) { return; } if ((deathmatch->value || coop->value) && !sv_cheats->value) { gi.cprintf( ent, PRINT_HIGH, "You must run the server with '+set cheats 1' to enable this command.\n"); return; } name = gi.args(); if (Q_stricmp(name, "all") == 0) { give_all = true; } else { give_all = false; } if (give_all || (Q_stricmp(gi.argv(1), "health") == 0)) { if (gi.argc() == 3) { ent->health = (int)strtol(gi.argv(2), (char **)NULL, 10); } else { ent->health = ent->max_health; } if (!give_all) { return; } } if (give_all || (Q_stricmp(name, "weapons") == 0)) { for (i = 0; i < game.num_items; i++) { it = itemlist + i; if (!it->pickup) { continue; } if (!(it->flags & IT_WEAPON)) { continue; } ent->client->pers.inventory[i] += 1; } if (!give_all) { return; } } if (give_all || (Q_stricmp(name, "ammo") == 0)) { for (i = 0; i < game.num_items; i++) { it = itemlist + i; if (!it->pickup) { continue; } if (!(it->flags & IT_AMMO)) { continue; } Add_Ammo(ent, it, 1000); } if (!give_all) { return; } } if (give_all || (Q_stricmp(name, "armor") == 0)) { gitem_armor_t *info; it = FindItem("Jacket Armor"); ent->client->pers.inventory[ITEM_INDEX(it)] = 0; it = FindItem("Combat Armor"); ent->client->pers.inventory[ITEM_INDEX(it)] = 0; it = FindItem("Body Armor"); info = (gitem_armor_t *)it->info; ent->client->pers.inventory[ITEM_INDEX(it)] = info->max_count; if (!give_all) { return; } } if (give_all || (Q_stricmp(name, "Power Shield") == 0)) { it = FindItem("Power Shield"); it_ent = G_Spawn(); it_ent->classname = it->classname; SpawnItem(it_ent, it); Touch_Item(it_ent, ent, NULL, NULL); if (it_ent->inuse) { G_FreeEdict(it_ent); } if (!give_all) { return; } } if (give_all) { for (i = 0; i < game.num_items; i++) { it = itemlist + i; if (!it->pickup) { continue; } if (it->flags & (IT_ARMOR | IT_WEAPON | IT_AMMO)) { continue; } ent->client->pers.inventory[i] = 1; } return; } it = FindItem(name); if (!it) { name = gi.argv(1); it = FindItem(name); if (!it) { gi.cprintf(ent, PRINT_HIGH, "unknown item\n"); return; } } if (!it->pickup) { gi.cprintf(ent, PRINT_HIGH, "non-pickup item\n"); return; } index = ITEM_INDEX(it); if (it->flags & IT_AMMO) { if (gi.argc() == 3) { ent->client->pers.inventory[index] = (int)strtol(gi.argv(2), (char **)NULL, 10); } else { ent->client->pers.inventory[index] += it->quantity; } } else { it_ent = G_Spawn(); it_ent->classname = it->classname; SpawnItem(it_ent, it); Touch_Item(it_ent, ent, NULL, NULL); if (it_ent->inuse) { G_FreeEdict(it_ent); } } } /* * Sets client to godmode */ void Cmd_God_f(edict_t *ent) { char *msg; if (!ent) { return; } if ((deathmatch->value || coop->value) && !sv_cheats->value) { gi.cprintf( ent, PRINT_HIGH, "You must run the server with '+set cheats 1' to enable this command.\n"); return; } ent->flags ^= FL_GODMODE; if (!(ent->flags & FL_GODMODE)) { msg = "godmode OFF\n"; } else { msg = "godmode ON\n"; } gi.cprintf(ent, PRINT_HIGH, msg); } /* * Sets client to notarget */ void Cmd_Notarget_f(edict_t *ent) { char *msg; if (!ent) { return; } if ((deathmatch->value || coop->value) && !sv_cheats->value) { gi.cprintf( ent, PRINT_HIGH, "You must run the server with '+set cheats 1' to enable this command.\n"); return; } ent->flags ^= FL_NOTARGET; if (!(ent->flags & FL_NOTARGET)) { msg = "notarget OFF\n"; } else { msg = "notarget ON\n"; } gi.cprintf(ent, PRINT_HIGH, msg); } /* * argv(0) noclip */ void Cmd_Noclip_f(edict_t *ent) { char *msg; if (!ent) { return; } if ((deathmatch->value || coop->value) && !sv_cheats->value) { gi.cprintf( ent, PRINT_HIGH, "You must run the server with '+set cheats 1' to enable this command.\n"); return; } if (ent->movetype == MOVETYPE_NOCLIP) { ent->movetype = MOVETYPE_WALK; msg = "noclip OFF\n"; } else { ent->movetype = MOVETYPE_NOCLIP; msg = "noclip ON\n"; } gi.cprintf(ent, PRINT_HIGH, msg); } /* * Use an inventory item */ void Cmd_Use_f(edict_t *ent) { int index; gitem_t *it; char *s; if (!ent) { return; } s = gi.args(); it = FindItem(s); if (!it) { gi.cprintf(ent, PRINT_HIGH, "unknown item: %s\n", s); return; } if (!it->use) { gi.cprintf(ent, PRINT_HIGH, "Item is not usable.\n"); return; } index = ITEM_INDEX(it); if (!ent->client->pers.inventory[index]) { gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", s); return; } it->use(ent, it); } /* * Drop an inventory item */ void Cmd_Drop_f(edict_t *ent) { int index; gitem_t *it; char *s; if (!ent) { return; } s = gi.args(); it = FindItem(s); if (!it) { gi.cprintf(ent, PRINT_HIGH, "unknown item: %s\n", s); return; } if (!it->drop) { gi.cprintf(ent, PRINT_HIGH, "Item is not dropable.\n"); return; } index = ITEM_INDEX(it); if (!ent->client->pers.inventory[index]) { gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", s); return; } it->drop(ent, it); } void Cmd_Score_f(edict_t *ent) { if (!ent) { return; } ent->client->showinventory = false; ent->client->showhelp = false; if (!deathmatch->value && !coop->value) { return; } if (ent->client->showscores) { ent->client->showscores = false; return; } ent->client->showscores = true; DeathmatchScoreboardMessage(ent, ent->enemy); gi.unicast(ent, true); } void Cmd_Help_f(edict_t *ent) { if (!ent) { return; } /* this is for backwards compatibility */ if (deathmatch->value) { Cmd_Score_f(ent); return; } ent->client->showinventory = false; ent->client->showscores = false; if (ent->client->showhelp) { ent->client->showhelp = false; return; } ent->client->showhelp = true; ent->client->pers.helpchanged = 0; HelpComputerMessage(ent); gi.unicast(ent, true); } void Cmd_Inven_f(edict_t *ent) { gclient_t *cl; if (!ent) { return; } cl = ent->client; cl->showscores = false; cl->showhelp = false; if (cl->showinventory) { cl->showinventory = false; return; } cl->showinventory = true; InventoryMessage(ent); gi.unicast(ent, true); } void Cmd_InvUse_f(edict_t *ent) { gitem_t *it; if (!ent) { return; } ValidateSelectedItem(ent); if (ent->client->pers.selected_item == -1) { gi.cprintf(ent, PRINT_HIGH, "No item to use.\n"); return; } it = &itemlist[ent->client->pers.selected_item]; if (!it->use) { gi.cprintf(ent, PRINT_HIGH, "Item is not usable.\n"); return; } it->use(ent, it); } void Cmd_WeapPrev_f(edict_t *ent) { gclient_t *cl; int i, index; gitem_t *it; int selected_weapon; if (!ent) { return; } cl = ent->client; if (!cl->pers.weapon) { return; } selected_weapon = ITEM_INDEX(cl->pers.weapon); /* scan for the next valid one */ for (i = 1; i <= MAX_ITEMS; i++) { index = (selected_weapon + i) % MAX_ITEMS; if (!cl->pers.inventory[index]) { continue; } it = &itemlist[index]; if (!it->use) { continue; } if (!(it->flags & IT_WEAPON)) { continue; } it->use(ent, it); if (cl->pers.weapon == it) { return; /* successful */ } } } void Cmd_WeapNext_f(edict_t *ent) { gclient_t *cl; int i, index; gitem_t *it; int selected_weapon; if (!ent) { return; } cl = ent->client; if (!cl->pers.weapon) { return; } selected_weapon = ITEM_INDEX(cl->pers.weapon); /* scan for the next valid one */ for (i = 1; i <= MAX_ITEMS; i++) { index = (selected_weapon + MAX_ITEMS - i) % MAX_ITEMS; if (!cl->pers.inventory[index]) { continue; } it = &itemlist[index]; if (!it->use) { continue; } if (!(it->flags & IT_WEAPON)) { continue; } it->use(ent, it); if (cl->pers.weapon == it) { return; /* successful */ } } } void Cmd_WeapLast_f(edict_t *ent) { gclient_t *cl; int index; gitem_t *it; if (!ent) { return; } cl = ent->client; if (!cl->pers.weapon || !cl->pers.lastweapon) { return; } index = ITEM_INDEX(cl->pers.lastweapon); if (!cl->pers.inventory[index]) { return; } it = &itemlist[index]; if (!it->use) { return; } if (!(it->flags & IT_WEAPON)) { return; } it->use(ent, it); } void Cmd_InvDrop_f(edict_t *ent) { gitem_t *it; if (!ent) { return; } ValidateSelectedItem(ent); if (ent->client->pers.selected_item == -1) { gi.cprintf(ent, PRINT_HIGH, "No item to drop.\n"); return; } it = &itemlist[ent->client->pers.selected_item]; if (!it->drop) { gi.cprintf(ent, PRINT_HIGH, "Item is not dropable.\n"); return; } it->drop(ent, it); } void Cmd_Kill_f(edict_t *ent) { if (!ent) { return; } if (((level.time - ent->client->respawn_time) < 5) || (ent->client->resp.spectator)) { return; } ent->flags &= ~FL_GODMODE; ent->health = 0; meansOfDeath = MOD_SUICIDE; player_die(ent, ent, ent, 100000, vec3_origin); } void Cmd_PutAway_f(edict_t *ent) { if (!ent) { return; } ent->client->showscores = false; ent->client->showhelp = false; ent->client->showinventory = false; } int PlayerSort(void const *a, void const *b) { int anum, bnum; if (!a || !b) { return 0; } anum = *(int *)a; bnum = *(int *)b; anum = game.clients[anum].ps.stats[STAT_FRAGS]; bnum = game.clients[bnum].ps.stats[STAT_FRAGS]; if (anum < bnum) { return -1; } if (anum > bnum) { return 1; } return 0; } void Cmd_Players_f(edict_t *ent) { int i; int count; char small[64]; char large[1280]; int index[256]; if (!ent) { return; } count = 0; for (i = 0; i < maxclients->value; i++) { if (game.clients[i].pers.connected) { index[count] = i; count++; } } /* sort by frags */ qsort(index, count, sizeof(index[0]), PlayerSort); /* print information */ large[0] = 0; for (i = 0; i < count; i++) { Com_sprintf(small, sizeof(small), "%3i %s\n", game.clients[index[i]].ps.stats[STAT_FRAGS], game.clients[index[i]].pers.netname); if (strlen(small) + strlen(large) > sizeof(large) - 100) { /* can't print all of them in one packet */ strcat(large, "...\n"); break; } strcat(large, small); } gi.cprintf(ent, PRINT_HIGH, "%s\n%i players\n", large, count); } void Cmd_Wave_f(edict_t *ent) { int i; if (!ent) { return; } i = (int)strtol(gi.argv(1), (char **)NULL, 10); /* can't wave when ducked */ if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { return; } if (ent->client->anim_priority > ANIM_WAVE) { return; } ent->client->anim_priority = ANIM_WAVE; switch (i) { case 0: gi.cprintf(ent, PRINT_HIGH, "flipoff\n"); ent->s.frame = FRAME_flip01 - 1; ent->client->anim_end = FRAME_flip12; break; case 1: gi.cprintf(ent, PRINT_HIGH, "salute\n"); ent->s.frame = FRAME_salute01 - 1; ent->client->anim_end = FRAME_salute11; break; case 2: gi.cprintf(ent, PRINT_HIGH, "taunt\n"); ent->s.frame = FRAME_taunt01 - 1; ent->client->anim_end = FRAME_taunt17; break; case 3: gi.cprintf(ent, PRINT_HIGH, "wave\n"); ent->s.frame = FRAME_wave01 - 1; ent->client->anim_end = FRAME_wave11; break; case 4: default: gi.cprintf(ent, PRINT_HIGH, "point\n"); ent->s.frame = FRAME_point01 - 1; ent->client->anim_end = FRAME_point12; break; } } void Cmd_Say_f(edict_t *ent, qboolean team, qboolean arg0) { int i, j; edict_t *other; char *p; char text[2048]; gclient_t *cl; if (!ent) { return; } if ((gi.argc() < 2) && !arg0) { return; } if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) { team = false; } if (team) { Com_sprintf(text, sizeof(text), "(%s): ", ent->client->pers.netname); } else { Com_sprintf(text, sizeof(text), "%s: ", ent->client->pers.netname); } if (arg0) { strcat(text, gi.argv(0)); strcat(text, " "); strcat(text, gi.args()); } else { p = gi.args(); if (*p == '"') { p++; p[strlen(p) - 1] = 0; } strcat(text, p); } /* don't let text be too long for malicious reasons */ if (strlen(text) > 150) { text[150] = 0; } strcat(text, "\n"); if (flood_msgs->value) { cl = ent->client; if (level.time < cl->flood_locktill) { gi.cprintf(ent, PRINT_HIGH, "You can't talk for %d more seconds\n", (int)(cl->flood_locktill - level.time)); return; } i = cl->flood_whenhead - flood_msgs->value + 1; if (i < 0) { i = (sizeof(cl->flood_when) / sizeof(cl->flood_when[0])) + i; } if (cl->flood_when[i] && (level.time - cl->flood_when[i] < flood_persecond->value)) { cl->flood_locktill = level.time + flood_waitdelay->value; gi.cprintf(ent, PRINT_CHAT, "Flood protection: You can't talk for %d seconds.\n", (int)flood_waitdelay->value); return; } cl->flood_whenhead = (cl->flood_whenhead + 1) % (sizeof(cl->flood_when) / sizeof(cl->flood_when[0])); cl->flood_when[cl->flood_whenhead] = level.time; } if (dedicated->value) { gi.cprintf(NULL, PRINT_CHAT, "%s", text); } for (j = 1; j <= game.maxclients; j++) { other = &g_edicts[j]; if (!other->inuse) { continue; } if (!other->client) { continue; } if (team) { if (!OnSameTeam(ent, other)) { continue; } } gi.cprintf(other, PRINT_CHAT, "%s", text); } } void Cmd_PlayerList_f(edict_t *ent) { int i; char st[80]; char text[1400]; edict_t *e2; if (!ent) { return; } /* connect time, ping, score, name */ *text = 0; for (i = 0, e2 = g_edicts + 1; i < maxclients->value; i++, e2++) { if (!e2->inuse) { continue; } Com_sprintf(st, sizeof(st), "%02d:%02d %4d %3d %s%s\n", (level.framenum - e2->client->resp.enterframe) / 600, ((level.framenum - e2->client->resp.enterframe) % 600) / 10, e2->client->ping, e2->client->resp.score, e2->client->pers.netname, e2->client->resp.spectator ? " (spectator)" : ""); if (strlen(text) + strlen(st) > sizeof(text) - 50) { strcpy(text + strlen(text), "And more...\n"); gi.cprintf(ent, PRINT_HIGH, "%s", text); return; } strcat(text, st); } gi.cprintf(ent, PRINT_HIGH, "%s", text); } void ClientCommand(edict_t *ent) { char *cmd; if (!ent) { return; } if (!ent->client) { return; /* not fully in game yet */ } cmd = gi.argv(0); if (Q_stricmp(cmd, "players") == 0) { Cmd_Players_f(ent); return; } if (Q_stricmp(cmd, "say") == 0) { Cmd_Say_f(ent, false, false); return; } if (Q_stricmp(cmd, "say_team") == 0) { Cmd_Say_f(ent, true, false); return; } if (Q_stricmp(cmd, "score") == 0) { Cmd_Score_f(ent); return; } if (Q_stricmp(cmd, "help") == 0) { Cmd_Help_f(ent); return; } if (level.intermissiontime) { return; } if (Q_stricmp(cmd, "use") == 0) { Cmd_Use_f(ent); } else if (Q_stricmp(cmd, "drop") == 0) { Cmd_Drop_f(ent); } else if (Q_stricmp(cmd, "give") == 0) { Cmd_Give_f(ent); } else if (Q_stricmp(cmd, "god") == 0) { Cmd_God_f(ent); } else if (Q_stricmp(cmd, "notarget") == 0) { Cmd_Notarget_f(ent); } else if (Q_stricmp(cmd, "noclip") == 0) { Cmd_Noclip_f(ent); } else if (Q_stricmp(cmd, "inven") == 0) { Cmd_Inven_f(ent); } else if (Q_stricmp(cmd, "invnext") == 0) { SelectNextItem(ent, -1); } else if (Q_stricmp(cmd, "invprev") == 0) { SelectPrevItem(ent, -1); } else if (Q_stricmp(cmd, "invnextw") == 0) { SelectNextItem(ent, IT_WEAPON); } else if (Q_stricmp(cmd, "invprevw") == 0) { SelectPrevItem(ent, IT_WEAPON); } else if (Q_stricmp(cmd, "invnextp") == 0) { SelectNextItem(ent, IT_POWERUP); } else if (Q_stricmp(cmd, "invprevp") == 0) { SelectPrevItem(ent, IT_POWERUP); } else if (Q_stricmp(cmd, "invuse") == 0) { Cmd_InvUse_f(ent); } else if (Q_stricmp(cmd, "invdrop") == 0) { Cmd_InvDrop_f(ent); } else if (Q_stricmp(cmd, "weapprev") == 0) { Cmd_WeapPrev_f(ent); } else if (Q_stricmp(cmd, "weapnext") == 0) { Cmd_WeapNext_f(ent); } else if (Q_stricmp(cmd, "weaplast") == 0) { Cmd_WeapLast_f(ent); } else if (Q_stricmp(cmd, "kill") == 0) { Cmd_Kill_f(ent); } else if (Q_stricmp(cmd, "putaway") == 0) { Cmd_PutAway_f(ent); } else if (Q_stricmp(cmd, "wave") == 0) { Cmd_Wave_f(ent); } else if (Q_stricmp(cmd, "playerlist") == 0) { Cmd_PlayerList_f(ent); } else /* anything that doesn't match a command will be a chat */ { Cmd_Say_f(ent, false, true); } } yquake2-QUAKE2_5_32/src/game/header/0000755000175000017500000000000012615162034017547 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/game/header/game.h0000644000175000017500000001747712615162034020651 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Here are the client, server and game are tied together. * * ======================================================================= */ /* * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * * THIS FILE IS _VERY_ FRAGILE AND THERE'S NOTHING IN IT THAT CAN OR * MUST BE CHANGED. IT'S MOST LIKELY A VERY GOOD IDEA TO CLOSE THE * EDITOR NOW AND NEVER LOOK BACK. OTHERWISE YOU MAY SCREW UP EVERYTHING! * * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ #define GAME_API_VERSION 3 #define SVF_NOCLIENT 0x00000001 /* don't send entity to clients, even if it has effects */ #define SVF_DEADMONSTER 0x00000002 /* treat as CONTENTS_DEADMONSTER for collision */ #define SVF_MONSTER 0x00000004 /* treat as CONTENTS_MONSTER for collision */ #define MAX_ENT_CLUSTERS 16 typedef enum { SOLID_NOT, /* no interaction with other objects */ SOLID_TRIGGER, /* only touch when inside, after moving */ SOLID_BBOX, /* touch on edge */ SOLID_BSP /* bsp clip, touch on edge */ } solid_t; /* =============================================================== */ /* link_t is only used for entity area links now */ typedef struct link_s { struct link_s *prev, *next; } link_t; typedef struct edict_s edict_t; typedef struct gclient_s gclient_t; #ifndef GAME_INCLUDE struct gclient_s { player_state_t ps; /* communicated by server to clients */ int ping; /* the game dll can add anything it wants after this point in the structure */ }; struct edict_s { entity_state_t s; struct gclient_s *client; qboolean inuse; int linkcount; link_t area; /* linked to a division node or leaf */ int num_clusters; /* if -1, use headnode instead */ int clusternums[MAX_ENT_CLUSTERS]; int headnode; /* unused if num_clusters != -1 */ int areanum, areanum2; int svflags; /* SVF_NOCLIENT, SVF_DEADMONSTER, SVF_MONSTER, etc */ vec3_t mins, maxs; vec3_t absmin, absmax, size; solid_t solid; int clipmask; edict_t *owner; /* the game dll can add anything it wants after this point in the structure */ }; #endif /* GAME_INCLUDE */ /* =============================================================== */ /* functions provided by the main engine */ typedef struct { /* special messages */ void (*bprintf)(int printlevel, char *fmt, ...); void (*dprintf)(char *fmt, ...); void (*cprintf)(edict_t *ent, int printlevel, char *fmt, ...); void (*centerprintf)(edict_t *ent, char *fmt, ...); void (*sound)(edict_t *ent, int channel, int soundindex, float volume, float attenuation, float timeofs); void (*positioned_sound)(vec3_t origin, edict_t *ent, int channel, int soundinedex, float volume, float attenuation, float timeofs); /* config strings hold all the index strings, the lightstyles, and misc data like the sky definition and cdtrack. All of the current configstrings are sent to clients when they connect, and changes are sent to all connected clients. */ void (*configstring)(int num, char *string); void (*error)(char *fmt, ...); /* the *index functions create configstrings and some internal server state */ int (*modelindex)(char *name); int (*soundindex)(char *name); int (*imageindex)(char *name); void (*setmodel)(edict_t *ent, char *name); /* collision detection */ trace_t (*trace)(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *passent, int contentmask); int (*pointcontents)(vec3_t point); qboolean (*inPVS)(vec3_t p1, vec3_t p2); qboolean (*inPHS)(vec3_t p1, vec3_t p2); void (*SetAreaPortalState)(int portalnum, qboolean open); qboolean (*AreasConnected)(int area1, int area2); /* an entity will never be sent to a client or used for collision if it is not passed to linkentity. If the size, position, or solidity changes, it must be relinked. */ void (*linkentity)(edict_t *ent); void (*unlinkentity)(edict_t *ent); /* call before removing an interactive edict */ int (*BoxEdicts)(vec3_t mins, vec3_t maxs, edict_t **list, int maxcount, int areatype); void (*Pmove)(pmove_t *pmove); /* player movement code common with client prediction */ /* network messaging */ void (*multicast)(vec3_t origin, multicast_t to); void (*unicast)(edict_t *ent, qboolean reliable); void (*WriteChar)(int c); void (*WriteByte)(int c); void (*WriteShort)(int c); void (*WriteLong)(int c); void (*WriteFloat)(float f); void (*WriteString)(char *s); void (*WritePosition)(vec3_t pos); /* some fractional bits */ void (*WriteDir)(vec3_t pos); /* single byte encoded, very coarse */ void (*WriteAngle)(float f); /* managed memory allocation */ void *(*TagMalloc)(int size, int tag); void (*TagFree)(void *block); void (*FreeTags)(int tag); /* console variable interaction */ cvar_t *(*cvar)(char *var_name, char *value, int flags); cvar_t *(*cvar_set)(char *var_name, char *value); cvar_t *(*cvar_forceset)(char *var_name, char *value); /* ClientCommand and ServerCommand parameter access */ int (*argc)(void); char *(*argv)(int n); char *(*args)(void); /* concatenation of all argv >= 1 */ /* add commands to the server console as if they were typed in for map changing, etc */ void (*AddCommandString)(char *text); void (*DebugGraph)(float value, int color); } game_import_t; /* functions exported by the game subsystem */ typedef struct { int apiversion; /* the init function will only be called when a game starts, not each time a level is loaded. Persistant data for clients and the server can be allocated in init */ void (*Init)(void); void (*Shutdown)(void); /* each new level entered will cause a call to SpawnEntities */ void (*SpawnEntities)(char *mapname, char *entstring, char *spawnpoint); /* Read/Write Game is for storing persistant cross level information about the world state and the clients. WriteGame is called every time a level is exited. ReadGame is called on a loadgame. */ void (*WriteGame)(char *filename, qboolean autosave); void (*ReadGame)(char *filename); /* ReadLevel is called after the default map information has been loaded with SpawnEntities */ void (*WriteLevel)(char *filename); void (*ReadLevel)(char *filename); qboolean (*ClientConnect)(edict_t *ent, char *userinfo); void (*ClientBegin)(edict_t *ent); void (*ClientUserinfoChanged)(edict_t *ent, char *userinfo); void (*ClientDisconnect)(edict_t *ent); void (*ClientCommand)(edict_t *ent); void (*ClientThink)(edict_t *ent, usercmd_t *cmd); void (*RunFrame)(void); /* ServerCommand will be called when an "sv " command is issued on the server console. The game can issue gi.argc() / gi.argv() commands to get the rest of the parameters */ void (*ServerCommand)(void); /* global variables shared between game and server */ /* The edict array is allocated in the game dll so it can vary in size from one game to another. The size will be fixed when ge->Init() is called */ struct edict_s *edicts; int edict_size; int num_edicts; /* current number, <= max_edicts */ int max_edicts; } game_export_t; yquake2-QUAKE2_5_32/src/game/header/local.h0000644000175000017500000006707712615162034021033 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Main header file for the game module. * * ======================================================================= */ #ifndef GAME_LOCAL_H #define GAME_LOCAL_H #include "../../common/header/shared.h" /* define GAME_INCLUDE so that game.h does not define the short, server-visible gclient_t and edict_t structures, because we define the full size ones in this file */ #define GAME_INCLUDE #include "game.h" /* the "gameversion" client command will print this plus compile date */ #define GAMEVERSION "baseq2" /* protocol bytes that can be directly added to messages */ #define svc_muzzleflash 1 #define svc_muzzleflash2 2 #define svc_temp_entity 3 #define svc_layout 4 #define svc_inventory 5 #define svc_stufftext 11 /* ================================================================== */ /* view pitching times */ #define DAMAGE_TIME 0.5 #define FALL_TIME 0.3 /* these are set with checkboxes on each entity in the map editor */ #define SPAWNFLAG_NOT_EASY 0x00000100 #define SPAWNFLAG_NOT_MEDIUM 0x00000200 #define SPAWNFLAG_NOT_HARD 0x00000400 #define SPAWNFLAG_NOT_DEATHMATCH 0x00000800 #define SPAWNFLAG_NOT_COOP 0x00001000 #define FL_FLY 0x00000001 #define FL_SWIM 0x00000002 /* implied immunity to drowining */ #define FL_IMMUNE_LASER 0x00000004 #define FL_INWATER 0x00000008 #define FL_GODMODE 0x00000010 #define FL_NOTARGET 0x00000020 #define FL_IMMUNE_SLIME 0x00000040 #define FL_IMMUNE_LAVA 0x00000080 #define FL_PARTIALGROUND 0x00000100 /* not all corners are valid */ #define FL_WATERJUMP 0x00000200 /* player jumping out of water */ #define FL_TEAMSLAVE 0x00000400 /* not the first on the team */ #define FL_NO_KNOCKBACK 0x00000800 #define FL_POWER_ARMOR 0x00001000 /* power armor (if any) is active */ #define FL_RESPAWN 0x80000000 /* used for item respawning */ #define FRAMETIME 0.1 /* memory tags to allow dynamic memory to be cleaned up */ #define TAG_GAME 765 /* clear when unloading the dll */ #define TAG_LEVEL 766 /* clear when loading a new level */ #define MELEE_DISTANCE 80 #define BODY_QUEUE_SIZE 8 typedef enum { DAMAGE_NO, DAMAGE_YES, /* will take damage if hit */ DAMAGE_AIM /* auto targeting recognizes this */ } damage_t; typedef enum { WEAPON_READY, WEAPON_ACTIVATING, WEAPON_DROPPING, WEAPON_FIRING } weaponstate_t; typedef enum { AMMO_BULLETS, AMMO_SHELLS, AMMO_ROCKETS, AMMO_GRENADES, AMMO_CELLS, AMMO_SLUGS } ammo_t; /* deadflag */ #define DEAD_NO 0 #define DEAD_DYING 1 #define DEAD_DEAD 2 #define DEAD_RESPAWNABLE 3 /* range */ #define RANGE_MELEE 0 #define RANGE_NEAR 1 #define RANGE_MID 2 #define RANGE_FAR 3 /* gib types */ #define GIB_ORGANIC 0 #define GIB_METALLIC 1 /* monster ai flags */ #define AI_STAND_GROUND 0x00000001 #define AI_TEMP_STAND_GROUND 0x00000002 #define AI_SOUND_TARGET 0x00000004 #define AI_LOST_SIGHT 0x00000008 #define AI_PURSUIT_LAST_SEEN 0x00000010 #define AI_PURSUE_NEXT 0x00000020 #define AI_PURSUE_TEMP 0x00000040 #define AI_HOLD_FRAME 0x00000080 #define AI_GOOD_GUY 0x00000100 #define AI_BRUTAL 0x00000200 #define AI_NOSTEP 0x00000400 #define AI_DUCKED 0x00000800 #define AI_COMBAT_POINT 0x00001000 #define AI_MEDIC 0x00002000 #define AI_RESURRECTING 0x00004000 /* monster attack state */ #define AS_STRAIGHT 1 #define AS_SLIDING 2 #define AS_MELEE 3 #define AS_MISSILE 4 /* armor types */ #define ARMOR_NONE 0 #define ARMOR_JACKET 1 #define ARMOR_COMBAT 2 #define ARMOR_BODY 3 #define ARMOR_SHARD 4 /* power armor types */ #define POWER_ARMOR_NONE 0 #define POWER_ARMOR_SCREEN 1 #define POWER_ARMOR_SHIELD 2 /* handedness values */ #define RIGHT_HANDED 0 #define LEFT_HANDED 1 #define CENTER_HANDED 2 /* game.serverflags values */ #define SFL_CROSS_TRIGGER_1 0x00000001 #define SFL_CROSS_TRIGGER_2 0x00000002 #define SFL_CROSS_TRIGGER_3 0x00000004 #define SFL_CROSS_TRIGGER_4 0x00000008 #define SFL_CROSS_TRIGGER_5 0x00000010 #define SFL_CROSS_TRIGGER_6 0x00000020 #define SFL_CROSS_TRIGGER_7 0x00000040 #define SFL_CROSS_TRIGGER_8 0x00000080 #define SFL_CROSS_TRIGGER_MASK 0x000000ff /* noise types for PlayerNoise */ #define PNOISE_SELF 0 #define PNOISE_WEAPON 1 #define PNOISE_IMPACT 2 /* edict->movetype values */ typedef enum { MOVETYPE_NONE, /* never moves */ MOVETYPE_NOCLIP, /* origin and angles change with no interaction */ MOVETYPE_PUSH, /* no clip to world, push on box contact */ MOVETYPE_STOP, /* no clip to world, stops on box contact */ MOVETYPE_WALK, /* gravity */ MOVETYPE_STEP, /* gravity, special edge handling */ MOVETYPE_FLY, MOVETYPE_TOSS, /* gravity */ MOVETYPE_FLYMISSILE, /* extra size to monsters */ MOVETYPE_BOUNCE } movetype_t; typedef struct { int base_count; int max_count; float normal_protection; float energy_protection; int armor; } gitem_armor_t; #define IT_WEAPON 1 /* use makes active weapon */ #define IT_AMMO 2 #define IT_ARMOR 4 #define IT_STAY_COOP 8 #define IT_KEY 16 #define IT_POWERUP 32 /* gitem_t->weapmodel for weapons indicates model index */ #define WEAP_BLASTER 1 #define WEAP_SHOTGUN 2 #define WEAP_SUPERSHOTGUN 3 #define WEAP_MACHINEGUN 4 #define WEAP_CHAINGUN 5 #define WEAP_GRENADES 6 #define WEAP_GRENADELAUNCHER 7 #define WEAP_ROCKETLAUNCHER 8 #define WEAP_HYPERBLASTER 9 #define WEAP_RAILGUN 10 #define WEAP_BFG 11 typedef struct gitem_s { char *classname; /* spawning name */ qboolean (*pickup)(struct edict_s *ent, struct edict_s *other); void (*use)(struct edict_s *ent, struct gitem_s *item); void (*drop)(struct edict_s *ent, struct gitem_s *item); void (*weaponthink)(struct edict_s *ent); char *pickup_sound; char *world_model; int world_model_flags; char *view_model; /* client side info */ char *icon; char *pickup_name; /* for printing on pickup */ int count_width; /* number of digits to display by icon */ int quantity; /* for ammo how much, for weapons how much is used per shot */ char *ammo; /* for weapons */ int flags; /* IT_* flags */ int weapmodel; /* weapon model index (for weapons) */ void *info; int tag; char *precaches; /* string of all models, sounds, and images this item will use */ } gitem_t; /* this structure is left intact through an entire game it should be initialized at dll load time, and read/written to the server.ssv file for savegames */ typedef struct { char helpmessage1[512]; char helpmessage2[512]; int helpchanged; /* flash F1 icon if non 0, play sound and increment only if 1, 2, or 3 */ gclient_t *clients; /* [maxclients] */ /* can't store spawnpoint in level, because it would get overwritten by the savegame restore */ char spawnpoint[512]; /* needed for coop respawns */ /* store latched cvars here that we want to get at often */ int maxclients; int maxentities; /* cross level triggers */ int serverflags; /* items */ int num_items; qboolean autosaved; } game_locals_t; /* this structure is cleared as each map is entered it is read/written to the level.sav file for savegames */ typedef struct { int framenum; float time; char level_name[MAX_QPATH]; /* the descriptive name (Outer Base, etc) */ char mapname[MAX_QPATH]; /* the server name (base1, etc) */ char nextmap[MAX_QPATH]; /* go here when fraglimit is hit */ /* intermission state */ float intermissiontime; /* time the intermission was started */ char *changemap; int exitintermission; vec3_t intermission_origin; vec3_t intermission_angle; edict_t *sight_client; /* changed once each frame for coop games */ edict_t *sight_entity; int sight_entity_framenum; edict_t *sound_entity; int sound_entity_framenum; edict_t *sound2_entity; int sound2_entity_framenum; int pic_health; int total_secrets; int found_secrets; int total_goals; int found_goals; int total_monsters; int killed_monsters; edict_t *current_entity; /* entity running from G_RunFrame */ int body_que; /* dead bodies */ int power_cubes; /* ugly necessity for coop */ } level_locals_t; /* spawn_temp_t is only used to hold entity field values that can be set from the editor, but aren't actualy present in edict_t during gameplay */ typedef struct { /* world vars */ char *sky; float skyrotate; vec3_t skyaxis; char *nextmap; int lip; int distance; int height; char *noise; float pausetime; char *item; char *gravity; float minyaw; float maxyaw; float minpitch; float maxpitch; } spawn_temp_t; typedef struct { /* fixed data */ vec3_t start_origin; vec3_t start_angles; vec3_t end_origin; vec3_t end_angles; int sound_start; int sound_middle; int sound_end; float accel; float speed; float decel; float distance; float wait; /* state data */ int state; vec3_t dir; float current_speed; float move_speed; float next_speed; float remaining_distance; float decel_distance; void (*endfunc)(edict_t *); } moveinfo_t; typedef struct { void (*aifunc)(edict_t *self, float dist); float dist; void (*thinkfunc)(edict_t *self); } mframe_t; typedef struct { int firstframe; int lastframe; mframe_t *frame; void (*endfunc)(edict_t *self); } mmove_t; typedef struct { mmove_t *currentmove; int aiflags; int nextframe; float scale; void (*stand)(edict_t *self); void (*idle)(edict_t *self); void (*search)(edict_t *self); void (*walk)(edict_t *self); void (*run)(edict_t *self); void (*dodge)(edict_t *self, edict_t *other, float eta); void (*attack)(edict_t *self); void (*melee)(edict_t *self); void (*sight)(edict_t *self, edict_t *other); qboolean (*checkattack)(edict_t *self); float pausetime; float attack_finished; vec3_t saved_goal; float search_time; float trail_time; vec3_t last_sighting; int attack_state; int lefty; float idle_time; int linkcount; int power_armor_type; int power_armor_power; } monsterinfo_t; extern game_locals_t game; extern level_locals_t level; extern game_import_t gi; extern game_export_t globals; extern spawn_temp_t st; extern int sm_meat_index; extern int snd_fry; extern int gibsthisframe; extern int lastgibframe; /* means of death */ #define MOD_UNKNOWN 0 #define MOD_BLASTER 1 #define MOD_SHOTGUN 2 #define MOD_SSHOTGUN 3 #define MOD_MACHINEGUN 4 #define MOD_CHAINGUN 5 #define MOD_GRENADE 6 #define MOD_G_SPLASH 7 #define MOD_ROCKET 8 #define MOD_R_SPLASH 9 #define MOD_HYPERBLASTER 10 #define MOD_RAILGUN 11 #define MOD_BFG_LASER 12 #define MOD_BFG_BLAST 13 #define MOD_BFG_EFFECT 14 #define MOD_HANDGRENADE 15 #define MOD_HG_SPLASH 16 #define MOD_WATER 17 #define MOD_SLIME 18 #define MOD_LAVA 19 #define MOD_CRUSH 20 #define MOD_TELEFRAG 21 #define MOD_FALLING 22 #define MOD_SUICIDE 23 #define MOD_HELD_GRENADE 24 #define MOD_EXPLOSIVE 25 #define MOD_BARREL 26 #define MOD_BOMB 27 #define MOD_EXIT 28 #define MOD_SPLASH 29 #define MOD_TARGET_LASER 30 #define MOD_TRIGGER_HURT 31 #define MOD_HIT 32 #define MOD_TARGET_BLASTER 33 #define MOD_FRIENDLY_FIRE 0x8000000 extern int meansOfDeath; extern edict_t *g_edicts; #define FOFS(x) (size_t)&(((edict_t *)NULL)->x) #define STOFS(x) (size_t)&(((spawn_temp_t *)NULL)->x) #define LLOFS(x) (size_t)&(((level_locals_t *)NULL)->x) #define CLOFS(x) (size_t)&(((gclient_t *)NULL)->x) #define random() ((randk() & 0x7fff) / ((float)0x7fff)) #define crandom() (2.0 * (random() - 0.5)) extern cvar_t *maxentities; extern cvar_t *deathmatch; extern cvar_t *coop; extern cvar_t *dmflags; extern cvar_t *skill; extern cvar_t *fraglimit; extern cvar_t *timelimit; extern cvar_t *password; extern cvar_t *spectator_password; extern cvar_t *needpass; extern cvar_t *g_select_empty; extern cvar_t *dedicated; extern cvar_t *filterban; extern cvar_t *sv_gravity; extern cvar_t *sv_maxvelocity; extern cvar_t *gun_x, *gun_y, *gun_z; extern cvar_t *sv_rollspeed; extern cvar_t *sv_rollangle; extern cvar_t *run_pitch; extern cvar_t *run_roll; extern cvar_t *bob_up; extern cvar_t *bob_pitch; extern cvar_t *bob_roll; extern cvar_t *sv_cheats; extern cvar_t *maxclients; extern cvar_t *maxspectators; extern cvar_t *flood_msgs; extern cvar_t *flood_persecond; extern cvar_t *flood_waitdelay; extern cvar_t *sv_maplist; #define world (&g_edicts[0]) /* item spawnflags */ #define ITEM_TRIGGER_SPAWN 0x00000001 #define ITEM_NO_TOUCH 0x00000002 /* 6 bits reserved for editor flags */ /* 8 bits used as power cube id bits for coop games */ #define DROPPED_ITEM 0x00010000 #define DROPPED_PLAYER_ITEM 0x00020000 #define ITEM_TARGETS_USED 0x00040000 /* fields are needed for spawning from the entity string and saving / loading games */ #define FFL_SPAWNTEMP 1 #define FFL_NOSPAWN 2 typedef enum { F_INT, F_FLOAT, F_LSTRING, /* string on disk, pointer in memory, TAG_LEVEL */ F_GSTRING, /* string on disk, pointer in memory, TAG_GAME */ F_VECTOR, F_ANGLEHACK, F_EDICT, /* index on disk, pointer in memory */ F_ITEM, /* index on disk, pointer in memory */ F_CLIENT, /* index on disk, pointer in memory */ F_FUNCTION, F_MMOVE, F_IGNORE } fieldtype_t; typedef struct { char *name; int ofs; fieldtype_t type; int flags; } field_t; extern field_t fields[]; extern gitem_t itemlist[]; /* g_cmds.c */ void Cmd_Help_f(edict_t *ent); /* g_items.c */ void PrecacheItem(gitem_t *it); void InitItems(void); void SetItemNames(void); gitem_t *FindItem(char *pickup_name); gitem_t *FindItemByClassname(char *classname); #define ITEM_INDEX(x) ((x) - itemlist) edict_t *Drop_Item(edict_t *ent, gitem_t *item); void SetRespawn(edict_t *ent, float delay); void ChangeWeapon(edict_t *ent); void SpawnItem(edict_t *ent, gitem_t *item); void Think_Weapon(edict_t *ent); int ArmorIndex(edict_t *ent); int PowerArmorType(edict_t *ent); gitem_t *GetItemByIndex(int index); qboolean Add_Ammo(edict_t *ent, gitem_t *item, int count); void Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf); /* g_utils.c */ qboolean KillBox(edict_t *ent); void G_ProjectSource(vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result); edict_t *G_Find(edict_t *from, int fieldofs, char *match); edict_t *findradius(edict_t *from, vec3_t org, float rad); edict_t *G_PickTarget(char *targetname); void G_UseTargets(edict_t *ent, edict_t *activator); void G_SetMovedir(vec3_t angles, vec3_t movedir); void G_InitEdict(edict_t *e); edict_t *G_Spawn(void); void G_FreeEdict(edict_t *e); void G_TouchTriggers(edict_t *ent); void G_TouchSolids(edict_t *ent); char *G_CopyString(char *in); float *tv(float x, float y, float z); char *vtos(vec3_t v); float vectoyaw(vec3_t vec); void vectoangles(vec3_t vec, vec3_t angles); /* g_combat.c */ qboolean OnSameTeam(edict_t *ent1, edict_t *ent2); qboolean CanDamage(edict_t *targ, edict_t *inflictor); void T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod); void T_RadiusDamage(edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod); /* damage flags */ #define DAMAGE_RADIUS 0x00000001 /* damage was indirect */ #define DAMAGE_NO_ARMOR 0x00000002 /* armour does not protect from this damage */ #define DAMAGE_ENERGY 0x00000004 /* damage is from an energy based weapon */ #define DAMAGE_NO_KNOCKBACK 0x00000008 /* do not affect velocity, just view angles */ #define DAMAGE_BULLET 0x00000010 /* damage is from a bullet (used for ricochets) */ #define DAMAGE_NO_PROTECTION 0x00000020 /* armor, shields, invulnerability, and godmode have no effect */ #define DEFAULT_BULLET_HSPREAD 300 #define DEFAULT_BULLET_VSPREAD 500 #define DEFAULT_SHOTGUN_HSPREAD 1000 #define DEFAULT_SHOTGUN_VSPREAD 500 #define DEFAULT_DEATHMATCH_SHOTGUN_COUNT 12 #define DEFAULT_SHOTGUN_COUNT 12 #define DEFAULT_SSHOTGUN_COUNT 20 /* g_monster.c */ void monster_fire_bullet(edict_t *self, vec3_t start, vec3_t dir, int damage, int kick, int hspread, int vspread, int flashtype); void monster_fire_shotgun(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int flashtype); void monster_fire_blaster(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect); void monster_fire_grenade(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype); void monster_fire_rocket(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype); void monster_fire_railgun(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype); void monster_fire_bfg(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick, float damage_radius, int flashtype); void M_droptofloor(edict_t *ent); void monster_think(edict_t *self); void walkmonster_start(edict_t *self); void swimmonster_start(edict_t *self); void flymonster_start(edict_t *self); void AttackFinished(edict_t *self, float time); void monster_death_use(edict_t *self); void M_CatagorizePosition(edict_t *ent); qboolean M_CheckAttack(edict_t *self); void M_FlyCheck(edict_t *self); void M_CheckGround(edict_t *ent); /* g_misc.c */ void ThrowHead(edict_t *self, char *gibname, int damage, int type); void ThrowClientHead(edict_t *self, int damage); void ThrowGib(edict_t *self, char *gibname, int damage, int type); void BecomeExplosion1(edict_t *self); /* g_ai.c */ void AI_SetSightClient(void); void ai_stand(edict_t *self, float dist); void ai_move(edict_t *self, float dist); void ai_walk(edict_t *self, float dist); void ai_turn(edict_t *self, float dist); void ai_run(edict_t *self, float dist); void ai_charge(edict_t *self, float dist); int range(edict_t *self, edict_t *other); void FoundTarget(edict_t *self); qboolean infront(edict_t *self, edict_t *other); qboolean visible(edict_t *self, edict_t *other); qboolean FacingIdeal(edict_t *self); /* g_weapon.c */ void ThrowDebris(edict_t *self, char *modelname, float speed, vec3_t origin); qboolean fire_hit(edict_t *self, vec3_t aim, int damage, int kick); void fire_bullet(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod); void fire_shotgun(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int mod); void fire_blaster(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int effect, qboolean hyper); void fire_grenade(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius); void fire_grenade2(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held); void fire_rocket(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage); void fire_rail(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick); void fire_bfg(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius); /* g_ptrail.c */ void PlayerTrail_Init(void); void PlayerTrail_Add(vec3_t spot); void PlayerTrail_New(vec3_t spot); edict_t *PlayerTrail_PickFirst(edict_t *self); edict_t *PlayerTrail_PickNext(edict_t *self); edict_t *PlayerTrail_LastSpot(void); /* g_client.c */ void respawn(edict_t *ent); void BeginIntermission(edict_t *targ); void PutClientInServer(edict_t *ent); void InitClientPersistant(gclient_t *client); void InitClientResp(gclient_t *client); void InitBodyQue(void); void ClientBeginServerFrame(edict_t *ent); /* g_player.c */ void player_pain(edict_t *self, edict_t *other, float kick, int damage); void player_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); /* g_svcmds.c */ void ServerCommand(void); qboolean SV_FilterPacket(char *from); /* p_view.c */ void ClientEndServerFrame(edict_t *ent); /* p_hud.c */ void MoveClientToIntermission(edict_t *client); void G_SetStats(edict_t *ent); void G_SetSpectatorStats(edict_t *ent); void G_CheckChaseStats(edict_t *ent); void ValidateSelectedItem(edict_t *ent); void DeathmatchScoreboardMessage(edict_t *client, edict_t *killer); void HelpComputerMessage(edict_t *client); void InventoryMessage(edict_t *client); /* g_pweapon.c */ void PlayerNoise(edict_t *who, vec3_t where, int type); /* m_move.c */ qboolean M_CheckBottom(edict_t *ent); qboolean M_walkmove(edict_t *ent, float yaw, float dist); void M_MoveToGoal(edict_t *ent, float dist); void M_ChangeYaw(edict_t *ent); /* g_phys.c */ void G_RunEntity(edict_t *ent); /* g_main.c */ void SaveClientData(void); void FetchClientEntData(edict_t *ent); /* g_chase.c */ void UpdateChaseCam(edict_t *ent); void ChaseNext(edict_t *ent); void ChasePrev(edict_t *ent); void GetChaseTarget(edict_t *ent); /* ============================================================================ */ /* client_t->anim_priority */ #define ANIM_BASIC 0 /* stand / run */ #define ANIM_WAVE 1 #define ANIM_JUMP 2 #define ANIM_PAIN 3 #define ANIM_ATTACK 4 #define ANIM_DEATH 5 #define ANIM_REVERSE 6 /* client data that stays across multiple level loads */ typedef struct { char userinfo[MAX_INFO_STRING]; char netname[16]; int hand; qboolean connected; /* a loadgame will leave valid entities that just don't have a connection yet */ /* values saved and restored from edicts when changing levels */ int health; int max_health; int savedFlags; int selected_item; int inventory[MAX_ITEMS]; /* ammo capacities */ int max_bullets; int max_shells; int max_rockets; int max_grenades; int max_cells; int max_slugs; gitem_t *weapon; gitem_t *lastweapon; int power_cubes; /* used for tracking the cubes in coop games */ int score; /* for calculating total unit score in coop games */ int game_helpchanged; int helpchanged; qboolean spectator; /* client is a spectator */ } client_persistant_t; /* client data that stays across deathmatch respawns */ typedef struct { client_persistant_t coop_respawn; /* what to set client->pers to on a respawn */ int enterframe; /* level.framenum the client entered the game */ int score; /* frags, etc */ vec3_t cmd_angles; /* angles sent over in the last command */ qboolean spectator; /* client is a spectator */ } client_respawn_t; /* this structure is cleared on each PutClientInServer(), except for 'client->pers' */ struct gclient_s { /* known to server */ player_state_t ps; /* communicated by server to clients */ int ping; /* private to game */ client_persistant_t pers; client_respawn_t resp; pmove_state_t old_pmove; /* for detecting out-of-pmove changes */ qboolean showscores; /* set layout stat */ qboolean showinventory; /* set layout stat */ qboolean showhelp; qboolean showhelpicon; int ammo_index; int buttons; int oldbuttons; int latched_buttons; qboolean weapon_thunk; gitem_t *newweapon; /* sum up damage over an entire frame, so shotgun blasts give a single big kick */ int damage_armor; /* damage absorbed by armor */ int damage_parmor; /* damage absorbed by power armor */ int damage_blood; /* damage taken out of health */ int damage_knockback; /* impact damage */ vec3_t damage_from; /* origin for vector calculation */ float killer_yaw; /* when dead, look at killer */ weaponstate_t weaponstate; vec3_t kick_angles; /* weapon kicks */ vec3_t kick_origin; float v_dmg_roll, v_dmg_pitch, v_dmg_time; /* damage kicks */ float fall_time, fall_value; /* for view drop on fall */ float damage_alpha; float bonus_alpha; vec3_t damage_blend; vec3_t v_angle; /* aiming direction */ float bobtime; /* so off-ground doesn't change it */ vec3_t oldviewangles; vec3_t oldvelocity; float next_drown_time; int old_waterlevel; int breather_sound; int machinegun_shots; /* for weapon raising */ /* animation vars */ int anim_end; int anim_priority; qboolean anim_duck; qboolean anim_run; /* powerup timers */ float quad_framenum; float invincible_framenum; float breather_framenum; float enviro_framenum; qboolean grenade_blew_up; float grenade_time; int silencer_shots; int weapon_sound; float pickup_msg_time; float flood_locktill; /* locked from talking */ float flood_when[10]; /* when messages were said */ int flood_whenhead; /* head pointer for when said */ float respawn_time; /* can respawn when time > this */ edict_t *chase_target; /* player we are chasing */ qboolean update_chase; /* need to update chase info? */ }; struct edict_s { entity_state_t s; struct gclient_s *client; /* NULL if not a player the server expects the first part of gclient_s to be a player_state_t but the rest of it is opaque */ qboolean inuse; int linkcount; link_t area; /* linked to a division node or leaf */ int num_clusters; /* if -1, use headnode instead */ int clusternums[MAX_ENT_CLUSTERS]; int headnode; /* unused if num_clusters != -1 */ int areanum, areanum2; /* ================================ */ int svflags; vec3_t mins, maxs; vec3_t absmin, absmax, size; solid_t solid; int clipmask; edict_t *owner; /* DO NOT MODIFY ANYTHING ABOVE THIS, THE SERVER */ /* EXPECTS THE FIELDS IN THAT ORDER! */ /* ================================ */ int movetype; int flags; char *model; float freetime; /* sv.time when the object was freed */ /* only used locally in game, not by server */ char *message; char *classname; int spawnflags; float timestamp; float angle; /* set in qe3, -1 = up, -2 = down */ char *target; char *targetname; char *killtarget; char *team; char *pathtarget; char *deathtarget; char *combattarget; edict_t *target_ent; float speed, accel, decel; vec3_t movedir; vec3_t pos1, pos2; vec3_t velocity; vec3_t avelocity; int mass; float air_finished; float gravity; /* per entity gravity multiplier (1.0 is normal) use for lowgrav artifact, flares */ edict_t *goalentity; edict_t *movetarget; float yaw_speed; float ideal_yaw; float nextthink; void (*prethink)(edict_t *ent); void (*think)(edict_t *self); void (*blocked)(edict_t *self, edict_t *other); void (*touch)(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf); void (*use)(edict_t *self, edict_t *other, edict_t *activator); void (*pain)(edict_t *self, edict_t *other, float kick, int damage); void (*die)(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); float touch_debounce_time; float pain_debounce_time; float damage_debounce_time; float fly_sound_debounce_time; float last_move_time; int health; int max_health; int gib_health; int deadflag; qboolean show_hostile; float powerarmor_time; char *map; /* target_changelevel */ int viewheight; /* height above origin where eyesight is determined */ int takedamage; int dmg; int radius_dmg; float dmg_radius; int sounds; /* make this a spawntemp var? */ int count; edict_t *chain; edict_t *enemy; edict_t *oldenemy; edict_t *activator; edict_t *groundentity; int groundentity_linkcount; edict_t *teamchain; edict_t *teammaster; edict_t *mynoise; /* can go in client only */ edict_t *mynoise2; int noise_index; int noise_index2; float volume; float attenuation; /* timing variables */ float wait; float delay; /* before firing targets */ float random; float teleport_time; int watertype; int waterlevel; vec3_t move_origin; vec3_t move_angles; /* move this to clientinfo? */ int light_level; int style; /* also used as areaportal number */ gitem_t *item; /* for bonus items */ /* common data blocks */ moveinfo_t moveinfo; monsterinfo_t monsterinfo; }; #endif /* GAME_LOCAL_H */ yquake2-QUAKE2_5_32/src/game/g_monster.c0000644000175000017500000004473112615162034020471 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Monster utility functions. * * ======================================================================= */ #include "header/local.h" void monster_start_go(edict_t *self); /* Monster weapons */ void monster_fire_bullet(edict_t *self, vec3_t start, vec3_t dir, int damage, int kick, int hspread, int vspread, int flashtype) { if (!self) { return; } fire_bullet(self, start, dir, damage, kick, hspread, vspread, MOD_UNKNOWN); gi.WriteByte(svc_muzzleflash2); gi.WriteShort(self - g_edicts); gi.WriteByte(flashtype); gi.multicast(start, MULTICAST_PVS); } void monster_fire_shotgun(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int flashtype) { if (!self) { return; } fire_shotgun(self, start, aimdir, damage, kick, hspread, vspread, count, MOD_UNKNOWN); gi.WriteByte(svc_muzzleflash2); gi.WriteShort(self - g_edicts); gi.WriteByte(flashtype); gi.multicast(start, MULTICAST_PVS); } void monster_fire_blaster(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect) { if (!self) { return; } fire_blaster(self, start, dir, damage, speed, effect, false); gi.WriteByte(svc_muzzleflash2); gi.WriteShort(self - g_edicts); gi.WriteByte(flashtype); gi.multicast(start, MULTICAST_PVS); } void monster_fire_grenade(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype) { if (!self) { return; } fire_grenade(self, start, aimdir, damage, speed, 2.5, damage + 40); gi.WriteByte(svc_muzzleflash2); gi.WriteShort(self - g_edicts); gi.WriteByte(flashtype); gi.multicast(start, MULTICAST_PVS); } void monster_fire_rocket(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype) { if (!self) { return; } fire_rocket(self, start, dir, damage, speed, damage + 20, damage); gi.WriteByte(svc_muzzleflash2); gi.WriteShort(self - g_edicts); gi.WriteByte(flashtype); gi.multicast(start, MULTICAST_PVS); } void monster_fire_railgun(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype) { if (!self) { return; } fire_rail(self, start, aimdir, damage, kick); gi.WriteByte(svc_muzzleflash2); gi.WriteShort(self - g_edicts); gi.WriteByte(flashtype); gi.multicast(start, MULTICAST_PVS); } void monster_fire_bfg(edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick /* unused */, float damage_radius, int flashtype) { if (!self) { return; } fire_bfg(self, start, aimdir, damage, speed, damage_radius); gi.WriteByte(svc_muzzleflash2); gi.WriteShort(self - g_edicts); gi.WriteByte(flashtype); gi.multicast(start, MULTICAST_PVS); } /* ================================================================== */ /* Monster utility functions */ void M_FliesOff(edict_t *self) { if (!self) { return; } self->s.effects &= ~EF_FLIES; self->s.sound = 0; } void M_FliesOn(edict_t *self) { if (!self) { return; } if (self->waterlevel) { return; } self->s.effects |= EF_FLIES; self->s.sound = gi.soundindex("infantry/inflies1.wav"); self->think = M_FliesOff; self->nextthink = level.time + 60; } void M_FlyCheck(edict_t *self) { if (!self) { return; } if (self->waterlevel) { return; } if (random() > 0.5) { return; } self->think = M_FliesOn; self->nextthink = level.time + 5 + 10 * random(); } void AttackFinished(edict_t *self, float time) { if (!self) { return; } self->monsterinfo.attack_finished = level.time + time; } void M_CheckGround(edict_t *ent) { vec3_t point; trace_t trace; if (!ent) { return; } if (ent->flags & (FL_SWIM | FL_FLY)) { return; } if (ent->velocity[2] > 100) { ent->groundentity = NULL; return; } /* if the hull point one-quarter unit down is solid the entity is on ground */ point[0] = ent->s.origin[0]; point[1] = ent->s.origin[1]; point[2] = ent->s.origin[2] - 0.25; trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_MONSTERSOLID); /* check steepness */ if ((trace.plane.normal[2] < 0.7) && !trace.startsolid) { ent->groundentity = NULL; return; } /* This two lines were commented out by id. But why? */ ent->groundentity = trace.ent; ent->groundentity_linkcount = trace.ent->linkcount; if (!trace.startsolid && !trace.allsolid) { VectorCopy(trace.endpos, ent->s.origin); ent->groundentity = trace.ent; ent->groundentity_linkcount = trace.ent->linkcount; ent->velocity[2] = trace.ent->velocity[2]; } } void M_CatagorizePosition(edict_t *ent) { vec3_t point; int cont; if (!ent) { return; } /* get waterlevel */ point[0] = (ent->absmax[0] + ent->absmin[0])/2; point[1] = (ent->absmax[1] + ent->absmin[1])/2; point[2] = ent->absmin[2] + 2; cont = gi.pointcontents(point); if (!(cont & MASK_WATER)) { ent->waterlevel = 0; ent->watertype = 0; return; } ent->watertype = cont; ent->waterlevel = 1; point[2] += 26; cont = gi.pointcontents(point); if (!(cont & MASK_WATER)) { return; } ent->waterlevel = 2; point[2] += 22; cont = gi.pointcontents(point); if (cont & MASK_WATER) { ent->waterlevel = 3; } } void M_WorldEffects(edict_t *ent) { int dmg; if (!ent) { return; } if (ent->health > 0) { if (!(ent->flags & FL_SWIM)) { if (ent->waterlevel < 3) { ent->air_finished = level.time + 12; } else if (ent->air_finished < level.time) { /* drown! */ if (ent->pain_debounce_time < level.time) { dmg = 2 + 2 * floor(level.time - ent->air_finished); if (dmg > 15) { dmg = 15; } T_Damage(ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); ent->pain_debounce_time = level.time + 1; } } } else { if (ent->waterlevel > 0) { ent->air_finished = level.time + 9; } else if (ent->air_finished < level.time) { /* suffocate! */ if (ent->pain_debounce_time < level.time) { dmg = 2 + 2 * floor(level.time - ent->air_finished); if (dmg > 15) { dmg = 15; } T_Damage(ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER); ent->pain_debounce_time = level.time + 1; } } } } if (ent->waterlevel == 0) { if (ent->flags & FL_INWATER) { gi.sound(ent, CHAN_BODY, gi.soundindex( "player/watr_out.wav"), 1, ATTN_NORM, 0); ent->flags &= ~FL_INWATER; } return; } if ((ent->watertype & CONTENTS_LAVA) && !(ent->flags & FL_IMMUNE_LAVA)) { if (ent->damage_debounce_time < level.time) { ent->damage_debounce_time = level.time + 0.2; T_Damage(ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 10 * ent->waterlevel, 0, 0, MOD_LAVA); } } if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME) && !(ent->svflags & SVF_DEADMONSTER)) { if (ent->damage_debounce_time < level.time) { ent->damage_debounce_time = level.time + 1; T_Damage(ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 4 * ent->waterlevel, 0, 0, MOD_SLIME); } } if (!(ent->flags & FL_INWATER)) { if (!(ent->svflags & SVF_DEADMONSTER)) { if (ent->watertype & CONTENTS_LAVA) { if (random() <= 0.5) { gi.sound(ent, CHAN_BODY, gi.soundindex( "player/lava1.wav"), 1, ATTN_NORM, 0); } else { gi.sound(ent, CHAN_BODY, gi.soundindex( "player/lava2.wav"), 1, ATTN_NORM, 0); } } else if (ent->watertype & CONTENTS_SLIME) { gi.sound(ent, CHAN_BODY, gi.soundindex( "player/watr_in.wav"), 1, ATTN_NORM, 0); } else if (ent->watertype & CONTENTS_WATER) { gi.sound(ent, CHAN_BODY, gi.soundindex( "player/watr_in.wav"), 1, ATTN_NORM, 0); } } ent->flags |= FL_INWATER; ent->damage_debounce_time = 0; } } void M_droptofloor(edict_t *ent) { vec3_t end; trace_t trace; if (!ent) { return; } ent->s.origin[2] += 1; VectorCopy(ent->s.origin, end); end[2] -= 256; trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID); if ((trace.fraction == 1) || trace.allsolid) { return; } VectorCopy(trace.endpos, ent->s.origin); gi.linkentity(ent); M_CheckGround(ent); M_CatagorizePosition(ent); } void M_SetEffects(edict_t *ent) { if (!ent) { return; } ent->s.effects &= ~(EF_COLOR_SHELL | EF_POWERSCREEN); ent->s.renderfx &= ~(RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE); if (ent->monsterinfo.aiflags & AI_RESURRECTING) { ent->s.effects |= EF_COLOR_SHELL; ent->s.renderfx |= RF_SHELL_RED; } if (ent->health <= 0) { return; } if (ent->powerarmor_time > level.time) { if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SCREEN) { ent->s.effects |= EF_POWERSCREEN; } else if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SHIELD) { ent->s.effects |= EF_COLOR_SHELL; ent->s.renderfx |= RF_SHELL_GREEN; } } } void M_MoveFrame(edict_t *self) { mmove_t *move; int index; if (!self) { return; } move = self->monsterinfo.currentmove; self->nextthink = level.time + FRAMETIME; if ((self->monsterinfo.nextframe) && (self->monsterinfo.nextframe >= move->firstframe) && (self->monsterinfo.nextframe <= move->lastframe)) { self->s.frame = self->monsterinfo.nextframe; self->monsterinfo.nextframe = 0; } else { if (self->s.frame == move->lastframe) { if (move->endfunc) { move->endfunc(self); /* regrab move, endfunc is very likely to change it */ move = self->monsterinfo.currentmove; /* check for death */ if (self->svflags & SVF_DEADMONSTER) { return; } } } if ((self->s.frame < move->firstframe) || (self->s.frame > move->lastframe)) { self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; self->s.frame = move->firstframe; } else { if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) { self->s.frame++; if (self->s.frame > move->lastframe) { self->s.frame = move->firstframe; } } } } index = self->s.frame - move->firstframe; if (move->frame[index].aifunc) { if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) { move->frame[index].aifunc(self, move->frame[index].dist * self->monsterinfo.scale); } else { move->frame[index].aifunc(self, 0); } } if (move->frame[index].thinkfunc) { move->frame[index].thinkfunc(self); } } void monster_think(edict_t *self) { if (!self) { return; } M_MoveFrame(self); if (self->linkcount != self->monsterinfo.linkcount) { self->monsterinfo.linkcount = self->linkcount; M_CheckGround(self); } M_CatagorizePosition(self); M_WorldEffects(self); M_SetEffects(self); } /* * Using a monster makes it angry * at the current activator */ void monster_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) { if (!self || !activator) { return; } if (self->enemy) { return; } if (self->health <= 0) { return; } if (activator->flags & FL_NOTARGET) { return; } if (!(activator->client) && !(activator->monsterinfo.aiflags & AI_GOOD_GUY)) { return; } /* delay reaction so if the monster is teleported, its sound is still heard */ self->enemy = activator; FoundTarget(self); } void monster_triggered_spawn(edict_t *self) { if (!self) { return; } self->s.origin[2] += 1; KillBox(self); self->solid = SOLID_BBOX; self->movetype = MOVETYPE_STEP; self->svflags &= ~SVF_NOCLIENT; self->air_finished = level.time + 12; gi.linkentity(self); monster_start_go(self); if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET)) { FoundTarget(self); } else { self->enemy = NULL; } } void monster_triggered_spawn_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) { if (!self || !activator) { return; } /* we have a one frame delay here so we don't telefrag the guy who activated us */ self->think = monster_triggered_spawn; self->nextthink = level.time + FRAMETIME; if (activator->client) { self->enemy = activator; } self->use = monster_use; } void monster_triggered_start(edict_t *self) { if (!self) { return; } self->solid = SOLID_NOT; self->movetype = MOVETYPE_NONE; self->svflags |= SVF_NOCLIENT; self->nextthink = 0; self->use = monster_triggered_spawn_use; } /* * When a monster dies, it fires all of its targets * with the current enemy as activator. */ void monster_death_use(edict_t *self) { if (!self) { return; } self->flags &= ~(FL_FLY | FL_SWIM); self->monsterinfo.aiflags &= AI_GOOD_GUY; if (self->item) { Drop_Item(self, self->item); self->item = NULL; } if (self->deathtarget) { self->target = self->deathtarget; } if (!self->target) { return; } G_UseTargets(self, self->enemy); } /* ================================================================== */ qboolean monster_start(edict_t *self) { if (!self) { return false; } if (deathmatch->value) { G_FreeEdict(self); return false; } if ((self->spawnflags & 4) && !(self->monsterinfo.aiflags & AI_GOOD_GUY)) { self->spawnflags &= ~4; self->spawnflags |= 1; } if (!(self->monsterinfo.aiflags & AI_GOOD_GUY)) { level.total_monsters++; } self->nextthink = level.time + FRAMETIME; self->svflags |= SVF_MONSTER; self->s.renderfx |= RF_FRAMELERP; self->takedamage = DAMAGE_AIM; self->air_finished = level.time + 12; self->use = monster_use; if(!self->max_health) { self->max_health = self->health; } self->clipmask = MASK_MONSTERSOLID; self->s.skinnum = 0; self->deadflag = DEAD_NO; self->svflags &= ~SVF_DEADMONSTER; if (!self->monsterinfo.checkattack) { self->monsterinfo.checkattack = M_CheckAttack; } VectorCopy(self->s.origin, self->s.old_origin); if (st.item) { self->item = FindItemByClassname(st.item); if (!self->item) { gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item); } } /* randomize what frame they start on */ if (self->monsterinfo.currentmove) { self->s.frame = self->monsterinfo.currentmove->firstframe + (randk() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1)); } return true; } void monster_start_go(edict_t *self) { vec3_t v; if (!self) { return; } if (self->health <= 0) { return; } /* check for target to combat_point and change to combattarget */ if (self->target) { qboolean notcombat; qboolean fixup; edict_t *target; target = NULL; notcombat = false; fixup = false; while ((target = G_Find(target, FOFS(targetname), self->target)) != NULL) { if (strcmp(target->classname, "point_combat") == 0) { self->combattarget = self->target; fixup = true; } else { notcombat = true; } } if (notcombat && self->combattarget) { gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin)); } if (fixup) { self->target = NULL; } } /* validate combattarget */ if (self->combattarget) { edict_t *target; target = NULL; while ((target = G_Find(target, FOFS(targetname), self->combattarget)) != NULL) { if (strcmp(target->classname, "point_combat") != 0) { gi.dprintf( "%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n", self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2], self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1], (int)target->s.origin[2]); } } } if (self->target) { self->goalentity = self->movetarget = G_PickTarget(self->target); if (!self->movetarget) { gi.dprintf("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin)); self->target = NULL; self->monsterinfo.pausetime = 100000000; self->monsterinfo.stand(self); } else if (strcmp(self->movetarget->classname, "path_corner") == 0) { VectorSubtract(self->goalentity->s.origin, self->s.origin, v); self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v); self->monsterinfo.walk(self); self->target = NULL; } else { self->goalentity = self->movetarget = NULL; self->monsterinfo.pausetime = 100000000; self->monsterinfo.stand(self); } } else { self->monsterinfo.pausetime = 100000000; self->monsterinfo.stand(self); } self->think = monster_think; self->nextthink = level.time + FRAMETIME; } void walkmonster_start_go(edict_t *self) { if (!self) { return; } if (!(self->spawnflags & 2) && (level.time < 1)) { M_droptofloor(self); if (self->groundentity) { if (!M_walkmove(self, 0, 0)) { gi.dprintf("%s in solid at %s\n", self->classname, vtos(self->s.origin)); } } } if (!self->yaw_speed) { self->yaw_speed = 20; } self->viewheight = 25; monster_start_go(self); if (self->spawnflags & 2) { monster_triggered_start(self); } } void walkmonster_start(edict_t *self) { if (!self) { return; } self->think = walkmonster_start_go; monster_start(self); } void flymonster_start_go(edict_t *self) { if (!self) { return; } if (!M_walkmove(self, 0, 0)) { gi.dprintf("%s in solid at %s\n", self->classname, vtos(self->s.origin)); } if (!self->yaw_speed) { self->yaw_speed = 10; } self->viewheight = 25; monster_start_go(self); if (self->spawnflags & 2) { monster_triggered_start(self); } } void flymonster_start(edict_t *self) { if (!self) { return; } self->flags |= FL_FLY; self->think = flymonster_start_go; monster_start(self); } void swimmonster_start_go(edict_t *self) { if (!self) { return; } if (!self->yaw_speed) { self->yaw_speed = 10; } self->viewheight = 10; monster_start_go(self); if (self->spawnflags & 2) { monster_triggered_start(self); } } void swimmonster_start(edict_t *self) { if (!self) { return; } self->flags |= FL_SWIM; self->think = swimmonster_start_go; monster_start(self); } yquake2-QUAKE2_5_32/src/game/g_main.c0000644000175000017500000002145212615162034017721 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Jump in into the game.so and support functions. * * ======================================================================= */ #include "header/local.h" game_locals_t game; level_locals_t level; game_import_t gi; game_export_t globals; spawn_temp_t st; int sm_meat_index; int snd_fry; int meansOfDeath; edict_t *g_edicts; cvar_t *deathmatch; cvar_t *coop; cvar_t *dmflags; cvar_t *skill; cvar_t *fraglimit; cvar_t *timelimit; cvar_t *password; cvar_t *spectator_password; cvar_t *needpass; cvar_t *maxclients; cvar_t *maxspectators; cvar_t *maxentities; cvar_t *g_select_empty; cvar_t *dedicated; cvar_t *filterban; cvar_t *sv_maxvelocity; cvar_t *sv_gravity; cvar_t *sv_rollspeed; cvar_t *sv_rollangle; cvar_t *gun_x; cvar_t *gun_y; cvar_t *gun_z; cvar_t *run_pitch; cvar_t *run_roll; cvar_t *bob_up; cvar_t *bob_pitch; cvar_t *bob_roll; cvar_t *sv_cheats; cvar_t *flood_msgs; cvar_t *flood_persecond; cvar_t *flood_waitdelay; cvar_t *sv_maplist; cvar_t *gib_on; void SpawnEntities(char *mapname, char *entities, char *spawnpoint); void ClientThink(edict_t *ent, usercmd_t *cmd); qboolean ClientConnect(edict_t *ent, char *userinfo); void ClientUserinfoChanged(edict_t *ent, char *userinfo); void ClientDisconnect(edict_t *ent); void ClientBegin(edict_t *ent); void ClientCommand(edict_t *ent); void RunEntity(edict_t *ent); void WriteGame(char *filename, qboolean autosave); void ReadGame(char *filename); void WriteLevel(char *filename); void ReadLevel(char *filename); void InitGame(void); void G_RunFrame(void); /* =================================================================== */ void ShutdownGame(void) { gi.dprintf("==== ShutdownGame ====\n"); gi.FreeTags(TAG_LEVEL); gi.FreeTags(TAG_GAME); } /* * Returns a pointer to the structure * with all entry points and global * variables */ game_export_t * GetGameAPI(game_import_t *import) { gi = *import; globals.apiversion = GAME_API_VERSION; globals.Init = InitGame; globals.Shutdown = ShutdownGame; globals.SpawnEntities = SpawnEntities; globals.WriteGame = WriteGame; globals.ReadGame = ReadGame; globals.WriteLevel = WriteLevel; globals.ReadLevel = ReadLevel; globals.ClientThink = ClientThink; globals.ClientConnect = ClientConnect; globals.ClientUserinfoChanged = ClientUserinfoChanged; globals.ClientDisconnect = ClientDisconnect; globals.ClientBegin = ClientBegin; globals.ClientCommand = ClientCommand; globals.RunFrame = G_RunFrame; globals.ServerCommand = ServerCommand; globals.edict_size = sizeof(edict_t); /* Initalize the PRNG */ randk_seed(); return &globals; } /* * this is only here so the functions * in shared source files can link */ void Sys_Error(char *error, ...) { va_list argptr; char text[1024]; va_start(argptr, error); vsprintf(text, error, argptr); va_end(argptr); gi.error("%s", text); } void Com_Printf(char *msg, ...) { va_list argptr; char text[1024]; va_start(argptr, msg); vsprintf(text, msg, argptr); va_end(argptr); gi.dprintf("%s", text); } /* ====================================================================== */ void ClientEndServerFrames(void) { int i; edict_t *ent; /* calc the player views now that all pushing and damage has been added */ for (i = 0; i < maxclients->value; i++) { ent = g_edicts + 1 + i; if (!ent->inuse || !ent->client) { continue; } ClientEndServerFrame(ent); } } /* * Returns the created target changelevel */ edict_t * CreateTargetChangeLevel(char *map) { edict_t *ent; if (!map) { return NULL; } ent = G_Spawn(); ent->classname = "target_changelevel"; Com_sprintf(level.nextmap, sizeof(level.nextmap), "%s", map); ent->map = level.nextmap; return ent; } /* * The timelimit or fraglimit has been exceeded */ void EndDMLevel(void) { edict_t *ent; char *s, *t, *f; static const char *seps = " ,\n\r"; /* stay on same level flag */ if ((int)dmflags->value & DF_SAME_LEVEL) { BeginIntermission(CreateTargetChangeLevel(level.mapname)); return; } /* see if it's in the map list */ if (*sv_maplist->string) { s = strdup(sv_maplist->string); f = NULL; t = strtok(s, seps); while (t != NULL) { if (Q_stricmp(t, level.mapname) == 0) { /* it's in the list, go to the next one */ t = strtok(NULL, seps); if (t == NULL) /* end of list, go to first one */ { if (f == NULL) /* there isn't a first one, same level */ { BeginIntermission(CreateTargetChangeLevel(level.mapname)); } else { BeginIntermission(CreateTargetChangeLevel(f)); } } else { BeginIntermission(CreateTargetChangeLevel(t)); } free(s); return; } if (!f) { f = t; } t = strtok(NULL, seps); } free(s); } if (level.nextmap[0]) /* go to a specific map */ { BeginIntermission(CreateTargetChangeLevel(level.nextmap)); } else /* search for a changelevel */ { ent = G_Find(NULL, FOFS(classname), "target_changelevel"); if (!ent) { /* the map designer didn't include a changelevel, so create a fake ent that goes back to the same level */ BeginIntermission(CreateTargetChangeLevel(level.mapname)); return; } BeginIntermission(ent); } } void CheckNeedPass(void) { int need; /* if password or spectator_password has changed, update needpass as needed */ if (password->modified || spectator_password->modified) { password->modified = spectator_password->modified = false; need = 0; if (*password->string && Q_stricmp(password->string, "none")) { need |= 1; } if (*spectator_password->string && Q_stricmp(spectator_password->string, "none")) { need |= 2; } gi.cvar_set("needpass", va("%d", need)); } } void CheckDMRules(void) { int i; gclient_t *cl; if (level.intermissiontime) { return; } if (!deathmatch->value) { return; } if (timelimit->value) { if (level.time >= timelimit->value * 60) { gi.bprintf(PRINT_HIGH, "Timelimit hit.\n"); EndDMLevel(); return; } } if (fraglimit->value) { for (i = 0; i < maxclients->value; i++) { cl = game.clients + i; if (!g_edicts[i + 1].inuse) { continue; } if (cl->resp.score >= fraglimit->value) { gi.bprintf(PRINT_HIGH, "Fraglimit hit.\n"); EndDMLevel(); return; } } } } void ExitLevel(void) { int i; edict_t *ent; char command[256]; Com_sprintf(command, sizeof(command), "gamemap \"%s\"\n", level.changemap); gi.AddCommandString(command); level.changemap = NULL; level.exitintermission = 0; level.intermissiontime = 0; ClientEndServerFrames(); /* clear some things before going to next level */ for (i = 0; i < maxclients->value; i++) { ent = g_edicts + 1 + i; if (!ent->inuse) { continue; } if (ent->health > ent->client->pers.max_health) { ent->health = ent->client->pers.max_health; } } gibsthisframe = 0; lastgibframe = 0; } /* * Advances the world by 0.1 seconds */ void G_RunFrame(void) { int i; edict_t *ent; level.framenum++; level.time = level.framenum * FRAMETIME; /* choose a client for monsters to target this frame */ AI_SetSightClient(); /* exit intermissions */ if (level.exitintermission) { ExitLevel(); return; } /* treat each object in turn even the world gets a chance to think */ ent = &g_edicts[0]; for (i = 0; i < globals.num_edicts; i++, ent++) { if (!ent->inuse) { continue; } level.current_entity = ent; VectorCopy(ent->s.origin, ent->s.old_origin); /* if the ground entity moved, make sure we are still on it */ if ((ent->groundentity) && (ent->groundentity->linkcount != ent->groundentity_linkcount)) { ent->groundentity = NULL; if (!(ent->flags & (FL_SWIM | FL_FLY)) && (ent->svflags & SVF_MONSTER)) { M_CheckGround(ent); } } if ((i > 0) && (i <= maxclients->value)) { ClientBeginServerFrame(ent); continue; } G_RunEntity(ent); } /* see if it is time to end a deathmatch */ CheckDMRules(); /* see if needpass needs updated */ CheckNeedPass(); /* build the playerstate_t structures for all players */ ClientEndServerFrames(); } yquake2-QUAKE2_5_32/src/game/g_func.c0000644000175000017500000015724312615162034017740 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Level functions. Platforms, buttons, dooors and so on. * * ======================================================================= */ #include "header/local.h" /* * ========================================================= * * PLATS * * movement options: * * linear * smooth start, hard stop * smooth start, smooth stop * * start * end * acceleration * speed * deceleration * begin sound * end sound * target fired when reaching end * wait at end * * object characteristics that use move segments * --------------------------------------------- * movetype_push, or movetype_stop * action when touched * action when blocked * action when used * disabled? * auto trigger spawning * * * ========================================================= */ #define PLAT_LOW_TRIGGER 1 #define STATE_TOP 0 #define STATE_BOTTOM 1 #define STATE_UP 2 #define STATE_DOWN 3 #define DOOR_START_OPEN 1 #define DOOR_REVERSE 2 #define DOOR_CRUSHER 4 #define DOOR_NOMONSTER 8 #define DOOR_TOGGLE 32 #define DOOR_X_AXIS 64 #define DOOR_Y_AXIS 128 /* Support routines for movement (changes in origin using velocity) */ void Think_AccelMove(edict_t *ent); void plat_go_down(edict_t *ent); void Move_Done(edict_t *ent) { if (!ent) { return; } VectorClear(ent->velocity); ent->moveinfo.endfunc(ent); } void Move_Final(edict_t *ent) { if (!ent) { return; } if (ent->moveinfo.remaining_distance == 0) { Move_Done(ent); return; } VectorScale(ent->moveinfo.dir, ent->moveinfo.remaining_distance / FRAMETIME, ent->velocity); ent->think = Move_Done; ent->nextthink = level.time + FRAMETIME; } void Move_Begin(edict_t *ent) { float frames; if (!ent) { return; } if ((ent->moveinfo.speed * FRAMETIME) >= ent->moveinfo.remaining_distance) { Move_Final(ent); return; } VectorScale(ent->moveinfo.dir, ent->moveinfo.speed, ent->velocity); frames = floor( (ent->moveinfo.remaining_distance / ent->moveinfo.speed) / FRAMETIME); ent->moveinfo.remaining_distance -= frames * ent->moveinfo.speed * FRAMETIME; ent->nextthink = level.time + (frames * FRAMETIME); ent->think = Move_Final; } void Move_Calc(edict_t *ent, vec3_t dest, void (*func)(edict_t *)) { if (!ent || !func) { return; } VectorClear(ent->velocity); VectorSubtract(dest, ent->s.origin, ent->moveinfo.dir); ent->moveinfo.remaining_distance = VectorNormalize(ent->moveinfo.dir); ent->moveinfo.endfunc = func; if ((ent->moveinfo.speed == ent->moveinfo.accel) && (ent->moveinfo.speed == ent->moveinfo.decel)) { if (level.current_entity == ((ent->flags & FL_TEAMSLAVE) ? ent->teammaster : ent)) { Move_Begin(ent); } else { ent->nextthink = level.time + FRAMETIME; ent->think = Move_Begin; } } else { /* accelerative */ ent->moveinfo.current_speed = 0; ent->think = Think_AccelMove; ent->nextthink = level.time + FRAMETIME; } } /* Support routines for angular movement (changes in angle using avelocity) */ void AngleMove_Done(edict_t *ent) { if (!ent) { return; } VectorClear(ent->avelocity); ent->moveinfo.endfunc(ent); } void AngleMove_Final(edict_t *ent) { vec3_t move; if (!ent) { return; } if (ent->moveinfo.state == STATE_UP) { VectorSubtract(ent->moveinfo.end_angles, ent->s.angles, move); } else { VectorSubtract(ent->moveinfo.start_angles, ent->s.angles, move); } if (VectorCompare(move, vec3_origin)) { AngleMove_Done(ent); return; } VectorScale(move, 1.0 / FRAMETIME, ent->avelocity); ent->think = AngleMove_Done; ent->nextthink = level.time + FRAMETIME; } void AngleMove_Begin(edict_t *ent) { vec3_t destdelta; float len; float traveltime; float frames; if (!ent) { return; } /* set destdelta to the vector needed to move */ if (ent->moveinfo.state == STATE_UP) { VectorSubtract(ent->moveinfo.end_angles, ent->s.angles, destdelta); } else { VectorSubtract(ent->moveinfo.start_angles, ent->s.angles, destdelta); } /* calculate length of vector */ len = VectorLength(destdelta); /* divide by speed to get time to reach dest */ traveltime = len / ent->moveinfo.speed; if (traveltime < FRAMETIME) { AngleMove_Final(ent); return; } frames = floor(traveltime / FRAMETIME); /* scale the destdelta vector by the time spent traveling to get velocity */ VectorScale(destdelta, 1.0 / traveltime, ent->avelocity); /* set nextthink to trigger a think when dest is reached */ ent->nextthink = level.time + frames * FRAMETIME; ent->think = AngleMove_Final; } void AngleMove_Calc(edict_t *ent, void (*func)(edict_t *)) { if (!ent || !func) { return; } VectorClear(ent->avelocity); ent->moveinfo.endfunc = func; if (level.current_entity == ((ent->flags & FL_TEAMSLAVE) ? ent->teammaster : ent)) { AngleMove_Begin(ent); } else { ent->nextthink = level.time + FRAMETIME; ent->think = AngleMove_Begin; } } #define AccelerationDistance(target, rate) (target * ((target / rate) + 1) / 2) void plat_CalcAcceleratedMove(moveinfo_t *moveinfo) { float accel_dist; float decel_dist; if (!moveinfo) { return; } moveinfo->move_speed = moveinfo->speed; if (moveinfo->remaining_distance < moveinfo->accel) { moveinfo->current_speed = moveinfo->remaining_distance; return; } accel_dist = AccelerationDistance(moveinfo->speed, moveinfo->accel); decel_dist = AccelerationDistance(moveinfo->speed, moveinfo->decel); if ((moveinfo->remaining_distance - accel_dist - decel_dist) < 0) { float f; f = (moveinfo->accel + moveinfo->decel) / (moveinfo->accel * moveinfo->decel); moveinfo->move_speed = (-2 + sqrt(4 - 4 * f * (-2 * moveinfo->remaining_distance))) / (2 * f); decel_dist = AccelerationDistance(moveinfo->move_speed, moveinfo->decel); } moveinfo->decel_distance = decel_dist; } void plat_Accelerate(moveinfo_t *moveinfo) { if (!moveinfo) { return; } /* are we decelerating? */ if (moveinfo->remaining_distance <= moveinfo->decel_distance) { if (moveinfo->remaining_distance < moveinfo->decel_distance) { if (moveinfo->next_speed) { moveinfo->current_speed = moveinfo->next_speed; moveinfo->next_speed = 0; return; } if (moveinfo->current_speed > moveinfo->decel) { moveinfo->current_speed -= moveinfo->decel; } } return; } /* are we at full speed and need to start decelerating during this move? */ if (moveinfo->current_speed == moveinfo->move_speed) { if ((moveinfo->remaining_distance - moveinfo->current_speed) < moveinfo->decel_distance) { float p1_distance; float p2_distance; float distance; p1_distance = moveinfo->remaining_distance - moveinfo->decel_distance; p2_distance = moveinfo->move_speed * (1.0 - (p1_distance / moveinfo->move_speed)); distance = p1_distance + p2_distance; moveinfo->current_speed = moveinfo->move_speed; moveinfo->next_speed = moveinfo->move_speed - moveinfo->decel * (p2_distance / distance); return; } } /* are we accelerating? */ if (moveinfo->current_speed < moveinfo->speed) { float old_speed; float p1_distance; float p1_speed; float p2_distance; float distance; old_speed = moveinfo->current_speed; /* figure simple acceleration up to move_speed */ moveinfo->current_speed += moveinfo->accel; if (moveinfo->current_speed > moveinfo->speed) { moveinfo->current_speed = moveinfo->speed; } /* are we accelerating throughout this entire move? */ if ((moveinfo->remaining_distance - moveinfo->current_speed) >= moveinfo->decel_distance) { return; } /* during this move we will accelrate from current_speed to move_speed and cross over the decel_distance; figure the average speed for the entire move */ p1_distance = moveinfo->remaining_distance - moveinfo->decel_distance; p1_speed = (old_speed + moveinfo->move_speed) / 2.0; p2_distance = moveinfo->move_speed * (1.0 - (p1_distance / p1_speed)); distance = p1_distance + p2_distance; moveinfo->current_speed = (p1_speed * (p1_distance / distance)) + (moveinfo->move_speed * (p2_distance / distance)); moveinfo->next_speed = moveinfo->move_speed - moveinfo->decel * (p2_distance / distance); return; } /* we are at constant velocity (move_speed) */ return; } /* * The team has completed a frame of movement, * so change the speed for the next frame */ void Think_AccelMove(edict_t *ent) { if (!ent) { return; } ent->moveinfo.remaining_distance -= ent->moveinfo.current_speed; if (ent->moveinfo.current_speed == 0) /* starting or blocked */ { plat_CalcAcceleratedMove(&ent->moveinfo); } plat_Accelerate(&ent->moveinfo); /* will the entire move complete on next frame? */ if (ent->moveinfo.remaining_distance <= ent->moveinfo.current_speed) { Move_Final(ent); return; } VectorScale(ent->moveinfo.dir, ent->moveinfo.current_speed * 10, ent->velocity); ent->nextthink = level.time + FRAMETIME; ent->think = Think_AccelMove; } void plat_hit_top(edict_t *ent) { if (!ent) { return; } if (!(ent->flags & FL_TEAMSLAVE)) { if (ent->moveinfo.sound_end) { gi.sound(ent, CHAN_NO_PHS_ADD + CHAN_VOICE, ent->moveinfo.sound_end, 1, ATTN_STATIC, 0); } ent->s.sound = 0; } ent->moveinfo.state = STATE_TOP; ent->think = plat_go_down; ent->nextthink = level.time + 3; } void plat_hit_bottom(edict_t *ent) { if (!ent) { return; } if (!(ent->flags & FL_TEAMSLAVE)) { if (ent->moveinfo.sound_end) { gi.sound(ent, CHAN_NO_PHS_ADD + CHAN_VOICE, ent->moveinfo.sound_end, 1, ATTN_STATIC, 0); } ent->s.sound = 0; } ent->moveinfo.state = STATE_BOTTOM; } void plat_go_down(edict_t *ent) { if (!ent) { return; } if (!(ent->flags & FL_TEAMSLAVE)) { if (ent->moveinfo.sound_start) { gi.sound(ent, CHAN_NO_PHS_ADD + CHAN_VOICE, ent->moveinfo.sound_start, 1, ATTN_STATIC, 0); } ent->s.sound = ent->moveinfo.sound_middle; } ent->moveinfo.state = STATE_DOWN; Move_Calc(ent, ent->moveinfo.end_origin, plat_hit_bottom); } void plat_go_up(edict_t *ent) { if (!ent) { return; } if (!(ent->flags & FL_TEAMSLAVE)) { if (ent->moveinfo.sound_start) { gi.sound(ent, CHAN_NO_PHS_ADD + CHAN_VOICE, ent->moveinfo.sound_start, 1, ATTN_STATIC, 0); } ent->s.sound = ent->moveinfo.sound_middle; } ent->moveinfo.state = STATE_UP; Move_Calc(ent, ent->moveinfo.start_origin, plat_hit_top); } void plat_blocked(edict_t *self, edict_t *other) { if (!self || !other) { return; } if (!(other->svflags & SVF_MONSTER) && (!other->client)) { /* give it a chance to go away on it's own terms (like gibs) */ T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH); /* if it's still there, nuke it */ if (other) { /* Hack for entity without it's origin near the model */ VectorMA (other->absmin, 0.5, other->size, other->s.origin); BecomeExplosion1(other); } return; } T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH); if (self->moveinfo.state == STATE_UP) { plat_go_down(self); } else if (self->moveinfo.state == STATE_DOWN) { plat_go_up(self); } } void Use_Plat(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!ent) { return; } if (ent->think) { return; /* already down */ } plat_go_down(ent); } void Touch_Plat_Center(edict_t *ent, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { if (!ent || !other) { return; } if (!other->client) { return; } if (other->health <= 0) { return; } ent = ent->enemy; /* now point at the plat, not the trigger */ if (ent->moveinfo.state == STATE_BOTTOM) { plat_go_up(ent); } else if (ent->moveinfo.state == STATE_TOP) { /* the player is still on the plat, so delay going down */ ent->nextthink = level.time + 1; } } void plat_spawn_inside_trigger(edict_t *ent) { if (!ent) { return; } edict_t *trigger; vec3_t tmin, tmax; /* middle trigger */ trigger = G_Spawn(); trigger->touch = Touch_Plat_Center; trigger->movetype = MOVETYPE_NONE; trigger->solid = SOLID_TRIGGER; trigger->enemy = ent; tmin[0] = ent->mins[0] + 25; tmin[1] = ent->mins[1] + 25; tmin[2] = ent->mins[2]; tmax[0] = ent->maxs[0] - 25; tmax[1] = ent->maxs[1] - 25; tmax[2] = ent->maxs[2] + 8; tmin[2] = tmax[2] - (ent->pos1[2] - ent->pos2[2] + st.lip); if (ent->spawnflags & PLAT_LOW_TRIGGER) { tmax[2] = tmin[2] + 8; } if (tmax[0] - tmin[0] <= 0) { tmin[0] = (ent->mins[0] + ent->maxs[0]) * 0.5; tmax[0] = tmin[0] + 1; } if (tmax[1] - tmin[1] <= 0) { tmin[1] = (ent->mins[1] + ent->maxs[1]) * 0.5; tmax[1] = tmin[1] + 1; } VectorCopy(tmin, trigger->mins); VectorCopy(tmax, trigger->maxs); gi.linkentity(trigger); } /* * QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER * * speed -> default 150 * * Plats are always drawn in the extended position, * so they will light correctly. * * If the plat is the target of another trigger or button, * it will start out disabled in the extended position until * it is trigger, when it will lower and become a normal plat. * * "speed" overrides default 200. * "accel" overrides default 500 * "lip" overrides default 8 pixel lip * * If the "height" key is set, that will determine the amount * the plat moves, instead of being implicitly determoveinfoned * by the model's height. * * Set "sounds" to one of the following: * 1) base fast * 2) chain slow */ void SP_func_plat(edict_t *ent) { if (!ent) { return; } VectorClear(ent->s.angles); ent->solid = SOLID_BSP; ent->movetype = MOVETYPE_PUSH; gi.setmodel(ent, ent->model); ent->blocked = plat_blocked; if (!ent->speed) { ent->speed = 20; } else { ent->speed *= 0.1; } if (!ent->accel) { ent->accel = 5; } else { ent->accel *= 0.1; } if (!ent->decel) { ent->decel = 5; } else { ent->decel *= 0.1; } if (!ent->dmg) { ent->dmg = 2; } if (!st.lip) { st.lip = 8; } /* pos1 is the top position, pos2 is the bottom */ VectorCopy(ent->s.origin, ent->pos1); VectorCopy(ent->s.origin, ent->pos2); if (st.height) { ent->pos2[2] -= st.height; } else { ent->pos2[2] -= (ent->maxs[2] - ent->mins[2]) - st.lip; } ent->use = Use_Plat; plat_spawn_inside_trigger(ent); /* the "start moving" trigger */ if (ent->targetname) { ent->moveinfo.state = STATE_UP; } else { VectorCopy(ent->pos2, ent->s.origin); gi.linkentity(ent); ent->moveinfo.state = STATE_BOTTOM; } ent->moveinfo.speed = ent->speed; ent->moveinfo.accel = ent->accel; ent->moveinfo.decel = ent->decel; ent->moveinfo.wait = ent->wait; VectorCopy(ent->pos1, ent->moveinfo.start_origin); VectorCopy(ent->s.angles, ent->moveinfo.start_angles); VectorCopy(ent->pos2, ent->moveinfo.end_origin); VectorCopy(ent->s.angles, ent->moveinfo.end_angles); ent->moveinfo.sound_start = gi.soundindex("plats/pt1_strt.wav"); ent->moveinfo.sound_middle = gi.soundindex("plats/pt1_mid.wav"); ent->moveinfo.sound_end = gi.soundindex("plats/pt1_end.wav"); } /* ==================================================================== */ /* * QUAKED func_rotating (0 .5 .8) ? START_ON REVERSE X_AXIS Y_AXIS * TOUCH_PAIN STOP ANIMATED ANIMATED_FAST * * You need to have an origin brush as part of this entity. * The center of that brush will be the point around which it * is rotated. It will rotate around the Z axis by default. * You can check either the X_AXIS or Y_AXIS box to change that. * * "speed" determines how fast it moves; default value is 100. * "dmg" damage to inflict when blocked (2 default) * * REVERSE will cause the it to rotate in the opposite direction. * STOP mean it will stop moving instead of pushing entities */ void rotating_blocked(edict_t *self, edict_t *other) { if (!self || !other) { return; } T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH); } void rotating_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { if (!self || !other) { return; } if (self->avelocity[0] || self->avelocity[1] || self->avelocity[2]) { T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH); } } void rotating_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!self) { return; } if (!VectorCompare(self->avelocity, vec3_origin)) { self->s.sound = 0; VectorClear(self->avelocity); self->touch = NULL; } else { self->s.sound = self->moveinfo.sound_middle; VectorScale(self->movedir, self->speed, self->avelocity); if (self->spawnflags & 16) { self->touch = rotating_touch; } } } void SP_func_rotating(edict_t *ent) { if (!ent) { return; } ent->solid = SOLID_BSP; if (ent->spawnflags & 32) { ent->movetype = MOVETYPE_STOP; } else { ent->movetype = MOVETYPE_PUSH; } /* set the axis of rotation */ VectorClear(ent->movedir); if (ent->spawnflags & 4) { ent->movedir[2] = 1.0; } else if (ent->spawnflags & 8) { ent->movedir[0] = 1.0; } else /* Z_AXIS */ { ent->movedir[1] = 1.0; } /* check for reverse rotation */ if (ent->spawnflags & 2) { VectorNegate(ent->movedir, ent->movedir); } if (!ent->speed) { ent->speed = 100; } if (!ent->dmg) { ent->dmg = 2; } ent->use = rotating_use; if (ent->dmg) { ent->blocked = rotating_blocked; } if (ent->spawnflags & 1) { ent->use(ent, NULL, NULL); } if (ent->spawnflags & 64) { ent->s.effects |= EF_ANIM_ALL; } if (ent->spawnflags & 128) { ent->s.effects |= EF_ANIM_ALLFAST; } gi.setmodel(ent, ent->model); gi.linkentity(ent); } /* ==================================================================== */ /* BUTTONS */ /* * QUAKED func_button (0 .5 .8) ? * * When a button is touched, it moves some distance * in the direction of it's angle, triggers all of it's * targets, waits some time, then returns to it's original * position where it can be triggered again. * * "angle" determines the opening direction * "target" all entities with a matching targetname will be used * "speed" override the default 40 speed * "wait" override the default 1 second wait (-1 = never return) * "lip" override the default 4 pixel lip remaining at end of move * "health" if set, the button must be killed instead of touched * "sounds" * 1) silent * 2) steam metal * 3) wooden clunk * 4) metallic click * 5) in-out */ void button_done(edict_t *self) { if (!self) { return; } self->moveinfo.state = STATE_BOTTOM; self->s.effects &= ~EF_ANIM23; self->s.effects |= EF_ANIM01; } void button_return(edict_t *self) { if (!self) { return; } self->moveinfo.state = STATE_DOWN; Move_Calc(self, self->moveinfo.start_origin, button_done); self->s.frame = 0; if (self->health) { self->takedamage = DAMAGE_YES; } } void button_wait(edict_t *self) { if (!self) { return; } self->moveinfo.state = STATE_TOP; self->s.effects &= ~EF_ANIM01; self->s.effects |= EF_ANIM23; G_UseTargets(self, self->activator); self->s.frame = 1; if (self->moveinfo.wait >= 0) { self->nextthink = level.time + self->moveinfo.wait; self->think = button_return; } } void button_fire(edict_t *self) { if (!self) { return; } if ((self->moveinfo.state == STATE_UP) || (self->moveinfo.state == STATE_TOP)) { return; } self->moveinfo.state = STATE_UP; if (self->moveinfo.sound_start && !(self->flags & FL_TEAMSLAVE)) { gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0); } Move_Calc(self, self->moveinfo.end_origin, button_wait); } void button_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) { if (!self ||!activator) { return; } self->activator = activator; button_fire(self); } void button_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { if (!self || !other) { return; } if (!other->client) { return; } if (other->health <= 0) { return; } self->activator = other; button_fire(self); } void button_killed(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unsued */, int damage /* unused */, vec3_t point /* unused */) { if (!self) { return; } self->activator = attacker; self->health = self->max_health; self->takedamage = DAMAGE_NO; button_fire(self); } void SP_func_button(edict_t *ent) { vec3_t abs_movedir; float dist; if (!ent) { return; } G_SetMovedir(ent->s.angles, ent->movedir); ent->movetype = MOVETYPE_STOP; ent->solid = SOLID_BSP; gi.setmodel(ent, ent->model); if (ent->sounds != 1) { ent->moveinfo.sound_start = gi.soundindex("switches/butn2.wav"); } if (!ent->speed) { ent->speed = 40; } if (!ent->accel) { ent->accel = ent->speed; } if (!ent->decel) { ent->decel = ent->speed; } if (!ent->wait) { ent->wait = 3; } if (!st.lip) { st.lip = 4; } VectorCopy(ent->s.origin, ent->pos1); abs_movedir[0] = fabs(ent->movedir[0]); abs_movedir[1] = fabs(ent->movedir[1]); abs_movedir[2] = fabs(ent->movedir[2]); dist = abs_movedir[0] * ent->size[0] + abs_movedir[1] * ent->size[1] + abs_movedir[2] * ent->size[2] - st.lip; VectorMA(ent->pos1, dist, ent->movedir, ent->pos2); ent->use = button_use; ent->s.effects |= EF_ANIM01; if (ent->health) { ent->max_health = ent->health; ent->die = button_killed; ent->takedamage = DAMAGE_YES; } else if (!ent->targetname) { ent->touch = button_touch; } ent->moveinfo.state = STATE_BOTTOM; ent->moveinfo.speed = ent->speed; ent->moveinfo.accel = ent->accel; ent->moveinfo.decel = ent->decel; ent->moveinfo.wait = ent->wait; VectorCopy(ent->pos1, ent->moveinfo.start_origin); VectorCopy(ent->s.angles, ent->moveinfo.start_angles); VectorCopy(ent->pos2, ent->moveinfo.end_origin); VectorCopy(ent->s.angles, ent->moveinfo.end_angles); gi.linkentity(ent); } /* ==================================================================== */ /* * DOORS * * spawn a trigger surrounding the entire team * unless it is already targeted by another */ void door_go_down(edict_t *self); /* * QUAKED func_door (0 .5 .8) ? START_OPEN x CRUSHER NOMONSTER ANIMATED TOGGLE ANIMATED_FAST * * TOGGLE wait in both the start and end states for a trigger event. * START_OPEN the door to moves to its destination when spawned, and operate in reverse. * It is used to temporarily or permanently close off an area when triggered * (not useful for touch or takedamage doors). * NOMONSTER monsters will not trigger this door * * "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet * "angle" determines the opening direction * "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. * "health" if set, door must be shot open * "speed" movement speed (100 default) * "wait" wait before returning (3 default, -1 = never return) * "lip" lip remaining at end of move (8 default) * "dmg" damage to inflict when blocked (2 default) * "sounds" * 1) silent * 2) light * 3) medium * 4) heavy */ void door_use_areaportals(edict_t *self, qboolean open) { edict_t *t = NULL; if (!self) { return; } if (!self->target) { return; } while ((t = G_Find(t, FOFS(targetname), self->target))) { if (Q_stricmp(t->classname, "func_areaportal") == 0) { gi.SetAreaPortalState(t->style, open); } } } void door_hit_top(edict_t *self) { if (!self) { return; } if (!(self->flags & FL_TEAMSLAVE)) { if (self->moveinfo.sound_end) { gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0); } self->s.sound = 0; } self->moveinfo.state = STATE_TOP; if (self->spawnflags & DOOR_TOGGLE) { return; } if (self->moveinfo.wait >= 0) { self->think = door_go_down; self->nextthink = level.time + self->moveinfo.wait; } } void door_hit_bottom(edict_t *self) { if (!self) { return; } if (!(self->flags & FL_TEAMSLAVE)) { if (self->moveinfo.sound_end) { gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0); } self->s.sound = 0; } self->moveinfo.state = STATE_BOTTOM; door_use_areaportals(self, false); } void door_go_down(edict_t *self) { if (!self) { return; } if (!(self->flags & FL_TEAMSLAVE)) { if (self->moveinfo.sound_start) { gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0); } self->s.sound = self->moveinfo.sound_middle; } if (self->max_health) { self->takedamage = DAMAGE_YES; self->health = self->max_health; } self->moveinfo.state = STATE_DOWN; if (strcmp(self->classname, "func_door") == 0) { Move_Calc(self, self->moveinfo.start_origin, door_hit_bottom); } else if (strcmp(self->classname, "func_door_rotating") == 0) { AngleMove_Calc(self, door_hit_bottom); } } void door_go_up(edict_t *self, edict_t *activator) { if (!self || !activator) { return; } if (self->moveinfo.state == STATE_UP) { return; /* already going up */ } if (self->moveinfo.state == STATE_TOP) { /* reset top wait time */ if (self->moveinfo.wait >= 0) { self->nextthink = level.time + self->moveinfo.wait; } return; } if (!(self->flags & FL_TEAMSLAVE)) { if (self->moveinfo.sound_start) { gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0); } self->s.sound = self->moveinfo.sound_middle; } self->moveinfo.state = STATE_UP; if (strcmp(self->classname, "func_door") == 0) { Move_Calc(self, self->moveinfo.end_origin, door_hit_top); } else if (strcmp(self->classname, "func_door_rotating") == 0) { AngleMove_Calc(self, door_hit_top); } G_UseTargets(self, activator); door_use_areaportals(self, true); } void door_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) { if (!self || !activator) { return; } edict_t *ent; if (self->flags & FL_TEAMSLAVE) { return; } if (self->spawnflags & DOOR_TOGGLE) { if ((self->moveinfo.state == STATE_UP) || (self->moveinfo.state == STATE_TOP)) { /* trigger all paired doors */ for (ent = self; ent; ent = ent->teamchain) { ent->message = NULL; ent->touch = NULL; door_go_down(ent); } return; } } /* trigger all paired doors */ for (ent = self; ent; ent = ent->teamchain) { ent->message = NULL; ent->touch = NULL; door_go_up(ent, activator); } } void Touch_DoorTrigger(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { if (!self || !other) { return; } if (other->health <= 0) { return; } if (!(other->svflags & SVF_MONSTER) && (!other->client)) { return; } if ((self->owner->spawnflags & DOOR_NOMONSTER) && (other->svflags & SVF_MONSTER)) { return; } if (level.time < self->touch_debounce_time) { return; } self->touch_debounce_time = level.time + 1.0; door_use(self->owner, other, other); } void Think_CalcMoveSpeed(edict_t *self) { edict_t *ent; float min; float time; float newspeed; float ratio; float dist; if (!self) { return; } if (self->flags & FL_TEAMSLAVE) { return; /* only the team master does this */ } /* find the smallest distance any member of the team will be moving */ min = fabs(self->moveinfo.distance); for (ent = self->teamchain; ent; ent = ent->teamchain) { dist = fabs(ent->moveinfo.distance); if (dist < min) { min = dist; } } time = min / self->moveinfo.speed; /* adjust speeds so they will all complete at the same time */ for (ent = self; ent; ent = ent->teamchain) { newspeed = fabs(ent->moveinfo.distance) / time; ratio = newspeed / ent->moveinfo.speed; if (ent->moveinfo.accel == ent->moveinfo.speed) { ent->moveinfo.accel = newspeed; } else { ent->moveinfo.accel *= ratio; } if (ent->moveinfo.decel == ent->moveinfo.speed) { ent->moveinfo.decel = newspeed; } else { ent->moveinfo.decel *= ratio; } ent->moveinfo.speed = newspeed; } } void Think_SpawnDoorTrigger(edict_t *ent) { edict_t *other; vec3_t mins, maxs; if (!ent) { return; } if (ent->flags & FL_TEAMSLAVE) { return; /* only the team leader spawns a trigger */ } VectorCopy(ent->absmin, mins); VectorCopy(ent->absmax, maxs); for (other = ent->teamchain; other; other = other->teamchain) { AddPointToBounds(other->absmin, mins, maxs); AddPointToBounds(other->absmax, mins, maxs); } /* expand */ mins[0] -= 60; mins[1] -= 60; maxs[0] += 60; maxs[1] += 60; other = G_Spawn(); VectorCopy(mins, other->mins); VectorCopy(maxs, other->maxs); other->owner = ent; other->solid = SOLID_TRIGGER; other->movetype = MOVETYPE_NONE; other->touch = Touch_DoorTrigger; gi.linkentity(other); if (ent->spawnflags & DOOR_START_OPEN) { door_use_areaportals(ent, true); } Think_CalcMoveSpeed(ent); } void door_blocked(edict_t *self, edict_t *other) { edict_t *ent; if (!self || !other) { return; } if (!(other->svflags & SVF_MONSTER) && (!other->client)) { /* give it a chance to go away on it's own terms (like gibs) */ T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH); /* if it's still there, nuke it */ if (other) { /* Hack for entitiy without their origin near the model */ VectorMA (other->absmin, 0.5, other->size, other->s.origin); BecomeExplosion1(other); } return; } T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH); if (self->spawnflags & DOOR_CRUSHER) { return; } /* if a door has a negative wait, it would never come back if blocked, so let it just squash the object to death real fast */ if (self->moveinfo.wait >= 0) { if (self->moveinfo.state == STATE_DOWN) { for (ent = self->teammaster; ent; ent = ent->teamchain) { door_go_up(ent, ent->activator); } } else { for (ent = self->teammaster; ent; ent = ent->teamchain) { door_go_down(ent); } } } } void door_killed(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker, int damage /* unused */, vec3_t point /* unused */) { edict_t *ent; if (!self || !attacker) { return; } for (ent = self->teammaster; ent; ent = ent->teamchain) { ent->health = ent->max_health; ent->takedamage = DAMAGE_NO; } door_use(self->teammaster, attacker, attacker); } void door_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { if (!self || !other) { return; } if (!other->client) { return; } if (level.time < self->touch_debounce_time) { return; } self->touch_debounce_time = level.time + 5.0; gi.centerprintf(other, "%s", self->message); gi.sound(other, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0); } void SP_func_door(edict_t *ent) { vec3_t abs_movedir; if (!ent) { return; } if (ent->sounds != 1) { ent->moveinfo.sound_start = gi.soundindex("doors/dr1_strt.wav"); ent->moveinfo.sound_middle = gi.soundindex("doors/dr1_mid.wav"); ent->moveinfo.sound_end = gi.soundindex("doors/dr1_end.wav"); } G_SetMovedir(ent->s.angles, ent->movedir); ent->movetype = MOVETYPE_PUSH; ent->solid = SOLID_BSP; gi.setmodel(ent, ent->model); ent->blocked = door_blocked; ent->use = door_use; if (!ent->speed) { ent->speed = 100; } if (deathmatch->value) { ent->speed *= 2; } if (!ent->accel) { ent->accel = ent->speed; } if (!ent->decel) { ent->decel = ent->speed; } if (!ent->wait) { ent->wait = 3; } if (!st.lip) { st.lip = 8; } if (!ent->dmg) { ent->dmg = 2; } /* calculate second position */ VectorCopy(ent->s.origin, ent->pos1); abs_movedir[0] = fabs(ent->movedir[0]); abs_movedir[1] = fabs(ent->movedir[1]); abs_movedir[2] = fabs(ent->movedir[2]); ent->moveinfo.distance = abs_movedir[0] * ent->size[0] + abs_movedir[1] * ent->size[1] + abs_movedir[2] * ent->size[2] - st.lip; VectorMA(ent->pos1, ent->moveinfo.distance, ent->movedir, ent->pos2); /* if it starts open, switch the positions */ if (ent->spawnflags & DOOR_START_OPEN) { VectorCopy(ent->pos2, ent->s.origin); VectorCopy(ent->pos1, ent->pos2); VectorCopy(ent->s.origin, ent->pos1); } ent->moveinfo.state = STATE_BOTTOM; if (ent->health) { ent->takedamage = DAMAGE_YES; ent->die = door_killed; ent->max_health = ent->health; } else if (ent->targetname && ent->message) { gi.soundindex("misc/talk.wav"); ent->touch = door_touch; } ent->moveinfo.speed = ent->speed; ent->moveinfo.accel = ent->accel; ent->moveinfo.decel = ent->decel; ent->moveinfo.wait = ent->wait; VectorCopy(ent->pos1, ent->moveinfo.start_origin); VectorCopy(ent->s.angles, ent->moveinfo.start_angles); VectorCopy(ent->pos2, ent->moveinfo.end_origin); VectorCopy(ent->s.angles, ent->moveinfo.end_angles); if (ent->spawnflags & 16) { ent->s.effects |= EF_ANIM_ALL; } if (ent->spawnflags & 64) { ent->s.effects |= EF_ANIM_ALLFAST; } /* to simplify logic elsewhere, make non-teamed doors into a team of one */ if (!ent->team) { ent->teammaster = ent; } gi.linkentity(ent); ent->nextthink = level.time + FRAMETIME; if (ent->health || ent->targetname) { ent->think = Think_CalcMoveSpeed; } else { ent->think = Think_SpawnDoorTrigger; } } /* * QUAKED func_door_rotating (0 .5 .8) ? START_OPEN REVERSE CRUSHER NOMONSTER ANIMATED TOGGLE X_AXIS Y_AXIS * * TOGGLE causes the door to wait in both the start and end states for a trigger event. * START_OPEN the door to moves to its destination when spawned, and operate in reverse. * It is used to temporarily or permanently close off an area when triggered * (not useful for touch or takedamage doors). * NOMONSTER monsters will not trigger this door * * You need to have an origin brush as part of this entity. The center of that brush will be * the point around which it is rotated. It will rotate around the Z axis by default. You can * check either the X_AXIS or Y_AXIS box to change that. * * "distance" is how many degrees the door will be rotated. * "speed" determines how fast the door moves; default value is 100. * * REVERSE will cause the door to rotate in the opposite direction. * * "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet * "angle" determines the opening direction * "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. * "health" if set, door must be shot open * "speed" movement speed (100 default) * "wait" wait before returning (3 default, -1 = never return) * "dmg" damage to inflict when blocked (2 default) * "sounds" * 1) silent * 2) light * 3) medium * 4) heavy */ void SP_func_door_rotating(edict_t *ent) { if (!ent) { return; } VectorClear(ent->s.angles); /* set the axis of rotation */ VectorClear(ent->movedir); if (ent->spawnflags & DOOR_X_AXIS) { ent->movedir[2] = 1.0; } else if (ent->spawnflags & DOOR_Y_AXIS) { ent->movedir[0] = 1.0; } else /* Z_AXIS */ { ent->movedir[1] = 1.0; } /* check for reverse rotation */ if (ent->spawnflags & DOOR_REVERSE) { VectorNegate(ent->movedir, ent->movedir); } if (!st.distance) { gi.dprintf("%s at %s with no distance set\n", ent->classname, vtos(ent->s.origin)); st.distance = 90; } VectorCopy(ent->s.angles, ent->pos1); VectorMA(ent->s.angles, st.distance, ent->movedir, ent->pos2); ent->moveinfo.distance = st.distance; ent->movetype = MOVETYPE_PUSH; ent->solid = SOLID_BSP; gi.setmodel(ent, ent->model); ent->blocked = door_blocked; ent->use = door_use; if (!ent->speed) { ent->speed = 100; } if (!ent->accel) { ent->accel = ent->speed; } if (!ent->decel) { ent->decel = ent->speed; } if (!ent->wait) { ent->wait = 3; } if (!ent->dmg) { ent->dmg = 2; } if (ent->sounds != 1) { ent->moveinfo.sound_start = gi.soundindex("doors/dr1_strt.wav"); ent->moveinfo.sound_middle = gi.soundindex("doors/dr1_mid.wav"); ent->moveinfo.sound_end = gi.soundindex("doors/dr1_end.wav"); } /* if it starts open, switch the positions */ if (ent->spawnflags & DOOR_START_OPEN) { VectorCopy(ent->pos2, ent->s.angles); VectorCopy(ent->pos1, ent->pos2); VectorCopy(ent->s.angles, ent->pos1); VectorNegate(ent->movedir, ent->movedir); } if (ent->health) { ent->takedamage = DAMAGE_YES; ent->die = door_killed; ent->max_health = ent->health; } if (ent->targetname && ent->message) { gi.soundindex("misc/talk.wav"); ent->touch = door_touch; } ent->moveinfo.state = STATE_BOTTOM; ent->moveinfo.speed = ent->speed; ent->moveinfo.accel = ent->accel; ent->moveinfo.decel = ent->decel; ent->moveinfo.wait = ent->wait; VectorCopy(ent->s.origin, ent->moveinfo.start_origin); VectorCopy(ent->pos1, ent->moveinfo.start_angles); VectorCopy(ent->s.origin, ent->moveinfo.end_origin); VectorCopy(ent->pos2, ent->moveinfo.end_angles); if (ent->spawnflags & 16) { ent->s.effects |= EF_ANIM_ALL; } /* to simplify logic elsewhere, make non-teamed doors into a team of one */ if (!ent->team) { ent->teammaster = ent; } gi.linkentity(ent); ent->nextthink = level.time + FRAMETIME; if (ent->health || ent->targetname) { ent->think = Think_CalcMoveSpeed; } else { ent->think = Think_SpawnDoorTrigger; } } /* ==================================================================== */ /* * QUAKED func_water (0 .5 .8) ? START_OPEN * * func_water is a moveable water brush. It must be targeted to operate. * Use a non-water texture at your own risk. * * START_OPEN causes the water to move to its destination when spawned * and operate in reverse. * * "angle" determines the opening direction (up or down only) * "speed" movement speed (25 default) * "wait" wait before returning (-1 default, -1 = TOGGLE) * "lip" lip remaining at end of move (0 default) * "sounds" (yes, these need to be changed) * 0) no sound * 1) water * 2) lava */ void SP_func_water(edict_t *self) { vec3_t abs_movedir; if (!self) { return; } G_SetMovedir(self->s.angles, self->movedir); self->movetype = MOVETYPE_PUSH; self->solid = SOLID_BSP; gi.setmodel(self, self->model); switch (self->sounds) { default: break; case 1: /* water */ self->moveinfo.sound_start = gi.soundindex("world/mov_watr.wav"); self->moveinfo.sound_end = gi.soundindex("world/stp_watr.wav"); break; case 2: /* lava */ self->moveinfo.sound_start = gi.soundindex("world/mov_watr.wav"); self->moveinfo.sound_end = gi.soundindex("world/stp_watr.wav"); break; } /* calculate second position */ VectorCopy(self->s.origin, self->pos1); abs_movedir[0] = fabs(self->movedir[0]); abs_movedir[1] = fabs(self->movedir[1]); abs_movedir[2] = fabs(self->movedir[2]); self->moveinfo.distance = abs_movedir[0] * self->size[0] + abs_movedir[1] * self->size[1] + abs_movedir[2] * self->size[2] - st.lip; VectorMA(self->pos1, self->moveinfo.distance, self->movedir, self->pos2); /* if it starts open, switch the positions */ if (self->spawnflags & DOOR_START_OPEN) { VectorCopy(self->pos2, self->s.origin); VectorCopy(self->pos1, self->pos2); VectorCopy(self->s.origin, self->pos1); } VectorCopy(self->pos1, self->moveinfo.start_origin); VectorCopy(self->s.angles, self->moveinfo.start_angles); VectorCopy(self->pos2, self->moveinfo.end_origin); VectorCopy(self->s.angles, self->moveinfo.end_angles); self->moveinfo.state = STATE_BOTTOM; if (!self->speed) { self->speed = 25; } self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed = self->speed; if (!self->wait) { self->wait = -1; } self->moveinfo.wait = self->wait; self->use = door_use; if (self->wait == -1) { self->spawnflags |= DOOR_TOGGLE; } self->classname = "func_door"; gi.linkentity(self); } /* ==================================================================== */ #define TRAIN_START_ON 1 #define TRAIN_TOGGLE 2 #define TRAIN_BLOCK_STOPS 4 void train_next(edict_t *self); /* * QUAKED func_train (0 .5 .8) ? START_ON TOGGLE BLOCK_STOPS * * Trains are moving platforms that players can ride. * The targets origin specifies the min point of the train * at each corner. The train spawns at the first target it * is pointing at. If the train is the target of a button * or trigger, it will not begin moving until activated. * * speed default 100 * dmg default 2 * noise looping sound to play when the train is in motion * */ void train_blocked(edict_t *self, edict_t *other) { if (!self || !other) { return; } if (!(other->svflags & SVF_MONSTER) && (!other->client)) { /* give it a chance to go away on it's own terms (like gibs) */ T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH); /* if it's still there, nuke it */ if (other) { /* Hack for entity without an origin near the model */ VectorMA (other->absmin, 0.5, other->size, other->s.origin); BecomeExplosion1(other); } return; } if (level.time < self->touch_debounce_time) { return; } if (!self->dmg) { return; } self->touch_debounce_time = level.time + 0.5; T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH); } void train_wait(edict_t *self) { if (!self) { return; } if (self->target_ent->pathtarget) { char *savetarget; edict_t *ent; ent = self->target_ent; savetarget = ent->target; ent->target = ent->pathtarget; G_UseTargets(ent, self->activator); ent->target = savetarget; /* make sure we didn't get killed by a killtarget */ if (!self->inuse) { return; } } if (self->moveinfo.wait) { if (self->moveinfo.wait > 0) { self->nextthink = level.time + self->moveinfo.wait; self->think = train_next; } else if (self->spawnflags & TRAIN_TOGGLE) { train_next(self); self->spawnflags &= ~TRAIN_START_ON; VectorClear(self->velocity); self->nextthink = 0; } if (!(self->flags & FL_TEAMSLAVE)) { if (self->moveinfo.sound_end) { gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0); } self->s.sound = 0; } } else { train_next(self); } } void train_next(edict_t *self) { if (!self) { return; } edict_t *ent; vec3_t dest; qboolean first; first = true; again: if (!self->target) { return; } ent = G_PickTarget(self->target); if (!ent) { gi.dprintf("train_next: bad target %s\n", self->target); return; } self->target = ent->target; /* check for a teleport path_corner */ if (ent->spawnflags & 1) { if (!first) { gi.dprintf("connected teleport path_corners, see %s at %s\n", ent->classname, vtos(ent->s.origin)); return; } first = false; VectorSubtract(ent->s.origin, self->mins, self->s.origin); VectorCopy(self->s.origin, self->s.old_origin); self->s.event = EV_OTHER_TELEPORT; gi.linkentity(self); goto again; } self->moveinfo.wait = ent->wait; self->target_ent = ent; if (!(self->flags & FL_TEAMSLAVE)) { if (self->moveinfo.sound_start) { gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0); } self->s.sound = self->moveinfo.sound_middle; } VectorSubtract(ent->s.origin, self->mins, dest); self->moveinfo.state = STATE_TOP; VectorCopy(self->s.origin, self->moveinfo.start_origin); VectorCopy(dest, self->moveinfo.end_origin); Move_Calc(self, dest, train_wait); self->spawnflags |= TRAIN_START_ON; } void train_resume(edict_t *self) { edict_t *ent; vec3_t dest; if (!self) { return; } ent = self->target_ent; VectorSubtract(ent->s.origin, self->mins, dest); self->moveinfo.state = STATE_TOP; VectorCopy(self->s.origin, self->moveinfo.start_origin); VectorCopy(dest, self->moveinfo.end_origin); Move_Calc(self, dest, train_wait); self->spawnflags |= TRAIN_START_ON; } void func_train_find(edict_t *self) { edict_t *ent; if (!self) { return; } if (!self->target) { gi.dprintf("train_find: no target\n"); return; } ent = G_PickTarget(self->target); if (!ent) { gi.dprintf("train_find: target %s not found\n", self->target); return; } self->target = ent->target; VectorSubtract(ent->s.origin, self->mins, self->s.origin); gi.linkentity(self); /* if not triggered, start immediately */ if (!self->targetname) { self->spawnflags |= TRAIN_START_ON; } if (self->spawnflags & TRAIN_START_ON) { self->nextthink = level.time + FRAMETIME; self->think = train_next; self->activator = self; } } void train_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) { if (!self || !activator) { return; } self->activator = activator; if (self->spawnflags & TRAIN_START_ON) { if (!(self->spawnflags & TRAIN_TOGGLE)) { return; } self->spawnflags &= ~TRAIN_START_ON; VectorClear(self->velocity); self->nextthink = 0; } else { if (self->target_ent) { train_resume(self); } else { train_next(self); } } } void SP_func_train(edict_t *self) { if (!self) { return; } self->movetype = MOVETYPE_PUSH; VectorClear(self->s.angles); self->blocked = train_blocked; if (self->spawnflags & TRAIN_BLOCK_STOPS) { self->dmg = 0; } else { if (!self->dmg) { self->dmg = 100; } } self->solid = SOLID_BSP; gi.setmodel(self, self->model); if (st.noise) { self->moveinfo.sound_middle = gi.soundindex(st.noise); } if (!self->speed) { self->speed = 100; } self->moveinfo.speed = self->speed; self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed; self->use = train_use; gi.linkentity(self); if (self->target) { /* start trains on the second frame, to make sure their targets have had a chance to spawn */ self->nextthink = level.time + FRAMETIME; self->think = func_train_find; } else { gi.dprintf("func_train without a target at %s\n", vtos(self->absmin)); } } /* ==================================================================== */ /* * QUAKED trigger_elevator (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) */ void trigger_elevator_use(edict_t *self, edict_t *other, edict_t *activator /* unused */) { edict_t *target; if (!self || !other) { return; } if (self->movetarget->nextthink) { return; } if (!other->pathtarget) { gi.dprintf("elevator used with no pathtarget\n"); return; } target = G_PickTarget(other->pathtarget); if (!target) { gi.dprintf("elevator used with bad pathtarget: %s\n", other->pathtarget); return; } self->movetarget->target_ent = target; train_resume(self->movetarget); } void trigger_elevator_init(edict_t *self) { if (!self) { return; } if (!self->target) { gi.dprintf("trigger_elevator has no target\n"); return; } self->movetarget = G_PickTarget(self->target); if (!self->movetarget) { gi.dprintf("trigger_elevator unable to find target %s\n", self->target); return; } if (strcmp(self->movetarget->classname, "func_train") != 0) { gi.dprintf("trigger_elevator target %s is not a train\n", self->target); return; } self->use = trigger_elevator_use; self->svflags = SVF_NOCLIENT; } void SP_trigger_elevator(edict_t *self) { if (!self) { return; } self->think = trigger_elevator_init; self->nextthink = level.time + FRAMETIME; } /* ==================================================================== */ /* * QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON * * "wait" base time between triggering all targets, default is 1 * "random" wait variance, default is 0 * * so, the basic time between firing is a random time * between (wait - random) and (wait + random) * * "delay" delay before first firing when turned on, default is 0 * "pausetime" additional delay used only the very first time * and only if spawned with START_ON * * These can used but not touched. */ void func_timer_think(edict_t *self) { if (!self) { return; } G_UseTargets(self, self->activator); self->nextthink = level.time + self->wait + crandom() * self->random; } void func_timer_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) { if (!self || !activator) { return; } self->activator = activator; /* if on, turn it off */ if (self->nextthink) { self->nextthink = 0; return; } /* turn it on */ if (self->delay) { self->nextthink = level.time + self->delay; } else { func_timer_think(self); } } void SP_func_timer(edict_t *self) { if (!self) { return; } if (!self->wait) { self->wait = 1.0; } self->use = func_timer_use; self->think = func_timer_think; if (self->random >= self->wait) { self->random = self->wait - FRAMETIME; gi.dprintf("func_timer at %s has random >= wait\n", vtos(self->s.origin)); } if (self->spawnflags & 1) { self->nextthink = level.time + 1.0 + st.pausetime + self->delay + self->wait + crandom() * self->random; self->activator = self; } self->svflags = SVF_NOCLIENT; } /* ==================================================================== */ /* * QUAKED func_conveyor (0 .5 .8) ? START_ON TOGGLE * * Conveyors are stationary brushes that move what's on them. * The brush should be have a surface with at least one current * content enabled. * * speed default 100 */ void func_conveyor_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!self) { return; } if (self->spawnflags & 1) { self->speed = 0; self->spawnflags &= ~1; } else { self->speed = self->count; self->spawnflags |= 1; } if (!(self->spawnflags & 2)) { self->count = 0; } } void SP_func_conveyor(edict_t *self) { if (!self) { return; } if (!self->speed) { self->speed = 100; } if (!(self->spawnflags & 1)) { self->count = self->speed; self->speed = 0; } self->use = func_conveyor_use; gi.setmodel(self, self->model); self->solid = SOLID_BSP; gi.linkentity(self); } /* ==================================================================== */ /* * QUAKED func_door_secret (0 .5 .8) ? always_shoot 1st_left 1st_down * A secret door. Slide back and then to the side. * * open_once doors never closes * 1st_left 1st move is left of arrow * 1st_down 1st move is down from arrow * always_shoot door is shootebale even if targeted * * "angle" determines the direction * "dmg" damage to inflic when blocked (default 2) * "wait" how long to hold in the open position (default 5, -1 means hold) */ #define SECRET_ALWAYS_SHOOT 1 #define SECRET_1ST_LEFT 2 #define SECRET_1ST_DOWN 4 void door_secret_move1(edict_t *self); void door_secret_move2(edict_t *self); void door_secret_move3(edict_t *self); void door_secret_move4(edict_t *self); void door_secret_move5(edict_t *self); void door_secret_move6(edict_t *self); void door_secret_done(edict_t *self); void door_secret_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!self) { return; } /* make sure we're not already moving */ if (!VectorCompare(self->s.origin, vec3_origin)) { return; } Move_Calc(self, self->pos1, door_secret_move1); door_use_areaportals(self, true); } void door_secret_move1(edict_t *self) { if (!self) { return; } self->nextthink = level.time + 1.0; self->think = door_secret_move2; } void door_secret_move2(edict_t *self) { if (!self) { return; } Move_Calc(self, self->pos2, door_secret_move3); } void door_secret_move3(edict_t *self) { if (!self) { return; } if (self->wait == -1) { return; } self->nextthink = level.time + self->wait; self->think = door_secret_move4; } void door_secret_move4(edict_t *self) { if (!self) { return; } Move_Calc(self, self->pos1, door_secret_move5); } void door_secret_move5(edict_t *self) { if (!self) { return; } self->nextthink = level.time + 1.0; self->think = door_secret_move6; } void door_secret_move6(edict_t *self) { if (!self) { return; } Move_Calc(self, vec3_origin, door_secret_done); } void door_secret_done(edict_t *self) { if (!self) { return; } if (!(self->targetname) || (self->spawnflags & SECRET_ALWAYS_SHOOT)) { self->health = 0; self->takedamage = DAMAGE_YES; } door_use_areaportals(self, false); } void door_secret_blocked(edict_t *self, edict_t *other) { if (!self || !other) { return; } if (!(other->svflags & SVF_MONSTER) && (!other->client)) { /* give it a chance to go away on it's own terms (like gibs) */ T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH); /* if it's still there, nuke it */ if (other) { /* Hack for entities without their origin near the model */ VectorMA (other->absmin, 0.5, other->size, other->s.origin); BecomeExplosion1(other); } return; } if (level.time < self->touch_debounce_time) { return; } self->touch_debounce_time = level.time + 0.5; T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH); } void door_secret_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker, int damage /* unused */, vec3_t point /* unused */) { if (!self || !attacker) { return; } self->takedamage = DAMAGE_NO; door_secret_use(self, attacker, attacker); } void SP_func_door_secret(edict_t *ent) { vec3_t forward, right, up; float side; float width; float length; if (!ent) { return; } ent->moveinfo.sound_start = gi.soundindex("doors/dr1_strt.wav"); ent->moveinfo.sound_middle = gi.soundindex("doors/dr1_mid.wav"); ent->moveinfo.sound_end = gi.soundindex("doors/dr1_end.wav"); ent->movetype = MOVETYPE_PUSH; ent->solid = SOLID_BSP; gi.setmodel(ent, ent->model); ent->blocked = door_secret_blocked; ent->use = door_secret_use; if (!(ent->targetname) || (ent->spawnflags & SECRET_ALWAYS_SHOOT)) { ent->health = 0; ent->takedamage = DAMAGE_YES; ent->die = door_secret_die; } if (!ent->dmg) { ent->dmg = 2; } if (!ent->wait) { ent->wait = 5; } ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = 50; /* calculate positions */ AngleVectors(ent->s.angles, forward, right, up); VectorClear(ent->s.angles); side = 1.0 - (ent->spawnflags & SECRET_1ST_LEFT); if (ent->spawnflags & SECRET_1ST_DOWN) { width = fabs(DotProduct(up, ent->size)); } else { width = fabs(DotProduct(right, ent->size)); } length = fabs(DotProduct(forward, ent->size)); if (ent->spawnflags & SECRET_1ST_DOWN) { VectorMA(ent->s.origin, -1 * width, up, ent->pos1); } else { VectorMA(ent->s.origin, side * width, right, ent->pos1); } VectorMA(ent->pos1, length, forward, ent->pos2); if (ent->health) { ent->takedamage = DAMAGE_YES; ent->die = door_killed; ent->max_health = ent->health; } else if (ent->targetname && ent->message) { gi.soundindex("misc/talk.wav"); ent->touch = door_touch; } ent->classname = "func_door"; gi.linkentity(ent); } /* ==================================================================== */ /* * QUAKED func_killbox (1 0 0) ? * * Kills everything inside when fired, * irrespective of protection. */ void use_killbox(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!self) { return; } KillBox(self); /* Hack to make sure that really everything is killed */ self->count--; if (!self->count) { self->think = G_FreeEdict; self->nextthink = level.time + 1; } } void SP_func_killbox(edict_t *ent) { if (!ent) { return; } gi.setmodel(ent, ent->model); ent->use = use_killbox; ent->svflags = SVF_NOCLIENT; } yquake2-QUAKE2_5_32/src/game/g_misc.c0000644000175000017500000014433012615162034017731 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Miscellaneos entities, functs and functions. * * ======================================================================= */ #include "header/local.h" int gibsthisframe; int lastgibframe; void Use_Areaportal(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!ent) { return; } ent->count ^= 1; /* toggle state */ gi.SetAreaPortalState(ent->style, ent->count); } /* * QUAKED func_areaportal (0 0 0) ? * * This is a non-visible object that divides the world into * areas that are seperated when this portal is not activated. * Usually enclosed in the middle of a door. */ void SP_func_areaportal(edict_t *ent) { if (!ent) { return; } ent->use = Use_Areaportal; ent->count = 0; /* always start closed; */ } /* ===================================================== */ void VelocityForDamage(int damage, vec3_t v) { v[0] = 100.0 * crandom(); v[1] = 100.0 * crandom(); v[2] = 200.0 + 100.0 * random(); if (damage < 50) { VectorScale(v, 0.7, v); } else { VectorScale(v, 1.2, v); } } void ClipGibVelocity(edict_t *ent) { if (!ent) { return; } if (ent->velocity[0] < -300) { ent->velocity[0] = -300; } else if (ent->velocity[0] > 300) { ent->velocity[0] = 300; } if (ent->velocity[1] < -300) { ent->velocity[1] = -300; } else if (ent->velocity[1] > 300) { ent->velocity[1] = 300; } if (ent->velocity[2] < 200) { ent->velocity[2] = 200; /* always some upwards */ } else if (ent->velocity[2] > 500) { ent->velocity[2] = 500; } } /* ===================================================== */ void gib_think(edict_t *self) { if (!self) { return; } self->s.frame++; self->nextthink = level.time + FRAMETIME; if (self->s.frame == 10) { self->think = G_FreeEdict; self->nextthink = level.time + 8 + random() * 10; } } void gib_touch(edict_t *self, edict_t *other /* unused */, cplane_t *plane, csurface_t *surf /* unused */) { if (!self || !plane) { return; } vec3_t normal_angles, right; if (!self->groundentity) { return; } self->touch = NULL; if (plane) { gi.sound(self, CHAN_VOICE, gi.soundindex( "misc/fhit3.wav"), 1, ATTN_NORM, 0); vectoangles(plane->normal, normal_angles); AngleVectors(normal_angles, NULL, right, NULL); vectoangles(right, self->s.angles); if (self->s.modelindex == sm_meat_index) { self->s.frame++; self->think = gib_think; self->nextthink = level.time + FRAMETIME; } } } void gib_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage /* unused */, vec3_t point /* unused */) { if (!self) { return; } G_FreeEdict(self); } void ThrowGib(edict_t *self, char *gibname, int damage, int type) { edict_t *gib; vec3_t vd; vec3_t origin; vec3_t size; float vscale; if (!self || !gibname) { return; } if (level.framenum > lastgibframe) { gibsthisframe = 0; lastgibframe = level.framenum; } gibsthisframe++; if (gibsthisframe > 20) { return; } gib = G_Spawn(); VectorScale(self->size, 0.5, size); VectorAdd(self->absmin, size, origin); gib->s.origin[0] = origin[0] + crandom() * size[0]; gib->s.origin[1] = origin[1] + crandom() * size[1]; gib->s.origin[2] = origin[2] + crandom() * size[2]; gi.setmodel(gib, gibname); gib->solid = SOLID_NOT; gib->s.effects |= EF_GIB; gib->flags |= FL_NO_KNOCKBACK; gib->takedamage = DAMAGE_YES; gib->die = gib_die; if (type == GIB_ORGANIC) { gib->movetype = MOVETYPE_TOSS; gib->touch = gib_touch; vscale = 0.5; } else { gib->movetype = MOVETYPE_BOUNCE; vscale = 1.0; } VelocityForDamage(damage, vd); VectorMA(self->velocity, vscale, vd, gib->velocity); ClipGibVelocity(gib); gib->avelocity[0] = random() * 600; gib->avelocity[1] = random() * 600; gib->avelocity[2] = random() * 600; gib->think = G_FreeEdict; gib->nextthink = level.time + 10 + random() * 10; gi.linkentity(gib); } void ThrowHead(edict_t *self, char *gibname, int damage, int type) { vec3_t vd; float vscale; if (!self || !gibname) { return; } self->s.skinnum = 0; self->s.frame = 0; VectorClear(self->mins); VectorClear(self->maxs); self->s.modelindex2 = 0; gi.setmodel(self, gibname); self->solid = SOLID_NOT; self->s.effects |= EF_GIB; self->s.effects &= ~EF_FLIES; self->s.sound = 0; self->flags |= FL_NO_KNOCKBACK; self->svflags &= ~SVF_MONSTER; self->takedamage = DAMAGE_YES; self->targetname = NULL; self->die = gib_die; if (type == GIB_ORGANIC) { self->movetype = MOVETYPE_TOSS; self->touch = gib_touch; vscale = 0.5; } else { self->movetype = MOVETYPE_BOUNCE; vscale = 1.0; } VelocityForDamage(damage, vd); VectorMA(self->velocity, vscale, vd, self->velocity); ClipGibVelocity(self); self->avelocity[YAW] = crandom() * 600; self->think = G_FreeEdict; self->nextthink = level.time + 10 + random() * 10; gi.linkentity(self); } void ThrowClientHead(edict_t *self, int damage) { vec3_t vd; char *gibname; if (!self) { return; } if (randk() & 1) { gibname = "models/objects/gibs/head2/tris.md2"; self->s.skinnum = 1; /* second skin is player */ } else { gibname = "models/objects/gibs/skull/tris.md2"; self->s.skinnum = 0; } self->s.origin[2] += 32; self->s.frame = 0; gi.setmodel(self, gibname); VectorSet(self->mins, -16, -16, 0); VectorSet(self->maxs, 16, 16, 16); self->takedamage = DAMAGE_NO; self->solid = SOLID_NOT; self->s.effects = EF_GIB; self->s.sound = 0; self->flags |= FL_NO_KNOCKBACK; self->movetype = MOVETYPE_BOUNCE; VelocityForDamage(damage, vd); VectorAdd(self->velocity, vd, self->velocity); if (self->client) /* bodies in the queue don't have a client anymore */ { self->client->anim_priority = ANIM_DEATH; self->client->anim_end = self->s.frame; } else { self->think = NULL; self->nextthink = 0; } gi.linkentity(self); } /* ===================================================== */ void debris_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage /* unused */, vec3_t point /* unused */) { if (!self) { return; } G_FreeEdict(self); } void ThrowDebris(edict_t *self, char *modelname, float speed, vec3_t origin) { edict_t *chunk; vec3_t v; if (!self || !modelname) { return; } if (level.framenum > lastgibframe) { gibsthisframe = 0; lastgibframe = level.framenum; } gibsthisframe++; if (gibsthisframe > 20) { return; } chunk = G_Spawn(); VectorCopy(origin, chunk->s.origin); gi.setmodel(chunk, modelname); v[0] = 100 * crandom(); v[1] = 100 * crandom(); v[2] = 100 + 100 * crandom(); VectorMA(self->velocity, speed, v, chunk->velocity); chunk->movetype = MOVETYPE_BOUNCE; chunk->solid = SOLID_NOT; chunk->avelocity[0] = random() * 600; chunk->avelocity[1] = random() * 600; chunk->avelocity[2] = random() * 600; chunk->think = G_FreeEdict; chunk->nextthink = level.time + 5 + random() * 5; chunk->s.frame = 0; chunk->flags = 0; chunk->classname = "debris"; chunk->takedamage = DAMAGE_YES; chunk->die = debris_die; gi.linkentity(chunk); } void BecomeExplosion1(edict_t *self) { if (!self) { return; } gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_EXPLOSION1); gi.WritePosition(self->s.origin); gi.multicast(self->s.origin, MULTICAST_PVS); G_FreeEdict(self); } void BecomeExplosion2(edict_t *self) { if (!self) { return; } gi.WriteByte(svc_temp_entity); gi.WriteByte(TE_EXPLOSION2); gi.WritePosition(self->s.origin); gi.multicast(self->s.origin, MULTICAST_PVS); G_FreeEdict(self); } /* ===================================================== */ /* * QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) TELEPORT * Target: next path corner * Pathtarget: gets used when an entity that has * this path_corner targeted touches it */ void path_corner_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { vec3_t v; edict_t *next; if (!self || !other) { return; } if (other->movetarget != self) { return; } if (other->enemy) { return; } if (self->pathtarget) { char *savetarget; savetarget = self->target; self->target = self->pathtarget; G_UseTargets(self, other); self->target = savetarget; } if (self->target) { next = G_PickTarget(self->target); } else { next = NULL; } if ((next) && (next->spawnflags & 1)) { VectorCopy(next->s.origin, v); v[2] += next->mins[2]; v[2] -= other->mins[2]; VectorCopy(v, other->s.origin); next = G_PickTarget(next->target); other->s.event = EV_OTHER_TELEPORT; } other->goalentity = other->movetarget = next; if (self->wait) { other->monsterinfo.pausetime = level.time + self->wait; other->monsterinfo.stand(other); return; } if (!other->movetarget) { other->monsterinfo.pausetime = level.time + 100000000; other->monsterinfo.stand(other); } else { VectorSubtract(other->goalentity->s.origin, other->s.origin, v); other->ideal_yaw = vectoyaw(v); } } void SP_path_corner(edict_t *self) { if (!self) { return; } if (!self->targetname) { gi.dprintf("path_corner with no targetname at %s\n", vtos(self->s.origin)); G_FreeEdict(self); return; } self->solid = SOLID_TRIGGER; self->touch = path_corner_touch; VectorSet(self->mins, -8, -8, -8); VectorSet(self->maxs, 8, 8, 8); self->svflags |= SVF_NOCLIENT; gi.linkentity(self); } /* ===================================================== */ /* * QUAKED point_combat (0.5 0.3 0) (-8 -8 -8) (8 8 8) Hold * * Makes this the target of a monster and it will head here * when first activated before going after the activator. If * hold is selected, it will stay here. */ void point_combat_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { edict_t *activator; if (!self || !other) { return; } if (other->movetarget != self) { return; } if (self->target) { other->target = self->target; other->goalentity = other->movetarget = G_PickTarget(other->target); if (!other->goalentity) { gi.dprintf("%s at %s target %s does not exist\n", self->classname, vtos(self->s.origin), self->target); other->movetarget = self; } self->target = NULL; } else if ((self->spawnflags & 1) && !(other->flags & (FL_SWIM | FL_FLY))) { other->monsterinfo.pausetime = level.time + 100000000; other->monsterinfo.aiflags |= AI_STAND_GROUND; other->monsterinfo.stand(other); } if (other->movetarget == self) { other->target = NULL; other->movetarget = NULL; other->goalentity = other->enemy; other->monsterinfo.aiflags &= ~AI_COMBAT_POINT; } if (self->pathtarget) { char *savetarget; savetarget = self->target; self->target = self->pathtarget; if (other->enemy && other->enemy->client) { activator = other->enemy; } else if (other->oldenemy && other->oldenemy->client) { activator = other->oldenemy; } else if (other->activator && other->activator->client) { activator = other->activator; } else { activator = other; } G_UseTargets(self, activator); self->target = savetarget; } } void SP_point_combat(edict_t *self) { if (!self) { return; } if (deathmatch->value) { G_FreeEdict(self); return; } self->solid = SOLID_TRIGGER; self->touch = point_combat_touch; VectorSet(self->mins, -8, -8, -16); VectorSet(self->maxs, 8, 8, 16); self->svflags = SVF_NOCLIENT; gi.linkentity(self); } /* ===================================================== */ /* * QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8) * Just for the debugging level. Don't use */ void TH_viewthing(edict_t *ent) { if (!ent) { return; } ent->s.frame = (ent->s.frame + 1) % 7; ent->nextthink = level.time + FRAMETIME; } void SP_viewthing(edict_t *ent) { if (!ent) { return; } gi.dprintf("viewthing spawned\n"); ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_BBOX; ent->s.renderfx = RF_FRAMELERP; VectorSet(ent->mins, -16, -16, -24); VectorSet(ent->maxs, 16, 16, 32); ent->s.modelindex = gi.modelindex("models/objects/banner/tris.md2"); gi.linkentity(ent); ent->nextthink = level.time + 0.5; ent->think = TH_viewthing; return; } /* ===================================================== */ /* * QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4) * Used as a positional target for spotlights, etc. */ void SP_info_null(edict_t *self) { if (!self) { return; } G_FreeEdict(self); } /* * QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4) * Used as a positional target for lightning. */ void SP_info_notnull(edict_t *self) { if (!self) { return; } VectorCopy(self->s.origin, self->absmin); VectorCopy(self->s.origin, self->absmax); } #define START_OFF 1 /* * QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF * Non-displayed light. * Default light value is 300. * Default style is 0. * If targeted, will toggle between on and off. * Default _cone value is 10 (used to set size of light for spotlights) */ void light_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!self) { return; } if (self->spawnflags & START_OFF) { gi.configstring(CS_LIGHTS + self->style, "m"); self->spawnflags &= ~START_OFF; } else { gi.configstring(CS_LIGHTS + self->style, "a"); self->spawnflags |= START_OFF; } } void SP_light(edict_t *self) { if (!self) { return; } /* no targeted lights in deathmatch, because they cause global messages */ if (!self->targetname || deathmatch->value) { G_FreeEdict(self); return; } if (self->style >= 32) { self->use = light_use; if (self->spawnflags & START_OFF) { gi.configstring(CS_LIGHTS + self->style, "a"); } else { gi.configstring(CS_LIGHTS + self->style, "m"); } } } /* ===================================================== */ /* * QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST * This is just a solid wall if not inhibited * * TRIGGER_SPAWN the wall will not be present until triggered * it will then blink in to existance; it will * kill anything that was in it's way * * TOGGLE only valid for TRIGGER_SPAWN walls * this allows the wall to be turned on and off * * START_ON only valid for TRIGGER_SPAWN walls * the wall will initially be present */ void func_wall_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!self) { return; } if (self->solid == SOLID_NOT) { self->solid = SOLID_BSP; self->svflags &= ~SVF_NOCLIENT; KillBox(self); } else { self->solid = SOLID_NOT; self->svflags |= SVF_NOCLIENT; } gi.linkentity(self); if (!(self->spawnflags & 2)) { self->use = NULL; } } void SP_func_wall(edict_t *self) { if (!self) { return; } self->movetype = MOVETYPE_PUSH; gi.setmodel(self, self->model); if (self->spawnflags & 8) { self->s.effects |= EF_ANIM_ALL; } if (self->spawnflags & 16) { self->s.effects |= EF_ANIM_ALLFAST; } /* just a wall */ if ((self->spawnflags & 7) == 0) { self->solid = SOLID_BSP; gi.linkentity(self); return; } /* it must be TRIGGER_SPAWN */ if (!(self->spawnflags & 1)) { self->spawnflags |= 1; } /* yell if the spawnflags are odd */ if (self->spawnflags & 4) { if (!(self->spawnflags & 2)) { gi.dprintf("func_wall START_ON without TOGGLE\n"); self->spawnflags |= 2; } } self->use = func_wall_use; if (self->spawnflags & 4) { self->solid = SOLID_BSP; } else { self->solid = SOLID_NOT; self->svflags |= SVF_NOCLIENT; } gi.linkentity(self); } /* ===================================================== */ /* * QUAKED func_object (0 .5 .8) ? TRIGGER_SPAWN ANIMATED ANIMATED_FAST * This is solid bmodel that will fall if it's support it removed. */ void func_object_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf /* unused */) { if (!self || !other || !plane) { return; } /* only squash thing we fall on top of */ if (!plane) { return; } if (plane->normal[2] < 1.0) { return; } if (other->takedamage == DAMAGE_NO) { return; } T_Damage(other, self, self, vec3_origin, self->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH); } void func_object_release(edict_t *self) { if (!self) { return; } self->movetype = MOVETYPE_TOSS; self->touch = func_object_touch; } void func_object_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!self) { return; } self->solid = SOLID_BSP; self->svflags &= ~SVF_NOCLIENT; self->use = NULL; KillBox(self); func_object_release(self); } void SP_func_object(edict_t *self) { if (!self) { return; } gi.setmodel(self, self->model); self->mins[0] += 1; self->mins[1] += 1; self->mins[2] += 1; self->maxs[0] -= 1; self->maxs[1] -= 1; self->maxs[2] -= 1; if (!self->dmg) { self->dmg = 100; } if (self->spawnflags == 0) { self->solid = SOLID_BSP; self->movetype = MOVETYPE_PUSH; self->think = func_object_release; self->nextthink = level.time + 2 * FRAMETIME; } else { self->solid = SOLID_NOT; self->movetype = MOVETYPE_PUSH; self->use = func_object_use; self->svflags |= SVF_NOCLIENT; } if (self->spawnflags & 2) { self->s.effects |= EF_ANIM_ALL; } if (self->spawnflags & 4) { self->s.effects |= EF_ANIM_ALLFAST; } self->clipmask = MASK_MONSTERSOLID; gi.linkentity(self); } /* ===================================================== */ /* * QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST * Any brush that you want to explode or break apart. If you want an * explosion, set dmg and it will do a radius explosion of that amount * at the center of the bursh. * * If targeted it will not be shootable. * * health defaults to 100. * * mass defaults to 75. This determines how much debris is emitted when * it explodes. You get one large chunk per 100 of mass (up to 8) and * one small chunk per 25 of mass (up to 16). So 800 gives the most. */ void func_explosive_explode(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage /* unused */, vec3_t point /* unused */) { vec3_t origin; vec3_t chunkorigin; vec3_t size; int count; int mass; if (!self || !inflictor || !attacker) { return; } /* bmodel origins are (0 0 0), we need to adjust that here */ VectorScale(self->size, 0.5, size); VectorAdd(self->absmin, size, origin); VectorCopy(origin, self->s.origin); self->takedamage = DAMAGE_NO; if (self->dmg) { T_RadiusDamage(self, attacker, self->dmg, NULL, self->dmg + 40, MOD_EXPLOSIVE); } VectorSubtract(self->s.origin, inflictor->s.origin, self->velocity); VectorNormalize(self->velocity); VectorScale(self->velocity, 150, self->velocity); /* start chunks towards the center */ VectorScale(size, 0.5, size); mass = self->mass; if (!mass) { mass = 75; } /* big chunks */ if (mass >= 100) { count = mass / 100; if (count > 8) { count = 8; } while (count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris(self, "models/objects/debris1/tris.md2", 1, chunkorigin); } } /* small chunks */ count = mass / 25; if (count > 16) { count = 16; } while (count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris(self, "models/objects/debris2/tris.md2", 2, chunkorigin); } G_UseTargets(self, attacker); if (self->dmg) { BecomeExplosion1(self); } else { G_FreeEdict(self); } } void func_explosive_use(edict_t *self, edict_t *other, edict_t *activator) { func_explosive_explode(self, self, other, self->health, vec3_origin); } void func_explosive_spawn(edict_t *self, edict_t *other, edict_t *activator) { self->solid = SOLID_BSP; self->svflags &= ~SVF_NOCLIENT; self->use = NULL; KillBox(self); gi.linkentity(self); } void SP_func_explosive(edict_t *self) { if (!self) { return; } if (deathmatch->value) { /* auto-remove for deathmatch */ G_FreeEdict(self); return; } self->movetype = MOVETYPE_PUSH; gi.modelindex("models/objects/debris1/tris.md2"); gi.modelindex("models/objects/debris2/tris.md2"); gi.setmodel(self, self->model); if (self->spawnflags & 1) { self->svflags |= SVF_NOCLIENT; self->solid = SOLID_NOT; self->use = func_explosive_spawn; } else { self->solid = SOLID_BSP; if (self->targetname) { self->use = func_explosive_use; } } if (self->spawnflags & 2) { self->s.effects |= EF_ANIM_ALL; } if (self->spawnflags & 4) { self->s.effects |= EF_ANIM_ALLFAST; } if (self->use != func_explosive_use) { if (!self->health) { self->health = 100; } self->die = func_explosive_explode; self->takedamage = DAMAGE_YES; } gi.linkentity(self); } /* ===================================================== */ /* * QUAKED misc_explobox (0 .5 .8) (-16 -16 0) (16 16 40) * Large exploding box. You can override its mass (100), * health (80), and dmg (150). */ void barrel_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /*unused */) { float ratio; vec3_t v; if (!self || !other) { return; } if ((!other->groundentity) || (other->groundentity == self)) { return; } ratio = (float)other->mass / (float)self->mass; VectorSubtract(self->s.origin, other->s.origin, v); M_walkmove(self, vectoyaw(v), 20 * ratio * FRAMETIME); } void barrel_explode(edict_t *self) { vec3_t org; float spd; vec3_t save; if (!self) { return; } T_RadiusDamage(self, self->activator, self->dmg, NULL, self->dmg + 40, MOD_BARREL); VectorCopy(self->s.origin, save); VectorMA(self->absmin, 0.5, self->size, self->s.origin); /* a few big chunks */ spd = 1.5 * (float)self->dmg / 200.0; org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris(self, "models/objects/debris1/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris(self, "models/objects/debris1/tris.md2", spd, org); /* bottom corners */ spd = 1.75 * (float)self->dmg / 200.0; VectorCopy(self->absmin, org); ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org); VectorCopy(self->absmin, org); org[0] += self->size[0]; ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org); VectorCopy(self->absmin, org); org[1] += self->size[1]; ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org); VectorCopy(self->absmin, org); org[0] += self->size[0]; org[1] += self->size[1]; ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org); /* a bunch of little chunks */ spd = 2 * self->dmg / 200; org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org); VectorCopy(save, self->s.origin); if (self->groundentity) { BecomeExplosion2(self); } else { BecomeExplosion1(self); } } void barrel_delay(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker, int damage /* unused */, vec3_t point /* unused */) { if (!self || !attacker) { return; } self->takedamage = DAMAGE_NO; self->nextthink = level.time + 2 * FRAMETIME; self->think = barrel_explode; self->activator = attacker; } void SP_misc_explobox(edict_t *self) { if (!self) { return; } if (deathmatch->value) { /* auto-remove for deathmatch */ G_FreeEdict(self); return; } gi.modelindex("models/objects/debris1/tris.md2"); gi.modelindex("models/objects/debris2/tris.md2"); gi.modelindex("models/objects/debris3/tris.md2"); self->solid = SOLID_BBOX; self->movetype = MOVETYPE_STEP; self->model = "models/objects/barrels/tris.md2"; self->s.modelindex = gi.modelindex(self->model); VectorSet(self->mins, -16, -16, 0); VectorSet(self->maxs, 16, 16, 40); if (!self->mass) { self->mass = 400; } if (!self->health) { self->health = 10; } if (!self->dmg) { self->dmg = 150; } self->die = barrel_delay; self->takedamage = DAMAGE_YES; self->monsterinfo.aiflags = AI_NOSTEP; self->touch = barrel_touch; self->think = M_droptofloor; self->nextthink = level.time + 2 * FRAMETIME; gi.linkentity(self); } /* ===================================================== */ /* * QUAKED misc_blackhole (1 .5 0) (-8 -8 -8) (8 8 8) */ void misc_blackhole_use(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!ent) { return; } G_FreeEdict(ent); } void misc_blackhole_think(edict_t *self) { if (!self) { return; } if (++self->s.frame < 19) { self->nextthink = level.time + FRAMETIME; } else { self->s.frame = 0; self->nextthink = level.time + FRAMETIME; } } void misc_blackhole_transparent (edict_t *ent) { ent->s.renderfx = RF_TRANSLUCENT|RF_NOSHADOW; ent->prethink = NULL; gi.linkentity(ent); } void SP_misc_blackhole(edict_t *ent) { if (!ent) { return; } ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_NOT; VectorSet(ent->mins, -64, -64, 0); VectorSet(ent->maxs, 64, 64, 8); ent->s.modelindex = gi.modelindex("models/objects/black/tris.md2"); ent->s.renderfx = RF_TRANSLUCENT; ent->use = misc_blackhole_use; ent->think = misc_blackhole_think; ent->prethink = misc_blackhole_transparent; ent->nextthink = level.time + 2 * FRAMETIME; gi.linkentity(ent); } /* ===================================================== */ /* * QUAKED misc_eastertank (1 .5 0) (-32 -32 -16) (32 32 32) */ void misc_eastertank_think(edict_t *self) { if (!self) { return; } if (++self->s.frame < 293) { self->nextthink = level.time + FRAMETIME; } else { self->s.frame = 254; self->nextthink = level.time + FRAMETIME; } } void SP_misc_eastertank(edict_t *ent) { if (!ent) { return; } ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_BBOX; VectorSet(ent->mins, -32, -32, -16); VectorSet(ent->maxs, 32, 32, 32); ent->s.modelindex = gi.modelindex("models/monsters/tank/tris.md2"); ent->s.frame = 254; ent->think = misc_eastertank_think; ent->nextthink = level.time + 2 * FRAMETIME; gi.linkentity(ent); } /* ===================================================== */ /* * QUAKED misc_easterchick (1 .5 0) (-32 -32 0) (32 32 32) */ void misc_easterchick_think(edict_t *self) { if (!self) { return; } if (++self->s.frame < 247) { self->nextthink = level.time + FRAMETIME; } else { self->s.frame = 208; self->nextthink = level.time + FRAMETIME; } } void SP_misc_easterchick(edict_t *ent) { if (!ent) { return; } ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_BBOX; VectorSet(ent->mins, -32, -32, 0); VectorSet(ent->maxs, 32, 32, 32); ent->s.modelindex = gi.modelindex("models/monsters/bitch/tris.md2"); ent->s.frame = 208; ent->think = misc_easterchick_think; ent->nextthink = level.time + 2 * FRAMETIME; gi.linkentity(ent); } /* * QUAKED misc_easterchick2 (1 .5 0) (-32 -32 0) (32 32 32) */ void misc_easterchick2_think(edict_t *self) { if (!self) { return; } if (++self->s.frame < 287) { self->nextthink = level.time + FRAMETIME; } else { self->s.frame = 248; self->nextthink = level.time + FRAMETIME; } } void SP_misc_easterchick2(edict_t *ent) { if (!ent) { return; } ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_BBOX; VectorSet(ent->mins, -32, -32, 0); VectorSet(ent->maxs, 32, 32, 32); ent->s.modelindex = gi.modelindex("models/monsters/bitch/tris.md2"); ent->s.frame = 248; ent->think = misc_easterchick2_think; ent->nextthink = level.time + 2 * FRAMETIME; gi.linkentity(ent); } /* ===================================================== */ /* * QUAKED monster_commander_body (1 .5 0) (-32 -32 0) (32 32 48) * Not really a monster, this is the Tank Commander's decapitated body. * There should be a item_commander_head that has this as it's target. */ void commander_body_think(edict_t *self) { if (!self) { return; } if (++self->s.frame < 24) { self->nextthink = level.time + FRAMETIME; } else { self->nextthink = 0; } if (self->s.frame == 22) { gi.sound(self, CHAN_BODY, gi.soundindex( "tank/thud.wav"), 1, ATTN_NORM, 0); } } void commander_body_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!self) { return; } self->think = commander_body_think; self->nextthink = level.time + FRAMETIME; gi.sound(self, CHAN_BODY, gi.soundindex("tank/pain.wav"), 1, ATTN_NORM, 0); } void commander_body_drop(edict_t *self) { if (!self) { return; } self->movetype = MOVETYPE_TOSS; self->s.origin[2] += 2; } void SP_monster_commander_body(edict_t *self) { if (!self) { return; } self->movetype = MOVETYPE_NONE; self->solid = SOLID_BBOX; self->model = "models/monsters/commandr/tris.md2"; self->s.modelindex = gi.modelindex(self->model); VectorSet(self->mins, -32, -32, 0); VectorSet(self->maxs, 32, 32, 48); self->use = commander_body_use; self->takedamage = DAMAGE_YES; self->flags = FL_GODMODE; self->s.renderfx |= RF_FRAMELERP; gi.linkentity(self); gi.soundindex("tank/thud.wav"); gi.soundindex("tank/pain.wav"); self->think = commander_body_drop; self->nextthink = level.time + 5 * FRAMETIME; } /* ===================================================== */ /* * QUAKED misc_banner (1 .5 0) (-4 -4 -4) (4 4 4) * The origin is the bottom of the banner. * The banner is 128 tall. */ void misc_banner_think(edict_t *ent) { if (!ent) { return; } ent->s.frame = (ent->s.frame + 1) % 16; ent->nextthink = level.time + FRAMETIME; } void SP_misc_banner(edict_t *ent) { if (!ent) { return; } ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_NOT; ent->s.modelindex = gi.modelindex("models/objects/banner/tris.md2"); ent->s.frame = randk() % 16; gi.linkentity(ent); ent->think = misc_banner_think; ent->nextthink = level.time + FRAMETIME; } /* ===================================================== */ /* * QUAKED misc_deadsoldier (1 .5 0) (-16 -16 0) (16 16 16) ON_BACK ON_STOMACH BACK_DECAP FETAL_POS SIT_DECAP IMPALED * This is the dead player model. Comes in 6 exciting different poses! */ void misc_deadsoldier_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage, vec3_t point /* unused */) { int n; if (!self) { return; } if (self->health > -80) { return; } gi.sound(self, CHAN_BODY, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0); for (n = 0; n < 4; n++) { ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC); } ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC); } void SP_misc_deadsoldier(edict_t *ent) { if (!ent) { return; } if (deathmatch->value) { /* auto-remove for deathmatch */ G_FreeEdict(ent); return; } ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_BBOX; ent->s.modelindex = gi.modelindex("models/deadbods/dude/tris.md2"); /* Defaults to frame 0 */ if (ent->spawnflags & 2) { ent->s.frame = 1; } else if (ent->spawnflags & 4) { ent->s.frame = 2; } else if (ent->spawnflags & 8) { ent->s.frame = 3; } else if (ent->spawnflags & 16) { ent->s.frame = 4; } else if (ent->spawnflags & 32) { ent->s.frame = 5; } else { ent->s.frame = 0; } VectorSet(ent->mins, -16, -16, 0); VectorSet(ent->maxs, 16, 16, 16); ent->deadflag = DEAD_DEAD; ent->takedamage = DAMAGE_YES; ent->svflags |= SVF_MONSTER | SVF_DEADMONSTER; ent->die = misc_deadsoldier_die; ent->monsterinfo.aiflags |= AI_GOOD_GUY; gi.linkentity(ent); } /* ===================================================== */ /* * QUAKED misc_viper (1 .5 0) (-16 -16 0) (16 16 32) * This is the Viper for the flyby bombing. * It is trigger_spawned, so you must have something use it for it to show up. * There must be a path for it to follow once it is activated. * * "speed" How fast the Viper should fly */ extern void train_use(edict_t *self, edict_t *other, edict_t *activator); extern void func_train_find(edict_t *self); void misc_viper_use(edict_t *self, edict_t *other, edict_t *activator) { if (!self || !other || !activator) { return; } self->svflags &= ~SVF_NOCLIENT; self->use = train_use; train_use(self, other, activator); } void SP_misc_viper(edict_t *ent) { if (!ent) { return; } if (!ent->target) { gi.dprintf("misc_viper without a target at %s\n", vtos(ent->absmin)); G_FreeEdict(ent); return; } if (!ent->speed) { ent->speed = 300; } ent->movetype = MOVETYPE_PUSH; ent->solid = SOLID_NOT; ent->s.modelindex = gi.modelindex("models/ships/viper/tris.md2"); VectorSet(ent->mins, -16, -16, 0); VectorSet(ent->maxs, 16, 16, 32); ent->think = func_train_find; ent->nextthink = level.time + FRAMETIME; ent->use = misc_viper_use; ent->svflags |= SVF_NOCLIENT; ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed; gi.linkentity(ent); } /* ===================================================== */ /* * QUAKED misc_bigviper (1 .5 0) (-176 -120 -24) (176 120 72) * This is a large stationary viper as seen in Paul's intro */ void SP_misc_bigviper(edict_t *ent) { ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_BBOX; VectorSet(ent->mins, -176, -120, -24); VectorSet(ent->maxs, 176, 120, 72); ent->s.modelindex = gi.modelindex("models/ships/bigviper/tris.md2"); gi.linkentity(ent); } /* ===================================================== */ /* * QUAKED misc_viper_bomb (1 0 0) (-8 -8 -8) (8 8 8) * "dmg" how much boom should the bomb make? */ void misc_viper_bomb_touch(edict_t *self, edict_t *other /* unused */, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { if (!self) { return; } G_UseTargets(self, self->activator); self->s.origin[2] = self->absmin[2] + 1; T_RadiusDamage(self, self, self->dmg, NULL, self->dmg + 40, MOD_BOMB); BecomeExplosion2(self); } void misc_viper_bomb_prethink(edict_t *self) { vec3_t v; float diff; if (!self) { return; } self->groundentity = NULL; diff = self->timestamp - level.time; if (diff < -1.0) { diff = -1.0; } VectorScale(self->moveinfo.dir, 1.0 + diff, v); v[2] = diff; diff = self->s.angles[2]; vectoangles(v, self->s.angles); self->s.angles[2] = diff + 10; } void misc_viper_bomb_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) { edict_t *viper; if (!self || !activator) { return; } self->solid = SOLID_BBOX; self->svflags &= ~SVF_NOCLIENT; self->s.effects |= EF_ROCKET; self->use = NULL; self->movetype = MOVETYPE_TOSS; self->prethink = misc_viper_bomb_prethink; self->touch = misc_viper_bomb_touch; self->activator = activator; viper = G_Find(NULL, FOFS(classname), "misc_viper"); VectorScale(viper->moveinfo.dir, viper->moveinfo.speed, self->velocity); self->timestamp = level.time; VectorCopy(viper->moveinfo.dir, self->moveinfo.dir); } void SP_misc_viper_bomb(edict_t *self) { if (!self) { return; } self->movetype = MOVETYPE_NONE; self->solid = SOLID_NOT; VectorSet(self->mins, -8, -8, -8); VectorSet(self->maxs, 8, 8, 8); self->s.modelindex = gi.modelindex("models/objects/bomb/tris.md2"); if (!self->dmg) { self->dmg = 1000; } self->use = misc_viper_bomb_use; self->svflags |= SVF_NOCLIENT; gi.linkentity(self); } /* ===================================================== */ /* * QUAKED misc_strogg_ship (1 .5 0) (-16 -16 0) (16 16 32) * This is a Storgg ship for the flybys. * It is trigger_spawned, so you must have something use it for it to show up. * There must be a path for it to follow once it is activated. * * "speed" How fast it should fly */ extern void train_use(edict_t *self, edict_t *other, edict_t *activator); extern void func_train_find(edict_t *self); void misc_strogg_ship_use(edict_t *self, edict_t *other /* other */, edict_t *activator) { if (!self || !activator) { return; } self->svflags &= ~SVF_NOCLIENT; self->use = train_use; train_use(self, other, activator); } void SP_misc_strogg_ship(edict_t *ent) { if (!ent) { return; } if (!ent->target) { gi.dprintf("%s without a target at %s\n", ent->classname, vtos(ent->absmin)); G_FreeEdict(ent); return; } if (!ent->speed) { ent->speed = 300; } ent->movetype = MOVETYPE_PUSH; ent->solid = SOLID_NOT; ent->s.modelindex = gi.modelindex("models/ships/strogg1/tris.md2"); VectorSet(ent->mins, -16, -16, 0); VectorSet(ent->maxs, 16, 16, 32); ent->think = func_train_find; ent->nextthink = level.time + FRAMETIME; ent->use = misc_strogg_ship_use; ent->svflags |= SVF_NOCLIENT; ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed; gi.linkentity(ent); } /* ===================================================== */ /* * QUAKED misc_satellite_dish (1 .5 0) (-64 -64 0) (64 64 128) */ void misc_satellite_dish_think(edict_t *self) { if (!self) { return; } self->s.frame++; if (self->s.frame < 38) { self->nextthink = level.time + FRAMETIME; } } void misc_satellite_dish_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!self) { return; } self->s.frame = 0; self->think = misc_satellite_dish_think; self->nextthink = level.time + FRAMETIME; } void SP_misc_satellite_dish(edict_t *ent) { if (!ent) { return; } ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_BBOX; VectorSet(ent->mins, -64, -64, 0); VectorSet(ent->maxs, 64, 64, 128); ent->s.modelindex = gi.modelindex("models/objects/satellite/tris.md2"); ent->use = misc_satellite_dish_use; gi.linkentity(ent); } /* ===================================================== */ /* * QUAKED light_mine1 (0 1 0) (-2 -2 -12) (2 2 12) */ void SP_light_mine1(edict_t *ent) { if (!ent) { return; } ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_BBOX; ent->s.modelindex = gi.modelindex("models/objects/minelite/light1/tris.md2"); gi.linkentity(ent); } /* * QUAKED light_mine2 (0 1 0) (-2 -2 -12) (2 2 12) */ void SP_light_mine2(edict_t *ent) { if (!ent) { return; } ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_BBOX; ent->s.modelindex = gi.modelindex("models/objects/minelite/light2/tris.md2"); gi.linkentity(ent); } /* ===================================================== */ /* * QUAKED misc_gib_arm (1 0 0) (-8 -8 -8) (8 8 8) * Intended for use with the target_spawner */ void SP_misc_gib_arm(edict_t *ent) { if (!ent) { return; } gi.setmodel(ent, "models/objects/gibs/arm/tris.md2"); ent->solid = SOLID_NOT; ent->s.effects |= EF_GIB; ent->takedamage = DAMAGE_YES; ent->die = gib_die; ent->movetype = MOVETYPE_TOSS; ent->svflags |= SVF_MONSTER; ent->deadflag = DEAD_DEAD; ent->avelocity[0] = random() * 200; ent->avelocity[1] = random() * 200; ent->avelocity[2] = random() * 200; ent->think = G_FreeEdict; ent->nextthink = level.time + 30; gi.linkentity(ent); } /* * QUAKED misc_gib_leg (1 0 0) (-8 -8 -8) (8 8 8) * Intended for use with the target_spawner */ void SP_misc_gib_leg(edict_t *ent) { if (!ent) { return; } gi.setmodel(ent, "models/objects/gibs/leg/tris.md2"); ent->solid = SOLID_NOT; ent->s.effects |= EF_GIB; ent->takedamage = DAMAGE_YES; ent->die = gib_die; ent->movetype = MOVETYPE_TOSS; ent->svflags |= SVF_MONSTER; ent->deadflag = DEAD_DEAD; ent->avelocity[0] = random() * 200; ent->avelocity[1] = random() * 200; ent->avelocity[2] = random() * 200; ent->think = G_FreeEdict; ent->nextthink = level.time + 30; gi.linkentity(ent); } /* * QUAKED misc_gib_head (1 0 0) (-8 -8 -8) (8 8 8) * Intended for use with the target_spawner */ void SP_misc_gib_head(edict_t *ent) { if (!ent) { return; } gi.setmodel(ent, "models/objects/gibs/head/tris.md2"); ent->solid = SOLID_NOT; ent->s.effects |= EF_GIB; ent->takedamage = DAMAGE_YES; ent->die = gib_die; ent->movetype = MOVETYPE_TOSS; ent->svflags |= SVF_MONSTER; ent->deadflag = DEAD_DEAD; ent->avelocity[0] = random() * 200; ent->avelocity[1] = random() * 200; ent->avelocity[2] = random() * 200; ent->think = G_FreeEdict; ent->nextthink = level.time + 30; gi.linkentity(ent); } /* ===================================================== */ /* * QUAKED target_character (0 0 1) ? * used with target_string (must be on same "team") * "count" is position in the string (starts at 1) */ void SP_target_character(edict_t *self) { if (!self) { return; } self->movetype = MOVETYPE_PUSH; gi.setmodel(self, self->model); self->solid = SOLID_BSP; self->s.frame = 12; gi.linkentity(self); return; } /* ===================================================== */ /* * QUAKED target_string (0 0 1) (-8 -8 -8) (8 8 8) */ void target_string_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */) { edict_t *e; int n, l; char c; if (!self) { return; } l = strlen(self->message); for (e = self->teammaster; e; e = e->teamchain) { if (!e->count) { continue; } n = e->count - 1; if (n > l) { e->s.frame = 12; continue; } c = self->message[n]; if ((c >= '0') && (c <= '9')) { e->s.frame = c - '0'; } else if (c == '-') { e->s.frame = 10; } else if (c == ':') { e->s.frame = 11; } else { e->s.frame = 12; } } } void SP_target_string(edict_t *self) { if (!self->message) { self->message = ""; } self->use = target_string_use; } /* ===================================================== */ /* * QUAKED func_clock (0 0 1) (-8 -8 -8) (8 8 8) TIMER_UP TIMER_DOWN START_OFF MULTI_USE * target a target_string with this * * The default is to be a time of day clock * * TIMER_UP and TIMER_DOWN run for "count" seconds and the fire "pathtarget" * If START_OFF, this entity must be used before it starts * * "style" 0 "xx" * 1 "xx:xx" * 2 "xx:xx:xx" */ #define CLOCK_MESSAGE_SIZE 16 /* don't let field width of any clock messages change, or it could cause an overwrite after a game load */ void func_clock_reset(edict_t *self) { if (!self) { return; } self->activator = NULL; if (self->spawnflags & 1) { self->health = 0; self->wait = self->count; } else if (self->spawnflags & 2) { self->health = self->count; self->wait = 0; } } /* * This is an evil hack to * prevent a rare crahs at * biggun exit. */ typedef struct zhead_s { struct zhead_s *prev, *next; short magic; short tag; int size; } zhead_t; void func_clock_format_countdown(edict_t *self) { if (!self) { return; } zhead_t *z = ( zhead_t * )self->message - 1; int size = z->size - sizeof (zhead_t); if (size < CLOCK_MESSAGE_SIZE) { gi.TagFree (self->message); self->message = gi.TagMalloc (CLOCK_MESSAGE_SIZE, TAG_LEVEL); } if (self->style == 0) { Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i", self->health); return; } if (self->style == 1) { Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i", self->health / 60, self->health % 60); if (self->message[3] == ' ') { self->message[3] = '0'; } return; } if (self->style == 2) { Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", self->health / 3600, (self->health - (self->health / 3600) * 3600) / 60, self->health % 60); if (self->message[3] == ' ') { self->message[3] = '0'; } if (self->message[6] == ' ') { self->message[6] = '0'; } return; } } void func_clock_think(edict_t *self) { if (!self) { return; } if (!self->enemy) { self->enemy = G_Find(NULL, FOFS(targetname), self->target); if (!self->enemy) { return; } } if (self->spawnflags & 1) { func_clock_format_countdown(self); self->health++; } else if (self->spawnflags & 2) { func_clock_format_countdown(self); self->health--; } else { struct tm *ltime; time_t gmtime; time(&gmtime); ltime = localtime(&gmtime); Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", ltime->tm_hour, ltime->tm_min, ltime->tm_sec); if (self->message[3] == ' ') { self->message[3] = '0'; } if (self->message[6] == ' ') { self->message[6] = '0'; } } self->enemy->message = self->message; self->enemy->use(self->enemy, self, self); if (((self->spawnflags & 1) && (self->health > self->wait)) || ((self->spawnflags & 2) && (self->health < self->wait))) { if (self->pathtarget) { char *savetarget; char *savemessage; savetarget = self->target; savemessage = self->message; self->target = self->pathtarget; self->message = NULL; G_UseTargets(self, self->activator); self->target = savetarget; self->message = savemessage; } if (!(self->spawnflags & 8)) { self->think = G_FreeEdict; self->nextthink = level.time + 1; return; } func_clock_reset(self); if (self->spawnflags & 4) { return; } } self->nextthink = level.time + 1; } void func_clock_use(edict_t *self, edict_t *other /* unused */, edict_t *activator) { if (!self || !activator) { return; } if (!(self->spawnflags & 8)) { self->use = NULL; } if (self->activator) { return; } self->activator = activator; self->think(self); } void SP_func_clock(edict_t *self) { if (!self) { return; } if (!self->target) { gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin)); G_FreeEdict(self); return; } if ((self->spawnflags & 2) && (!self->count)) { gi.dprintf("%s with no count at %s\n", self->classname, vtos(self->s.origin)); G_FreeEdict(self); return; } if ((self->spawnflags & 1) && (!self->count)) { self->count = 60 * 60; } func_clock_reset(self); self->message = gi.TagMalloc(CLOCK_MESSAGE_SIZE, TAG_LEVEL); self->think = func_clock_think; if (self->spawnflags & 4) { self->use = func_clock_use; } else { self->nextthink = level.time + 1; } } /* ================================================================================= */ void teleporter_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { edict_t *dest; int i; if (!self || !other) { return; } if (!other->client) { return; } dest = G_Find(NULL, FOFS(targetname), self->target); if (!dest) { gi.dprintf("Couldn't find destination\n"); return; } /* unlink to make sure it can't possibly interfere with KillBox */ gi.unlinkentity(other); VectorCopy(dest->s.origin, other->s.origin); VectorCopy(dest->s.origin, other->s.old_origin); other->s.origin[2] += 10; /* clear the velocity and hold them in place briefly */ VectorClear(other->velocity); other->client->ps.pmove.pm_time = 160 >> 3; /* hold time */ other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT; /* draw the teleport splash at source and on the player */ self->owner->s.event = EV_PLAYER_TELEPORT; other->s.event = EV_PLAYER_TELEPORT; /* set angles */ for (i = 0; i < 3; i++) { other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT( dest->s.angles[i] - other->client->resp.cmd_angles[i]); } VectorClear(other->s.angles); VectorClear(other->client->ps.viewangles); VectorClear(other->client->v_angle); /* kill anything at the destination */ KillBox(other); gi.linkentity(other); } /* * QUAKED misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16) * Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object. */ void SP_misc_teleporter(edict_t *ent) { if (!ent) { return; } edict_t *trig; if (!ent->target) { gi.dprintf("teleporter without a target.\n"); G_FreeEdict(ent); return; } gi.setmodel(ent, "models/objects/dmspot/tris.md2"); ent->s.skinnum = 1; ent->s.effects = EF_TELEPORTER; ent->s.sound = gi.soundindex("world/amb10.wav"); ent->solid = SOLID_BBOX; VectorSet(ent->mins, -32, -32, -24); VectorSet(ent->maxs, 32, 32, -16); gi.linkentity(ent); trig = G_Spawn(); trig->touch = teleporter_touch; trig->solid = SOLID_TRIGGER; trig->target = ent->target; trig->owner = ent; VectorCopy(ent->s.origin, trig->s.origin); VectorSet(trig->mins, -8, -8, 8); VectorSet(trig->maxs, 8, 8, 24); gi.linkentity(trig); } /* * QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16) * Point teleporters at these. */ void SP_misc_teleporter_dest(edict_t *ent) { if (!ent) { return; } gi.setmodel(ent, "models/objects/dmspot/tris.md2"); ent->s.skinnum = 0; ent->solid = SOLID_BBOX; VectorSet(ent->mins, -32, -32, -24); VectorSet(ent->maxs, 32, 32, -16); gi.linkentity(ent); } yquake2-QUAKE2_5_32/src/game/g_svcmds.c0000644000175000017500000001421412615162034020272 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Game side of server CMDs. At this time only the ipfilter. * * ======================================================================= */ #include "header/local.h" #define MAX_IPFILTERS 1024 void Svcmd_Test_f(void) { gi.cprintf(NULL, PRINT_HIGH, "Svcmd_Test_f()\n"); } /* * ============================================================================== * * PACKET FILTERING * * * You can add or remove addresses from the filter list with: * * addip * removeip * * The ip address is specified in dot format, and any unspecified * digits will match any value, so you can specify an entire class * C network with "addip 192.246.40". * * Removeip will only remove an address specified exactly the same * way. You cannot addip a subnet, then removeip a single host. * * listip * Prints the current list of filters. * * writeip * Dumps "addip " commands to listip.cfg so it can be execed * at a later date. The filter lists are not saved and restored * by default, because I belive it would cause too much confusion. * * filterban <0 or 1> * If 1 (the default), then ip addresses matching the current list * will be prohibited from entering the game.This is the default * setting. * If 0, then only addresses matching the list will be allowed. * This lets you easily set up a private game, or a game that only * allows players from your local network. * * ============================================================================== */ typedef struct { unsigned mask; unsigned compare; } ipfilter_t; ipfilter_t ipfilters[MAX_IPFILTERS]; int numipfilters; qboolean StringToFilter(char *s, ipfilter_t *f) { char num[128]; int i, j; byte b[4]; byte m[4]; if (!s || !f) { return false; } for (i = 0; i < 4; i++) { b[i] = 0; m[i] = 0; } for (i = 0; i < 4; i++) { if ((*s < '0') || (*s > '9')) { gi.cprintf(NULL, PRINT_HIGH, "Bad filter address: %s\n", s); return false; } j = 0; while (*s >= '0' && *s <= '9') { num[j++] = *s++; } num[j] = 0; b[i] = (int)strtol(num, (char **)NULL, 10); if (b[i] != 0) { m[i] = 255; } if (!*s) { break; } s++; } f->mask = *(unsigned *)m; f->compare = *(unsigned *)b; return true; } qboolean SV_FilterPacket(char *from) { int i; unsigned in; byte m[4]; char *p; if (!from) { return false; } i = 0; p = from; while (*p && i < 4) { m[i] = 0; while (*p >= '0' && *p <= '9') { m[i] = m[i] * 10 + (*p - '0'); p++; } if (!*p || (*p == ':')) { break; } i++, p++; } in = *(unsigned *)m; for (i = 0; i < numipfilters; i++) { if ((in & ipfilters[i].mask) == ipfilters[i].compare) { return (int)filterban->value; } } return (int)!filterban->value; } void SVCmd_AddIP_f(void) { int i; if (gi.argc() < 3) { gi.cprintf(NULL, PRINT_HIGH, "Usage: addip \n"); return; } for (i = 0; i < numipfilters; i++) { if (ipfilters[i].compare == 0xffffffff) { break; /* free spot */ } } if (i == numipfilters) { if (numipfilters == MAX_IPFILTERS) { gi.cprintf(NULL, PRINT_HIGH, "IP filter list is full\n"); return; } numipfilters++; } if (!StringToFilter(gi.argv(2), &ipfilters[i])) { ipfilters[i].compare = 0xffffffff; } } void SVCmd_RemoveIP_f(void) { ipfilter_t f; int i, j; if (gi.argc() < 3) { gi.cprintf(NULL, PRINT_HIGH, "Usage: sv removeip \n"); return; } if (!StringToFilter(gi.argv(2), &f)) { return; } for (i = 0; i < numipfilters; i++) { if ((ipfilters[i].mask == f.mask) && (ipfilters[i].compare == f.compare)) { for (j = i + 1; j < numipfilters; j++) { ipfilters[j - 1] = ipfilters[j]; } numipfilters--; gi.cprintf(NULL, PRINT_HIGH, "Removed.\n"); return; } } gi.cprintf(NULL, PRINT_HIGH, "Didn't find %s.\n", gi.argv(2)); } void SVCmd_ListIP_f(void) { int i; byte b[4]; gi.cprintf(NULL, PRINT_HIGH, "Filter list:\n"); for (i = 0; i < numipfilters; i++) { *(unsigned *)b = ipfilters[i].compare; gi.cprintf(NULL, PRINT_HIGH, "%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]); } } void SVCmd_WriteIP_f(void) { FILE *f; char name[MAX_OSPATH]; byte b[4]; int i; cvar_t *game; game = gi.cvar("game", "", 0); if (!*game->string) { sprintf(name, "%s/listip.cfg", GAMEVERSION); } else { sprintf(name, "%s/listip.cfg", game->string); } gi.cprintf(NULL, PRINT_HIGH, "Writing %s.\n", name); f = fopen(name, "wb"); if (!f) { gi.cprintf(NULL, PRINT_HIGH, "Couldn't open %s\n", name); return; } fprintf(f, "set filterban %d\n", (int)filterban->value); for (i = 0; i < numipfilters; i++) { *(unsigned *)b = ipfilters[i].compare; fprintf(f, "sv addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]); } fclose(f); } /* * ServerCommand will be called when an "sv" command is issued. * The game can issue gi.argc() / gi.argv() commands to get the rest * of the parameters */ void ServerCommand(void) { char *cmd; cmd = gi.argv(1); if (Q_stricmp(cmd, "test") == 0) { Svcmd_Test_f(); } else if (Q_stricmp(cmd, "addip") == 0) { SVCmd_AddIP_f(); } else if (Q_stricmp(cmd, "removeip") == 0) { SVCmd_RemoveIP_f(); } else if (Q_stricmp(cmd, "listip") == 0) { SVCmd_ListIP_f(); } else if (Q_stricmp(cmd, "writeip") == 0) { SVCmd_WriteIP_f(); } else { gi.cprintf(NULL, PRINT_HIGH, "Unknown server command \"%s\"\n", cmd); } } yquake2-QUAKE2_5_32/src/game/g_combat.c0000644000175000017500000003542412615162034020246 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Combat code like damage, death and so on. * * ======================================================================= */ #include "header/local.h" /* * Returns true if the inflictor can * directly damage the target. Used for * explosions and melee attacks. */ qboolean CanDamage(edict_t *targ, edict_t *inflictor) { vec3_t dest; trace_t trace; if (!targ || !inflictor) { return false; } /* bmodels need special checking because their origin is 0,0,0 */ if (targ->movetype == MOVETYPE_PUSH) { VectorAdd(targ->absmin, targ->absmax, dest); VectorScale(dest, 0.5, dest); trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); if (trace.fraction == 1.0) { return true; } if (trace.ent == targ) { return true; } return false; } trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, targ->s.origin, inflictor, MASK_SOLID); if (trace.fraction == 1.0) { return true; } VectorCopy(targ->s.origin, dest); dest[0] += 15.0; dest[1] += 15.0; trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); if (trace.fraction == 1.0) { return true; } VectorCopy(targ->s.origin, dest); dest[0] += 15.0; dest[1] -= 15.0; trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); if (trace.fraction == 1.0) { return true; } VectorCopy(targ->s.origin, dest); dest[0] -= 15.0; dest[1] += 15.0; trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); if (trace.fraction == 1.0) { return true; } VectorCopy(targ->s.origin, dest); dest[0] -= 15.0; dest[1] -= 15.0; trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID); if (trace.fraction == 1.0) { return true; } return false; } void Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { if (!targ || !inflictor || !attacker) { return; } if (targ->health < -999) { targ->health = -999; } targ->enemy = attacker; if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) { if (!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) { level.killed_monsters++; if (coop->value && attacker->client) { attacker->client->resp.score++; } /* medics won't heal monsters that they kill themselves */ if (strcmp(attacker->classname, "monster_medic") == 0) { targ->owner = attacker; } } } if ((targ->movetype == MOVETYPE_PUSH) || (targ->movetype == MOVETYPE_STOP) || (targ->movetype == MOVETYPE_NONE)) { /* doors, triggers, etc */ targ->die(targ, inflictor, attacker, damage, point); return; } if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) { targ->touch = NULL; monster_death_use(targ); } targ->die(targ, inflictor, attacker, damage, point); } void SpawnDamage(int type, vec3_t origin, vec3_t normal) { gi.WriteByte(svc_temp_entity); gi.WriteByte(type); gi.WritePosition(origin); gi.WriteDir(normal); gi.multicast(origin, MULTICAST_PVS); } /* * targ entity that is being damaged * inflictor entity that is causing the damage * attacker entity that caused the inflictor to damage targ * example: targ=monster, inflictor=rocket, attacker=player * * dir direction of the attack * point point at which the damage is being inflicted * normal normal vector from that point * damage amount of damage being inflicted * knockback force to be applied against targ as a result of the damage * * dflags -> these flags are used to control how T_Damage works * DAMAGE_RADIUS damage was indirect (from a nearby explosion) * DAMAGE_NO_ARMOR armor does not protect from this damage * DAMAGE_ENERGY damage is from an energy based weapon * DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles * DAMAGE_BULLET damage is from a bullet (used for ricochets) * DAMAGE_NO_PROTECTION kills godmode, armor, everything */ int CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal, int damage, int dflags) { gclient_t *client; int save; int power_armor_type; int index; int damagePerCell; int pa_te_type; int power = 0; int power_used; if (!ent) { return 0; } if (!damage) { return 0; } index = 0; client = ent->client; if (dflags & DAMAGE_NO_ARMOR) { return 0; } if (client) { power_armor_type = PowerArmorType(ent); if (power_armor_type != POWER_ARMOR_NONE) { index = ITEM_INDEX(FindItem("Cells")); power = client->pers.inventory[index]; } } else if (ent->svflags & SVF_MONSTER) { power_armor_type = ent->monsterinfo.power_armor_type; power = ent->monsterinfo.power_armor_power; index = 0; } else { return 0; } if (power_armor_type == POWER_ARMOR_NONE) { return 0; } if (!power) { return 0; } if (power_armor_type == POWER_ARMOR_SCREEN) { vec3_t vec; float dot; vec3_t forward; /* only works if damage point is in front */ AngleVectors(ent->s.angles, forward, NULL, NULL); VectorSubtract(point, ent->s.origin, vec); VectorNormalize(vec); dot = DotProduct(vec, forward); if (dot <= 0.3) { return 0; } damagePerCell = 1; pa_te_type = TE_SCREEN_SPARKS; damage = damage / 3; } else { damagePerCell = 2; pa_te_type = TE_SHIELD_SPARKS; damage = (2 * damage) / 3; } save = power * damagePerCell; if (!save) { return 0; } if (save > damage) { save = damage; } SpawnDamage(pa_te_type, point, normal); ent->powerarmor_time = level.time + 0.2; power_used = save / damagePerCell; if (client) { client->pers.inventory[index] -= power_used; } else { ent->monsterinfo.power_armor_power -= power_used; } return save; } int CheckArmor(edict_t *ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags) { gclient_t *client; int save; int index; gitem_t *armor; if (!ent) { return 0; } if (!damage) { return 0; } client = ent->client; if (!client) { return 0; } if (dflags & DAMAGE_NO_ARMOR) { return 0; } index = ArmorIndex(ent); if (!index) { return 0; } armor = GetItemByIndex(index); if (dflags & DAMAGE_ENERGY) { save = ceil(((gitem_armor_t *)armor->info)->energy_protection * damage); } else { save = ceil(((gitem_armor_t *)armor->info)->normal_protection * damage); } if (save >= client->pers.inventory[index]) { save = client->pers.inventory[index]; } if (!save) { return 0; } client->pers.inventory[index] -= save; SpawnDamage(te_sparks, point, normal); return save; } void M_ReactToDamage(edict_t *targ, edict_t *attacker) { if (!targ || !attacker) { return; } if (targ->health <= 0) { return; } if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER)) { return; } if ((attacker == targ) || (attacker == targ->enemy)) { return; } /* if we are a good guy monster and our attacker is a player or another good guy, do not get mad at them */ if (targ->monsterinfo.aiflags & AI_GOOD_GUY) { if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY)) { return; } } /* if attacker is a client, get mad at them because he's good and we're not */ if (attacker->client) { targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET; /* this can only happen in coop (both new and old enemies are clients) only switch if can't see the current enemy */ if (targ->enemy && targ->enemy->client) { if (visible(targ, targ->enemy)) { targ->oldenemy = attacker; return; } targ->oldenemy = targ->enemy; } targ->enemy = attacker; if (!(targ->monsterinfo.aiflags & AI_DUCKED)) { FoundTarget(targ); } return; } /* it's the same base (walk/swim/fly) type and a different classname and it's not a tank (they spray too much), get mad at them */ if (((targ->flags & (FL_FLY | FL_SWIM)) == (attacker->flags & (FL_FLY | FL_SWIM))) && (strcmp(targ->classname, attacker->classname) != 0) && (strcmp(attacker->classname, "monster_tank") != 0) && (strcmp(attacker->classname, "monster_supertank") != 0) && (strcmp(attacker->classname, "monster_makron") != 0) && (strcmp(attacker->classname, "monster_jorg") != 0)) { if (targ->enemy && targ->enemy->client) { targ->oldenemy = targ->enemy; } targ->enemy = attacker; if (!(targ->monsterinfo.aiflags & AI_DUCKED)) { FoundTarget(targ); } } /* if they *meant* to shoot us, then shoot back */ else if (attacker->enemy == targ) { if (targ->enemy && targ->enemy->client) { targ->oldenemy = targ->enemy; } targ->enemy = attacker; if (!(targ->monsterinfo.aiflags & AI_DUCKED)) { FoundTarget(targ); } } /* otherwise get mad at whoever they are mad at (help our buddy) unless it is us! */ else if (attacker->enemy && (attacker->enemy != targ)) { if (targ->enemy && targ->enemy->client) { targ->oldenemy = targ->enemy; } targ->enemy = attacker->enemy; if (!(targ->monsterinfo.aiflags & AI_DUCKED)) { FoundTarget(targ); } } } void T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod) { gclient_t *client; int take; int save; int asave; int psave; int te_sparks; if (!targ || !inflictor || !attacker) { return; } if (!targ->takedamage) { return; } /* friendly fire avoidance if enabled you can't hurt teammates (but you can hurt yourself) knockback still occurs */ if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value)) { if (OnSameTeam(targ, attacker)) { if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) { damage = 0; } else { mod |= MOD_FRIENDLY_FIRE; } } } meansOfDeath = mod; /* easy mode takes half damage */ if ((skill->value == 0) && (deathmatch->value == 0) && targ->client) { damage *= 0.5; if (!damage) { damage = 1; } } client = targ->client; if (dflags & DAMAGE_BULLET) { te_sparks = TE_BULLET_SPARKS; } else { te_sparks = TE_SPARKS; } VectorNormalize(dir); /* bonus damage for suprising a monster */ if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0)) { damage *= 2; } if (targ->flags & FL_NO_KNOCKBACK) { knockback = 0; } /* figure momentum add */ if (!(dflags & DAMAGE_NO_KNOCKBACK)) { if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP)) { vec3_t kvel; float mass; if (targ->mass < 50) { mass = 50; } else { mass = targ->mass; } if (targ->client && (attacker == targ)) { /* This allows rocket jumps */ VectorScale(dir, 1600.0 * (float)knockback / mass, kvel); } else { VectorScale(dir, 500.0 * (float)knockback / mass, kvel); } VectorAdd(targ->velocity, kvel, targ->velocity); } } take = damage; save = 0; /* check for godmode */ if ((targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION)) { take = 0; save = damage; SpawnDamage(te_sparks, point, normal); } /* check for invincibility */ if ((client && (client->invincible_framenum > level.framenum)) && !(dflags & DAMAGE_NO_PROTECTION)) { if (targ->pain_debounce_time < level.time) { gi.sound(targ, CHAN_ITEM, gi.soundindex( "items/protect4.wav"), 1, ATTN_NORM, 0); targ->pain_debounce_time = level.time + 2; } take = 0; save = damage; } psave = CheckPowerArmor(targ, point, normal, take, dflags); take -= psave; asave = CheckArmor(targ, point, normal, take, te_sparks, dflags); take -= asave; /* treat cheat/powerup savings the same as armor */ asave += save; /* team damage avoidance */ if (!(dflags & DAMAGE_NO_PROTECTION) && false) { return; } /* do the damage */ if (take) { if ((targ->svflags & SVF_MONSTER) || (client)) { SpawnDamage(TE_BLOOD, point, normal); } else { SpawnDamage(te_sparks, point, normal); } targ->health = targ->health - take; if (targ->health <= 0) { if ((targ->svflags & SVF_MONSTER) || (client)) { targ->flags |= FL_NO_KNOCKBACK; } Killed(targ, inflictor, attacker, take, point); return; } } if (targ->svflags & SVF_MONSTER) { M_ReactToDamage(targ, attacker); if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take)) { targ->pain(targ, attacker, knockback, take); /* nightmare mode monsters don't go into pain frames often */ if (skill->value == 3) { targ->pain_debounce_time = level.time + 5; } } } else if (client) { if (!(targ->flags & FL_GODMODE) && (take)) { targ->pain(targ, attacker, knockback, take); } } else if (take) { if (targ->pain) { targ->pain(targ, attacker, knockback, take); } } /* add to the damage inflicted on a player this frame the total will be turned into screen blends and view angle kicks at the end of the frame */ if (client) { client->damage_parmor += psave; client->damage_armor += asave; client->damage_blood += take; client->damage_knockback += knockback; VectorCopy(point, client->damage_from); } } void T_RadiusDamage(edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod) { float points; edict_t *ent = NULL; vec3_t v; vec3_t dir; if (!inflictor || !attacker) { return; } while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) { if (ent == ignore) { continue; } if (!ent->takedamage) { continue; } VectorAdd(ent->mins, ent->maxs, v); VectorMA(ent->s.origin, 0.5, v, v); VectorSubtract(inflictor->s.origin, v, v); points = damage - 0.5 * VectorLength(v); if (ent == attacker) { points = points * 0.5; } if (points > 0) { if (CanDamage(ent, inflictor)) { VectorSubtract(ent->s.origin, inflictor->s.origin, dir); T_Damage(ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); } } } } yquake2-QUAKE2_5_32/src/game/g_ai.c0000644000175000017500000005721312615162034017372 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 basic AI functions like enemy detection, attacking and so on. * * ======================================================================= */ #include "header/local.h" extern cvar_t *maxclients; qboolean enemy_vis; qboolean enemy_infront; int enemy_range; float enemy_yaw; qboolean FindTarget(edict_t *self); qboolean ai_checkattack(edict_t *self); /* * Called once each frame to set level.sight_client * to the player to be checked for in findtarget. * If all clients are either dead or in notarget, * sight_client will be null. * In coop games, sight_client will cycle * between the clients. */ void AI_SetSightClient(void) { edict_t *ent; int start, check; if (level.sight_client == NULL) { start = 1; } else { start = level.sight_client - g_edicts; } check = start; while (1) { check++; if (check > game.maxclients) { check = 1; } ent = &g_edicts[check]; if (ent->inuse && (ent->health > 0) && !(ent->flags & FL_NOTARGET)) { level.sight_client = ent; return; /* got one */ } if (check == start) { level.sight_client = NULL; return; /* nobody to see */ } } } /* * Move the specified distance at current facing. */ void ai_move(edict_t *self, float dist) { if (!self) { return; } M_walkmove(self, self->s.angles[YAW], dist); } /* * * Used for standing around and looking * for players Distance is for slight * position adjustments needed by the * animations */ void ai_stand(edict_t *self, float dist) { vec3_t v; if (!self) { return; } if (dist) { M_walkmove(self, self->s.angles[YAW], dist); } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { if (self->enemy) { VectorSubtract(self->enemy->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); if ((self->s.angles[YAW] != self->ideal_yaw) && self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND) { self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND); self->monsterinfo.run(self); } M_ChangeYaw(self); ai_checkattack(self); } else { FindTarget(self); } return; } if (FindTarget(self)) { return; } if (level.time > self->monsterinfo.pausetime) { self->monsterinfo.walk(self); return; } if (!(self->spawnflags & 1) && (self->monsterinfo.idle) && (level.time > self->monsterinfo.idle_time)) { if (self->monsterinfo.idle_time) { self->monsterinfo.idle(self); self->monsterinfo.idle_time = level.time + 15 + random() * 15; } else { self->monsterinfo.idle_time = level.time + random() * 15; } } } /* * The monster is walking it's beat */ void ai_walk(edict_t *self, float dist) { if (!self) { return; } M_MoveToGoal(self, dist); /* check for noticing a player */ if (FindTarget(self)) { return; } if ((self->monsterinfo.search) && (level.time > self->monsterinfo.idle_time)) { if (self->monsterinfo.idle_time) { self->monsterinfo.search(self); self->monsterinfo.idle_time = level.time + 15 + random() * 15; } else { self->monsterinfo.idle_time = level.time + random() * 15; } } } /* * Turns towards target and advances * Use this call with a distance of 0 * to replace ai_face */ void ai_charge(edict_t *self, float dist) { vec3_t v; if (!self) { return; } VectorSubtract(self->enemy->s.origin, self->s.origin, v); self->ideal_yaw = vectoyaw(v); M_ChangeYaw(self); if (dist) { M_walkmove(self, self->s.angles[YAW], dist); } } /* * Don't move, but turn towards * ideal_yaw. Distance is for * slight position adjustments * needed by the animations */ void ai_turn(edict_t *self, float dist) { if (!self) { return; } if (dist) { M_walkmove(self, self->s.angles[YAW], dist); } if (FindTarget(self)) { return; } M_ChangeYaw(self); } /* ============================================================================ */ /* * .enemy * Will be world if not currently angry at anyone. * * .movetarget * The next path spot to walk toward. If .enemy, ignore .movetarget. * When an enemy is killed, the monster will try to return to it's path. * * .hunt_time * Set to time + something when the player is in sight, but movement straight for * him is blocked. This causes the monster to use wall following code for * movement direction instead of sighting on the player. * * .ideal_yaw * A yaw angle of the intended direction, which will be turned towards at up * to 45 deg / state. If the enemy is in view and hunt_time is not active, * this will be the exact line towards the enemy. * * .pausetime * A monster will leave it's stand state and head towards it's .movetarget when * time > .pausetime. */ /* ============================================================================ */ /* * returns the range categorization of an entity relative to self * 0 melee range, will become hostile even if back is turned * 1 visibility and infront, or visibility and show hostile * 2 infront and show hostile * 3 only triggered by damage */ int range(edict_t *self, edict_t *other) { vec3_t v; float len; if (!self || !other) { return 0; } VectorSubtract(self->s.origin, other->s.origin, v); len = VectorLength(v); if (len < MELEE_DISTANCE) { return RANGE_MELEE; } if (len < 500) { return RANGE_NEAR; } if (len < 1000) { return RANGE_MID; } return RANGE_FAR; } /* * returns 1 if the entity is visible * to self, even if not infront */ qboolean visible(edict_t *self, edict_t *other) { vec3_t spot1; vec3_t spot2; trace_t trace; if (!self || !other) { return false; } VectorCopy(self->s.origin, spot1); spot1[2] += self->viewheight; VectorCopy(other->s.origin, spot2); spot2[2] += other->viewheight; trace = gi.trace(spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE); if (trace.fraction == 1.0) { return true; } return false; } /* * returns 1 if the entity is in * front (in sight) of self */ qboolean infront(edict_t *self, edict_t *other) { vec3_t vec; float dot; vec3_t forward; if (!self || !other) { return false; } AngleVectors(self->s.angles, forward, NULL, NULL); VectorSubtract(other->s.origin, self->s.origin, vec); VectorNormalize(vec); dot = DotProduct(vec, forward); if (dot > 0.3) { return true; } return false; } /* ============================================================================ */ void HuntTarget(edict_t *self) { vec3_t vec; if (!self) { return; } self->goalentity = self->enemy; if (self->monsterinfo.aiflags & AI_STAND_GROUND) { self->monsterinfo.stand(self); } else { self->monsterinfo.run(self); } VectorSubtract(self->enemy->s.origin, self->s.origin, vec); self->ideal_yaw = vectoyaw(vec); /* wait a while before first attack */ if (!(self->monsterinfo.aiflags & AI_STAND_GROUND)) { AttackFinished(self, 1); } } void FoundTarget(edict_t *self) { if (!self) { return; } /* let other monsters see this monster for a while */ if (self->enemy->client) { level.sight_entity = self; level.sight_entity_framenum = level.framenum; level.sight_entity->light_level = 128; } self->show_hostile = level.time + 1; /* wake up other monsters */ VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); self->monsterinfo.trail_time = level.time; if (!self->combattarget) { HuntTarget(self); return; } self->goalentity = self->movetarget = G_PickTarget(self->combattarget); if (!self->movetarget) { self->goalentity = self->movetarget = self->enemy; HuntTarget(self); gi.dprintf("%s at %s, combattarget %s not found\n", self->classname, vtos(self->s.origin), self->combattarget); return; } /* clear out our combattarget, these are a one shot deal */ self->combattarget = NULL; self->monsterinfo.aiflags |= AI_COMBAT_POINT; /* clear the targetname, that point is ours! */ self->movetarget->targetname = NULL; self->monsterinfo.pausetime = 0; /* run for it */ self->monsterinfo.run(self); } /* * Self is currently not attacking anything, * so try to find a target * * Returns TRUE if an enemy was sighted * * When a player fires a missile, the point * of impact becomes a fakeplayer so that * monsters that see the impact will respond * as if they had seen the player. * * To avoid spending too much time, only * a single client (or fakeclient) is * checked each frame. This means multi * player games will have slightly * slower noticing monsters. */ qboolean FindTarget(edict_t *self) { edict_t *client; qboolean heardit; int r; if (!self) { return false; } if (self->monsterinfo.aiflags & AI_GOOD_GUY) { return false; } /* if we're going to a combat point, just proceed */ if (self->monsterinfo.aiflags & AI_COMBAT_POINT) { return false; } /* if the first spawnflag bit is set, the monster will only wake up on really seeing the player, not another monster getting angry or hearing something */ heardit = false; if ((level.sight_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1)) { client = level.sight_entity; if (client->enemy == self->enemy) { return false; } } else if (level.sound_entity_framenum >= (level.framenum - 1)) { client = level.sound_entity; heardit = true; } else if (!(self->enemy) && (level.sound2_entity_framenum >= (level.framenum - 1)) && !(self->spawnflags & 1)) { client = level.sound2_entity; heardit = true; } else { client = level.sight_client; if (!client) { return false; /* no clients to get mad at */ } } /* if the entity went away, forget it */ if (!client->inuse) { return false; } if (client == self->enemy) { return true; } if (client->client) { if (client->flags & FL_NOTARGET) { return false; } } else if (client->svflags & SVF_MONSTER) { if (!client->enemy) { return false; } if (client->enemy->flags & FL_NOTARGET) { return false; } } else if (heardit) { if (client->owner->flags & FL_NOTARGET) { return false; } } else { return false; } if (!heardit) { r = range(self, client); if (r == RANGE_FAR) { return false; } /* is client in an spot too dark to be seen? */ if (client->light_level <= 5) { return false; } if (!visible(self, client)) { return false; } if (r == RANGE_NEAR) { if ((client->show_hostile < level.time) && !infront(self, client)) { return false; } } else if (r == RANGE_MID) { if (!infront(self, client)) { return false; } } self->enemy = client; if (strcmp(self->enemy->classname, "player_noise") != 0) { self->monsterinfo.aiflags &= ~AI_SOUND_TARGET; if (!self->enemy->client) { self->enemy = self->enemy->enemy; if (!self->enemy->client) { self->enemy = NULL; return false; } } } } else /* heardit */ { vec3_t temp; if (self->spawnflags & 1) { if (!visible(self, client)) { return false; } } else { if (!gi.inPHS(self->s.origin, client->s.origin)) { return false; } } VectorSubtract(client->s.origin, self->s.origin, temp); if (VectorLength(temp) > 1000) /* too far to hear */ { return false; } /* check area portals - if they are different and not connected then we can't hear it */ if (client->areanum != self->areanum) { if (!gi.AreasConnected(self->areanum, client->areanum)) { return false; } } self->ideal_yaw = vectoyaw(temp); M_ChangeYaw(self); /* hunt the sound for a bit; hopefully find the real player */ self->monsterinfo.aiflags |= AI_SOUND_TARGET; self->enemy = client; } FoundTarget(self); if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight)) { self->monsterinfo.sight(self, self->enemy); } return true; } /* ============================================================================= */ qboolean FacingIdeal(edict_t *self) { float delta; if (!self) { return false; } delta = anglemod(self->s.angles[YAW] - self->ideal_yaw); if ((delta > 45) && (delta < 315)) { return false; } return true; } /* ============================================================================= */ qboolean M_CheckAttack(edict_t *self) { vec3_t spot1, spot2; float chance; trace_t tr; if (!self) { return false; } if (self->enemy->health > 0) { /* see if any entities are in the way of the shot */ VectorCopy(self->s.origin, spot1); spot1[2] += self->viewheight; VectorCopy(self->enemy->s.origin, spot2); spot2[2] += self->enemy->viewheight; tr = gi.trace(spot1, NULL, NULL, spot2, self, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_SLIME | CONTENTS_LAVA | CONTENTS_WINDOW); /* do we have a clear shot? */ if (tr.ent != self->enemy) { return false; } } /* melee attack */ if (enemy_range == RANGE_MELEE) { /* don't always melee in easy mode */ if ((skill->value == 0) && (randk() & 3)) { return false; } if (self->monsterinfo.melee) { self->monsterinfo.attack_state = AS_MELEE; } else { self->monsterinfo.attack_state = AS_MISSILE; } return true; } /* missile attack */ if (!self->monsterinfo.attack) { return false; } if (level.time < self->monsterinfo.attack_finished) { return false; } if (enemy_range == RANGE_FAR) { return false; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { chance = 0.4; } else if (enemy_range == RANGE_MELEE) { chance = 0.2; } else if (enemy_range == RANGE_NEAR) { chance = 0.1; } else if (enemy_range == RANGE_MID) { chance = 0.02; } else { return false; } if (skill->value == 0) { chance *= 0.5; } else if (skill->value >= 2) { chance *= 2; } if (random() < chance) { self->monsterinfo.attack_state = AS_MISSILE; self->monsterinfo.attack_finished = level.time + 2 * random(); return true; } if (self->flags & FL_FLY) { if (random() < 0.3) { self->monsterinfo.attack_state = AS_SLIDING; } else { self->monsterinfo.attack_state = AS_STRAIGHT; } } return false; } /* * Turn and close until within an * angle to launch a melee attack */ void ai_run_melee(edict_t *self) { if (!self) { return; } self->ideal_yaw = enemy_yaw; M_ChangeYaw(self); if (FacingIdeal(self)) { self->monsterinfo.melee(self); self->monsterinfo.attack_state = AS_STRAIGHT; } } /* * Turn in place until within an * angle to launch a missile attack */ void ai_run_missile(edict_t *self) { if (!self) { return; } self->ideal_yaw = enemy_yaw; M_ChangeYaw(self); if (FacingIdeal(self)) { self->monsterinfo.attack(self); self->monsterinfo.attack_state = AS_STRAIGHT; } } /* * Strafe sideways, but stay at * approximately the same range */ void ai_run_slide(edict_t *self, float distance) { float ofs; if (!self) { return; } self->ideal_yaw = enemy_yaw; M_ChangeYaw(self); if (self->monsterinfo.lefty) { ofs = 90; } else { ofs = -90; } if (M_walkmove(self, self->ideal_yaw + ofs, distance)) { return; } self->monsterinfo.lefty = 1 - self->monsterinfo.lefty; M_walkmove(self, self->ideal_yaw - ofs, distance); } /* * Decides if we're going to attack * or do something else used by * ai_run and ai_stand */ qboolean ai_checkattack(edict_t *self) { vec3_t temp; qboolean hesDeadJim; if (!self) { return false; } /* this causes monsters to run blindly to the combat point w/o firing */ if (self->goalentity) { if (self->monsterinfo.aiflags & AI_COMBAT_POINT) { return false; } if (self->monsterinfo.aiflags & AI_SOUND_TARGET) { if ((level.time - self->enemy->teleport_time) > 5.0) { if (self->goalentity == self->enemy) { if (self->movetarget) { self->goalentity = self->movetarget; } else { self->goalentity = NULL; } } self->monsterinfo.aiflags &= ~AI_SOUND_TARGET; if (self->monsterinfo.aiflags & AI_TEMP_STAND_GROUND) { self->monsterinfo.aiflags &= ~(AI_STAND_GROUND | AI_TEMP_STAND_GROUND); } } else { self->show_hostile = level.time + 1; return false; } } } enemy_vis = false; /* see if the enemy is dead */ hesDeadJim = false; if ((!self->enemy) || (!self->enemy->inuse)) { hesDeadJim = true; } else if (self->monsterinfo.aiflags & AI_MEDIC) { if (self->enemy->health > 0) { hesDeadJim = true; self->monsterinfo.aiflags &= ~AI_MEDIC; } } else { if (self->monsterinfo.aiflags & AI_BRUTAL) { if (self->enemy->health <= -80) { hesDeadJim = true; } } else { if (self->enemy->health <= 0) { hesDeadJim = true; } } } if (hesDeadJim) { self->enemy = NULL; if (self->oldenemy && (self->oldenemy->health > 0)) { self->enemy = self->oldenemy; self->oldenemy = NULL; HuntTarget(self); } else { if (self->movetarget) { self->goalentity = self->movetarget; self->monsterinfo.walk(self); } else { /* we need the pausetime otherwise the stand code will just revert to walking with no target and the monsters will wonder around aimlessly trying to hunt the world entity */ self->monsterinfo.pausetime = level.time + 100000000; self->monsterinfo.stand(self); } return true; } } /* wake up other monsters */ self->show_hostile = level.time + 1; /* check knowledge of enemy */ enemy_vis = visible(self, self->enemy); if (enemy_vis) { self->monsterinfo.search_time = level.time + 5; VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); } if (coop && (self->monsterinfo.search_time < level.time)) { if (FindTarget(self)) { return true; } } enemy_infront = infront(self, self->enemy); enemy_range = range(self, self->enemy); VectorSubtract(self->enemy->s.origin, self->s.origin, temp); enemy_yaw = vectoyaw(temp); if (self->monsterinfo.attack_state == AS_MISSILE) { ai_run_missile(self); return true; } if (self->monsterinfo.attack_state == AS_MELEE) { ai_run_melee(self); return true; } /* if enemy is not currently visible, we will never attack */ if (!enemy_vis) { return false; } return self->monsterinfo.checkattack(self); } /* * The monster has an enemy * it is trying to kill */ void ai_run(edict_t *self, float dist) { vec3_t v; edict_t *tempgoal; edict_t *save; qboolean new; edict_t *marker; float d1, d2; trace_t tr; vec3_t v_forward, v_right; float left, center, right; vec3_t left_target, right_target; if (!self) { return; } /* if we're going to a combat point, just proceed */ if (self->monsterinfo.aiflags & AI_COMBAT_POINT) { M_MoveToGoal(self, dist); return; } if (self->monsterinfo.aiflags & AI_SOUND_TARGET) { VectorSubtract(self->s.origin, self->enemy->s.origin, v); if (VectorLength(v) < 64) { self->monsterinfo.aiflags |= (AI_STAND_GROUND | AI_TEMP_STAND_GROUND); self->monsterinfo.stand(self); return; } M_MoveToGoal(self, dist); if (!FindTarget(self)) { return; } } if (ai_checkattack(self)) { return; } if (self->monsterinfo.attack_state == AS_SLIDING) { ai_run_slide(self, dist); return; } if (enemy_vis) { M_MoveToGoal(self, dist); self->monsterinfo.aiflags &= ~AI_LOST_SIGHT; VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); self->monsterinfo.trail_time = level.time; return; } if ((self->monsterinfo.search_time) && (level.time > (self->monsterinfo.search_time + 20))) { M_MoveToGoal(self, dist); self->monsterinfo.search_time = 0; return; } save = self->goalentity; tempgoal = G_Spawn(); self->goalentity = tempgoal; new = false; if (!(self->monsterinfo.aiflags & AI_LOST_SIGHT)) { /* just lost sight of the player, decide where to go first */ self->monsterinfo.aiflags |= (AI_LOST_SIGHT | AI_PURSUIT_LAST_SEEN); self->monsterinfo.aiflags &= ~(AI_PURSUE_NEXT | AI_PURSUE_TEMP); new = true; } if (self->monsterinfo.aiflags & AI_PURSUE_NEXT) { self->monsterinfo.aiflags &= ~AI_PURSUE_NEXT; /* give ourself more time since we got this far */ self->monsterinfo.search_time = level.time + 5; if (self->monsterinfo.aiflags & AI_PURSUE_TEMP) { self->monsterinfo.aiflags &= ~AI_PURSUE_TEMP; marker = NULL; VectorCopy(self->monsterinfo.saved_goal, self->monsterinfo.last_sighting); new = true; } else if (self->monsterinfo.aiflags & AI_PURSUIT_LAST_SEEN) { self->monsterinfo.aiflags &= ~AI_PURSUIT_LAST_SEEN; marker = PlayerTrail_PickFirst(self); } else { marker = PlayerTrail_PickNext(self); } if (marker) { VectorCopy(marker->s.origin, self->monsterinfo.last_sighting); self->monsterinfo.trail_time = marker->timestamp; self->s.angles[YAW] = self->ideal_yaw = marker->s.angles[YAW]; new = true; } } VectorSubtract(self->s.origin, self->monsterinfo.last_sighting, v); d1 = VectorLength(v); if (d1 <= dist) { self->monsterinfo.aiflags |= AI_PURSUE_NEXT; dist = d1; } VectorCopy(self->monsterinfo.last_sighting, self->goalentity->s.origin); if (new) { tr = gi.trace(self->s.origin, self->mins, self->maxs, self->monsterinfo.last_sighting, self, MASK_PLAYERSOLID); if (tr.fraction < 1) { VectorSubtract(self->goalentity->s.origin, self->s.origin, v); d1 = VectorLength(v); center = tr.fraction; d2 = d1 * ((center + 1) / 2); self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v); AngleVectors(self->s.angles, v_forward, v_right, NULL); VectorSet(v, d2, -16, 0); G_ProjectSource(self->s.origin, v, v_forward, v_right, left_target); tr = gi.trace(self->s.origin, self->mins, self->maxs, left_target, self, MASK_PLAYERSOLID); left = tr.fraction; VectorSet(v, d2, 16, 0); G_ProjectSource(self->s.origin, v, v_forward, v_right, right_target); tr = gi.trace(self->s.origin, self->mins, self->maxs, right_target, self, MASK_PLAYERSOLID); right = tr.fraction; center = (d1 * center) / d2; if ((left >= center) && (left > right)) { if (left < 1) { VectorSet(v, d2 * left * 0.5, -16, 0); G_ProjectSource(self->s.origin, v, v_forward, v_right, left_target); } VectorCopy(self->monsterinfo.last_sighting, self->monsterinfo.saved_goal); self->monsterinfo.aiflags |= AI_PURSUE_TEMP; VectorCopy(left_target, self->goalentity->s.origin); VectorCopy(left_target, self->monsterinfo.last_sighting); VectorSubtract(self->goalentity->s.origin, self->s.origin, v); self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v); } else if ((right >= center) && (right > left)) { if (right < 1) { VectorSet(v, d2 * right * 0.5, 16, 0); G_ProjectSource(self->s.origin, v, v_forward, v_right, right_target); } VectorCopy(self->monsterinfo.last_sighting, self->monsterinfo.saved_goal); self->monsterinfo.aiflags |= AI_PURSUE_TEMP; VectorCopy(right_target, self->goalentity->s.origin); VectorCopy(right_target, self->monsterinfo.last_sighting); VectorSubtract(self->goalentity->s.origin, self->s.origin, v); self->s.angles[YAW] = self->ideal_yaw = vectoyaw(v); } } } M_MoveToGoal(self, dist); G_FreeEdict(tempgoal); if (self) { self->goalentity = save; } } yquake2-QUAKE2_5_32/src/backends/0000755000175000017500000000000012615162034017160 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/backends/unix/0000755000175000017500000000000012615162034020143 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/backends/unix/signalhandler.c0000644000175000017500000000744112615162034023130 0ustar greffrathgreffrath/* * Copyright (C) 2011 Yamagi Burmeister * * This program is free software; you can 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 is a signal handler for printing some hints to debug problem in * the case of a crash. On Linux a backtrace is printed. Additionally * a special handler for SIGINT and SIGTERM ist supplied. * * ======================================================================= */ #include #ifdef __linux__ #include #endif #include "../../common/header/common.h" #ifdef __linux__ void printBacktrace(int sig) { void *array[15]; size_t size; char **strings; int i; size = backtrace(array, 15); strings = backtrace_symbols(array, size); printf("Product: Yamagi Quake II\n"); printf("Version: %s\n", YQ2VERSION); printf("Plattform: %s\n", BUILDSTRING); printf("Architecture: %s\n", CPUSTRING); printf("Compiler: %s\n", __VERSION__); printf("Signal: %i\n", sig); printf("\nBacktrace:\n"); for (i = 0; i < size; i++) { printf(" %s\n", strings[i]); } printf("\n"); } #else void printBacktrace(int sig) { printf("Product: Yamagi Quake II\n"); printf("Version: %s\n", YQ2VERSION); printf("Plattform: %s\n", BUILDSTRING); printf("Architecture: %s\n", CPUSTRING); printf("Compiler: %s\n", __VERSION__); printf("Signal: %i\n", sig); printf("\nBacktrace:\n"); printf(" Not available on this plattform.\n\n"); } #endif void signalhandler(int sig) { printf("\n=======================================================\n"); printf("\nYamagi Quake II crashed! This should not happen...\n"); printf("\nMake sure that you're using the last version. It can\n"); printf("be found at http://www.yamagi.org/quake2. If you do,\n"); printf("send a bug report to quake2@yamagi.org and include:\n\n"); printf(" - This output\n"); printf(" - The conditions that triggered the crash\n"); printf(" - How to reproduce the crash (if known)\n"); printf(" - The following files. None of them contains private\n"); printf(" data. They're necessary to analyze the backtrace:\n\n"); printf(" - quake2 (the executable / binary)\n\n"); printf(" - game.so (the game.so of the mod you were playing\n"); printf(" when the game crashed. baseq2/game.so for the\n"); printf(" main game)\n\n"); printf(" - Any other data which you think might be usefull\n"); printf("\nThank you very much for your help, making Yamagi Quake\n"); printf("II an even better source port. It's much appreciated.\n"); printf("\n=======================================================\n\n"); printBacktrace(sig); /* make sure this is written */ fflush(stdout); /* reset signalhandler */ signal(SIGSEGV, SIG_DFL); signal(SIGILL, SIG_DFL); signal(SIGFPE, SIG_DFL); signal(SIGABRT, SIG_DFL); /* pass signal to the os */ raise(sig); } void terminate(int sig) { Cbuf_AddText("quit"); } void registerHandler(void) { /* Crash */ signal(SIGSEGV, signalhandler); signal(SIGILL, signalhandler); signal(SIGFPE, signalhandler); signal(SIGABRT, signalhandler); /* User abort */ signal(SIGINT, terminate); signal(SIGTERM, terminate); } yquake2-QUAKE2_5_32/src/backends/unix/system.c0000644000175000017500000002023712615162034021637 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements all system dependend generic funktions * * ======================================================================= */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../common/header/common.h" #include "../../common/header/glob.h" #include "../generic/header/input.h" #include "header/unix.h" unsigned sys_frame_time; int curtime; static void *game_library; static char findbase[MAX_OSPATH]; static char findpath[MAX_OSPATH]; static char findpattern[MAX_OSPATH]; static DIR *fdir; qboolean stdin_active = true; extern FILE *logfile; static qboolean CompareAttributes(char *path, char *name, unsigned musthave, unsigned canthave) { /* . and .. never match */ if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) { return false; } return true; } void Sys_Init(void) { } int Sys_Milliseconds(void) { struct timeval tp; struct timezone tzp; static int secbase; gettimeofday(&tp, &tzp); if (!secbase) { secbase = tp.tv_sec; return tp.tv_usec / 1000; } curtime = (tp.tv_sec - secbase) * 1000 + tp.tv_usec / 1000; return curtime; } void Sys_Mkdir(char *path) { mkdir(path, 0755); } char * Sys_GetCurrentDirectory(void) { static char dir[MAX_OSPATH]; if (!getcwd(dir, sizeof(dir))) { Sys_Error("Couldn't get current working directory"); } return dir; } char * Sys_FindFirst(char *path, unsigned musthave, unsigned canhave) { struct dirent *d; char *p; if (fdir) { Sys_Error("Sys_BeginFind without close"); } strcpy(findbase, path); if ((p = strrchr(findbase, '/')) != NULL) { *p = 0; strcpy(findpattern, p + 1); } else { strcpy(findpattern, "*"); } if (strcmp(findpattern, "*.*") == 0) { strcpy(findpattern, "*"); } if ((fdir = opendir(findbase)) == NULL) { return NULL; } while ((d = readdir(fdir)) != NULL) { if (!*findpattern || glob_match(findpattern, d->d_name)) { if (CompareAttributes(findbase, d->d_name, musthave, canhave)) { sprintf(findpath, "%s/%s", findbase, d->d_name); return findpath; } } } return NULL; } char * Sys_FindNext(unsigned musthave, unsigned canhave) { struct dirent *d; if (fdir == NULL) { return NULL; } while ((d = readdir(fdir)) != NULL) { if (!*findpattern || glob_match(findpattern, d->d_name)) { if (CompareAttributes(findbase, d->d_name, musthave, canhave)) { sprintf(findpath, "%s/%s", findbase, d->d_name); return findpath; } } } return NULL; } void Sys_FindClose(void) { if (fdir != NULL) { closedir(fdir); } fdir = NULL; } void Sys_ConsoleOutput(char *string) { fputs(string, stdout); } void Sys_Printf(char *fmt, ...) { va_list argptr; char text[1024]; unsigned char *p; va_start(argptr, fmt); vsnprintf(text, 1024, fmt, argptr); va_end(argptr); for (p = (unsigned char *)text; *p; p++) { *p &= 0x7f; if (((*p > 128) || (*p < 32)) && (*p != 10) && (*p != 13) && (*p != 9)) { printf("[%02x]", *p); } else { putc(*p, stdout); } } } void Sys_Quit(void) { #ifndef DEDICATED_ONLY CL_Shutdown(); #endif if (logfile) { fclose(logfile); logfile = NULL; } Qcommon_Shutdown(); fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~FNDELAY); printf("------------------------------------\n"); exit(0); } void Sys_Error(char *error, ...) { va_list argptr; char string[1024]; /* change stdin to non blocking */ fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~FNDELAY); #ifndef DEDICATED_ONLY CL_Shutdown(); #endif Qcommon_Shutdown(); va_start(argptr, error); vsnprintf(string, 1024, error, argptr); va_end(argptr); fprintf(stderr, "Error: %s\n", string); exit(1); } /* * returns -1 if not present */ int Sys_FileTime(char *path) { struct stat buf; if (stat(path, &buf) == -1) { return -1; } return buf.st_mtime; } void floating_point_exception_handler(int whatever) { signal(SIGFPE, floating_point_exception_handler); } char * Sys_ConsoleInput(void) { static char text[256]; int len; fd_set fdset; struct timeval timeout; if (!dedicated || !dedicated->value) { return NULL; } if (!stdin_active) { return NULL; } FD_ZERO(&fdset); FD_SET(0, &fdset); /* stdin */ timeout.tv_sec = 0; timeout.tv_usec = 0; if ((select(1, &fdset, NULL, NULL, &timeout) == -1) || !FD_ISSET(0, &fdset)) { return NULL; } len = read(0, text, sizeof(text)); if (len == 0) /* eof! */ { stdin_active = false; return NULL; } if (len < 1) { return NULL; } text[len - 1] = 0; /* rip off the /n and terminate */ return text; } void Sys_UnloadGame(void) { if (game_library) { dlclose(game_library); } game_library = NULL; } /* * Loads the game dll */ void * Sys_GetGameAPI(void *parms) { void *(*GetGameAPI)(void *); FILE *fp; char name[MAX_OSPATH]; char *path; char *str_p; #ifdef __APPLE__ const char *gamename = "game.dylib"; #else const char *gamename = "game.so"; #endif setreuid(getuid(), getuid()); setegid(getgid()); if (game_library) { Com_Error(ERR_FATAL, "Sys_GetGameAPI without Sys_UnloadingGame"); } Com_Printf("LoadLibrary(\"%s\")\n", gamename); /* now run through the search paths */ path = NULL; while (1) { path = FS_NextPath(path); if (!path) { return NULL; /* couldn't find one anywhere */ } snprintf(name, MAX_OSPATH, "%s/%s", path, gamename); /* skip it if it just doesn't exist */ fp = fopen(name, "rb"); if (fp == NULL) { continue; } fclose(fp); game_library = dlopen(name, RTLD_NOW); if (game_library) { Com_MDPrintf("LoadLibrary (%s)\n", name); break; } else { Com_Printf("LoadLibrary (%s):", name); path = (char *)dlerror(); str_p = strchr(path, ':'); /* skip the path (already shown) */ if (str_p == NULL) { str_p = path; } else { str_p++; } Com_Printf("%s\n", str_p); return NULL; } } GetGameAPI = (void *)dlsym(game_library, "GetGameAPI"); if (!GetGameAPI) { Sys_UnloadGame(); return NULL; } return GetGameAPI(parms); } void Sys_SendKeyEvents(void) { #ifndef DEDICATED_ONLY IN_Update(); #endif /* grab frame time */ sys_frame_time = Sys_Milliseconds(); } char * Sys_GetHomeDir(void) { static char gdir[MAX_OSPATH]; char *home; home = getenv("HOME"); if (!home) { return NULL; } snprintf(gdir, sizeof(gdir), "%s/%s/", home, CFGDIR); return gdir; } void * Sys_GetProcAddress(void *handle, const char *sym) { return dlsym(handle, sym); } void * Sys_LoadLibrary(const char *path, const char *sym, void **handle) { void *module, *entry; *handle = NULL; module = dlopen(path, RTLD_LAZY); if (!module) { Com_Printf("%s failed: %s\n", __func__, dlerror()); return NULL; } if (sym) { entry = dlsym(module, sym); if (!entry) { Com_Printf("%s failed: %s\n", __func__, dlerror()); dlclose(module); return NULL; } } else { entry = NULL; } Com_DPrintf("%s succeeded: %s\n", __func__, path); *handle = module; return entry; } void Sys_FreeLibrary(void *handle) { if (handle && dlclose(handle)) { Com_Error(ERR_FATAL, "dlclose failed on %p: %s", handle, dlerror()); } } yquake2-QUAKE2_5_32/src/backends/unix/shared/0000755000175000017500000000000012615162034021411 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/backends/unix/shared/hunk.c0000644000175000017500000000736212615162034022532 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements the low level part of the Hunk_* memory system * * ======================================================================= */ /* For mremap() - must be before sys/mman.h include! */ #if defined(__linux__) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif #include #include #include #include #include "../../../common/header/common.h" #if defined(__FreeBSD__) || defined(__OpenBSD__) #include #define MAP_ANONYMOUS MAP_ANON #endif #if defined(__APPLE__) #include #define MAP_ANONYMOUS MAP_ANON #endif byte *membase; int maxhunksize; int curhunksize; void * Hunk_Begin(int maxsize) { /* reserve a huge chunk of memory, but don't commit any yet */ maxhunksize = maxsize + sizeof(int); curhunksize = 0; membase = mmap(0, maxhunksize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if ((membase == NULL) || (membase == (byte *)-1)) { Sys_Error("unable to virtual allocate %d bytes", maxsize); } *((int *)membase) = curhunksize; return membase + sizeof(int); } void * Hunk_Alloc(int size) { byte *buf; /* round to cacheline */ size = (size + 31) & ~31; if (curhunksize + size > maxhunksize) { Sys_Error("Hunk_Alloc overflow"); } buf = membase + sizeof(int) + curhunksize; curhunksize += size; return buf; } int Hunk_End(void) { byte *n = NULL; #if defined(__linux__) n = (byte *)mremap(membase, maxhunksize, curhunksize + sizeof(int), 0); #elif defined(__FreeBSD__) size_t old_size = maxhunksize; size_t new_size = curhunksize + sizeof(int); void *unmap_base; size_t unmap_len; new_size = round_page(new_size); old_size = round_page(old_size); if (new_size > old_size) { n = 0; /* error */ } else if (new_size < old_size) { unmap_base = (caddr_t)(membase + new_size); unmap_len = old_size - new_size; n = munmap(unmap_base, unmap_len) + membase; } #else #ifndef round_page #define round_page(x) (((size_t)(x) + (page_size - 1)) / page_size) * \ page_size #endif size_t old_size = maxhunksize; size_t new_size = curhunksize + sizeof(int); void *unmap_base; size_t unmap_len; long page_size; page_size = sysconf(_SC_PAGESIZE); if (page_size == -1) { Sys_Error("Hunk_End: sysconf _SC_PAGESIZE failed (%d)", errno); } new_size = round_page(new_size); old_size = round_page(old_size); if (new_size > old_size) { n = 0; /* error */ } else if (new_size < old_size) { unmap_base = (caddr_t)(membase + new_size); unmap_len = old_size - new_size; n = munmap(unmap_base, unmap_len) + membase; } #endif if (n != membase) { Sys_Error("Hunk_End: Could not remap virtual block (%d)", errno); } *((int *)membase) = curhunksize + sizeof(int); return curhunksize; } void Hunk_Free(void *base) { byte *m; if (base) { m = ((byte *)base) - sizeof(int); if (munmap(m, *((int *)m))) { Sys_Error("Hunk_Free: munmap failed (%d)", errno); } } } yquake2-QUAKE2_5_32/src/backends/unix/header/0000755000175000017500000000000012615162034021373 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/backends/unix/header/unix.h0000644000175000017500000000205712615162034022533 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * A header file for some of the plattform dependend stuff. * * ======================================================================= */ #ifndef UNIX_UNIX_H #define UNIX_UNIX_H void registerHandler(void); #endif yquake2-QUAKE2_5_32/src/backends/unix/main.c0000644000175000017500000000636212615162034021242 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 the starting point of the program and implements * the main loop * * ======================================================================= */ #include #include #include #include #include #include "../../common/header/common.h" #include "header/unix.h" int main(int argc, char **argv) { int time, oldtime, newtime; /* register signal handler */ registerHandler(); /* Prevent running Quake II as root. Only very mad minded or stupid people even think about it. :) */ if (getuid() == 0) { printf("Quake II shouldn't be run as root! Backing out to save your ass. If\n"); printf("you really know what you're doing, edit src/unix/main.c and remove\n"); printf("this check. But don't complain if Quake II eats your dog afterwards!\n"); return 1; } /* Enforce the real UID to prevent setuid crap */ if (getuid() != geteuid()) { printf("The effective UID is not the real UID! Your binary is probably marked\n"); printf("'setuid'. That is not good idea, please fix it :) If you really know\n"); printf("what you're doin edit src/unix/main.c and remove this check. Don't\n"); printf("complain if Quake II eats your dog afterwards!\n"); return 1; } /* enforce C locale */ setenv("LC_ALL", "C", 1); printf("\nYamagi Quake II v%s\n", YQ2VERSION); printf("=====================\n\n"); #ifndef DEDICATED_ONLY printf("Client build options:\n"); #ifdef SDL2 printf(" + SDL2\n"); #else printf(" - SDL2 (using 1.2)\n"); #endif #ifdef CDA printf(" + CD audio\n"); #else printf(" - CD audio\n"); #endif #ifdef OGG printf(" + OGG/Vorbis\n"); #else printf(" - OGG/Vorbis\n"); #endif #ifdef USE_OPENAL printf(" + OpenAL audio\n"); #else printf(" - OpenAL audio\n"); #endif #ifdef ZIP printf(" + Zip file support\n"); #else printf(" - Zip file support\n"); #endif #endif printf("Platform: %s\n", BUILDSTRING); printf("Architecture: %s\n", CPUSTRING); /* Seed PRNG */ randk_seed(); /* Initialze the game */ Qcommon_Init(argc, argv); /* Do not delay reads on stdin*/ fcntl(fileno(stdin), F_SETFL, fcntl(fileno(stdin), F_GETFL, NULL) | FNDELAY); oldtime = Sys_Milliseconds(); /* The legendary Quake II mainloop */ while (1) { /* find time spent rendering last frame */ do { newtime = Sys_Milliseconds(); time = newtime - oldtime; } while (time < 1); Qcommon_Frame(time); oldtime = newtime; } return 0; } yquake2-QUAKE2_5_32/src/backends/unix/network.c0000644000175000017500000004640612615162034022012 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Low level network code, based upon the BSD socket api. * * ======================================================================= */ #include "../../common/header/common.h" #include #include #include #include #include #include #include #include #include #include #include netadr_t net_local_adr; #define LOOPBACK 0x7f000001 #define MAX_LOOPBACK 4 #define QUAKE2MCAST "ff12::666" typedef struct { byte data[MAX_MSGLEN]; int datalen; } loopmsg_t; typedef struct { loopmsg_t msgs[MAX_LOOPBACK]; int get, send; } loopback_t; loopback_t loopbacks[2]; int ip_sockets[2]; int ip6_sockets[2]; int ipx_sockets[2]; char *multicast_interface = NULL; int NET_Socket(char *net_interface, int port, netsrc_t type, int family); char *NET_ErrorString(void); void NetadrToSockadr(netadr_t *a, struct sockaddr_storage *s) { struct sockaddr_in6 *s6; memset(s, 0, sizeof(*s)); switch (a->type) { case NA_BROADCAST: ((struct sockaddr_in *)s)->sin_family = AF_INET; ((struct sockaddr_in *)s)->sin_port = a->port; ((struct sockaddr_in *)s)->sin_addr.s_addr = (in_addr_t)INADDR_BROADCAST; break; case NA_IP: ((struct sockaddr_in *)s)->sin_family = AF_INET; *(int *)&((struct sockaddr_in *)s)->sin_addr = *(int *)&a->ip; ((struct sockaddr_in *)s)->sin_port = a->port; break; case NA_MULTICAST6: s6 = (struct sockaddr_in6 *)s; if (inet_pton(AF_INET6, QUAKE2MCAST, &s6->sin6_addr.s6_addr) != 1) { Com_Printf("NET_NetadrToSockadr: inet_pton: %s\n", strerror(errno)); return; } s6->sin6_family = AF_INET6; s6->sin6_port = a->port; #ifdef SIN6_LEN s6->sin6_len = sizeof(struct sockaddr_in6); #endif /* scope_id is important for * link-local destination.*/ s6->sin6_scope_id = a->scope_id; break; case NA_IP6: if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)a->ip)) { #ifdef SIN6_LEN s->ss_len = sizeof(struct sockaddr_in); #endif s->ss_family = AF_INET; memcpy(&((struct sockaddr_in *)s)->sin_addr, &((struct in6_addr *)a->ip)->s6_addr[12], sizeof(struct in_addr)); ((struct sockaddr_in *)s)->sin_port = a->port; } else { s6 = (struct sockaddr_in6 *)s; s6->sin6_family = AF_INET6; memcpy(&s6->sin6_addr, a->ip, sizeof(s6->sin6_addr)); s6->sin6_port = a->port; #ifdef SIN6_LEN s6->sin6_len = sizeof(struct sockaddr_in6); #endif /* scope_id is important for * link-local destination. */ s6->sin6_scope_id = a->scope_id; } break; case NA_LOOPBACK: case NA_IPX: case NA_BROADCAST_IPX: break; } } void SockadrToNetadr(struct sockaddr_storage *s, netadr_t *a) { struct sockaddr_in6 *s6; if (s->ss_family == AF_INET) { *(int *) &a->ip = *(int *)&((struct sockaddr_in *)s)->sin_addr; a->port = ((struct sockaddr_in *)s)->sin_port; a->type = NA_IP; } else if (s->ss_family == AF_INET6) { s6 = (struct sockaddr_in6 *)s; if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)&s6->sin6_addr)) { memcpy(a->ip, (struct in_addr *)&s6->sin6_addr.s6_addr[12], sizeof(struct in_addr)); a->port = ((struct sockaddr_in *)s)->sin_port; a->type = NA_IP; } else { memcpy(a->ip, &s6->sin6_addr, sizeof(a->ip)); a->port = s6->sin6_port; a->type = NA_IP6; a->scope_id = s6->sin6_scope_id; } } else { s = NULL; } } void NET_Init() { } qboolean NET_CompareAdr(netadr_t a, netadr_t b) { if (a.type != b.type) { return false; } if (a.type == NA_LOOPBACK) { return true; } if (a.type == NA_IP) { if ((a.ip[0] == b.ip[0]) && (a.ip[1] == b.ip[1]) && (a.ip[2] == b.ip[2]) && (a.ip[3] == b.ip[3]) && (a.port == b.port)) { return true; } } if (a.type == NA_IP6) { if ((memcmp(a.ip, b.ip, 16) == 0) && (a.port == b.port)) { return true; } } return false; } /* * Compares without the port */ qboolean NET_CompareBaseAdr(netadr_t a, netadr_t b) { if (a.type != b.type) { return false; } if (a.type == NA_LOOPBACK) { return true; } if (a.type == NA_IP) { if ((a.ip[0] == b.ip[0]) && (a.ip[1] == b.ip[1]) && (a.ip[2] == b.ip[2]) && (a.ip[3] == b.ip[3])) { return true; } return false; } if (a.type == NA_IP6) { if ((memcmp(a.ip, b.ip, 16) == 0)) { return true; } return false; } if (a.type == NA_IPX) { if ((memcmp(a.ipx, b.ipx, 10) == 0)) { return true; } return false; } return false; } char * NET_BaseAdrToString(netadr_t a) { static char s[64], tmp[64]; struct sockaddr_storage ss; struct sockaddr_in6 *s6; switch (a.type) { case NA_IP: case NA_LOOPBACK: Com_sprintf(s, sizeof(s), "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]); break; case NA_BROADCAST: Com_sprintf(s, sizeof(s), "255.255.255.255"); break; case NA_IP6: case NA_MULTICAST6: memset(&ss, 0, sizeof(ss)); s6 = (struct sockaddr_in6 *)&ss; if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)a.ip)) { #ifdef SIN6_LEN ss.ss_len = sizeof(struct sockaddr_in); #endif ss.ss_family = AF_INET; memcpy(&((struct sockaddr_in *)&ss)->sin_addr, &((struct in6_addr *)a.ip)->s6_addr[12], sizeof(struct in_addr)); } else { #ifdef SIN6_LEN s6->sin6_len = sizeof(struct sockaddr_in6); #endif s6->sin6_scope_id = a.scope_id; s6->sin6_family = AF_INET6; memcpy(&s6->sin6_addr, a.ip, sizeof(struct in6_addr)); } #ifdef SIN6_LEN socklen_t const salen = ss.ss_len; #else socklen_t const salen = sizeof(ss); #endif if (getnameinfo((struct sockaddr *)&ss, salen, s, sizeof(s), NULL, 0, NI_NUMERICHOST)) { Com_sprintf(s, sizeof(s), ""); } else { if ((a.type == NA_MULTICAST6) || IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&ss)-> sin6_addr)) { /* If the address is multicast (link) or a link-local, need to carry the scope. The string format of the IPv6 address is used by the client to extablish the connect to the server. */ Com_sprintf(tmp, sizeof(tmp), "%s%%%d", s, s6->sin6_scope_id); memcpy(s, tmp, sizeof(s)); } } break; default: Com_sprintf(s, sizeof(s), "invalid IP address family type"); break; } return s; } char * NET_AdrToString(netadr_t a) { static char s[64]; const char *base; base = NET_BaseAdrToString(a); Com_sprintf(s, sizeof(s), "[%s]:%d", base, ntohs(a.port)); return s; } qboolean NET_StringToSockaddr(char *s, struct sockaddr_storage *sadr) { char copy[128]; char *addrs, *space; char *ports = NULL; int err; struct addrinfo hints; struct addrinfo *resultp; memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_DGRAM; hints.ai_family = PF_UNSPEC; strcpy(copy, s); addrs = space = copy; if (*addrs == '[') { addrs++; for ( ; *space && *space != ']'; space++) { } if (!*space) { Com_Printf("NET_StringToSockaddr: invalid IPv6 address %s\n", s); return 0; } *space++ = '\0'; } for ( ; *space; space++) { if (*space == ':') { *space = '\0'; ports = space + 1; } } if ((err = getaddrinfo(addrs, ports, &hints, &resultp))) { /* Error */ Com_Printf("NET_StringToSockaddr: string %s:\n%s\n", s, gai_strerror(err)); return 0; } switch (resultp->ai_family) { case AF_INET: /* convert to ipv4 addr */ memset(sadr, 0, sizeof(struct sockaddr_storage)); memcpy(sadr, resultp->ai_addr, resultp->ai_addrlen); break; case AF_INET6: /* convert to ipv6 addr */ memset(sadr, 0, sizeof(struct sockaddr_storage)); memcpy(sadr, resultp->ai_addr, resultp->ai_addrlen); break; default: Com_Printf("NET_StringToSockaddr: string %s:\nprotocol family %d not supported\n", s, resultp->ai_family); return 0; } return true; } qboolean NET_StringToAdr(char *s, netadr_t *a) { struct sockaddr_storage sadr; memset(a, 0, sizeof(*a)); if (!strcmp(s, "localhost")) { a->type = NA_LOOPBACK; return true; } if (!NET_StringToSockaddr(s, &sadr)) { return false; } SockadrToNetadr(&sadr, a); return true; } qboolean NET_IsLocalAddress(netadr_t adr) { return NET_CompareAdr(adr, net_local_adr); } qboolean NET_GetLoopPacket(netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message) { int i; loopback_t *loop; loop = &loopbacks[sock]; if (loop->send - loop->get > MAX_LOOPBACK) { loop->get = loop->send - MAX_LOOPBACK; } if (loop->get >= loop->send) { return false; } i = loop->get & (MAX_LOOPBACK - 1); loop->get++; memcpy(net_message->data, loop->msgs[i].data, loop->msgs[i].datalen); net_message->cursize = loop->msgs[i].datalen; *net_from = net_local_adr; return true; } void NET_SendLoopPacket(netsrc_t sock, int length, void *data, netadr_t to) { int i; loopback_t *loop; loop = &loopbacks[sock ^ 1]; i = loop->send & (MAX_LOOPBACK - 1); loop->send++; memcpy(loop->msgs[i].data, data, length); loop->msgs[i].datalen = length; } qboolean NET_GetPacket(netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message) { int ret; struct sockaddr_storage from; socklen_t fromlen; int net_socket; int protocol; int err; if (NET_GetLoopPacket(sock, net_from, net_message)) { return true; } for (protocol = 0; protocol < 3; protocol++) { if (protocol == 0) { net_socket = ip_sockets[sock]; } else if (protocol == 1) { net_socket = ip6_sockets[sock]; } else { net_socket = ipx_sockets[sock]; } if (!net_socket) { continue; } fromlen = sizeof(from); ret = recvfrom(net_socket, net_message->data, net_message->maxsize, 0, (struct sockaddr *)&from, &fromlen); SockadrToNetadr(&from, net_from); if (ret == -1) { err = errno; if ((err == EWOULDBLOCK) || (err == ECONNREFUSED)) { continue; } Com_Printf("NET_GetPacket: %s from %s\n", NET_ErrorString(), NET_AdrToString(*net_from)); continue; } if (ret == net_message->maxsize) { Com_Printf("Oversize packet from %s\n", NET_AdrToString(*net_from)); continue; } net_message->cursize = ret; return true; } return false; } void NET_SendPacket(netsrc_t sock, int length, void *data, netadr_t to) { int ret; struct sockaddr_storage addr; int net_socket; int addr_size = sizeof(struct sockaddr_in); switch (to.type) { case NA_LOOPBACK: NET_SendLoopPacket(sock, length, data, to); return; break; case NA_BROADCAST: case NA_IP: net_socket = ip_sockets[sock]; if (!net_socket) { return; } break; case NA_IP6: case NA_MULTICAST6: net_socket = ip6_sockets[sock]; addr_size = sizeof(struct sockaddr_in6); if (!net_socket) { return; } break; case NA_IPX: case NA_BROADCAST_IPX: net_socket = ipx_sockets[sock]; if (!net_socket) { return; } break; default: Com_Error(ERR_FATAL, "NET_SendPacket: bad address type"); return; break; } NetadrToSockadr(&to, &addr); /* Re-check the address family. If to.type is NA_IP6 but contains an IPv4 mapped address, NetadrToSockadr will return an AF_INET struct. If so, switch back to AF_INET socket.*/ if ((to.type == NA_IP6) && (addr.ss_family == AF_INET)) { net_socket = ip_sockets[sock]; addr_size = sizeof(struct sockaddr_in); if (!net_socket) { return; } } if (addr.ss_family == AF_INET6) { struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&addr; /* If multicast socket, must specify scope. So multicast_interface must be specified */ if (IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) { struct addrinfo hints; struct addrinfo *res; char tmp[128], mcast_addr[128], mcast_port[10]; int error; if (multicast_interface != NULL) { /* Do a getnameinfo/getaddrinfo cycle to calculate the scope_id of the multicast address. getaddrinfo is passed a multicast address of the form ff0x::xxx%multicast_interface */ #ifdef SIN6_LEN error = getnameinfo((struct sockaddr *)s6, s6->sin6_len, tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST); #else error = getnameinfo((struct sockaddr *)s6, sizeof(struct sockaddr_in6), tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST); #endif if (error) { Com_Printf("NET_SendPacket: getnameinfo: %s\n", gai_strerror(error)); return; } Com_sprintf(mcast_addr, sizeof(mcast_addr), "%s%%%s", tmp, multicast_interface); Com_sprintf(mcast_port, sizeof(mcast_port), "%d", ntohs(s6->sin6_port)); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_NUMERICHOST; error = getaddrinfo(mcast_addr, mcast_port, &hints, &res); if (error) { Com_Printf("NET_SendPacket: getaddrinfo: %s\n", gai_strerror(error)); return; } /* sockaddr_in6 should now have a valid scope_id. */ memcpy(s6, res->ai_addr, res->ai_addrlen); } else { Com_Printf("NET_SendPacket: IPv6 multicast destination but +set multicast not specified: %s\n", inet_ntop(AF_INET6, &s6->sin6_addr, tmp, sizeof(tmp))); return; } } } ret = sendto(net_socket, data, length, 0, (struct sockaddr *)&addr, addr_size); if (ret == -1) { Com_Printf("NET_SendPacket ERROR: %s to %s\n", NET_ErrorString(), NET_AdrToString(to)); } } void NET_OpenIP(void) { cvar_t *port, *ip; port = Cvar_Get("port", va("%i", PORT_SERVER), CVAR_NOSET); ip = Cvar_Get("ip", "localhost", CVAR_NOSET); if (!ip6_sockets[NS_SERVER]) { ip6_sockets[NS_SERVER] = NET_Socket(ip->string, port->value, NS_SERVER, AF_INET6); } if (!ip6_sockets[NS_CLIENT]) { ip6_sockets[NS_CLIENT] = NET_Socket(ip->string, PORT_ANY, NS_CLIENT, AF_INET6); } if (!ip_sockets[NS_SERVER]) { ip_sockets[NS_SERVER] = NET_Socket(ip->string, port->value, NS_SERVER, AF_INET); } if (!ip_sockets[NS_CLIENT]) { ip_sockets[NS_CLIENT] = NET_Socket(ip->string, PORT_ANY, NS_CLIENT, AF_INET); } } /* * A single player game will only use the loopback code */ void NET_Config(qboolean multiplayer) { int i; if (!multiplayer) { /* shut down any existing sockets */ for (i = 0; i < 2; i++) { if (ip_sockets[i]) { close(ip_sockets[i]); ip_sockets[i] = 0; } if (ip6_sockets[i]) { close(ip6_sockets[i]); ip6_sockets[i] = 0; } if (ipx_sockets[i]) { close(ipx_sockets[i]); ipx_sockets[i] = 0; } } } else { /* open sockets */ NET_OpenIP(); } } /* =================================================================== */ int NET_Socket(char *net_interface, int port, netsrc_t type, int family) { char Buf[BUFSIZ], *Host, *Service; int newsocket, Error; struct sockaddr_storage ss; struct addrinfo hints, *res, *ai; qboolean _true = true; int i = 1; struct ipv6_mreq mreq; cvar_t *mcast; memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = AI_PASSIVE; if (!net_interface || !net_interface[0] || !Q_stricmp(net_interface, "localhost")) { Host = (family == AF_INET6) ? "::" : "0.0.0.0"; } else { Host = net_interface; } if (port == PORT_ANY) { Service = NULL; } else { sprintf(Buf, "%5d", port); Service = Buf; } if ((Error = getaddrinfo(Host, Service, &hints, &res))) { return 0; } for (ai = res; ai != NULL; ai = ai->ai_next) { if ((newsocket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) { Com_Printf("NET_Socket: socket: %s\n", strerror(errno)); continue; } /* make it non-blocking */ if (ioctl(newsocket, FIONBIO, (char *)&_true) == -1) { Com_Printf("NET_Socket: ioctl FIONBIO: %s\n", strerror(errno)); continue; } if (family == AF_INET) { /* make it broadcast capable */ if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) == -1) { Com_Printf("ERROR: NET_Socket: setsockopt SO_BROADCAST:%s\n", NET_ErrorString()); return 0; } } /* make it reusable */ if (setsockopt(newsocket, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i)) == -1) { Com_Printf("ERROR: NET_Socket: setsockopt SO_REUSEADDR:%s\n", NET_ErrorString()); return 0; } if (bind(newsocket, ai->ai_addr, ai->ai_addrlen) < 0) { Com_Printf("NET_Socket: bind: %s\n", strerror(errno)); } else { memcpy(&ss, ai->ai_addr, ai->ai_addrlen); break; } } if (res != NULL) { freeaddrinfo(res); } if (ai == NULL) { return 0; } switch (ss.ss_family) { case AF_INET: break; case AF_INET6: /* Multicast outgoing interface is specified for client and server (+set multicast ) */ mcast = Cvar_Get("multicast", "NULL", CVAR_NOSET); multicast_interface = (strcmp(mcast->string, "NULL") ? mcast->string : NULL); if (multicast_interface != NULL) { /* multicast_interface is a global variable. Also used in NET_SendPacket() */ if ((mreq.ipv6mr_interface = if_nametoindex(multicast_interface)) == 0) { Com_Printf("NET_Socket: invalid interface: %s\n", multicast_interface); } if (setsockopt(newsocket, IPPROTO_IPV6, IPV6_MULTICAST_IF, &mreq.ipv6mr_interface, sizeof(mreq.ipv6mr_interface)) < 0) { Com_Printf("NET_Socket: IPV6_MULTICAST_IF: %s\n", strerror(errno)); } /* Join multicast group ONLY if server */ if (type == NS_SERVER) { if (inet_pton(AF_INET6, QUAKE2MCAST, &mreq.ipv6mr_multiaddr.s6_addr) != 1) { Com_Printf("NET_Socket: inet_pton: %s\n", strerror(errno)); } if (setsockopt(newsocket, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) { Com_Printf("NET_Socket: IPV6_JOIN_GROUP: %s\n", strerror(errno)); } } } break; } return newsocket; } void NET_Shutdown(void) { NET_Config(false); /* close sockets */ } char * NET_ErrorString(void) { int code; code = errno; return strerror(code); } /* * sleeps msec or until net socket is ready */ void NET_Sleep(int msec) { struct timeval timeout; fd_set fdset; extern cvar_t *dedicated; extern qboolean stdin_active; if ((!ip_sockets[NS_SERVER] && !ip6_sockets[NS_SERVER]) || (dedicated && !dedicated->value)) { return; /* we're not a server, just run full speed */ } FD_ZERO(&fdset); if (stdin_active) { FD_SET(0, &fdset); /* stdin is processed too */ } FD_SET(ip_sockets[NS_SERVER], &fdset); /* IPv4 network socket */ FD_SET(ip6_sockets[NS_SERVER], &fdset); /* IPv6 network socket */ timeout.tv_sec = msec / 1000; timeout.tv_usec = (msec % 1000) * 1000; select(MAX(ip_sockets[NS_SERVER], ip6_sockets[NS_SERVER]) + 1, &fdset, NULL, NULL, &timeout); } yquake2-QUAKE2_5_32/src/backends/windows/0000755000175000017500000000000012615162034020652 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/backends/windows/system.c0000644000175000017500000003506412615162034022352 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 the starting point of the program and implements * several support functions and the main loop. * * ======================================================================= */ #include #include #include #include #include #include #include #include #include "../../common/header/common.h" #include "../generic/header/input.h" #include "header/resource.h" #include "header/winquake.h" #define MAX_NUM_ARGVS 128 int curtime; int starttime; qboolean ActiveApp; qboolean Minimized; static HANDLE hinput, houtput; static HANDLE qwclsemaphore; HINSTANCE global_hInstance; static HINSTANCE game_library; unsigned int sys_msg_time; unsigned int sys_frame_time; static char console_text[256]; static int console_textlen; char findbase[MAX_OSPATH]; char findpath[MAX_OSPATH]; int findhandle; int argc; char *argv[MAX_NUM_ARGVS]; /* ================================================================ */ void Sys_Error(char *error, ...) { va_list argptr; char text[1024]; #ifndef DEDICATED_ONLY CL_Shutdown(); #endif Qcommon_Shutdown(); va_start(argptr, error); vsprintf(text, error, argptr); va_end(argptr); #ifndef DEDICATED_ONLY fprintf(stderr, "Error: %s\n", text); #endif MessageBox(NULL, text, "Error", 0 /* MB_OK */); if (qwclsemaphore) { CloseHandle(qwclsemaphore); } /* Close stdout and stderr */ #ifndef DEDICATED_ONLY fclose(stdout); fclose(stderr); #endif exit(1); } void Sys_Quit(void) { timeEndPeriod(1); #ifndef DEDICATED_ONLY CL_Shutdown(); #endif Qcommon_Shutdown(); CloseHandle(qwclsemaphore); if (dedicated && dedicated->value) { FreeConsole(); } /* Close stdout and stderr */ #ifndef DEDICATED_ONLY fclose(stdout); fclose(stderr); #endif exit(0); } void WinError(void) { LPVOID lpMsgBuf; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); /* Display the string. */ MessageBox(NULL, lpMsgBuf, "GetLastError", MB_OK | MB_ICONINFORMATION); /* Free the buffer. */ LocalFree(lpMsgBuf); } /* ================================================================ */ void Sys_Init(void) { OSVERSIONINFO vinfo; timeBeginPeriod(1); vinfo.dwOSVersionInfoSize = sizeof(vinfo); if (!GetVersionEx(&vinfo)) { Sys_Error("Couldn't get OS info"); } /* While Quake II should run on older versions, limit Yamagi Quake II to Windows XP and above. Testing older version would be a PITA. */ if (!((vinfo.dwMajorVersion > 5) || ((vinfo.dwMajorVersion == 5) && (vinfo.dwMinorVersion >= 1)))) { Sys_Error("Yamagi Quake II needs Windows XP or higher!\n"); } if (dedicated->value) { AllocConsole(); hinput = GetStdHandle(STD_INPUT_HANDLE); houtput = GetStdHandle(STD_OUTPUT_HANDLE); } } char * Sys_ConsoleInput(void) { INPUT_RECORD recs[1024]; int ch; DWORD dummy, numread, numevents; if (!dedicated || !dedicated->value) { return NULL; } for ( ; ; ) { if (!GetNumberOfConsoleInputEvents(hinput, &numevents)) { Sys_Error("Error getting # of console events"); } if (numevents <= 0) { break; } if (!ReadConsoleInput(hinput, recs, 1, &numread)) { Sys_Error("Error reading console input"); } if (numread != 1) { Sys_Error("Couldn't read console input"); } if (recs[0].EventType == KEY_EVENT) { if (!recs[0].Event.KeyEvent.bKeyDown) { ch = recs[0].Event.KeyEvent.uChar.AsciiChar; switch (ch) { case '\r': WriteFile(houtput, "\r\n", 2, &dummy, NULL); if (console_textlen) { console_text[console_textlen] = 0; console_textlen = 0; return console_text; } break; case '\b': if (console_textlen) { console_textlen--; WriteFile(houtput, "\b \b", 3, &dummy, NULL); } break; default: if (ch >= ' ') { if (console_textlen < sizeof(console_text) - 2) { WriteFile(houtput, &ch, 1, &dummy, NULL); console_text[console_textlen] = ch; console_textlen++; } } break; } } } } return NULL; } void Sys_ConsoleOutput(char *string) { char text[256]; DWORD dummy; if (!dedicated || !dedicated->value) { fputs(string, stdout); } else { if (console_textlen) { text[0] = '\r'; memset(&text[1], ' ', console_textlen); text[console_textlen + 1] = '\r'; text[console_textlen + 2] = 0; WriteFile(houtput, text, console_textlen + 2, &dummy, NULL); } WriteFile(houtput, string, strlen(string), &dummy, NULL); if (console_textlen) { WriteFile(houtput, console_text, console_textlen, &dummy, NULL); } } } void Sys_SendKeyEvents(void) { #ifndef DEDICATED_ONLY IN_Update(); #endif /* grab frame time */ sys_frame_time = timeGetTime(); } /* ================================================================ */ void Sys_UnloadGame(void) { if (!FreeLibrary(game_library)) { Com_Error(ERR_FATAL, "FreeLibrary failed for game library"); } game_library = NULL; } void * Sys_GetGameAPI(void *parms) { void *(*GetGameAPI)(void *); char name[MAX_OSPATH]; char *path = NULL; if (game_library) { Com_Error(ERR_FATAL, "Sys_GetGameAPI without Sys_UnloadingGame"); } /* now run through the search paths */ path = NULL; while (1) { path = FS_NextPath(path); if (!path) { return NULL; /* couldn't find one anywhere */ } /* Try game.dll */ Com_sprintf(name, sizeof(name), "%s/%s", path, "game.dll"); game_library = LoadLibrary(name); if (game_library) { Com_DPrintf("LoadLibrary (%s)\n", name); break; } /* Try gamex86.dll as fallback */ Com_sprintf(name, sizeof(name), "%s/%s", path, "gamex86.dll"); game_library = LoadLibrary(name); if (game_library) { Com_DPrintf("LoadLibrary (%s)\n", name); break; } } GetGameAPI = (void *)GetProcAddress(game_library, "GetGameAPI"); if (!GetGameAPI) { Sys_UnloadGame(); return NULL; } return GetGameAPI(parms); } /* ======================================================================= */ void ParseCommandLine(LPSTR lpCmdLine) { argc = 1; argv[0] = "exe"; while (*lpCmdLine && (argc < MAX_NUM_ARGVS)) { while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126))) { lpCmdLine++; } if (*lpCmdLine) { argv[argc] = lpCmdLine; argc++; while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126))) { lpCmdLine++; } if (*lpCmdLine) { *lpCmdLine = 0; lpCmdLine++; } } } } /* ======================================================================= */ int Sys_Milliseconds(void) { static int base; static qboolean initialized = false; if (!initialized) { /* let base retain 16 bits of effectively random data */ base = timeGetTime() & 0xffff0000; initialized = true; } curtime = timeGetTime() - base; return curtime; } /* ======================================================================= */ static qboolean CompareAttributes(unsigned found, unsigned musthave, unsigned canthave) { if ((found & _A_RDONLY) && (canthave & SFF_RDONLY)) { return false; } if ((found & _A_HIDDEN) && (canthave & SFF_HIDDEN)) { return false; } if ((found & _A_SYSTEM) && (canthave & SFF_SYSTEM)) { return false; } if ((found & _A_SUBDIR) && (canthave & SFF_SUBDIR)) { return false; } if ((found & _A_ARCH) && (canthave & SFF_ARCH)) { return false; } if ((musthave & SFF_RDONLY) && !(found & _A_RDONLY)) { return false; } if ((musthave & SFF_HIDDEN) && !(found & _A_HIDDEN)) { return false; } if ((musthave & SFF_SYSTEM) && !(found & _A_SYSTEM)) { return false; } if ((musthave & SFF_SUBDIR) && !(found & _A_SUBDIR)) { return false; } if ((musthave & SFF_ARCH) && !(found & _A_ARCH)) { return false; } return true; } char * Sys_FindFirst(char *path, unsigned musthave, unsigned canthave) { struct _finddata_t findinfo; if (findhandle) { Sys_Error("Sys_BeginFind without close"); } findhandle = 0; COM_FilePath(path, findbase); findhandle = _findfirst(path, &findinfo); if (findhandle == -1) { return NULL; } if (!CompareAttributes(findinfo.attrib, musthave, canthave)) { return NULL; } Com_sprintf(findpath, sizeof(findpath), "%s/%s", findbase, findinfo.name); return findpath; } char * Sys_FindNext(unsigned musthave, unsigned canthave) { struct _finddata_t findinfo; if (findhandle == -1) { return NULL; } if (_findnext(findhandle, &findinfo) == -1) { return NULL; } if (!CompareAttributes(findinfo.attrib, musthave, canthave)) { return NULL; } Com_sprintf(findpath, sizeof(findpath), "%s/%s", findbase, findinfo.name); return findpath; } void Sys_FindClose(void) { if (findhandle != -1) { _findclose(findhandle); } findhandle = 0; } void Sys_Mkdir(char *path) { _mkdir(path); } char * Sys_GetCurrentDirectory(void) { static char dir[MAX_OSPATH]; if (!_getcwd(dir, sizeof(dir))) { Sys_Error("Couldn't get current working directory"); } return dir; } char * Sys_GetHomeDir(void) { char *cur; char *old; char profile[MAX_PATH]; int len; static char gdir[MAX_OSPATH]; WCHAR sprofile[MAX_PATH]; WCHAR uprofile[MAX_PATH]; /* The following lines implement a horrible hack to connect the UTF-16 WinAPI to the ASCII Quake II. While this should work in most cases, it'll fail if the "Windows to DOS filename translation" is switched off. In that case the function will return NULL and no homedir is used. */ /* Get the path to "My Documents" directory */ SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, 0, uprofile); /* Create a UTF-16 DOS path */ len = GetShortPathNameW(uprofile, sprofile, sizeof(sprofile)); if (len == 0) { return NULL; } /* Since the DOS path contains no UTF-16 characters, just convert it to ASCII */ WideCharToMultiByte(CP_ACP, 0, sprofile, -1, profile, sizeof(profile), NULL, NULL); if (len == 0) { return NULL; } /* Check if path is too long */ if ((len + strlen(CFGDIR) + 3) >= 256) { return NULL; } /* Replace backslashes by slashes */ cur = old = profile; if (strstr(cur, "\\") != NULL) { while (cur != NULL) { if ((cur - old) > 1) { *cur = '/'; } old = cur; cur = strchr(old + 1, '\\'); } } snprintf(gdir, sizeof(gdir), "%s/%s/", profile, CFGDIR); return gdir; } void Sys_RedirectStdout(void) { char *cur; char *home; char *old; char path_stdout[MAX_OSPATH]; char path_stderr[MAX_OSPATH]; if (dedicated && dedicated->value) { return; } home = Sys_GetHomeDir(); if (home == NULL) { return; } cur = old = home; while (cur != NULL) { if ((cur - old) > 1) { *cur = '\0'; Sys_Mkdir(home); *cur = '/'; } old = cur; cur = strchr(old + 1, '/'); } snprintf(path_stdout, sizeof(path_stdout), "%s/%s", home, "stdout.txt"); snprintf(path_stderr, sizeof(path_stderr), "%s/%s", home, "stderr.txt"); freopen(path_stdout, "w", stdout); freopen(path_stderr, "w", stderr); } /* ======================================================================= */ /* * Windows main function. Containts the * initialization code and the main loop */ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; int time, oldtime, newtime; /* Previous instances do not exist in Win32 */ if (hPrevInstance) { return 0; } /* Make the current instance global */ global_hInstance = hInstance; /* Redirect stdout and stderr into a file */ #ifndef DEDICATED_ONLY Sys_RedirectStdout(); #endif printf("Yamagi Quake II v%s\n", YQ2VERSION); printf("=====================\n\n"); #ifndef DEDICATED_ONLY printf("Client build options:\n"); #ifdef SDL2 printf(" + SDL2\n"); #else printf(" - SDL2 (using 1.2)\n"); #endif #ifdef CDA printf(" + CD audio\n"); #else printf(" - CD audio\n"); #endif #ifdef OGG printf(" + OGG/Vorbis\n"); #else printf(" - OGG/Vorbis\n"); #endif #ifdef USE_OPENAL printf(" + OpenAL audio\n"); #else printf(" - OpenAL audio\n"); #endif #ifdef ZIP printf(" + Zip file support\n"); #else printf(" - Zip file support\n"); #endif #endif printf("Platform: %s\n", BUILDSTRING); printf("Architecture: %s\n", CPUSTRING); /* Seed PRNG */ randk_seed(); /* Parse the command line arguments */ ParseCommandLine(lpCmdLine); /* Call the initialization code */ Qcommon_Init(argc, argv); /* Save our time */ oldtime = Sys_Milliseconds(); /* The legendary main loop */ while (1) { /* If at a full screen console, don't update unless needed */ if (Minimized || (dedicated && dedicated->value)) { Sleep(1); } while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { if (!GetMessage(&msg, NULL, 0, 0)) { Com_Quit(); } sys_msg_time = msg.time; TranslateMessage(&msg); DispatchMessage(&msg); } do { newtime = Sys_Milliseconds(); time = newtime - oldtime; } while (time < 1); Qcommon_Frame(time); oldtime = newtime; } /* never gets here */ return TRUE; } void Sys_FreeLibrary(void *handle) { if (!handle) { return; } if (!FreeLibrary(handle)) { Com_Error(ERR_FATAL, "FreeLibrary failed on %p", handle); } } void * Sys_LoadLibrary(const char *path, const char *sym, void **handle) { HMODULE module; void *entry; *handle = NULL; module = LoadLibraryA(path); if (!module) { Com_Printf("%s failed: LoadLibrary returned %lu on %s\n", __func__, GetLastError(), path); return NULL; } if (sym) { entry = GetProcAddress(module, sym); if (!entry) { Com_Printf("%s failed: GetProcAddress returned %lu on %s\n", __func__, GetLastError(), path); FreeLibrary(module); return NULL; } } else { entry = NULL; } *handle = module; Com_DPrintf("%s succeeded: %s\n", __func__, path); return entry; } void * Sys_GetProcAddress(void *handle, const char *sym) { return GetProcAddress(handle, sym); } yquake2-QUAKE2_5_32/src/backends/windows/shared/0000755000175000017500000000000012615162034022120 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/backends/windows/shared/mem.c0000644000175000017500000000414012615162034023041 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 handling functions. * * ======================================================================= */ #include "../../../common/header/common.h" #include "../header/winquake.h" byte *membase; int hunkcount; int hunkmaxsize; int cursize; void * Hunk_Begin(int maxsize) { /* reserve a huge chunk of memory, but don't commit any yet */ cursize = 0; hunkmaxsize = maxsize; membase = VirtualAlloc(NULL, maxsize, MEM_RESERVE, PAGE_NOACCESS); if (!membase) { Sys_Error("VirtualAlloc reserve failed"); } return (void *)membase; } void * Hunk_Alloc(int size) { void *buf; /* round to cacheline */ size = (size + 31) & ~31; /* commit pages as needed */ buf = VirtualAlloc(membase, cursize + size, MEM_COMMIT, PAGE_READWRITE); if (!buf) { FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buf, 0, NULL); Sys_Error("VirtualAlloc commit failed.\n%s", buf); } cursize += size; if (cursize > hunkmaxsize) { Sys_Error("Hunk_Alloc overflow"); } return (void *)(membase + cursize - size); } int Hunk_End(void) { hunkcount++; return cursize; } void Hunk_Free(void *base) { if (base) { VirtualFree(base, 0, MEM_RELEASE); } hunkcount--; } yquake2-QUAKE2_5_32/src/backends/windows/header/0000755000175000017500000000000012615162034022102 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/backends/windows/header/winquake.h0000644000175000017500000000254112615162034024101 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Header file for Windows specific stuff. * * ======================================================================= */ #ifndef WIN_WINQUAKE_H #define WIN_WINQUAKE_H #include #define WINDOW_STYLE (WS_OVERLAPPED | WS_BORDER | WS_CAPTION | WS_VISIBLE) #ifndef _PC_24 #define _PC_24 0x00020000 #endif #ifndef _MCW_PC #define _MCW_PC 0x00030000 #endif extern HINSTANCE global_hInstance; extern qboolean ActiveApp, Minimized; extern int window_center_x, window_center_y; extern RECT window_rect; #endif yquake2-QUAKE2_5_32/src/backends/windows/header/resource.h0000644000175000017500000000053712615162034024107 0ustar greffrathgreffrath#ifndef WIN_RESOURCE_H #define WIN_RESOURCE_H #define IDI_ICON1 101 #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif #endif yquake2-QUAKE2_5_32/src/backends/windows/network.c0000644000175000017500000006567012615162034022525 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Network connections over IPv4, IPv6 and IPX via Winsocks. * * ======================================================================= */ /* Require Win XP or higher */ #define _WIN32_WINNT 0x0501 #include #include #include #include "../../common/header/common.h" #define MAX_LOOPBACK 4 #define QUAKE2MCAST "ff12::666" typedef struct { byte data[MAX_MSGLEN]; int datalen; } loopmsg_t; typedef struct { loopmsg_t msgs[MAX_LOOPBACK]; int get, send; } loopback_t; cvar_t *net_shownet; static cvar_t *noudp; static cvar_t *noipx; loopback_t loopbacks[2]; int ip_sockets[2]; int ip6_sockets[2]; int ipx_sockets[2]; char *multicast_interface; char *NET_ErrorString(void); static WSADATA winsockdata; /* ============================================================================= */ void NetadrToSockadr(netadr_t *a, struct sockaddr_storage *s) { struct sockaddr_in6 *s6; struct addrinfo hints; struct addrinfo *res; int error; memset(s, 0, sizeof(*s)); switch (a->type) { case NA_BROADCAST: ((struct sockaddr_in *)s)->sin_family = AF_INET; ((struct sockaddr_in *)s)->sin_port = a->port; ((struct sockaddr_in *)s)->sin_addr.s_addr = INADDR_BROADCAST; break; case NA_IP: ((struct sockaddr_in *)s)->sin_family = AF_INET; *(int *)&((struct sockaddr_in *)s)->sin_addr = *(int *)&a->ip; ((struct sockaddr_in *)s)->sin_port = a->port; break; case NA_MULTICAST6: s6 = (struct sockaddr_in6 *)s; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_NUMERICHOST; error = getaddrinfo(QUAKE2MCAST, NULL, &hints, &res); if (error) { Com_Printf("NET_NetadrToSockadr: inet_pton: %s", gai_strerror(error)); return; } /* sockaddr_in6 should now have a valid scope_id. */ memcpy(s6, res->ai_addr, res->ai_addrlen); s6->sin6_port = a->port; /* scope_id is important for link-local * destination. */ s6->sin6_scope_id = a->scope_id; break; case NA_IP6: if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)a->ip)) { s->ss_family = AF_INET; memcpy(&((struct sockaddr_in *)s)->sin_addr, &((struct in6_addr *)a->ip)->s6_addr[12], sizeof(struct in_addr)); ((struct sockaddr_in *)s)->sin_port = a->port; } else { s6 = (struct sockaddr_in6 *)s; s6->sin6_family = AF_INET6; memcpy(&s6->sin6_addr, a->ip, sizeof(s6->sin6_addr)); s6->sin6_port = a->port; /* scope_id is important for link-local * destination. */ s6->sin6_scope_id = a->scope_id; } break; case NA_LOOPBACK: case NA_IPX: case NA_BROADCAST_IPX: /* no handling of NA_LOOPBACK, NA_IPX, NA_BROADCAST_IPX */ break; } } void SockadrToNetadr(struct sockaddr_storage *s, netadr_t *a) { struct sockaddr_in6 *s6; if (s->ss_family == AF_INET) { *(int *) &a->ip = *(int *)&((struct sockaddr_in *)s)->sin_addr; a->port = ((struct sockaddr_in *)s)->sin_port; a->type = NA_IP; } else if (s->ss_family == AF_INET6) { s6 = (struct sockaddr_in6 *)s; if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)&s6->sin6_addr)) { memcpy(a->ip, (struct in_addr *)&s6->sin6_addr.s6_addr[12], sizeof(struct in_addr)); a->port = ((struct sockaddr_in *)s)->sin_port; a->type = NA_IP; } else { memcpy(a->ip, &s6->sin6_addr, sizeof(a->ip)); a->port = s6->sin6_port; a->type = NA_IP6; a->scope_id = s6->sin6_scope_id; } } else { s = NULL; } } qboolean NET_CompareAdr(netadr_t a, netadr_t b) { if (a.type != b.type) { return false; } if (a.type == NA_LOOPBACK) { return TRUE; } if (a.type == NA_IP) { if ((a.ip[0] == b.ip[0]) && (a.ip[1] == b.ip[1]) && (a.ip[2] == b.ip[2]) && (a.ip[3] == b.ip[3]) && (a.port == b.port)) { return true; } return false; } if (a.type == NA_IP6) { if ((memcmp(a.ip, b.ip, 16) == 0) && (a.port == b.port)) { return true; } } if (a.type == NA_IPX) { if ((memcmp(a.ipx, b.ipx, 10) == 0) && (a.port == b.port)) { return true; } return false; } return false; } qboolean NET_CompareBaseAdr(netadr_t a, netadr_t b) { if (a.type != b.type) { return false; } if (a.type == NA_LOOPBACK) { return TRUE; } if (a.type == NA_IP) { if ((a.ip[0] == b.ip[0]) && (a.ip[1] == b.ip[1]) && (a.ip[2] == b.ip[2]) && (a.ip[3] == b.ip[3])) { return true; } return false; } if (a.type == NA_IP6) { if ((memcmp(a.ip, b.ip, 16) == 0)) { return true; } return false; } if (a.type == NA_IPX) { if ((memcmp(a.ipx, b.ipx, 10) == 0)) { return true; } return false; } return false; } char * NET_BaseAdrToString(netadr_t a) { static char s[64], tmp[64]; struct sockaddr_storage ss; struct sockaddr_in6 *s6; switch (a.type) { case NA_IP: case NA_LOOPBACK: Com_sprintf(s, sizeof(s), "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]); break; case NA_BROADCAST: Com_sprintf(s, sizeof(s), "255.255.255.255"); break; case NA_IP6: case NA_MULTICAST6: memset(&ss, 0, sizeof(ss)); s6 = (struct sockaddr_in6 *)&ss; if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)a.ip)) { ss.ss_family = AF_INET; memcpy(&((struct sockaddr_in *)&ss)->sin_addr, &((struct in6_addr *)a.ip)->s6_addr[12], sizeof(struct in_addr)); } else { s6->sin6_scope_id = a.scope_id; s6->sin6_family = AF_INET6; memcpy(&s6->sin6_addr, a.ip, sizeof(struct in6_addr)); } if (getnameinfo((struct sockaddr *)&ss, sizeof(struct sockaddr_in6), s, sizeof(s), NULL, 0, NI_NUMERICHOST)) { Com_sprintf(s, sizeof(s), ""); } else { if ((a.type == NA_MULTICAST6) || IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&ss)-> sin6_addr)) { /* If the address is multicast (link) or a link-local, need to carry the scope. The string format of the IPv6 address is used by the client to extablish the connect to the server. A better way to handle this is to always use sockaddr_storage to represent an IP (v4, v6) address. Check first if address is already scoped. getnameinfo under Windows and Linux (?) already return scoped IPv6 address. */ if (strchr(s, '%') == NULL) { Com_sprintf(tmp, sizeof(tmp), "%s%%%d", s, s6->sin6_scope_id); memcpy(s, tmp, sizeof(s)); } } } break; case NA_IPX: case NA_BROADCAST_IPX: Com_sprintf(s, sizeof(s), "%02x%02x%02x%02x:%02x%02x%02x%02x%02x%02x:%i", a.ipx[0], a.ipx[1], a.ipx[2], a.ipx[3], a.ipx[4], a.ipx[5], a.ipx[6], a.ipx[7], a.ipx[8], a.ipx[9], ntohs(a.port)); break; default: Com_sprintf(s, sizeof(s), "invalid IP address family type"); break; } return s; } char * NET_AdrToString(netadr_t a) { static char s[64]; const char *base; base = NET_BaseAdrToString(a); Com_sprintf(s, sizeof(s), "[%s]:%d", base, ntohs(a.port)); return s; } /* * localhost * idnewt * idnewt:28000 * 192.246.40.70 * 192.246.40.70:28000 */ qboolean NET_StringToSockaddr(char *s, struct sockaddr_storage *sadr) { char copy[128]; char *addrs, *space; char *ports = NULL; int err; struct addrinfo hints; struct addrinfo *resultp; memset(sadr, 0, sizeof(*sadr)); memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_DGRAM; hints.ai_family = PF_UNSPEC; strcpy(copy, s); addrs = space = copy; if (*addrs == '[') { addrs++; for ( ; *space && *space != ']'; space++) { } if (!*space) { Com_Printf("NET_StringToSockaddr: invalid IPv6 address %s\n", s); return 0; } *space++ = '\0'; } for ( ; *space; space++) { if (*space == ':') { *space = '\0'; ports = space + 1; } } if ((err = getaddrinfo(addrs, ports, &hints, &resultp))) { /* Error */ Com_Printf("NET_StringToSockaddr: string %s:\n%s\n", s, gai_strerror(err)); return 0; } switch (resultp->ai_family) { case AF_INET: /* convert to ipv4 addr */ memset(sadr, 0, sizeof(struct sockaddr_storage)); memcpy(sadr, resultp->ai_addr, resultp->ai_addrlen); break; case AF_INET6: /* convert to ipv6 addr */ memset(sadr, 0, sizeof(struct sockaddr_storage)); memcpy(sadr, resultp->ai_addr, resultp->ai_addrlen); break; default: Com_Printf ("NET_StringToSockaddr: string %s:\nprotocol family %d not supported\n", s, resultp->ai_family); return 0; } return true; } /* * localhost * idnewt * idnewt:28000 * 192.246.40.70 * 192.246.40.70:28000 */ qboolean NET_StringToAdr(char *s, netadr_t *a) { struct sockaddr_storage sadr; if (!strcmp(s, "localhost")) { memset(a, 0, sizeof(*a)); a->type = NA_LOOPBACK; return true; } if (!NET_StringToSockaddr(s, &sadr)) { return false; } SockadrToNetadr(&sadr, a); return true; } qboolean NET_IsLocalAddress(netadr_t adr) { return adr.type == NA_LOOPBACK; } /* ============================================================================= */ qboolean NET_GetLoopPacket(netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message) { int i; loopback_t *loop; loop = &loopbacks[sock]; if (loop->send - loop->get > MAX_LOOPBACK) { loop->get = loop->send - MAX_LOOPBACK; } if (loop->get >= loop->send) { return false; } i = loop->get & (MAX_LOOPBACK - 1); loop->get++; memcpy(net_message->data, loop->msgs[i].data, loop->msgs[i].datalen); net_message->cursize = loop->msgs[i].datalen; memset(net_from, 0, sizeof(*net_from)); net_from->type = NA_LOOPBACK; return true; } void NET_SendLoopPacket(netsrc_t sock, int length, void *data, netadr_t to) { int i; loopback_t *loop; loop = &loopbacks[sock ^ 1]; i = loop->send & (MAX_LOOPBACK - 1); loop->send++; memcpy(loop->msgs[i].data, data, length); loop->msgs[i].datalen = length; } /* ============================================================================= */ qboolean NET_GetPacket(netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message) { int ret; struct sockaddr_storage from; socklen_t fromlen; int net_socket; int protocol; int err; if (NET_GetLoopPacket(sock, net_from, net_message)) { return true; } for (protocol = 0; protocol < 3; protocol++) { if (protocol == 0) { net_socket = ip_sockets[sock]; } else if (protocol == 1) { net_socket = ip6_sockets[sock]; } else { net_socket = ipx_sockets[sock]; } if (!net_socket) { continue; } fromlen = sizeof(from); ret = recvfrom(net_socket, (char *)net_message->data, net_message->maxsize, 0, (struct sockaddr *)&from, &fromlen); SockadrToNetadr(&from, net_from); if (ret == -1) { err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) { continue; } if (err == WSAEMSGSIZE) { Com_Printf("Warning: Oversize packet from %s\n", NET_AdrToString(*net_from)); continue; } if (dedicated->value) /* let dedicated servers continue after errors */ { Com_Printf("NET_GetPacket: %s from %s\n", NET_ErrorString(), NET_AdrToString(*net_from)); } else { Com_Printf("NET_GetPacket: %s from %s", NET_ErrorString(), NET_AdrToString(*net_from)); } continue; } if (ret == net_message->maxsize) { Com_Printf("Oversize packet from %s\n", NET_AdrToString(*net_from)); continue; } net_message->cursize = ret; return true; } return false; } /* ============================================================================= */ void NET_SendPacket(netsrc_t sock, int length, void *data, netadr_t to) { int ret; struct sockaddr_storage addr; int net_socket; int addr_size = sizeof(struct sockaddr_in); switch (to.type) { case NA_LOOPBACK: NET_SendLoopPacket(sock, length, data, to); return; break; case NA_BROADCAST: case NA_IP: net_socket = ip_sockets[sock]; if (!net_socket) { return; } break; case NA_IP6: case NA_MULTICAST6: net_socket = ip6_sockets[sock]; addr_size = sizeof(struct sockaddr_in6); if (!net_socket) { return; } break; case NA_IPX: case NA_BROADCAST_IPX: net_socket = ipx_sockets[sock]; if (!net_socket) { return; } break; default: Com_Printf("NET_SendPacket: bad address type"); return; break; } NetadrToSockadr(&to, &addr); /* Re-check the address family. If to.type is NA_IP6 but contains an IPv4 mapped address, NetadrToSockadr will return an AF_INET struct. If so, switch back to AF_INET socket. */ if ((to.type == NA_IP6) && (addr.ss_family == AF_INET)) { net_socket = ip_sockets[sock]; addr_size = sizeof(struct sockaddr_in); if (!net_socket) { return; } } if (addr.ss_family == AF_INET6) { struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&addr; /* If multicast socket, must specify scope. So multicast_interface must be specified */ if (IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) { struct addrinfo hints; struct addrinfo *res; char tmp[128], mcast_addr[128], mcast_port[10]; int error; /* Do a getnameinfo/getaddrinfo cycle to calculate the scope_id of the multicast address. getaddrinfo is passed a multicast address of the form ff0x::xxx%multicast_interface */ error = getnameinfo((struct sockaddr *)s6, sizeof(struct sockaddr_in6), tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST); if (error) { Com_Printf("NET_SendPacket: getnameinfo: %s", gai_strerror(error)); return; } if (multicast_interface != NULL) { Com_sprintf(mcast_addr, sizeof(mcast_addr), "%s", tmp); Com_sprintf(mcast_port, sizeof(mcast_port), "%d", ntohs(s6->sin6_port)); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_NUMERICHOST; error = getaddrinfo(mcast_addr, mcast_port, &hints, &res); if (error) { Com_Printf("NET_SendPacket: getaddrinfo: %s", gai_strerror(error)); return; } /* sockaddr_in6 should now have a valid scope_id. */ memcpy(s6, res->ai_addr, res->ai_addrlen); } else { Com_Printf("NET_SendPacket: IPv6 multicast destination but +set multicast not specified: %s", tmp); return; } } } ret = sendto(net_socket, data, length, 0, (struct sockaddr *)&addr, addr_size); if (ret == -1) { int err = WSAGetLastError(); /* wouldblock is silent */ if (err == WSAEWOULDBLOCK) { return; } /* some PPP links dont allow broadcasts */ if ((err == WSAEADDRNOTAVAIL) && ((to.type == NA_BROADCAST) || (to.type == NA_BROADCAST_IPX))) { return; } if (dedicated->value) /* let dedicated servers continue after errors */ { Com_Printf("NET_SendPacket ERROR: %s to %s\n", NET_ErrorString(), NET_AdrToString(to)); } else { if (err == WSAEADDRNOTAVAIL) { Com_DPrintf("NET_SendPacket Warning: %s : %s\n", NET_ErrorString(), NET_AdrToString(to)); } else { Com_Printf("NET_SendPacket ERROR: %s to %s\n", NET_ErrorString(), NET_AdrToString(to)); } } } } /* ============================================================================= */ int NET_IPSocket(char *net_interface, int port, netsrc_t type, int family) { char Buf[BUFSIZ], *Host, *Service; int newsocket, Error; struct sockaddr_storage ss; struct addrinfo hints, *res, *ai; unsigned long t = true; int one = 1; struct ipv6_mreq mreq; cvar_t *mcast; memset(&hints, 0, sizeof(hints)); hints.ai_family = family; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = AI_PASSIVE; if (!net_interface || !net_interface[0] || !stricmp(net_interface, "localhost")) { Host = (family == AF_INET6) ? "::" : "0.0.0.0"; } else { Host = net_interface; } if (port == PORT_ANY) { Service = NULL; } else { sprintf(Buf, "%5d", port); Service = Buf; } if ((Error = getaddrinfo(Host, Service, &hints, &res))) { return 0; } for (ai = res; ai != NULL; ai = ai->ai_next) { if ((newsocket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) { Com_Printf("NET_IPSocket: socket: %s\n", strerror(errno)); continue; } /* make it non-blocking */ if (ioctlsocket(newsocket, FIONBIO, &t) == -1) { Com_Printf("NET_IPSocket: ioctl FIONBIO: %s\n", strerror(errno)); continue; } if (setsockopt(newsocket, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one))) { printf("NET_IPSocket: setsockopt(SO_REUSEADDR) failed: %u\n", WSAGetLastError()); continue; } if (family == AF_INET) { /* make it broadcast capable */ if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&one, sizeof(one))) { Com_Printf("ERROR: NET_IPSocket: setsockopt SO_BROADCAST:%s\n", strerror(errno)); return 0; } } if (bind(newsocket, ai->ai_addr, ai->ai_addrlen) < 0) { Com_Printf("NET_IPSocket: bind: %s\n", strerror(errno)); closesocket(newsocket); } else { memcpy(&ss, ai->ai_addr, ai->ai_addrlen); break; } } if (res != NULL) { freeaddrinfo(res); } if (ai == NULL) { return 0; } switch (ss.ss_family) { case AF_INET: break; case AF_INET6: /* Multicast outgoing interface is specified for client and server (+set multicast ) */ mcast = Cvar_Get("multicast", "NULL", CVAR_NOSET); multicast_interface = (strcmp(mcast->string, "NULL") ? mcast->string : NULL); if (multicast_interface != NULL) { /* multicast_interface is a global variable. Also used in NET_SendPacket() */ mreq.ipv6mr_interface = (int)mcast->value; if (setsockopt(newsocket, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *)&mreq.ipv6mr_interface, sizeof(mreq.ipv6mr_interface)) < 0) { Com_Printf("NET_IPSocket: IPV6_MULTICAST_IF: %s\n", strerror(errno)); } /* Join multicast group ONLY if server */ if (type == NS_SERVER) { memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; hints.ai_flags = AI_PASSIVE; if ((Error = getaddrinfo(QUAKE2MCAST, NULL, &hints, &res))) { Com_Printf("NET_IPSocket: getaddrinfo: %s\n", gai_strerror(Error)); break; } memcpy(&mreq.ipv6mr_multiaddr.s6_addr, &((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr, sizeof(mreq.ipv6mr_multiaddr.s6_addr)); freeaddrinfo(res); Error = setsockopt(newsocket, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq, sizeof(mreq)); if (Error) { Com_Printf("NET_IPSocket: IPV6_JOIN_GROUP: %s\n", strerror(errno)); break; } } } break; } return newsocket; } void NET_OpenIP(void) { cvar_t *ip; int port; int dedicated; ip = Cvar_Get("ip", "localhost", CVAR_NOSET); dedicated = Cvar_VariableValue("dedicated"); if (!ip_sockets[NS_SERVER]) { port = Cvar_Get("ip_hostport", "0", CVAR_NOSET)->value; if (!port) { port = Cvar_Get("hostport", "0", CVAR_NOSET)->value; if (!port) { port = Cvar_Get("port", va("%i", PORT_SERVER), CVAR_NOSET)->value; } } ip6_sockets[NS_SERVER] = NET_IPSocket(ip->string, port, NS_SERVER, AF_INET6); ip_sockets[NS_SERVER] = NET_IPSocket(ip->string, port, NS_SERVER, AF_INET); if (!ip_sockets[NS_SERVER] && !ip6_sockets[NS_SERVER] && dedicated) { Com_Error(ERR_FATAL, "Couldn't allocate dedicated server IP port"); } } /* dedicated servers don't need client ports */ if (dedicated) { return; } if (!ip_sockets[NS_CLIENT]) { port = Cvar_Get("ip_clientport", "0", CVAR_NOSET)->value; if (!port) { port = Cvar_Get("clientport", va("%i", PORT_CLIENT), CVAR_NOSET)->value; if (!port) { port = PORT_ANY; } } ip6_sockets[NS_CLIENT] = NET_IPSocket(ip->string, port, NS_CLIENT, AF_INET6); ip_sockets[NS_CLIENT] = NET_IPSocket(ip->string, port, NS_CLIENT, AF_INET); if (!ip_sockets[NS_CLIENT] && !ip6_sockets[NS_CLIENT]) { ip6_sockets[NS_CLIENT] = NET_IPSocket(ip->string, PORT_ANY, NS_CLIENT, AF_INET6); ip_sockets[NS_CLIENT] = NET_IPSocket(ip->string, PORT_ANY, NS_CLIENT, AF_INET); } } } int NET_IPXSocket(int port) { int newsocket; struct sockaddr_ipx address; unsigned long t = 1; int err; if ((newsocket = socket(PF_IPX, SOCK_DGRAM, NSPROTO_IPX)) == -1) { err = WSAGetLastError(); if (err != WSAEAFNOSUPPORT) { Com_Printf("WARNING: IPX_Socket: socket: %s\n", NET_ErrorString()); } return 0; } /* make it non-blocking */ if (ioctlsocket(newsocket, FIONBIO, &t) == -1) { Com_Printf("WARNING: IPX_Socket: ioctl FIONBIO: %s\n", NET_ErrorString()); return 0; } /* make it broadcast capable */ if (setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char *)&t, sizeof(t)) == -1) { Com_Printf("WARNING: IPX_Socket: setsockopt SO_BROADCAST: %s\n", NET_ErrorString()); return 0; } address.sa_family = AF_IPX; memset(address.sa_netnum, 0, 4); memset(address.sa_nodenum, 0, 6); if (port == PORT_ANY) { address.sa_socket = 0; } else { address.sa_socket = htons((short)port); } if (bind(newsocket, (void *)&address, sizeof(address)) == -1) { Com_Printf("WARNING: IPX_Socket: bind: %s\n", NET_ErrorString()); closesocket(newsocket); return 0; } return newsocket; } void NET_OpenIPX(void) { int port; int dedicated; dedicated = Cvar_VariableValue("dedicated"); if (!ipx_sockets[NS_SERVER]) { port = Cvar_Get("ipx_hostport", "0", CVAR_NOSET)->value; if (!port) { port = Cvar_Get("hostport", "0", CVAR_NOSET)->value; if (!port) { port = Cvar_Get("port", va("%i", PORT_SERVER), CVAR_NOSET)->value; } } ipx_sockets[NS_SERVER] = NET_IPXSocket(port); } /* dedicated servers don't need client ports */ if (dedicated) { return; } if (!ipx_sockets[NS_CLIENT]) { port = Cvar_Get("ipx_clientport", "0", CVAR_NOSET)->value; if (!port) { port = Cvar_Get("clientport", va("%i", PORT_CLIENT), CVAR_NOSET)->value; if (!port) { port = PORT_ANY; } } ipx_sockets[NS_CLIENT] = NET_IPXSocket(port); if (!ipx_sockets[NS_CLIENT]) { ipx_sockets[NS_CLIENT] = NET_IPXSocket(PORT_ANY); } } } /* * A single player game will * only use the loopback code */ void NET_Config(qboolean multiplayer) { int i; static qboolean old_config; if (old_config == multiplayer) { return; } old_config = multiplayer; if (!multiplayer) { /* shut down any existing sockets */ for (i = 0; i < 2; i++) { if (ip_sockets[i]) { closesocket(ip_sockets[i]); ip_sockets[i] = 0; } if (ip6_sockets[i]) { closesocket(ip6_sockets[i]); ip6_sockets[i] = 0; } if (ipx_sockets[i]) { closesocket(ipx_sockets[i]); ipx_sockets[i] = 0; } } } else { /* open sockets */ if (!noudp->value) { NET_OpenIP(); } if (!noipx->value) { NET_OpenIPX(); } } } /* * sleeps msec or until * net socket is ready */ void NET_Sleep(int msec) { struct timeval timeout; fd_set fdset; extern cvar_t *dedicated; int i; if (!dedicated || !dedicated->value) { return; /* we're not a server, just run full speed */ } FD_ZERO(&fdset); i = 0; if (ip6_sockets[NS_SERVER]) { FD_SET(ip6_sockets[NS_SERVER], &fdset); /* network socket */ i = ip6_sockets[NS_SERVER]; } if (ip_sockets[NS_SERVER]) { FD_SET(ip_sockets[NS_SERVER], &fdset); /* network socket */ i = ip_sockets[NS_SERVER]; } if (ipx_sockets[NS_SERVER]) { FD_SET(ipx_sockets[NS_SERVER], &fdset); /* network socket */ if (ipx_sockets[NS_SERVER] > i) { i = ipx_sockets[NS_SERVER]; } } timeout.tv_sec = msec / 1000; timeout.tv_usec = (msec % 1000) * 1000; i = max(ip_sockets[NS_SERVER], ip6_sockets[NS_SERVER]); i = max(i, ipx_sockets[NS_SERVER]); select(i + 1, &fdset, NULL, NULL, &timeout); } /* =================================================================== */ void NET_Init(void) { int r; r = WSAStartup(MAKEWORD(1, 1), &winsockdata); if (r) { Com_Error(ERR_FATAL, "Winsock initialization failed."); } Com_Printf("Winsock Initialized\n"); noudp = Cvar_Get("noudp", "0", CVAR_NOSET); noipx = Cvar_Get("noipx", "0", CVAR_NOSET); net_shownet = Cvar_Get("net_shownet", "0", 0); } void NET_Shutdown(void) { NET_Config(false); /* close sockets */ WSACleanup(); } char * NET_ErrorString(void) { int code; code = WSAGetLastError(); switch (code) { case WSAEINTR: return "WSAEINTR"; case WSAEBADF: return "WSAEBADF"; case WSAEACCES: return "WSAEACCES"; case WSAEDISCON: return "WSAEDISCON"; case WSAEFAULT: return "WSAEFAULT"; case WSAEINVAL: return "WSAEINVAL"; case WSAEMFILE: return "WSAEMFILE"; case WSAEWOULDBLOCK: return "WSAEWOULDBLOCK"; case WSAEINPROGRESS: return "WSAEINPROGRESS"; case WSAEALREADY: return "WSAEALREADY"; case WSAENOTSOCK: return "WSAENOTSOCK"; case WSAEDESTADDRREQ: return "WSAEDESTADDRREQ"; case WSAEMSGSIZE: return "WSAEMSGSIZE"; case WSAEPROTOTYPE: return "WSAEPROTOTYPE"; case WSAENOPROTOOPT: return "WSAENOPROTOOPT"; case WSAEPROTONOSUPPORT: return "WSAEPROTONOSUPPORT"; case WSAESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT"; case WSAEOPNOTSUPP: return "WSAEOPNOTSUPP"; case WSAEPFNOSUPPORT: return "WSAEPFNOSUPPORT"; case WSAEAFNOSUPPORT: return "WSAEAFNOSUPPORT"; case WSAEADDRINUSE: return "WSAEADDRINUSE"; case WSAEADDRNOTAVAIL: return "WSAEADDRNOTAVAIL"; case WSAENETDOWN: return "WSAENETDOWN"; case WSAENETUNREACH: return "WSAENETUNREACH"; case WSAENETRESET: return "WSAENETRESET"; case WSAECONNABORTED: return "WSWSAECONNABORTEDAEINTR"; case WSAECONNRESET: return "WSAECONNRESET"; case WSAENOBUFS: return "WSAENOBUFS"; case WSAEISCONN: return "WSAEISCONN"; case WSAENOTCONN: return "WSAENOTCONN"; case WSAESHUTDOWN: return "WSAESHUTDOWN"; case WSAETOOMANYREFS: return "WSAETOOMANYREFS"; case WSAETIMEDOUT: return "WSAETIMEDOUT"; case WSAECONNREFUSED: return "WSAECONNREFUSED"; case WSAELOOP: return "WSAELOOP"; case WSAENAMETOOLONG: return "WSAENAMETOOLONG"; case WSAEHOSTDOWN: return "WSAEHOSTDOWN"; case WSASYSNOTREADY: return "WSASYSNOTREADY"; case WSAVERNOTSUPPORTED: return "WSAVERNOTSUPPORTED"; case WSANOTINITIALISED: return "WSANOTINITIALISED"; case WSAHOST_NOT_FOUND: return "WSAHOST_NOT_FOUND"; case WSATRY_AGAIN: return "WSATRY_AGAIN"; case WSANO_RECOVERY: return "WSANO_RECOVERY"; case WSANO_DATA: return "WSANO_DATA"; default: return "NO ERROR"; } } yquake2-QUAKE2_5_32/src/backends/generic/0000755000175000017500000000000012615162034020574 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/backends/generic/header/0000755000175000017500000000000012615162034022024 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/backends/generic/header/qgl.h0000644000175000017500000000736212615162034022770 0ustar greffrathgreffrath/* * Copyright (C) 2013 Alejandro Ricoveri * Copyright (C) 1999-2005 Id Software, Inc. * Copyright (C) 1997-2001 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. * * ======================================================================= * * Quake GL prototypes based on ioquake3 source code * * ======================================================================= */ #ifndef REF_QGL_H #define REF_QGL_H #ifdef _WIN32 #include #endif #if defined(__APPLE__) #include #else #include #endif #ifndef APIENTRY #define APIENTRY #endif #define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB #define GL_TEXTURE0_SGIS 0x835E #define GL_TEXTURE1_SGIS 0x835F #define GL_POINT_SIZE_MIN_EXT 0x8126 #define GL_POINT_SIZE_MAX_EXT 0x8127 #define GL_DISTANCE_ATTENUATION_EXT 0x8129 #ifndef GL_EXT_texture_env_combine #define GL_COMBINE_EXT 0x8570 #define GL_COMBINE_RGB_EXT 0x8571 #define GL_COMBINE_ALPHA_EXT 0x8572 #define GL_RGB_SCALE_EXT 0x8573 #define GL_ADD_SIGNED_EXT 0x8574 #define GL_INTERPOLATE_EXT 0x8575 #define GL_CONSTANT_EXT 0x8576 #define GL_PRIMARY_COLOR_EXT 0x8577 #define GL_PREVIOUS_EXT 0x8578 #define GL_SOURCE0_RGB_EXT 0x8580 #define GL_SOURCE1_RGB_EXT 0x8581 #define GL_SOURCE2_RGB_EXT 0x8582 #define GL_SOURCE3_RGB_EXT 0x8583 #define GL_SOURCE4_RGB_EXT 0x8584 #define GL_SOURCE5_RGB_EXT 0x8585 #define GL_SOURCE6_RGB_EXT 0x8586 #define GL_SOURCE7_RGB_EXT 0x8587 #define GL_SOURCE0_ALPHA_EXT 0x8588 #define GL_SOURCE1_ALPHA_EXT 0x8589 #define GL_SOURCE2_ALPHA_EXT 0x858A #define GL_SOURCE3_ALPHA_EXT 0x858B #define GL_SOURCE4_ALPHA_EXT 0x858C #define GL_SOURCE5_ALPHA_EXT 0x858D #define GL_SOURCE6_ALPHA_EXT 0x858E #define GL_SOURCE7_ALPHA_EXT 0x858F #define GL_OPERAND0_RGB_EXT 0x8590 #define GL_OPERAND1_RGB_EXT 0x8591 #define GL_OPERAND2_RGB_EXT 0x8592 #define GL_OPERAND3_RGB_EXT 0x8593 #define GL_OPERAND4_RGB_EXT 0x8594 #define GL_OPERAND5_RGB_EXT 0x8595 #define GL_OPERAND6_RGB_EXT 0x8596 #define GL_OPERAND7_RGB_EXT 0x8597 #define GL_OPERAND0_ALPHA_EXT 0x8598 #define GL_OPERAND1_ALPHA_EXT 0x8599 #define GL_OPERAND2_ALPHA_EXT 0x859A #define GL_OPERAND3_ALPHA_EXT 0x859B #define GL_OPERAND4_ALPHA_EXT 0x859C #define GL_OPERAND5_ALPHA_EXT 0x859D #define GL_OPERAND6_ALPHA_EXT 0x859E #define GL_OPERAND7_ALPHA_EXT 0x859F #endif /* QGL main functions */ /* * This is responsible for setting up our QGL extension pointers */ qboolean QGL_Init ( void ); /* * Unloads the specified DLL then nulls out all the proc pointers. */ void QGL_Shutdown ( void ); /* GL extensions */ extern void ( APIENTRY *qglPointParameterfEXT ) ( GLenum param, GLfloat value ); extern void ( APIENTRY *qglPointParameterfvEXT ) ( GLenum param, const GLfloat *value ); extern void ( APIENTRY *qglColorTableEXT ) ( GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid * ); extern void ( APIENTRY *qglLockArraysEXT ) ( int, int ); extern void ( APIENTRY *qglUnlockArraysEXT ) ( void ); extern void ( APIENTRY *qglMultiTexCoord2fARB) ( GLenum, GLfloat, GLfloat ); extern void ( APIENTRY *qglActiveTextureARB ) ( GLenum ); extern void ( APIENTRY *qglClientActiveTextureARB ) ( GLenum ); #endif yquake2-QUAKE2_5_32/src/backends/generic/header/input.h0000644000175000017500000000244412615162034023340 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Header file for the generic input backend. * * ======================================================================= */ #ifndef GEN_INPUT_H #define GEN_INPUT_H #include "../../../common/header/shared.h" /* * Initializes the input backend */ void IN_Init(void); /* * Move handling */ void IN_Move(usercmd_t *cmd); /* * Shuts the backend down */ void IN_Shutdown(void); /* * Updates the state of the input queue */ void IN_Update(void); #endif yquake2-QUAKE2_5_32/src/backends/generic/header/qal.h0000644000175000017500000001073012615162034022753 0ustar greffrathgreffrath/* * Copyright (C) 2012 Yamagi Burmeister * Copyright (C) 2010 skuller.net * * This program is free software; you can 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. * * ======================================================================= * * Header file to the low level "qal" API implementation. This source file * was taken from Q2Pro and modified by the YQ2 authors. * * ======================================================================= */ #ifdef USE_OPENAL #ifndef _QAL_API_H_ #define _QAL_API_H_ #include #include /* Function pointers used to tie * the qal API to the OpenAL API */ extern LPALENABLE qalEnable; extern LPALDISABLE qalDisable; extern LPALISENABLED qalIsEnabled; extern LPALGETSTRING qalGetString; extern LPALGETBOOLEANV qalGetBooleanv; extern LPALGETINTEGERV qalGetIntegerv; extern LPALGETFLOATV qalGetFloatv; extern LPALGETDOUBLEV qalGetDoublev; extern LPALGETBOOLEAN qalGetBoolean; extern LPALGETINTEGER qalGetInteger; extern LPALGETFLOAT qalGetFloat; extern LPALGETDOUBLE qalGetDouble; extern LPALGETERROR qalGetError; extern LPALISEXTENSIONPRESENT qalIsExtensionPresent; extern LPALGETPROCADDRESS qalGetProcAddress; extern LPALGETENUMVALUE qalGetEnumValue; extern LPALLISTENERF qalListenerf; extern LPALLISTENER3F qalListener3f; extern LPALLISTENERFV qalListenerfv; extern LPALLISTENERI qalListeneri; extern LPALLISTENER3I qalListener3i; extern LPALLISTENERIV qalListeneriv; extern LPALGETLISTENERF qalGetListenerf; extern LPALGETLISTENER3F qalGetListener3f; extern LPALGETLISTENERFV qalGetListenerfv; extern LPALGETLISTENERI qalGetListeneri; extern LPALGETLISTENER3I qalGetListener3i; extern LPALGETLISTENERIV qalGetListeneriv; extern LPALGENSOURCES qalGenSources; extern LPALDELETESOURCES qalDeleteSources; extern LPALISSOURCE qalIsSource; extern LPALSOURCEF qalSourcef; extern LPALSOURCE3F qalSource3f; extern LPALSOURCEFV qalSourcefv; extern LPALSOURCEI qalSourcei; extern LPALSOURCE3I qalSource3i; extern LPALSOURCEIV qalSourceiv; extern LPALGETSOURCEF qalGetSourcef; extern LPALGETSOURCE3F qalGetSource3f; extern LPALGETSOURCEFV qalGetSourcefv; extern LPALGETSOURCEI qalGetSourcei; extern LPALGETSOURCE3I qalGetSource3i; extern LPALGETSOURCEIV qalGetSourceiv; extern LPALSOURCEPLAYV qalSourcePlayv; extern LPALSOURCESTOPV qalSourceStopv; extern LPALSOURCEREWINDV qalSourceRewindv; extern LPALSOURCEPAUSEV qalSourcePausev; extern LPALSOURCEPLAY qalSourcePlay; extern LPALSOURCESTOP qalSourceStop; extern LPALSOURCEREWIND qalSourceRewind; extern LPALSOURCEPAUSE qalSourcePause; extern LPALSOURCEQUEUEBUFFERS qalSourceQueueBuffers; extern LPALSOURCEUNQUEUEBUFFERS qalSourceUnqueueBuffers; extern LPALGENBUFFERS qalGenBuffers; extern LPALDELETEBUFFERS qalDeleteBuffers; extern LPALISBUFFER qalIsBuffer; extern LPALBUFFERDATA qalBufferData; extern LPALBUFFERF qalBufferf; extern LPALBUFFER3F qalBuffer3f; extern LPALBUFFERFV qalBufferfv; extern LPALBUFFERI qalBufferi; extern LPALBUFFER3I qalBuffer3i; extern LPALBUFFERIV qalBufferiv; extern LPALGETBUFFERF qalGetBufferf; extern LPALGETBUFFER3F qalGetBuffer3f; extern LPALGETBUFFERFV qalGetBufferfv; extern LPALGETBUFFERI qalGetBufferi; extern LPALGETBUFFER3I qalGetBuffer3i; extern LPALGETBUFFERIV qalGetBufferiv; extern LPALDOPPLERFACTOR qalDopplerFactor; extern LPALDOPPLERVELOCITY qalDopplerVelocity; extern LPALSPEEDOFSOUND qalSpeedOfSound; extern LPALDISTANCEMODEL qalDistanceModel; extern LPALGENFILTERS qalGenFilters; extern LPALFILTERI qalFilteri; extern LPALFILTERF qalFilterf; extern LPALDELETEFILTERS qalDeleteFilters; /* * Gives information over the OpenAL * implementation and it's state */ void QAL_SoundInfo(void); /* * Loads the OpenAL shared lib, creates * a context and device handle. */ qboolean QAL_Init(void); /* * Shuts OpenAL down, frees all context and * device handles and unloads the shared lib. */ void QAL_Shutdown(void); #endif /* _QAL_API_H_ */ #endif /* USE_OPENAL */ yquake2-QUAKE2_5_32/src/backends/generic/qgl.c0000644000175000017500000000536012615162034021527 0ustar greffrathgreffrath/* * Copyright (C) 2013 Alejandro Ricoveri * Copyright (C) 1997-2001 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 implements the operating system binding of GL to QGL function * pointers. When doing a port of Quake2 you must implement the following * two functions: * * QGL_Init() - loads libraries, assigns function pointers, etc. * QGL_Shutdown() - unloads libraries, NULLs function pointers * * This implementation should work for Windows and unixoid platforms, * other platforms may need an own implementation. * * ======================================================================= */ #include "../../client/refresh/header/local.h" /* * GL extensions */ void (APIENTRY *qglLockArraysEXT)(int, int); void (APIENTRY *qglUnlockArraysEXT)(void); void (APIENTRY *qglPointParameterfEXT)(GLenum param, GLfloat value); void (APIENTRY *qglPointParameterfvEXT)(GLenum param, const GLfloat *value); void (APIENTRY *qglColorTableEXT)(GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *); void ( APIENTRY *qgl3DfxSetPaletteEXT ) ( GLuint * ); void ( APIENTRY *qglMultiTexCoord2fARB ) ( GLenum, GLfloat, GLfloat ); void ( APIENTRY *qglActiveTextureARB ) ( GLenum ); void ( APIENTRY *qglClientActiveTextureARB ) ( GLenum ); /* ========================================================================= */ void QGL_EXT_Reset ( void ) { qglLockArraysEXT = NULL; qglUnlockArraysEXT = NULL; qglPointParameterfEXT = NULL; qglPointParameterfvEXT = NULL; qglColorTableEXT = NULL; qgl3DfxSetPaletteEXT = NULL; qglMultiTexCoord2fARB = NULL; qglActiveTextureARB = NULL; qglClientActiveTextureARB = NULL; } /* ========================================================================= */ void QGL_Shutdown ( void ) { // Reset GL extension pointers QGL_EXT_Reset(); } /* ========================================================================= */ qboolean QGL_Init (void) { // Reset GL extension pointers QGL_EXT_Reset(); return true; } yquake2-QUAKE2_5_32/src/backends/generic/vid.c0000644000175000017500000001412612615162034021526 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 is the "heart" of the id Tech 2 refresh engine. This file * implements the main window in which Quake II is running. The window * itself is created by the SDL backend, but here the refresh module is * loaded, initialized and it's interaction with the operating system * implemented. This code is also the interconnect between the input * system (the mouse) and the keyboard system, both are here tied * together with the refresher. The direct interaction between the * refresher and those subsystems are the main cause for the very * acurate and precise input controls of the id Tech 2. * * This implementation works for Windows and unixoid systems, but * other platforms may need an own implementation! * * ======================================================================= */ #include #include #include "../../client/header/client.h" #include "../../client/header/keyboard.h" typedef struct vidmode_s { const char *description; int width, height; int mode; } vidmode_t; /* This must be the same as in videomenu.c! */ vidmode_t vid_modes[] = { {"Mode 0: 320x240", 320, 240, 0}, {"Mode 1: 400x300", 400, 300, 1}, {"Mode 2: 512x384", 512, 384, 2}, {"Mode 3: 640x400", 640, 400, 3}, {"Mode 4: 640x480", 640, 480, 4}, {"Mode 5: 800x500", 800, 500, 5}, {"Mode 6: 800x600", 800, 600, 6}, {"Mode 7: 960x720", 960, 720, 7}, {"Mode 8: 1024x480", 1024, 480, 8}, {"Mode 9: 1024x640", 1024, 640, 9}, {"Mode 10: 1024x768", 1024, 768, 10}, {"Mode 11: 1152x768", 1152, 768, 11}, {"Mode 12: 1152x864", 1152, 864, 12}, {"Mode 13: 1280x800", 1280, 800, 13}, {"Mode 14: 1280x854", 1280, 854, 14}, {"Mode 15: 1280x960", 1280, 960, 15}, {"Mode 16: 1280x1024", 1280, 1024, 16}, {"Mode 17: 1366x768", 1366, 768, 17}, {"Mode 18: 1440x900", 1440, 900, 18}, {"Mode 19: 1600x1200", 1600, 1200, 19}, {"Mode 20: 1680x1050", 1680, 1050, 20}, {"Mode 21: 1920x1080", 1920, 1080, 21}, {"Mode 22: 1920x1200", 1920, 1200, 22}, {"Mode 23: 2048x1536", 2048, 1536, 23}, }; /* Console variables that we need to access from this module */ cvar_t *vid_gamma; cvar_t *vid_xpos; /* X coordinate of window position */ cvar_t *vid_ypos; /* Y coordinate of window position */ cvar_t *vid_fullscreen; /* Global variables used internally by this module */ viddef_t viddef; /* global video state; used by other modules */ qboolean ref_active = false; /* Is the refresher being used? */ #define VID_NUM_MODES (sizeof(vid_modes) / sizeof(vid_modes[0])) #define MAXPRINTMSG 4096 void VID_Printf(int print_level, char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; va_start(argptr, fmt); vsnprintf(msg, MAXPRINTMSG, fmt, argptr); va_end(argptr); if (print_level == PRINT_ALL) { Com_Printf("%s", msg); } else { Com_DPrintf("%s", msg); } } void VID_Error(int err_level, char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; va_start(argptr, fmt); vsnprintf(msg, MAXPRINTMSG, fmt, argptr); va_end(argptr); Com_Error(err_level, "%s", msg); } /* * Console command to re-start the video mode and refresh. We do this * simply by setting the modified flag for the vid_fullscreen variable, which will * cause the entire video mode and refreshto be reset on the next frame. */ void VID_Restart_f(void) { vid_fullscreen->modified = true; } qboolean VID_GetModeInfo(int *width, int *height, int mode) { if ((mode < 0) || (mode >= VID_NUM_MODES)) { return false; } *width = vid_modes[mode].width; *height = vid_modes[mode].height; return true; } void VID_NewWindow(int width, int height) { viddef.width = width; viddef.height = height; } qboolean VID_LoadRefresh(void) { // If the refresher is already active // we'll shut it down VID_Shutdown(); // Log it! Com_Printf("----- refresher initialization -----\n"); // Declare the refresher as active ref_active = true; // Initiate the refresher if (R_Init(0, 0) == -1) { VID_Shutdown(); // Isn't that just too bad? :( return false; } /* Ensure that all key states are cleared */ Key_MarkAllUp(); Com_Printf("------------------------------------\n\n"); return true; } /* * This function gets called once just before drawing each frame, and * it's sole purpose in life is to check to see if any of the video mode * parameters have changed, and if they have to update the refresh * and/or video mode to match. */ void VID_CheckChanges(void) { if (vid_fullscreen->modified) { S_StopAllSounds(); /* refresh has changed */ cl.refresh_prepped = false; cl.cinematicpalette_active = false; cls.disable_screen = true; // Proceed to reboot the refresher VID_LoadRefresh(); cls.disable_screen = false; } } void VID_Init(void) { /* Create the video variables so we know how to start the graphics drivers */ vid_xpos = Cvar_Get("vid_xpos", "3", CVAR_ARCHIVE); vid_ypos = Cvar_Get("vid_ypos", "22", CVAR_ARCHIVE); vid_fullscreen = Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); vid_gamma = Cvar_Get("vid_gamma", "1", CVAR_ARCHIVE); /* Add some console commands that we want to handle */ Cmd_AddCommand("vid_restart", VID_Restart_f); /* Start the graphics mode and load refresh DLL */ VID_CheckChanges(); } void VID_Shutdown(void) { if (ref_active) { /* Shut down the renderer */ R_Shutdown(); } // Declare the refresher as inactive ref_active = false; } yquake2-QUAKE2_5_32/src/backends/generic/qal.c0000644000175000017500000004125212615162034021521 0ustar greffrathgreffrath/* * Copyright (C) 2012 Yamagi Burmeister * Copyright (C) 2010 skuller.net * * This program is free software; you can 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. * * ======================================================================= * * Low level, platform depended "qal" API implementation. This files * provides functions to load, initialize, shutdown und unload the * OpenAL library and connects the "qal" funtion pointers to the * OpenAL functions. It shopuld work on Windows and unixoid Systems, * other platforms may need an own implementation. This source file * was taken from Q2Pro and modified by the YQ2 authors. * * ======================================================================= */ #ifdef USE_OPENAL #include #include #include #include "../../common/header/common.h" #include "../../client/sound/header/local.h" #include "header/qal.h" static ALCcontext *context; static ALCdevice *device; static cvar_t *al_device; static cvar_t *al_driver; static void *handle; /* Function pointers for OpenAL management */ static LPALCCREATECONTEXT qalcCreateContext; static LPALCMAKECONTEXTCURRENT qalcMakeContextCurrent; static LPALCPROCESSCONTEXT qalcProcessContext; static LPALCSUSPENDCONTEXT qalcSuspendContext; static LPALCDESTROYCONTEXT qalcDestroyContext; static LPALCGETCURRENTCONTEXT qalcGetCurrentContext; static LPALCGETCONTEXTSDEVICE qalcGetContextsDevice; static LPALCOPENDEVICE qalcOpenDevice; static LPALCCLOSEDEVICE qalcCloseDevice; static LPALCGETERROR qalcGetError; static LPALCISEXTENSIONPRESENT qalcIsExtensionPresent; static LPALCGETPROCADDRESS qalcGetProcAddress; static LPALCGETENUMVALUE qalcGetEnumValue; static LPALCGETSTRING qalcGetString; static LPALCGETINTEGERV qalcGetIntegerv; static LPALCCAPTUREOPENDEVICE qalcCaptureOpenDevice; static LPALCCAPTURECLOSEDEVICE qalcCaptureCloseDevice; static LPALCCAPTURESTART qalcCaptureStart; static LPALCCAPTURESTOP qalcCaptureStop; static LPALCCAPTURESAMPLES qalcCaptureSamples ; /* Declaration of function pointers used to connect OpenAL to our internal API */ LPALENABLE qalEnable; LPALDISABLE qalDisable; LPALISENABLED qalIsEnabled; LPALGETSTRING qalGetString; LPALGETBOOLEANV qalGetBooleanv; LPALGETINTEGERV qalGetIntegerv; LPALGETFLOATV qalGetFloatv; LPALGETDOUBLEV qalGetDoublev; LPALGETBOOLEAN qalGetBoolean; LPALGETINTEGER qalGetInteger; LPALGETFLOAT qalGetFloat; LPALGETDOUBLE qalGetDouble; LPALGETERROR qalGetError; LPALISEXTENSIONPRESENT qalIsExtensionPresent; LPALGETPROCADDRESS qalGetProcAddress; LPALGETENUMVALUE qalGetEnumValue; LPALLISTENERF qalListenerf; LPALLISTENER3F qalListener3f; LPALLISTENERFV qalListenerfv; LPALLISTENERI qalListeneri; LPALLISTENER3I qalListener3i; LPALLISTENERIV qalListeneriv; LPALGETLISTENERF qalGetListenerf; LPALGETLISTENER3F qalGetListener3f; LPALGETLISTENERFV qalGetListenerfv; LPALGETLISTENERI qalGetListeneri; LPALGETLISTENER3I qalGetListener3i; LPALGETLISTENERIV qalGetListeneriv; LPALGENSOURCES qalGenSources; LPALDELETESOURCES qalDeleteSources; LPALISSOURCE qalIsSource; LPALSOURCEF qalSourcef; LPALSOURCE3F qalSource3f; LPALSOURCEFV qalSourcefv; LPALSOURCEI qalSourcei; LPALSOURCE3I qalSource3i; LPALSOURCEIV qalSourceiv; LPALGETSOURCEF qalGetSourcef; LPALGETSOURCE3F qalGetSource3f; LPALGETSOURCEFV qalGetSourcefv; LPALGETSOURCEI qalGetSourcei; LPALGETSOURCE3I qalGetSource3i; LPALGETSOURCEIV qalGetSourceiv; LPALSOURCEPLAYV qalSourcePlayv; LPALSOURCESTOPV qalSourceStopv; LPALSOURCEREWINDV qalSourceRewindv; LPALSOURCEPAUSEV qalSourcePausev; LPALSOURCEPLAY qalSourcePlay; LPALSOURCESTOP qalSourceStop; LPALSOURCEREWIND qalSourceRewind; LPALSOURCEPAUSE qalSourcePause; LPALSOURCEQUEUEBUFFERS qalSourceQueueBuffers; LPALSOURCEUNQUEUEBUFFERS qalSourceUnqueueBuffers; LPALGENBUFFERS qalGenBuffers; LPALDELETEBUFFERS qalDeleteBuffers; LPALISBUFFER qalIsBuffer; LPALBUFFERDATA qalBufferData; LPALBUFFERF qalBufferf; LPALBUFFER3F qalBuffer3f; LPALBUFFERFV qalBufferfv; LPALBUFFERI qalBufferi; LPALBUFFER3I qalBuffer3i; LPALBUFFERIV qalBufferiv; LPALGETBUFFERF qalGetBufferf; LPALGETBUFFER3F qalGetBuffer3f; LPALGETBUFFERFV qalGetBufferfv; LPALGETBUFFERI qalGetBufferi; LPALGETBUFFER3I qalGetBuffer3i; LPALGETBUFFERIV qalGetBufferiv; LPALDOPPLERFACTOR qalDopplerFactor; LPALDOPPLERVELOCITY qalDopplerVelocity; LPALSPEEDOFSOUND qalSpeedOfSound; LPALDISTANCEMODEL qalDistanceModel; LPALGENFILTERS qalGenFilters; LPALFILTERI qalFilteri; LPALFILTERF qalFilterf; LPALDELETEFILTERS qalDeleteFilters; /* * Gives information over the OpenAL * implementation and it's state */ void QAL_SoundInfo() { Com_Printf("OpenAL settings:\n"); Com_Printf("AL_VENDOR: %s\n", qalGetString(AL_VENDOR)); Com_Printf("AL_RENDERER: %s\n", qalGetString(AL_RENDERER)); Com_Printf("AL_VERSION: %s\n", qalGetString(AL_VERSION)); Com_Printf("AL_EXTENSIONS: %s\n", qalGetString(AL_EXTENSIONS)); if (qalcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT")) { const char *devs = qalcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); Com_Printf("\nAvailable OpenAL devices:\n"); if (devs == NULL) { Com_Printf("- No devices found. Depending on your\n"); Com_Printf(" platform this may be expected and\n"); Com_Printf(" doesn't indicate a problem!\n"); } else { while (devs && *devs) { Com_Printf("- %s\n", devs); devs += strlen(devs) + 1; } } } if (qalcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT")) { const char *devs = qalcGetString(device, ALC_DEVICE_SPECIFIER); Com_Printf("\nCurrent OpenAL device:\n"); if (devs == NULL) { Com_Printf("- No OpenAL device in use\n"); } else { Com_Printf("- %s\n", devs); } } } /* * Shuts OpenAL down, frees all context and * device handles and unloads the shared lib. */ void QAL_Shutdown() { if (context) { qalcMakeContextCurrent( NULL ); qalcDestroyContext( context ); context = NULL; } if (device) { qalcCloseDevice( device ); device = NULL; } /* Disconnect function pointers used for OpenAL management calls */ qalcCreateContext = NULL; qalcMakeContextCurrent = NULL; qalcProcessContext = NULL; qalcSuspendContext = NULL; qalcDestroyContext = NULL; qalcGetCurrentContext = NULL; qalcGetContextsDevice = NULL; qalcOpenDevice = NULL; qalcCloseDevice = NULL; qalcGetError = NULL; qalcIsExtensionPresent = NULL; qalcGetProcAddress = NULL; qalcGetEnumValue = NULL; qalcGetString = NULL; qalcGetIntegerv = NULL; qalcCaptureOpenDevice = NULL; qalcCaptureCloseDevice = NULL; qalcCaptureStart = NULL; qalcCaptureStop = NULL; qalcCaptureSamples = NULL; /* Disconnect OpenAL * function pointers */ qalEnable = NULL; qalDisable = NULL; qalIsEnabled = NULL; qalGetString = NULL; qalGetBooleanv = NULL; qalGetIntegerv = NULL; qalGetFloatv = NULL; qalGetDoublev = NULL; qalGetBoolean = NULL; qalGetInteger = NULL; qalGetFloat = NULL; qalGetDouble = NULL; qalGetError = NULL; qalIsExtensionPresent = NULL; qalGetProcAddress = NULL; qalGetEnumValue = NULL; qalListenerf = NULL; qalListener3f = NULL; qalListenerfv = NULL; qalListeneri = NULL; qalListener3i = NULL; qalListeneriv = NULL; qalGetListenerf = NULL; qalGetListener3f = NULL; qalGetListenerfv = NULL; qalGetListeneri = NULL; qalGetListener3i = NULL; qalGetListeneriv = NULL; qalGenSources = NULL; qalDeleteSources = NULL; qalIsSource = NULL; qalSourcef = NULL; qalSource3f = NULL; qalSourcefv = NULL; qalSourcei = NULL; qalSource3i = NULL; qalSourceiv = NULL; qalGetSourcef = NULL; qalGetSource3f = NULL; qalGetSourcefv = NULL; qalGetSourcei = NULL; qalGetSource3i = NULL; qalGetSourceiv = NULL; qalSourcePlayv = NULL; qalSourceStopv = NULL; qalSourceRewindv = NULL; qalSourcePausev = NULL; qalSourcePlay = NULL; qalSourceStop = NULL; qalSourceRewind = NULL; qalSourcePause = NULL; qalSourceQueueBuffers = NULL; qalSourceUnqueueBuffers = NULL; qalGenBuffers = NULL; qalDeleteBuffers = NULL; qalIsBuffer = NULL; qalBufferData = NULL; qalBufferf = NULL; qalBuffer3f = NULL; qalBufferfv = NULL; qalBufferi = NULL; qalBuffer3i = NULL; qalBufferiv = NULL; qalGetBufferf = NULL; qalGetBuffer3f = NULL; qalGetBufferfv = NULL; qalGetBufferi = NULL; qalGetBuffer3i = NULL; qalGetBufferiv = NULL; qalDopplerFactor = NULL; qalDopplerVelocity = NULL; qalSpeedOfSound = NULL; qalDistanceModel = NULL; qalGenFilters = NULL; qalFilteri = NULL; qalFilterf = NULL; qalDeleteFilters = NULL; /* Unload the shared lib */ Sys_FreeLibrary(handle); handle = NULL; } /* * Loads the OpenAL shared lib, creates * a context and device handle. */ qboolean QAL_Init() { /* DEFAULT_OPENAL_DRIVER is defined at compile time via the compiler */ al_driver = Cvar_Get("al_driver", DEFAULT_OPENAL_DRIVER, CVAR_ARCHIVE); al_device = Cvar_Get("al_device", "", CVAR_ARCHIVE); Com_Printf("LoadLibrary(%s)\n", al_driver->string); /* Load the library */ Sys_LoadLibrary(al_driver->string, NULL, &handle); if (!handle) { Com_Printf("Loading %s failed! Disabling OpenAL.\n", al_driver->string); return false; } /* Connect function pointers to management functions */ qalcCreateContext = Sys_GetProcAddress(handle, "alcCreateContext"); qalcMakeContextCurrent = Sys_GetProcAddress(handle, "alcMakeContextCurrent"); qalcProcessContext = Sys_GetProcAddress(handle, "alcProcessContext"); qalcSuspendContext = Sys_GetProcAddress(handle, "alcSuspendContext"); qalcDestroyContext = Sys_GetProcAddress(handle, "alcDestroyContext"); qalcGetCurrentContext = Sys_GetProcAddress(handle, "alcGetCurrentContext"); qalcGetContextsDevice = Sys_GetProcAddress(handle, "alcGetContextsDevice"); qalcOpenDevice = Sys_GetProcAddress(handle, "alcOpenDevice"); qalcCloseDevice = Sys_GetProcAddress(handle, "alcCloseDevice"); qalcGetError = Sys_GetProcAddress(handle, "alcGetError"); qalcIsExtensionPresent = Sys_GetProcAddress(handle, "alcIsExtensionPresent"); qalcGetProcAddress = Sys_GetProcAddress(handle, "alcGetProcAddress"); qalcGetEnumValue = Sys_GetProcAddress(handle, "alcGetEnumValue"); qalcGetString = Sys_GetProcAddress(handle, "alcGetString"); qalcGetIntegerv = Sys_GetProcAddress(handle, "alcGetIntegerv"); qalcCaptureOpenDevice = Sys_GetProcAddress(handle, "alcCaptureOpenDevice"); qalcCaptureCloseDevice = Sys_GetProcAddress(handle, "alcCaptureCloseDevice"); qalcCaptureStart = Sys_GetProcAddress(handle, "alcCaptureStart"); qalcCaptureStop = Sys_GetProcAddress(handle, "alcCaptureStop"); qalcCaptureSamples = Sys_GetProcAddress(handle, "alcCaptureSamples"); /* Connect function pointers to to OpenAL API functions */ qalEnable = Sys_GetProcAddress(handle, "alEnable"); qalDisable = Sys_GetProcAddress(handle, "alDisable"); qalIsEnabled = Sys_GetProcAddress(handle, "alIsEnabled"); qalGetString = Sys_GetProcAddress(handle, "alGetString"); qalGetBooleanv = Sys_GetProcAddress(handle, "alGetBooleanv"); qalGetIntegerv = Sys_GetProcAddress(handle, "alGetIntegerv"); qalGetFloatv = Sys_GetProcAddress(handle, "alGetFloatv"); qalGetDoublev = Sys_GetProcAddress(handle, "alGetDoublev"); qalGetBoolean = Sys_GetProcAddress(handle, "alGetBoolean"); qalGetInteger = Sys_GetProcAddress(handle, "alGetInteger"); qalGetFloat = Sys_GetProcAddress(handle, "alGetFloat"); qalGetDouble = Sys_GetProcAddress(handle, "alGetDouble"); qalGetError = Sys_GetProcAddress(handle, "alGetError"); qalIsExtensionPresent = Sys_GetProcAddress(handle, "alIsExtensionPresent"); qalGetProcAddress = Sys_GetProcAddress(handle, "alGetProcAddress"); qalGetEnumValue = Sys_GetProcAddress(handle, "alGetEnumValue"); qalListenerf = Sys_GetProcAddress(handle, "alListenerf"); qalListener3f = Sys_GetProcAddress(handle, "alListener3f"); qalListenerfv = Sys_GetProcAddress(handle, "alListenerfv"); qalListeneri = Sys_GetProcAddress(handle, "alListeneri"); qalListener3i = Sys_GetProcAddress(handle, "alListener3i"); qalListeneriv = Sys_GetProcAddress(handle, "alListeneriv"); qalGetListenerf = Sys_GetProcAddress(handle, "alGetListenerf"); qalGetListener3f = Sys_GetProcAddress(handle, "alGetListener3f"); qalGetListenerfv = Sys_GetProcAddress(handle, "alGetListenerfv"); qalGetListeneri = Sys_GetProcAddress(handle, "alGetListeneri"); qalGetListener3i = Sys_GetProcAddress(handle, "alGetListener3i"); qalGetListeneriv = Sys_GetProcAddress(handle, "alGetListeneriv"); qalGenSources = Sys_GetProcAddress(handle, "alGenSources"); qalDeleteSources = Sys_GetProcAddress(handle, "alDeleteSources"); qalIsSource = Sys_GetProcAddress(handle, "alIsSource"); qalSourcef = Sys_GetProcAddress(handle, "alSourcef"); qalSource3f = Sys_GetProcAddress(handle, "alSource3f"); qalSourcefv = Sys_GetProcAddress(handle, "alSourcefv"); qalSourcei = Sys_GetProcAddress(handle, "alSourcei"); qalSource3i = Sys_GetProcAddress(handle, "alSource3i"); qalSourceiv = Sys_GetProcAddress(handle, "alSourceiv"); qalGetSourcef = Sys_GetProcAddress(handle, "alGetSourcef"); qalGetSource3f = Sys_GetProcAddress(handle, "alGetSource3f"); qalGetSourcefv = Sys_GetProcAddress(handle, "alGetSourcefv"); qalGetSourcei = Sys_GetProcAddress(handle, "alGetSourcei"); qalGetSource3i = Sys_GetProcAddress(handle, "alGetSource3i"); qalGetSourceiv = Sys_GetProcAddress(handle, "alGetSourceiv"); qalSourcePlayv = Sys_GetProcAddress(handle, "alSourcePlayv"); qalSourceStopv = Sys_GetProcAddress(handle, "alSourceStopv"); qalSourceRewindv = Sys_GetProcAddress(handle, "alSourceRewindv"); qalSourcePausev = Sys_GetProcAddress(handle, "alSourcePausev"); qalSourcePlay = Sys_GetProcAddress(handle, "alSourcePlay"); qalSourceStop = Sys_GetProcAddress(handle, "alSourceStop"); qalSourceRewind = Sys_GetProcAddress(handle, "alSourceRewind"); qalSourcePause = Sys_GetProcAddress(handle, "alSourcePause"); qalSourceQueueBuffers = Sys_GetProcAddress(handle, "alSourceQueueBuffers"); qalSourceUnqueueBuffers = Sys_GetProcAddress(handle, "alSourceUnqueueBuffers"); qalGenBuffers = Sys_GetProcAddress(handle, "alGenBuffers"); qalDeleteBuffers = Sys_GetProcAddress(handle, "alDeleteBuffers"); qalIsBuffer = Sys_GetProcAddress(handle, "alIsBuffer"); qalBufferData = Sys_GetProcAddress(handle, "alBufferData"); qalBufferf = Sys_GetProcAddress(handle, "alBufferf"); qalBuffer3f = Sys_GetProcAddress(handle, "alBuffer3f"); qalBufferfv = Sys_GetProcAddress(handle, "alBufferfv"); qalBufferi = Sys_GetProcAddress(handle, "alBufferi"); qalBuffer3i = Sys_GetProcAddress(handle, "alBuffer3i"); qalBufferiv = Sys_GetProcAddress(handle, "alBufferiv"); qalGetBufferf = Sys_GetProcAddress(handle, "alGetBufferf"); qalGetBuffer3f = Sys_GetProcAddress(handle, "alGetBuffer3f"); qalGetBufferfv = Sys_GetProcAddress(handle, "alGetBufferfv"); qalGetBufferi = Sys_GetProcAddress(handle, "alGetBufferi"); qalGetBuffer3i = Sys_GetProcAddress(handle, "alGetBuffer3i"); qalGetBufferiv = Sys_GetProcAddress(handle, "alGetBufferiv"); qalDopplerFactor = Sys_GetProcAddress(handle, "alDopplerFactor"); qalDopplerVelocity = Sys_GetProcAddress(handle, "alDopplerVelocity"); qalSpeedOfSound = Sys_GetProcAddress(handle, "alSpeedOfSound"); qalDistanceModel = Sys_GetProcAddress(handle, "alDistanceModel"); /* Open the OpenAL device */ Com_Printf("...opening OpenAL device:"); device = qalcOpenDevice(al_device->string[0] ? al_device->string : NULL); if(!device) { Com_DPrintf("failed\n"); QAL_Shutdown(); return false; } Com_Printf("ok\n"); /* Create the OpenAL context */ Com_Printf("...creating OpenAL context: "); context = qalcCreateContext(device, NULL); if(!context) { Com_DPrintf("failed\n"); QAL_Shutdown(); return false; } Com_Printf("ok\n"); /* Set the created context as current context */ Com_Printf("...making context current: "); if (!qalcMakeContextCurrent(context)) { Com_DPrintf("failed\n"); QAL_Shutdown(); return false; } if (qalcIsExtensionPresent(device, "ALC_EXT_EFX") != AL_FALSE) { qalGenFilters = qalGetProcAddress("alGenFilters"); qalFilteri = qalGetProcAddress("alFilteri"); qalFilterf = qalGetProcAddress("alFilterf"); qalDeleteFilters = qalGetProcAddress("alDeleteFilters"); } else { qalGenFilters = NULL; qalFilteri = NULL; qalFilterf = NULL; qalDeleteFilters = NULL; } Com_Printf("ok\n"); /* Print OpenAL informations */ Com_Printf("\n"); QAL_SoundInfo(); Com_Printf("\n"); return true; } #endif /* USE_OPENAL */ yquake2-QUAKE2_5_32/src/backends/generic/misc.c0000644000175000017500000000753112615162034021701 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements some generic funktions * * ======================================================================= */ #include #include #if defined(__linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #include // readlink(), amongst others #endif #ifdef __FreeBSD__ #include // for sysctl() to get path to executable #endif #ifdef _WIN32 #include // GetModuleFileNameA() #endif #ifdef __APPLE__ #include // _NSGetExecutablePath #endif #ifndef PATH_MAX // this is mostly for windows. windows has a MAX_PATH = 260 #define, but allows // longer paths anyway.. this might not be the maximum allowed length, but is // hopefully good enough for realistic usecases #define PATH_MAX 4096 #define _DG__DEFINED_PATH_MAX #endif static void SetExecutablePath(char* exePath) { // !!! this assumes that exePath can hold PATH_MAX chars !!! #ifdef _WIN32 DWORD len = GetModuleFileNameA(NULL, exePath, PATH_MAX); if(len <= 0 || len == PATH_MAX) { // an error occured, clear exe path exePath[0] = '\0'; } #elif defined(__linux) || defined(__NetBSD__) || defined(__OpenBSD__) // all the platforms that have /proc/$pid/exe or similar that symlink the // real executable - basiscally Linux and the BSDs except for FreeBSD which // doesn't enable proc by default and has a sysctl() for this char buf[PATH_MAX] = {0}; #ifdef __linux snprintf(buf, sizeof(buf), "/proc/%d/exe", getpid()); #else // the BSDs snprintf(buf, sizeof(buf), "/proc/%d/file", getpid()); #endif // readlink() doesn't null-terminate! int len = readlink(buf, exePath, PATH_MAX-1); if (len <= 0) { // an error occured, clear exe path exePath[0] = '\0'; } else { exePath[len] = '\0'; } #elif defined(__FreeBSD__) // the sysctl should also work when /proc/ is not mounted (which seems to // be common on FreeBSD), so use it.. int name[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; size_t len = PATH_MAX-1; int ret = sysctl(name, sizeof(name)/sizeof(name[0]), exePath, &len, NULL, 0); if(ret != 0) { // an error occured, clear exe path exePath[0] = '\0'; } #elif defined(__APPLE__) uint32_t bufSize = PATH_MAX; if(_NSGetExecutablePath(exePath, &bufSize) != 0) { // WTF, PATH_MAX is not enough to hold the path? // an error occured, clear exe path exePath[0] = '\0'; } // TODO: realpath() ? // TODO: no idea what this is if the executable is in an app bundle #else #error "Unsupported Platform!" // feel free to add implementation for your platform and send me a patch #endif } const char *Sys_GetBinaryDir(void) { static char exeDir[PATH_MAX] = {0}; if(exeDir[0] != '\0') return exeDir; SetExecutablePath(exeDir); // cut off executable name char* lastSlash = strrchr(exeDir, '/'); #ifdef _WIN32 char* lastBackSlash = strrchr(exeDir, '\\'); if(lastSlash == NULL || lastBackSlash > lastSlash) lastSlash = lastBackSlash; #endif // _WIN32 if(lastSlash != NULL) lastSlash[1] = '\0'; // cut off after last (back)slash return exeDir; } yquake2-QUAKE2_5_32/src/backends/sdl/0000755000175000017500000000000012615162034017742 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/backends/sdl/cd.c0000644000175000017500000001773312615162034020507 0ustar greffrathgreffrath/* * Copyright (C) 2001 Robert Bäuml * Copyright (C) 2002 W. P. va Paassen * * This program is free software; you can 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. * * ======================================================================= * * Audio-CD backend in SDL * * ======================================================================= */ #ifdef CDA #include #ifdef _WIN32 #include "SDL/SDL.h" #elif defined(__APPLE__) #include #else #include "SDL.h" #endif #include "../../client/header/client.h" static qboolean cdValid = false; static qboolean initialized = false; static qboolean enabled = true; static qboolean playLooping = false; static SDL_CD *cd_id; static float cdvolume = 1.0; static int lastTrack = 0; cvar_t *cd_volume; cvar_t *cd_nocd; cvar_t *cd_dev; static void CD_f(); static void CDAudio_Eject() { if (!cd_id || !enabled) { return; } if (SDL_CDEject(cd_id)) { Com_DPrintf("Unable to eject CD-ROM tray.\n"); } } void CDAudio_Play(int track, qboolean looping) { CDstatus cd_stat; lastTrack = track + 1; if (!cd_id || !enabled) { return; } cd_stat = SDL_CDStatus(cd_id); if (!cdValid) { if (!CD_INDRIVE(cd_stat) || (!cd_id->numtracks)) { return; } cdValid = true; } if ((track < 1) || (track >= cd_id->numtracks)) { Com_DPrintf("CDAudio: Bad track number: %d\n", track); return; } track--; if (cd_stat == CD_PLAYING) { if (cd_id->cur_track == track) { return; } CDAudio_Stop(); } if (SDL_CDPlay(cd_id, cd_id->track[track].offset, cd_id->track[track].length)) { Com_DPrintf("CDAudio_Play: Unable to play track: %d (%s)\n", track + 1, SDL_GetError()); return; } playLooping = looping; } void CDAudio_RandomPlay(void) { int track, i = 0, free_tracks = 0; float f; CDstatus cd_stat; byte *track_bools; if (!cd_id || !enabled) { return; } track_bools = (byte *)malloc(cd_id->numtracks * sizeof(byte)); if (track_bools == 0) { return; } /* create an array of available audio tracknumbers */ for ( ; i < cd_id->numtracks; i++) { track_bools[i] = cd_id->track[i].type == SDL_AUDIO_TRACK; free_tracks += track_bools[i]; } if (!free_tracks) { Com_DPrintf("CDAudio_RandomPlay: Unable to find and play a random audio track, insert an audio cd please"); goto free_end; } /*choose random audio track */ do { do { f = ((float)randk()) / ((float)RAND_MAX + 1.0); track = (int)(cd_id->numtracks * f); } while (!track_bools[track]); lastTrack = track + 1; cd_stat = SDL_CDStatus(cd_id); if (!cdValid) { if (!CD_INDRIVE(cd_stat) || (!cd_id->numtracks)) { goto free_end; } cdValid = true; } if (cd_stat == CD_PLAYING) { if (cd_id->cur_track == track + 1) { goto free_end; } CDAudio_Stop(); } if (SDL_CDPlay(cd_id, cd_id->track[track].offset, cd_id->track[track].length)) { track_bools[track] = 0; free_tracks--; } else { playLooping = true; break; } } while (free_tracks > 0); free_end: free((void *)track_bools); } void CDAudio_Stop() { int cdstate; if (!cd_id || !enabled) { return; } cdstate = SDL_CDStatus(cd_id); if ((cdstate != CD_PLAYING) && (cdstate != CD_PAUSED)) { return; } if (SDL_CDStop(cd_id)) { Com_DPrintf("CDAudio_Stop: Failed to stop track.\n"); } playLooping = 0; } void CDAudio_Pause() { if (!cd_id || !enabled) { return; } if (SDL_CDStatus(cd_id) != CD_PLAYING) { return; } if (SDL_CDPause(cd_id)) { Com_DPrintf("CDAudio_Pause: Failed to pause track.\n"); } } void CDAudio_Resume() { if (!cd_id || !enabled) { return; } if (SDL_CDStatus(cd_id) != CD_PAUSED) { return; } if (SDL_CDResume(cd_id)) { Com_DPrintf("CDAudio_Resume: Failed to resume track.\n"); } } void CDAudio_Update() { static int cnt = 0; if (!cd_id || !enabled) { return; } if (cd_volume && (cd_volume->value != cdvolume)) { if (cdvolume) { Cvar_SetValue("cd_volume", 0.0); CDAudio_Pause(); } else { Cvar_SetValue("cd_volume", 1.0); CDAudio_Resume(); } cdvolume = cd_volume->value; return; } /* this causes too much overhead to be executed every frame */ if (++cnt == 16) { cnt = 0; if (cd_nocd->value) { CDAudio_Stop(); return; } if (playLooping && (SDL_CDStatus(cd_id) != CD_PLAYING) && (SDL_CDStatus(cd_id) != CD_PAUSED)) { CDAudio_Play(lastTrack, true); } } } int CDAudio_Init() { cvar_t *cv; if (initialized) { return 0; } cv = Cvar_Get("nocdaudio", "0", CVAR_NOSET); if (cv->value) { return -1; } #ifdef OGG cd_nocd = Cvar_Get("cd_nocd", "1", CVAR_ARCHIVE); #else cd_nocd = Cvar_Get("cd_nocd", "0", CVAR_ARCHIVE); #endif if (cd_nocd->value) { return -1; } cd_volume = Cvar_Get("cd_volume", "1", CVAR_ARCHIVE); if (SDL_WasInit(SDL_INIT_EVERYTHING) == 0) { if (SDL_Init(SDL_INIT_CDROM) < 0) { Com_Printf("Couldn't init SDL cdrom: %s\n", SDL_GetError()); return -1; } } else if (SDL_WasInit(SDL_INIT_CDROM) == 0) { if (SDL_InitSubSystem(SDL_INIT_CDROM) < 0) { Com_Printf("Couldn't init SDL cdrom: %s\n", SDL_GetError()); return -1; } } cd_id = SDL_CDOpen(0); if (!cd_id) { Com_Printf("CDAudio_Init: Unable to open default CD-ROM drive: %s\n", SDL_GetError()); return -1; } initialized = true; enabled = true; cdValid = true; if (!CD_INDRIVE(SDL_CDStatus(cd_id))) { Com_Printf("CDAudio_Init: No CD in drive.\n"); cdValid = false; } if (!cd_id->numtracks) { Com_Printf("CDAudio_Init: CD contains no audio tracks.\n"); cdValid = false; } Cmd_AddCommand("cd", CD_f); Com_Printf("CD Audio Initialized.\n"); return 0; } void CDAudio_Shutdown() { if (!cd_id) { return; } CDAudio_Stop(); SDL_CDClose(cd_id); cd_id = NULL; if (SDL_WasInit(SDL_INIT_EVERYTHING) == SDL_INIT_CDROM) { SDL_Quit(); } else { SDL_QuitSubSystem(SDL_INIT_CDROM); } initialized = false; } static void CD_f() { char *command; int cdstate; if (Cmd_Argc() < 2) { return; } command = Cmd_Argv(1); if (!Q_strcasecmp(command, "on")) { enabled = true; } if (!Q_strcasecmp(command, "off")) { if (!cd_id) { return; } cdstate = SDL_CDStatus(cd_id); if ((cdstate == CD_PLAYING) || (cdstate == CD_PAUSED)) { CDAudio_Stop(); } enabled = false; return; } if (!Q_strcasecmp(command, "play")) { CDAudio_Play((byte)(int)strtol(Cmd_Argv(2), (char **)NULL, 10), false); return; } if (!Q_strcasecmp(command, "loop")) { CDAudio_Play((byte)(int)strtol(Cmd_Argv(2), (char **)NULL, 10), true); return; } if (!Q_strcasecmp(command, "stop")) { CDAudio_Stop(); return; } if (!Q_strcasecmp(command, "pause")) { CDAudio_Pause(); return; } if (!Q_strcasecmp(command, "resume")) { CDAudio_Resume(); return; } if (!Q_strcasecmp(command, "eject")) { CDAudio_Eject(); return; } if (!Q_strcasecmp(command, "info")) { if (!cd_id) { return; } cdstate = SDL_CDStatus(cd_id); Com_Printf("%d tracks\n", cd_id->numtracks); if (cdstate == CD_PLAYING) { Com_Printf("Currently %s track %d\n", playLooping ? "looping" : "playing", cd_id->cur_track + 1); } else if (cdstate == CD_PAUSED) { Com_Printf("Paused %s track %d\n", playLooping ? "looping" : "playing", cd_id->cur_track + 1); } return; } } void CDAudio_Activate(qboolean active) { if (active) { CDAudio_Resume(); } else { CDAudio_Pause(); } } #endif /* CDA */ yquake2-QUAKE2_5_32/src/backends/sdl/input.c0000644000175000017500000002760512615162034021257 0ustar greffrathgreffrath/* * Copyright (C) 2010 Yamagi Burmeister * Copyright (C) 1997-2001 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 is the Quake II input system backend, implemented with SDL. * * ======================================================================= */ #include "../../client/refresh/header/local.h" #include "../../client/header/keyboard.h" #include "../generic/header/input.h" #include "../../client/header/client.h" /* There's no sdl-config on OS X and Windows */ #ifdef SDL2 #include #else /* SDL1.2 */ #include #endif /*SDL2 */ /* SDL 1.2 <-> 2.0 compatiblity cruft */ #if SDL_VERSION_ATLEAST(2, 0, 0) #define SDLK_KP0 SDLK_KP_0 #define SDLK_KP1 SDLK_KP_1 #define SDLK_KP2 SDLK_KP_2 #define SDLK_KP3 SDLK_KP_3 #define SDLK_KP4 SDLK_KP_4 #define SDLK_KP5 SDLK_KP_5 #define SDLK_KP6 SDLK_KP_6 #define SDLK_KP7 SDLK_KP_7 #define SDLK_KP8 SDLK_KP_8 #define SDLK_KP9 SDLK_KP_9 #define SDLK_RMETA SDLK_RGUI #define SDLK_LMETA SDLK_LGUI #define SDLK_COMPOSE SDLK_APPLICATION #define SDLK_PRINT SDLK_PRINTSCREEN #define SDLK_SCROLLOCK SDLK_SCROLLLOCK #define SDLK_NUMLOCK SDLK_NUMLOCKCLEAR #endif #define MOUSE_MAX 3000 #define MOUSE_MIN 40 /* Globals */ static int mouse_x, mouse_y; static int old_mouse_x, old_mouse_y; static qboolean mlooking; /* CVars */ cvar_t *vid_fullscreen; static cvar_t *in_grab; static cvar_t *in_mouse; static cvar_t *exponential_speedup; cvar_t *freelook; cvar_t *lookstrafe; cvar_t *m_forward; static cvar_t *m_filter; cvar_t *m_pitch; cvar_t *m_side; cvar_t *m_yaw; cvar_t *sensitivity; static cvar_t *windowed_mouse; /* ------------------------------------------------------------------ */ /* * This creepy function translates SDL keycodes into * the id Tech 2 engines interal representation. */ static int IN_TranslateSDLtoQ2Key(unsigned int keysym) { int key = 0; /* These must be translated */ switch (keysym) { case SDLK_PAGEUP: key = K_PGUP; break; case SDLK_KP9: key = K_KP_PGUP; break; case SDLK_PAGEDOWN: key = K_PGDN; break; case SDLK_KP3: key = K_KP_PGDN; break; case SDLK_KP7: key = K_KP_HOME; break; case SDLK_HOME: key = K_HOME; break; case SDLK_KP1: key = K_KP_END; break; case SDLK_END: key = K_END; break; case SDLK_KP4: key = K_KP_LEFTARROW; break; case SDLK_LEFT: key = K_LEFTARROW; break; case SDLK_KP6: key = K_KP_RIGHTARROW; break; case SDLK_RIGHT: key = K_RIGHTARROW; break; case SDLK_KP2: key = K_KP_DOWNARROW; break; case SDLK_DOWN: key = K_DOWNARROW; break; case SDLK_KP8: key = K_KP_UPARROW; break; case SDLK_UP: key = K_UPARROW; break; case SDLK_ESCAPE: key = K_ESCAPE; break; case SDLK_KP_ENTER: key = K_KP_ENTER; break; case SDLK_RETURN: key = K_ENTER; break; case SDLK_TAB: key = K_TAB; break; case SDLK_F1: key = K_F1; break; case SDLK_F2: key = K_F2; break; case SDLK_F3: key = K_F3; break; case SDLK_F4: key = K_F4; break; case SDLK_F5: key = K_F5; break; case SDLK_F6: key = K_F6; break; case SDLK_F7: key = K_F7; break; case SDLK_F8: key = K_F8; break; case SDLK_F9: key = K_F9; break; case SDLK_F10: key = K_F10; break; case SDLK_F11: key = K_F11; break; case SDLK_F12: key = K_F12; break; case SDLK_F13: key = K_F13; break; case SDLK_F14: key = K_F14; break; case SDLK_F15: key = K_F15; break; case SDLK_BACKSPACE: key = K_BACKSPACE; break; case SDLK_KP_PERIOD: key = K_KP_DEL; break; case SDLK_DELETE: key = K_DEL; break; case SDLK_PAUSE: key = K_PAUSE; break; case SDLK_LSHIFT: case SDLK_RSHIFT: key = K_SHIFT; break; case SDLK_LCTRL: case SDLK_RCTRL: key = K_CTRL; break; case SDLK_RMETA: case SDLK_LMETA: key = K_COMMAND; break; case SDLK_RALT: case SDLK_LALT: key = K_ALT; break; case SDLK_KP5: key = K_KP_5; break; case SDLK_INSERT: key = K_INS; break; case SDLK_KP0: key = K_KP_INS; break; case SDLK_KP_MULTIPLY: key = K_KP_STAR; break; case SDLK_KP_PLUS: key = K_KP_PLUS; break; case SDLK_KP_MINUS: key = K_KP_MINUS; break; case SDLK_KP_DIVIDE: key = K_KP_SLASH; break; case SDLK_MODE: key = K_MODE; break; case SDLK_COMPOSE: key = K_COMPOSE; break; case SDLK_HELP: key = K_HELP; break; case SDLK_PRINT: key = K_PRINT; break; case SDLK_SYSREQ: key = K_SYSREQ; break; case SDLK_MENU: key = K_MENU; break; case SDLK_POWER: key = K_POWER; break; case SDLK_UNDO: key = K_UNDO; break; case SDLK_SCROLLOCK: key = K_SCROLLOCK; break; case SDLK_NUMLOCK: key = K_KP_NUMLOCK; break; case SDLK_CAPSLOCK: key = K_CAPSLOCK; break; default: break; } return key; } /* ------------------------------------------------------------------ */ /* * Updates the input queue state. Called every * frame by the client and does nearly all the * input magic. */ void IN_Update(void) { qboolean want_grab; SDL_Event event; unsigned int key; /* Get and process an event */ while (SDL_PollEvent(&event)) { switch (event.type) { #if SDL_VERSION_ATLEAST(2, 0, 0) case SDL_MOUSEWHEEL: Key_Event((event.wheel.y > 0 ? K_MWHEELUP : K_MWHEELDOWN), true, true); Key_Event((event.wheel.y > 0 ? K_MWHEELUP : K_MWHEELDOWN), false, true); break; #endif case SDL_MOUSEBUTTONDOWN: #if !SDL_VERSION_ATLEAST(2, 0, 0) if (event.button.button == 4) { Key_Event(K_MWHEELUP, true, true); Key_Event(K_MWHEELUP, false, true); break; } else if (event.button.button == 5) { Key_Event(K_MWHEELDOWN, true, true); Key_Event(K_MWHEELDOWN, false, true); break; } #endif case SDL_MOUSEBUTTONUP: switch( event.button.button ) { case SDL_BUTTON_LEFT: key = K_MOUSE1; break; case SDL_BUTTON_MIDDLE: key = K_MOUSE3; break; case SDL_BUTTON_RIGHT: key = K_MOUSE2; break; case SDL_BUTTON_X1: key = K_MOUSE4; break; case SDL_BUTTON_X2: key = K_MOUSE5; break; default: return; } Key_Event(key, (event.type == SDL_MOUSEBUTTONDOWN), true); break; case SDL_MOUSEMOTION: if (cls.key_dest == key_game && (int)cl_paused->value == 0) { mouse_x += event.motion.xrel; mouse_y += event.motion.yrel; } break; #if SDL_VERSION_ATLEAST(2, 0, 0) case SDL_TEXTINPUT: if ((event.text.text[0] >= SDLK_SPACE) && (event.text.text[0] < SDLK_DELETE)) { Char_Event(event.text.text[0]); } break; #endif case SDL_KEYDOWN: #if !SDL_VERSION_ATLEAST(2, 0, 0) if ((event.key.keysym.unicode >= SDLK_SPACE) && (event.key.keysym.unicode < SDLK_DELETE)) { Char_Event(event.key.keysym.unicode); } #endif if ((event.key.keysym.sym >= SDLK_SPACE) && (event.key.keysym.sym < SDLK_DELETE)) { Key_Event(event.key.keysym.sym, true, false); } else { Key_Event(IN_TranslateSDLtoQ2Key(event.key.keysym.sym), true, true); } break; case SDL_KEYUP: if ((event.key.keysym.sym >= SDLK_SPACE) && (event.key.keysym.sym < SDLK_DELETE)) { Key_Event(event.key.keysym.sym, false, false); } else { Key_Event(IN_TranslateSDLtoQ2Key(event.key.keysym.sym), false, true); } break; #if SDL_VERSION_ATLEAST(2, 0, 0) case SDL_WINDOWEVENT: if(event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) { Key_MarkAllUp(); } #else // SDL1.2 case SDL_ACTIVEEVENT: if(event.active.gain == 0 && (event.active.state & SDL_APPINPUTFOCUS)) { Key_MarkAllUp(); } #endif break; } } /* Grab and ungrab the mouse if the* console or the menu is opened */ want_grab = (vid_fullscreen->value || in_grab->value == 1 || (in_grab->value == 2 && windowed_mouse->value)); /* calling GLimp_GrabInput() each is a but ugly but simple and should work. * + the called SDL functions return after a cheap check, if there's * nothing to do, anyway */ GLimp_GrabInput(want_grab); } /* * Move handling */ void IN_Move(usercmd_t *cmd) { if (m_filter->value) { if ((mouse_x > 1) || (mouse_x < -1)) { mouse_x = (mouse_x + old_mouse_x) * 0.5; } if ((mouse_y > 1) || (mouse_y < -1)) { mouse_y = (mouse_y + old_mouse_y) * 0.5; } } old_mouse_x = mouse_x; old_mouse_y = mouse_y; if (mouse_x || mouse_y) { if (!exponential_speedup->value) { mouse_x *= sensitivity->value; mouse_y *= sensitivity->value; } else { if ((mouse_x > MOUSE_MIN) || (mouse_y > MOUSE_MIN) || (mouse_x < -MOUSE_MIN) || (mouse_y < -MOUSE_MIN)) { mouse_x = (mouse_x * mouse_x * mouse_x) / 4; mouse_y = (mouse_y * mouse_y * mouse_y) / 4; if (mouse_x > MOUSE_MAX) { mouse_x = MOUSE_MAX; } else if (mouse_x < -MOUSE_MAX) { mouse_x = -MOUSE_MAX; } if (mouse_y > MOUSE_MAX) { mouse_y = MOUSE_MAX; } else if (mouse_y < -MOUSE_MAX) { mouse_y = -MOUSE_MAX; } } } /* add mouse X/Y movement to cmd */ if ((in_strafe.state & 1) || (lookstrafe->value && mlooking)) { cmd->sidemove += m_side->value * mouse_x; } else { cl.viewangles[YAW] -= m_yaw->value * mouse_x; } if ((mlooking || freelook->value) && !(in_strafe.state & 1)) { cl.viewangles[PITCH] += m_pitch->value * mouse_y; } else { cmd->forwardmove -= m_forward->value * mouse_y; } mouse_x = mouse_y = 0; } } /* ------------------------------------------------------------------ */ /* * Look down */ static void IN_MLookDown(void) { mlooking = true; } /* * Look up */ static void IN_MLookUp(void) { mlooking = false; IN_CenterView(); } /* ------------------------------------------------------------------ */ /* * Initializes the backend */ void IN_Init(void) { Com_Printf("------- input initialization -------\n"); mouse_x = mouse_y = 0; exponential_speedup = Cvar_Get("exponential_speedup", "0", CVAR_ARCHIVE); freelook = Cvar_Get("freelook", "1", 0); in_grab = Cvar_Get("in_grab", "2", CVAR_ARCHIVE); in_mouse = Cvar_Get("in_mouse", "0", CVAR_ARCHIVE); lookstrafe = Cvar_Get("lookstrafe", "0", 0); m_filter = Cvar_Get("m_filter", "0", CVAR_ARCHIVE); m_forward = Cvar_Get("m_forward", "1", 0); m_pitch = Cvar_Get("m_pitch", "0.022", 0); m_side = Cvar_Get("m_side", "0.8", 0); m_yaw = Cvar_Get("m_yaw", "0.022", 0); sensitivity = Cvar_Get("sensitivity", "3", 0); vid_fullscreen = Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); windowed_mouse = Cvar_Get("windowed_mouse", "1", CVAR_USERINFO | CVAR_ARCHIVE); Cmd_AddCommand("+mlook", IN_MLookDown); Cmd_AddCommand("-mlook", IN_MLookUp); #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_StartTextInput(); #else SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); #endif Com_Printf("------------------------------------\n\n"); } /* * Shuts the backend down */ void IN_Shutdown(void) { Cmd_RemoveCommand("force_centerview"); Cmd_RemoveCommand("+mlook"); Cmd_RemoveCommand("-mlook"); Com_Printf("Shutting down input.\n"); } /* ------------------------------------------------------------------ */ yquake2-QUAKE2_5_32/src/backends/sdl/sound.c0000644000175000017500000006727612615162034021260 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2010, 2013 Yamagi Burmeister * Copyright (C) 2005 Ryan C. Gordon * * This program is free software; you can 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. * * ======================================================================= * * SDL sound backend. Since SDL is just an API for sound playback, we * must caculate everything in software: mixing, resampling, stereo * spartializations, etc. Therefor this file is rather complex. :) * Samples are read from the cache (see the upper layer of the sound * system), manipulated and written into sound.buffer. sound.buffer is * passed to SDL (in fact requested by SDL via the callback) and played * with a platform dependend SDL driver. Parts of this file are based * on ioQuake3s snd_sdl.c. * * ======================================================================= */ /* SDL includes */ #ifdef SDL2 #include #else // SDL1.2 #include #endif //SDL2 /* Local includes */ #include "../../client/header/client.h" #include "../../client/sound/header/local.h" /* Defines */ #define SDL_PAINTBUFFER_SIZE 2048 #define SDL_FULLVOLUME 80 #define SDL_LOOPATTENUATE 0.003 /* Globals */ cvar_t *s_sdldriver; int *snd_p; static sound_t *backend; static portable_samplepair_t paintbuffer[SDL_PAINTBUFFER_SIZE]; static int beginofs; static int playpos = 0; static int samplesize = 0; static int snd_inited = 0; static int snd_scaletable[32][256]; static int snd_vol; static int soundtime; /* ------------------------------------------------------------------ */ /* =============================== */ /* Low-pass filter */ /* Based on OpenAL implementation. */ /* Filter's context */ typedef struct { float a; float gain_hf; portable_samplepair_t history[2]; qboolean is_history_initialized; } LpfContext; static const int lpf_reference_frequency = 5000; static const float lpf_default_gain_hf = 0.25F; static LpfContext lpf_context; static qboolean lpf_is_enabled; static void lpf_initialize( LpfContext* lpf_context, float gain_hf, int target_frequency) { assert(target_frequency > 0); assert(lpf_context); float g; float cw; float a; const float k_2_pi = 6.283185307F; g = gain_hf; if (g < 0.01F) g = 0.01F; else if (gain_hf > 1.0F) g = 1.0F; cw = cosf((k_2_pi * lpf_reference_frequency) / target_frequency); a = 0.0F; if (g < 0.9999F) { a = (1.0F - (g * cw) - sqrtf((2.0F * g * (1.0F - cw)) - (g * g * (1.0F - (cw * cw))))) / (1.0F - g); } lpf_context->a = a; lpf_context->gain_hf = gain_hf; lpf_context->is_history_initialized = false; } static void lpf_update_samples( LpfContext* lpf_context, int sample_count, portable_samplepair_t* samples) { assert(lpf_context); assert(sample_count >= 0); assert(samples); int s; float a; portable_samplepair_t y; portable_samplepair_t* history; if (sample_count <= 0) return; a = lpf_context->a; history = lpf_context->history; if (!lpf_context->is_history_initialized) { lpf_context->is_history_initialized = true; for (s = 0; s < 2; ++s) { history[s].left = 0; history[s].right = 0; } } for (s = 0; s < sample_count; ++s) { /* Update left channel */ y.left = samples[s].left; y.left = (int)(y.left + a * (history[0].left - y.left)); history[0].left = y.left; y.left = (int)(y.left + a * (history[1].left - y.left)); history[1].left = y.left; /* Update right channel */ y.right = samples[s].right; y.right = (int)(y.right + a * (history[0].right - y.right)); history[0].right = y.right; y.right = (int)(y.right + a * (history[1].right - y.right)); history[1].right = y.right; /* Update sample */ samples[s] = y; } } /* End of low-pass filter stuff */ /* ============================ */ /* * Transfers a mixed "paint buffer" to * the SDL output buffer and places it * at the appropriate position. */ void SDL_TransferPaintBuffer(int endtime) { int i; int lpos; int ls_paintedtime; int out_idx; int count; int out_mask; int *p; int snd_linear_count; int step; int val; short *snd_out; unsigned char *pbuf; pbuf = sound.buffer; if (s_testsound->value) { int i; int count; /* write a fixed sine wave */ count = (endtime - paintedtime); for (i = 0; i < count; i++) { paintbuffer[i].left = paintbuffer[i].right = (int)((float)sin((paintedtime + i) * 0.1f) * 20000 * 256); } } if ((sound.samplebits == 16) && (sound.channels == 2)) { snd_p = (int *)paintbuffer; ls_paintedtime = paintedtime; while (ls_paintedtime < endtime) { lpos = ls_paintedtime & ((sound.samples >> 1) - 1); snd_out = (short *)pbuf + (lpos << 1); snd_linear_count = (sound.samples >> 1) - lpos; if (ls_paintedtime + snd_linear_count > endtime) { snd_linear_count = endtime - ls_paintedtime; } snd_linear_count <<= 1; for (i = 0; i < snd_linear_count; i += 2) { val = snd_p[i] >> 8; if (val > 0x7fff) { snd_out[i] = 0x7fff; } else if (val < -32768) { snd_out[i] = -32768; } else { snd_out[i] = val; } val = snd_p[i + 1] >> 8; if (val > 0x7fff) { snd_out[i + 1] = 0x7fff; } else if (val < -32768) { snd_out[i + 1] = -32768; } else { snd_out[i + 1] = val; } } snd_p += snd_linear_count; ls_paintedtime += (snd_linear_count >> 1); } } else { p = (int *)paintbuffer; count = (endtime - paintedtime) * sound.channels; out_mask = sound.samples - 1; out_idx = paintedtime * sound.channels & out_mask; step = 3 - sound.channels; if (sound.samplebits == 16) { short *out = (short *)pbuf; while (count--) { val = *p >> 8; p += step; if (val > 0x7fff) { val = 0x7fff; } else if (val < -32768) { val = -32768; } out[out_idx] = val; out_idx = (out_idx + 1) & out_mask; } } else if (sound.samplebits == 8) { unsigned char *out = (unsigned char *)pbuf; while (count--) { val = *p >> 8; p += step; if (val > 0x7fff) { val = 0x7fff; } else if (val < -32768) { val = -32768; } out[out_idx] = (val >> 8) + 128; out_idx = (out_idx + 1) & out_mask; } } } } /* * Mixes an 8 bit sample into a channel. */ void SDL_PaintChannelFrom8(channel_t *ch, sfxcache_t *sc, int count, int offset) { int data; int *lscale, *rscale; unsigned char *sfx; int i; portable_samplepair_t *samp; 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 = sc->data + ch->pos; samp = &paintbuffer[offset]; for (i = 0; i < count; i++, samp++) { data = sfx[i]; samp->left += lscale[data]; samp->right += rscale[data]; } ch->pos += count; } /* * Mixes an 16 bit sample into a channel */ void SDL_PaintChannelFrom16(channel_t *ch, sfxcache_t *sc, int count, int offset) { int data; int left, right; int leftvol, rightvol; signed short *sfx; int i; portable_samplepair_t *samp; leftvol = ch->leftvol * snd_vol; rightvol = ch->rightvol * snd_vol; sfx = (signed short *)sc->data + ch->pos; samp = &paintbuffer[offset]; for (i = 0; i < count; i++, samp++) { data = sfx[i]; left = (data * leftvol) >> 8; right = (data * rightvol) >> 8; samp->left += left; samp->right += right; } ch->pos += count; } /* * Mixes all pending sounds into * the available output channels. */ void SDL_PaintChannels(int endtime) { int i; int end; channel_t *ch; sfxcache_t *sc; int ltime, count; playsound_t *ps; snd_vol = (int)(s_volume->value * 256); while (paintedtime < endtime) { /* if paintbuffer is smaller than SDL buffer */ end = endtime; if (endtime - paintedtime > SDL_PAINTBUFFER_SIZE) { end = paintedtime + SDL_PAINTBUFFER_SIZE; } /* start any playsounds */ for ( ; ; ) { ps = s_pendingplays.next; if (ps == NULL) { break; } if (ps == &s_pendingplays) { break; /* no more pending sounds */ } if (ps->begin <= paintedtime) { S_IssuePlaysound(ps); continue; } if (ps->begin < end) { end = ps->begin; /* stop here */ } break; } memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t)); /* paint in the channels. */ ch = channels; for (i = 0; i < s_numchannels; i++, ch++) { ltime = paintedtime; while (ltime < end) { if (!ch->sfx || (!ch->leftvol && !ch->rightvol)) { break; } /* max painting is to the end of the buffer */ count = end - ltime; /* might be stopped by running out of data */ if (ch->end - ltime < count) { count = ch->end - ltime; } sc = S_LoadSound(ch->sfx); if (!sc) { break; } if ((count > 0) && ch->sfx) { if (sc->width == 1) { SDL_PaintChannelFrom8(ch, sc, count, ltime - paintedtime); } else { SDL_PaintChannelFrom16(ch, sc, count, ltime - paintedtime); } ltime += count; } /* if at end of loop, restart */ if (ltime >= ch->end) { if (ch->autosound) { /* autolooping sounds always go back to start */ ch->pos = 0; ch->end = ltime + sc->length; } else if (sc->loopstart >= 0) { ch->pos = sc->loopstart; ch->end = ltime + sc->length - ch->pos; } else { /* channel just stopped */ ch->sfx = NULL; } } } } if (lpf_is_enabled && snd_is_underwater) lpf_update_samples(&lpf_context, end - paintedtime, paintbuffer); else lpf_context.is_history_initialized = false; if (s_rawend >= paintedtime) { /* add from the streaming sound source */ int s; int stop; stop = (end < s_rawend) ? end : s_rawend; for (i = paintedtime; i < stop; i++) { s = i & (MAX_RAW_SAMPLES - 1); paintbuffer[i - paintedtime].left += s_rawsamples[s].left; paintbuffer[i - paintedtime].right += s_rawsamples[s].right; } } /* transfer out according to SDL format */ SDL_TransferPaintBuffer(end); paintedtime = end; } } /* ------------------------------------------------------------------ */ /* * Calculates when a sound * must be started. */ int SDL_DriftBeginofs(float timeofs) { int start = (int)(cl.frame.servertime * 0.001f * sound.speed + beginofs); if (start < paintedtime) { start = paintedtime; beginofs = (int)(start - (cl.frame.servertime * 0.001f * sound.speed)); } else if (start > paintedtime + 0.3f * sound.speed) { start = (int)(paintedtime + 0.1f * sound.speed); beginofs = (int)(start - (cl.frame.servertime * 0.001f * sound.speed)); } else { beginofs -= 10; } return timeofs ? start + timeofs * sound.speed : paintedtime; } /* * Spatialize a sound effect based on it's origin. */ void SDL_SpatializeOrigin(vec3_t origin, float master_vol, float dist_mult, int *left_vol, int *right_vol) { vec_t dot; vec_t dist; vec_t lscale, rscale, scale; vec3_t source_vec; if (cls.state != ca_active) { *left_vol = *right_vol = 255; return; } /* Calculate stereo seperation and distance attenuation */ VectorSubtract(origin, listener_origin, source_vec); dist = VectorNormalize(source_vec); dist -= SDL_FULLVOLUME; if (dist < 0) { dist = 0; /* Close enough to be at full volume */ } dist *= dist_mult; dot = DotProduct(listener_right, source_vec); if ((sound.channels == 1) || !dist_mult) { rscale = 1.0f; lscale = 1.0f; } else { rscale = 0.5f * (1.0f + dot); lscale = 0.5f * (1.0f - dot); } /* Add in distance effect */ scale = (1.0f - dist) * rscale; *right_vol = (int)(master_vol * scale); if (*right_vol < 0) { *right_vol = 0; } scale = (1.0 - dist) * lscale; *left_vol = (int)(master_vol * scale); if (*left_vol < 0) { *left_vol = 0; } } /* * Spatializes a channel. */ void SDL_Spatialize(channel_t *ch) { vec3_t origin; /* Anything coming from the view entity will always be full volume */ if (ch->entnum == cl.playernum + 1) { ch->leftvol = ch->master_vol; ch->rightvol = ch->master_vol; return; } if (ch->fixed_origin) { VectorCopy(ch->origin, origin); } else { CL_GetEntitySoundOrigin(ch->entnum, origin); } SDL_SpatializeOrigin(origin, (float)ch->master_vol, ch->dist_mult, &ch->leftvol, &ch->rightvol); } /* * Entities with a "sound" field will generated looped sounds * that are automatically started, stopped, and merged together * as the entities are sent to the client */ void SDL_AddLoopSounds(void) { int i, j; int sounds[MAX_EDICTS]; int left, right, left_total, right_total; channel_t *ch; sfx_t *sfx; sfxcache_t *sc; int num; entity_state_t *ent; vec3_t origin; if (cl_paused->value) { return; } if (cls.state != ca_active) { return; } if (!cl.sound_prepped || !s_ambient->value) { return; } memset(&sounds, 0, sizeof(int) * MAX_EDICTS); S_BuildSoundList(sounds); for (i = 0; i < cl.frame.num_entities; i++) { if (!sounds[i]) { continue; } sfx = cl.sound_precache[sounds[i]]; if (!sfx) { continue; /* bad sound effect */ } sc = sfx->cache; if (!sc) { continue; } num = (cl.frame.parse_entities + i) & (MAX_PARSE_ENTITIES - 1); ent = &cl_parse_entities[num]; CL_GetEntitySoundOrigin(ent->number, origin); /* find the total contribution of all sounds of this type */ SDL_SpatializeOrigin(ent->origin, 255.0f, SDL_LOOPATTENUATE, &left_total, &right_total); for (j = i + 1; j < cl.frame.num_entities; j++) { if (sounds[j] != sounds[i]) { continue; } sounds[j] = 0; /* don't check this again later */ num = (cl.frame.parse_entities + j) & (MAX_PARSE_ENTITIES - 1); ent = &cl_parse_entities[num]; SDL_SpatializeOrigin(ent->origin, 255.0f, SDL_LOOPATTENUATE, &left, &right); left_total += left; right_total += right; } if ((left_total == 0) && (right_total == 0)) { continue; /* not audible */ } /* allocate a channel */ ch = S_PickChannel(0, 0); if (!ch) { return; } if (left_total > 255) { left_total = 255; } if (right_total > 255) { right_total = 255; } ch->leftvol = left_total; ch->rightvol = right_total; ch->autosound = true; /* remove next frame */ ch->sfx = sfx; /* Sometimes, the sc->length argument can become 0, and in that case we get a SIGFPE in the next modulo operation. The workaround checks for this situation and in that case, sets the pos and end parameters to 0. */ if (sc->length == 0) { ch->pos = 0; ch->end = 0; } else { ch->pos = paintedtime % sc->length; ch->end = paintedtime + sc->length - ch->pos; } } } /* * Clears the playback buffer so * that all playback stops. */ void SDL_ClearBuffer(void) { int clear; int i; unsigned char *ptr = sound.buffer; if (!sound_started) { return; } s_rawend = 0; if (sound.samplebits == 8) { clear = 0x80; } else { clear = 0; } SDL_LockAudio(); if (sound.buffer) { i = sound.samples * sound.samplebits / 8; while (i--) { *ptr = clear; ptr++; } } SDL_UnlockAudio(); } /* * Calculates the absolute timecode * of current playback. */ void SDL_UpdateSoundtime(void) { static int buffers; static int oldsamplepos; int fullsamples; fullsamples = sound.samples / sound.channels; /* it is possible to miscount buffers if it has wrapped twice between calls to S_Update. Oh well. This a hack around that. */ if (playpos < oldsamplepos) { buffers++; /* buffer wrapped */ if (paintedtime > 0x40000000) { /* time to chop things off to avoid 32 bit limits */ buffers = 0; paintedtime = fullsamples; S_StopAllSounds(); } } oldsamplepos = playpos; soundtime = buffers * fullsamples + playpos / sound.channels; } /* * Updates the volume scale table * based on current volume setting. */ void SDL_UpdateScaletable(void) { int i, j; int scale; if (s_volume->value > 2.0f) { Cvar_Set("s_volume", "2"); } else if (s_volume->value < 0) { Cvar_Set("s_volume", "0"); } s_volume->modified = false; for (i = 0; i < 32; i++) { scale = (int)(i * 8 * 256 * s_volume->value); for (j = 0; j < 256; j++) { snd_scaletable[i][j] = ((j < 128) ? j : j - 0xff) * scale; } } } /* * Saves a sound sample into cache. If * necessary endianess convertions are * performed. */ qboolean SDL_Cache(sfx_t *sfx, wavinfo_t *info, byte *data) { float stepscale; int i; int len; int sample; int srcsample; sfxcache_t *sc; unsigned int samplefrac = 0; stepscale = (float)info->rate / sound.speed; len = (int)(info->samples / stepscale); if ((info->samples == 0) || (len == 0)) { Com_Printf("WARNING: Zero length sound encountered: %s\n", sfx->name); return false; } len = len * info->width * info->channels; sc = sfx->cache = Z_Malloc(len + sizeof(sfxcache_t)); if (!sc) { return false; } sc->loopstart = info->loopstart; sc->stereo = 0; sc->length = (int)(info->samples / stepscale); sc->speed = sound.speed; if ((int)(info->samples / stepscale) == 0) { Com_Printf("ResampleSfx: Invalid sound file '%s' (zero length)\n", sfx->name); Z_Free(sfx->cache); sfx->cache = NULL; return false; } if (sc->loopstart != -1) { sc->loopstart = (int)(sc->loopstart / stepscale); } if (s_loadas8bit->value) { sc->width = 1; } else { sc->width = info->width; } /* resample / decimate to the current source rate */ for (i = 0; i < (int)(info->samples / stepscale); i++) { srcsample = samplefrac >> 8; samplefrac += (int)(stepscale * 256); if (info->width == 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; } } return true; } /* * Playback of "raw samples", e.g. samples * without an origin entity. Used for music * and cinematic playback. */ void SDL_RawSamples(int samples, int rate, int width, int channels, byte *data, float volume) { float scale; int dst; int i; int src; int intVolume; scale = (float)rate / sound.speed; intVolume = (int)(256 * volume); if ((channels == 2) && (width == 2)) { for (i = 0; ; i++) { src = (int)(i * scale); if (src >= samples) { break; } dst = s_rawend & (MAX_RAW_SAMPLES - 1); s_rawend++; s_rawsamples[dst].left = ((short *)data)[src * 2] * intVolume; s_rawsamples[dst].right = ((short *)data)[src * 2 + 1] * intVolume; } } else if ((channels == 1) && (width == 2)) { for (i = 0; ; i++) { src = (int)(i * scale); if (src >= samples) { break; } dst = s_rawend & (MAX_RAW_SAMPLES - 1); s_rawend++; s_rawsamples[dst].left = ((short *)data)[src] * intVolume; s_rawsamples[dst].right = ((short *)data)[src] * intVolume; } } else if ((channels == 2) && (width == 1)) { intVolume *= 256; for (i = 0; ; i++) { src = (int)(i * scale); if (src >= samples) { break; } dst = s_rawend & (MAX_RAW_SAMPLES - 1); s_rawend++; s_rawsamples[dst].left = (((byte *)data)[src * 2] - 128) * intVolume; s_rawsamples[dst].right = (((byte *)data)[src * 2 + 1] - 128) * intVolume; } } else if ((channels == 1) && (width == 1)) { intVolume *= 256; for (i = 0; ; i++) { src = (int)(i * scale); if (src >= samples) { break; } dst = s_rawend & (MAX_RAW_SAMPLES - 1); s_rawend++; s_rawsamples[dst].left = (((byte *)data)[src] - 128) * intVolume; s_rawsamples[dst].right = (((byte *)data)[src] - 128) * intVolume; } } } /* * Runs every frame, handles all necessary * sound calculations and fills the play- * back buffer. */ void SDL_Update(void) { channel_t *ch; int i; int samps; int total; unsigned int endtime; if (s_underwater->modified) { s_underwater->modified = false; lpf_is_enabled = ((int)s_underwater->value != 0); } if (s_underwater_gain_hf->modified) { s_underwater_gain_hf->modified = false; lpf_initialize( &lpf_context, s_underwater_gain_hf->value, backend->speed); } /* if the loading plaque is up, clear everything out to make sure we aren't looping a dirty SDL buffer while loading */ if (cls.disable_screen) { SDL_ClearBuffer(); return; } /* rebuild scale tables if volume is modified */ if (s_volume->modified) { SDL_UpdateScaletable(); } /* update spatialization for dynamic sounds */ ch = channels; for (i = 0; i < s_numchannels; i++, ch++) { if (!ch->sfx) { continue; } if (ch->autosound) { /* autosounds are regenerated fresh each frame */ memset(ch, 0, sizeof(*ch)); continue; } /* respatialize channel */ SDL_Spatialize(ch); if (!ch->leftvol && !ch->rightvol) { memset(ch, 0, sizeof(*ch)); continue; } } /* add loopsounds */ SDL_AddLoopSounds(); /* debugging output */ if (s_show->value) { total = 0; ch = channels; for (i = 0; i < s_numchannels; i++, ch++) { if (ch->sfx && (ch->leftvol || ch->rightvol)) { Com_Printf("%3i %3i %s\n", ch->leftvol, ch->rightvol, ch->sfx->name); total++; } } Com_Printf("----(%i)---- painted: %i\n", total, paintedtime); } #ifdef OGG /* stream music */ OGG_Stream(); #endif if (!sound.buffer) { return; } /* Mix the samples */ SDL_LockAudio(); /* Updates SDL time */ SDL_UpdateSoundtime(); if (!soundtime) { return; } /* check to make sure that we haven't overshot */ if (paintedtime < soundtime) { Com_DPrintf("S_Update_ : overflow\n"); paintedtime = soundtime; } /* mix ahead of current position */ endtime = (int)(soundtime + s_mixahead->value * sound.speed); /* mix to an even submission block size */ endtime = (endtime + sound.submission_chunk - 1) & ~(sound.submission_chunk - 1); samps = sound.samples >> (sound.channels - 1); if (endtime - soundtime > samps) { endtime = soundtime + samps; } SDL_PaintChannels(endtime); SDL_UnlockAudio(); } /* ------------------------------------------------------------------ */ /* * Gives information over user * defineable variables */ void SDL_SoundInfo(void) { Com_Printf("%5d stereo\n", sound.channels - 1); Com_Printf("%5d samples\n", sound.samples); Com_Printf("%5d samplepos\n", sound.samplepos); Com_Printf("%5d samplebits\n", sound.samplebits); Com_Printf("%5d submission_chunk\n", sound.submission_chunk); Com_Printf("%5d speed\n", sound.speed); Com_Printf("%p sound buffer\n", sound.buffer); } /* * Callback funktion for SDL. Writes * sound data to SDL when requested. */ static void SDL_Callback(void *data, Uint8 *stream, int length) { int length1; int length2; int pos = (playpos * (backend->samplebits / 8)); if (pos >= samplesize) { playpos = pos = 0; } /* This can't happen! */ if (!snd_inited) { memset(stream, '\0', length); return; } int tobufferend = samplesize - pos; if (length > tobufferend) { length1 = tobufferend; length2 = length - length1; } else { length1= length; length2 = 0; } memcpy(stream, backend->buffer + pos, length1); /* Set new position */ if (length2 <= 0) { playpos += (length1 / (backend->samplebits / 8)); } else { memcpy(stream + length1, backend->buffer, length2); playpos = (length2 / (backend->samplebits / 8)); } if (playpos >= samplesize) { playpos = 0; } } /* * Initializes the SDL sound * backend and sets up SDL. */ qboolean SDL_BackendInit(void) { char reqdriver[128]; SDL_AudioSpec desired; SDL_AudioSpec obtained; int tmp, val; /* This should never happen, but this is Quake 2 ... */ if (snd_inited) { return 1; } int sndbits = (Cvar_Get("sndbits", "16", CVAR_ARCHIVE))->value; int sndfreq = (Cvar_Get("s_khz", "44", CVAR_ARCHIVE))->value; int sndchans = (Cvar_Get("sndchannels", "2", CVAR_ARCHIVE))->value; #ifdef _WIN32 #if SDL_VERSION_ATLEAST(2, 0, 0) s_sdldriver = (Cvar_Get("s_sdldriver", "directsound", CVAR_ARCHIVE)); #else s_sdldriver = (Cvar_Get("s_sdldriver", "dsound", CVAR_ARCHIVE)); #endif #elif __linux__ s_sdldriver = (Cvar_Get("s_sdldriver", "alsa", CVAR_ARCHIVE)); #elif __APPLE__ s_sdldriver = (Cvar_Get("s_sdldriver", "CoreAudio", CVAR_ARCHIVE)); #else s_sdldriver = (Cvar_Get("s_sdldriver", "dsp", CVAR_ARCHIVE)); #endif snprintf(reqdriver, sizeof(reqdriver), "%s=%s", "SDL_AUDIODRIVER", s_sdldriver->string); putenv(reqdriver); Com_Printf("Starting SDL audio callback.\n"); if (!SDL_WasInit(SDL_INIT_AUDIO)) { if (SDL_Init(SDL_INIT_AUDIO) == -1) { Com_Printf ("Couldn't init SDL audio: %s.\n", SDL_GetError ()); return 0; } } #if SDL_VERSION_ATLEAST(2, 0, 0) const char* drivername = SDL_GetCurrentAudioDriver(); if(drivername == NULL) { drivername = "(UNKNOWN)"; } #else char drivername[128]; if (SDL_AudioDriverName(drivername, sizeof(drivername)) == NULL) { strcpy(drivername, "(UNKNOWN)"); } #endif Com_Printf("SDL audio driver is \"%s\".\n", drivername); memset(&desired, '\0', sizeof(desired)); memset(&obtained, '\0', sizeof(obtained)); /* Users are stupid */ if ((sndbits != 16) && (sndbits != 8)) { sndbits = 16; } if (sndfreq == 48) { desired.freq = 48000; } else if (sndfreq == 44) { desired.freq = 44100; } else if (sndfreq == 22) { desired.freq = 22050; } else if (sndfreq == 11) { desired.freq = 11025; } desired.format = ((sndbits == 16) ? AUDIO_S16SYS : AUDIO_U8); if (desired.freq <= 11025) { desired.samples = 256; } else if (desired.freq <= 22050) { desired.samples = 512; } else if (desired.freq <= 44100) { desired.samples = 1024; } else { desired.samples = 2048; } desired.channels = sndchans; desired.callback = SDL_Callback; /* Okay, let's try our luck */ if (SDL_OpenAudio(&desired, &obtained) == -1) { Com_Printf("SDL_OpenAudio() failed: %s\n", SDL_GetError()); SDL_QuitSubSystem(SDL_INIT_AUDIO); return 0; } /* This points to the frontend */ backend = &sound; playpos = 0; backend->samplebits = obtained.format & 0xFF; backend->channels = obtained.channels; tmp = (obtained.samples * obtained.channels) * 10; if (tmp & (tmp - 1)) { /* make it a power of two */ val = 1; while (val < tmp) val <<= 1; tmp = val; } backend->samples = tmp; backend->submission_chunk = 1; backend->speed = obtained.freq; samplesize = (backend->samples * (backend->samplebits / 8)); backend->buffer = calloc(1, samplesize); s_numchannels = MAX_CHANNELS; s_underwater->modified = true; s_underwater_gain_hf->modified = true; lpf_initialize(&lpf_context, lpf_default_gain_hf, backend->speed); SDL_UpdateScaletable(); SDL_PauseAudio(0); Com_Printf("SDL audio initialized.\n"); soundtime = 0; snd_inited = 1; return 1; } /* * Shuts the SDL backend down. */ void SDL_BackendShutdown(void) { Com_Printf("Closing SDL audio device...\n"); SDL_PauseAudio(1); SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); free(backend->buffer); backend->buffer = NULL; playpos = samplesize = 0; snd_inited = 0; Com_Printf("SDL audio device shut down.\n"); } yquake2-QUAKE2_5_32/src/backends/sdl/icon/0000755000175000017500000000000012615162034020672 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/backends/sdl/icon/q2icon64.h0000644000175000017500000010646612615162034022425 0ustar greffrathgreffrathstatic const struct { unsigned int width; unsigned int height; unsigned int bytes_per_pixel; /* 3:RGB, 4:RGBA */ unsigned char pixel_data[64 * 64 * 4 + 1]; } q2icon64 = { 64, 64, 4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\4\0\0\0\0\0\0\0_\0\0\0\256\0\0\0b\0\0\0\3\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0/\0\0\0\377\0\0\0" "y\0\0\0\0\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0\35\0\0\0\361\0\0\0\377\0\0\0w\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\6\0\0\0\302\0\0\0\377\0\0\0\66\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0k\0\0\0\377\0\0\0\237" "\0\0\0\0\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0a\0\0\0\377\0" "\0\0\242\0\0\0\0\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\0\0\0\0\337" "\0\0\0\377\0\0\0\66\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\4\0\0\0 \0\0\0\374\0\0\0\347\0\0\0\0\0\0\0\4\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\3\0\0\0\0\0\0\0\377\0\0\0\373\0\0\0\f\0\0\0\a\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0\0\0\0\0\354" "\0\0\0\371\0\0\0\2\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\370\0\0\0\377\0\0\0\f\0\0\0\6\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\4\0\0\0\0\0\0\0\375\0\0\0\360\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\304\0\0\0" "\377\0\0\0\65\0\0\0\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0)\0\0\0" "\377\0\0\0\313\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\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\4\0\0\0Z\0\0\0\377\0\0\0\237\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\1\0\0\0\200\0\0\0\376\0\0\0v\0\0\0\3\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\3\0\0\0\34\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\357\0" "\0\0\377\0\0\0\21\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\320\0\0\0\367" "\0\0\0\203\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\4\0\0\09\0\0\0\377\0\0\0\322\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\5\0\0\0L\0\0\0\377\0\0\0\377\0\0\0\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\255\0\0\0\377" "\0\0\08\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\307\0\0\0\367\0\0\0\255\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\5\0\0\0f\0\0\0\373\0\0\0\313\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0*\0\0\0\377\0\0\0" "\377\0\0\0\?\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\r\0\0\0\377\0\0\0\377\0\0\0" "\33\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\3\0\0\0\211\0\0\0\372\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\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\355\0\0\0\370\0\0\0\240\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\356\0\0\0\372\0\0\0\325" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\257\0\0\0\371\0\0\0\371" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0" "\0\25\0\0\0\377\0\0\0\373\0\0\0\261\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\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\1\0\0\0\225\0\0\0\372\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0q\0\0\0\374\0\0\0\373\0\0\0{\0\0\0" "\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\6\0\0\0y\0\0\0\372\0\0\0\377\0\0\0*\0" "\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\260\0\0" "\0\373\0\0\0\377\0\0\0a\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\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\5\0\0\0Z\0" "\0\0\377\0\0\0\372\0\0\0\226\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\324\0\0\0\374\0\0\0\372\0\0\0\177\0\0\0\6\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\1\0\0\0\21\0\0\0\337\0\0\0\372\0\0\0\310\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\320\0\0\0\374" "\0\0\0\371\0\0\0\206\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\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\4\0\0\0R\0\0" "\0\377\0\0\0\375\0\0\0\341\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\372\0\0\0\222\0\0\0\2\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\6\0\0\0u\0\0\0\373\0\0\0\377\0\0\0\377\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\375\0\0\0\376\0" "\0\0\374\0\0\0\217\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\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\260\0" "\0\0\373\0\0\0\374\0\0\0\327\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\334\0\0\0\374\0\0\0\370\0\0\0r\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\355\0\0\0\375\0\0\0\376\0\0\0\377\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\303\0\0\0\373" "\0\0\0\377\0\0\0\377\0\0\0\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\377" "\0\0\0\377\0\0\0\375\0\0\0\370\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\246\0\0\0\372\0\0\0\377\0\0\0\377\0\0\0j\0\0\0" "\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\5\0\0\0\\\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\27\0\0\0" "\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0\204\0\0\0\372" "\0\0\0\377\0\0\0\376\0\0\0\373\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\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\326\0\0\0" "\374\0\0\0\377\0\0\0\377\0\0\0W\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\4\0\0\0R\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" "R\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\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\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\2\0\0\0\26\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\63\0\0" "\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\b\0\0\0\377" "\0\0\0\377\0\0\0\377\0\0\0\375\0\0\0\351\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\b\0\0\0\6\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\f\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\206\0\0\0\372\0" "\0\0\377\0\0\0\376\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\337\0\0\0\373\0\0\0\377\0\0\0\377" "\0\0\0\377\0\0\0M\0\0\0\6\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\1\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\370\0\0\0\355\0\0\0\354\0" "\0\0\370\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\375\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0$\0\0\0\2\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\6\0\0\0\67\0\0\0\361\0\0\0\376\0\0" "\0\377\0\0\0\372\0\0\0\230\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\65\0\0\0\377\0\0\0\373\0\0\0\377\0" "\0\0\376\0\0\0\377\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\1\0\0\0\0\0\0\0\61\0\0\0\337\0\0\0\373\0\0\0\375\0\0" "\0\376\0\0\0\376\0\0\0\365\0\0\0\335\0\0\0\375\0\0\0\376\0\0\0\376\0\0" "\0\374\0\0\0\341\0\0\0\0\0\0\0\0\0\0\0\2\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\3\0\0\0\0\0\0\0\376\0\0\0\375\0\0\0\377\0\0\0" "\377\0\0\0\377\0\0\0\27\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0\63\0\0\0\377\0\0\0\377\0\0\0\377" "\0\0\0\372\0\0\0\312\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\b\0\0\0\205\0\0\0\372\0\0\0\377\0\0\0\377" "\0\0\0\373\0\0\0\206\0\0\0H\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" "\0M\0\0\0\a\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\367\0\0\0\372\0\0\0\377\0\0\0\377\0\0\0\373\0\0\0\307" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\3\0\0\0\63\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" "\0\0\374\0\0\0\260\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\4\0\0\0P\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" "\0\62\0\0\0\5\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0&\0\0\0\3\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\317" "\0\0\0\371\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0&\0\0\0\3\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\316\0\0\0\371\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" "\377\0\0\0\272\0\0\0\0\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\3\0\0\0\62\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\22\0\0" "\0\0\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0$\0\0\0\3\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\0\0\0\0\314\0\0\0\373\0\0\0" "\377\0\0\0\377\0\0\0\377\0\0\0\371\0\0\0\266\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\0\0\0\377\0\0\0\376\0\0\0\377\0\0\0\377\0\0\0\377\0" "\0\0\371\0\0\0\373\0\0\0\4\0\0\0\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\3\0\0\0&\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\27\0\0\0\0\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\25\0\0\0\2\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0\0\0\0\0\347\0\0\0\372\0\0\0\377\0\0\0\377" "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\6\0\0\0#\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" "\0\0\373\0\0\0\377\0\0\0+\0\0\0\4\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0" "\"\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\32\0\0\0\0\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\0" "\0\0\0\0\0\0=\0\0\0\377\0\0\0\372\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" "\0\0\0\377\0\0\0H\0\0\0\6\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\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\4\0\0\0U\0\0\0\377\0\0\0\377\0\0\0\376\0\0\0\377\0\0\0\377\0\0\0\375" "\0\0\0\376\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\3\0\0\0\'\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0" "\377\0\0\0\377\0\0\0\30\0\0\0\2\0\0\0\1\0\0\0\2\0\0\0\0\0\0\0\263\0\0\0" "\377\0\0\0\376\0\0\0\377\0\0\0\377\0\0\0\376\0\0\0\377\0\0\0\377\0\0\0" "\201\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0`\0\0\0\377\0\0\0\351\0\0\0\377\0\0\0\377\0\0\0\377\0\0" "\0\375\0\0\0\377\0\0\0\377\0\0\0\250\0\0\0\22\0\0\0\4\0\0\0\3\0\0\0\377" "\0\0\0\376\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0" "\0\377\0\0\0\377\0\0\0#\0\0\0\0\0\0\0\f\0\0\0\220\0\0\0\377\0\0\0\372\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\376\0\0\0\356\0\0\0\377\0\0\0\237\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\1\0\0\0\b\0\0\0\246\0\0\0\372\0\0\0\377\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\372\0\0\0\377\0\0\0\0\0\0\0]\0\0\0\377" "\0\0\0\377\0\0\0\376\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0" "\0\377\0\0\0\377\0\0\0\241\0\0\0\337\0\0\0\377\0\0\0\373\0\0\0\377\0\0" "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0|\0\0\0\267\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\16\0\0\0\377\0\0\0\375\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\370\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\375\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\372\0\0\0\375\0\0\0\377\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0;\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\65\0\0\0\372\0\0" "\0\372\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\376\0\0" "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0" "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\372\0\0\0\377\0\0\0r\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0\0" "\0\0\0\226\0\0\0\377\0\0\0\372\0\0\0\373\0\0\0\373\0\0\0\377\0\0\0\377" "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" "\0\0\0\377\0\0\0\377\0\0\0\374\0\0\0\377\0\0\0\313\0\0\0\r\0\0\0\4\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\27\0\0\0\267\0\0\0\271\0\0\0\263\0\0\0\377\0\0\0\377" "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\376" "\0\0\0\372\0\0\0\377\0\0\0\335\0\0\09\0\0\0\0\0\0\0\2\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\3\0\0\0\0\0\0\0\0\0\0\0\24\0\0\0\377\0\0\0\377\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\372\0\0\0\377\0\0\0\377\0\0\0\234\0" "\0\0\"\0\0\0\0\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\35\0\0\0\232\0\0\0\377\0\0\0\377" "\0\0\0\377\0\0\0\375\0\0\0\377\0\0\0\377\0\0\0\376\0\0\0\377\0\0\0\377" "\0\0\0\377\0\0\0\241\0\0\08\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\a\0\0\0Z\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\20" "\0\0\0\34\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0A\0\0\0\a\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0T\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\26\0\0\0\'\0\0\0\377\0\0\0\377\0\0\0" "\377\0\0\0\377\0\0\0H\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\4\0\0\0F\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\24" "\0\0\0\b\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\09\0\0\0\3\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\37\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\350\0\0\0\375\0\0\0" "\377\0\0\0\377\0\0\0+\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\1\0\0\0\20\0\0\0\361\0\0\0\376\0\0\0\376\0\0\0\377\0\0\0" "\0\0\0\0\0\0\0\0\377\0\0\0\376\0\0\0\377\0\0\0\377\0\0\0-\0\0\0\3\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\61\0\0\0\377" "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0(\0\0\0\377\0\0\0\377\0\0\0" "\377\0\0\0\377\0\0\0+\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\2\0\0\0\35\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0" "\0\0\0\0\0\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\27\0\0\0\2\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377" "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\376\0\0" "\0\377\0\0\0\377\0\0\0\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\354\0\0\0\375\0\0\0\375\0\0\0\344\0" "\0\0\0\0\0\0\0\0\0\0\350\0\0\0\375\0\0\0\376\0\0\0\370\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\312\0\0\0\374\0\0\0\372\0\0\0\236\0\0\0\0\0\0\0\0\0\0\0\251\0\0\0\373" "\0\0\0\374\0\0\0\307\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\241\0\0\0\372\0\0\0\377\0\0\0Y\0" "\0\0\5\0\0\0\6\0\0\0x\0\0\0\373\0\0\0\372\0\0\0\225\0\0\0\2\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\6\0\0\0t\0" "\0\0\373\0\0\0\377\0\0\0*\0\0\0\3\0\0\0\4\0\0\0O\0\0\0\377\0\0\0\376\0" "\0\0h\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\4\0\0\0K\0\0\0\377\0\0\0\377\0\0\0\21\0\0\0\2\0\0\0\3\0" "\0\0)\0\0\0\377\0\0\0\377\0\0\0\62\0\0\0\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\'\0\0\0\377\0\0\0" "\377\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\6\0\0\0\377\0\0\0\377\0\0\0\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\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\2\0\0\0\r\0\0\0\377\0\0\0\350\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\377\0\0\0\274\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\372\0\0\0\332\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\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\336\0\0\0\244\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\316\0\0" "\0\271\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\240\0\0\0\207\0\0\0\4\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\234\0\0\0\232\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\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\5\0\0\0" "~\0\0\0Z\0\0\0\5\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0g\0\0\0s\0\0\0\5\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\3\0\0\0\?\0\0\0\33\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\20" "\0\0\0(\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0" }; yquake2-QUAKE2_5_32/src/backends/sdl/icon/q2icon.xbm0000644000175000017500000000157312615162034022603 0ustar greffrathgreffrath#define q2icon_width 32 #define q2icon_height 32 static unsigned char q2icon_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x80, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x0c, 0x70, 0x00, 0x00, 0x0e, 0x70, 0xf0, 0x0f, 0x0e, 0xe0, 0xe0, 0x07, 0x07, 0xe0, 0x61, 0x86, 0x07, 0xc0, 0x63, 0xc6, 0x03, 0x80, 0x7f, 0xfe, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00 }; yquake2-QUAKE2_5_32/src/backends/sdl/refresh.c0000644000175000017500000004272212615162034021553 0ustar greffrathgreffrath/* * Copyright (C) 2010 Yamagi Burmeister * Copyright (C) 1997-2001 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. * * ---------------------------------------------------------------------- * * CalculateGammaRamp() is derived from SDL2's SDL_CalculateGammaRamp() * (C) 1997-2013 Sam Lantinga * Published under zlib License: http://www.libsdl.org/license.php * * ======================================================================= * * This file implements an OpenGL context and window handling through * SDL. The code is complicated by supporting the fairly different SDL * 1.2 and SDL 2 APIs, each with hardware gamma or software gamma by * RANDR. * * ======================================================================= */ #include "../../client/refresh/header/local.h" #if defined(__APPLE__) #include #else #include #endif #ifdef SDL2 #include #else // SDL1.2 #include #endif //SDL2 /* X.org stuff */ #ifdef X11GAMMA #include #include #include #include #include #include #endif #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_Window* window = NULL; SDL_GLContext context = NULL; #else SDL_Surface* window = NULL; #endif qboolean have_stencil = false; #ifdef X11GAMMA XRRCrtcGamma** gammaRamps = NULL; int noGammaRamps = 0; #endif #if SDL_VERSION_ATLEAST(2, 0, 0) // some compatibility defines #define SDL_SRCCOLORKEY SDL_TRUE #define SDL_FULLSCREEN SDL_WINDOW_FULLSCREEN #define SDL_OPENGL SDL_WINDOW_OPENGL #endif /* * Initialzes the SDL OpenGL context */ int GLimp_Init(void) { if (!SDL_WasInit(SDL_INIT_VIDEO)) { if (SDL_Init(SDL_INIT_VIDEO) == -1) { VID_Printf(PRINT_ALL, "Couldn't init SDL video: %s.\n", SDL_GetError()); return false; } #if SDL_VERSION_ATLEAST(2, 0, 0) const char* driverName = SDL_GetCurrentVideoDriver(); #else char driverName[64]; SDL_VideoDriverName(driverName, sizeof(driverName)); #endif VID_Printf(PRINT_ALL, "SDL video driver is \"%s\".\n", driverName); } return true; } /* * Returns the adress of a GL function */ void * GLimp_GetProcAddress (const char* proc) { return SDL_GL_GetProcAddress ( proc ); } /* * Sets the window icon */ #if SDL_VERSION_ATLEAST(2, 0, 0) /* The 64x64 32bit window icon */ #include "icon/q2icon64.h" static void SetSDLIcon() { /* these masks are needed to tell SDL_CreateRGBSurface(From) to assume the data it gets is byte-wise RGB(A) data */ Uint32 rmask, gmask, bmask, amask; #if SDL_BYTEORDER == SDL_BIG_ENDIAN int shift = (q2icon64.bytes_per_pixel == 3) ? 8 : 0; rmask = 0xff000000 >> shift; gmask = 0x00ff0000 >> shift; bmask = 0x0000ff00 >> shift; amask = 0x000000ff >> shift; #else /* little endian, like x86 */ rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = (q2icon64.bytes_per_pixel == 3) ? 0 : 0xff000000; #endif SDL_Surface* icon = SDL_CreateRGBSurfaceFrom((void*)q2icon64.pixel_data, q2icon64.width, q2icon64.height, q2icon64.bytes_per_pixel*8, q2icon64.bytes_per_pixel*q2icon64.width, rmask, gmask, bmask, amask); SDL_SetWindowIcon(window, icon); SDL_FreeSurface(icon); } #else /* SDL 1.2 */ /* The window icon */ #include "icon/q2icon.xbm" static void SetSDLIcon() { SDL_Surface *icon; SDL_Color transColor, solidColor; Uint8 *ptr; int i; int mask; icon = SDL_CreateRGBSurface(SDL_SWSURFACE, q2icon_width, q2icon_height, 8, 0, 0, 0, 0); if (icon == NULL) { return; } SDL_SetColorKey(icon, SDL_SRCCOLORKEY, 0); transColor.r = 255; transColor.g = 255; transColor.b = 255; solidColor.r = 0; solidColor.g = 16; solidColor.b = 0; SDL_SetColors(icon, &transColor, 0, 1); SDL_SetColors(icon, &solidColor, 1, 1); ptr = (Uint8 *)icon->pixels; for (i = 0; i < sizeof(q2icon_bits); i++) { for (mask = 1; mask != 0x100; mask <<= 1) { *ptr = (q2icon_bits[i] & mask) ? 1 : 0; ptr++; } } SDL_WM_SetIcon(icon, NULL); SDL_FreeSurface(icon); } #endif /* SDL 1.2 */ /* * from SDL2 SDL_CalculateGammaRamp, adjusted for arbitrary ramp sizes * because xrandr seems to support ramp sizes != 256 (in theory at least) */ void CalculateGammaRamp(float gamma, Uint16* ramp, int len) { int i; /* Input validation */ if (gamma < 0.0f ) { return; } if (ramp == NULL) { return; } /* 0.0 gamma is all black */ if (gamma == 0.0f) { for (i = 0; i < len; ++i) { ramp[i] = 0; } return; } else if (gamma == 1.0f) { /* 1.0 gamma is identity */ for (i = 0; i < len; ++i) { ramp[i] = (i << 8) | i; } return; } else { /* Calculate a real gamma ramp */ int value; gamma = 1.0f / gamma; for (i = 0; i < len; ++i) { value = (int) (pow((double) i / (double) len, gamma) * 65535.0 + 0.5); if (value > 65535) { value = 65535; } ramp[i] = (Uint16) value; } } } /* * Sets the hardware gamma */ #ifdef X11GAMMA void UpdateHardwareGamma(void) { float gamma = (vid_gamma->value); int i; Display* dpy = NULL; SDL_SysWMinfo info; #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_VERSION(&info.version); if(!SDL_GetWindowWMInfo(window, &info)) #else if(SDL_GetWMInfo(&info) != 1) #endif { VID_Printf(PRINT_ALL, "Couldn't get Window info from SDL\n"); return; } dpy = info.info.x11.display; XRRScreenResources* res = XRRGetScreenResources(dpy, info.info.x11.window); if(res == NULL) { VID_Printf(PRINT_ALL, "Unable to get xrandr screen resources.\n"); return; } for(i=0; i < res->ncrtc; ++i) { int len = XRRGetCrtcGammaSize(dpy, res->crtcs[i]); size_t rampSize = len*sizeof(Uint16); Uint16* ramp = malloc(rampSize); // TODO: check for NULL if(ramp == NULL) { VID_Printf(PRINT_ALL, "Couldn't allocate &zd byte of memory for gamma ramp - OOM?!\n", rampSize); return; } CalculateGammaRamp(gamma, ramp, len); XRRCrtcGamma* gamma = XRRAllocGamma(len); memcpy(gamma->red, ramp, rampSize); memcpy(gamma->green, ramp, rampSize); memcpy(gamma->blue, ramp, rampSize); free(ramp); XRRSetCrtcGamma(dpy, res->crtcs[i], gamma); XRRFreeGamma(gamma); } XRRFreeScreenResources(res); } #else // no X11GAMMA void UpdateHardwareGamma(void) { float gamma = (vid_gamma->value); Uint16 ramp[256]; CalculateGammaRamp(gamma, ramp, 256); #if SDL_VERSION_ATLEAST(2, 0, 0) if(SDL_SetWindowGammaRamp(window, ramp, ramp, ramp) != 0) { #else if(SDL_SetGammaRamp(ramp, ramp, ramp) < 0) { #endif VID_Printf(PRINT_ALL, "Setting gamma failed: %s\n", SDL_GetError()); } } #endif // X11GAMMA static qboolean IsFullscreen() { #if SDL_VERSION_ATLEAST(2, 0, 0) return !!(SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN); #else return !!(window->flags & SDL_FULLSCREEN); #endif } static qboolean CreateSDLWindow(int flags) { #if SDL_VERSION_ATLEAST(2, 0, 0) int windowPos = SDL_WINDOWPOS_UNDEFINED; // TODO: support fullscreen on different displays with SDL_WINDOWPOS_UNDEFINED_DISPLAY(displaynum) window = SDL_CreateWindow("Yamagi Quake II", windowPos, windowPos, vid.width, vid.height, flags); if(window == NULL) { return false; } context = SDL_GL_CreateContext(window); if(context == NULL) { SDL_DestroyWindow(window); window = NULL; return false; } return true; #else window = SDL_SetVideoMode(vid.width, vid.height, 0, flags); SDL_EnableUNICODE(SDL_TRUE); return window != NULL; #endif } static qboolean GetWindowSize(int* w, int* h) { if(window == NULL || w == NULL || h == NULL) return false; #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_DisplayMode m; if(SDL_GetWindowDisplayMode(window, &m) != 0) { VID_Printf(PRINT_ALL, "Can't get Displaymode: %s\n", SDL_GetError()); return false; } *w = m.w; *h = m.h; #else *w = window->w; *h = window->h; #endif return true; } static void InitGamma() { #ifdef X11GAMMA int i=0; SDL_SysWMinfo info; Display* dpy = NULL; if(gammaRamps != NULL) // already saved gamma return; #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_VERSION(&info.version); if(!SDL_GetWindowWMInfo(window, &info)) #else if(SDL_GetWMInfo(&info) != 1) #endif { VID_Printf(PRINT_ALL, "Couldn't get Window info from SDL\n"); return; } dpy = info.info.x11.display; XRRScreenResources* res = XRRGetScreenResources(dpy, info.info.x11.window); if(res == NULL) { VID_Printf(PRINT_ALL, "Unable to get xrandr screen resources.\n"); return; } noGammaRamps = res->ncrtc; gammaRamps = calloc(noGammaRamps, sizeof(XRRCrtcGamma*)); if(gammaRamps == NULL) { VID_Printf(PRINT_ALL, "Couldn't allocate memory for %d gamma ramps - OOM?!\n", noGammaRamps); return; } for(i=0; i < noGammaRamps; ++i) { int len = XRRGetCrtcGammaSize(dpy, res->crtcs[i]); size_t rampSize = len*sizeof(Uint16); XRRCrtcGamma* origGamma = XRRGetCrtcGamma(dpy, res->crtcs[i]); XRRCrtcGamma* gammaCopy = XRRAllocGamma(len); memcpy(gammaCopy->red, origGamma->red, rampSize); memcpy(gammaCopy->green, origGamma->green, rampSize); memcpy(gammaCopy->blue, origGamma->blue, rampSize); gammaRamps[i] = gammaCopy; } XRRFreeScreenResources(res); VID_Printf(PRINT_ALL, "Using hardware gamma via X11/xRandR.\n"); #else VID_Printf(PRINT_ALL, "Using hardware gamma via SDL.\n"); #endif gl_state.hwgamma = true; vid_gamma->modified = true; } #ifdef X11GAMMA static void RestoreGamma() { int i=0; SDL_SysWMinfo info; Display* dpy = NULL; if(gammaRamps == NULL) return; #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_VERSION(&info.version); if(!SDL_GetWindowWMInfo(window, &info)) #else if(SDL_GetWMInfo(&info) != 1) #endif { VID_Printf(PRINT_ALL, "Couldn't get Window info from SDL\n"); return; } dpy = info.info.x11.display; XRRScreenResources* res = XRRGetScreenResources(dpy, info.info.x11.window); if(res == NULL) { VID_Printf(PRINT_ALL, "Unable to get xrandr screen resources.\n"); return; } for(i=0; i < noGammaRamps; ++i) { // in case a display was unplugged or something, noGammaRamps may be > res->ncrtc if(i < res->ncrtc) { int len = XRRGetCrtcGammaSize(dpy, res->crtcs[i]); if(len != gammaRamps[i]->size) { VID_Printf(PRINT_ALL, "WTF, gamma ramp size for display %d has changed from %d to %d!\n", i, gammaRamps[i]->size, len); continue; } XRRSetCrtcGamma(dpy, res->crtcs[i], gammaRamps[i]); } // the ramp needs to be free()d either way XRRFreeGamma(gammaRamps[i]); gammaRamps[i] = NULL; } XRRFreeScreenResources(res); free(gammaRamps); gammaRamps = NULL; VID_Printf(PRINT_ALL, "Restored original Gamma\n"); } #endif // X11GAMMA /* * Initializes the OpenGL window */ static qboolean GLimp_InitGraphics(qboolean fullscreen) { int flags; int msaa_samples; int stencil_bits; int width, height; char title[24]; if (GetWindowSize(&width, &height) && (width == vid.width) && (height == vid.height)) { /* If we want fullscreen, but aren't */ if (fullscreen != IsFullscreen()) { #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_SetWindowFullscreen(window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0); #else SDL_WM_ToggleFullScreen(window); #endif Cvar_SetValue("vid_fullscreen", fullscreen); } /* Are we now? */ if (fullscreen == IsFullscreen()) { return true; } } /* Is the surface used? */ if (window) { #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_GL_DeleteContext(context); SDL_DestroyWindow(window); #else SDL_FreeSurface(window); #endif } /* Create the window */ VID_NewWindow(vid.width, vid.height); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); if (gl_msaa_samples->value) { msaa_samples = gl_msaa_samples->value; if (SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1) < 0) { Com_Printf("MSAA is unsupported: %s\n", SDL_GetError()); Cvar_SetValue ("gl_msaa_samples", 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); } else if (SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, msaa_samples) < 0) { Com_Printf("MSAA %ix is unsupported: %s\n", msaa_samples, SDL_GetError()); Cvar_SetValue("gl_msaa_samples", 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); } } /* Initiate the flags */ flags = SDL_OPENGL; if (fullscreen) { flags |= SDL_FULLSCREEN; } #if !SDL_VERSION_ATLEAST(2, 0, 0) /* For SDL1.2, these things must be done before creating the window */ /* Set the icon */ SetSDLIcon(); /* Set vsync */ SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, gl_swapinterval->value ? 1 : 0); #endif while (1) { if (!CreateSDLWindow(flags)) { if (gl_msaa_samples->value) { VID_Printf(PRINT_ALL, "SDL SetVideoMode failed: %s\n", SDL_GetError()); VID_Printf(PRINT_ALL, "Reverting to %s gl_mode %i (%ix%i) without MSAA.\n", (flags & SDL_FULLSCREEN) ? "fullscreen" : "windowed", (int)Cvar_VariableValue("gl_mode"), vid.width, vid.height); /* Try to recover */ Cvar_SetValue("gl_msaa_samples", 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); } else if (vid.width != 640 || vid.height != 480 || (flags & SDL_FULLSCREEN)) { VID_Printf(PRINT_ALL, "SDL SetVideoMode failed: %s\n", SDL_GetError()); VID_Printf(PRINT_ALL, "Reverting to windowed gl_mode 4 (640x480).\n"); /* Try to recover */ Cvar_SetValue("gl_mode", 4); Cvar_SetValue("vid_fullscreen", 0); vid.width = 640; vid.height = 480; flags &= ~SDL_FULLSCREEN; } else { VID_Error(ERR_FATAL, "Failed to revert to gl_mode 4. Exiting...\n"); return false; } } else { break; } } if (gl_msaa_samples->value) { if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &msaa_samples) == 0) { Cvar_SetValue("gl_msaa_samples", msaa_samples); } } #if SDL_VERSION_ATLEAST(2, 0, 0) /* For SDL2, these things must be done after creating the window */ /* Set the icon */ SetSDLIcon(); /* Set vsync - TODO: -1 could be set for "late swap tearing" */ SDL_GL_SetSwapInterval(gl_swapinterval->value ? 1 : 0); #endif /* Initialize the stencil buffer */ if (!SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil_bits)) { VID_Printf(PRINT_ALL, "Got %d bits of stencil.\n", stencil_bits); if (stencil_bits >= 1) { have_stencil = true; } } /* Initialize hardware gamma */ InitGamma(); /* Window title */ snprintf(title, sizeof(title), "Yamagi Quake II %s", YQ2VERSION); #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_SetWindowTitle(window, title); #else SDL_WM_SetCaption(title, title); #endif /* No cursor */ SDL_ShowCursor(0); return true; } /* * Swaps the buffers to show the new frame */ void GLimp_EndFrame(void) { #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_GL_SwapWindow(window); #else SDL_GL_SwapBuffers(); #endif } /* * Changes the video mode */ int GLimp_SetMode(int *pwidth, int *pheight, int mode, qboolean fullscreen) { VID_Printf(PRINT_ALL, "setting mode %d:", mode); /* mode -1 is not in the vid mode table - so we keep the values in pwidth and pheight and don't even try to look up the mode info */ if ((mode != -1) && !VID_GetModeInfo(pwidth, pheight, mode)) { VID_Printf(PRINT_ALL, " invalid mode\n"); return rserr_invalid_mode; } VID_Printf(PRINT_ALL, " %d %d\n", *pwidth, *pheight); if (!GLimp_InitGraphics(fullscreen)) { return rserr_invalid_mode; } return rserr_ok; } /* * (Un)grab Input */ void GLimp_GrabInput(qboolean grab) { #if SDL_VERSION_ATLEAST(2, 0, 0) if(window != NULL) { SDL_SetWindowGrab(window, grab ? SDL_TRUE : SDL_FALSE); } if(SDL_SetRelativeMouseMode(grab ? SDL_TRUE : SDL_FALSE) < 0) { VID_Printf(PRINT_ALL, "WARNING: Setting Relative Mousemode failed, reason: %s\n", SDL_GetError()); VID_Printf(PRINT_ALL, " You should probably update to SDL 2.0.3 or newer!\n"); } #else SDL_WM_GrabInput(grab ? SDL_GRAB_ON : SDL_GRAB_OFF); #endif } /* * Shuts the SDL render backend down */ void GLimp_Shutdown(void) { /* Clear the backbuffer and make it current. This may help some broken video drivers like the AMD Catalyst to avoid artifacts in unused screen areas. */ if (SDL_WasInit(SDL_INIT_VIDEO)) { glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); GLimp_EndFrame(); } #ifdef X11GAMMA RestoreGamma(); #endif if (window) { /* cleanly ungrab input (needs window) */ GLimp_GrabInput(false); #if SDL_VERSION_ATLEAST(2, 0, 0) if(context) { SDL_GL_DeleteContext(context); context = NULL; } SDL_DestroyWindow(window); #else SDL_FreeSurface(window); #endif } window = NULL; if (SDL_WasInit(SDL_INIT_EVERYTHING) == SDL_INIT_VIDEO) { SDL_Quit(); } else { SDL_QuitSubSystem(SDL_INIT_VIDEO); } gl_state.hwgamma = false; } yquake2-QUAKE2_5_32/src/server/0000755000175000017500000000000012615162034016714 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/server/sv_init.c0000644000175000017500000002557612615162034020552 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 startup. * * ======================================================================= */ #include "header/server.h" server_static_t svs; /* persistant server info */ server_t sv; /* local server */ int SV_FindIndex(char *name, int start, int max, qboolean create) { int i; if (!name || !name[0]) { return 0; } for (i = 1; i < max && sv.configstrings[start + i][0]; i++) { if (!strcmp(sv.configstrings[start + i], name)) { return i; } } if (!create) { return 0; } if (i == max) { Com_Error(ERR_DROP, "*Index: overflow"); } Q_strlcpy(sv.configstrings[start + i], name, sizeof(sv.configstrings[start + i])); if (sv.state != ss_loading) { /* send the update to everyone */ MSG_WriteChar(&sv.multicast, svc_configstring); MSG_WriteShort(&sv.multicast, start + i); MSG_WriteString(&sv.multicast, name); SV_Multicast(vec3_origin, MULTICAST_ALL_R); } return i; } int SV_ModelIndex(char *name) { return SV_FindIndex(name, CS_MODELS, MAX_MODELS, true); } int SV_SoundIndex(char *name) { return SV_FindIndex(name, CS_SOUNDS, MAX_SOUNDS, true); } int SV_ImageIndex(char *name) { return SV_FindIndex(name, CS_IMAGES, MAX_IMAGES, true); } /* * Entity baselines are used to compress the update messages * to the clients -- only the fields that differ from the * baseline will be transmitted */ void SV_CreateBaseline(void) { edict_t *svent; int entnum; for (entnum = 1; entnum < ge->num_edicts; entnum++) { svent = EDICT_NUM(entnum); if (!svent->inuse) { continue; } if (!svent->s.modelindex && !svent->s.sound && !svent->s.effects) { continue; } svent->s.number = entnum; /* take current state as baseline */ VectorCopy(svent->s.origin, svent->s.old_origin); sv.baselines[entnum] = svent->s; } } void SV_CheckForSavegame(void) { char name[MAX_OSPATH]; FILE *f; int i; if (sv_noreload->value) { return; } if (Cvar_VariableValue("deathmatch")) { return; } Com_sprintf(name, sizeof(name), "%s/save/current/%s.sav", FS_Gamedir(), sv.name); f = fopen(name, "rb"); if (!f) { return; /* no savegame */ } fclose(f); SV_ClearWorld(); /* get configstrings and areaportals */ SV_ReadLevelFile(); if (!sv.loadgame) { /* coming back to a level after being in a different level, so run it for ten seconds */ server_state_t previousState; previousState = sv.state; sv.state = ss_loading; for (i = 0; i < 100; i++) { ge->RunFrame(); } sv.state = previousState; } } /* * Change the server to a new map, taking all connected * clients along with it. */ void SV_SpawnServer(char *server, char *spawnpoint, server_state_t serverstate, qboolean attractloop, qboolean loadgame) { int i; unsigned checksum; if (attractloop) { Cvar_Set("paused", "0"); } Com_Printf("------- server initialization ------\n"); Com_DPrintf("SpawnServer: %s\n", server); if (sv.demofile) { FS_FCloseFile(sv.demofile); } svs.spawncount++; /* any partially connected client will be restarted */ sv.state = ss_dead; Com_SetServerState(sv.state); /* wipe the entire per-level structure */ memset(&sv, 0, sizeof(sv)); svs.realtime = 0; sv.loadgame = loadgame; sv.attractloop = attractloop; /* save name for levels that don't set message */ strcpy(sv.configstrings[CS_NAME], server); if (Cvar_VariableValue("deathmatch")) { sprintf(sv.configstrings[CS_AIRACCEL], "%g", sv_airaccelerate->value); pm_airaccelerate = sv_airaccelerate->value; } else { strcpy(sv.configstrings[CS_AIRACCEL], "0"); pm_airaccelerate = 0; } SZ_Init(&sv.multicast, sv.multicast_buf, sizeof(sv.multicast_buf)); strcpy(sv.name, server); /* leave slots at start for clients only */ for (i = 0; i < maxclients->value; i++) { /* needs to reconnect */ if (svs.clients[i].state > cs_connected) { svs.clients[i].state = cs_connected; } svs.clients[i].lastframe = -1; } sv.time = 1000; strcpy(sv.name, server); strcpy(sv.configstrings[CS_NAME], server); if (serverstate != ss_game) { sv.models[1] = CM_LoadMap("", false, &checksum); /* no real map */ } else { Com_sprintf(sv.configstrings[CS_MODELS + 1], sizeof(sv.configstrings[CS_MODELS + 1]), "maps/%s.bsp", server); sv.models[1] = CM_LoadMap(sv.configstrings[CS_MODELS + 1], false, &checksum); } Com_sprintf(sv.configstrings[CS_MAPCHECKSUM], sizeof(sv.configstrings[CS_MAPCHECKSUM]), "%i", checksum); /* clear physics interaction links */ SV_ClearWorld(); for (i = 1; i < CM_NumInlineModels(); i++) { Com_sprintf(sv.configstrings[CS_MODELS + 1 + i], sizeof(sv.configstrings[CS_MODELS + 1 + i]), "*%i", i); sv.models[i + 1] = CM_InlineModel(sv.configstrings[CS_MODELS + 1 + i]); } /* spawn the rest of the entities on the map */ sv.state = ss_loading; Com_SetServerState(sv.state); /* load and spawn all other entities */ ge->SpawnEntities(sv.name, CM_EntityString(), spawnpoint); /* run two frames to allow everything to settle */ ge->RunFrame(); ge->RunFrame(); /* verify game didn't clobber important stuff */ if ((int)checksum != (int)strtol(sv.configstrings[CS_MAPCHECKSUM], (char **)NULL, 10)) { Com_Error(ERR_DROP, "Game DLL corrupted server configstrings"); } /* all precaches are complete */ sv.state = serverstate; Com_SetServerState(sv.state); /* create a baseline for more efficient communications */ SV_CreateBaseline(); /* check for a savegame */ SV_CheckForSavegame(); /* set serverinfo variable */ Cvar_FullSet("mapname", sv.name, CVAR_SERVERINFO | CVAR_NOSET); Com_Printf("------------------------------------\n\n"); } /* * A brand new game has been started */ void SV_InitGame(void) { int i; edict_t *ent; char idmaster[32]; if (svs.initialized) { /* cause any connected clients to reconnect */ SV_Shutdown("Server restarted\n", true); } #ifndef DEDICATED_ONLY else { /* make sure the client is down */ CL_Drop(); SCR_BeginLoadingPlaque(); } #endif /* get any latched variable changes (maxclients, etc) */ Cvar_GetLatchedVars(); svs.initialized = true; if (Cvar_VariableValue("coop") && Cvar_VariableValue("deathmatch")) { Com_Printf("Deathmatch and Coop both set, disabling Coop\n"); Cvar_FullSet("coop", "0", CVAR_SERVERINFO | CVAR_LATCH); } /* dedicated servers can't be single player and are usually DM so unless they explicity set coop, force it to deathmatch */ if (dedicated->value) { if (!Cvar_VariableValue("coop")) { Cvar_FullSet("deathmatch", "1", CVAR_SERVERINFO | CVAR_LATCH); } } /* init clients */ if (Cvar_VariableValue("deathmatch")) { if (maxclients->value <= 1) { Cvar_FullSet("maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH); } else if (maxclients->value > MAX_CLIENTS) { Cvar_FullSet("maxclients", va("%i", MAX_CLIENTS), CVAR_SERVERINFO | CVAR_LATCH); } } else if (Cvar_VariableValue("coop")) { if ((maxclients->value <= 1) || (maxclients->value > 4)) { Cvar_FullSet("maxclients", "4", CVAR_SERVERINFO | CVAR_LATCH); } } else /* non-deathmatch, non-coop is one player */ { Cvar_FullSet("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH); } svs.spawncount = randk(); svs.clients = Z_Malloc(sizeof(client_t) * maxclients->value); svs.num_client_entities = maxclients->value * UPDATE_BACKUP * 64; svs.client_entities = Z_Malloc( sizeof(entity_state_t) * svs.num_client_entities); /* init network stuff */ NET_Config((maxclients->value > 1)); /* heartbeats will always be sent to the id master */ svs.last_heartbeat = -99999; /* send immediately */ Com_sprintf(idmaster, sizeof(idmaster), "192.246.40.37:%i", PORT_MASTER); NET_StringToAdr(idmaster, &master_adr[0]); /* init game */ SV_InitGameProgs(); for (i = 0; i < maxclients->value; i++) { ent = EDICT_NUM(i + 1); ent->s.number = i + 1; svs.clients[i].edict = ent; memset(&svs.clients[i].lastcmd, 0, sizeof(svs.clients[i].lastcmd)); } } /* * the full syntax is: * * map [*]$+ * * command from the console or progs. * Map can also be a.cin, .pcx, or .dm2 file * Nextserver is used to allow a cinematic to play, then proceed to * another level: * * map tram.cin+jail_e3 */ void SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame) { char level[MAX_QPATH]; char *ch; int l; char spawnpoint[MAX_QPATH]; sv.loadgame = loadgame; sv.attractloop = attractloop; if ((sv.state == ss_dead) && !sv.loadgame) { SV_InitGame(); /* the game is just starting */ } strcpy(level, levelstring); /* if there is a + in the map, set nextserver to the remainder */ ch = strstr(level, "+"); if (ch) { *ch = 0; Cvar_Set("nextserver", va("gamemap \"%s\"", ch + 1)); } else { Cvar_Set("nextserver", ""); } /* hack for end game screen in coop mode */ if (Cvar_VariableValue("coop") && !Q_stricmp(level, "victory.pcx")) { Cvar_Set("nextserver", "gamemap \"*base1\""); } /* if there is a $, use the remainder as a spawnpoint */ ch = strstr(level, "$"); if (ch) { *ch = 0; strcpy(spawnpoint, ch + 1); } else { spawnpoint[0] = 0; } /* skip the end-of-unit flag if necessary */ l = strlen(level); if (level[0] == '*') { memmove(level, level + 1, l); --l; } if ((l > 4) && !strcmp(level + l - 4, ".cin")) { #ifndef DEDICATED_ONLY SCR_BeginLoadingPlaque(); /* for local system */ #endif SV_BroadcastCommand("changing\n"); SV_SpawnServer(level, spawnpoint, ss_cinematic, attractloop, loadgame); } else if ((l > 4) && !strcmp(level + l - 4, ".dm2")) { #ifndef DEDICATED_ONLY SCR_BeginLoadingPlaque(); /* for local system */ #endif SV_BroadcastCommand("changing\n"); SV_SpawnServer(level, spawnpoint, ss_demo, attractloop, loadgame); } else if ((l > 4) && !strcmp(level + l - 4, ".pcx")) { #ifndef DEDICATED_ONLY SCR_BeginLoadingPlaque(); /* for local system */ #endif SV_BroadcastCommand("changing\n"); SV_SpawnServer(level, spawnpoint, ss_pic, attractloop, loadgame); } else { #ifndef DEDICATED_ONLY SCR_BeginLoadingPlaque(); /* for local system */ #endif SV_BroadcastCommand("changing\n"); SV_SendClientMessages(); SV_SpawnServer(level, spawnpoint, ss_game, attractloop, loadgame); Cbuf_CopyToDefer(); } SV_BroadcastCommand("reconnect\n"); } yquake2-QUAKE2_5_32/src/server/sv_save.c0000644000175000017500000002144612615162034020535 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Serverside savegame code. * * ======================================================================= */ #include "header/server.h" void CM_ReadPortalState(fileHandle_t f); /* * Delete save// */ void SV_WipeSavegame(char *savename) { char name[MAX_OSPATH]; char *s; Com_DPrintf("SV_WipeSaveGame(%s)\n", savename); Com_sprintf(name, sizeof(name), "%s/save/%s/server.ssv", FS_Gamedir(), savename); remove(name); Com_sprintf(name, sizeof(name), "%s/save/%s/game.ssv", FS_Gamedir(), savename); remove(name); Com_sprintf(name, sizeof(name), "%s/save/%s/*.sav", FS_Gamedir(), savename); s = Sys_FindFirst(name, 0, 0); while (s) { remove(s); s = Sys_FindNext(0, 0); } Sys_FindClose(); Com_sprintf(name, sizeof(name), "%s/save/%s/*.sv2", FS_Gamedir(), savename); s = Sys_FindFirst(name, 0, 0); while (s) { remove(s); s = Sys_FindNext(0, 0); } Sys_FindClose(); } void CopyFile(char *src, char *dst) { FILE *f1, *f2; size_t l; byte buffer[65536]; Com_DPrintf("CopyFile (%s, %s)\n", src, dst); f1 = fopen(src, "rb"); if (!f1) { return; } f2 = fopen(dst, "wb"); if (!f2) { fclose(f1); return; } while (1) { l = fread(buffer, 1, sizeof(buffer), f1); if (!l) { break; } fwrite(buffer, 1, l, f2); } fclose(f1); fclose(f2); } void SV_CopySaveGame(char *src, char *dst) { char name[MAX_OSPATH], name2[MAX_OSPATH]; size_t l, len; char *found; Com_DPrintf("SV_CopySaveGame(%s, %s)\n", src, dst); SV_WipeSavegame(dst); /* copy the savegame over */ Com_sprintf(name, sizeof(name), "%s/save/%s/server.ssv", FS_Gamedir(), src); Com_sprintf(name2, sizeof(name2), "%s/save/%s/server.ssv", FS_Gamedir(), dst); FS_CreatePath(name2); CopyFile(name, name2); Com_sprintf(name, sizeof(name), "%s/save/%s/game.ssv", FS_Gamedir(), src); Com_sprintf(name2, sizeof(name2), "%s/save/%s/game.ssv", FS_Gamedir(), dst); CopyFile(name, name2); Com_sprintf(name, sizeof(name), "%s/save/%s/", FS_Gamedir(), src); len = strlen(name); Com_sprintf(name, sizeof(name), "%s/save/%s/*.sav", FS_Gamedir(), src); found = Sys_FindFirst(name, 0, 0); while (found) { strcpy(name + len, found + len); Com_sprintf(name2, sizeof(name2), "%s/save/%s/%s", FS_Gamedir(), dst, found + len); CopyFile(name, name2); /* change sav to sv2 */ l = strlen(name); strcpy(name + l - 3, "sv2"); l = strlen(name2); strcpy(name2 + l - 3, "sv2"); CopyFile(name, name2); found = Sys_FindNext(0, 0); } Sys_FindClose(); } void SV_WriteLevelFile(void) { char name[MAX_OSPATH]; FILE *f; Com_DPrintf("SV_WriteLevelFile()\n"); Com_sprintf(name, sizeof(name), "%s/save/current/%s.sv2", FS_Gamedir(), sv.name); f = fopen(name, "wb"); if (!f) { Com_Printf("Failed to open %s\n", name); return; } fwrite(sv.configstrings, sizeof(sv.configstrings), 1, f); CM_WritePortalState(f); fclose(f); Com_sprintf(name, sizeof(name), "%s/save/current/%s.sav", FS_Gamedir(), sv.name); ge->WriteLevel(name); } void SV_ReadLevelFile(void) { char name[MAX_OSPATH]; fileHandle_t f; Com_DPrintf("SV_ReadLevelFile()\n"); Com_sprintf(name, sizeof(name), "save/current/%s.sv2", sv.name); FS_FOpenFile(name, &f, true); if (!f) { Com_Printf("Failed to open %s\n", name); return; } FS_Read(sv.configstrings, sizeof(sv.configstrings), f); CM_ReadPortalState(f); FS_FCloseFile(f); Com_sprintf(name, sizeof(name), "%s/save/current/%s.sav", FS_Gamedir(), sv.name); ge->ReadLevel(name); } void SV_WriteServerFile(qboolean autosave) { FILE *f; cvar_t *var; char name[MAX_OSPATH], string[128]; char comment[32]; time_t aclock; struct tm *newtime; Com_DPrintf("SV_WriteServerFile(%s)\n", autosave ? "true" : "false"); Com_sprintf(name, sizeof(name), "%s/save/current/server.ssv", FS_Gamedir()); f = fopen(name, "wb"); if (!f) { Com_Printf("Couldn't write %s\n", name); return; } /* write the comment field */ memset(comment, 0, sizeof(comment)); if (!autosave) { time(&aclock); newtime = localtime(&aclock); Com_sprintf(comment, sizeof(comment), "%2i:%i%i %2i/%2i ", newtime->tm_hour, newtime->tm_min / 10, newtime->tm_min % 10, newtime->tm_mon + 1, newtime->tm_mday); Q_strlcat(comment, sv.configstrings[CS_NAME], sizeof(comment)); } else { /* autosaved */ Com_sprintf(comment, sizeof(comment), "ENTERING %s", sv.configstrings[CS_NAME]); } fwrite(comment, 1, sizeof(comment), f); /* write the mapcmd */ fwrite(svs.mapcmd, 1, sizeof(svs.mapcmd), f); /* write all CVAR_LATCH cvars these will be things like coop, skill, deathmatch, etc */ for (var = cvar_vars; var; var = var->next) { char cvarname[LATCH_CVAR_SAVELENGTH] = {0}; if (!(var->flags & CVAR_LATCH)) { continue; } if ((strlen(var->name) >= sizeof(cvarname) - 1) || (strlen(var->string) >= sizeof(string) - 1)) { Com_Printf("Cvar too long: %s = %s\n", var->name, var->string); continue; } memset(string, 0, sizeof(string)); strcpy(cvarname, var->name); strcpy(string, var->string); fwrite(cvarname, 1, sizeof(cvarname), f); fwrite(string, 1, sizeof(string), f); } fclose(f); /* write game state */ Com_sprintf(name, sizeof(name), "%s/save/current/game.ssv", FS_Gamedir()); ge->WriteGame(name, autosave); } void SV_ReadServerFile(void) { fileHandle_t f; char name[MAX_OSPATH], string[128]; char comment[32]; char mapcmd[MAX_TOKEN_CHARS]; Com_DPrintf("SV_ReadServerFile()\n"); Com_sprintf(name, sizeof(name), "save/current/server.ssv"); FS_FOpenFile(name, &f, true); if (!f) { Com_Printf("Couldn't read %s\n", name); return; } /* read the comment field */ FS_Read(comment, sizeof(comment), f); /* read the mapcmd */ FS_Read(mapcmd, sizeof(mapcmd), f); /* read all CVAR_LATCH cvars these will be things like coop, skill, deathmatch, etc */ while (1) { char cvarname[LATCH_CVAR_SAVELENGTH] = {0}; if (!FS_FRead(cvarname, 1, sizeof(cvarname), f)) { break; } FS_Read(string, sizeof(string), f); Com_DPrintf("Set %s = %s\n", cvarname, string); Cvar_ForceSet(cvarname, string); } FS_FCloseFile(f); /* start a new game fresh with new cvars */ SV_InitGame(); strcpy(svs.mapcmd, mapcmd); /* read game state */ Com_sprintf(name, sizeof(name), "%s/save/current/game.ssv", FS_Gamedir()); ge->ReadGame(name); } void SV_Loadgame_f(void) { char name[MAX_OSPATH]; FILE *f; char *dir; if (Cmd_Argc() != 2) { Com_Printf("USAGE: loadgame \n"); return; } Com_Printf("Loading game...\n"); dir = Cmd_Argv(1); if (strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\")) { Com_Printf("Bad savedir.\n"); } /* make sure the server.ssv file exists */ Com_sprintf(name, sizeof(name), "%s/save/%s/server.ssv", FS_Gamedir(), Cmd_Argv(1)); f = fopen(name, "rb"); if (!f) { Com_Printf("No such savegame: %s\n", name); return; } fclose(f); SV_CopySaveGame(Cmd_Argv(1), "current"); SV_ReadServerFile(); /* go to the map */ sv.state = ss_dead; /* don't save current level when changing */ SV_Map(false, svs.mapcmd, true); } void SV_Savegame_f(void) { char *dir; if (sv.state != ss_game) { Com_Printf("You must be in a game to save.\n"); return; } if (Cmd_Argc() != 2) { Com_Printf("USAGE: savegame \n"); return; } if (Cvar_VariableValue("deathmatch")) { Com_Printf("Can't savegame in a deathmatch\n"); return; } if (!strcmp(Cmd_Argv(1), "current")) { Com_Printf("Can't save to 'current'\n"); return; } if ((maxclients->value == 1) && (svs.clients[0].edict->client->ps.stats[STAT_HEALTH] <= 0)) { Com_Printf("\nCan't savegame while dead!\n"); return; } dir = Cmd_Argv(1); if (strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\")) { Com_Printf("Bad savedir.\n"); } Com_Printf("Saving game...\n"); /* archive current level, including all client edicts. when the level is reloaded, they will be shells awaiting a connecting client */ SV_WriteLevelFile(); /* save server state */ SV_WriteServerFile(false); /* copy it off */ SV_CopySaveGame("current", dir); Com_Printf("Done.\n"); } yquake2-QUAKE2_5_32/src/server/sv_main.c0000644000175000017500000003524212615162034020522 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 main function and correspondig stuff * * ======================================================================= */ #include "header/server.h" #define HEARTBEAT_SECONDS 300 netadr_t master_adr[MAX_MASTERS]; /* address of group servers */ client_t *sv_client; /* current client */ cvar_t *sv_paused; cvar_t *sv_timedemo; cvar_t *sv_enforcetime; cvar_t *timeout; /* seconds without any message */ cvar_t *zombietime; /* seconds to sink messages after disconnect */ cvar_t *rcon_password; /* password for remote server commands */ cvar_t *allow_download; cvar_t *allow_download_players; cvar_t *allow_download_models; cvar_t *allow_download_sounds; cvar_t *allow_download_maps; cvar_t *sv_airaccelerate; cvar_t *sv_noreload; /* don't reload level state when reentering */ cvar_t *maxclients; /* rename sv_maxclients */ cvar_t *sv_showclamp; cvar_t *hostname; cvar_t *public_server; /* should heartbeats be sent */ void Master_Shutdown(void); void SV_ConnectionlessPacket(void); /* * Called when the player is totally leaving the server, either willingly * or unwillingly. This is NOT called if the entire server is quiting * or crashing. */ void SV_DropClient(client_t *drop) { /* add the disconnect */ MSG_WriteByte(&drop->netchan.message, svc_disconnect); if (drop->state == cs_spawned) { /* call the prog function for removing a client this will remove the body, among other things */ ge->ClientDisconnect(drop->edict); } if (drop->download) { FS_FreeFile(drop->download); drop->download = NULL; } drop->state = cs_zombie; /* become free in a few seconds */ drop->name[0] = 0; } /* * Builds the string that is sent as heartbeats and status replies */ char * SV_StatusString(void) { char player[1024]; static char status[MAX_MSGLEN - 16]; int i; client_t *cl; int statusLength; int playerLength; strcpy(status, Cvar_Serverinfo()); strcat(status, "\n"); statusLength = (int)strlen(status); for (i = 0; i < maxclients->value; i++) { cl = &svs.clients[i]; if ((cl->state == cs_connected) || (cl->state == cs_spawned)) { Com_sprintf(player, sizeof(player), "%i %i \"%s\"\n", cl->edict->client->ps.stats[STAT_FRAGS], cl->ping, cl->name); playerLength = (int)strlen(player); if (statusLength + playerLength >= sizeof(status)) { break; /* can't hold any more */ } strcpy(status + statusLength, player); statusLength += playerLength; } } return status; } /* * Updates the cl->ping variables */ void SV_CalcPings(void) { int i, j; client_t *cl; int total, count; for (i = 0; i < maxclients->value; i++) { cl = &svs.clients[i]; if (cl->state != cs_spawned) { continue; } total = 0; count = 0; for (j = 0; j < LATENCY_COUNTS; j++) { if (cl->frame_latency[j] > 0) { count++; total += cl->frame_latency[j]; } } if (!count) { cl->ping = 0; } else { cl->ping = total / count; } /* let the game dll know about the ping */ cl->edict->client->ping = cl->ping; } } /* * Every few frames, gives all clients an allotment of milliseconds * for their command moves. If they exceed it, assume cheating. */ void SV_GiveMsec(void) { int i; client_t *cl; if (sv.framenum & 15) { return; } for (i = 0; i < maxclients->value; i++) { cl = &svs.clients[i]; if (cl->state == cs_free) { continue; } cl->commandMsec = 1800; /* 1600 + some slop */ } } void SV_ReadPackets(void) { int i; client_t *cl; int qport; while (NET_GetPacket(NS_SERVER, &net_from, &net_message)) { /* check for connectionless packet (0xffffffff) first */ if (*(int *)net_message.data == -1) { SV_ConnectionlessPacket(); continue; } /* read the qport out of the message so we can fix up stupid address translating routers */ MSG_BeginReading(&net_message); MSG_ReadLong(&net_message); /* sequence number */ MSG_ReadLong(&net_message); /* sequence number */ qport = MSG_ReadShort(&net_message) & 0xffff; /* check for packets from connected clients */ for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) { if (cl->state == cs_free) { continue; } if (!NET_CompareBaseAdr(net_from, cl->netchan.remote_address)) { continue; } if (cl->netchan.qport != qport) { continue; } if (cl->netchan.remote_address.port != net_from.port) { Com_Printf("SV_ReadPackets: fixing up a translated port\n"); cl->netchan.remote_address.port = net_from.port; } if (Netchan_Process(&cl->netchan, &net_message)) { /* this is a valid, sequenced packet, so process it */ if (cl->state != cs_zombie) { cl->lastmessage = svs.realtime; /* don't timeout */ if (!(sv.demofile && (sv.state == ss_demo))) { SV_ExecuteClientMessage(cl); } } } break; } if (i != maxclients->value) { continue; } } } /* * If a packet has not been received from a client for timeout->value * seconds, drop the conneciton. Server frames are used instead of * realtime to avoid dropping the local client while debugging. * * When a client is normally dropped, the client_t goes into a zombie state * for a few seconds to make sure any final reliable message gets resent * if necessary */ void SV_CheckTimeouts(void) { int i; client_t *cl; int droppoint; int zombiepoint; droppoint = svs.realtime - 1000 * timeout->value; zombiepoint = svs.realtime - 1000 * zombietime->value; for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) { /* message times may be wrong across a changelevel */ if (cl->lastmessage > svs.realtime) { cl->lastmessage = svs.realtime; } if ((cl->state == cs_zombie) && (cl->lastmessage < zombiepoint)) { cl->state = cs_free; /* can now be reused */ continue; } if (((cl->state == cs_connected) || (cl->state == cs_spawned)) && (cl->lastmessage < droppoint)) { SV_BroadcastPrintf(PRINT_HIGH, "%s timed out\n", cl->name); SV_DropClient(cl); cl->state = cs_free; /* don't bother with zombie state */ } } } /* * This has to be done before the world logic, because * player processing happens outside RunWorldFrame */ void SV_PrepWorldFrame(void) { edict_t *ent; int i; for (i = 0; i < ge->num_edicts; i++, ent++) { ent = EDICT_NUM(i); /* events only last for a single message */ ent->s.event = 0; } } void SV_RunGameFrame(void) { #ifndef DEDICATED_ONLY if (host_speeds->value) { time_before_game = Sys_Milliseconds(); } #endif /* we always need to bump framenum, even if we don't run the world, otherwise the delta compression can get confused when a client has the "current" frame */ sv.framenum++; sv.time = sv.framenum * 100; /* don't run if paused */ if (!sv_paused->value || (maxclients->value > 1)) { ge->RunFrame(); /* never get more than one tic behind */ if (sv.time < svs.realtime) { if (sv_showclamp->value) { Com_Printf("sv highclamp\n"); } svs.realtime = sv.time; } } #ifndef DEDICATED_ONLY if (host_speeds->value) { time_after_game = Sys_Milliseconds(); } #endif } void SV_Frame(int msec) { #ifndef DEDICATED_ONLY time_before_game = time_after_game = 0; #endif /* if server is not active, do nothing */ if (!svs.initialized) { return; } svs.realtime += msec; /* keep the random time dependent */ randk(); /* check timeouts */ SV_CheckTimeouts(); /* get packets from clients */ SV_ReadPackets(); /* move autonomous things around if enough time has passed */ if (!sv_timedemo->value && (svs.realtime < sv.time)) { /* never let the time get too far off */ if (sv.time - svs.realtime > 100) { if (sv_showclamp->value) { Com_Printf("sv lowclamp\n"); } svs.realtime = sv.time - 100; } NET_Sleep(sv.time - svs.realtime); return; } /* update ping based on the last known frame from all clients */ SV_CalcPings(); /* give the clients some timeslices */ SV_GiveMsec(); /* let everything in the world think and move */ SV_RunGameFrame(); /* send messages back to the clients that had packets read this frame */ SV_SendClientMessages(); /* save the entire world state if recording a serverdemo */ SV_RecordDemoMessage(); /* send a heartbeat to the master if needed */ Master_Heartbeat(); /* clear teleport flags, etc for next frame */ SV_PrepWorldFrame(); } /* * Send a message to the master every few minutes to * let it know we are alive, and log information */ void Master_Heartbeat(void) { char *string; int i; if (!dedicated || !dedicated->value) { return; /* only dedicated servers send heartbeats */ } if (!public_server || !public_server->value) { return; /* a private dedicated game */ } /* check for time wraparound */ if (svs.last_heartbeat > svs.realtime) { svs.last_heartbeat = svs.realtime; } if (svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS * 1000) { return; /* not time to send yet */ } svs.last_heartbeat = svs.realtime; /* send the same string that we would give for a status OOB command */ string = SV_StatusString(); /* send to group master */ for (i = 0; i < MAX_MASTERS; i++) { if (master_adr[i].port) { Com_Printf("Sending heartbeat to %s\n", NET_AdrToString(master_adr[i])); Netchan_OutOfBandPrint(NS_SERVER, master_adr[i], "heartbeat\n%s", string); } } } /* * Informs all masters that this server is going down */ void Master_Shutdown(void) { int i; if (!dedicated || !dedicated->value) { return; /* only dedicated servers send heartbeats */ } if (!public_server || !public_server->value) { return; /* a private dedicated game */ } /* send to group master */ for (i = 0; i < MAX_MASTERS; i++) { if (master_adr[i].port) { if (i > 0) { Com_Printf("Sending heartbeat to %s\n", NET_AdrToString(master_adr[i])); } Netchan_OutOfBandPrint(NS_SERVER, master_adr[i], "shutdown"); } } } /* * Pull specific info from a newly changed userinfo string * into a more C freindly form. */ void SV_UserinfoChanged(client_t *cl) { char *val; int i; /* call prog code to allow overrides */ ge->ClientUserinfoChanged(cl->edict, cl->userinfo); /* name for C code */ Q_strlcpy(cl->name, Info_ValueForKey(cl->userinfo, "name"), sizeof(cl->name)); /* mask off high bit */ for (i = 0; i < sizeof(cl->name); i++) { cl->name[i] &= 127; } /* rate command */ val = Info_ValueForKey(cl->userinfo, "rate"); if (strlen(val)) { i = (int)strtol(val, (char **)NULL, 10); cl->rate = i; if (cl->rate < 100) { cl->rate = 100; } if (cl->rate > 15000) { cl->rate = 15000; } } else { cl->rate = 5000; } /* msg command */ val = Info_ValueForKey(cl->userinfo, "msg"); if (strlen(val)) { cl->messagelevel = (int)strtol(val, (char **)NULL, 10); } } /* * Only called at quake2.exe startup, not for each game */ void SV_Init(void) { SV_InitOperatorCommands(); rcon_password = Cvar_Get("rcon_password", "", 0); Cvar_Get("skill", "1", 0); Cvar_Get("deathmatch", "0", CVAR_LATCH); Cvar_Get("coop", "0", CVAR_LATCH); Cvar_Get("dmflags", va("%i", DF_INSTANT_ITEMS), CVAR_SERVERINFO); Cvar_Get("fraglimit", "0", CVAR_SERVERINFO); Cvar_Get("timelimit", "0", CVAR_SERVERINFO); Cvar_Get("cheats", "0", CVAR_SERVERINFO | CVAR_LATCH); Cvar_Get("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_NOSET); maxclients = Cvar_Get("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH); hostname = Cvar_Get("hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE); timeout = Cvar_Get("timeout", "125", 0); zombietime = Cvar_Get("zombietime", "2", 0); sv_showclamp = Cvar_Get("showclamp", "0", 0); sv_paused = Cvar_Get("paused", "0", 0); sv_timedemo = Cvar_Get("timedemo", "0", 0); sv_enforcetime = Cvar_Get("sv_enforcetime", "0", 0); allow_download = Cvar_Get("allow_download", "1", CVAR_ARCHIVE); allow_download_players = Cvar_Get("allow_download_players", "0", CVAR_ARCHIVE); allow_download_models = Cvar_Get("allow_download_models", "1", CVAR_ARCHIVE); allow_download_sounds = Cvar_Get("allow_download_sounds", "1", CVAR_ARCHIVE); allow_download_maps = Cvar_Get("allow_download_maps", "1", CVAR_ARCHIVE); sv_noreload = Cvar_Get("sv_noreload", "0", 0); sv_airaccelerate = Cvar_Get("sv_airaccelerate", "0", CVAR_LATCH); public_server = Cvar_Get("public", "0", 0); SZ_Init(&net_message, net_message_buffer, sizeof(net_message_buffer)); } /* * Used by SV_Shutdown to send a final message to all * connected clients before the server goes down. The * messages are sent immediately, not just stuck on the * outgoing message list, because the server is going * to totally exit after returning from this function. */ void SV_FinalMessage(char *message, qboolean reconnect) { int i; client_t *cl; SZ_Clear(&net_message); MSG_WriteByte(&net_message, svc_print); MSG_WriteByte(&net_message, PRINT_HIGH); MSG_WriteString(&net_message, message); if (reconnect) { MSG_WriteByte(&net_message, svc_reconnect); } else { MSG_WriteByte(&net_message, svc_disconnect); } /* stagger the packets to crutch operating system limited buffers */ for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) { if (cl->state >= cs_connected) { Netchan_Transmit(&cl->netchan, net_message.cursize, net_message.data); } } for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) { if (cl->state >= cs_connected) { Netchan_Transmit(&cl->netchan, net_message.cursize, net_message.data); } } } /* * Called when each game quits, * before Sys_Quit or Sys_Error */ void SV_Shutdown(char *finalmsg, qboolean reconnect) { if (svs.clients) { SV_FinalMessage(finalmsg, reconnect); } Master_Shutdown(); SV_ShutdownGameProgs(); /* free current level */ if (sv.demofile) { FS_FCloseFile(sv.demofile); } memset(&sv, 0, sizeof(sv)); Com_SetServerState(sv.state); /* free server static data */ if (svs.clients) { Z_Free(svs.clients); } if (svs.client_entities) { Z_Free(svs.client_entities); } if (svs.demofile) { fclose(svs.demofile); } memset(&svs, 0, sizeof(svs)); } yquake2-QUAKE2_5_32/src/server/header/0000755000175000017500000000000012615162034020144 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/server/header/server.h0000644000175000017500000002142312615162034021625 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Main header file for the client * * ======================================================================= */ #ifndef SV_SERVER_H #define SV_SERVER_H #include "../../common/header/common.h" #include "../../game/header/game.h" #define MAX_MASTERS 8 #define LATENCY_COUNTS 16 #define RATE_MESSAGES 10 /* MAX_CHALLENGES is made large to prevent a denial of service attack that could cycle all of them out before legitimate users connected */ #define MAX_CHALLENGES 1024 #define SV_OUTPUTBUF_LENGTH (MAX_MSGLEN - 16) #define EDICT_NUM(n) ((edict_t *)((byte *)ge->edicts + ge->edict_size * (n))) #define NUM_FOR_EDICT(e) (((byte *)(e) - (byte *)ge->edicts) / ge->edict_size) typedef enum { ss_dead, /* no map loaded */ ss_loading, /* spawning level edicts */ ss_game, /* actively running */ ss_cinematic, ss_demo, ss_pic } server_state_t; typedef struct { server_state_t state; /* precache commands are only valid during load */ qboolean attractloop; /* running cinematics and demos for the local system only */ qboolean loadgame; /* client begins should reuse existing entity */ unsigned time; /* always sv.framenum * 100 msec */ int framenum; char name[MAX_QPATH]; /* map name, or cinematic name */ struct cmodel_s *models[MAX_MODELS]; char configstrings[MAX_CONFIGSTRINGS][MAX_QPATH]; entity_state_t baselines[MAX_EDICTS]; /* the multicast buffer is used to send a message to a set of clients it is only used to marshall data until SV_Multicast is called */ sizebuf_t multicast; byte multicast_buf[MAX_MSGLEN]; /* demo server information */ fileHandle_t demofile; qboolean timedemo; /* don't time sync */ } server_t; typedef enum { cs_free, /* can be reused for a new connection */ cs_zombie, /* client has been disconnected, but don't reuse connection for a couple seconds */ cs_connected, /* has been assigned to a client_t, but not in game yet */ cs_spawned /* client is fully in game */ } client_state_t; typedef struct { int areabytes; byte areabits[MAX_MAP_AREAS / 8]; /* portalarea visibility bits */ player_state_t ps; int num_entities; int first_entity; /* into the circular sv_packet_entities[] */ int senttime; /* for ping calculations */ } client_frame_t; typedef struct client_s { client_state_t state; char userinfo[MAX_INFO_STRING]; /* name, etc */ int lastframe; /* for delta compression */ usercmd_t lastcmd; /* for filling in big drops */ int commandMsec; /* every seconds this is reset, if user */ /* commands exhaust it, assume time cheating */ int frame_latency[LATENCY_COUNTS]; int ping; int message_size[RATE_MESSAGES]; /* used to rate drop packets */ int rate; int surpressCount; /* number of messages rate supressed */ edict_t *edict; /* EDICT_NUM(clientnum+1) */ char name[32]; /* extracted from userinfo, high bits masked */ int messagelevel; /* for filtering printed messages */ /* The datagram is written to by sound calls, prints, temp ents, etc. It can be harmlessly overflowed. */ sizebuf_t datagram; byte datagram_buf[MAX_MSGLEN]; client_frame_t frames[UPDATE_BACKUP]; /* updates can be delta'd from here */ byte *download; /* file being downloaded */ int downloadsize; /* total bytes (can't use EOF because of paks) */ int downloadcount; /* bytes sent */ int lastmessage; /* sv.framenum when packet was last received */ int lastconnect; int challenge; /* challenge of this user, randomly generated */ netchan_t netchan; } client_t; typedef struct { netadr_t adr; int challenge; int time; } challenge_t; typedef struct { qboolean initialized; /* sv_init has completed */ int realtime; /* always increasing, no clamping, etc */ char mapcmd[MAX_TOKEN_CHARS]; /* ie: *intro.cin+base */ int spawncount; /* incremented each server start */ /* used to check late spawns */ client_t *clients; /* [maxclients->value]; */ int num_client_entities; /* maxclients->value*UPDATE_BACKUP*MAX_PACKET_ENTITIES */ int next_client_entities; /* next client_entity to use */ entity_state_t *client_entities; /* [num_client_entities] */ int last_heartbeat; challenge_t challenges[MAX_CHALLENGES]; /* to prevent invalid IPs from connecting */ /* serverrecord values */ FILE *demofile; sizebuf_t demo_multicast; byte demo_multicast_buf[MAX_MSGLEN]; } server_static_t; extern netadr_t net_from; extern sizebuf_t net_message; extern netadr_t master_adr[MAX_MASTERS]; /* address of the master server */ extern server_static_t svs; /* persistant server info */ extern server_t sv; /* local server */ extern cvar_t *sv_paused; extern cvar_t *maxclients; extern cvar_t *sv_noreload; /* don't reload level state when reentering */ extern cvar_t *sv_airaccelerate; /* don't reload level state when reentering */ /* development tool */ extern cvar_t *sv_enforcetime; extern client_t *sv_client; extern edict_t *sv_player; void SV_FinalMessage(char *message, qboolean reconnect); void SV_DropClient(client_t *drop); int SV_ModelIndex(char *name); int SV_SoundIndex(char *name); int SV_ImageIndex(char *name); void SV_WriteClientdataToMessage(client_t *client, sizebuf_t *msg); void SV_ExecuteUserCommand(char *s); void SV_InitOperatorCommands(void); void SV_SendServerinfo(client_t *client); void SV_UserinfoChanged(client_t *cl); void Master_Heartbeat(void); void Master_Packet(void); void SV_InitGame(void); void SV_Map(qboolean attractloop, char *levelstring, qboolean loadgame); void SV_PrepWorldFrame(void); typedef enum {RD_NONE, RD_CLIENT, RD_PACKET} redirect_t; extern char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; void SV_FlushRedirect(int sv_redirected, char *outputbuf); void SV_DemoCompleted(void); void SV_SendClientMessages(void); void SV_Multicast(vec3_t origin, multicast_t to); void SV_StartSound(vec3_t origin, edict_t *entity, int channel, int soundindex, float volume, float attenuation, float timeofs); void SV_ClientPrintf(client_t *cl, int level, char *fmt, ...); void SV_BroadcastPrintf(int level, char *fmt, ...); void SV_BroadcastCommand(char *fmt, ...); void SV_Nextserver(void); void SV_ExecuteClientMessage(client_t *cl); void SV_ReadLevelFile(void); void SV_Status_f(void); void SV_WriteFrameToClient(client_t *client, sizebuf_t *msg); void SV_RecordDemoMessage(void); void SV_BuildClientFrame(client_t *client); void SV_Error(char *error, ...); extern game_export_t *ge; void SV_InitGameProgs(void); void SV_ShutdownGameProgs(void); void SV_InitEdict(edict_t *e); /* server side savegame stuff */ void SV_WipeSavegame(char *savename); void SV_CopySaveGame(char *src, char *dst); void SV_WriteLevelFile(void); void SV_WriteServerFile(qboolean autosave); void SV_Loadgame_f(void); void SV_Savegame_f(void); /* high level object sorting to reduce interaction tests */ 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 */ void SV_LinkEdict(edict_t *ent); /* Needs to be called any time an entity changes origin, mins, maxs, or solid. Automatically unlinks if needed. sets ent->v.absmin and ent->v.absmax sets ent->leafnums[] for pvs determination even if the entity is not solid */ int SV_AreaEdicts(vec3_t mins, vec3_t maxs, edict_t **list, int maxcount, int areatype); int SV_PointContents(vec3_t p); trace_t SV_Trace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *passedict, int contentmask); #endif yquake2-QUAKE2_5_32/src/server/sv_entities.c0000644000175000017500000003515212615162034021422 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 entity handling. Just encodes the entties of a client side * frame into network / local communication packages and sends them to * the appropriate clients. * * ======================================================================= */ #include "header/server.h" byte fatpvs[65536 / 8]; /* * Writes a delta update of an entity_state_t list to the message. */ void SV_EmitPacketEntities(client_frame_t *from, client_frame_t *to, sizebuf_t *msg) { entity_state_t *oldent, *newent; int oldindex, newindex; int oldnum, newnum; int from_num_entities; int bits; MSG_WriteByte(msg, svc_packetentities); if (!from) { from_num_entities = 0; } else { from_num_entities = from->num_entities; } newindex = 0; oldindex = 0; newent = NULL; oldent = NULL; while (newindex < to->num_entities || oldindex < from_num_entities) { if (msg->cursize > MAX_MSGLEN - 150) { break; } if (newindex >= to->num_entities) { newnum = 9999; } else { newent = &svs.client_entities[(to->first_entity + newindex) % svs.num_client_entities]; newnum = newent->number; } if (oldindex >= from_num_entities) { oldnum = 9999; } else { oldent = &svs.client_entities[(from->first_entity + oldindex) % svs.num_client_entities]; oldnum = oldent->number; } if (newnum == oldnum) { /* delta update from old position. because the force parm is false, this will not result in any bytes being emited if the entity has not changed at all note that players are always 'newentities', this updates their oldorigin always and prevents warping */ MSG_WriteDeltaEntity(oldent, newent, msg, false, newent->number <= maxclients->value); oldindex++; newindex++; continue; } if (newnum < oldnum) { /* this is a new entity, send it from the baseline */ MSG_WriteDeltaEntity(&sv.baselines[newnum], newent, msg, true, true); newindex++; continue; } if (newnum > oldnum) { /* the old entity isn't present in the new message */ bits = U_REMOVE; if (oldnum >= 256) { bits |= U_NUMBER16 | U_MOREBITS1; } MSG_WriteByte(msg, bits & 255); if (bits & 0x0000ff00) { MSG_WriteByte(msg, (bits >> 8) & 255); } if (bits & U_NUMBER16) { MSG_WriteShort(msg, oldnum); } else { MSG_WriteByte(msg, oldnum); } oldindex++; continue; } } MSG_WriteShort(msg, 0); } void SV_WritePlayerstateToClient(client_frame_t *from, client_frame_t *to, sizebuf_t *msg) { int i; int pflags; player_state_t *ps, *ops; player_state_t dummy; int statbits; ps = &to->ps; if (!from) { memset(&dummy, 0, sizeof(dummy)); ops = &dummy; } else { ops = &from->ps; } /* determine what needs to be sent */ pflags = 0; if (ps->pmove.pm_type != ops->pmove.pm_type) { pflags |= PS_M_TYPE; } if ((ps->pmove.origin[0] != ops->pmove.origin[0]) || (ps->pmove.origin[1] != ops->pmove.origin[1]) || (ps->pmove.origin[2] != ops->pmove.origin[2])) { pflags |= PS_M_ORIGIN; } if ((ps->pmove.velocity[0] != ops->pmove.velocity[0]) || (ps->pmove.velocity[1] != ops->pmove.velocity[1]) || (ps->pmove.velocity[2] != ops->pmove.velocity[2])) { pflags |= PS_M_VELOCITY; } if (ps->pmove.pm_time != ops->pmove.pm_time) { pflags |= PS_M_TIME; } if (ps->pmove.pm_flags != ops->pmove.pm_flags) { pflags |= PS_M_FLAGS; } if (ps->pmove.gravity != ops->pmove.gravity) { pflags |= PS_M_GRAVITY; } if ((ps->pmove.delta_angles[0] != ops->pmove.delta_angles[0]) || (ps->pmove.delta_angles[1] != ops->pmove.delta_angles[1]) || (ps->pmove.delta_angles[2] != ops->pmove.delta_angles[2])) { pflags |= PS_M_DELTA_ANGLES; } if ((ps->viewoffset[0] != ops->viewoffset[0]) || (ps->viewoffset[1] != ops->viewoffset[1]) || (ps->viewoffset[2] != ops->viewoffset[2])) { pflags |= PS_VIEWOFFSET; } if ((ps->viewangles[0] != ops->viewangles[0]) || (ps->viewangles[1] != ops->viewangles[1]) || (ps->viewangles[2] != ops->viewangles[2])) { pflags |= PS_VIEWANGLES; } if ((ps->kick_angles[0] != ops->kick_angles[0]) || (ps->kick_angles[1] != ops->kick_angles[1]) || (ps->kick_angles[2] != ops->kick_angles[2])) { pflags |= PS_KICKANGLES; } if ((ps->blend[0] != ops->blend[0]) || (ps->blend[1] != ops->blend[1]) || (ps->blend[2] != ops->blend[2]) || (ps->blend[3] != ops->blend[3])) { pflags |= PS_BLEND; } if (ps->fov != ops->fov) { pflags |= PS_FOV; } if (ps->rdflags != ops->rdflags) { pflags |= PS_RDFLAGS; } if (ps->gunframe != ops->gunframe) { pflags |= PS_WEAPONFRAME; } pflags |= PS_WEAPONINDEX; /* write it */ MSG_WriteByte(msg, svc_playerinfo); MSG_WriteShort(msg, pflags); /* write the pmove_state_t */ if (pflags & PS_M_TYPE) { MSG_WriteByte(msg, ps->pmove.pm_type); } if (pflags & PS_M_ORIGIN) { MSG_WriteShort(msg, ps->pmove.origin[0]); MSG_WriteShort(msg, ps->pmove.origin[1]); MSG_WriteShort(msg, ps->pmove.origin[2]); } if (pflags & PS_M_VELOCITY) { MSG_WriteShort(msg, ps->pmove.velocity[0]); MSG_WriteShort(msg, ps->pmove.velocity[1]); MSG_WriteShort(msg, ps->pmove.velocity[2]); } if (pflags & PS_M_TIME) { MSG_WriteByte(msg, ps->pmove.pm_time); } if (pflags & PS_M_FLAGS) { MSG_WriteByte(msg, ps->pmove.pm_flags); } if (pflags & PS_M_GRAVITY) { MSG_WriteShort(msg, ps->pmove.gravity); } if (pflags & PS_M_DELTA_ANGLES) { MSG_WriteShort(msg, ps->pmove.delta_angles[0]); MSG_WriteShort(msg, ps->pmove.delta_angles[1]); MSG_WriteShort(msg, ps->pmove.delta_angles[2]); } /* write the rest of the player_state_t */ if (pflags & PS_VIEWOFFSET) { MSG_WriteChar(msg, ps->viewoffset[0] * 4); MSG_WriteChar(msg, ps->viewoffset[1] * 4); MSG_WriteChar(msg, ps->viewoffset[2] * 4); } if (pflags & PS_VIEWANGLES) { MSG_WriteAngle16(msg, ps->viewangles[0]); MSG_WriteAngle16(msg, ps->viewangles[1]); MSG_WriteAngle16(msg, ps->viewangles[2]); } if (pflags & PS_KICKANGLES) { MSG_WriteChar(msg, ps->kick_angles[0] * 4); MSG_WriteChar(msg, ps->kick_angles[1] * 4); MSG_WriteChar(msg, ps->kick_angles[2] * 4); } if (pflags & PS_WEAPONINDEX) { MSG_WriteByte(msg, ps->gunindex); } if (pflags & PS_WEAPONFRAME) { MSG_WriteByte(msg, ps->gunframe); MSG_WriteChar(msg, ps->gunoffset[0] * 4); MSG_WriteChar(msg, ps->gunoffset[1] * 4); MSG_WriteChar(msg, ps->gunoffset[2] * 4); MSG_WriteChar(msg, ps->gunangles[0] * 4); MSG_WriteChar(msg, ps->gunangles[1] * 4); MSG_WriteChar(msg, ps->gunangles[2] * 4); } if (pflags & PS_BLEND) { MSG_WriteByte(msg, ps->blend[0] * 255); MSG_WriteByte(msg, ps->blend[1] * 255); MSG_WriteByte(msg, ps->blend[2] * 255); MSG_WriteByte(msg, ps->blend[3] * 255); } if (pflags & PS_FOV) { MSG_WriteByte(msg, ps->fov); } if (pflags & PS_RDFLAGS) { MSG_WriteByte(msg, ps->rdflags); } /* send stats */ statbits = 0; for (i = 0; i < MAX_STATS; i++) { if (ps->stats[i] != ops->stats[i]) { statbits |= 1 << i; } } MSG_WriteLong(msg, statbits); for (i = 0; i < MAX_STATS; i++) { if (statbits & (1 << i)) { MSG_WriteShort(msg, ps->stats[i]); } } } void SV_WriteFrameToClient(client_t *client, sizebuf_t *msg) { client_frame_t *frame, *oldframe; int lastframe; /* this is the frame we are creating */ frame = &client->frames[sv.framenum & UPDATE_MASK]; if (client->lastframe <= 0) { /* client is asking for a retransmit */ oldframe = NULL; lastframe = -1; } else if (sv.framenum - client->lastframe >= (UPDATE_BACKUP - 3)) { /* client hasn't gotten a good message through in a long time */ oldframe = NULL; lastframe = -1; } else { /* we have a valid message to delta from */ oldframe = &client->frames[client->lastframe & UPDATE_MASK]; lastframe = client->lastframe; } MSG_WriteByte(msg, svc_frame); MSG_WriteLong(msg, sv.framenum); MSG_WriteLong(msg, lastframe); /* what we are delta'ing from */ MSG_WriteByte(msg, client->surpressCount); /* rate dropped packets */ client->surpressCount = 0; /* send over the areabits */ MSG_WriteByte(msg, frame->areabytes); SZ_Write(msg, frame->areabits, frame->areabytes); /* delta encode the playerstate */ SV_WritePlayerstateToClient(oldframe, frame, msg); /* delta encode the entities */ SV_EmitPacketEntities(oldframe, frame, msg); } /* * The client will interpolate the view position, * so we can't use a single PVS point */ void SV_FatPVS(vec3_t org) { int leafs[64]; int i, j, count; int longs; byte *src; vec3_t mins, maxs; for (i = 0; i < 3; i++) { mins[i] = org[i] - 8; maxs[i] = org[i] + 8; } count = CM_BoxLeafnums(mins, maxs, leafs, 64, NULL); if (count < 1) { Com_Error(ERR_FATAL, "SV_FatPVS: count < 1"); } longs = (CM_NumClusters() + 31) >> 5; /* convert leafs to clusters */ for (i = 0; i < count; i++) { leafs[i] = CM_LeafCluster(leafs[i]); } memcpy(fatpvs, CM_ClusterPVS(leafs[0]), longs << 2); /* or in all the other leaf bits */ for (i = 1; i < count; i++) { for (j = 0; j < i; j++) { if (leafs[i] == leafs[j]) { break; } } if (j != i) { continue; /* already have the cluster we want */ } src = CM_ClusterPVS(leafs[i]); for (j = 0; j < longs; j++) { ((long *)fatpvs)[j] |= ((long *)src)[j]; } } } /* * Decides which entities are going to be visible to the client, and * copies off the playerstat and areabits. */ void SV_BuildClientFrame(client_t *client) { int e, i; vec3_t org; edict_t *ent; edict_t *clent; client_frame_t *frame; entity_state_t *state; int l; int clientarea, clientcluster; int leafnum; int c_fullsend; byte *clientphs; byte *bitvector; clent = client->edict; if (!clent->client) { return; /* not in game yet */ } /* this is the frame we are creating */ frame = &client->frames[sv.framenum & UPDATE_MASK]; frame->senttime = svs.realtime; /* save it for ping calc later */ /* find the client's PVS */ for (i = 0; i < 3; i++) { org[i] = clent->client->ps.pmove.origin[i] * 0.125 + clent->client->ps.viewoffset[i]; } leafnum = CM_PointLeafnum(org); clientarea = CM_LeafArea(leafnum); clientcluster = CM_LeafCluster(leafnum); /* calculate the visible areas */ frame->areabytes = CM_WriteAreaBits(frame->areabits, clientarea); /* grab the current player_state_t */ frame->ps = clent->client->ps; SV_FatPVS(org); clientphs = CM_ClusterPHS(clientcluster); /* build up the list of visible entities */ frame->num_entities = 0; frame->first_entity = svs.next_client_entities; c_fullsend = 0; for (e = 1; e < ge->num_edicts; e++) { ent = EDICT_NUM(e); /* ignore ents without visible models */ if (ent->svflags & SVF_NOCLIENT) { continue; } /* ignore ents without visible models unless they have an effect */ if (!ent->s.modelindex && !ent->s.effects && !ent->s.sound && !ent->s.event) { continue; } /* ignore if not touching a PV leaf */ if (ent != clent) { /* check area */ if (!CM_AreasConnected(clientarea, ent->areanum)) { /* doors can legally straddle two areas, so we may need to check another one */ if (!ent->areanum2 || !CM_AreasConnected(clientarea, ent->areanum2)) { continue; /* blocked by a door */ } } /* beams just check one point for PHS */ if (ent->s.renderfx & RF_BEAM) { l = ent->clusternums[0]; if (!(clientphs[l >> 3] & (1 << (l & 7)))) { continue; } } else { bitvector = fatpvs; if (ent->num_clusters == -1) { /* too many leafs for individual check, go by headnode */ if (!CM_HeadnodeVisible(ent->headnode, bitvector)) { continue; } c_fullsend++; } else { /* check individual leafs */ for (i = 0; i < ent->num_clusters; i++) { l = ent->clusternums[i]; if (bitvector[l >> 3] & (1 << (l & 7))) { break; } } if (i == ent->num_clusters) { continue; /* not visible */ } } if (!ent->s.modelindex) { /* don't send sounds if they will be attenuated away */ vec3_t delta; float len; VectorSubtract(org, ent->s.origin, delta); len = VectorLength(delta); if (len > 400) { continue; } } } } /* add it to the circular client_entities array */ state = &svs.client_entities[svs.next_client_entities % svs.num_client_entities]; if (ent->s.number != e) { Com_DPrintf("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } *state = ent->s; /* don't mark players missiles as solid */ if (ent->owner == client->edict) { state->solid = 0; } svs.next_client_entities++; frame->num_entities++; } } /* * Save everything in the world out without deltas. * Used for recording footage for merged or assembled demos */ void SV_RecordDemoMessage(void) { int e; edict_t *ent; entity_state_t nostate; sizebuf_t buf; byte buf_data[32768]; int len; if (!svs.demofile) { return; } memset(&nostate, 0, sizeof(nostate)); SZ_Init(&buf, buf_data, sizeof(buf_data)); /* write a frame message that doesn't contain a player_state_t */ MSG_WriteByte(&buf, svc_frame); MSG_WriteLong(&buf, sv.framenum); MSG_WriteByte(&buf, svc_packetentities); e = 1; ent = EDICT_NUM(e); while (e < ge->num_edicts) { /* ignore ents without visible models unless they have an effect */ if (ent->inuse && ent->s.number && (ent->s.modelindex || ent->s.effects || ent->s.sound || ent->s.event) && !(ent->svflags & SVF_NOCLIENT)) { MSG_WriteDeltaEntity(&nostate, &ent->s, &buf, false, true); } e++; ent = EDICT_NUM(e); } MSG_WriteShort(&buf, 0); /* end of packetentities */ /* now add the accumulated multicast information */ SZ_Write(&buf, svs.demo_multicast.data, svs.demo_multicast.cursize); SZ_Clear(&svs.demo_multicast); /* now write the entire message to the file, prefixed by the length */ len = LittleLong(buf.cursize); fwrite(&len, 4, 1, svs.demofile); fwrite(buf.data, buf.cursize, 1, svs.demofile); } yquake2-QUAKE2_5_32/src/server/sv_world.c0000644000175000017500000003266612615162034020734 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Interface to the world model. Clipping and stuff like that... * * ======================================================================= */ #include "header/server.h" #define AREA_DEPTH 4 #define AREA_NODES 32 #define MAX_TOTAL_ENT_LEAFS 128 #define STRUCT_FROM_LINK(l, t, m) ((t *)((byte *)l - (byte *)&(((t *)NULL)->m))) #define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l, edict_t, area) 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; areanode_t sv_areanodes[AREA_NODES]; int sv_numareanodes; float *area_mins, *area_maxs; edict_t **area_list; int area_count, area_maxcount; int area_type; int SV_HullForEntity(edict_t *ent); /* 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; } /* * Builds a uniformly subdivided tree for the given world size */ 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.5f * (maxs[anode->axis] + mins[anode->axis]); VectorCopy(mins, mins1); VectorCopy(mins, mins2); VectorCopy(maxs, maxs1); VectorCopy(maxs, maxs2); maxs1[anode->axis] = mins2[anode->axis] = anode->dist; anode->children[0] = SV_CreateAreaNode(depth + 1, mins2, maxs2); anode->children[1] = SV_CreateAreaNode(depth + 1, mins1, maxs1); return anode; } void SV_ClearWorld(void) { memset(sv_areanodes, 0, sizeof(sv_areanodes)); sv_numareanodes = 0; SV_CreateAreaNode(0, sv.models[1]->mins, sv.models[1]->maxs); } void SV_UnlinkEdict(edict_t *ent) { if (!ent->area.prev) { return; /* not linked in anywhere */ } RemoveLink(&ent->area); ent->area.prev = ent->area.next = NULL; } void SV_LinkEdict(edict_t *ent) { areanode_t *node; int leafs[MAX_TOTAL_ENT_LEAFS]; int clusters[MAX_TOTAL_ENT_LEAFS]; int num_leafs; int i, j, k; int area; int topnode; if (ent->area.prev) { SV_UnlinkEdict(ent); /* unlink from old position */ } if (ent == ge->edicts) { return; /* don't add the world */ } if (!ent->inuse) { return; } /* set the size */ VectorSubtract(ent->maxs, ent->mins, ent->size); /* encode the size into the entity_state for client prediction */ if ((ent->solid == SOLID_BBOX) && !(ent->svflags & SVF_DEADMONSTER)) { /* assume that x/y are equal and symetric */ i = (int)ent->maxs[0] / 8; if (i < 1) { i = 1; } if (i > 31) { i = 31; } /* z is not symetric */ j = (int)(-ent->mins[2]) / 8; if (j < 1) { j = 1; } if (j > 31) { j = 31; } /* and z maxs can be negative... */ k = (int)(ent->maxs[2] + 32) / 8; if (k < 1) { k = 1; } if (k > 63) { k = 63; } ent->s.solid = (k << 10) | (j << 5) | i; } else if (ent->solid == SOLID_BSP) { ent->s.solid = 31; /* a solid_bbox will never create this value */ } else { ent->s.solid = 0; } /* set the abs box */ if ((ent->solid == SOLID_BSP) && (ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2])) { /* expand for rotation */ float max, v; int i; max = 0; for (i = 0; i < 3; i++) { v = (float)fabs(ent->mins[i]); if (v > max) { max = v; } v = (float)fabs(ent->maxs[i]); if (v > max) { max = v; } } for (i = 0; i < 3; i++) { ent->absmin[i] = ent->s.origin[i] - max; ent->absmax[i] = ent->s.origin[i] + max; } } else { /* normal */ VectorAdd(ent->s.origin, ent->mins, ent->absmin); VectorAdd(ent->s.origin, ent->maxs, ent->absmax); } /* because movement is clipped an epsilon away from an actual edge, we must fully check even when bounding boxes don't quite touch */ ent->absmin[0] -= 1; ent->absmin[1] -= 1; ent->absmin[2] -= 1; ent->absmax[0] += 1; ent->absmax[1] += 1; ent->absmax[2] += 1; /* link to PVS leafs */ ent->num_clusters = 0; ent->areanum = 0; ent->areanum2 = 0; /* get all leafs, including solids */ num_leafs = CM_BoxLeafnums(ent->absmin, ent->absmax, leafs, MAX_TOTAL_ENT_LEAFS, &topnode); /* set areas */ for (i = 0; i < num_leafs; i++) { clusters[i] = CM_LeafCluster(leafs[i]); area = CM_LeafArea(leafs[i]); if (area) { /* doors may legally straggle two areas, but nothing should evern need more than that */ if (ent->areanum && (ent->areanum != area)) { if (ent->areanum2 && (ent->areanum2 != area) && (sv.state == ss_loading)) { Com_DPrintf("Object touching 3 areas at %f %f %f\n", ent->absmin[0], ent->absmin[1], ent->absmin[2]); } ent->areanum2 = area; } else { ent->areanum = area; } } } if (num_leafs >= MAX_TOTAL_ENT_LEAFS) { /* assume we missed some leafs, and mark by headnode */ ent->num_clusters = -1; ent->headnode = topnode; } else { ent->num_clusters = 0; for (i = 0; i < num_leafs; i++) { if (clusters[i] == -1) { continue; /* not a visible leaf */ } for (j = 0; j < i; j++) { if (clusters[j] == clusters[i]) { break; } } if (j == i) { if (ent->num_clusters == MAX_ENT_CLUSTERS) { /* assume we missed some leafs, and mark by headnode */ ent->num_clusters = -1; ent->headnode = topnode; break; } ent->clusternums[ent->num_clusters++] = clusters[i]; } } } /* if first time, make sure old_origin is valid */ if (!ent->linkcount) { VectorCopy(ent->s.origin, ent->s.old_origin); } ent->linkcount++; if (ent->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->absmin[node->axis] > node->dist) { node = node->children[0]; } else if (ent->absmax[node->axis] < node->dist) { node = node->children[1]; } else { break; /* crosses the node */ } } /* link it in */ if (ent->solid == SOLID_TRIGGER) { InsertLinkBefore(&ent->area, &node->trigger_edicts); } else { InsertLinkBefore(&ent->area, &node->solid_edicts); } } void SV_AreaEdicts_r(areanode_t *node) { link_t *l, *next, *start; edict_t *check; /* touch linked edicts */ if (area_type == AREA_SOLID) { start = &node->solid_edicts; } else { start = &node->trigger_edicts; } for (l = start->next; l != start; l = next) { next = l->next; check = (EDICT_FROM_AREA(l)); if (check->solid == SOLID_NOT) { continue; /* deactivated */ } if ((check->absmin[0] > area_maxs[0]) || (check->absmin[1] > area_maxs[1]) || (check->absmin[2] > area_maxs[2]) || (check->absmax[0] < area_mins[0]) || (check->absmax[1] < area_mins[1]) || (check->absmax[2] < area_mins[2])) { continue; /* not touching */ } if (area_count == area_maxcount) { Com_Printf("SV_AreaEdicts: MAXCOUNT\n"); return; } area_list[area_count] = check; area_count++; } if (node->axis == -1) { return; /* terminal node */ } /* recurse down both sides */ if (area_maxs[node->axis] > node->dist) { SV_AreaEdicts_r(node->children[0]); } if (area_mins[node->axis] < node->dist) { SV_AreaEdicts_r(node->children[1]); } } int SV_AreaEdicts(vec3_t mins, vec3_t maxs, edict_t **list, int maxcount, int areatype) { area_mins = mins; area_maxs = maxs; area_list = list; area_maxcount = maxcount; area_type = areatype; area_count = 0; SV_AreaEdicts_r(sv_areanodes); area_mins = 0; area_maxs = 0; area_list = 0; area_maxcount = 0; area_type = 0; return area_count; } int SV_PointContents(vec3_t p) { edict_t *touch[MAX_EDICTS], *hit; int i, num; int contents, c2; int headnode; /* get base contents from world */ contents = CM_PointContents(p, sv.models[1]->headnode); /* or in contents from all the other entities */ num = SV_AreaEdicts(p, p, touch, MAX_EDICTS, AREA_SOLID); for (i = 0; i < num; i++) { hit = touch[i]; /* might intersect, so do an exact clip */ headnode = SV_HullForEntity(hit); c2 = CM_TransformedPointContents(p, headnode, hit->s.origin, hit->s.angles); contents |= c2; } return contents; } 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; edict_t *passedict; int contentmask; } moveclip_t; /* * Returns a headnode 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. */ int SV_HullForEntity(edict_t *ent) { cmodel_t *model; /* decide which clipping hull to use, based on the size */ if (ent->solid == SOLID_BSP) { /* explicit hulls in the BSP model */ model = sv.models[ent->s.modelindex]; if (!model) { Com_Error(ERR_FATAL, "MOVETYPE_PUSH with a non bsp model"); } return model->headnode; } /* create a temp hull from bounding box sizes */ return CM_HeadnodeForBox(ent->mins, ent->maxs); } void SV_ClipMoveToEntities(moveclip_t *clip) { int i, num; edict_t *touchlist[MAX_EDICTS], *touch; trace_t trace; int headnode; float *angles; num = SV_AreaEdicts(clip->boxmins, clip->boxmaxs, touchlist, MAX_EDICTS, AREA_SOLID); /* be careful, it is possible to have an entity in this list removed before we get to it (killtriggered) */ for (i = 0; i < num; i++) { touch = touchlist[i]; if (touch->solid == SOLID_NOT) { continue; } if (touch == clip->passedict) { continue; } if (clip->trace.allsolid) { return; } if (clip->passedict) { if (touch->owner == clip->passedict) { continue; /* don't clip against own missiles */ } if (clip->passedict->owner == touch) { continue; /* don't clip against owner */ } } if (!(clip->contentmask & CONTENTS_DEADMONSTER) && (touch->svflags & SVF_DEADMONSTER)) { continue; } /* might intersect, so do an exact clip */ headnode = SV_HullForEntity(touch); angles = touch->s.angles; if (touch->solid != SOLID_BSP) { angles = vec3_origin; /* boxes don't rotate */ } if (touch->svflags & SVF_MONSTER) { trace = CM_TransformedBoxTrace(clip->start, clip->end, clip->mins2, clip->maxs2, headnode, clip->contentmask, touch->s.origin, angles); } else { trace = CM_TransformedBoxTrace(clip->start, clip->end, clip->mins, clip->maxs, headnode, clip->contentmask, touch->s.origin, angles); } 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; } } } void SV_TraceBounds(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs) { int i; for (i = 0; i < 3; i++) { if (end[i] > start[i]) { boxmins[i] = start[i] + mins[i] - 1; boxmaxs[i] = end[i] + maxs[i] + 1; } else { boxmins[i] = end[i] + mins[i] - 1; boxmaxs[i] = start[i] + maxs[i] + 1; } } } /* * Moves the given mins/maxs volume through the world from start to end. * Passedict and edicts owned by passedict are explicitly not checked. */ trace_t SV_Trace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *passedict, int contentmask) { moveclip_t clip; if (!mins) { mins = vec3_origin; } if (!maxs) { maxs = vec3_origin; } memset(&clip, 0, sizeof(moveclip_t)); /* clip to world */ clip.trace = CM_BoxTrace(start, end, mins, maxs, 0, contentmask); clip.trace.ent = ge->edicts; if (clip.trace.fraction == 0) { return clip.trace; /* blocked by the world */ } clip.contentmask = contentmask; clip.start = start; clip.end = end; clip.mins = mins; clip.maxs = maxs; clip.passedict = passedict; VectorCopy(mins, clip.mins2); VectorCopy(maxs, clip.maxs2); /* create the bounding box of the entire move */ SV_TraceBounds(start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs); /* clip to other solid entities */ SV_ClipMoveToEntities(&clip); return clip.trace; } yquake2-QUAKE2_5_32/src/server/sv_game.c0000644000175000017500000002022212615162034020477 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Interface between the server and the game module. * * ======================================================================= */ #include "header/server.h" #ifndef DEDICATED_ONLY void SCR_DebugGraph(float value, int color); #endif game_export_t *ge; /* * Sends the contents of the mutlicast buffer to a single client */ void PF_Unicast(edict_t *ent, qboolean reliable) { int p; client_t *client; if (!ent) { return; } p = NUM_FOR_EDICT(ent); if ((p < 1) || (p > maxclients->value)) { return; } client = svs.clients + (p - 1); if (reliable) { SZ_Write(&client->netchan.message, sv.multicast.data, sv.multicast.cursize); } else { SZ_Write(&client->datagram, sv.multicast.data, sv.multicast.cursize); } SZ_Clear(&sv.multicast); } /* * Debug print to server console */ void PF_dprintf(char *fmt, ...) { char msg[1024]; va_list argptr; va_start(argptr, fmt); vsprintf(msg, fmt, argptr); va_end(argptr); Com_Printf("%s", msg); } /* * Print to a single client */ void PF_cprintf(edict_t *ent, int level, char *fmt, ...) { char msg[1024]; va_list argptr; int n; n = 0; if (ent) { n = NUM_FOR_EDICT(ent); if ((n < 1) || (n > maxclients->value)) { Com_Error(ERR_DROP, "cprintf to a non-client"); } } va_start(argptr, fmt); vsprintf(msg, fmt, argptr); va_end(argptr); if (ent) { SV_ClientPrintf(svs.clients + (n - 1), level, "%s", msg); } else { Com_Printf("%s", msg); } } /* * centerprint to a single client */ void PF_centerprintf(edict_t *ent, char *fmt, ...) { char msg[1024]; va_list argptr; int n; n = NUM_FOR_EDICT(ent); if ((n < 1) || (n > maxclients->value)) { return; } va_start(argptr, fmt); vsprintf(msg, fmt, argptr); va_end(argptr); MSG_WriteByte(&sv.multicast, svc_centerprint); MSG_WriteString(&sv.multicast, msg); PF_Unicast(ent, true); } /* * Abort the server with a game error */ void PF_error(char *fmt, ...) { char msg[1024]; va_list argptr; va_start(argptr, fmt); vsprintf(msg, fmt, argptr); va_end(argptr); Com_Error(ERR_DROP, "Game Error: %s", msg); } /* * Also sets mins and maxs for inline bmodels */ void PF_setmodel(edict_t *ent, char *name) { int i; cmodel_t *mod; if (!name) { Com_Error(ERR_DROP, "PF_setmodel: NULL"); } i = SV_ModelIndex(name); ent->s.modelindex = i; /* if it is an inline model, get the size information for it */ if (name[0] == '*') { mod = CM_InlineModel(name); VectorCopy(mod->mins, ent->mins); VectorCopy(mod->maxs, ent->maxs); SV_LinkEdict(ent); } } void PF_Configstring(int index, char *val) { if ((index < 0) || (index >= MAX_CONFIGSTRINGS)) { Com_Error(ERR_DROP, "configstring: bad index %i\n", index); } if (!val) { val = ""; } /* change the string in sv */ strcpy(sv.configstrings[index], val); if (sv.state != ss_loading) { /* send the update to everyone */ SZ_Clear(&sv.multicast); MSG_WriteChar(&sv.multicast, svc_configstring); MSG_WriteShort(&sv.multicast, index); MSG_WriteString(&sv.multicast, val); SV_Multicast(vec3_origin, MULTICAST_ALL_R); } } void PF_WriteChar(int c) { MSG_WriteChar(&sv.multicast, c); } void PF_WriteByte(int c) { MSG_WriteByte(&sv.multicast, c); } void PF_WriteShort(int c) { MSG_WriteShort(&sv.multicast, c); } void PF_WriteLong(int c) { MSG_WriteLong(&sv.multicast, c); } void PF_WriteFloat(float f) { MSG_WriteFloat(&sv.multicast, f); } void PF_WriteString(char *s) { MSG_WriteString(&sv.multicast, s); } void PF_WritePos(vec3_t pos) { MSG_WritePos(&sv.multicast, pos); } void PF_WriteDir(vec3_t dir) { MSG_WriteDir(&sv.multicast, dir); } void PF_WriteAngle(float f) { MSG_WriteAngle(&sv.multicast, f); } /* * Also checks portalareas so that doors block sight */ qboolean PF_inPVS(vec3_t p1, vec3_t p2) { int leafnum; int cluster; int area1, area2; byte *mask; leafnum = CM_PointLeafnum(p1); cluster = CM_LeafCluster(leafnum); area1 = CM_LeafArea(leafnum); mask = CM_ClusterPVS(cluster); leafnum = CM_PointLeafnum(p2); cluster = CM_LeafCluster(leafnum); area2 = CM_LeafArea(leafnum); if (mask && (!(mask[cluster >> 3] & (1 << (cluster & 7))))) { return false; } if (!CM_AreasConnected(area1, area2)) { return false; /* a door blocks sight */ } return true; } /* * Also checks portalareas so that doors block sound */ qboolean PF_inPHS(vec3_t p1, vec3_t p2) { int leafnum; int cluster; int area1, area2; byte *mask; leafnum = CM_PointLeafnum(p1); cluster = CM_LeafCluster(leafnum); area1 = CM_LeafArea(leafnum); mask = CM_ClusterPHS(cluster); leafnum = CM_PointLeafnum(p2); cluster = CM_LeafCluster(leafnum); area2 = CM_LeafArea(leafnum); if (mask && (!(mask[cluster >> 3] & (1 << (cluster & 7))))) { return false; /* more than one bounce away */ } if (!CM_AreasConnected(area1, area2)) { return false; /* a door blocks hearing */ } return true; } void PF_StartSound(edict_t *entity, int channel, int sound_num, float volume, float attenuation, float timeofs) { if (!entity) { return; } SV_StartSound(NULL, entity, channel, sound_num, volume, attenuation, timeofs); } /* * Called when either the entire server is being killed, or * it is changing to a different game directory. */ void SV_ShutdownGameProgs(void) { if (!ge) { return; } ge->Shutdown(); Sys_UnloadGame(); ge = NULL; } /* * Init the game subsystem for a new map */ void SV_InitGameProgs(void) { game_import_t import; /* unload anything we have now */ if (ge) { SV_ShutdownGameProgs(); } Com_Printf("-------- game initialization -------\n"); /* load a new game dll */ import.multicast = SV_Multicast; import.unicast = PF_Unicast; import.bprintf = SV_BroadcastPrintf; import.dprintf = PF_dprintf; import.cprintf = PF_cprintf; import.centerprintf = PF_centerprintf; import.error = PF_error; import.linkentity = SV_LinkEdict; import.unlinkentity = SV_UnlinkEdict; import.BoxEdicts = SV_AreaEdicts; import.trace = SV_Trace; import.pointcontents = SV_PointContents; import.setmodel = PF_setmodel; import.inPVS = PF_inPVS; import.inPHS = PF_inPHS; import.Pmove = Pmove; import.modelindex = SV_ModelIndex; import.soundindex = SV_SoundIndex; import.imageindex = SV_ImageIndex; import.configstring = PF_Configstring; import.sound = PF_StartSound; import.positioned_sound = SV_StartSound; import.WriteChar = PF_WriteChar; import.WriteByte = PF_WriteByte; import.WriteShort = PF_WriteShort; import.WriteLong = PF_WriteLong; import.WriteFloat = PF_WriteFloat; import.WriteString = PF_WriteString; import.WritePosition = PF_WritePos; import.WriteDir = PF_WriteDir; import.WriteAngle = PF_WriteAngle; import.TagMalloc = Z_TagMalloc; import.TagFree = Z_Free; import.FreeTags = Z_FreeTags; import.cvar = Cvar_Get; import.cvar_set = Cvar_Set; import.cvar_forceset = Cvar_ForceSet; import.argc = Cmd_Argc; import.argv = Cmd_Argv; import.args = Cmd_Args; import.AddCommandString = Cbuf_AddText; #ifndef DEDICATED_ONLY import.DebugGraph = SCR_DebugGraph; #endif import.SetAreaPortalState = CM_SetAreaPortalState; import.AreasConnected = CM_AreasConnected; ge = (game_export_t *)Sys_GetGameAPI(&import); if (!ge) { Com_Error(ERR_DROP, "failed to load game DLL"); } if (ge->apiversion != GAME_API_VERSION) { Com_Error(ERR_DROP, "game is version %i, not %i", ge->apiversion, GAME_API_VERSION); } ge->Init(); Com_Printf("------------------------------------\n\n"); } yquake2-QUAKE2_5_32/src/server/sv_cmd.c0000644000175000017500000003023612615162034020337 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 commands received by clients. There are only two ways on which * those can be received. Typed via stdin into the server console or via * a network / internal communication datagram. * * ======================================================================= */ #include "header/server.h" /* * Specify a list of master servers */ void SV_SetMaster_f(void) { int i, slot; /* only dedicated servers send heartbeats */ if (!dedicated->value) { Com_Printf("Only dedicated servers use masters.\n"); return; } /* make sure the server is listed public */ Cvar_Set("public", "1"); for (i = 1; i < MAX_MASTERS; i++) { memset(&master_adr[i], 0, sizeof(master_adr[i])); } slot = 1; /* slot 0 will always contain the id master */ for (i = 1; i < Cmd_Argc(); i++) { if (slot == MAX_MASTERS) { break; } if (!NET_StringToAdr(Cmd_Argv(i), &master_adr[i])) { Com_Printf("Bad address: %s\n", Cmd_Argv(i)); continue; } if (master_adr[slot].port == 0) { master_adr[slot].port = BigShort(PORT_MASTER); } Com_Printf("Master server at %s\n", NET_AdrToString(master_adr[slot])); Com_Printf("Sending a ping.\n"); Netchan_OutOfBandPrint(NS_SERVER, master_adr[slot], "ping"); slot++; } svs.last_heartbeat = -9999999; } /* * Sets sv_client and sv_player to the player with idnum Cmd_Argv(1) */ qboolean SV_SetPlayer(void) { client_t *cl; int i; int idnum; char *s; if (Cmd_Argc() < 2) { return false; } s = Cmd_Argv(1); /* numeric values are just slot numbers */ if ((s[0] >= '0') && (s[0] <= '9')) { idnum = (int)strtol(Cmd_Argv(1), (char **)NULL, 10); if ((idnum < 0) || (idnum >= maxclients->value)) { Com_Printf("Bad client slot: %i\n", idnum); return false; } sv_client = &svs.clients[idnum]; sv_player = sv_client->edict; if (!sv_client->state) { Com_Printf("Client %i is not active\n", idnum); return false; } return true; } /* check for a name match */ for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) { if (!cl->state) { continue; } if (!strcmp(cl->name, s)) { sv_client = cl; sv_player = sv_client->edict; return true; } } Com_Printf("Userid %s is not on the server\n", s); return false; } /* * Puts the server in demo mode on a specific map/cinematic */ void SV_DemoMap_f(void) { if (Cmd_Argc() != 2) { Com_Printf("USAGE: demomap \n"); return; } SV_Map(true, Cmd_Argv(1), false); } /* * Saves the state of the map just being exited and goes to a new map. * * If the initial character of the map string is '*', the next map is * in a new unit, so the current savegame directory is cleared of * map files. * * Example: * inter.cin+jail * * Clears the archived maps, plays the inter.cin cinematic, then * goes to map jail.bsp. */ void SV_GameMap_f(void) { char *map; int i; client_t *cl; qboolean *savedInuse; if (Cmd_Argc() != 2) { Com_Printf("USAGE: gamemap \n"); return; } Com_DPrintf("SV_GameMap(%s)\n", Cmd_Argv(1)); FS_CreatePath(va("%s/save/current/", FS_Gamedir())); /* check for clearing the current savegame */ map = Cmd_Argv(1); if (map[0] == '*') { /* wipe all the *.sav files */ SV_WipeSavegame("current"); } else { /* save the map just exited */ if (sv.state == ss_game) { /* clear all the client inuse flags before saving so that when the level is re-entered, the clients will spawn at spawn points instead of occupying body shells */ savedInuse = malloc(maxclients->value * sizeof(qboolean)); for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) { savedInuse[i] = cl->edict->inuse; cl->edict->inuse = false; } SV_WriteLevelFile(); /* we must restore these for clients to transfer over correctly */ for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) { cl->edict->inuse = savedInuse[i]; } free(savedInuse); } } /* start up the next map */ SV_Map(false, Cmd_Argv(1), false); /* archive server state */ Q_strlcpy(svs.mapcmd, Cmd_Argv(1), sizeof(svs.mapcmd)); /* copy off the level to the autosave slot */ if (!dedicated->value) { SV_WriteServerFile(true); SV_CopySaveGame("current", "save0"); } } /* * Goes directly to a given map without any savegame archiving. * For development work */ void SV_Map_f(void) { char *map; char expanded[MAX_QPATH]; if (Cmd_Argc() != 2) { Com_Printf("USAGE: map \n"); return; } /* if not a pcx, demo, or cinematic, check to make sure the level exists */ map = Cmd_Argv(1); if (!strstr(map, ".") && !strstr(map, "$") && (*map != '*')) { Com_sprintf(expanded, sizeof(expanded), "maps/%s.bsp", map); if (FS_LoadFile(expanded, NULL) == -1) { Com_Printf("Can't find %s\n", expanded); return; } } sv.state = ss_dead; /* don't save current level when changing */ SV_WipeSavegame("current"); SV_GameMap_f(); } /* * Kick a user off of the server */ void SV_Kick_f(void) { if (!svs.initialized) { Com_Printf("No server running.\n"); return; } if (Cmd_Argc() != 2) { Com_Printf("Usage: kick \n"); return; } if (!SV_SetPlayer()) { return; } if ((sv_client->state == cs_spawned) && *sv_client->name) { SV_BroadcastPrintf(PRINT_HIGH, "%s was kicked\n", sv_client->name); } /* print directly, because the dropped client won't get the SV_BroadcastPrintf message */ SV_ClientPrintf(sv_client, PRINT_HIGH, "You were kicked from the game\n"); SV_DropClient(sv_client); sv_client->lastmessage = svs.realtime; /* min case there is a funny zombie */ } void SV_Status_f(void) { int i, j, l; client_t *cl; char *s; int ping; if (!svs.clients) { Com_Printf("No server running.\n"); return; } Com_Printf("map : %s\n", sv.name); Com_Printf("num score ping name lastmsg address qport \n"); Com_Printf("--- ----- ---- --------------- ------- --------------------- ------\n"); for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) { if (!cl->state) { continue; } Com_Printf("%2i ", i); Com_Printf("%5i ", cl->edict->client->ps.stats[STAT_FRAGS]); if (cl->state == cs_connected) { Com_Printf("CNCT "); } else if (cl->state == cs_zombie) { Com_Printf("ZMBI "); } else { ping = cl->ping < 9999 ? cl->ping : 9999; Com_Printf("%4i ", ping); } Com_Printf("%s", cl->name); l = 16 - strlen(cl->name); for (j = 0; j < l; j++) { Com_Printf(" "); } Com_Printf("%7i ", svs.realtime - cl->lastmessage); s = NET_AdrToString(cl->netchan.remote_address); Com_Printf("%s", s); l = 22 - strlen(s); for (j = 0; j < l; j++) { Com_Printf(" "); } Com_Printf("%5i", cl->netchan.qport); Com_Printf("\n"); } Com_Printf("\n"); } void SV_ConSay_f(void) { client_t *client; int j; char *p; char text[1024]; if (Cmd_Argc() < 2) { return; } if (!svs.initialized) { Com_Printf("No server running.\n"); return; } strcpy(text, "console: "); p = Cmd_Args(); if (*p == '"') { p++; p[strlen(p) - 1] = 0; } strcat(text, p); for (j = 0, client = svs.clients; j < maxclients->value; j++, client++) { if (client->state != cs_spawned) { continue; } SV_ClientPrintf(client, PRINT_CHAT, "%s\n", text); } } void SV_Heartbeat_f(void) { svs.last_heartbeat = -9999999; } /* * Examine or change the serverinfo string */ void SV_Serverinfo_f(void) { Com_Printf("Server info settings:\n"); Info_Print(Cvar_Serverinfo()); } /* * Examine all a users info strings */ void SV_DumpUser_f(void) { if (!svs.initialized) { Com_Printf("No server running.\n"); return; } if (Cmd_Argc() != 2) { Com_Printf("Usage: info \n"); return; } if (!SV_SetPlayer()) { return; } Com_Printf("userinfo\n"); Com_Printf("--------\n"); Info_Print(sv_client->userinfo); } /* * Begins server demo recording. Every entity and every message will be * recorded, but no playerinfo will be stored. Primarily for demo merging. */ void SV_ServerRecord_f(void) { char name[MAX_OSPATH]; byte buf_data[32768]; sizebuf_t buf; int len; int i; if (Cmd_Argc() != 2) { Com_Printf("serverrecord \n"); return; } if (svs.demofile) { Com_Printf("Already recording.\n"); return; } if (sv.state != ss_game) { Com_Printf("You must be in a level to record.\n"); return; } if (strstr(Cmd_Argv(1), "..") || strstr(Cmd_Argv(1), "/") || strstr(Cmd_Argv(1), "\\")) { Com_Printf("Illegal filename.\n"); return; } /* open the demo file */ Com_sprintf(name, sizeof(name), "%s/demos/%s.dm2", FS_Gamedir(), Cmd_Argv(1)); Com_Printf("recording to %s.\n", name); FS_CreatePath(name); svs.demofile = fopen(name, "wb"); if (!svs.demofile) { Com_Printf("ERROR: couldn't open.\n"); return; } /* setup a buffer to catch all multicasts */ SZ_Init(&svs.demo_multicast, svs.demo_multicast_buf, sizeof(svs.demo_multicast_buf)); /* write a single giant fake message with all the startup info */ SZ_Init(&buf, buf_data, sizeof(buf_data)); /* serverdata needs to go over for all types of servers to make sure the protocol is right, and to set the gamedir */ MSG_WriteByte(&buf, svc_serverdata); MSG_WriteLong(&buf, PROTOCOL_VERSION); MSG_WriteLong(&buf, svs.spawncount); /* 2 means server demo */ MSG_WriteByte(&buf, 2); /* demos are always attract loops */ MSG_WriteString(&buf, (char *)Cvar_VariableString("gamedir")); MSG_WriteShort(&buf, -1); /* send full levelname */ MSG_WriteString(&buf, sv.configstrings[CS_NAME]); for (i = 0; i < MAX_CONFIGSTRINGS; i++) { if (sv.configstrings[i][0]) { MSG_WriteByte(&buf, svc_configstring); MSG_WriteShort(&buf, i); MSG_WriteString(&buf, sv.configstrings[i]); if (buf.cursize + 67 >= buf.maxsize) { Com_Printf("not enough buffer space available.\n"); fclose(svs.demofile); svs.demofile = NULL; return; } } } /* write it to the demo file */ Com_DPrintf("signon message length: %i\n", buf.cursize); len = LittleLong(buf.cursize); fwrite(&len, 4, 1, svs.demofile); fwrite(buf.data, buf.cursize, 1, svs.demofile); } /* * Ends server demo recording */ void SV_ServerStop_f(void) { if (!svs.demofile) { Com_Printf("Not doing a serverrecord.\n"); return; } fclose(svs.demofile); svs.demofile = NULL; Com_Printf("Recording completed.\n"); } /* * Kick everyone off, possibly in preparation for a new game */ void SV_KillServer_f(void) { if (!svs.initialized) { return; } SV_Shutdown("Server was killed.\n", false); NET_Config(false); /* close network sockets */ } /* * Let the game dll handle a command */ void SV_ServerCommand_f(void) { if (!ge) { Com_Printf("No game loaded.\n"); return; } ge->ServerCommand(); } void SV_InitOperatorCommands(void) { Cmd_AddCommand("heartbeat", SV_Heartbeat_f); Cmd_AddCommand("kick", SV_Kick_f); Cmd_AddCommand("status", SV_Status_f); Cmd_AddCommand("serverinfo", SV_Serverinfo_f); Cmd_AddCommand("dumpuser", SV_DumpUser_f); Cmd_AddCommand("map", SV_Map_f); Cmd_AddCommand("demomap", SV_DemoMap_f); Cmd_AddCommand("gamemap", SV_GameMap_f); Cmd_AddCommand("setmaster", SV_SetMaster_f); if (dedicated->value) { Cmd_AddCommand("say", SV_ConSay_f); } Cmd_AddCommand("serverrecord", SV_ServerRecord_f); Cmd_AddCommand("serverstop", SV_ServerStop_f); Cmd_AddCommand("save", SV_Savegame_f); Cmd_AddCommand("load", SV_Loadgame_f); Cmd_AddCommand("killserver", SV_KillServer_f); Cmd_AddCommand("sv", SV_ServerCommand_f); } yquake2-QUAKE2_5_32/src/server/sv_send.c0000644000175000017500000002716412615162034020533 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Message sending and multiplexing. * * ======================================================================= */ #include "header/server.h" char sv_outputbuf[SV_OUTPUTBUF_LENGTH]; void SV_FlushRedirect(int sv_redirected, char *outputbuf) { if (sv_redirected == RD_PACKET) { Netchan_OutOfBandPrint(NS_SERVER, net_from, "print\n%s", outputbuf); } else if (sv_redirected == RD_CLIENT) { MSG_WriteByte(&sv_client->netchan.message, svc_print); MSG_WriteByte(&sv_client->netchan.message, PRINT_HIGH); MSG_WriteString(&sv_client->netchan.message, outputbuf); } } /* * Sends text across to be displayed if the level passes */ void SV_ClientPrintf(client_t *cl, int level, char *fmt, ...) { va_list argptr; char string[1024]; if (level < cl->messagelevel) { return; } va_start(argptr, fmt); vsprintf(string, fmt, argptr); va_end(argptr); MSG_WriteByte(&cl->netchan.message, svc_print); MSG_WriteByte(&cl->netchan.message, level); MSG_WriteString(&cl->netchan.message, string); } /* * Sends text to all active clients */ void SV_BroadcastPrintf(int level, char *fmt, ...) { va_list argptr; char string[2048]; client_t *cl; int i; va_start(argptr, fmt); vsprintf(string, fmt, argptr); va_end(argptr); /* echo to console */ if (dedicated->value) { char copy[1024]; int i; /* mask off high bits */ for (i = 0; i < 1023 && string[i]; i++) { copy[i] = string[i] & 127; } copy[i] = 0; Com_Printf("%s", copy); } for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) { if (level < cl->messagelevel) { continue; } if (cl->state != cs_spawned) { continue; } MSG_WriteByte(&cl->netchan.message, svc_print); MSG_WriteByte(&cl->netchan.message, level); MSG_WriteString(&cl->netchan.message, string); } } /* * Sends text to all active clients */ void SV_BroadcastCommand(char *fmt, ...) { va_list argptr; char string[1024]; if (!sv.state) { return; } va_start(argptr, fmt); vsprintf(string, fmt, argptr); va_end(argptr); MSG_WriteByte(&sv.multicast, svc_stufftext); MSG_WriteString(&sv.multicast, string); SV_Multicast(NULL, MULTICAST_ALL_R); } /* * Sends the contents of sv.multicast to a subset of the clients, * then clears sv.multicast. * * MULTICAST_ALL same as broadcast (origin can be NULL) * MULTICAST_PVS send to clients potentially visible from org * MULTICAST_PHS send to clients potentially hearable from org */ void SV_Multicast(vec3_t origin, multicast_t to) { client_t *client; byte *mask; int leafnum = 0, cluster; int j; qboolean reliable; int area1, area2; reliable = false; if ((to != MULTICAST_ALL_R) && (to != MULTICAST_ALL)) { leafnum = CM_PointLeafnum(origin); area1 = CM_LeafArea(leafnum); } else { area1 = 0; } /* if doing a serverrecord, store everything */ if (svs.demofile) { SZ_Write(&svs.demo_multicast, sv.multicast.data, sv.multicast.cursize); } switch (to) { case MULTICAST_ALL_R: reliable = true; /* intentional fallthrough */ case MULTICAST_ALL: mask = NULL; break; case MULTICAST_PHS_R: reliable = true; /* intentional fallthrough */ case MULTICAST_PHS: leafnum = CM_PointLeafnum(origin); cluster = CM_LeafCluster(leafnum); mask = CM_ClusterPHS(cluster); break; case MULTICAST_PVS_R: reliable = true; /* intentional fallthrough */ case MULTICAST_PVS: leafnum = CM_PointLeafnum(origin); cluster = CM_LeafCluster(leafnum); mask = CM_ClusterPVS(cluster); break; default: mask = NULL; Com_Error(ERR_FATAL, "SV_Multicast: bad to:%i", to); } /* send the data to all relevent clients */ for (j = 0, client = svs.clients; j < maxclients->value; j++, client++) { if ((client->state == cs_free) || (client->state == cs_zombie)) { continue; } if ((client->state != cs_spawned) && !reliable) { continue; } if (mask) { leafnum = CM_PointLeafnum(client->edict->s.origin); cluster = CM_LeafCluster(leafnum); area2 = CM_LeafArea(leafnum); if (!CM_AreasConnected(area1, area2)) { continue; } if (mask && (!(mask[cluster >> 3] & (1 << (cluster & 7))))) { continue; } } if (reliable) { SZ_Write(&client->netchan.message, sv.multicast.data, sv.multicast.cursize); } else { SZ_Write(&client->datagram, sv.multicast.data, sv.multicast.cursize); } } SZ_Clear(&sv.multicast); } /* * Each entity can have eight independant sound sources, like voice, * weapon, feet, etc. * * If cahnnel & 8, the sound will be sent to everyone, not just * things in the PHS. * * Channel 0 is an auto-allocate channel, the others override anything * already running on that entity/channel pair. * * An attenuation of 0 will play full volume everywhere in the level. * Larger attenuations will drop off. (max 4 attenuation) * * Timeofs can range from 0.0 to 0.1 to cause sounds to be started * later in the frame than they normally would. * * If origin is NULL, the origin is determined from the entity origin * or the midpoint of the entity box for bmodels. */ void SV_StartSound(vec3_t origin, edict_t *entity, int channel, int soundindex, float volume, float attenuation, float timeofs) { int sendchan; int flags; int i; int ent; vec3_t origin_v; qboolean use_phs; if ((volume < 0) || (volume > 1.0)) { Com_Error(ERR_FATAL, "SV_StartSound: volume = %f", volume); } if ((attenuation < 0) || (attenuation > 4)) { Com_Error(ERR_FATAL, "SV_StartSound: attenuation = %f", attenuation); } if ((timeofs < 0) || (timeofs > 0.255)) { Com_Error(ERR_FATAL, "SV_StartSound: timeofs = %f", timeofs); } ent = NUM_FOR_EDICT(entity); if (channel & 8) /* no PHS flag */ { use_phs = false; channel &= 7; } else { use_phs = true; } sendchan = (ent << 3) | (channel & 7); flags = 0; if (volume != DEFAULT_SOUND_PACKET_VOLUME) { flags |= SND_VOLUME; } if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION) { flags |= SND_ATTENUATION; } /* the client doesn't know that bmodels have weird origins the origin can also be explicitly set */ if ((entity->svflags & SVF_NOCLIENT) || (entity->solid == SOLID_BSP) || origin) { flags |= SND_POS; } /* always send the entity number for channel overrides */ flags |= SND_ENT; if (timeofs) { flags |= SND_OFFSET; } /* use the entity origin unless it is a bmodel or explicitly specified */ if (!origin) { origin = origin_v; if (entity->solid == SOLID_BSP) { for (i = 0; i < 3; i++) { origin_v[i] = entity->s.origin[i] + 0.5f * (entity->mins[i] + entity->maxs[i]); } } else { VectorCopy(entity->s.origin, origin_v); } } MSG_WriteByte(&sv.multicast, svc_sound); MSG_WriteByte(&sv.multicast, flags); MSG_WriteByte(&sv.multicast, soundindex); if (flags & SND_VOLUME) { MSG_WriteByte(&sv.multicast, volume * 255); } if (flags & SND_ATTENUATION) { MSG_WriteByte(&sv.multicast, attenuation * 64); } if (flags & SND_OFFSET) { MSG_WriteByte(&sv.multicast, timeofs * 1000); } if (flags & SND_ENT) { MSG_WriteShort(&sv.multicast, sendchan); } if (flags & SND_POS) { MSG_WritePos(&sv.multicast, origin); } /* if the sound doesn't attenuate,send it to everyone (global radio chatter, voiceovers, etc) */ if (attenuation == ATTN_NONE) { use_phs = false; } if (channel & CHAN_RELIABLE) { if (use_phs) { SV_Multicast(origin, MULTICAST_PHS_R); } else { SV_Multicast(origin, MULTICAST_ALL_R); } } else { if (use_phs) { SV_Multicast(origin, MULTICAST_PHS); } else { SV_Multicast(origin, MULTICAST_ALL); } } } qboolean SV_SendClientDatagram(client_t *client) { byte msg_buf[MAX_MSGLEN]; sizebuf_t msg; SV_BuildClientFrame(client); SZ_Init(&msg, msg_buf, sizeof(msg_buf)); msg.allowoverflow = true; /* send over all the relevant entity_state_t and the player_state_t */ SV_WriteFrameToClient(client, &msg); /* copy the accumulated multicast datagram for this client out to the message it is necessary for this to be after the WriteEntities so that entity references will be current */ if (client->datagram.overflowed) { Com_Printf("WARNING: datagram overflowed for %s\n", client->name); } else { SZ_Write(&msg, client->datagram.data, client->datagram.cursize); } SZ_Clear(&client->datagram); if (msg.overflowed) { /* must have room left for the packet header */ Com_Printf("WARNING: msg overflowed for %s\n", client->name); SZ_Clear(&msg); } /* send the datagram */ Netchan_Transmit(&client->netchan, msg.cursize, msg.data); /* record the size for rate estimation */ client->message_size[sv.framenum % RATE_MESSAGES] = msg.cursize; return true; } void SV_DemoCompleted(void) { if (sv.demofile) { FS_FCloseFile(sv.demofile); sv.demofile = 0; } SV_Nextserver(); } /* * Returns true if the client is over its current * bandwidth estimation and should not be sent another packet */ qboolean SV_RateDrop(client_t *c) { int total; int i; /* never drop over the loopback */ if (c->netchan.remote_address.type == NA_LOOPBACK) { return false; } total = 0; for (i = 0; i < RATE_MESSAGES; i++) { total += c->message_size[i]; } if (total > c->rate) { c->surpressCount++; c->message_size[sv.framenum % RATE_MESSAGES] = 0; return true; } return false; } void SV_SendClientMessages(void) { int i; client_t *c; int msglen; byte msgbuf[MAX_MSGLEN]; size_t r; msglen = 0; /* read the next demo message if needed */ if (sv.demofile && (sv.state == ss_demo)) { if (sv_paused->value) { msglen = 0; } else { /* get the next message */ r = FS_FRead(&msglen, 4, 1, sv.demofile); if (r != 4) { SV_DemoCompleted(); return; } msglen = LittleLong(msglen); if (msglen == -1) { SV_DemoCompleted(); return; } if (msglen > MAX_MSGLEN) { Com_Error(ERR_DROP, "SV_SendClientMessages: msglen > MAX_MSGLEN"); } r = FS_FRead(msgbuf, msglen, 1, sv.demofile); if (r != msglen) { SV_DemoCompleted(); return; } } } /* send a message to each connected client */ for (i = 0, c = svs.clients; i < maxclients->value; i++, c++) { if (!c->state) { continue; } /* if the reliable message overflowed, drop the client */ if (c->netchan.message.overflowed) { SZ_Clear(&c->netchan.message); SZ_Clear(&c->datagram); SV_BroadcastPrintf(PRINT_HIGH, "%s overflowed\n", c->name); SV_DropClient(c); } if ((sv.state == ss_cinematic) || (sv.state == ss_demo) || (sv.state == ss_pic)) { Netchan_Transmit(&c->netchan, msglen, msgbuf); } else if (c->state == cs_spawned) { /* don't overrun bandwidth */ if (SV_RateDrop(c)) { continue; } SV_SendClientDatagram(c); } else { /* just update reliable if needed */ if (c->netchan.message.cursize || (curtime - c->netchan.last_sent > 1000)) { Netchan_Transmit(&c->netchan, 0, NULL); } } } } yquake2-QUAKE2_5_32/src/server/sv_conless.c0000644000175000017500000002154212615162034021242 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Connectionless server commands. * * ======================================================================= */ #include "header/server.h" extern cvar_t *hostname; extern cvar_t *rcon_password; char *SV_StatusString(void); /* * Responds with all the info that qplug or qspy can see */ void SVC_Status(void) { Netchan_OutOfBandPrint(NS_SERVER, net_from, "print\n%s", SV_StatusString()); } void SVC_Ack(void) { Com_Printf("Ping acknowledge from %s\n", NET_AdrToString(net_from)); } /* * Responds with short info for broadcast scans * The second parameter should be the current protocol version number. */ void SVC_Info(void) { char string[64]; int i, count; int version; if (maxclients->value == 1) { return; /* ignore in single player */ } version = (int)strtol(Cmd_Argv(1), (char **)NULL, 10); if (version != PROTOCOL_VERSION) { Com_sprintf(string, sizeof(string), "%s: wrong version\n", hostname->string, sizeof(string)); } else { count = 0; for (i = 0; i < maxclients->value; i++) { if (svs.clients[i].state >= cs_connected) { count++; } } Com_sprintf(string, sizeof(string), "%16s %8s %2i/%2i\n", hostname->string, sv.name, count, (int)maxclients->value); } Netchan_OutOfBandPrint(NS_SERVER, net_from, "info\n%s", string); } /* * SVC_Ping */ void SVC_Ping(void) { Netchan_OutOfBandPrint(NS_SERVER, net_from, "ack"); } /* * Returns a challenge number that can be used * in a subsequent client_connect command. * We do this to prevent denial of service attacks that * flood the server with invalid connection IPs. With a * challenge, they must give a valid IP address. */ void SVC_GetChallenge(void) { int i; int oldest; int oldestTime; oldest = 0; oldestTime = 0x7fffffff; /* see if we already have a challenge for this ip */ for (i = 0; i < MAX_CHALLENGES; i++) { if (NET_CompareBaseAdr(net_from, svs.challenges[i].adr)) { break; } if (svs.challenges[i].time < oldestTime) { oldestTime = svs.challenges[i].time; oldest = i; } } if (i == MAX_CHALLENGES) { /* overwrite the oldest */ svs.challenges[oldest].challenge = randk() & 0x7fff; svs.challenges[oldest].adr = net_from; svs.challenges[oldest].time = curtime; i = oldest; } /* send it back */ Netchan_OutOfBandPrint(NS_SERVER, net_from, "challenge %i", svs.challenges[i].challenge); } /* * A connection request that did not come from the master */ void SVC_DirectConnect(void) { char userinfo[MAX_INFO_STRING]; netadr_t adr; int i; client_t *cl, *newcl; client_t temp; edict_t *ent; int edictnum; int version; int qport; int challenge; adr = net_from; Com_DPrintf("SVC_DirectConnect ()\n"); version = (int)strtol(Cmd_Argv(1), (char **)NULL, 10); if (version != PROTOCOL_VERSION) { Netchan_OutOfBandPrint(NS_SERVER, adr, "print\nServer is version %s.\n", YQ2VERSION); Com_DPrintf(" rejected connect from version %i\n", version); return; } qport = (int)strtol(Cmd_Argv(2), (char **)NULL, 10); challenge = (int)strtol(Cmd_Argv(3), (char **)NULL, 10); Q_strlcpy(userinfo, Cmd_Argv(4), sizeof(userinfo)); /* force the IP key/value pair so the game can filter based on ip */ Info_SetValueForKey(userinfo, "ip", NET_AdrToString(net_from)); /* attractloop servers are ONLY for local clients */ if (sv.attractloop) { if (!NET_IsLocalAddress(adr)) { Com_Printf("Remote connect in attract loop. Ignored.\n"); Netchan_OutOfBandPrint(NS_SERVER, adr, "print\nConnection refused.\n"); return; } } /* see if the challenge is valid */ if (!NET_IsLocalAddress(adr)) { for (i = 0; i < MAX_CHALLENGES; i++) { if (NET_CompareBaseAdr(net_from, svs.challenges[i].adr)) { if (challenge == svs.challenges[i].challenge) { break; /* good */ } Netchan_OutOfBandPrint(NS_SERVER, adr, "print\nBad challenge.\n"); return; } } if (i == MAX_CHALLENGES) { Netchan_OutOfBandPrint(NS_SERVER, adr, "print\nNo challenge for address.\n"); return; } } newcl = &temp; memset(newcl, 0, sizeof(client_t)); /* if there is already a slot for this ip, reuse it */ for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) { if (cl->state < cs_connected) { continue; } if (NET_CompareBaseAdr(adr, cl->netchan.remote_address) && ((cl->netchan.qport == qport) || (adr.port == cl->netchan.remote_address.port))) { if (!NET_IsLocalAddress(adr)) { Com_DPrintf("%s:reconnect rejected : too soon\n", NET_AdrToString(adr)); return; } Com_Printf("%s:reconnect\n", NET_AdrToString(adr)); newcl = cl; goto gotnewcl; } } /* find a client slot */ newcl = NULL; for (i = 0, cl = svs.clients; i < maxclients->value; i++, cl++) { if (cl->state == cs_free) { newcl = cl; break; } } if (!newcl) { Netchan_OutOfBandPrint(NS_SERVER, adr, "print\nServer is full.\n"); Com_DPrintf("Rejected a connection.\n"); return; } gotnewcl: /* build a new connection accept the new client this is the only place a client_t is ever initialized */ *newcl = temp; sv_client = newcl; edictnum = (newcl - svs.clients) + 1; ent = EDICT_NUM(edictnum); newcl->edict = ent; newcl->challenge = challenge; /* save challenge for checksumming */ /* get the game a chance to reject this connection or modify the userinfo */ if (!(ge->ClientConnect(ent, userinfo))) { if (*Info_ValueForKey(userinfo, "rejmsg")) { Netchan_OutOfBandPrint(NS_SERVER, adr, "print\n%s\nConnection refused.\n", Info_ValueForKey(userinfo, "rejmsg")); } else { Netchan_OutOfBandPrint(NS_SERVER, adr, "print\nConnection refused.\n"); } Com_DPrintf("Game rejected a connection.\n"); return; } /* parse some info from the info strings */ Q_strlcpy(newcl->userinfo, userinfo, sizeof(newcl->userinfo)); SV_UserinfoChanged(newcl); /* send the connect packet to the client */ Netchan_OutOfBandPrint(NS_SERVER, adr, "client_connect"); Netchan_Setup(NS_SERVER, &newcl->netchan, adr, qport); newcl->state = cs_connected; SZ_Init(&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf)); newcl->datagram.allowoverflow = true; newcl->lastmessage = svs.realtime; /* don't timeout */ newcl->lastconnect = svs.realtime; } int Rcon_Validate(void) { if (!strlen(rcon_password->string)) { return 0; } if (strcmp(Cmd_Argv(1), rcon_password->string)) { return 0; } return 1; } /* * A client issued an rcon command. * Shift down the remaining args * Redirect all printfs */ void SVC_RemoteCommand(void) { int i; char remaining[1024]; i = Rcon_Validate(); if (i == 0) { Com_Printf("Bad rcon from %s:\n%s\n", NET_AdrToString( net_from), net_message.data + 4); } else { Com_Printf("Rcon from %s:\n%s\n", NET_AdrToString( net_from), net_message.data + 4); } Com_BeginRedirect(RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect); if (!Rcon_Validate()) { Com_Printf("Bad rcon_password.\n"); } else { remaining[0] = 0; for (i = 2; i < Cmd_Argc(); i++) { strcat(remaining, Cmd_Argv(i)); strcat(remaining, " "); } Cmd_ExecuteString(remaining); } Com_EndRedirect(); } /* * A connectionless packet has four leading 0xff * characters to distinguish it from a game channel. * Clients that are in the game can still send * connectionless packets. */ void SV_ConnectionlessPacket(void) { char *s; char *c; MSG_BeginReading(&net_message); MSG_ReadLong(&net_message); /* skip the -1 marker */ s = MSG_ReadStringLine(&net_message); Cmd_TokenizeString(s, false); c = Cmd_Argv(0); Com_DPrintf("Packet %s : %s\n", NET_AdrToString(net_from), c); if (!strcmp(c, "ping")) { SVC_Ping(); } else if (!strcmp(c, "ack")) { SVC_Ack(); } else if (!strcmp(c, "status")) { SVC_Status(); } else if (!strcmp(c, "info")) { SVC_Info(); } else if (!strcmp(c, "getchallenge")) { SVC_GetChallenge(); } else if (!strcmp(c, "connect")) { SVC_DirectConnect(); } else if (!strcmp(c, "rcon")) { SVC_RemoteCommand(); } else { Com_Printf("bad connectionless packet from %s:\n%s\n", NET_AdrToString(net_from), s); } } yquake2-QUAKE2_5_32/src/server/sv_user.c0000644000175000017500000003406312615162034020554 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 side user (player entity) moving. * * ======================================================================= */ #include "header/server.h" #define MAX_STRINGCMDS 8 edict_t *sv_player; void SV_BeginDemoserver(void) { char name[MAX_OSPATH]; Com_sprintf(name, sizeof(name), "demos/%s", sv.name); FS_FOpenFile(name, &sv.demofile, false); if (!sv.demofile) { Com_Error(ERR_DROP, "Couldn't open %s\n", name); } } /* * 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_New_f(void) { static char *gamedir; int playernum; edict_t *ent; Com_DPrintf("New() from %s\n", sv_client->name); if (sv_client->state != cs_connected) { Com_Printf("New not valid -- already spawned\n"); return; } /* demo servers just dump the file message */ if (sv.state == ss_demo) { SV_BeginDemoserver(); return; } /* serverdata needs to go over for all types of servers to make sure the protocol is right, and to set the gamedir */ gamedir = (char *)Cvar_VariableString("gamedir"); /* send the serverdata */ MSG_WriteByte(&sv_client->netchan.message, svc_serverdata); MSG_WriteLong(&sv_client->netchan.message, PROTOCOL_VERSION); MSG_WriteLong(&sv_client->netchan.message, svs.spawncount); MSG_WriteByte(&sv_client->netchan.message, sv.attractloop); MSG_WriteString(&sv_client->netchan.message, gamedir); if ((sv.state == ss_cinematic) || (sv.state == ss_pic)) { playernum = -1; } else { playernum = sv_client - svs.clients; } MSG_WriteShort(&sv_client->netchan.message, playernum); /* send full levelname */ MSG_WriteString(&sv_client->netchan.message, sv.configstrings[CS_NAME]); /* game server */ if (sv.state == ss_game) { /* set up the entity for the client */ ent = EDICT_NUM(playernum + 1); ent->s.number = playernum + 1; sv_client->edict = ent; memset(&sv_client->lastcmd, 0, sizeof(sv_client->lastcmd)); /* begin fetching configstrings */ MSG_WriteByte(&sv_client->netchan.message, svc_stufftext); MSG_WriteString(&sv_client->netchan.message, va("cmd configstrings %i 0\n", svs.spawncount)); } } void SV_Configstrings_f(void) { int start; Com_DPrintf("Configstrings() from %s\n", sv_client->name); if (sv_client->state != cs_connected) { Com_Printf("configstrings not valid -- already spawned\n"); return; } /* handle the case of a level changing while a client was connecting */ if ((int)strtol(Cmd_Argv(1), (char **)NULL, 10) != svs.spawncount) { Com_Printf("SV_Configstrings_f from different level\n"); SV_New_f(); return; } start = (int)strtol(Cmd_Argv(2), (char **)NULL, 10); /* write a packet full of data */ while (sv_client->netchan.message.cursize < MAX_MSGLEN / 2 && start < MAX_CONFIGSTRINGS) { if (sv.configstrings[start][0]) { MSG_WriteByte(&sv_client->netchan.message, svc_configstring); MSG_WriteShort(&sv_client->netchan.message, start); MSG_WriteString(&sv_client->netchan.message, sv.configstrings[start]); } start++; } /* send next command */ if (start == MAX_CONFIGSTRINGS) { MSG_WriteByte(&sv_client->netchan.message, svc_stufftext); MSG_WriteString(&sv_client->netchan.message, va("cmd baselines %i 0\n", svs.spawncount)); } else { MSG_WriteByte(&sv_client->netchan.message, svc_stufftext); MSG_WriteString(&sv_client->netchan.message, va("cmd configstrings %i %i\n", svs.spawncount, start)); } } void SV_Baselines_f(void) { int start; entity_state_t nullstate; entity_state_t *base; Com_DPrintf("Baselines() from %s\n", sv_client->name); if (sv_client->state != cs_connected) { Com_Printf("baselines not valid -- already spawned\n"); return; } /* handle the case of a level changing while a client was connecting */ if ((int)strtol(Cmd_Argv(1), (char **)NULL, 10) != svs.spawncount) { Com_Printf("SV_Baselines_f from different level\n"); SV_New_f(); return; } start = (int)strtol(Cmd_Argv(2), (char **)NULL, 10); memset(&nullstate, 0, sizeof(nullstate)); /* write a packet full of data */ while (sv_client->netchan.message.cursize < MAX_MSGLEN / 2 && start < MAX_EDICTS) { base = &sv.baselines[start]; if (base->modelindex || base->sound || base->effects) { MSG_WriteByte(&sv_client->netchan.message, svc_spawnbaseline); MSG_WriteDeltaEntity(&nullstate, base, &sv_client->netchan.message, true, true); } start++; } /* send next command */ if (start == MAX_EDICTS) { MSG_WriteByte(&sv_client->netchan.message, svc_stufftext); MSG_WriteString(&sv_client->netchan.message, va("precache %i\n", svs.spawncount)); } else { MSG_WriteByte(&sv_client->netchan.message, svc_stufftext); MSG_WriteString(&sv_client->netchan.message, va("cmd baselines %i %i\n", svs.spawncount, start)); } } void SV_Begin_f(void) { Com_DPrintf("Begin() from %s\n", sv_client->name); /* handle the case of a level changing while a client was connecting */ if ((int)strtol(Cmd_Argv(1), (char **)NULL, 10) != svs.spawncount) { Com_Printf("SV_Begin_f from different level\n"); SV_New_f(); return; } sv_client->state = cs_spawned; /* call the game begin function */ ge->ClientBegin(sv_player); Cbuf_InsertFromDefer(); } void SV_NextDownload_f(void) { int r; int percent; int size; if (!sv_client->download) { return; } r = sv_client->downloadsize - sv_client->downloadcount; if (r > 1024) { r = 1024; } MSG_WriteByte(&sv_client->netchan.message, svc_download); MSG_WriteShort(&sv_client->netchan.message, r); sv_client->downloadcount += r; size = sv_client->downloadsize; if (!size) { size = 1; } percent = sv_client->downloadcount * 100 / size; MSG_WriteByte(&sv_client->netchan.message, percent); SZ_Write(&sv_client->netchan.message, sv_client->download + sv_client->downloadcount - r, r); if (sv_client->downloadcount != sv_client->downloadsize) { return; } FS_FreeFile(sv_client->download); sv_client->download = NULL; } void SV_BeginDownload_f(void) { char *name; extern cvar_t *allow_download; extern cvar_t *allow_download_players; extern cvar_t *allow_download_models; extern cvar_t *allow_download_sounds; extern cvar_t *allow_download_maps; extern int file_from_pak; int offset = 0; name = Cmd_Argv(1); if (Cmd_Argc() > 2) { offset = (int)strtol(Cmd_Argv(2), (char **)NULL, 10); /* downloaded offset */ } /* hacked by zoid to allow more conrol over download first off, no .. or global allow check */ if (strstr(name, "..") || strstr(name, "\\") || strstr(name, ":") || !allow_download->value /* leading dot is no good */ || (*name == '.') /* leading slash bad as well, must be in subdir */ || (*name == '/') /* next up, skin check */ || ((strncmp(name, "players/", 6) == 0) && !allow_download_players->value) /* now models */ || ((strncmp(name, "models/", 6) == 0) && !allow_download_models->value) /* now sounds */ || ((strncmp(name, "sound/", 6) == 0) && !allow_download_sounds->value) /* now maps (note special case for maps, must not be in pak) */ || ((strncmp(name, "maps/", 6) == 0) && !allow_download_maps->value) /* MUST be in a subdirectory */ || !strstr(name, "/")) { MSG_WriteByte(&sv_client->netchan.message, svc_download); MSG_WriteShort(&sv_client->netchan.message, -1); MSG_WriteByte(&sv_client->netchan.message, 0); return; } if (sv_client->download) { FS_FreeFile(sv_client->download); } sv_client->downloadsize = FS_LoadFile(name, (void **)&sv_client->download); sv_client->downloadcount = offset; if (offset > sv_client->downloadsize) { sv_client->downloadcount = sv_client->downloadsize; } if (!sv_client->download || ((strncmp(name, "maps/", 5) == 0) && file_from_pak)) { Com_DPrintf("Couldn't download %s to %s\n", name, sv_client->name); if (sv_client->download) { FS_FreeFile(sv_client->download); sv_client->download = NULL; } MSG_WriteByte(&sv_client->netchan.message, svc_download); MSG_WriteShort(&sv_client->netchan.message, -1); MSG_WriteByte(&sv_client->netchan.message, 0); return; } SV_NextDownload_f(); Com_DPrintf("Downloading %s to %s\n", name, sv_client->name); } /* * The client is going to disconnect, so remove the connection immediately */ void SV_Disconnect_f(void) { SV_DropClient(sv_client); } /* * Dumps the serverinfo info string */ void SV_ShowServerinfo_f(void) { Info_Print(Cvar_Serverinfo()); } void SV_Nextserver(void) { const char *v; if ((sv.state == ss_game) || ((sv.state == ss_pic) && !Cvar_VariableValue("coop"))) { return; /* can't nextserver while playing a normal game */ } svs.spawncount++; /* make sure another doesn't sneak in */ v = Cvar_VariableString("nextserver"); if (!v[0]) { Cbuf_AddText("killserver\n"); } else { Cbuf_AddText((char *)v); Cbuf_AddText("\n"); } Cvar_Set("nextserver", ""); } /* * A cinematic has completed or been aborted by a client, so move * to the next server, */ void SV_Nextserver_f(void) { if ((int)strtol(Cmd_Argv(1), (char **)NULL, 10) != svs.spawncount) { Com_DPrintf("Nextserver() from wrong level, from %s\n", sv_client->name); return; /* leftover from last server */ } Com_DPrintf("Nextserver() from %s\n", sv_client->name); SV_Nextserver(); } typedef struct { char *name; void (*func)(void); } ucmd_t; ucmd_t ucmds[] = { /* auto issued */ {"new", SV_New_f}, {"configstrings", SV_Configstrings_f}, {"baselines", SV_Baselines_f}, {"begin", SV_Begin_f}, {"nextserver", SV_Nextserver_f}, {"disconnect", SV_Disconnect_f}, /* issued by hand at client consoles */ {"info", SV_ShowServerinfo_f}, {"download", SV_BeginDownload_f}, {"nextdl", SV_NextDownload_f}, {NULL, NULL} }; void SV_ExecuteUserCommand(char *s) { ucmd_t *u; /* Security Fix... This is being set to false so that client's can't macro expand variables on the server. It seems unlikely that a client ever ought to need to be able to do this... */ Cmd_TokenizeString(s, false); sv_player = sv_client->edict; for (u = ucmds; u->name; u++) { if (!strcmp(Cmd_Argv(0), u->name)) { u->func(); break; } } if (!u->name && (sv.state == ss_game)) { ge->ClientCommand(sv_player); } } void SV_ClientThink(client_t *cl, usercmd_t *cmd) { cl->commandMsec -= cmd->msec; if ((cl->commandMsec < 0) && sv_enforcetime->value) { Com_DPrintf("commandMsec underflow from %s\n", cl->name); return; } ge->ClientThink(cl->edict, cmd); } /* * The current net_message is parsed for the given client */ void SV_ExecuteClientMessage(client_t *cl) { int c; char *s; usercmd_t nullcmd; usercmd_t oldest, oldcmd, newcmd; int net_drop; int stringCmdCount; int checksum, calculatedChecksum; int checksumIndex; qboolean move_issued; int lastframe; sv_client = cl; sv_player = sv_client->edict; /* only allow one move command */ move_issued = false; stringCmdCount = 0; while (1) { if (net_message.readcount > net_message.cursize) { Com_Printf("SV_ReadClientMessage: badread\n"); SV_DropClient(cl); return; } c = MSG_ReadByte(&net_message); if (c == -1) { break; } switch (c) { default: Com_Printf("SV_ReadClientMessage: unknown command char\n"); SV_DropClient(cl); return; case clc_nop: break; case clc_userinfo: Q_strlcpy(cl->userinfo, MSG_ReadString(&net_message), sizeof(cl->userinfo)); SV_UserinfoChanged(cl); break; case clc_move: if (move_issued) { return; /* someone is trying to cheat... */ } move_issued = true; checksumIndex = net_message.readcount; checksum = MSG_ReadByte(&net_message); lastframe = MSG_ReadLong(&net_message); if (lastframe != cl->lastframe) { cl->lastframe = lastframe; if (cl->lastframe > 0) { cl->frame_latency[cl->lastframe & (LATENCY_COUNTS - 1)] = svs.realtime - cl->frames[cl->lastframe & UPDATE_MASK].senttime; } } memset(&nullcmd, 0, sizeof(nullcmd)); MSG_ReadDeltaUsercmd(&net_message, &nullcmd, &oldest); MSG_ReadDeltaUsercmd(&net_message, &oldest, &oldcmd); MSG_ReadDeltaUsercmd(&net_message, &oldcmd, &newcmd); if (cl->state != cs_spawned) { cl->lastframe = -1; break; } /* if the checksum fails, ignore the rest of the packet */ calculatedChecksum = COM_BlockSequenceCRCByte( net_message.data + checksumIndex + 1, net_message.readcount - checksumIndex - 1, cl->netchan.incoming_sequence); if (calculatedChecksum != checksum) { Com_DPrintf("Failed command checksum for %s (%d != %d)/%d\n", cl->name, calculatedChecksum, checksum, cl->netchan.incoming_sequence); return; } if (!sv_paused->value) { net_drop = cl->netchan.dropped; if (net_drop < 20) { while (net_drop > 2) { SV_ClientThink(cl, &cl->lastcmd); net_drop--; } if (net_drop > 1) { SV_ClientThink(cl, &oldest); } if (net_drop > 0) { SV_ClientThink(cl, &oldcmd); } } SV_ClientThink(cl, &newcmd); } cl->lastcmd = newcmd; break; case clc_stringcmd: s = MSG_ReadString(&net_message); /* malicious users may try using too many string commands */ if (++stringCmdCount < MAX_STRINGCMDS) { SV_ExecuteUserCommand(s); } if (cl->state == cs_zombie) { return; /* disconnect command */ } break; } } } yquake2-QUAKE2_5_32/src/client/0000755000175000017500000000000012615162034016664 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/client/refresh/0000755000175000017500000000000012615162034020322 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/client/refresh/r_misc.c0000644000175000017500000001405212615162034021744 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Misc refresher functions * * ======================================================================= */ #include "header/local.h" byte dottexture[8][8] = { {0, 0, 0, 0, 0, 0, 0, 0}, {0, 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}, }; 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; void R_InitParticleTexture(void) { int x, y; byte data[8][8][4]; /* particle texture */ for (x = 0; x < 8; x++) { for (y = 0; y < 8; y++) { data[y][x][0] = 255; data[y][x][1] = 255; data[y][x][2] = 255; data[y][x][3] = dottexture[x][y] * 255; } } r_particletexture = R_LoadPic("***particle***", (byte *)data, 8, 0, 8, 0, it_sprite, 32); /* also use this for bad textures, but without alpha */ for (x = 0; x < 8; x++) { for (y = 0; y < 8; y++) { data[y][x][0] = dottexture[x & 3][y & 3] * 255; data[y][x][1] = 0; data[y][x][2] = 0; data[y][x][3] = 255; } } r_notexture = R_LoadPic("***r_notexture***", (byte *)data, 8, 0, 8, 0, it_wall, 32); } void R_ScreenShot(void) { byte *buffer, temp; char picname[80]; char checkname[MAX_OSPATH]; int i, c; FILE *f; /* create the scrnshots directory if it doesn't exist */ Com_sprintf(checkname, sizeof(checkname), "%s/scrnshot", FS_Gamedir()); Sys_Mkdir(checkname); /* find a file name to save it to */ strcpy(picname, "quake00.tga"); for (i = 0; i <= 99; i++) { picname[5] = i / 10 + '0'; picname[6] = i % 10 + '0'; Com_sprintf(checkname, sizeof(checkname), "%s/scrnshot/%s", FS_Gamedir(), picname); f = fopen(checkname, "rb"); if (!f) { break; /* file doesn't exist */ } fclose(f); } if (i == 100) { VID_Printf(PRINT_ALL, "SCR_ScreenShot_f: Couldn't create a file\n"); return; } static const int headerLength = 18+4; c = headerLength + vid.width * vid.height * 3; buffer = malloc(c); if (!buffer) { VID_Printf(PRINT_ALL, "SCR_ScreenShot_f: Couldn't malloc %d bytes\n", c); return; } memset(buffer, 0, headerLength); buffer[0] = 4; // image ID: "yq2\0" buffer[2] = 2; /* uncompressed type */ buffer[12] = vid.width & 255; buffer[13] = vid.width >> 8; buffer[14] = vid.height & 255; buffer[15] = vid.height >> 8; buffer[16] = 24; /* pixel size */ buffer[17] = 0; // image descriptor buffer[18] = 'y'; // following: the 4 image ID fields buffer[19] = 'q'; buffer[20] = '2'; buffer[21] = '\0'; glPixelStorei(GL_PACK_ALIGNMENT, 1); glReadPixels(0, 0, vid.width, vid.height, GL_RGB, GL_UNSIGNED_BYTE, buffer + headerLength); /* swap rgb to bgr */ for (i = headerLength; i < c; i += 3) { temp = buffer[i]; buffer[i] = buffer[i + 2]; buffer[i + 2] = temp; } f = fopen(checkname, "wb"); if (f) { fwrite(buffer, 1, c, f); fclose(f); VID_Printf(PRINT_ALL, "Wrote %s\n", picname); } else { VID_Printf(PRINT_ALL, "SCR_ScreenShot_f: Couldn't write %s\n", picname); } free(buffer); } void R_Strings(void) { VID_Printf(PRINT_ALL, "GL_VENDOR: %s\n", gl_config.vendor_string); VID_Printf(PRINT_ALL, "GL_RENDERER: %s\n", gl_config.renderer_string); VID_Printf(PRINT_ALL, "GL_VERSION: %s\n", gl_config.version_string); VID_Printf(PRINT_ALL, "GL_EXTENSIONS: %s\n", gl_config.extensions_string); } void R_SetDefaultState(void) { glClearColor(1, 0, 0.5, 0.5); glDisable(GL_MULTISAMPLE); glCullFace(GL_FRONT); glEnable(GL_TEXTURE_2D); glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.666); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glDisable(GL_BLEND); glColor4f(1, 1, 1, 1); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glShadeModel(GL_FLAT); R_TextureMode(gl_texturemode->string); R_TextureAlphaMode(gl_texturealphamode->string); R_TextureSolidMode(gl_texturesolidmode->string); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); R_TexEnv(GL_REPLACE); if (qglPointParameterfEXT) { float attenuations[3]; attenuations[0] = gl_particle_att_a->value; attenuations[1] = gl_particle_att_b->value; attenuations[2] = gl_particle_att_c->value; /* GL_POINT_SMOOTH is not implemented by some OpenGL drivers, especially the crappy Mesa3D backends like i915.so. That the points are squares and not circles is not a problem by Quake II! */ glEnable(GL_POINT_SMOOTH); qglPointParameterfEXT(GL_POINT_SIZE_MIN_EXT, gl_particle_min_size->value); qglPointParameterfEXT(GL_POINT_SIZE_MAX_EXT, gl_particle_max_size->value); qglPointParameterfvEXT(GL_DISTANCE_ATTENUATION_EXT, attenuations); } if (qglColorTableEXT && gl_ext_palettedtexture->value) { glEnable(GL_SHARED_TEXTURE_PALETTE_EXT); R_SetTexturePalette(d_8to24table); } if (gl_msaa_samples->value) { glEnable(GL_MULTISAMPLE); glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST); } } yquake2-QUAKE2_5_32/src/client/refresh/r_light.c0000644000175000017500000003065512615162034022127 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Lightmaps and dynamic lighting * * ======================================================================= */ #include "header/local.h" #define DLIGHT_CUTOFF 64 int r_dlightframecount; vec3_t pointcolor; cplane_t *lightplane; /* used as shadow plane */ vec3_t lightspot; static float s_blocklights[34 * 34 * 3]; void R_RenderDlight(dlight_t *light) { int i, j; float a; vec3_t v; float rad; rad = light->intensity * 0.35; VectorSubtract(light->origin, r_origin, v); glBegin(GL_TRIANGLE_FAN); glColor3f(light->color[0] * 0.2, light->color[1] * 0.2, light->color[2] * 0.2); for (i = 0; i < 3; i++) { v[i] = light->origin[i] - vpn[i] * rad; } glVertex3fv(v); glColor3f(0, 0, 0); for (i = 16; i >= 0; i--) { a = i / 16.0 * M_PI * 2; for (j = 0; j < 3; j++) { v[j] = light->origin[j] + vright[j] * cos(a) * rad + vup[j] * sin(a) * rad; } glVertex3fv(v); } glEnd(); } void R_RenderDlights(void) { int i; dlight_t *l; if (!gl_flashblend->value) { return; } /* because the count hasn't advanced yet for this frame */ r_dlightframecount = r_framecount + 1; glDepthMask(0); glDisable(GL_TEXTURE_2D); glShadeModel(GL_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE); l = r_newrefdef.dlights; for (i = 0; i < r_newrefdef.num_dlights; i++, l++) { 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); } void R_MarkLights(dlight_t *light, int bit, mnode_t *node) { cplane_t *splitplane; float dist; msurface_t *surf; int i; int sidebit; if (node->contents != -1) { return; } splitplane = node->plane; dist = DotProduct(light->origin, splitplane->normal) - splitplane->dist; if (dist > light->intensity - DLIGHT_CUTOFF) { R_MarkLights(light, bit, node->children[0]); return; } if (dist < -light->intensity + DLIGHT_CUTOFF) { R_MarkLights(light, bit, node->children[1]); return; } /* mark the polygons */ surf = r_worldmodel->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { dist = DotProduct(light->origin, surf->plane->normal) - surf->plane->dist; if (dist >= 0) { sidebit = 0; } else { sidebit = SURF_PLANEBACK; } if ((surf->flags & SURF_PLANEBACK) != sidebit) { continue; } if (surf->dlightframe != r_dlightframecount) { surf->dlightbits = 0; surf->dlightframe = r_dlightframecount; } surf->dlightbits |= bit; } R_MarkLights(light, bit, node->children[0]); R_MarkLights(light, bit, node->children[1]); } void R_PushDlights(void) { int i; dlight_t *l; if (gl_flashblend->value) { return; } /* because the count hasn't advanced yet for this frame */ r_dlightframecount = r_framecount + 1; l = r_newrefdef.dlights; for (i = 0; i < r_newrefdef.num_dlights; i++, l++) { R_MarkLights(l, 1 << i, r_worldmodel->nodes); } } int R_RecursiveLightPoint(mnode_t *node, vec3_t start, vec3_t end) { float front, back, frac; int side; cplane_t *plane; vec3_t mid; msurface_t *surf; int s, t, ds, dt; int i; mtexinfo_t *tex; byte *lightmap; int maps; int r; if (node->contents != -1) { return -1; /* didn't hit anything */ } /* calculate mid point */ plane = node->plane; front = DotProduct(start, plane->normal) - plane->dist; back = DotProduct(end, plane->normal) - plane->dist; side = front < 0; if ((back < 0) == side) { return R_RecursiveLightPoint(node->children[side], start, end); } 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 */ r = R_RecursiveLightPoint(node->children[side], start, mid); if (r >= 0) { return r; /* hit something */ } if ((back < 0) == side) { return -1; /* didn't hit anuthing */ } /* check for impact on this node */ VectorCopy(mid, lightspot); lightplane = plane; surf = r_worldmodel->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { if (surf->flags & (SURF_DRAWTURB | SURF_DRAWSKY)) { continue; /* no lightmaps */ } tex = surf->texinfo; s = DotProduct(mid, tex->vecs[0]) + tex->vecs[0][3]; t = DotProduct(mid, tex->vecs[1]) + tex->vecs[1][3]; if ((s < surf->texturemins[0]) || (t < surf->texturemins[1])) { continue; } ds = s - surf->texturemins[0]; dt = t - surf->texturemins[1]; if ((ds > surf->extents[0]) || (dt > surf->extents[1])) { continue; } if (!surf->samples) { return 0; } ds >>= 4; dt >>= 4; lightmap = surf->samples; VectorCopy(vec3_origin, pointcolor); if (lightmap) { vec3_t scale; lightmap += 3 * (dt * ((surf->extents[0] >> 4) + 1) + ds); for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { for (i = 0; i < 3; i++) { scale[i] = gl_modulate->value * r_newrefdef.lightstyles[surf->styles[maps]].rgb[i]; } pointcolor[0] += lightmap[0] * scale[0] * (1.0 / 255); pointcolor[1] += lightmap[1] * scale[1] * (1.0 / 255); pointcolor[2] += lightmap[2] * scale[2] * (1.0 / 255); lightmap += 3 * ((surf->extents[0] >> 4) + 1) * ((surf->extents[1] >> 4) + 1); } } return 1; } /* go down back side */ return R_RecursiveLightPoint(node->children[!side], mid, end); } void R_LightPoint(vec3_t p, vec3_t color) { vec3_t end; float r; int lnum; dlight_t *dl; vec3_t dist; float add; if (!r_worldmodel->lightdata) { color[0] = color[1] = color[2] = 1.0; return; } end[0] = p[0]; end[1] = p[1]; end[2] = p[2] - 2048; r = R_RecursiveLightPoint(r_worldmodel->nodes, p, end); if (r == -1) { VectorCopy(vec3_origin, color); } else { VectorCopy(pointcolor, color); } /* add dynamic lights */ dl = r_newrefdef.dlights; for (lnum = 0; lnum < r_newrefdef.num_dlights; lnum++, dl++) { VectorSubtract(currententity->origin, dl->origin, dist); add = dl->intensity - VectorLength(dist); add *= (1.0 / 256); if (add > 0) { VectorMA(color, add, dl->color, color); } } VectorScale(color, gl_modulate->value, color); } void R_AddDynamicLights(msurface_t *surf) { int lnum; int sd, td; float fdist, frad, fminlight; vec3_t impact, local; int s, t; int i; int smax, tmax; mtexinfo_t *tex; dlight_t *dl; float *pfBL; float fsacc, ftacc; smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; tex = surf->texinfo; for (lnum = 0; lnum < r_newrefdef.num_dlights; lnum++) { if (!(surf->dlightbits & (1 << lnum))) { continue; /* not lit by this light */ } dl = &r_newrefdef.dlights[lnum]; frad = dl->intensity; fdist = DotProduct(dl->origin, surf->plane->normal) - surf->plane->dist; frad -= fabs(fdist); /* rad is now the highest intensity on the plane */ fminlight = DLIGHT_CUTOFF; if (frad < fminlight) { continue; } fminlight = frad - fminlight; for (i = 0; i < 3; i++) { impact[i] = dl->origin[i] - surf->plane->normal[i] * fdist; } local[0] = DotProduct(impact, tex->vecs[0]) + tex->vecs[0][3] - surf->texturemins[0]; local[1] = DotProduct(impact, tex->vecs[1]) + tex->vecs[1][3] - surf->texturemins[1]; pfBL = s_blocklights; for (t = 0, ftacc = 0; t < tmax; t++, ftacc += 16) { td = local[1] - ftacc; if (td < 0) { td = -td; } for (s = 0, fsacc = 0; s < smax; s++, fsacc += 16, pfBL += 3) { sd = Q_ftol(local[0] - fsacc); if (sd < 0) { sd = -sd; } if (sd > td) { fdist = sd + (td >> 1); } else { fdist = td + (sd >> 1); } if (fdist < fminlight) { pfBL[0] += (frad - fdist) * dl->color[0]; pfBL[1] += (frad - fdist) * dl->color[1]; pfBL[2] += (frad - fdist) * dl->color[2]; } } } } } void R_SetCacheState(msurface_t *surf) { int maps; for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { surf->cached_light[maps] = r_newrefdef.lightstyles[surf->styles[maps]].white; } } /* * Combine and scale multiple lightmaps into the floating format in blocklights */ void R_BuildLightMap(msurface_t *surf, byte *dest, int stride) { int smax, tmax; int r, g, b, a, max; int i, j, size; byte *lightmap; float scale[4]; int nummaps; float *bl; if (surf->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP)) { VID_Error(ERR_DROP, "R_BuildLightMap called for non-lit surface"); } smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; size = smax * tmax; if (size > (sizeof(s_blocklights) >> 4)) { VID_Error(ERR_DROP, "Bad s_blocklights size"); } /* set to full bright if no light data */ if (!surf->samples) { for (i = 0; i < size * 3; i++) { s_blocklights[i] = 255; } goto store; } /* count the # of maps */ for (nummaps = 0; nummaps < MAXLIGHTMAPS && surf->styles[nummaps] != 255; nummaps++) { } lightmap = surf->samples; /* add all the lightmaps */ if (nummaps == 1) { int maps; for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { bl = s_blocklights; for (i = 0; i < 3; i++) { scale[i] = gl_modulate->value * r_newrefdef.lightstyles[surf->styles[maps]].rgb[i]; } if ((scale[0] == 1.0F) && (scale[1] == 1.0F) && (scale[2] == 1.0F)) { for (i = 0; i < size; i++, bl += 3) { bl[0] = lightmap[i * 3 + 0]; bl[1] = lightmap[i * 3 + 1]; bl[2] = lightmap[i * 3 + 2]; } } else { for (i = 0; i < size; i++, bl += 3) { bl[0] = lightmap[i * 3 + 0] * scale[0]; bl[1] = lightmap[i * 3 + 1] * scale[1]; bl[2] = lightmap[i * 3 + 2] * scale[2]; } } lightmap += size * 3; /* skip to next lightmap */ } } else { int maps; memset(s_blocklights, 0, sizeof(s_blocklights[0]) * size * 3); for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { bl = s_blocklights; for (i = 0; i < 3; i++) { scale[i] = gl_modulate->value * r_newrefdef.lightstyles[surf->styles[maps]].rgb[i]; } if ((scale[0] == 1.0F) && (scale[1] == 1.0F) && (scale[2] == 1.0F)) { for (i = 0; i < size; i++, bl += 3) { bl[0] += lightmap[i * 3 + 0]; bl[1] += lightmap[i * 3 + 1]; bl[2] += lightmap[i * 3 + 2]; } } else { for (i = 0; i < size; i++, bl += 3) { bl[0] += lightmap[i * 3 + 0] * scale[0]; bl[1] += lightmap[i * 3 + 1] * scale[1]; bl[2] += lightmap[i * 3 + 2] * scale[2]; } } lightmap += size * 3; /* skip to next lightmap */ } } /* add all the dynamic lights */ if (surf->dlightframe == r_framecount) { R_AddDynamicLights(surf); } store: stride -= (smax << 2); bl = s_blocklights; for (i = 0; i < tmax; i++, dest += stride) { for (j = 0; j < smax; j++) { r = Q_ftol(bl[0]); g = Q_ftol(bl[1]); b = Q_ftol(bl[2]); /* catch negative lights */ if (r < 0) { r = 0; } if (g < 0) { g = 0; } if (b < 0) { b = 0; } /* determine the brightest of the three color components */ if (r > g) { max = r; } else { max = g; } if (b > max) { max = b; } /* alpha is ONLY used for the mono lightmap case. For this reason we set it to the brightest of the color components so that things don't get too dim. */ a = max; /* rescale all the color components if the intensity of the greatest channel exceeds 1.0 */ if (max > 255) { float t = 255.0F / max; r = r * t; g = g * t; b = b * t; a = a * t; } dest[0] = r; dest[1] = g; dest[2] = b; dest[3] = a; bl += 3; dest += 4; } } } yquake2-QUAKE2_5_32/src/client/refresh/constants/0000755000175000017500000000000012615162034022336 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/client/refresh/constants/warpsin.h0000644000175000017500000000647012615162034024201 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Precalculated sinus warps * * ======================================================================= */ 0, 0.19633, 0.392541, 0.588517, 0.784137, 0.979285, 1.17384, 1.3677, 1.56072, 1.75281, 1.94384, 2.1337, 2.32228, 2.50945, 2.69512, 2.87916, 3.06147, 3.24193, 3.42044, 3.59689, 3.77117, 3.94319, 4.11282, 4.27998, 4.44456, 4.60647, 4.76559, 4.92185, 5.07515, 5.22538, 5.37247, 5.51632, 5.65685, 5.79398, 5.92761, 6.05767, 6.18408, 6.30677, 6.42566, 6.54068, 6.65176, 6.75883, 6.86183, 6.9607, 7.05537, 7.14579, 7.23191, 7.31368, 7.39104, 7.46394, 7.53235, 7.59623, 7.65552, 7.71021, 7.76025, 7.80562, 7.84628, 7.88222, 7.91341, 7.93984, 7.96148, 7.97832, 7.99036, 7.99759, 8, 7.99759, 7.99036, 7.97832, 7.96148, 7.93984, 7.91341, 7.88222, 7.84628, 7.80562, 7.76025, 7.71021, 7.65552, 7.59623, 7.53235, 7.46394, 7.39104, 7.31368, 7.23191, 7.14579, 7.05537, 6.9607, 6.86183, 6.75883, 6.65176, 6.54068, 6.42566, 6.30677, 6.18408, 6.05767, 5.92761, 5.79398, 5.65685, 5.51632, 5.37247, 5.22538, 5.07515, 4.92185, 4.76559, 4.60647, 4.44456, 4.27998, 4.11282, 3.94319, 3.77117, 3.59689, 3.42044, 3.24193, 3.06147, 2.87916, 2.69512, 2.50945, 2.32228, 2.1337, 1.94384, 1.75281, 1.56072, 1.3677, 1.17384, 0.979285, 0.784137, 0.588517, 0.392541, 0.19633, 9.79717e-16, -0.19633, -0.392541, -0.588517, -0.784137, -0.979285, -1.17384, -1.3677, -1.56072, -1.75281, -1.94384, -2.1337, -2.32228, -2.50945, -2.69512, -2.87916, -3.06147, -3.24193, -3.42044, -3.59689, -3.77117, -3.94319, -4.11282, -4.27998, -4.44456, -4.60647, -4.76559, -4.92185, -5.07515, -5.22538, -5.37247, -5.51632, -5.65685, -5.79398, -5.92761, -6.05767, -6.18408, -6.30677, -6.42566, -6.54068, -6.65176, -6.75883, -6.86183, -6.9607, -7.05537, -7.14579, -7.23191, -7.31368, -7.39104, -7.46394, -7.53235, -7.59623, -7.65552, -7.71021, -7.76025, -7.80562, -7.84628, -7.88222, -7.91341, -7.93984, -7.96148, -7.97832, -7.99036, -7.99759, -8, -7.99759, -7.99036, -7.97832, -7.96148, -7.93984, -7.91341, -7.88222, -7.84628, -7.80562, -7.76025, -7.71021, -7.65552, -7.59623, -7.53235, -7.46394, -7.39104, -7.31368, -7.23191, -7.14579, -7.05537, -6.9607, -6.86183, -6.75883, -6.65176, -6.54068, -6.42566, -6.30677, -6.18408, -6.05767, -5.92761, -5.79398, -5.65685, -5.51632, -5.37247, -5.22538, -5.07515, -4.92185, -4.76559, -4.60647, -4.44456, -4.27998, -4.11282, -3.94319, -3.77117, -3.59689, -3.42044, -3.24193, -3.06147, -2.87916, -2.69512, -2.50945, -2.32228, -2.1337, -1.94384, -1.75281, -1.56072, -1.3677, -1.17384, -0.979285, -0.784137, -0.588517, -0.392541, -0.19633, yquake2-QUAKE2_5_32/src/client/refresh/constants/anorms.h0000644000175000017500000001504512615162034024013 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Precalculates anormal values * * ======================================================================= */ { -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 }, yquake2-QUAKE2_5_32/src/client/refresh/constants/anormtab.h0000644000175000017500000006313612615162034024323 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Precalculated anormal tabulations * * ======================================================================= */ { { 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 } } yquake2-QUAKE2_5_32/src/client/refresh/r_image.c0000644000175000017500000006626612615162034022111 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Texture handling * * ======================================================================= */ #include "header/local.h" image_t gltextures[MAX_GLTEXTURES]; int numgltextures; int base_textureid; /* gltextures[i] = base_textureid+i */ extern qboolean scrap_dirty; extern byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH * BLOCK_HEIGHT]; static byte intensitytable[256]; static unsigned char gammatable[256]; cvar_t *intensity; unsigned d_8to24table[256]; qboolean R_Upload8(byte *data, int width, int height, qboolean mipmap, qboolean is_sky); qboolean R_Upload32(unsigned *data, int width, int height, qboolean mipmap); int gl_solid_format = 3; int gl_alpha_format = 4; int gl_tex_solid_format = 3; int gl_tex_alpha_format = 4; int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; int gl_filter_max = GL_LINEAR; int Draw_GetPalette(void); typedef struct { char *name; int minimize, maximize; } glmode_t; glmode_t modes[] = { {"GL_NEAREST", GL_NEAREST, GL_NEAREST}, {"GL_LINEAR", GL_LINEAR, GL_LINEAR}, {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} }; #define NUM_GL_MODES (sizeof(modes) / sizeof(glmode_t)) typedef struct { char *name; int mode; } gltmode_t; gltmode_t gl_alpha_modes[] = { {"default", 4}, {"GL_RGBA", GL_RGBA}, {"GL_RGBA8", GL_RGBA8}, {"GL_RGB5_A1", GL_RGB5_A1}, {"GL_RGBA4", GL_RGBA4}, {"GL_RGBA2", GL_RGBA2}, }; #define NUM_GL_ALPHA_MODES (sizeof(gl_alpha_modes) / sizeof(gltmode_t)) gltmode_t gl_solid_modes[] = { {"default", 3}, {"GL_RGB", GL_RGB}, {"GL_RGB8", GL_RGB8}, {"GL_RGB5", GL_RGB5}, {"GL_RGB4", GL_RGB4}, {"GL_R3_G3_B2", GL_R3_G3_B2}, #ifdef GL_RGB2_EXT {"GL_RGB2", GL_RGB2_EXT}, #endif }; #define NUM_GL_SOLID_MODES (sizeof(gl_solid_modes) / sizeof(gltmode_t)) typedef struct { short x, y; } floodfill_t; /* 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]; \ } \ } int upload_width, upload_height; qboolean uploaded_paletted; void R_SetTexturePalette(unsigned palette[256]) { int i; unsigned char temptable[768]; if (qglColorTableEXT && gl_ext_palettedtexture->value) { for (i = 0; i < 256; i++) { temptable[i * 3 + 0] = (palette[i] >> 0) & 0xff; temptable[i * 3 + 1] = (palette[i] >> 8) & 0xff; temptable[i * 3 + 2] = (palette[i] >> 16) & 0xff; } qglColorTableEXT(GL_SHARED_TEXTURE_PALETTE_EXT, GL_RGB, 256, GL_RGB, GL_UNSIGNED_BYTE, temptable); } } void R_EnableMultitexture(qboolean enable) { if (!qglActiveTextureARB) { return; } if (enable) { R_SelectTexture(GL_TEXTURE1_ARB); glEnable(GL_TEXTURE_2D); R_TexEnv(GL_REPLACE); } else { R_SelectTexture(GL_TEXTURE1_ARB); glDisable(GL_TEXTURE_2D); R_TexEnv(GL_REPLACE); } R_SelectTexture(GL_TEXTURE0_ARB); R_TexEnv(GL_REPLACE); } void R_SelectTexture(GLenum texture) { int tmu; if (!qglActiveTextureARB) { return; } if (texture == GL_TEXTURE0_ARB) { tmu = 0; } else { tmu = 1; } if (tmu == gl_state.currenttmu) { return; } gl_state.currenttmu = tmu; if (qglActiveTextureARB) { qglActiveTextureARB(texture); qglClientActiveTextureARB(texture); } } void R_TexEnv(GLenum mode) { static int lastmodes[2] = {-1, -1}; if (mode != lastmodes[gl_state.currenttmu]) { glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode); lastmodes[gl_state.currenttmu] = mode; } } void R_Bind(int texnum) { extern image_t *draw_chars; if (gl_nobind->value && draw_chars) /* performance evaluation option */ { texnum = draw_chars->texnum; } if (gl_state.currenttextures[gl_state.currenttmu] == texnum) { return; } gl_state.currenttextures[gl_state.currenttmu] = texnum; glBindTexture(GL_TEXTURE_2D, texnum); } void R_MBind(GLenum target, int texnum) { R_SelectTexture(target); if (target == GL_TEXTURE0_ARB) { if (gl_state.currenttextures[0] == texnum) { return; } } else { if (gl_state.currenttextures[1] == texnum) { return; } } R_Bind(texnum); } void R_TextureMode(char *string) { int i; image_t *glt; for (i = 0; i < NUM_GL_MODES; i++) { if (!Q_stricmp(modes[i].name, string)) { break; } } if (i == NUM_GL_MODES) { VID_Printf(PRINT_ALL, "bad filter name\n"); return; } gl_filter_min = modes[i].minimize; gl_filter_max = modes[i].maximize; /* clamp selected anisotropy */ if (gl_config.anisotropic) { if (gl_anisotropic->value > gl_config.max_anisotropy) { Cvar_SetValue("gl_anisotropic", gl_config.max_anisotropy); } else if (gl_anisotropic->value < 1.0) { Cvar_SetValue("gl_anisotropic", 1.0); } } else { Cvar_SetValue("gl_anisotropic", 0.0); } /* change all the existing mipmap texture objects */ for (i = 0, glt = gltextures; i < numgltextures; i++, glt++) { if ((glt->type != it_pic) && (glt->type != it_sky)) { R_Bind(glt->texnum); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); /* Set anisotropic filter if supported and enabled */ if (gl_config.anisotropic && gl_anisotropic->value) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropic->value); } } } } void R_TextureAlphaMode(char *string) { int i; for (i = 0; i < NUM_GL_ALPHA_MODES; i++) { if (!Q_stricmp(gl_alpha_modes[i].name, string)) { break; } } if (i == NUM_GL_ALPHA_MODES) { VID_Printf(PRINT_ALL, "bad alpha texture mode name\n"); return; } gl_tex_alpha_format = gl_alpha_modes[i].mode; } void R_TextureSolidMode(char *string) { int i; for (i = 0; i < NUM_GL_SOLID_MODES; i++) { if (!Q_stricmp(gl_solid_modes[i].name, string)) { break; } } if (i == NUM_GL_SOLID_MODES) { VID_Printf(PRINT_ALL, "bad solid texture mode name\n"); return; } gl_tex_solid_format = gl_solid_modes[i].mode; } void R_ImageList_f(void) { int i; image_t *image; int texels; const char *palstrings[2] = { "RGB", "PAL" }; VID_Printf(PRINT_ALL, "------------------\n"); texels = 0; for (i = 0, image = gltextures; i < numgltextures; i++, image++) { if (image->texnum <= 0) { continue; } texels += image->upload_width * image->upload_height; switch (image->type) { case it_skin: VID_Printf(PRINT_ALL, "M"); break; case it_sprite: VID_Printf(PRINT_ALL, "S"); break; case it_wall: VID_Printf(PRINT_ALL, "W"); break; case it_pic: VID_Printf(PRINT_ALL, "P"); break; default: VID_Printf(PRINT_ALL, " "); break; } VID_Printf(PRINT_ALL, " %3i %3i %s: %s\n", image->upload_width, image->upload_height, palstrings[image->paletted], image->name); } VID_Printf(PRINT_ALL, "Total texel count (not counting mipmaps): %i\n", texels); } /* * Fill background pixels so mipmapping doesn't have haloes */ void R_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 (LittleLong(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)) { 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; } } void R_ResampleTexture(unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight) { int i, j; unsigned *inrow, *inrow2; unsigned frac, fracstep; unsigned p1[1024], p2[1024]; byte *pix1, *pix2, *pix3, *pix4; fracstep = inwidth * 0x10000 / outwidth; frac = fracstep >> 2; for (i = 0; i < outwidth; i++) { p1[i] = 4 * (frac >> 16); frac += fracstep; } frac = 3 * (fracstep >> 2); for (i = 0; i < outwidth; i++) { p2[i] = 4 * (frac >> 16); frac += fracstep; } for (i = 0; i < outheight; i++, out += outwidth) { inrow = in + inwidth * (int)((i + 0.25) * inheight / outheight); inrow2 = in + inwidth * (int)((i + 0.75) * inheight / outheight); for (j = 0; j < outwidth; j++) { pix1 = (byte *)inrow + p1[j]; pix2 = (byte *)inrow + p2[j]; pix3 = (byte *)inrow2 + p1[j]; pix4 = (byte *)inrow2 + p2[j]; ((byte *)(out + j))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0]) >> 2; ((byte *)(out + j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1]) >> 2; ((byte *)(out + j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2]) >> 2; ((byte *)(out + j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3]) >> 2; } } } /* * Scale up the pixel values in a * texture to increase the * lighting range */ void R_LightScaleTexture(unsigned *in, int inwidth, int inheight, qboolean only_gamma) { if (only_gamma) { int i, c; byte *p; p = (byte *)in; c = inwidth * inheight; for (i = 0; i < c; i++, p += 4) { p[0] = gammatable[p[0]]; p[1] = gammatable[p[1]]; p[2] = gammatable[p[2]]; } } else { int i, c; byte *p; p = (byte *)in; c = inwidth * inheight; for (i = 0; i < c; i++, p += 4) { p[0] = gammatable[intensitytable[p[0]]]; p[1] = gammatable[intensitytable[p[1]]]; p[2] = gammatable[intensitytable[p[2]]]; } } } /* * Operates in place, quartering the size of the texture */ void R_MipMap(byte *in, int width, int height) { int i, j; byte *out; width <<= 2; height >>= 1; out = in; for (i = 0; i < height; i++, in += width) { for (j = 0; j < width; j += 8, out += 4, in += 8) { out[0] = (in[0] + in[4] + in[width + 0] + in[width + 4]) >> 2; out[1] = (in[1] + in[5] + in[width + 1] + in[width + 5]) >> 2; out[2] = (in[2] + in[6] + in[width + 2] + in[width + 6]) >> 2; out[3] = (in[3] + in[7] + in[width + 3] + in[width + 7]) >> 2; } } } /* * Returns has_alpha */ void R_BuildPalettedTexture(unsigned char *paletted_texture, unsigned char *scaled, int scaled_width, int scaled_height) { int i; for (i = 0; i < scaled_width * scaled_height; i++) { unsigned int r, g, b, c; r = (scaled[0] >> 3) & 31; g = (scaled[1] >> 2) & 63; b = (scaled[2] >> 3) & 31; c = r | (g << 5) | (b << 11); paletted_texture[i] = gl_state.d_16to8table[c]; scaled += 4; } } // Windows headers don't define this constant. #ifndef GL_GENERATE_MIPMAP #define GL_GENERATE_MIPMAP 0x8191 #endif qboolean R_Upload32Native(unsigned *data, int width, int height, qboolean mipmap) { // This is for GL 2.x so no palettes, no scaling, no messing around with the data here. :) int samples; int i, c; byte *scan; int comp; c = width * height; scan = ((byte *)data) + 3; samples = gl_solid_format; comp = gl_tex_solid_format; upload_width = width; upload_height = height; R_LightScaleTexture(data, upload_width, upload_height, !mipmap); for (i = 0; i < c; i++, scan += 4) { if (*scan != 255) { samples = gl_alpha_format; comp = gl_tex_alpha_format; break; } } glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, mipmap); glTexImage2D(GL_TEXTURE_2D, 0, comp, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false); return samples == gl_alpha_format; } qboolean R_Upload32Old(unsigned *data, int width, int height, qboolean mipmap) { int samples; unsigned scaled[256 * 256]; unsigned char paletted_texture[256 * 256]; int scaled_width, scaled_height; int i, c; byte *scan; int comp; uploaded_paletted = false; for (scaled_width = 1; scaled_width < width; scaled_width <<= 1) { } if (gl_round_down->value && (scaled_width > width) && mipmap) { scaled_width >>= 1; } for (scaled_height = 1; scaled_height < height; scaled_height <<= 1) { } if (gl_round_down->value && (scaled_height > height) && mipmap) { scaled_height >>= 1; } /* let people sample down the world textures for speed */ if (mipmap) { scaled_width >>= (int)gl_picmip->value; scaled_height >>= (int)gl_picmip->value; } /* don't ever bother with >256 textures */ if (scaled_width > 256) { scaled_width = 256; } if (scaled_height > 256) { scaled_height = 256; } if (scaled_width < 1) { scaled_width = 1; } if (scaled_height < 1) { scaled_height = 1; } upload_width = scaled_width; upload_height = scaled_height; if (scaled_width * scaled_height > sizeof(scaled) / 4) { VID_Error(ERR_DROP, "R_Upload32: too big"); } /* scan the texture for any non-255 alpha */ c = width * height; scan = ((byte *)data) + 3; samples = gl_solid_format; comp = gl_tex_solid_format; for (i = 0; i < c; i++, scan += 4) { if (*scan != 255) { samples = gl_alpha_format; comp = gl_tex_alpha_format; break; } } if ((scaled_width == width) && (scaled_height == height)) { if (!mipmap) { if (qglColorTableEXT && gl_ext_palettedtexture->value && (samples == gl_solid_format)) { uploaded_paletted = true; R_BuildPalettedTexture(paletted_texture, (unsigned char *)data, scaled_width, scaled_height); glTexImage2D(GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, paletted_texture); } else { glTexImage2D(GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); } goto done; } memcpy(scaled, data, width * height * 4); } else { R_ResampleTexture(data, width, height, scaled, scaled_width, scaled_height); } R_LightScaleTexture(scaled, scaled_width, scaled_height, !mipmap); if (qglColorTableEXT && gl_ext_palettedtexture->value && (samples == gl_solid_format)) { uploaded_paletted = true; R_BuildPalettedTexture(paletted_texture, (unsigned char *)scaled, scaled_width, scaled_height); glTexImage2D(GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, paletted_texture); } else { glTexImage2D(GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } if (mipmap) { int miplevel; miplevel = 0; while (scaled_width > 1 || scaled_height > 1) { R_MipMap((byte *)scaled, scaled_width, scaled_height); scaled_width >>= 1; scaled_height >>= 1; if (scaled_width < 1) { scaled_width = 1; } if (scaled_height < 1) { scaled_height = 1; } miplevel++; if (qglColorTableEXT && gl_ext_palettedtexture->value && (samples == gl_solid_format)) { uploaded_paletted = true; R_BuildPalettedTexture(paletted_texture, (unsigned char *)scaled, scaled_width, scaled_height); glTexImage2D(GL_TEXTURE_2D, miplevel, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, paletted_texture); } else { glTexImage2D(GL_TEXTURE_2D, miplevel, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled); } } } done: return samples == gl_alpha_format; } qboolean R_Upload32(unsigned *data, int width, int height, qboolean mipmap) { qboolean res; if (gl_config.tex_npot) { res = R_Upload32Native(data, width, height, mipmap); } else { res = R_Upload32Old(data, width, height, mipmap); } if (mipmap) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } if (mipmap && gl_config.anisotropic && gl_anisotropic->value) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropic->value); } return res; } /* * Returns has_alpha */ qboolean R_Upload8(byte *data, int width, int height, qboolean mipmap, qboolean is_sky) { unsigned trans[512 * 256]; int i, s; int p; s = width * height; if (s > sizeof(trans) / 4) { VID_Error(ERR_DROP, "R_Upload8: too large"); } if (qglColorTableEXT && gl_ext_palettedtexture->value && is_sky) { glTexImage2D(GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, width, height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); return false; /* SBF: FIXME - what is the correct return value? */ } else { for (i = 0; i < s; i++) { p = data[i]; trans[i] = d_8to24table[p]; /* transparent, so scan around for another color to avoid alpha fringes */ if (p == 255) { if ((i > width) && (data[i - width] != 255)) { p = data[i - width]; } else if ((i < s - width) && (data[i + width] != 255)) { p = data[i + width]; } else if ((i > 0) && (data[i - 1] != 255)) { p = data[i - 1]; } else if ((i < s - 1) && (data[i + 1] != 255)) { p = data[i + 1]; } else { p = 0; } /* copy rgb components */ ((byte *)&trans[i])[0] = ((byte *)&d_8to24table[p])[0]; ((byte *)&trans[i])[1] = ((byte *)&d_8to24table[p])[1]; ((byte *)&trans[i])[2] = ((byte *)&d_8to24table[p])[2]; } } return R_Upload32(trans, width, height, mipmap); } } /* * This is also used as an entry point for the generated r_notexture */ image_t * R_LoadPic(char *name, byte *pic, int width, int realwidth, int height, int realheight, imagetype_t type, int bits) { image_t *image; int i; /* find a free image_t */ for (i = 0, image = gltextures; i < numgltextures; i++, image++) { if (!image->texnum) { break; } } if (i == numgltextures) { if (numgltextures == MAX_GLTEXTURES) { VID_Error(ERR_DROP, "MAX_GLTEXTURES"); } numgltextures++; } image = &gltextures[i]; if (strlen(name) >= sizeof(image->name)) { VID_Error(ERR_DROP, "Draw_LoadPic: \"%s\" is too long", name); } strcpy(image->name, name); image->registration_sequence = registration_sequence; image->width = width; image->height = height; image->type = type; if ((type == it_skin) && (bits == 8)) { R_FloodFillSkin(pic, width, height); } /* load little pics into the scrap */ if ((image->type == it_pic) && (bits == 8) && (image->width < 64) && (image->height < 64)) { int x, y; int i, j, k; int texnum; texnum = Scrap_AllocBlock(image->width, image->height, &x, &y); if (texnum == -1) { goto nonscrap; } scrap_dirty = true; /* copy the texels into the scrap block */ k = 0; for (i = 0; i < image->height; i++) { for (j = 0; j < image->width; j++, k++) { scrap_texels[texnum][(y + i) * BLOCK_WIDTH + x + j] = pic[k]; } } image->texnum = TEXNUM_SCRAPS + texnum; image->scrap = true; image->has_alpha = true; image->sl = (x + 0.01) / (float)BLOCK_WIDTH; image->sh = (x + image->width - 0.01) / (float)BLOCK_WIDTH; image->tl = (y + 0.01) / (float)BLOCK_WIDTH; image->th = (y + image->height - 0.01) / (float)BLOCK_WIDTH; } else { nonscrap: image->scrap = false; image->texnum = TEXNUM_IMAGES + (image - gltextures); R_Bind(image->texnum); if (bits == 8) { image->has_alpha = R_Upload8(pic, width, height, (image->type != it_pic && image->type != it_sky), image->type == it_sky); } else { image->has_alpha = R_Upload32((unsigned *)pic, width, height, (image->type != it_pic && image->type != it_sky)); } image->upload_width = upload_width; /* after power of 2 and scales */ image->upload_height = upload_height; image->paletted = uploaded_paletted; if (realwidth && realheight) { if ((realwidth <= image->width) && (realheight <= image->height)) { image->width = realwidth; image->height = realheight; } else { VID_Printf(PRINT_DEVELOPER, "Warning, image '%s' has hi-res replacement smaller than the original! (%d x %d) < (%d x %d)\n", name, image->width, image->height, realwidth, realheight); } } image->sl = 0; image->sh = 1; image->tl = 0; image->th = 1; } return image; } /* * Finds or loads the given image */ image_t * R_FindImage(char *name, imagetype_t type) { image_t *image; int i, len; byte *pic, *palette; int width, height; char *ptr; char namewe[256]; int realwidth = 0, realheight = 0; const char* ext; if (!name) { return NULL; } ext = COM_FileExtension(name); if(!ext[0]) { /* file has no extension */ return NULL; } len = strlen(name); /* Remove the extension */ memset(namewe, 0, 256); memcpy(namewe, name, len - 4); if (len < 5) { return NULL; } /* fix backslashes */ while ((ptr = strchr(name, '\\'))) { *ptr = '/'; } /* look for it */ for (i = 0, image = gltextures; i < numgltextures; i++, image++) { if (!strcmp(name, image->name)) { image->registration_sequence = registration_sequence; return image; } } /* load the pic from disk */ pic = NULL; palette = NULL; if (strcmp(ext, "pcx") == 0) { if (gl_retexturing->value) { GetPCXInfo(name, &realwidth, &realheight); if(realwidth == 0) { /* No texture found */ return NULL; } /* try to load a tga, png or jpg (in that order/priority) */ if ( LoadSTB(namewe, "tga", &pic, &width, &height) || LoadSTB(namewe, "png", &pic, &width, &height) || LoadSTB(namewe, "jpg", &pic, &width, &height) ) { /* upload tga or png or jpg */ image = R_LoadPic(name, pic, width, realwidth, height, realheight, type, 32); } else { /* PCX if no TGA/PNG/JPEG available (exists always) */ LoadPCX(name, &pic, &palette, &width, &height); if (!pic) { /* No texture found */ return NULL; } /* Upload the PCX */ image = R_LoadPic(name, pic, width, 0, height, 0, type, 8); } } else /* gl_retexture is not set */ { LoadPCX(name, &pic, &palette, &width, &height); if (!pic) { return NULL; } image = R_LoadPic(name, pic, width, 0, height, 0, type, 8); } } else if (strcmp(ext, "wal") == 0) { if (gl_retexturing->value) { /* Get size of the original texture */ GetWalInfo(name, &realwidth, &realheight); if(realwidth == 0) { /* No texture found */ return NULL; } /* try to load a tga, png or jpg (in that order/priority) */ if ( LoadSTB(namewe, "tga", &pic, &width, &height) || LoadSTB(namewe, "png", &pic, &width, &height) || LoadSTB(namewe, "jpg", &pic, &width, &height) ) { /* upload tga or png or jpg */ image = R_LoadPic(name, pic, width, realwidth, height, realheight, type, 32); } else { /* WAL if no TGA/PNG/JPEG available (exists always) */ image = LoadWal(namewe); } if (!image) { /* No texture found */ return NULL; } } else /* gl_retexture is not set */ { image = LoadWal(name); if (!image) { /* No texture found */ return NULL; } } } else if (strcmp(ext, "tga") == 0 || strcmp(ext, "png") == 0 || strcmp(ext, "jpg") == 0) { char tmp_name[256]; realwidth = 0; realheight = 0; strcpy(tmp_name, namewe); strcat(tmp_name, ".wal"); GetWalInfo(tmp_name, &realwidth, &realheight); if (realwidth == 0 || realheight == 0) { /* It's a sky or model skin. */ strcpy(tmp_name, namewe); strcat(tmp_name, ".pcx"); GetPCXInfo(tmp_name, &realwidth, &realheight); } /* TODO: not sure if not having realwidth/heigth is bad - a tga/png/jpg * was requested, after all, so there might be no corresponding wal/pcx? * if (realwidth == 0 || realheight == 0) return NULL; */ LoadSTB(name, ext, &pic, &width, &height); image = R_LoadPic(name, pic, width, realwidth, height, realheight, type, 32); } else { return NULL; } if (pic) { free(pic); } if (palette) { free(palette); } return image; } struct image_s * R_RegisterSkin(char *name) { return R_FindImage(name, it_skin); } /* * Any image that was not touched on * this registration sequence * will be freed. */ void R_FreeUnusedImages(void) { int i; image_t *image; /* never free r_notexture or particle texture */ r_notexture->registration_sequence = registration_sequence; r_particletexture->registration_sequence = registration_sequence; for (i = 0, image = gltextures; i < numgltextures; i++, image++) { if (image->registration_sequence == registration_sequence) { continue; /* used this sequence */ } if (!image->registration_sequence) { continue; /* free image_t slot */ } if (image->type == it_pic) { continue; /* don't free pics */ } /* free it */ glDeleteTextures(1, (GLuint *)&image->texnum); memset(image, 0, sizeof(*image)); } } void R_InitImages(void) { int i, j; float g = vid_gamma->value; registration_sequence = 1; /* init intensity conversions */ intensity = Cvar_Get("intensity", "2", CVAR_ARCHIVE); if (intensity->value <= 1) { Cvar_Set("intensity", "1"); } gl_state.inverse_intensity = 1 / intensity->value; Draw_GetPalette(); if (qglColorTableEXT) { FS_LoadFile("pics/16to8.dat", (void **)&gl_state.d_16to8table); if (!gl_state.d_16to8table) { VID_Error(ERR_FATAL, "Couldn't load pics/16to8.pcx"); } } for (i = 0; i < 256; i++) { if ((g == 1) || gl_state.hwgamma) { gammatable[i] = i; } else { float inf; inf = 255 * pow((i + 0.5) / 255.5, g) + 0.5; if (inf < 0) { inf = 0; } if (inf > 255) { inf = 255; } gammatable[i] = inf; } } for (i = 0; i < 256; i++) { j = i * intensity->value; if (j > 255) { j = 255; } intensitytable[i] = j; } } void R_ShutdownImages(void) { int i; image_t *image; for (i = 0, image = gltextures; i < numgltextures; i++, image++) { if (!image->registration_sequence) { continue; /* free image_t slot */ } /* free it */ glDeleteTextures(1, (GLuint *)&image->texnum); memset(image, 0, sizeof(*image)); } } yquake2-QUAKE2_5_32/src/client/refresh/r_draw.c0000644000175000017500000001743512615162034021756 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Drawing of all images that are not textures * * ======================================================================= */ #include "header/local.h" image_t *draw_chars; extern qboolean scrap_dirty; void Scrap_Upload(void); extern unsigned r_rawpalette[256]; void Draw_InitLocal(void) { /* load console characters (don't bilerp characters) */ draw_chars = R_FindImage("pics/conchars.pcx", it_pic); R_Bind(draw_chars->texnum); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } /* * 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_Char(int x, int y, int num) { Draw_CharScaled(x, y, num, 1.0f); } /* * 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_CharScaled(int x, int y, int num, float scale) { int row, col; float frow, fcol, size, scaledSize; num &= 255; if ((num & 127) == 32) { return; /* space */ } if (y <= -8) { return; /* totally off screen */ } row = num >> 4; col = num & 15; frow = row * 0.0625; fcol = col * 0.0625; size = 0.0625; scaledSize = 8*scale; R_Bind(draw_chars->texnum); glBegin(GL_QUADS); glTexCoord2f(fcol, frow); glVertex2f(x, y); glTexCoord2f(fcol + size, frow); glVertex2f(x + scaledSize, y); glTexCoord2f(fcol + size, frow + size); glVertex2f(x + scaledSize, y + scaledSize); glTexCoord2f(fcol, frow + size); glVertex2f(x, y + scaledSize); glEnd(); } image_t * Draw_FindPic(char *name) { image_t *gl; char fullname[MAX_QPATH]; if ((name[0] != '/') && (name[0] != '\\')) { Com_sprintf(fullname, sizeof(fullname), "pics/%s.pcx", name); gl = R_FindImage(fullname, it_pic); } else { gl = R_FindImage(name + 1, it_pic); } return gl; } void Draw_GetPicSize(int *w, int *h, char *pic) { image_t *gl; gl = Draw_FindPic(pic); if (!gl) { *w = *h = -1; return; } *w = gl->width; *h = gl->height; } void Draw_StretchPic(int x, int y, int w, int h, char *pic) { image_t *gl; gl = Draw_FindPic(pic); if (!gl) { VID_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); return; } if (scrap_dirty) { Scrap_Upload(); } R_Bind(gl->texnum); glBegin(GL_QUADS); glTexCoord2f(gl->sl, gl->tl); glVertex2f(x, y); glTexCoord2f(gl->sh, gl->tl); glVertex2f(x + w, y); glTexCoord2f(gl->sh, gl->th); glVertex2f(x + w, y + h); glTexCoord2f(gl->sl, gl->th); glVertex2f(x, y + h); glEnd(); } void Draw_Pic(int x, int y, char *pic) { Draw_PicScaled(x, y, pic, 1.0f); } void Draw_PicScaled(int x, int y, char *pic, float factor) { image_t *gl; gl = Draw_FindPic(pic); if (!gl) { VID_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); return; } if (scrap_dirty) { Scrap_Upload(); } GLfloat w = gl->width*factor; GLfloat h = gl->height*factor; R_Bind(gl->texnum); glBegin(GL_QUADS); glTexCoord2f(gl->sl, gl->tl); glVertex2f(x, y); glTexCoord2f(gl->sh, gl->tl); glVertex2f(x + w, y); glTexCoord2f(gl->sh, gl->th); glVertex2f(x + w, y + h); glTexCoord2f(gl->sl, gl->th); glVertex2f(x, y + h); glEnd(); } /* * This repeats a 64*64 tile graphic to fill * the screen around a sized down * refresh window. */ void Draw_TileClear(int x, int y, int w, int h, char *pic) { image_t *image; image = Draw_FindPic(pic); if (!image) { VID_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); return; } R_Bind(image->texnum); glBegin(GL_QUADS); glTexCoord2f(x / 64.0, y / 64.0); glVertex2f(x, y); glTexCoord2f((x + w) / 64.0, y / 64.0); glVertex2f(x + w, y); glTexCoord2f((x + w) / 64.0, (y + h) / 64.0); glVertex2f(x + w, y + h); glTexCoord2f(x / 64.0, (y + h) / 64.0); glVertex2f(x, y + h); glEnd(); } /* * Fills a box of pixels with a single color */ void Draw_Fill(int x, int y, int w, int h, int c) { union { unsigned c; byte v[4]; } color; if ((unsigned)c > 255) { VID_Error(ERR_FATAL, "Draw_Fill: bad color"); } glDisable(GL_TEXTURE_2D); color.c = d_8to24table[c]; glColor3f(color.v[0] / 255.0, color.v[1] / 255.0, color.v[2] / 255.0); glBegin(GL_QUADS); glVertex2f(x, y); glVertex2f(x + w, y); glVertex2f(x + w, y + h); glVertex2f(x, y + h); glEnd(); glColor3f(1, 1, 1); glEnable(GL_TEXTURE_2D); } void Draw_FadeScreen(void) { glEnable(GL_BLEND); glDisable(GL_TEXTURE_2D); glColor4f(0, 0, 0, 0.8); glBegin(GL_QUADS); glVertex2f(0, 0); glVertex2f(vid.width, 0); glVertex2f(vid.width, vid.height); glVertex2f(0, vid.height); glEnd(); glColor4f(1, 1, 1, 1); glEnable(GL_TEXTURE_2D); glDisable(GL_BLEND); } void Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, byte *data) { unsigned image32[256 * 256]; unsigned char image8[256 * 256]; int i, j, trows; byte *source; int frac, fracstep; float hscale; int row; float t; R_Bind(0); if (rows <= 256) { hscale = 1; trows = rows; } else { hscale = rows / 256.0; trows = 256; } t = rows * hscale / 256 - 1.0 / 512.0; if (!qglColorTableEXT) { unsigned *dest; for (i = 0; i < trows; i++) { row = (int)(i * hscale); if (row > rows) { break; } source = data + cols * row; dest = &image32[i * 256]; fracstep = cols * 0x10000 / 256; frac = fracstep >> 1; for (j = 0; j < 256; j++) { dest[j] = r_rawpalette[source[frac >> 16]]; frac += fracstep; } } glTexImage2D(GL_TEXTURE_2D, 0, gl_tex_solid_format, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, image32); } else { unsigned char *dest; for (i = 0; i < trows; i++) { row = (int)(i * hscale); if (row > rows) { break; } source = data + cols * row; dest = &image8[i * 256]; fracstep = cols * 0x10000 / 256; frac = fracstep >> 1; for (j = 0; j < 256; j++) { dest[j] = source[frac >> 16]; frac += fracstep; } } glTexImage2D(GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, 256, 256, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, image8); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBegin(GL_QUADS); glTexCoord2f(1.0 / 512.0, 1.0 / 512.0); glVertex2f(x, y); glTexCoord2f(511.0 / 512.0, 1.0 / 512.0); glVertex2f(x + w, y); glTexCoord2f(511.0 / 512.0, t); glVertex2f(x + w, y + h); glTexCoord2f(1.0 / 512.0, t); glVertex2f(x, y + h); glEnd(); } int Draw_GetPalette(void) { int i; int r, g, b; unsigned v; byte *pic, *pal; int width, height; /* get the palette */ LoadPCX("pics/colormap.pcx", &pic, &pal, &width, &height); if (!pal) { VID_Error(ERR_FATAL, "Couldn't load pics/colormap.pcx"); } for (i = 0; i < 256; i++) { r = pal[i * 3 + 0]; g = pal[i * 3 + 1]; b = pal[i * 3 + 2]; v = (255 << 24) + (r << 0) + (g << 8) + (b << 16); d_8to24table[i] = LittleLong(v); } d_8to24table[255] &= LittleLong(0xffffff); /* 255 is transparent */ free(pic); free(pal); return 0; } yquake2-QUAKE2_5_32/src/client/refresh/r_lightmap.c0000644000175000017500000001446212615162034022623 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Lightmap handling * * ======================================================================= */ #include "header/local.h" extern gllightmapstate_t gl_lms; void R_SetCacheState(msurface_t *surf); void R_BuildLightMap(msurface_t *surf, byte *dest, int stride); void LM_InitBlock(void) { memset(gl_lms.allocated, 0, sizeof(gl_lms.allocated)); } void LM_UploadBlock(qboolean dynamic) { int texture; int height = 0; if (dynamic) { texture = 0; } else { texture = gl_lms.current_lightmap_texture; } R_Bind(gl_state.lightmap_textures + texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); if (dynamic) { int i; for (i = 0; i < BLOCK_WIDTH; i++) { if (gl_lms.allocated[i] > height) { height = gl_lms.allocated[i]; } } glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, BLOCK_WIDTH, height, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer); } else { glTexImage2D(GL_TEXTURE_2D, 0, gl_lms.internal_format, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer); if (++gl_lms.current_lightmap_texture == MAX_LIGHTMAPS) { VID_Error(ERR_DROP, "LM_UploadBlock() - MAX_LIGHTMAPS exceeded\n"); } } } /* * returns a texture number and the position inside it */ qboolean LM_AllocBlock(int w, int h, int *x, int *y) { int i, j; int best, best2; best = BLOCK_HEIGHT; for (i = 0; i < BLOCK_WIDTH - w; i++) { best2 = 0; for (j = 0; j < w; j++) { if (gl_lms.allocated[i + j] >= best) { break; } if (gl_lms.allocated[i + j] > best2) { best2 = gl_lms.allocated[i + j]; } } if (j == w) { /* this is a valid spot */ *x = i; *y = best = best2; } } if (best + h > BLOCK_HEIGHT) { return false; } for (i = 0; i < w; i++) { gl_lms.allocated[*x + i] = best + h; } return true; } void LM_BuildPolygonFromSurface(msurface_t *fa) { int i, lindex, lnumverts; medge_t *pedges, *r_pedge; float *vec; float s, t; glpoly_t *poly; vec3_t total; /* reconstruct the polygon */ pedges = currentmodel->edges; lnumverts = fa->numedges; VectorClear(total); /* draw texture */ poly = Hunk_Alloc(sizeof(glpoly_t) + (lnumverts - 4) * VERTEXSIZE * sizeof(float)); poly->next = fa->polys; poly->flags = fa->flags; fa->polys = poly; poly->numverts = lnumverts; for (i = 0; i < lnumverts; i++) { lindex = currentmodel->surfedges[fa->firstedge + i]; if (lindex > 0) { r_pedge = &pedges[lindex]; vec = currentmodel->vertexes[r_pedge->v[0]].position; } else { r_pedge = &pedges[-lindex]; vec = currentmodel->vertexes[r_pedge->v[1]].position; } s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s /= fa->texinfo->image->width; t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t /= fa->texinfo->image->height; VectorAdd(total, vec, total); VectorCopy(vec, poly->verts[i]); poly->verts[i][3] = s; poly->verts[i][4] = t; /* lightmap texture coordinates */ s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s -= fa->texturemins[0]; s += fa->light_s * 16; s += 8; s /= BLOCK_WIDTH * 16; /* fa->texinfo->texture->width; */ t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t -= fa->texturemins[1]; t += fa->light_t * 16; t += 8; t /= BLOCK_HEIGHT * 16; /* fa->texinfo->texture->height; */ poly->verts[i][5] = s; poly->verts[i][6] = t; } poly->numverts = lnumverts; } void LM_CreateSurfaceLightmap(msurface_t *surf) { int smax, tmax; byte *base; if (surf->flags & (SURF_DRAWSKY | SURF_DRAWTURB)) { return; } smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; if (!LM_AllocBlock(smax, tmax, &surf->light_s, &surf->light_t)) { LM_UploadBlock(false); LM_InitBlock(); if (!LM_AllocBlock(smax, tmax, &surf->light_s, &surf->light_t)) { VID_Error(ERR_FATAL, "Consecutive calls to LM_AllocBlock(%d,%d) failed\n", smax, tmax); } } surf->lightmaptexturenum = gl_lms.current_lightmap_texture; base = gl_lms.lightmap_buffer; base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * LIGHTMAP_BYTES; R_SetCacheState(surf); R_BuildLightMap(surf, base, BLOCK_WIDTH * LIGHTMAP_BYTES); } void LM_BeginBuildingLightmaps(model_t *m) { static lightstyle_t lightstyles[MAX_LIGHTSTYLES]; int i; unsigned dummy[128 * 128]; memset(gl_lms.allocated, 0, sizeof(gl_lms.allocated)); r_framecount = 1; /* no dlightcache */ R_EnableMultitexture(true); R_SelectTexture(GL_TEXTURE1_ARB); /* setup the base lightstyles so the lightmaps won't have to be regenerated the first time they're seen */ for (i = 0; i < MAX_LIGHTSTYLES; i++) { lightstyles[i].rgb[0] = 1; lightstyles[i].rgb[1] = 1; lightstyles[i].rgb[2] = 1; lightstyles[i].white = 3; } r_newrefdef.lightstyles = lightstyles; if (!gl_state.lightmap_textures) { gl_state.lightmap_textures = TEXNUM_LIGHTMAPS; } gl_lms.current_lightmap_texture = 1; gl_lms.internal_format = gl_tex_solid_format; /* initialize the dynamic lightmap texture */ R_Bind(gl_state.lightmap_textures + 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, gl_lms.internal_format, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, dummy); } void LM_EndBuildingLightmaps(void) { LM_UploadBlock(false); R_EnableMultitexture(false); } yquake2-QUAKE2_5_32/src/client/refresh/r_main.c0000644000175000017500000010127512615162034021741 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Refresher setup and main part of the frame generation * * ======================================================================= */ #include "header/local.h" #define NUM_BEAM_SEGS 6 viddef_t vid; model_t *r_worldmodel; float gldepthmin, gldepthmax; glconfig_t gl_config; glstate_t gl_state; image_t *r_notexture; /* use for bad textures */ image_t *r_particletexture; /* little dot for particles */ entity_t *currententity; model_t *currentmodel; cplane_t frustum[4]; 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; float v_blend[4]; /* final blending color */ void R_Strings(void); /* view origin */ vec3_t vup; vec3_t vpn; vec3_t vright; vec3_t r_origin; float r_world_matrix[16]; float r_base_world_matrix[16]; /* screen size info */ refdef_t r_newrefdef; int r_viewcluster, r_viewcluster2, r_oldviewcluster, r_oldviewcluster2; extern qboolean have_stencil; unsigned r_rawpalette[256]; cvar_t *gl_norefresh; cvar_t *gl_drawentities; cvar_t *gl_drawworld; cvar_t *gl_speeds; cvar_t *gl_fullbright; cvar_t *gl_novis; cvar_t *gl_nocull; cvar_t *gl_lerpmodels; cvar_t *gl_lefthand; cvar_t *gl_farsee; cvar_t *gl_lightlevel; cvar_t *gl_overbrightbits; cvar_t *gl_nosubimage; cvar_t *gl_allow_software; cvar_t *gl_vertex_arrays; cvar_t *gl_particle_min_size; cvar_t *gl_particle_max_size; cvar_t *gl_particle_size; cvar_t *gl_particle_att_a; cvar_t *gl_particle_att_b; cvar_t *gl_particle_att_c; cvar_t *gl_ext_swapinterval; cvar_t *gl_ext_palettedtexture; cvar_t *gl_ext_multitexture; cvar_t *gl_ext_pointparameters; cvar_t *gl_ext_compiled_vertex_array; cvar_t *gl_ext_mtexcombine; cvar_t *gl_bitdepth; cvar_t *gl_drawbuffer; cvar_t *gl_lightmap; cvar_t *gl_shadows; cvar_t *gl_stencilshadow; cvar_t *gl_mode; cvar_t *gl_customwidth; cvar_t *gl_customheight; cvar_t *gl_retexturing; cvar_t *gl_dynamic; cvar_t *gl_modulate; cvar_t *gl_nobind; cvar_t *gl_round_down; cvar_t *gl_picmip; cvar_t *gl_skymip; cvar_t *gl_showtris; cvar_t *gl_ztrick; cvar_t *gl_zfix; cvar_t *gl_finish; cvar_t *gl_clear; cvar_t *gl_cull; cvar_t *gl_polyblend; cvar_t *gl_flashblend; cvar_t *gl_playermip; cvar_t *gl_saturatelighting; cvar_t *gl_swapinterval; cvar_t *gl_texturemode; cvar_t *gl_texturealphamode; cvar_t *gl_texturesolidmode; cvar_t *gl_anisotropic; cvar_t *gl_anisotropic_avail; cvar_t *gl_lockpvs; cvar_t *gl_msaa_samples; cvar_t *vid_fullscreen; cvar_t *vid_gamma; /* * Returns true if the box is completely outside the frustom */ qboolean R_CullBox(vec3_t mins, vec3_t maxs) { int i; if (gl_nocull->value) { return false; } for (i = 0; i < 4; i++) { if (BOX_ON_PLANE_SIDE(mins, maxs, &frustum[i]) == 2) { return true; } } return false; } void R_RotateForEntity(entity_t *e) { glTranslatef(e->origin[0], e->origin[1], e->origin[2]); glRotatef(e->angles[1], 0, 0, 1); glRotatef(-e->angles[0], 0, 1, 0); glRotatef(-e->angles[2], 1, 0, 0); } void R_DrawSpriteModel(entity_t *e) { float alpha = 1.0F; vec3_t point; dsprframe_t *frame; float *up, *right; dsprite_t *psprite; /* don't even bother culling, because it's just a single polygon without a surface cache */ psprite = (dsprite_t *)currentmodel->extradata; e->frame %= psprite->numframes; frame = &psprite->frames[e->frame]; /* normal sprite */ up = vup; right = vright; if (e->flags & RF_TRANSLUCENT) { alpha = e->alpha; } if (alpha != 1.0F) { glEnable(GL_BLEND); } glColor4f(1, 1, 1, alpha); R_Bind(currentmodel->skins[e->frame]->texnum); R_TexEnv(GL_MODULATE); if (alpha == 1.0) { glEnable(GL_ALPHA_TEST); } else { glDisable(GL_ALPHA_TEST); } glBegin(GL_QUADS); glTexCoord2f(0, 1); VectorMA(e->origin, -frame->origin_y, up, point); VectorMA(point, -frame->origin_x, right, point); glVertex3fv(point); glTexCoord2f(0, 0); VectorMA(e->origin, frame->height - frame->origin_y, up, point); VectorMA(point, -frame->origin_x, right, point); glVertex3fv(point); glTexCoord2f(1, 0); VectorMA(e->origin, frame->height - frame->origin_y, up, point); VectorMA(point, frame->width - frame->origin_x, right, point); glVertex3fv(point); glTexCoord2f(1, 1); VectorMA(e->origin, -frame->origin_y, up, point); VectorMA(point, frame->width - frame->origin_x, right, point); glVertex3fv(point); glEnd(); glDisable(GL_ALPHA_TEST); R_TexEnv(GL_REPLACE); if (alpha != 1.0F) { glDisable(GL_BLEND); } glColor4f(1, 1, 1, 1); } void R_DrawNullModel(void) { vec3_t shadelight; int i; if (currententity->flags & RF_FULLBRIGHT) { shadelight[0] = shadelight[1] = shadelight[2] = 1.0F; } else { R_LightPoint(currententity->origin, shadelight); } glPushMatrix(); R_RotateForEntity(currententity); glDisable(GL_TEXTURE_2D); glColor3fv(shadelight); glBegin(GL_TRIANGLE_FAN); glVertex3f(0, 0, -16); for (i = 0; i <= 4; i++) { glVertex3f(16 * cos(i * M_PI / 2), 16 * sin(i * M_PI / 2), 0); } glEnd(); glBegin(GL_TRIANGLE_FAN); glVertex3f(0, 0, 16); for (i = 4; i >= 0; i--) { glVertex3f(16 * cos(i * M_PI / 2), 16 * sin(i * M_PI / 2), 0); } glEnd(); glColor3f(1, 1, 1); glPopMatrix(); glEnable(GL_TEXTURE_2D); } void R_DrawEntitiesOnList(void) { int i; if (!gl_drawentities->value) { return; } /* draw non-transparent first */ for (i = 0; i < r_newrefdef.num_entities; i++) { currententity = &r_newrefdef.entities[i]; if (currententity->flags & RF_TRANSLUCENT) { continue; /* solid */ } if (currententity->flags & RF_BEAM) { R_DrawBeam(currententity); } else { currentmodel = currententity->model; if (!currentmodel) { R_DrawNullModel(); continue; } switch (currentmodel->type) { case mod_alias: R_DrawAliasModel(currententity); break; case mod_brush: R_DrawBrushModel(currententity); break; case mod_sprite: R_DrawSpriteModel(currententity); break; default: VID_Error(ERR_DROP, "Bad modeltype"); break; } } } /* draw transparent entities we could sort these if it ever becomes a problem... */ glDepthMask(0); for (i = 0; i < r_newrefdef.num_entities; i++) { currententity = &r_newrefdef.entities[i]; if (!(currententity->flags & RF_TRANSLUCENT)) { continue; /* solid */ } if (currententity->flags & RF_BEAM) { R_DrawBeam(currententity); } else { currentmodel = currententity->model; if (!currentmodel) { R_DrawNullModel(); continue; } switch (currentmodel->type) { case mod_alias: R_DrawAliasModel(currententity); break; case mod_brush: R_DrawBrushModel(currententity); break; case mod_sprite: R_DrawSpriteModel(currententity); break; default: VID_Error(ERR_DROP, "Bad modeltype"); break; } } } glDepthMask(1); /* back to writing */ } void R_DrawParticles2(int num_particles, const particle_t particles[], const unsigned colortable[768]) { const particle_t *p; int i; vec3_t up, right; float scale; byte color[4]; R_Bind(r_particletexture->texnum); glDepthMask(GL_FALSE); /* no z buffering */ glEnable(GL_BLEND); R_TexEnv(GL_MODULATE); glBegin(GL_TRIANGLES); VectorScale(vup, 1.5, up); VectorScale(vright, 1.5, right); for (p = particles, i = 0; i < num_particles; i++, p++) { /* hack a scale up to keep particles from disapearing */ scale = (p->origin[0] - r_origin[0]) * vpn[0] + (p->origin[1] - r_origin[1]) * vpn[1] + (p->origin[2] - r_origin[2]) * vpn[2]; if (scale < 20) { scale = 1; } else { scale = 1 + scale * 0.004; } *(int *)color = colortable[p->color]; color[3] = p->alpha * 255; glColor4ubv(color); glTexCoord2f(0.0625, 0.0625); glVertex3fv(p->origin); glTexCoord2f(1.0625, 0.0625); glVertex3f(p->origin[0] + up[0] * scale, p->origin[1] + up[1] * scale, p->origin[2] + up[2] * scale); glTexCoord2f(0.0625, 1.0625); glVertex3f(p->origin[0] + right[0] * scale, p->origin[1] + right[1] * scale, p->origin[2] + right[2] * scale); } glEnd(); glDisable(GL_BLEND); glColor4f(1, 1, 1, 1); glDepthMask(1); /* back to normal Z buffering */ R_TexEnv(GL_REPLACE); } void R_DrawParticles(void) { if (gl_ext_pointparameters->value && qglPointParameterfEXT) { int i; unsigned char color[4]; const particle_t *p; glDepthMask(GL_FALSE); glEnable(GL_BLEND); glDisable(GL_TEXTURE_2D); glPointSize(LittleFloat(gl_particle_size->value)); glBegin(GL_POINTS); for (i = 0, p = r_newrefdef.particles; i < r_newrefdef.num_particles; i++, p++) { *(int *)color = d_8to24table[p->color & 0xFF]; color[3] = p->alpha * 255; glColor4ubv(color); glVertex3fv(p->origin); } glEnd(); glDisable(GL_BLEND); glColor4f(1.0F, 1.0F, 1.0F, 1.0F); glDepthMask(GL_TRUE); glEnable(GL_TEXTURE_2D); } else { R_DrawParticles2(r_newrefdef.num_particles, r_newrefdef.particles, d_8to24table); } } void R_PolyBlend(void) { if (!gl_polyblend->value) { return; } if (!v_blend[3]) { return; } glDisable(GL_ALPHA_TEST); glEnable(GL_BLEND); glDisable(GL_DEPTH_TEST); glDisable(GL_TEXTURE_2D); glLoadIdentity(); glRotatef(-90, 1, 0, 0); /* put Z going up */ glRotatef(90, 0, 0, 1); /* put Z going up */ glColor4fv(v_blend); glBegin(GL_QUADS); glVertex3f(10, 100, 100); glVertex3f(10, -100, 100); glVertex3f(10, -100, -100); glVertex3f(10, 100, -100); glEnd(); glDisable(GL_BLEND); glEnable(GL_TEXTURE_2D); glEnable(GL_ALPHA_TEST); glColor4f(1, 1, 1, 1); } int R_SignbitsForPlane(cplane_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 << j; } } return bits; } void R_SetFrustum(void) { int i; /* rotate VPN right by FOV_X/2 degrees */ RotatePointAroundVector(frustum[0].normal, vup, vpn, -(90 - r_newrefdef.fov_x / 2)); /* rotate VPN left by FOV_X/2 degrees */ RotatePointAroundVector(frustum[1].normal, vup, vpn, 90 - r_newrefdef.fov_x / 2); /* rotate VPN up by FOV_X/2 degrees */ RotatePointAroundVector(frustum[2].normal, vright, vpn, 90 - r_newrefdef.fov_y / 2); /* rotate VPN down by FOV_X/2 degrees */ RotatePointAroundVector(frustum[3].normal, vright, vpn, -(90 - r_newrefdef.fov_y / 2)); for (i = 0; i < 4; i++) { frustum[i].type = PLANE_ANYZ; frustum[i].dist = DotProduct(r_origin, frustum[i].normal); frustum[i].signbits = R_SignbitsForPlane(&frustum[i]); } } void R_SetupFrame(void) { int i; mleaf_t *leaf; r_framecount++; /* build the transformation matrix for the given view angles */ VectorCopy(r_newrefdef.vieworg, r_origin); AngleVectors(r_newrefdef.viewangles, vpn, vright, vup); /* current viewcluster */ if (!(r_newrefdef.rdflags & RDF_NOWORLDMODEL)) { r_oldviewcluster = r_viewcluster; r_oldviewcluster2 = r_viewcluster2; leaf = Mod_PointInLeaf(r_origin, r_worldmodel); r_viewcluster = r_viewcluster2 = leaf->cluster; /* check above and below so crossing solid water doesn't draw wrong */ if (!leaf->contents) { /* look down a bit */ vec3_t temp; VectorCopy(r_origin, temp); temp[2] -= 16; leaf = Mod_PointInLeaf(temp, r_worldmodel); if (!(leaf->contents & CONTENTS_SOLID) && (leaf->cluster != r_viewcluster2)) { r_viewcluster2 = leaf->cluster; } } else { /* look up a bit */ vec3_t temp; VectorCopy(r_origin, temp); temp[2] += 16; leaf = Mod_PointInLeaf(temp, r_worldmodel); if (!(leaf->contents & CONTENTS_SOLID) && (leaf->cluster != r_viewcluster2)) { r_viewcluster2 = leaf->cluster; } } } for (i = 0; i < 4; i++) { v_blend[i] = r_newrefdef.blend[i]; } c_brush_polys = 0; c_alias_polys = 0; /* clear out the portion of the screen that the NOWORLDMODEL defines */ if (r_newrefdef.rdflags & RDF_NOWORLDMODEL) { glEnable(GL_SCISSOR_TEST); glClearColor(0.3, 0.3, 0.3, 1); glScissor(r_newrefdef.x, vid.height - r_newrefdef.height - r_newrefdef.y, r_newrefdef.width, r_newrefdef.height); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClearColor(1, 0, 0.5, 0.5); glDisable(GL_SCISSOR_TEST); } } void R_MYgluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar) { GLdouble xmin, xmax, ymin, ymax; ymax = zNear * tan(fovy * M_PI / 360.0); ymin = -ymax; xmin = ymin * aspect; xmax = ymax * aspect; xmin += -(2 * gl_state.camera_separation) / zNear; xmax += -(2 * gl_state.camera_separation) / zNear; glFrustum(xmin, xmax, ymin, ymax, zNear, zFar); } void R_SetupGL(void) { float screenaspect; int x, x2, y2, y, w, h; /* set up viewport */ x = floor(r_newrefdef.x * vid.width / vid.width); x2 = ceil((r_newrefdef.x + r_newrefdef.width) * vid.width / vid.width); y = floor(vid.height - r_newrefdef.y * vid.height / vid.height); y2 = ceil(vid.height - (r_newrefdef.y + r_newrefdef.height) * vid.height / vid.height); w = x2 - x; h = y - y2; glViewport(x, y2, w, h); /* set up projection matrix */ screenaspect = (float)r_newrefdef.width / r_newrefdef.height; glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (gl_farsee->value == 0) { R_MYgluPerspective(r_newrefdef.fov_y, screenaspect, 4, 4096); } else { R_MYgluPerspective(r_newrefdef.fov_y, screenaspect, 4, 8192); } glCullFace(GL_FRONT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glRotatef(-90, 1, 0, 0); /* put Z going up */ glRotatef(90, 0, 0, 1); /* put Z going up */ glRotatef(-r_newrefdef.viewangles[2], 1, 0, 0); glRotatef(-r_newrefdef.viewangles[0], 0, 1, 0); glRotatef(-r_newrefdef.viewangles[1], 0, 0, 1); glTranslatef(-r_newrefdef.vieworg[0], -r_newrefdef.vieworg[1], -r_newrefdef.vieworg[2]); glGetFloatv(GL_MODELVIEW_MATRIX, r_world_matrix); /* set drawing parms */ if (gl_cull->value) { glEnable(GL_CULL_FACE); } else { glDisable(GL_CULL_FACE); } glDisable(GL_BLEND); glDisable(GL_ALPHA_TEST); glEnable(GL_DEPTH_TEST); } void R_Clear(void) { if (gl_ztrick->value) { static int trickframe; if (gl_clear->value) { glClear(GL_COLOR_BUFFER_BIT); } trickframe++; if (trickframe & 1) { gldepthmin = 0; gldepthmax = 0.49999; glDepthFunc(GL_LEQUAL); } else { gldepthmin = 1; gldepthmax = 0.5; glDepthFunc(GL_GEQUAL); } } else { if (gl_clear->value) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } else { glClear(GL_DEPTH_BUFFER_BIT); } gldepthmin = 0; gldepthmax = 1; glDepthFunc(GL_LEQUAL); } glDepthRange(gldepthmin, gldepthmax); if (gl_zfix->value) { if (gldepthmax > gldepthmin) { glPolygonOffset(0.05, 1); } else { glPolygonOffset(-0.05, -1); } } /* stencilbuffer shadows */ if (gl_shadows->value && have_stencil && gl_stencilshadow->value) { glClearStencil(1); glClear(GL_STENCIL_BUFFER_BIT); } } void R_Flash(void) { R_PolyBlend(); } /* * r_newrefdef must be set before the first call */ void R_RenderView(refdef_t *fd) { if (gl_norefresh->value) { return; } r_newrefdef = *fd; if (!r_worldmodel && !(r_newrefdef.rdflags & RDF_NOWORLDMODEL)) { VID_Error(ERR_DROP, "R_RenderView: NULL worldmodel"); } if (gl_speeds->value) { c_brush_polys = 0; c_alias_polys = 0; } R_PushDlights(); if (gl_finish->value) { glFinish(); } R_SetupFrame(); R_SetFrustum(); R_SetupGL(); R_MarkLeaves(); /* done here so we know if we're in water */ R_DrawWorld(); R_DrawEntitiesOnList(); R_RenderDlights(); R_DrawParticles(); R_DrawAlphaSurfaces(); R_Flash(); if (gl_speeds->value) { VID_Printf(PRINT_ALL, "%4i wpoly %4i epoly %i tex %i lmaps\n", c_brush_polys, c_alias_polys, c_visible_textures, c_visible_lightmaps); } } void R_SetGL2D(void) { /* set 2D virtual screen size */ glViewport(0, 0, vid.width, vid.height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, vid.width, vid.height, 0, -99999, 99999); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glDisable(GL_BLEND); glEnable(GL_ALPHA_TEST); glColor4f(1, 1, 1, 1); } void R_SetLightLevel(void) { vec3_t shadelight; if (r_newrefdef.rdflags & RDF_NOWORLDMODEL) { return; } /* save off light value for server to look at */ R_LightPoint(r_newrefdef.vieworg, shadelight); /* pick the greatest component, which should be the * same as the mono value returned by software */ if (shadelight[0] > shadelight[1]) { if (shadelight[0] > shadelight[2]) { gl_lightlevel->value = 150 * shadelight[0]; } else { gl_lightlevel->value = 150 * shadelight[2]; } } else { if (shadelight[1] > shadelight[2]) { gl_lightlevel->value = 150 * shadelight[1]; } else { gl_lightlevel->value = 150 * shadelight[2]; } } } void R_RenderFrame(refdef_t *fd) { R_RenderView(fd); R_SetLightLevel(); R_SetGL2D(); } void R_Register(void) { gl_lefthand = Cvar_Get("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE); gl_farsee = Cvar_Get("gl_farsee", "0", CVAR_LATCH | CVAR_ARCHIVE); gl_norefresh = Cvar_Get("gl_norefresh", "0", 0); gl_fullbright = Cvar_Get("gl_fullbright", "0", 0); gl_drawentities = Cvar_Get("gl_drawentities", "1", 0); gl_drawworld = Cvar_Get("gl_drawworld", "1", 0); gl_novis = Cvar_Get("gl_novis", "0", 0); gl_nocull = Cvar_Get("gl_nocull", "0", 0); gl_lerpmodels = Cvar_Get("gl_lerpmodels", "1", 0); gl_speeds = Cvar_Get("gl_speeds", "0", 0); gl_lightlevel = Cvar_Get("gl_lightlevel", "0", 0); gl_overbrightbits = Cvar_Get("gl_overbrightbits", "2", CVAR_ARCHIVE); gl_nosubimage = Cvar_Get("gl_nosubimage", "0", 0); gl_allow_software = Cvar_Get("gl_allow_software", "0", 0); gl_particle_min_size = Cvar_Get("gl_particle_min_size", "2", CVAR_ARCHIVE); gl_particle_max_size = Cvar_Get("gl_particle_max_size", "40", CVAR_ARCHIVE); gl_particle_size = Cvar_Get("gl_particle_size", "40", CVAR_ARCHIVE); gl_particle_att_a = Cvar_Get("gl_particle_att_a", "0.01", CVAR_ARCHIVE); gl_particle_att_b = Cvar_Get("gl_particle_att_b", "0.0", CVAR_ARCHIVE); gl_particle_att_c = Cvar_Get("gl_particle_att_c", "0.01", CVAR_ARCHIVE); gl_modulate = Cvar_Get("gl_modulate", "1", CVAR_ARCHIVE); gl_bitdepth = Cvar_Get("gl_bitdepth", "0", 0); gl_mode = Cvar_Get("gl_mode", "4", CVAR_ARCHIVE); gl_lightmap = Cvar_Get("gl_lightmap", "0", 0); gl_shadows = Cvar_Get("gl_shadows", "0", CVAR_ARCHIVE); gl_stencilshadow = Cvar_Get("gl_stencilshadow", "0", CVAR_ARCHIVE); gl_dynamic = Cvar_Get("gl_dynamic", "1", 0); gl_nobind = Cvar_Get("gl_nobind", "0", 0); gl_round_down = Cvar_Get("gl_round_down", "1", 0); gl_picmip = Cvar_Get("gl_picmip", "0", 0); gl_skymip = Cvar_Get("gl_skymip", "0", 0); gl_showtris = Cvar_Get("gl_showtris", "0", 0); gl_ztrick = Cvar_Get("gl_ztrick", "0", 0); gl_zfix = Cvar_Get("gl_zfix", "0", 0); gl_finish = Cvar_Get("gl_finish", "0", CVAR_ARCHIVE); gl_clear = Cvar_Get("gl_clear", "0", 0); gl_cull = Cvar_Get("gl_cull", "1", 0); gl_polyblend = Cvar_Get("gl_polyblend", "1", 0); gl_flashblend = Cvar_Get("gl_flashblend", "0", 0); gl_playermip = Cvar_Get("gl_playermip", "0", 0); gl_texturemode = Cvar_Get("gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE); gl_texturealphamode = Cvar_Get("gl_texturealphamode", "default", CVAR_ARCHIVE); gl_texturesolidmode = Cvar_Get("gl_texturesolidmode", "default", CVAR_ARCHIVE); gl_anisotropic = Cvar_Get("gl_anisotropic", "0", CVAR_ARCHIVE); gl_anisotropic_avail = Cvar_Get("gl_anisotropic_avail", "0", 0); gl_lockpvs = Cvar_Get("gl_lockpvs", "0", 0); gl_vertex_arrays = Cvar_Get("gl_vertex_arrays", "0", CVAR_ARCHIVE); gl_ext_swapinterval = Cvar_Get("gl_ext_swapinterval", "1", CVAR_ARCHIVE); gl_ext_palettedtexture = Cvar_Get("gl_ext_palettedtexture", "0", CVAR_ARCHIVE); gl_ext_multitexture = Cvar_Get("gl_ext_multitexture", "1", CVAR_ARCHIVE); gl_ext_pointparameters = Cvar_Get("gl_ext_pointparameters", "1", CVAR_ARCHIVE); gl_ext_compiled_vertex_array = Cvar_Get("gl_ext_compiled_vertex_array", "1", CVAR_ARCHIVE); gl_ext_mtexcombine = Cvar_Get("gl_ext_mtexcombine", "1", CVAR_ARCHIVE); gl_drawbuffer = Cvar_Get("gl_drawbuffer", "GL_BACK", 0); gl_swapinterval = Cvar_Get("gl_swapinterval", "1", CVAR_ARCHIVE); gl_saturatelighting = Cvar_Get("gl_saturatelighting", "0", 0); vid_fullscreen = Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); vid_gamma = Cvar_Get("vid_gamma", "1.0", CVAR_ARCHIVE); gl_customwidth = Cvar_Get("gl_customwidth", "1024", CVAR_ARCHIVE); gl_customheight = Cvar_Get("gl_customheight", "768", CVAR_ARCHIVE); gl_msaa_samples = Cvar_Get ( "gl_msaa_samples", "0", CVAR_ARCHIVE ); gl_retexturing = Cvar_Get("gl_retexturing", "1", CVAR_ARCHIVE); Cmd_AddCommand("imagelist", R_ImageList_f); Cmd_AddCommand("screenshot", R_ScreenShot); Cmd_AddCommand("modellist", Mod_Modellist_f); Cmd_AddCommand("gl_strings", R_Strings); } qboolean R_SetMode(void) { rserr_t err; qboolean fullscreen; fullscreen = vid_fullscreen->value; vid_fullscreen->modified = false; gl_mode->modified = false; /* a bit hackish approach to enable custom resolutions: Glimp_SetMode needs these values set for mode -1 */ vid.width = gl_customwidth->value; vid.height = gl_customheight->value; if ((err = GLimp_SetMode(&vid.width, &vid.height, gl_mode->value, fullscreen)) == rserr_ok) { if (gl_mode->value == -1) { gl_state.prev_mode = 4; /* safe default for custom mode */ } else { gl_state.prev_mode = gl_mode->value; } } else { if (err == rserr_invalid_fullscreen) { Cvar_SetValue("vid_fullscreen", 0); vid_fullscreen->modified = false; VID_Printf(PRINT_ALL, "ref_gl::R_SetMode() - fullscreen unavailable in this mode\n"); if ((err = GLimp_SetMode(&vid.width, &vid.height, gl_mode->value, false)) == rserr_ok) { return true; } } else if (err == rserr_invalid_mode) { Cvar_SetValue("gl_mode", gl_state.prev_mode); gl_mode->modified = false; VID_Printf(PRINT_ALL, "ref_gl::R_SetMode() - invalid mode\n"); } /* try setting it back to something safe */ if ((err = GLimp_SetMode(&vid.width, &vid.height, gl_state.prev_mode, false)) != rserr_ok) { VID_Printf(PRINT_ALL, "ref_gl::R_SetMode() - could not revert to safe mode\n"); return false; } } return true; } int R_Init(void *hinstance, void *hWnd) { char renderer_buffer[1000]; char vendor_buffer[1000]; int err; int j; extern float r_turbsin[256]; Swap_Init(); for (j = 0; j < 256; j++) { r_turbsin[j] *= 0.5; } /* Options */ VID_Printf(PRINT_ALL, "Refresher build options:\n"); VID_Printf(PRINT_ALL, " + Retexturing support\n"); #ifdef X11GAMMA VID_Printf(PRINT_ALL, " + Gamma via X11\n"); #else VID_Printf(PRINT_ALL, " - Gamma via X11\n"); #endif VID_Printf(PRINT_ALL, "Refresh: " REF_VERSION "\n"); Draw_GetPalette(); R_Register(); /* initialize our QGL dynamic bindings */ QGL_Init(); /* initialize OS-specific parts of OpenGL */ if (!GLimp_Init()) { QGL_Shutdown(); return -1; } /* set our "safe" mode */ gl_state.prev_mode = 4; /* create the window and set up the context */ if (!R_SetMode()) { QGL_Shutdown(); VID_Printf(PRINT_ALL, "ref_gl::R_Init() - could not R_SetMode()\n"); return -1; } VID_MenuInit(); /* get our various GL strings */ VID_Printf(PRINT_ALL, "\nOpenGL setting:\n", gl_config.vendor_string); gl_config.vendor_string = (char *)glGetString(GL_VENDOR); VID_Printf(PRINT_ALL, "GL_VENDOR: %s\n", gl_config.vendor_string); gl_config.renderer_string = (char *)glGetString(GL_RENDERER); VID_Printf(PRINT_ALL, "GL_RENDERER: %s\n", gl_config.renderer_string); gl_config.version_string = (char *)glGetString(GL_VERSION); VID_Printf(PRINT_ALL, "GL_VERSION: %s\n", gl_config.version_string); gl_config.extensions_string = (char *)glGetString(GL_EXTENSIONS); VID_Printf(PRINT_ALL, "GL_EXTENSIONS: %s\n", gl_config.extensions_string); Q_strlcpy(renderer_buffer, gl_config.renderer_string, sizeof(renderer_buffer)); Q_strlwr(renderer_buffer); Q_strlcpy(vendor_buffer, gl_config.vendor_string, sizeof(vendor_buffer)); Q_strlwr(vendor_buffer); Cvar_Set("scr_drawall", "0"); gl_config.allow_cds = true; VID_Printf(PRINT_ALL, "\n\nProbing for OpenGL extensions:\n"); /* grab extensions */ if (strstr(gl_config.extensions_string, "GL_EXT_compiled_vertex_array")) { VID_Printf(PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n"); qglLockArraysEXT = ( void * ) GLimp_GetProcAddress ( "glLockArraysEXT" ); qglUnlockArraysEXT = ( void * ) GLimp_GetProcAddress ( "glUnlockArraysEXT" ); } else { VID_Printf(PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n"); } if (strstr(gl_config.extensions_string, "GL_EXT_point_parameters")) { if (gl_ext_pointparameters->value) { VID_Printf(PRINT_ALL, "...using GL_EXT_point_parameters\n"); qglPointParameterfEXT = (void (APIENTRY *)(GLenum, GLfloat)) GLimp_GetProcAddress ( "glPointParameterfEXT" ); qglPointParameterfvEXT = (void (APIENTRY *)(GLenum, const GLfloat *)) GLimp_GetProcAddress ( "glPointParameterfvEXT" ); } else { VID_Printf(PRINT_ALL, "...ignoring GL_EXT_point_parameters\n"); } } else { VID_Printf(PRINT_ALL, "...GL_EXT_point_parameters not found\n"); } if (!qglColorTableEXT && strstr(gl_config.extensions_string, "GL_EXT_paletted_texture") && strstr(gl_config.extensions_string, "GL_EXT_shared_texture_palette")) { if (gl_ext_palettedtexture->value) { VID_Printf(PRINT_ALL, "...using GL_EXT_shared_texture_palette\n"); qglColorTableEXT = (void (APIENTRY *)(GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid * ) ) GLimp_GetProcAddress ("glColorTableEXT"); } else { VID_Printf(PRINT_ALL, "...ignoring GL_EXT_shared_texture_palette\n"); } } else { VID_Printf(PRINT_ALL, "...GL_EXT_shared_texture_palette not found\n"); } if (strstr(gl_config.extensions_string, "GL_ARB_multitexture")) { if (gl_ext_multitexture->value) { VID_Printf(PRINT_ALL, "...using GL_ARB_multitexture\n"); qglMultiTexCoord2fARB = ( void * ) GLimp_GetProcAddress ( "glMultiTexCoord2fARB" ); qglActiveTextureARB = ( void * ) GLimp_GetProcAddress ( "glActiveTextureARB" ); qglClientActiveTextureARB = ( void * ) GLimp_GetProcAddress ( "glClientActiveTextureARB" ); } else { VID_Printf(PRINT_ALL, "...ignoring GL_ARB_multitexture\n"); } } else { VID_Printf(PRINT_ALL, "...GL_ARB_multitexture not found\n"); } gl_config.anisotropic = false; if (strstr(gl_config.extensions_string, "GL_EXT_texture_filter_anisotropic")) { VID_Printf(PRINT_ALL, "...using GL_EXT_texture_filter_anisotropic\n"); gl_config.anisotropic = true; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_config.max_anisotropy); Cvar_SetValue("gl_anisotropic_avail", gl_config.max_anisotropy); } else { VID_Printf(PRINT_ALL, "...GL_EXT_texture_filter_anisotropic not found\n"); gl_config.anisotropic = false; gl_config.max_anisotropy = 0.0; Cvar_SetValue("gl_anisotropic_avail", 0.0); } if (strstr(gl_config.extensions_string, "GL_ARB_texture_non_power_of_two")) { VID_Printf(PRINT_ALL, "...using GL_ARB_texture_non_power_of_two\n"); gl_config.tex_npot = true; } gl_config.mtexcombine = false; if (strstr(gl_config.extensions_string, "GL_ARB_texture_env_combine")) { if (gl_ext_mtexcombine->value) { VID_Printf(PRINT_ALL, "...using GL_ARB_texture_env_combine\n"); gl_config.mtexcombine = true; } else { VID_Printf(PRINT_ALL, "...ignoring GL_ARB_texture_env_combine\n"); } } else { VID_Printf(PRINT_ALL, "...GL_ARB_texture_env_combine not found\n"); } if (!gl_config.mtexcombine) { if (strstr(gl_config.extensions_string, "GL_EXT_texture_env_combine")) { if (gl_ext_mtexcombine->value) { VID_Printf(PRINT_ALL, "...using GL_EXT_texture_env_combine\n"); gl_config.mtexcombine = true; } else { VID_Printf(PRINT_ALL, "...ignoring GL_EXT_texture_env_combine\n"); } } else { VID_Printf(PRINT_ALL, "...GL_EXT_texture_env_combine not found\n"); } } R_SetDefaultState(); R_InitImages(); Mod_Init(); R_InitParticleTexture(); Draw_InitLocal(); err = glGetError(); if (err != GL_NO_ERROR) { VID_Printf(PRINT_ALL, "glGetError() = 0x%x\n", err); } return true; } void R_Shutdown(void) { Cmd_RemoveCommand("modellist"); Cmd_RemoveCommand("screenshot"); Cmd_RemoveCommand("imagelist"); Cmd_RemoveCommand("gl_strings"); Mod_FreeAll(); R_ShutdownImages(); /* shutdown OS specific OpenGL stuff like contexts, etc. */ GLimp_Shutdown(); /* shutdown our QGL subsystem */ QGL_Shutdown(); } extern void UpdateHardwareGamma(); void R_BeginFrame(float camera_separation) { gl_state.camera_separation = camera_separation; /* change modes if necessary */ if (gl_mode->modified) { vid_fullscreen->modified = true; } if (vid_gamma->modified) { vid_gamma->modified = false; if (gl_state.hwgamma) { UpdateHardwareGamma(); } } /* go into 2D mode */ glViewport(0, 0, vid.width, vid.height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, vid.width, vid.height, 0, -99999, 99999); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glDisable(GL_BLEND); glEnable(GL_ALPHA_TEST); glColor4f(1, 1, 1, 1); /* draw buffer stuff */ if (gl_drawbuffer->modified) { gl_drawbuffer->modified = false; if ((gl_state.camera_separation == 0) || !gl_state.stereo_enabled) { if (Q_stricmp(gl_drawbuffer->string, "GL_FRONT") == 0) { glDrawBuffer(GL_FRONT); } else { glDrawBuffer(GL_BACK); } } } /* texturemode stuff */ if (gl_texturemode->modified || (gl_config.anisotropic && gl_anisotropic->modified)) { R_TextureMode(gl_texturemode->string); gl_texturemode->modified = false; gl_anisotropic->modified = false; } if (gl_texturealphamode->modified) { R_TextureAlphaMode(gl_texturealphamode->string); gl_texturealphamode->modified = false; } if (gl_texturesolidmode->modified) { R_TextureSolidMode(gl_texturesolidmode->string); gl_texturesolidmode->modified = false; } /* clear screen if desired */ R_Clear(); } void R_SetPalette(const unsigned char *palette) { int i; byte *rp = (byte *)r_rawpalette; if (palette) { for (i = 0; i < 256; i++) { rp[i * 4 + 0] = palette[i * 3 + 0]; rp[i * 4 + 1] = palette[i * 3 + 1]; rp[i * 4 + 2] = palette[i * 3 + 2]; rp[i * 4 + 3] = 0xff; } } else { for (i = 0; i < 256; i++) { rp[i * 4 + 0] = LittleLong(d_8to24table[i]) & 0xff; rp[i * 4 + 1] = (LittleLong(d_8to24table[i]) >> 8) & 0xff; rp[i * 4 + 2] = (LittleLong(d_8to24table[i]) >> 16) & 0xff; rp[i * 4 + 3] = 0xff; } } R_SetTexturePalette(r_rawpalette); glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT); glClearColor(1, 0, 0.5, 0.5); } /* R_DrawBeam */ void R_DrawBeam(entity_t *e) { int i; float r, g, b; vec3_t perpvec; vec3_t direction, normalized_direction; vec3_t start_points[NUM_BEAM_SEGS], end_points[NUM_BEAM_SEGS]; vec3_t oldorigin, origin; oldorigin[0] = e->oldorigin[0]; oldorigin[1] = e->oldorigin[1]; oldorigin[2] = e->oldorigin[2]; origin[0] = e->origin[0]; origin[1] = e->origin[1]; origin[2] = e->origin[2]; normalized_direction[0] = direction[0] = oldorigin[0] - origin[0]; normalized_direction[1] = direction[1] = oldorigin[1] - origin[1]; normalized_direction[2] = direction[2] = oldorigin[2] - origin[2]; if (VectorNormalize(normalized_direction) == 0) { return; } PerpendicularVector(perpvec, normalized_direction); VectorScale(perpvec, e->frame / 2, perpvec); for (i = 0; i < 6; i++) { RotatePointAroundVector(start_points[i], normalized_direction, perpvec, (360.0 / NUM_BEAM_SEGS) * i); VectorAdd(start_points[i], origin, start_points[i]); VectorAdd(start_points[i], direction, end_points[i]); } glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glDepthMask(GL_FALSE); r = (LittleLong(d_8to24table[e->skinnum & 0xFF])) & 0xFF; g = (LittleLong(d_8to24table[e->skinnum & 0xFF]) >> 8) & 0xFF; b = (LittleLong(d_8to24table[e->skinnum & 0xFF]) >> 16) & 0xFF; r *= 1 / 255.0F; g *= 1 / 255.0F; b *= 1 / 255.0F; glColor4f(r, g, b, e->alpha); glBegin(GL_TRIANGLE_STRIP); for (i = 0; i < NUM_BEAM_SEGS; i++) { glVertex3fv(start_points[i]); glVertex3fv(end_points[i]); glVertex3fv(start_points[(i + 1) % NUM_BEAM_SEGS]); glVertex3fv(end_points[(i + 1) % NUM_BEAM_SEGS]); } glEnd(); glEnable(GL_TEXTURE_2D); glDisable(GL_BLEND); glDepthMask(GL_TRUE); } /*void R_GetRefAPI(void) { Swap_Init(); }*/ yquake2-QUAKE2_5_32/src/client/refresh/r_surf.c0000644000175000017500000006255612615162034022004 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Surface generation and drawing * * ======================================================================= */ #include #include "header/local.h" int c_visible_lightmaps; int c_visible_textures; static vec3_t modelorg; /* relative to viewpoint */ msurface_t *r_alpha_surfaces; gllightmapstate_t gl_lms; void LM_InitBlock(void); void LM_UploadBlock(qboolean dynamic); qboolean LM_AllocBlock(int w, int h, int *x, int *y); void R_SetCacheState(msurface_t *surf); void R_BuildLightMap(msurface_t *surf, byte *dest, int stride); /* * Returns the proper texture for a given time and base texture */ image_t * R_TextureAnimation(mtexinfo_t *tex) { int c; if (!tex->next) { return tex->image; } c = currententity->frame % tex->numframes; while (c) { tex = tex->next; c--; } return tex->image; } void R_DrawGLPoly(glpoly_t *p) { int i; float *v; glBegin(GL_POLYGON); v = p->verts[0]; for (i = 0; i < p->numverts; i++, v += VERTEXSIZE) { glTexCoord2f(v[3], v[4]); glVertex3fv(v); } glEnd(); } void R_DrawGLFlowingPoly(msurface_t *fa) { int i; float *v; glpoly_t *p; float scroll; p = fa->polys; scroll = -64 * ((r_newrefdef.time / 40.0) - (int)(r_newrefdef.time / 40.0)); if (scroll == 0.0) { scroll = -64.0; } glBegin(GL_POLYGON); v = p->verts[0]; for (i = 0; i < p->numverts; i++, v += VERTEXSIZE) { glTexCoord2f((v[3] + scroll), v[4]); glVertex3fv(v); } glEnd(); } void R_DrawTriangleOutlines(void) { int i, j; glpoly_t *p; if (!gl_showtris->value) { return; } glDisable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glColor4f(1, 1, 1, 1); for (i = 0; i < MAX_LIGHTMAPS; i++) { msurface_t *surf; for (surf = gl_lms.lightmap_surfaces[i]; surf != 0; surf = surf->lightmapchain) { p = surf->polys; for ( ; p; p = p->chain) { for (j = 2; j < p->numverts; j++) { glBegin(GL_LINE_STRIP); glVertex3fv(p->verts[0]); glVertex3fv(p->verts[j - 1]); glVertex3fv(p->verts[j]); glVertex3fv(p->verts[0]); glEnd(); } } } } glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); } void R_DrawGLPolyChain(glpoly_t *p, float soffset, float toffset) { if ((soffset == 0) && (toffset == 0)) { for ( ; p != 0; p = p->chain) { float *v; int j; v = p->verts[0]; if (v == NULL) { fprintf(stderr, "BUGFIX: R_DrawGLPolyChain: v==NULL\n"); return; } glBegin(GL_POLYGON); for (j = 0; j < p->numverts; j++, v += VERTEXSIZE) { glTexCoord2f(v[5], v[6]); glVertex3fv(v); } glEnd(); } } else { for ( ; p != 0; p = p->chain) { float *v; int j; glBegin(GL_POLYGON); v = p->verts[0]; for (j = 0; j < p->numverts; j++, v += VERTEXSIZE) { glTexCoord2f(v[5] - soffset, v[6] - toffset); glVertex3fv(v); } glEnd(); } } } /* * This routine takes all the given light mapped surfaces * in the world and blends them into the framebuffer. */ void R_BlendLightmaps(void) { int i; msurface_t *surf, *newdrawsurf = 0; /* don't bother if we're set to fullbright */ if (gl_fullbright->value) { return; } if (!r_worldmodel->lightdata) { return; } /* don't bother writing Z */ glDepthMask(0); /* set the appropriate blending mode unless we're only looking at the lightmaps. */ if (!gl_lightmap->value) { glEnable(GL_BLEND); if (gl_saturatelighting->value) { glBlendFunc(GL_ONE, GL_ONE); } else { glBlendFunc(GL_ZERO, GL_SRC_COLOR); } } if (currentmodel == r_worldmodel) { c_visible_lightmaps = 0; } /* render static lightmaps first */ for (i = 1; i < MAX_LIGHTMAPS; i++) { if (gl_lms.lightmap_surfaces[i]) { if (currentmodel == r_worldmodel) { c_visible_lightmaps++; } R_Bind(gl_state.lightmap_textures + i); for (surf = gl_lms.lightmap_surfaces[i]; surf != 0; surf = surf->lightmapchain) { if (surf->polys) { R_DrawGLPolyChain(surf->polys, 0, 0); } } } } /* render dynamic lightmaps */ if (gl_dynamic->value) { LM_InitBlock(); R_Bind(gl_state.lightmap_textures + 0); if (currentmodel == r_worldmodel) { c_visible_lightmaps++; } newdrawsurf = gl_lms.lightmap_surfaces[0]; for (surf = gl_lms.lightmap_surfaces[0]; surf != 0; surf = surf->lightmapchain) { int smax, tmax; byte *base; smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; if (LM_AllocBlock(smax, tmax, &surf->dlight_s, &surf->dlight_t)) { base = gl_lms.lightmap_buffer; base += (surf->dlight_t * BLOCK_WIDTH + surf->dlight_s) * LIGHTMAP_BYTES; R_BuildLightMap(surf, base, BLOCK_WIDTH * LIGHTMAP_BYTES); } else { msurface_t *drawsurf; /* upload what we have so far */ LM_UploadBlock(true); /* draw all surfaces that use this lightmap */ for (drawsurf = newdrawsurf; drawsurf != surf; drawsurf = drawsurf->lightmapchain) { if (drawsurf->polys) { R_DrawGLPolyChain(drawsurf->polys, (drawsurf->light_s - drawsurf->dlight_s) * (1.0 / 128.0), (drawsurf->light_t - drawsurf->dlight_t) * (1.0 / 128.0)); } } newdrawsurf = drawsurf; /* clear the block */ LM_InitBlock(); /* try uploading the block now */ if (!LM_AllocBlock(smax, tmax, &surf->dlight_s, &surf->dlight_t)) { VID_Error(ERR_FATAL, "Consecutive calls to LM_AllocBlock(%d,%d) failed (dynamic)\n", smax, tmax); } base = gl_lms.lightmap_buffer; base += (surf->dlight_t * BLOCK_WIDTH + surf->dlight_s) * LIGHTMAP_BYTES; R_BuildLightMap(surf, base, BLOCK_WIDTH * LIGHTMAP_BYTES); } } /* draw remainder of dynamic lightmaps that haven't been uploaded yet */ if (newdrawsurf) { LM_UploadBlock(true); } for (surf = newdrawsurf; surf != 0; surf = surf->lightmapchain) { if (surf->polys) { R_DrawGLPolyChain(surf->polys, (surf->light_s - surf->dlight_s) * (1.0 / 128.0), (surf->light_t - surf->dlight_t) * (1.0 / 128.0)); } } } /* restore state */ glDisable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(1); } void R_RenderBrushPoly(msurface_t *fa) { int maps; image_t *image; qboolean is_dynamic = false; c_brush_polys++; image = R_TextureAnimation(fa->texinfo); if (fa->flags & SURF_DRAWTURB) { R_Bind(image->texnum); /* warp texture, no lightmaps */ R_TexEnv(GL_MODULATE); glColor4f(gl_state.inverse_intensity, gl_state.inverse_intensity, gl_state.inverse_intensity, 1.0F); R_EmitWaterPolys(fa); R_TexEnv(GL_REPLACE); return; } else { R_Bind(image->texnum); R_TexEnv(GL_REPLACE); } if (fa->texinfo->flags & SURF_FLOWING) { R_DrawGLFlowingPoly(fa); } else { R_DrawGLPoly(fa->polys); } /* check for lightmap modification */ for (maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++) { if (r_newrefdef.lightstyles[fa->styles[maps]].white != fa->cached_light[maps]) { goto dynamic; } } /* dynamic this frame or dynamic previously */ if (fa->dlightframe == r_framecount) { dynamic: if (gl_dynamic->value) { if (!(fa->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP))) { is_dynamic = true; } } } if (is_dynamic) { if (((fa->styles[maps] >= 32) || (fa->styles[maps] == 0)) && (fa->dlightframe != r_framecount)) { unsigned temp[34 * 34]; int smax, tmax; smax = (fa->extents[0] >> 4) + 1; tmax = (fa->extents[1] >> 4) + 1; R_BuildLightMap(fa, (void *)temp, smax * 4); R_SetCacheState(fa); R_Bind(gl_state.lightmap_textures + fa->lightmaptexturenum); glTexSubImage2D(GL_TEXTURE_2D, 0, fa->light_s, fa->light_t, smax, tmax, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, temp); fa->lightmapchain = gl_lms.lightmap_surfaces[fa->lightmaptexturenum]; gl_lms.lightmap_surfaces[fa->lightmaptexturenum] = fa; } else { fa->lightmapchain = gl_lms.lightmap_surfaces[0]; gl_lms.lightmap_surfaces[0] = fa; } } else { fa->lightmapchain = gl_lms.lightmap_surfaces[fa->lightmaptexturenum]; gl_lms.lightmap_surfaces[fa->lightmaptexturenum] = fa; } } /* * Draw water surfaces and windows. * The BSP tree is waled front to back, so unwinding the chain * of alpha_surfaces will draw back to front, giving proper ordering. */ void R_DrawAlphaSurfaces(void) { msurface_t *s; float intens; /* go back to the world matrix */ glLoadMatrixf(r_world_matrix); glEnable(GL_BLEND); R_TexEnv(GL_MODULATE); /* the textures are prescaled up for a better lighting range, so scale it back down */ intens = gl_state.inverse_intensity; for (s = r_alpha_surfaces; s; s = s->texturechain) { R_Bind(s->texinfo->image->texnum); c_brush_polys++; if (s->texinfo->flags & SURF_TRANS33) { glColor4f(intens, intens, intens, 0.33); } else if (s->texinfo->flags & SURF_TRANS66) { glColor4f(intens, intens, intens, 0.66); } else { glColor4f(intens, intens, intens, 1); } if (s->flags & SURF_DRAWTURB) { R_EmitWaterPolys(s); } else if (s->texinfo->flags & SURF_FLOWING) { R_DrawGLFlowingPoly(s); } else { R_DrawGLPoly(s->polys); } } R_TexEnv(GL_REPLACE); glColor4f(1, 1, 1, 1); glDisable(GL_BLEND); r_alpha_surfaces = NULL; } void R_DrawTextureChains(void) { int i; msurface_t *s; image_t *image; c_visible_textures = 0; if (!qglActiveTextureARB) { for (i = 0, image = gltextures; i < numgltextures; i++, image++) { if (!image->registration_sequence) { continue; } s = image->texturechain; if (!s) { continue; } c_visible_textures++; for ( ; s; s = s->texturechain) { R_RenderBrushPoly(s); } image->texturechain = NULL; } } else { for (i = 0, image = gltextures; i < numgltextures; i++, image++) { if (!image->registration_sequence) { continue; } if (!image->texturechain) { continue; } c_visible_textures++; for (s = image->texturechain; s; s = s->texturechain) { if (!(s->flags & SURF_DRAWTURB)) { R_RenderBrushPoly(s); } } } R_EnableMultitexture(false); for (i = 0, image = gltextures; i < numgltextures; i++, image++) { if (!image->registration_sequence) { continue; } s = image->texturechain; if (!s) { continue; } for ( ; s; s = s->texturechain) { if (s->flags & SURF_DRAWTURB) { R_RenderBrushPoly(s); } } image->texturechain = NULL; } } R_TexEnv(GL_REPLACE); } static void R_RenderLightmappedPoly(msurface_t *surf) { int i, nv = surf->polys->numverts; int map; float *v; image_t *image = R_TextureAnimation(surf->texinfo); qboolean is_dynamic = false; unsigned lmtex = surf->lightmaptexturenum; glpoly_t *p; for (map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++) { if (r_newrefdef.lightstyles[surf->styles[map]].white != surf->cached_light[map]) { goto dynamic; } } if (surf->dlightframe == r_framecount) { dynamic: if (gl_dynamic->value) { if (!(surf->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP))) { is_dynamic = true; } } } if (is_dynamic) { unsigned temp[128 * 128]; int smax, tmax; if (((surf->styles[map] >= 32) || (surf->styles[map] == 0)) && (surf->dlightframe != r_framecount)) { smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; R_BuildLightMap(surf, (void *)temp, smax * 4); R_SetCacheState(surf); R_MBind(GL_TEXTURE1_ARB, gl_state.lightmap_textures + surf->lightmaptexturenum); lmtex = surf->lightmaptexturenum; glTexSubImage2D(GL_TEXTURE_2D, 0, surf->light_s, surf->light_t, smax, tmax, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, temp); } else { smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; R_BuildLightMap(surf, (void *)temp, smax * 4); R_MBind(GL_TEXTURE1_ARB, gl_state.lightmap_textures + 0); lmtex = 0; glTexSubImage2D(GL_TEXTURE_2D, 0, surf->light_s, surf->light_t, smax, tmax, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, temp); } c_brush_polys++; R_MBind(GL_TEXTURE0_ARB, image->texnum); R_MBind(GL_TEXTURE1_ARB, gl_state.lightmap_textures + lmtex); if (surf->texinfo->flags & SURF_FLOWING) { float scroll; scroll = -64 * ((r_newrefdef.time / 40.0) - (int)(r_newrefdef.time / 40.0)); if (scroll == 0.0) { scroll = -64.0; } for (p = surf->polys; p; p = p->chain) { v = p->verts[0]; glBegin(GL_POLYGON); for (i = 0; i < nv; i++, v += VERTEXSIZE) { qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, (v[3] + scroll), v[4]); qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, v[5], v[6]); glVertex3fv(v); } glEnd(); } } else { for (p = surf->polys; p; p = p->chain) { v = p->verts[0]; glBegin(GL_POLYGON); for (i = 0; i < nv; i++, v += VERTEXSIZE) { qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]); qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, v[5], v[6]); glVertex3fv(v); } glEnd(); } } } else { c_brush_polys++; R_MBind(GL_TEXTURE0_ARB, image->texnum); R_MBind(GL_TEXTURE1_ARB, gl_state.lightmap_textures + lmtex); if (surf->texinfo->flags & SURF_FLOWING) { float scroll; scroll = -64 * ((r_newrefdef.time / 40.0) - (int)(r_newrefdef.time / 40.0)); if (scroll == 0.0) { scroll = -64.0; } for (p = surf->polys; p; p = p->chain) { v = p->verts[0]; glBegin(GL_POLYGON); for (i = 0; i < nv; i++, v += VERTEXSIZE) { qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, (v[3] + scroll), v[4]); qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, v[5], v[6]); glVertex3fv(v); } glEnd(); } } else { for (p = surf->polys; p; p = p->chain) { v = p->verts[0]; glBegin(GL_POLYGON); for (i = 0; i < nv; i++, v += VERTEXSIZE) { qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]); qglMultiTexCoord2fARB(GL_TEXTURE1_ARB, v[5], v[6]); glVertex3fv(v); } glEnd(); } } } } void R_DrawInlineBModel(void) { int i, k; cplane_t *pplane; float dot; msurface_t *psurf; dlight_t *lt; /* calculate dynamic lighting for bmodel */ if (!gl_flashblend->value) { lt = r_newrefdef.dlights; for (k = 0; k < r_newrefdef.num_dlights; k++, lt++) { R_MarkLights(lt, 1 << k, currentmodel->nodes + currentmodel->firstnode); } } psurf = ¤tmodel->surfaces[currentmodel->firstmodelsurface]; if (currententity->flags & RF_TRANSLUCENT) { glEnable(GL_BLEND); glColor4f(1, 1, 1, 0.25); R_TexEnv(GL_MODULATE); } /* draw texture */ for (i = 0; i < currentmodel->nummodelsurfaces; 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))) { if (psurf->texinfo->flags & (SURF_TRANS33 | SURF_TRANS66)) { /* add to the translucent chain */ psurf->texturechain = r_alpha_surfaces; r_alpha_surfaces = psurf; } else if (qglMultiTexCoord2fARB && !(psurf->flags & SURF_DRAWTURB)) { R_RenderLightmappedPoly(psurf); } else { R_EnableMultitexture(false); R_RenderBrushPoly(psurf); R_EnableMultitexture(true); } } } if (!(currententity->flags & RF_TRANSLUCENT)) { if (!qglMultiTexCoord2fARB) { R_BlendLightmaps(); } } else { glDisable(GL_BLEND); glColor4f(1, 1, 1, 1); R_TexEnv(GL_REPLACE); } } void R_DrawBrushModel(entity_t *e) { vec3_t mins, maxs; int i; qboolean rotated; if (currentmodel->nummodelsurfaces == 0) { return; } currententity = e; gl_state.currenttextures[0] = gl_state.currenttextures[1] = -1; if (e->angles[0] || e->angles[1] || e->angles[2]) { rotated = true; for (i = 0; i < 3; i++) { mins[i] = e->origin[i] - currentmodel->radius; maxs[i] = e->origin[i] + currentmodel->radius; } } else { rotated = false; VectorAdd(e->origin, currentmodel->mins, mins); VectorAdd(e->origin, currentmodel->maxs, maxs); } if (R_CullBox(mins, maxs)) { return; } if (gl_zfix->value) { glEnable(GL_POLYGON_OFFSET_FILL); } glColor3f(1, 1, 1); memset(gl_lms.lightmap_surfaces, 0, sizeof(gl_lms.lightmap_surfaces)); VectorSubtract(r_newrefdef.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); } glPushMatrix(); e->angles[0] = -e->angles[0]; e->angles[2] = -e->angles[2]; R_RotateForEntity(e); e->angles[0] = -e->angles[0]; e->angles[2] = -e->angles[2]; R_EnableMultitexture(true); R_SelectTexture(GL_TEXTURE0_ARB); if (!gl_config.mtexcombine) { R_TexEnv(GL_REPLACE); R_SelectTexture(GL_TEXTURE1); if (gl_lightmap->value) { R_TexEnv(GL_REPLACE); } else { R_TexEnv(GL_MODULATE); } } else { R_TexEnv(GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE); R_SelectTexture(GL_TEXTURE1); R_TexEnv(GL_COMBINE_EXT); if (gl_lightmap->value) { glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE); } else { glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_PREVIOUS_EXT); } if (gl_overbrightbits->value) { glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl_overbrightbits->value); } } R_DrawInlineBModel(); R_EnableMultitexture(false); glPopMatrix(); if (gl_zfix->value) { glDisable(GL_POLYGON_OFFSET_FILL); } } void R_RecursiveWorldNode(mnode_t *node) { int c, side, sidebit; cplane_t *plane; msurface_t *surf, **mark; mleaf_t *pleaf; float dot; image_t *image; 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 != -1) { pleaf = (mleaf_t *)node; /* check for door connected areas */ if (r_newrefdef.areabits) { if (!(r_newrefdef.areabits[pleaf->area >> 3] & (1 << (pleaf->area & 7)))) { return; /* not visible */ } } mark = pleaf->firstmarksurface; c = pleaf->nummarksurfaces; if (c) { do { (*mark)->visframe = r_framecount; mark++; } while (--c); } 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; sidebit = 0; } else { side = 1; sidebit = SURF_PLANEBACK; } /* recurse down the children, front side first */ R_RecursiveWorldNode(node->children[side]); /* draw stuff */ for (c = node->numsurfaces, surf = r_worldmodel->surfaces + node->firstsurface; c; c--, surf++) { if (surf->visframe != r_framecount) { continue; } if ((surf->flags & SURF_PLANEBACK) != sidebit) { continue; /* wrong side */ } if (surf->texinfo->flags & SURF_SKY) { /* just adds to visible sky bounds */ R_AddSkySurface(surf); } else if (surf->texinfo->flags & (SURF_TRANS33 | SURF_TRANS66)) { /* add to the translucent chain */ surf->texturechain = r_alpha_surfaces; r_alpha_surfaces = surf; r_alpha_surfaces->texinfo->image = R_TextureAnimation(surf->texinfo); } else { if (qglMultiTexCoord2fARB && !(surf->flags & SURF_DRAWTURB)) { R_RenderLightmappedPoly(surf); } else { /* the polygon is visible, so add it to the texture sorted chain */ image = R_TextureAnimation(surf->texinfo); surf->texturechain = image->texturechain; image->texturechain = surf; } } } /* recurse down the back side */ R_RecursiveWorldNode(node->children[!side]); } void R_DrawWorld(void) { entity_t ent; if (!gl_drawworld->value) { return; } if (r_newrefdef.rdflags & RDF_NOWORLDMODEL) { return; } currentmodel = r_worldmodel; VectorCopy(r_newrefdef.vieworg, modelorg); /* auto cycle the world frame for texture animation */ memset(&ent, 0, sizeof(ent)); ent.frame = (int)(r_newrefdef.time * 2); currententity = &ent; gl_state.currenttextures[0] = gl_state.currenttextures[1] = -1; glColor3f(1, 1, 1); memset(gl_lms.lightmap_surfaces, 0, sizeof(gl_lms.lightmap_surfaces)); R_ClearSkyBox(); if (qglMultiTexCoord2fARB) { R_EnableMultitexture(true); R_SelectTexture(GL_TEXTURE0_ARB); if (!gl_config.mtexcombine) { R_TexEnv(GL_REPLACE); R_SelectTexture(GL_TEXTURE1); if (gl_lightmap->value) { R_TexEnv(GL_REPLACE); } else { R_TexEnv(GL_MODULATE); } } else { R_TexEnv(GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE); R_SelectTexture(GL_TEXTURE1); R_TexEnv(GL_COMBINE_EXT); if (gl_lightmap->value) { glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE); } else { glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PREVIOUS_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_PREVIOUS_EXT); } if (gl_overbrightbits->value) { glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl_overbrightbits->value); } } R_RecursiveWorldNode(r_worldmodel->nodes); R_EnableMultitexture(false); } else { R_RecursiveWorldNode(r_worldmodel->nodes); } R_DrawTextureChains(); R_BlendLightmaps(); R_DrawSkyBox(); R_DrawTriangleOutlines(); currententity = NULL; } /* * Mark the leaves and nodes that are * in the PVS for the current cluster */ void R_MarkLeaves(void) { byte *vis; byte fatvis[MAX_MAP_LEAFS / 8]; mnode_t *node; int i, c; mleaf_t *leaf; int cluster; if ((r_oldviewcluster == r_viewcluster) && (r_oldviewcluster2 == r_viewcluster2) && !gl_novis->value && (r_viewcluster != -1)) { return; } /* development aid to let you run around and see exactly where the pvs ends */ if (gl_lockpvs->value) { return; } r_visframecount++; r_oldviewcluster = r_viewcluster; r_oldviewcluster2 = r_viewcluster2; if (gl_novis->value || (r_viewcluster == -1) || !r_worldmodel->vis) { /* mark everything */ for (i = 0; i < r_worldmodel->numleafs; i++) { r_worldmodel->leafs[i].visframe = r_visframecount; } for (i = 0; i < r_worldmodel->numnodes; i++) { r_worldmodel->nodes[i].visframe = r_visframecount; } return; } vis = Mod_ClusterPVS(r_viewcluster, r_worldmodel); /* may have to combine two clusters because of solid water boundaries */ if (r_viewcluster2 != r_viewcluster) { memcpy(fatvis, vis, (r_worldmodel->numleafs + 7) / 8); vis = Mod_ClusterPVS(r_viewcluster2, r_worldmodel); c = (r_worldmodel->numleafs + 31) / 32; for (i = 0; i < c; i++) { ((int *)fatvis)[i] |= ((int *)vis)[i]; } vis = fatvis; } for (i = 0, leaf = r_worldmodel->leafs; i < r_worldmodel->numleafs; i++, leaf++) { cluster = leaf->cluster; if (cluster == -1) { continue; } if (vis[cluster >> 3] & (1 << (cluster & 7))) { node = (mnode_t *)leaf; do { if (node->visframe == r_visframecount) { break; } node->visframe = r_visframecount; node = node->parent; } while (node); } } } yquake2-QUAKE2_5_32/src/client/refresh/r_model.c0000644000175000017500000004754212615162034022123 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Model loading and caching. Includes the .bsp file format * * ======================================================================= */ #include "header/local.h" #define MAX_MOD_KNOWN 512 model_t *loadmodel; int modfilelen; byte mod_novis[MAX_MAP_LEAFS / 8]; model_t mod_known[MAX_MOD_KNOWN]; int mod_numknown; int registration_sequence; byte *mod_base; void LoadSP2(model_t *mod, void *buffer); void Mod_LoadBrushModel(model_t *mod, void *buffer); void LoadMD2(model_t *mod, void *buffer); model_t *Mod_LoadModel(model_t *mod, qboolean crash); void LM_BuildPolygonFromSurface(msurface_t *fa); void LM_CreateSurfaceLightmap(msurface_t *surf); void LM_EndBuildingLightmaps(void); void LM_BeginBuildingLightmaps(model_t *m); /* the inline * models from the current map are kept seperate */ model_t mod_inline[MAX_MOD_KNOWN]; mleaf_t * Mod_PointInLeaf(vec3_t p, model_t *model) { mnode_t *node; float d; cplane_t *plane; if (!model || !model->nodes) { VID_Error(ERR_DROP, "Mod_PointInLeaf: bad model"); } node = model->nodes; while (1) { if (node->contents != -1) { 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 */ } byte * Mod_DecompressVis(byte *in, model_t *model) { static byte decompressed[MAX_MAP_LEAFS / 8]; int c; byte *out; int row; row = (model->vis->numclusters + 7) >> 3; out = decompressed; if (!in) { /* no vis info, so make all visible */ while (row) { *out++ = 0xff; row--; } return decompressed; } do { if (*in) { *out++ = *in++; continue; } c = in[1]; in += 2; while (c) { *out++ = 0; c--; } } while (out - decompressed < row); return decompressed; } byte * Mod_ClusterPVS(int cluster, model_t *model) { if ((cluster == -1) || !model->vis) { return mod_novis; } return Mod_DecompressVis((byte *)model->vis + model->vis->bitofs[cluster][DVIS_PVS], model); } void Mod_Modellist_f(void) { int i; model_t *mod; int total; total = 0; VID_Printf(PRINT_ALL, "Loaded models:\n"); for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { if (!mod->name[0]) { continue; } VID_Printf(PRINT_ALL, "%8i : %s\n", mod->extradatasize, mod->name); total += mod->extradatasize; } VID_Printf(PRINT_ALL, "Total resident: %i\n", total); } void Mod_Init(void) { memset(mod_novis, 0xff, sizeof(mod_novis)); } /* * Loads in a model for the given name */ model_t * Mod_ForName(char *name, qboolean crash) { model_t *mod; unsigned *buf; int i; if (!name[0]) { VID_Error(ERR_DROP, "Mod_ForName: NULL name"); } /* inline models are grabbed only from worldmodel */ if (name[0] == '*') { i = (int)strtol(name + 1, (char **)NULL, 10); if ((i < 1) || !r_worldmodel || (i >= r_worldmodel->numsubmodels)) { VID_Error(ERR_DROP, "bad inline model number"); } return &mod_inline[i]; } /* search the currently loaded models */ for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { if (!mod->name[0]) { continue; } if (!strcmp(mod->name, name)) { return mod; } } /* find a free model slot spot */ for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { if (!mod->name[0]) { break; /* free spot */ } } if (i == mod_numknown) { if (mod_numknown == MAX_MOD_KNOWN) { VID_Error(ERR_DROP, "mod_numknown == MAX_MOD_KNOWN"); } mod_numknown++; } strcpy(mod->name, name); /* load the file */ modfilelen = FS_LoadFile(mod->name, (void **)&buf); if (!buf) { if (crash) { VID_Error(ERR_DROP, "Mod_NumForName: %s not found", mod->name); } memset(mod->name, 0, sizeof(mod->name)); return NULL; } loadmodel = mod; /* call the apropriate loader */ switch (LittleLong(*(unsigned *)buf)) { case IDALIASHEADER: loadmodel->extradata = Hunk_Begin(0x200000); LoadMD2(mod, buf); break; case IDSPRITEHEADER: loadmodel->extradata = Hunk_Begin(0x10000); LoadSP2(mod, buf); break; case IDBSPHEADER: loadmodel->extradata = Hunk_Begin(0x1000000); Mod_LoadBrushModel(mod, buf); break; default: VID_Error(ERR_DROP, "Mod_NumForName: unknown fileid for %s", mod->name); break; } loadmodel->extradatasize = Hunk_End(); FS_FreeFile(buf); return mod; } void Mod_LoadLighting(lump_t *l) { if (!l->filelen) { loadmodel->lightdata = NULL; return; } loadmodel->lightdata = Hunk_Alloc(l->filelen); memcpy(loadmodel->lightdata, mod_base + l->fileofs, l->filelen); } void Mod_LoadVisibility(lump_t *l) { int i; if (!l->filelen) { loadmodel->vis = NULL; return; } loadmodel->vis = Hunk_Alloc(l->filelen); memcpy(loadmodel->vis, mod_base + l->fileofs, l->filelen); loadmodel->vis->numclusters = LittleLong(loadmodel->vis->numclusters); for (i = 0; i < loadmodel->vis->numclusters; i++) { loadmodel->vis->bitofs[i][0] = LittleLong(loadmodel->vis->bitofs[i][0]); loadmodel->vis->bitofs[i][1] = LittleLong(loadmodel->vis->bitofs[i][1]); } } void Mod_LoadVertexes(lump_t *l) { dvertex_t *in; mvertex_t *out; int i, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { VID_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count * sizeof(*out)); loadmodel->vertexes = out; loadmodel->numvertexes = count; for (i = 0; i < count; i++, in++, out++) { out->position[0] = LittleFloat(in->point[0]); out->position[1] = LittleFloat(in->point[1]); out->position[2] = LittleFloat(in->point[2]); } } float Mod_RadiusFromBounds(vec3_t mins, vec3_t maxs) { int i; vec3_t corner; for (i = 0; i < 3; i++) { corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]); } return VectorLength(corner); } void Mod_LoadSubmodels(lump_t *l) { dmodel_t *in; mmodel_t *out; int i, j, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { VID_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count * sizeof(*out)); loadmodel->submodels = out; loadmodel->numsubmodels = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { /* spread the mins / maxs by a pixel */ out->mins[j] = LittleFloat(in->mins[j]) - 1; out->maxs[j] = LittleFloat(in->maxs[j]) + 1; out->origin[j] = LittleFloat(in->origin[j]); } out->radius = Mod_RadiusFromBounds(out->mins, out->maxs); out->headnode = LittleLong(in->headnode); out->firstface = LittleLong(in->firstface); out->numfaces = LittleLong(in->numfaces); } } void Mod_LoadEdges(lump_t *l) { dedge_t *in; medge_t *out; int i, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { VID_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc((count + 1) * sizeof(*out)); loadmodel->edges = out; loadmodel->numedges = count; for (i = 0; i < count; i++, in++, out++) { out->v[0] = (unsigned short)LittleShort(in->v[0]); out->v[1] = (unsigned short)LittleShort(in->v[1]); } } void Mod_LoadTexinfo(lump_t *l) { texinfo_t *in; mtexinfo_t *out, *step; int i, j, count; char name[MAX_QPATH]; int next; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { VID_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count * sizeof(*out)); loadmodel->texinfo = out; loadmodel->numtexinfo = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 4; j++) { out->vecs[0][j] = LittleFloat(in->vecs[0][j]); out->vecs[1][j] = LittleFloat(in->vecs[1][j]); } out->flags = LittleLong(in->flags); next = LittleLong(in->nexttexinfo); if (next > 0) { out->next = loadmodel->texinfo + next; } else { out->next = NULL; } Com_sprintf(name, sizeof(name), "textures/%s.wal", in->texture); out->image = R_FindImage(name, it_wall); if (!out->image) { VID_Printf(PRINT_ALL, "Couldn't load %s\n", name); out->image = r_notexture; } } /* count animation frames */ for (i = 0; i < count; i++) { out = &loadmodel->texinfo[i]; out->numframes = 1; for (step = out->next; step && step != out; step = step->next) { out->numframes++; } } } /* * Fills in s->texturemins[] and s->extents[] */ void Mod_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; i < s->numedges; 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] = floor(mins[i] / 16); bmaxs[i] = ceil(maxs[i] / 16); s->texturemins[i] = bmins[i] * 16; s->extents[i] = (bmaxs[i] - bmins[i]) * 16; } } void Mod_LoadFaces(lump_t *l) { dface_t *in; msurface_t *out; int i, count, surfnum; int planenum, side; int ti; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { VID_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count * sizeof(*out)); loadmodel->surfaces = out; loadmodel->numsurfaces = count; currentmodel = loadmodel; LM_BeginBuildingLightmaps(loadmodel); for (surfnum = 0; surfnum < count; surfnum++, in++, out++) { out->firstedge = LittleLong(in->firstedge); out->numedges = LittleShort(in->numedges); out->flags = 0; out->polys = NULL; planenum = LittleShort(in->planenum); side = LittleShort(in->side); if (side) { out->flags |= SURF_PLANEBACK; } out->plane = loadmodel->planes + planenum; ti = LittleShort(in->texinfo); if ((ti < 0) || (ti >= loadmodel->numtexinfo)) { VID_Error(ERR_DROP, "MOD_LoadBmodel: bad texinfo number"); } out->texinfo = loadmodel->texinfo + ti; Mod_CalcSurfaceExtents(out); /* lighting info */ for (i = 0; i < MAXLIGHTMAPS; i++) { out->styles[i] = in->styles[i]; } i = LittleLong(in->lightofs); if (i == -1) { out->samples = NULL; } else { out->samples = loadmodel->lightdata + i; } /* set the drawing flags */ if (out->texinfo->flags & SURF_WARP) { out->flags |= SURF_DRAWTURB; for (i = 0; i < 2; i++) { out->extents[i] = 16384; out->texturemins[i] = -8192; } R_SubdivideSurface(out); /* cut up polygon for warps */ } /* create lightmaps and polygons */ if (!(out->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP))) { LM_CreateSurfaceLightmap(out); } if (!(out->texinfo->flags & SURF_WARP)) { LM_BuildPolygonFromSurface(out); } } LM_EndBuildingLightmaps(); } void Mod_SetParent(mnode_t *node, mnode_t *parent) { node->parent = parent; if (node->contents != -1) { return; } Mod_SetParent(node->children[0], node); Mod_SetParent(node->children[1], node); } void Mod_LoadNodes(lump_t *l) { int i, j, count, p; dnode_t *in; mnode_t *out; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { VID_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count * sizeof(*out)); loadmodel->nodes = out; loadmodel->numnodes = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->minmaxs[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); out->contents = -1; /* differentiate from leafs */ for (j = 0; j < 2; j++) { p = LittleLong(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 */ } void Mod_LoadLeafs(lump_t *l) { dleaf_t *in; mleaf_t *out; int i, j, count, p; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { VID_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count * sizeof(*out)); loadmodel->leafs = out; loadmodel->numleafs = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->minmaxs[j] = LittleShort(in->mins[j]); out->minmaxs[3 + j] = LittleShort(in->maxs[j]); } p = LittleLong(in->contents); out->contents = p; out->cluster = LittleShort(in->cluster); out->area = LittleShort(in->area); out->firstmarksurface = loadmodel->marksurfaces + LittleShort(in->firstleafface); out->nummarksurfaces = LittleShort(in->numleaffaces); } } void Mod_LoadMarksurfaces(lump_t *l) { int i, j, count; short *in; msurface_t **out; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { VID_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count * sizeof(*out)); loadmodel->marksurfaces = out; loadmodel->nummarksurfaces = count; for (i = 0; i < count; i++) { j = LittleShort(in[i]); if ((j < 0) || (j >= loadmodel->numsurfaces)) { VID_Error(ERR_DROP, "Mod_ParseMarksurfaces: bad surface number"); } out[i] = loadmodel->surfaces + j; } } void Mod_LoadSurfedges(lump_t *l) { int i, count; int *in, *out; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { VID_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); if ((count < 1) || (count >= MAX_MAP_SURFEDGES)) { VID_Error(ERR_DROP, "MOD_LoadBmodel: bad surfedges count in %s: %i", loadmodel->name, count); } out = Hunk_Alloc(count * sizeof(*out)); loadmodel->surfedges = out; loadmodel->numsurfedges = count; for (i = 0; i < count; i++) { out[i] = LittleLong(in[i]); } } void Mod_LoadPlanes(lump_t *l) { int i, j; cplane_t *out; dplane_t *in; int count; int bits; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { VID_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count * 2 * sizeof(*out)); loadmodel->planes = out; loadmodel->numplanes = count; for (i = 0; i < count; i++, in++, out++) { bits = 0; for (j = 0; j < 3; j++) { out->normal[j] = LittleFloat(in->normal[j]); if (out->normal[j] < 0) { bits |= 1 << j; } } out->dist = LittleFloat(in->dist); out->type = LittleLong(in->type); out->signbits = bits; } } void Mod_LoadBrushModel(model_t *mod, void *buffer) { int i; dheader_t *header; mmodel_t *bm; loadmodel->type = mod_brush; if (loadmodel != mod_known) { VID_Error(ERR_DROP, "Loaded a brush model after the world"); } header = (dheader_t *)buffer; i = LittleLong(header->version); if (i != BSPVERSION) { VID_Error(ERR_DROP, "Mod_LoadBrushModel: %s has wrong version number (%i should be %i)", mod->name, i, BSPVERSION); } /* swap all the lumps */ mod_base = (byte *)header; for (i = 0; i < sizeof(dheader_t) / 4; i++) { ((int *)header)[i] = LittleLong(((int *)header)[i]); } /* load into heap */ Mod_LoadVertexes(&header->lumps[LUMP_VERTEXES]); Mod_LoadEdges(&header->lumps[LUMP_EDGES]); Mod_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]); Mod_LoadLighting(&header->lumps[LUMP_LIGHTING]); Mod_LoadPlanes(&header->lumps[LUMP_PLANES]); Mod_LoadTexinfo(&header->lumps[LUMP_TEXINFO]); Mod_LoadFaces(&header->lumps[LUMP_FACES]); Mod_LoadMarksurfaces(&header->lumps[LUMP_LEAFFACES]); Mod_LoadVisibility(&header->lumps[LUMP_VISIBILITY]); Mod_LoadLeafs(&header->lumps[LUMP_LEAFS]); Mod_LoadNodes(&header->lumps[LUMP_NODES]); Mod_LoadSubmodels(&header->lumps[LUMP_MODELS]); mod->numframes = 2; /* regular and alternate animation */ /* set up the submodels */ for (i = 0; i < mod->numsubmodels; i++) { model_t *starmod; bm = &mod->submodels[i]; starmod = &mod_inline[i]; *starmod = *loadmodel; starmod->firstmodelsurface = bm->firstface; starmod->nummodelsurfaces = bm->numfaces; starmod->firstnode = bm->headnode; if (starmod->firstnode >= loadmodel->numnodes) { VID_Error(ERR_DROP, "Inline model %i has bad firstnode", i); } VectorCopy(bm->maxs, starmod->maxs); VectorCopy(bm->mins, starmod->mins); starmod->radius = bm->radius; if (i == 0) { *loadmodel = *starmod; } starmod->numleafs = bm->visleafs; } } void Mod_Free(model_t *mod) { Hunk_Free(mod->extradata); memset(mod, 0, sizeof(*mod)); } void Mod_FreeAll(void) { int i; for (i = 0; i < mod_numknown; i++) { if (mod_known[i].extradatasize) { Mod_Free(&mod_known[i]); } } } /* * Specifies the model that will be used as the world */ void R_BeginRegistration(char *model) { char fullname[MAX_QPATH]; cvar_t *flushmap; registration_sequence++; r_oldviewcluster = -1; /* force markleafs */ Com_sprintf(fullname, sizeof(fullname), "maps/%s.bsp", model); /* explicitly free the old map if different this guarantees that mod_known[0] is the world map */ flushmap = Cvar_Get("flushmap", "0", 0); if (strcmp(mod_known[0].name, fullname) || flushmap->value) { Mod_Free(&mod_known[0]); } r_worldmodel = Mod_ForName(fullname, true); r_viewcluster = -1; } struct model_s * R_RegisterModel(char *name) { model_t *mod; int i; dsprite_t *sprout; dmdl_t *pheader; mod = Mod_ForName(name, false); if (mod) { mod->registration_sequence = registration_sequence; /* register any images used by the models */ if (mod->type == mod_sprite) { sprout = (dsprite_t *)mod->extradata; for (i = 0; i < sprout->numframes; i++) { mod->skins[i] = R_FindImage(sprout->frames[i].name, it_sprite); } } else if (mod->type == mod_alias) { pheader = (dmdl_t *)mod->extradata; for (i = 0; i < pheader->num_skins; i++) { mod->skins[i] = R_FindImage((char *)pheader + pheader->ofs_skins + i * MAX_SKINNAME, it_skin); } mod->numframes = pheader->num_frames; } else if (mod->type == mod_brush) { for (i = 0; i < mod->numtexinfo; i++) { mod->texinfo[i].image->registration_sequence = registration_sequence; } } } return mod; } void R_EndRegistration(void) { int i; model_t *mod; for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { if (!mod->name[0]) { continue; } if (mod->registration_sequence != registration_sequence) { /* don't need this model */ Mod_Free(mod); } } R_FreeUnusedImages(); } yquake2-QUAKE2_5_32/src/client/refresh/header/0000755000175000017500000000000012615162034021552 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/client/refresh/header/local.h0000644000175000017500000002432212615162034023020 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Local header for the refresher. * * ======================================================================= */ #ifndef REF_LOCAL_H #define REF_LOCAL_H #include #include #include #ifdef _WIN32 #include #endif #if defined(__APPLE__) #include #else #include #endif #include "../../header/ref.h" #include "../../../backends/generic/header/qgl.h" #ifndef GL_COLOR_INDEX8_EXT #define GL_COLOR_INDEX8_EXT GL_COLOR_INDEX #endif #ifndef GL_EXT_texture_filter_anisotropic #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF #endif #ifndef GL_ARB_multitexture #define GL_TEXTURE0_ARB 0x84C0 #define GL_TEXTURE1_ARB 0x84C1 #endif #ifndef GL_VERSION_1_3 #define GL_TEXTURE1 0x84C1 #endif #ifndef GL_MULTISAMPLE #define GL_MULTISAMPLE 0x809D #endif #ifndef GL_MULTISAMPLE_FILTER_HINT_NV #define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534 #endif #define TEXNUM_LIGHTMAPS 1024 #define TEXNUM_SCRAPS 1152 #define TEXNUM_IMAGES 1153 #define MAX_GLTEXTURES 1024 #define MAX_SCRAPS 1 #define BLOCK_WIDTH 128 #define BLOCK_HEIGHT 128 #define REF_VERSION "Yamagi Quake II OpenGL Refresher" #define MAX_LBM_HEIGHT 480 #define BACKFACE_EPSILON 0.01 #define DYNAMIC_LIGHT_WIDTH 128 #define DYNAMIC_LIGHT_HEIGHT 128 #define LIGHTMAP_BYTES 4 #define MAX_LIGHTMAPS 128 #define GL_LIGHTMAP_FORMAT GL_RGBA /* up / down */ #define PITCH 0 /* left / right */ #define YAW 1 /* fall over */ #define ROLL 2 char *strlwr(char *s); extern viddef_t vid; /* * skins will be outline flood filled and mip mapped * pics and sprites with alpha will be outline flood filled * pic won't be mip mapped * * model skin * sprite frame * wall texture * pic */ typedef enum { it_skin, it_sprite, it_wall, it_pic, it_sky } imagetype_t; typedef struct image_s { char name[MAX_QPATH]; /* game path, including extension */ imagetype_t type; int width, height; /* source image */ int upload_width, upload_height; /* after power of two and picmip */ int registration_sequence; /* 0 = free */ struct msurface_s *texturechain; /* for sort-by-texture world drawing */ int texnum; /* gl texture binding */ float sl, tl, sh, th; /* 0,0 - 1,1 unless part of the scrap */ qboolean scrap; qboolean has_alpha; qboolean paletted; } image_t; typedef enum { rserr_ok, rserr_invalid_fullscreen, rserr_invalid_mode, rserr_unknown } rserr_t; #include "model.h" void GL_BeginRendering(int *x, int *y, int *width, int *height); void GL_EndRendering(void); void R_SetDefaultState(void); extern float gldepthmin, gldepthmax; typedef struct { float x, y, z; float s, t; float r, g, b; } glvert_t; extern image_t gltextures[MAX_GLTEXTURES]; extern int numgltextures; extern image_t *r_notexture; extern image_t *r_particletexture; extern entity_t *currententity; extern model_t *currentmodel; extern int r_visframecount; extern int r_framecount; extern cplane_t frustum[4]; extern int c_brush_polys, c_alias_polys; extern int gl_filter_min, gl_filter_max; /* 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_newrefdef; extern int r_viewcluster, r_viewcluster2, r_oldviewcluster, r_oldviewcluster2; extern cvar_t *gl_norefresh; extern cvar_t *gl_lefthand; extern cvar_t *gl_farsee; extern cvar_t *gl_drawentities; extern cvar_t *gl_drawworld; extern cvar_t *gl_speeds; extern cvar_t *gl_fullbright; extern cvar_t *gl_novis; extern cvar_t *gl_nocull; extern cvar_t *gl_lerpmodels; extern cvar_t *gl_lightlevel; extern cvar_t *gl_overbrightbits; extern cvar_t *gl_vertex_arrays; extern cvar_t *gl_ext_swapinterval; extern cvar_t *gl_ext_palettedtexture; extern cvar_t *gl_ext_multitexture; extern cvar_t *gl_ext_pointparameters; extern cvar_t *gl_ext_compiled_vertex_array; extern cvar_t *gl_ext_mtexcombine; extern cvar_t *gl_particle_min_size; extern cvar_t *gl_particle_max_size; extern cvar_t *gl_particle_size; extern cvar_t *gl_particle_att_a; extern cvar_t *gl_particle_att_b; extern cvar_t *gl_particle_att_c; extern cvar_t *gl_nosubimage; extern cvar_t *gl_bitdepth; extern cvar_t *gl_mode; extern cvar_t *gl_customwidth; extern cvar_t *gl_customheight; extern cvar_t *gl_retexturing; extern cvar_t *gl_lightmap; extern cvar_t *gl_shadows; extern cvar_t *gl_stencilshadow; extern cvar_t *gl_dynamic; extern cvar_t *gl_nobind; extern cvar_t *gl_round_down; extern cvar_t *gl_picmip; extern cvar_t *gl_skymip; extern cvar_t *gl_showtris; extern cvar_t *gl_finish; extern cvar_t *gl_ztrick; extern cvar_t *gl_zfix; extern cvar_t *gl_clear; extern cvar_t *gl_cull; extern cvar_t *gl_poly; extern cvar_t *gl_texsort; extern cvar_t *gl_polyblend; extern cvar_t *gl_flashblend; extern cvar_t *gl_lightmaptype; extern cvar_t *gl_modulate; extern cvar_t *gl_playermip; extern cvar_t *gl_drawbuffer; extern cvar_t *gl_3dlabs_broken; extern cvar_t *gl_swapinterval; extern cvar_t *gl_anisotropic; extern cvar_t *gl_anisotropic_avail; extern cvar_t *gl_texturemode; extern cvar_t *gl_texturealphamode; extern cvar_t *gl_texturesolidmode; extern cvar_t *gl_saturatelighting; extern cvar_t *gl_lockpvs; extern cvar_t *gl_msaa_samples; extern cvar_t *vid_fullscreen; extern cvar_t *vid_gamma; extern cvar_t *intensity; extern int gl_lightmap_format; extern int gl_solid_format; extern int gl_alpha_format; extern int gl_tex_solid_format; extern int gl_tex_alpha_format; extern int c_visible_lightmaps; extern int c_visible_textures; extern float r_world_matrix[16]; void R_TranslatePlayerSkin(int playernum); void R_Bind(int texnum); void R_MBind(GLenum target, int texnum); void R_TexEnv(GLenum value); void R_EnableMultitexture(qboolean enable); void R_SelectTexture(GLenum); void R_LightPoint(vec3_t p, vec3_t color); void R_PushDlights(void); extern model_t *r_worldmodel; extern unsigned d_8to24table[256]; extern int registration_sequence; void V_AddBlend(float r, float g, float b, float a, float *v_blend); void R_RenderView(refdef_t *fd); void R_ScreenShot(void); void R_DrawAliasModel(entity_t *e); void R_DrawBrushModel(entity_t *e); void R_DrawSpriteModel(entity_t *e); void R_DrawBeam(entity_t *e); void R_DrawWorld(void); void R_RenderDlights(void); void R_DrawAlphaSurfaces(void); void R_RenderBrushPoly(msurface_t *fa); void R_InitParticleTexture(void); void Draw_InitLocal(void); void R_SubdivideSurface(msurface_t *fa); qboolean R_CullBox(vec3_t mins, vec3_t maxs); void R_RotateForEntity(entity_t *e); void R_MarkLeaves(void); glpoly_t *WaterWarpPolyVerts(glpoly_t *p); void R_EmitWaterPolys(msurface_t *fa); void R_AddSkySurface(msurface_t *fa); void R_ClearSkyBox(void); void R_DrawSkyBox(void); void R_MarkLights(dlight_t *light, int bit, mnode_t *node); void COM_StripExtension(char *in, char *out); void R_SwapBuffers(int); int Draw_GetPalette(void); void R_ResampleTexture(unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight); void LoadPCX(char *filename, byte **pic, byte **palette, int *width, int *height); image_t *LoadWal(char *name); qboolean LoadSTB(const char *origname, const char* type, byte **pic, int *width, int *height); void GetWalInfo(char *name, int *width, int *height); void GetPCXInfo(char *filename, int *width, int *height); image_t *R_LoadPic(char *name, byte *pic, int width, int realwidth, int height, int realheight, imagetype_t type, int bits); image_t *R_FindImage(char *name, imagetype_t type); void R_TextureMode(char *string); void R_ImageList_f(void); void R_SetTexturePalette(unsigned palette[256]); void R_InitImages(void); void R_ShutdownImages(void); void R_FreeUnusedImages(void); void R_TextureAlphaMode(char *string); void R_TextureSolidMode(char *string); int Scrap_AllocBlock(int w, int h, int *x, int *y); /* GL extension emulation functions */ void R_DrawParticles2(int n, const particle_t particles[], const unsigned colortable[768]); /* * GL config stuff */ typedef struct { int renderer; const char *renderer_string; const char *vendor_string; const char *version_string; const char *extensions_string; qboolean allow_cds; qboolean mtexcombine; qboolean anisotropic; qboolean tex_npot; float max_anisotropy; } glconfig_t; typedef struct { float inverse_intensity; qboolean fullscreen; int prev_mode; unsigned char *d_16to8table; int lightmap_textures; int currenttextures[2]; int currenttmu; float camera_separation; qboolean stereo_enabled; qboolean hwgamma; unsigned char originalRedGammaTable[256]; unsigned char originalGreenGammaTable[256]; unsigned char originalBlueGammaTable[256]; } glstate_t; typedef struct { int internal_format; int current_lightmap_texture; msurface_t *lightmap_surfaces[MAX_LIGHTMAPS]; int allocated[BLOCK_WIDTH]; /* the lightmap texture data needs to be kept in main memory so texsubimage can update properly */ byte lightmap_buffer[4 * BLOCK_WIDTH * BLOCK_HEIGHT]; } gllightmapstate_t; extern glconfig_t gl_config; extern glstate_t gl_state; /* * Initializes the SDL OpenGL context */ int GLimp_Init(void); /* * Shuts the SDL render backend down */ void GLimp_Shutdown(void); /* * Changes the video mode */ int GLimp_SetMode(int *pwidth, int *pheight, int mode, qboolean fullscreen); /* * Returns the address of the GL function proc, * or NULL if the function is not found. */ void *GLimp_GetProcAddress (const char* proc); /* * (Un)grab Input */ void GLimp_GrabInput(qboolean grab); #endif yquake2-QUAKE2_5_32/src/client/refresh/header/model.h0000644000175000017500000001200712615162034023023 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Header for the model stuff. * * ======================================================================= */ #ifndef REF_MODEL_H #define REF_MODEL_H #define SIDE_FRONT 0 #define SIDE_BACK 1 #define SIDE_ON 2 #define SURF_PLANEBACK 2 #define SURF_DRAWSKY 4 #define SURF_DRAWTURB 0x10 #define SURF_DRAWBACKGROUND 0x40 #define SURF_UNDERWATER 0x80 #define VERTEXSIZE 7 /* in memory representation */ typedef struct { vec3_t position; } mvertex_t; typedef struct { vec3_t mins, maxs; vec3_t origin; /* for sounds or lights */ float radius; int headnode; int visleafs; /* not including the solid leaf 0 */ int firstface, numfaces; } mmodel_t; typedef struct { unsigned short v[2]; unsigned int cachededgeoffset; } medge_t; typedef struct mtexinfo_s { float vecs[2][4]; int flags; int numframes; struct mtexinfo_s *next; /* animation chain */ image_t *image; } mtexinfo_t; typedef struct glpoly_s { struct glpoly_s *next; struct glpoly_s *chain; int numverts; int flags; /* for SURF_UNDERWATER (not needed anymore?) */ float verts[4][VERTEXSIZE]; /* variable sized (xyz s1t1 s2t2) */ } glpoly_t; typedef struct msurface_s { int visframe; /* should be drawn when node is crossed */ cplane_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 */ int dlight_s, dlight_t; /* gl lightmap coordinates for dynamic lightmaps */ glpoly_t *polys; /* multiple if warped */ struct msurface_s *texturechain; struct msurface_s *lightmapchain; mtexinfo_t *texinfo; /* lighting info */ int dlightframe; int dlightbits; int lightmaptexturenum; byte styles[MAXLIGHTMAPS]; float cached_light[MAXLIGHTMAPS]; /* values currently used in lightmap */ byte *samples; /* [numstyles*surfsize] */ } msurface_t; typedef struct mnode_s { /* common with leaf */ int contents; /* -1, 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 */ cplane_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 */ int cluster; int area; msurface_t **firstmarksurface; int nummarksurfaces; } mleaf_t; /* Whole model */ typedef enum {mod_bad, mod_brush, mod_sprite, mod_alias} modtype_t; typedef struct model_s { char name[MAX_QPATH]; int registration_sequence; modtype_t type; int numframes; 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 lightmap; /* only for submodels */ int numsubmodels; mmodel_t *submodels; int numplanes; cplane_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; int firstnode; mnode_t *nodes; int numtexinfo; mtexinfo_t *texinfo; int numsurfaces; msurface_t *surfaces; int numsurfedges; int *surfedges; int nummarksurfaces; msurface_t **marksurfaces; dvis_t *vis; byte *lightdata; /* for alias models and skins */ image_t *skins[MAX_MD2SKINS]; int extradatasize; void *extradata; } model_t; void Mod_Init(void); void Mod_ClearAll(void); model_t *Mod_ForName(char *name, qboolean crash); mleaf_t *Mod_PointInLeaf(float *p, model_t *model); byte *Mod_ClusterPVS(int cluster, model_t *model); void Mod_Modellist_f(void); void *Hunk_Begin(int maxsize); void *Hunk_Alloc(int size); int Hunk_End(void); void Hunk_Free(void *base); void Mod_FreeAll(void); void Mod_Free(model_t *mod); #endif yquake2-QUAKE2_5_32/src/client/refresh/r_scrap.c0000644000175000017500000000431712615162034022124 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Allocate all the little status bar obejcts into a single texture * to crutch up inefficient hardware / drivers. * * ======================================================================= */ #include "header/local.h" int scrap_allocated[MAX_SCRAPS][BLOCK_WIDTH]; byte scrap_texels[MAX_SCRAPS][BLOCK_WIDTH * BLOCK_HEIGHT]; qboolean scrap_dirty; int scrap_uploads; qboolean R_Upload8(byte *data, int width, int height, qboolean mipmap, qboolean is_sky); /* returns a texture number and the position inside it */ int Scrap_AllocBlock(int w, int h, int *x, int *y) { int i, j; int best, best2; int texnum; for (texnum = 0; texnum < MAX_SCRAPS; texnum++) { best = BLOCK_HEIGHT; for (i = 0; i < BLOCK_WIDTH - w; i++) { best2 = 0; for (j = 0; j < w; j++) { if (scrap_allocated[texnum][i + j] >= best) { break; } if (scrap_allocated[texnum][i + j] > best2) { best2 = scrap_allocated[texnum][i + j]; } } if (j == w) { /* this is a valid spot */ *x = i; *y = best = best2; } } if (best + h > BLOCK_HEIGHT) { continue; } for (i = 0; i < w; i++) { scrap_allocated[texnum][*x + i] = best + h; } return texnum; } return -1; } void Scrap_Upload(void) { scrap_uploads++; R_Bind(TEXNUM_SCRAPS); R_Upload8(scrap_texels[0], BLOCK_WIDTH, BLOCK_HEIGHT, false, false); scrap_dirty = false; } yquake2-QUAKE2_5_32/src/client/refresh/files/0000755000175000017500000000000012615162034021424 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/client/refresh/files/pcx.c0000644000175000017500000000617512615162034022373 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 PCX file format * * ======================================================================= */ #include "../header/local.h" void LoadPCX(char *origname, byte **pic, byte **palette, int *width, int *height) { byte *raw; pcx_t *pcx; int x, y; int len; int dataByte, runLength; byte *out, *pix; char filename[256]; Q_strlcpy(filename, origname, sizeof(filename)); /* Add the extension */ if (strcmp(COM_FileExtension(filename), "pcx")) { Q_strlcat(filename, ".pcx", sizeof(filename)); } *pic = NULL; *palette = NULL; /* load the file */ len = FS_LoadFile(filename, (void **)&raw); if (!raw) { VID_Printf(PRINT_DEVELOPER, "Bad pcx file %s\n", filename); return; } /* parse the PCX file */ pcx = (pcx_t *)raw; pcx->xmin = LittleShort(pcx->xmin); pcx->ymin = LittleShort(pcx->ymin); pcx->xmax = LittleShort(pcx->xmax); pcx->ymax = LittleShort(pcx->ymax); pcx->hres = LittleShort(pcx->hres); pcx->vres = LittleShort(pcx->vres); pcx->bytes_per_line = LittleShort(pcx->bytes_per_line); pcx->palette_type = LittleShort(pcx->palette_type); raw = &pcx->data; if ((pcx->manufacturer != 0x0a) || (pcx->version != 5) || (pcx->encoding != 1) || (pcx->bits_per_pixel != 8) || (pcx->xmax >= 640) || (pcx->ymax >= 480)) { VID_Printf(PRINT_ALL, "Bad pcx file %s\n", filename); return; } out = malloc((pcx->ymax + 1) * (pcx->xmax + 1)); *pic = out; pix = out; if (palette) { *palette = malloc(768); memcpy(*palette, (byte *)pcx + len - 768, 768); } if (width) { *width = pcx->xmax + 1; } if (height) { *height = pcx->ymax + 1; } for (y = 0; y <= pcx->ymax; y++, pix += pcx->xmax + 1) { for (x = 0; x <= pcx->xmax; ) { dataByte = *raw++; if ((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; dataByte = *raw++; } else { runLength = 1; } while (runLength-- > 0) { pix[x++] = dataByte; } } } if (raw - (byte *)pcx > len) { VID_Printf(PRINT_DEVELOPER, "PCX file %s was malformed", filename); free(*pic); *pic = NULL; } FS_FreeFile(pcx); } void GetPCXInfo(char *filename, int *width, int *height) { pcx_t *pcx; byte *raw; FS_LoadFile(filename, (void **)&raw); if (!raw) { return; } pcx = (pcx_t *)raw; *width = pcx->xmax + 1; *height = pcx->ymax + 1; FS_FreeFile(raw); return; } yquake2-QUAKE2_5_32/src/client/refresh/files/stb.c0000644000175000017500000000514212615162034022362 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2015 Daniel Gibson * * This program is free software; you can 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. * * ======================================================================= * * File formats supported by stb_image, for now only tga, png, jpg * See also https://github.com/nothings/stb * * ======================================================================= */ #include #include "../header/local.h" // don't need HDR stuff #define STBI_NO_LINEAR #define STBI_NO_HDR // make sure STB_image uses standard malloc(), as we'll use standard free() to deallocate #define STBI_MALLOC(sz) malloc(sz) #define STBI_REALLOC(p,sz) realloc(p,sz) #define STBI_FREE(p) free(p) // include implementation part of stb_image into this file #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" /* * origname: the filename to be opened, might be without extension * type: extension of the type we wanna open ("jpg", "png" or "tga") * pic: pointer RGBA pixel data will be assigned to */ qboolean LoadSTB(const char *origname, const char* type, byte **pic, int *width, int *height) { char filename[256]; Q_strlcpy(filename, origname, sizeof(filename)); /* Add the extension */ if (strcmp(COM_FileExtension(filename), type) != 0) { Q_strlcat(filename, ".", sizeof(filename)); Q_strlcat(filename, type, sizeof(filename)); } *pic = NULL; byte* rawdata = NULL; int rawsize = FS_LoadFile(filename, (void **)&rawdata); if (rawdata == NULL) { return false; } int w, h, bytesPerPixel; byte* data = NULL; data = stbi_load_from_memory(rawdata, rawsize, &w, &h, &bytesPerPixel, STBI_rgb_alpha); if (data == NULL) { VID_Printf(PRINT_ALL, "stb_image couldn't load data from %s: %s!\n", filename, stbi_failure_reason()); FS_FreeFile(rawdata); return false; } FS_FreeFile(rawdata); VID_Printf(PRINT_DEVELOPER, "LoadSTB() loaded: %s\n", filename); *pic = data; *width = w; *height = h; return true; } yquake2-QUAKE2_5_32/src/client/refresh/files/md2.c0000644000175000017500000001030712615162034022253 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * MD2 file format * * ======================================================================= */ #include "../header/local.h" void LoadMD2(model_t *mod, void *buffer) { int i, j; dmdl_t *pinmodel, *pheader; dstvert_t *pinst, *poutst; dtriangle_t *pintri, *pouttri; daliasframe_t *pinframe, *poutframe; int *pincmd, *poutcmd; int version; pinmodel = (dmdl_t *)buffer; version = LittleLong(pinmodel->version); if (version != ALIAS_VERSION) { VID_Error(ERR_DROP, "%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_VERSION); } pheader = Hunk_Alloc(LittleLong(pinmodel->ofs_end)); /* byte swap the header fields and sanity check */ for (i = 0; i < sizeof(dmdl_t) / 4; i++) { ((int *)pheader)[i] = LittleLong(((int *)buffer)[i]); } if (pheader->skinheight > MAX_LBM_HEIGHT) { VID_Error(ERR_DROP, "model %s has a skin taller than %d", mod->name, MAX_LBM_HEIGHT); } if (pheader->num_xyz <= 0) { VID_Error(ERR_DROP, "model %s has no vertices", mod->name); } if (pheader->num_xyz > MAX_VERTS) { VID_Error(ERR_DROP, "model %s has too many vertices", mod->name); } if (pheader->num_st <= 0) { VID_Error(ERR_DROP, "model %s has no st vertices", mod->name); } if (pheader->num_tris <= 0) { VID_Error(ERR_DROP, "model %s has no triangles", mod->name); } if (pheader->num_frames <= 0) { VID_Error(ERR_DROP, "model %s has no frames", mod->name); } /* load base s and t vertices (not used in gl version) */ pinst = (dstvert_t *)((byte *)pinmodel + pheader->ofs_st); poutst = (dstvert_t *)((byte *)pheader + pheader->ofs_st); for (i = 0; i < pheader->num_st; i++) { poutst[i].s = LittleShort(pinst[i].s); poutst[i].t = LittleShort(pinst[i].t); } /* load triangle lists */ pintri = (dtriangle_t *)((byte *)pinmodel + pheader->ofs_tris); pouttri = (dtriangle_t *)((byte *)pheader + pheader->ofs_tris); for (i = 0; i < pheader->num_tris; i++) { for (j = 0; j < 3; j++) { pouttri[i].index_xyz[j] = LittleShort(pintri[i].index_xyz[j]); pouttri[i].index_st[j] = LittleShort(pintri[i].index_st[j]); } } /* load the frames */ for (i = 0; i < pheader->num_frames; i++) { pinframe = (daliasframe_t *)((byte *)pinmodel + pheader->ofs_frames + i * pheader->framesize); poutframe = (daliasframe_t *)((byte *)pheader + pheader->ofs_frames + i * pheader->framesize); memcpy(poutframe->name, pinframe->name, sizeof(poutframe->name)); for (j = 0; j < 3; j++) { poutframe->scale[j] = LittleFloat(pinframe->scale[j]); poutframe->translate[j] = LittleFloat(pinframe->translate[j]); } /* verts are all 8 bit, so no swapping needed */ memcpy(poutframe->verts, pinframe->verts, pheader->num_xyz * sizeof(dtrivertx_t)); } mod->type = mod_alias; /* load the glcmds */ pincmd = (int *)((byte *)pinmodel + pheader->ofs_glcmds); poutcmd = (int *)((byte *)pheader + pheader->ofs_glcmds); for (i = 0; i < pheader->num_glcmds; i++) { poutcmd[i] = LittleLong(pincmd[i]); } /* register all skins */ memcpy((char *)pheader + pheader->ofs_skins, (char *)pinmodel + pheader->ofs_skins, pheader->num_skins * MAX_SKINNAME); for (i = 0; i < pheader->num_skins; i++) { mod->skins[i] = R_FindImage( (char *)pheader + pheader->ofs_skins + i * MAX_SKINNAME, it_skin); } mod->mins[0] = -32; mod->mins[1] = -32; mod->mins[2] = -32; mod->maxs[0] = 32; mod->maxs[1] = 32; mod->maxs[2] = 32; } yquake2-QUAKE2_5_32/src/client/refresh/files/wal.c0000644000175000017500000000350412615162034022355 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 Wal image format * * ======================================================================= */ #include "../header/local.h" image_t * LoadWal(char *origname) { miptex_t *mt; int width, height, ofs; image_t *image; char name[256]; Q_strlcpy(name, origname, sizeof(name)); /* Add the extension */ if (strcmp(COM_FileExtension(name), "wal")) { Q_strlcat(name, ".wal", sizeof(name)); } FS_LoadFile(name, (void **)&mt); if (!mt) { VID_Printf(PRINT_ALL, "LoadWall: can't load %s\n", name); return r_notexture; } width = LittleLong(mt->width); height = LittleLong(mt->height); ofs = LittleLong(mt->offsets[0]); image = R_LoadPic(name, (byte *)mt + ofs, width, 0, height, 0, it_wall, 8); FS_FreeFile((void *)mt); return image; } void GetWalInfo(char *name, int *width, int *height) { miptex_t *mt; FS_LoadFile(name, (void **)&mt); if (!mt) { return; } *width = LittleLong(mt->width); *height = LittleLong(mt->height); FS_FreeFile((void *)mt); return; } yquake2-QUAKE2_5_32/src/client/refresh/files/stb_image.h0000644000175000017500000064551212615162034023544 0ustar greffrathgreffrath/* stb_image - v2.02 - public domain image loader - http://nothings.org/stb_image.h no warranty implied; use at your own risk Do this: #define STB_IMAGE_IMPLEMENTATION before you include this file in *one* C or C++ file to create the implementation. // i.e. it should look like this: #include ... #include ... #include ... #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free QUICK NOTES: Primarily of interest to game developers and other people who can avoid problematic images and only need the trivial interface JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) PNG 1/2/4/8-bit-per-channel (16 bpc not supported) TGA (not sure what subset, if a subset) BMP non-1bpp, non-RLE PSD (composited view only, no extra channels) GIF (*comp always reports as 4-channel) HDR (radiance rgbE format) PIC (Softimage PIC) PNM (PPM and PGM binary only) - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - decode from arbitrary I/O callbacks - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) Full documentation under "DOCUMENTATION" below. Revision 2.00 release notes: - Progressive JPEG is now supported. - PPM and PGM binary formats are now supported, thanks to Ken Miller. - x86 platforms now make use of SSE2 SIMD instructions for JPEG decoding, and ARM platforms can use NEON SIMD if requested. This work was done by Fabian "ryg" Giesen. SSE2 is used by default, but NEON must be enabled explicitly; see docs. With other JPEG optimizations included in this version, we see 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup on a JPEG on an ARM machine, relative to previous versions of this library. The same results will not obtain for all JPGs and for all x86/ARM machines. (Note that progressive JPEGs are significantly slower to decode than regular JPEGs.) This doesn't mean that this is the fastest JPEG decoder in the land; rather, it brings it closer to parity with standard libraries. If you want the fastest decode, look elsewhere. (See "Philosophy" section of docs below.) See final bullet items below for more info on SIMD. - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing the memory allocator. Unlike other STBI libraries, these macros don't support a context parameter, so if you need to pass a context in to the allocator, you'll have to store it in a global or a thread-local variable. - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and STBI_NO_LINEAR. STBI_NO_HDR: suppress implementation of .hdr reader format STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API - You can suppress implementation of any of the decoders to reduce your code footprint by #defining one or more of the following symbols before creating the implementation. STBI_NO_JPEG STBI_NO_PNG STBI_NO_BMP STBI_NO_PSD STBI_NO_TGA STBI_NO_GIF STBI_NO_HDR STBI_NO_PIC STBI_NO_PNM (.ppm and .pgm) - You can request *only* certain decoders and suppress all other ones (this will be more forward-compatible, as addition of new decoders doesn't require you to disable them explicitly): STBI_ONLY_JPEG STBI_ONLY_PNG STBI_ONLY_BMP STBI_ONLY_PSD STBI_ONLY_TGA STBI_ONLY_GIF STBI_ONLY_HDR STBI_ONLY_PIC STBI_ONLY_PNM (.ppm and .pgm) Note that you can define multiples of these, and you will get all of them ("only x" and "only y" is interpreted to mean "only x&y"). - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB - Compilation of all SIMD code can be suppressed with #define STBI_NO_SIMD It should not be necessary to disable SIMD unless you have issues compiling (e.g. using an x86 compiler which doesn't support SSE intrinsics or that doesn't support the method used to detect SSE2 support at run-time), and even those can be reported as bugs so I can refine the built-in compile-time checking to be smarter. - The old STBI_SIMD system which allowed installing a user-defined IDCT etc. has been removed. If you need this, don't upgrade. My assumption is that almost nobody was doing this, and those who were will find the built-in SIMD more satisfactory anyway. - RGB values computed for JPEG images are slightly different from previous versions of stb_image. (This is due to using less integer precision in SIMD.) The C code has been adjusted so that the same RGB values will be computed regardless of whether SIMD support is available, so your app should always produce consistent results. But these results are slightly different from previous versions. (Specifically, about 3% of available YCbCr values will compute different RGB results from pre-1.49 versions by +-1; most of the deviating values are one smaller in the G channel.) - If you must produce consistent results with previous versions of stb_image, #define STBI_JPEG_OLD and you will get the same results you used to; however, you will not get the SIMD speedups for the YCbCr-to-RGB conversion step (although you should still see significant JPEG speedup from the other changes). Please note that STBI_JPEG_OLD is a temporary feature; it will be removed in future versions of the library. It is only intended for near-term back-compatibility use. Latest revision history: 2.02 (2015-01-19) fix incorrect assert, fix warning 2.01 (2015-01-17) fix various warnings 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD progressive JPEG PGM/PPM support STBI_MALLOC,STBI_REALLOC,STBI_FREE STBI_NO_*, STBI_ONLY_* GIF bugfix 1.48 (2014-12-14) fix incorrectly-named assert() 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted) optimize PNG fix bug in interlaced PNG with user-specified channel count 1.46 (2014-08-26) fix broken tRNS chunk in non-paletted PNG 1.45 (2014-08-16) workaround MSVC-ARM internal compiler error by wrapping malloc See end of file for full revision history. ============================ Contributors ========================= Image formats Bug fixes & warning fixes Sean Barrett (jpeg, png, bmp) Marc LeBlanc Nicolas Schulz (hdr, psd) Christpher Lloyd Jonathan Dummer (tga) Dave Moore Jean-Marc Lienher (gif) Won Chun Tom Seddon (pic) the Horde3D community Thatcher Ulrich (psd) Janez Zemva Ken Miller (pgm, ppm) Jonathan Blow Laurent Gomila Aruelien Pocheville Extensions, features Ryamond Barbiero Jetro Lauha (stbi_info) David Woo Martin "SpartanJ" Golini (stbi_info) Martin Golini James "moose2000" Brown (iPhone PNG) Roy Eltham Ben "Disch" Wenger (io callbacks) Luke Graham Omar Cornut (1/2/4-bit PNG) Thomas Ruf John Bartholomew Ken Hamada Optimizations & bugfixes Cort Stratton Fabian "ryg" Giesen Blazej Dariusz Roszkowski Arseny Kapoulkine Thibault Reuille Paul Du Bois Guillaume George If your name should be here but Jerry Jansson isn't, let Sean know. Hayaki Saito Johan Duparc Ronny Chevalier Michal Cichon Tero Hanninen Sergio Gonzalez Cass Everitt Engin Manap License: This software is in the public domain. Where that dedication is not recognized, you are granted a perpetual, irrevocable license to copy and modify this file however you want. */ #ifndef STBI_INCLUDE_STB_IMAGE_H #define STBI_INCLUDE_STB_IMAGE_H // DOCUMENTATION // // Limitations: // - no 16-bit-per-channel PNG // - no 12-bit-per-channel JPEG // - no JPEGs with arithmetic coding // - no 1-bit BMP // - GIF always returns *comp=4 // // Basic usage (see HDR discussion below for HDR usage): // int x,y,n; // unsigned char *data = stbi_load(filename, &x, &y, &n, 0); // // ... process data if not NULL ... // // ... x = width, y = height, n = # 8-bit components per pixel ... // // ... replace '0' with '1'..'4' to force that many components per pixel // // ... but 'n' will always be the number that it would have been if you said 0 // stbi_image_free(data) // // Standard parameters: // int *x -- outputs image width in pixels // int *y -- outputs image height in pixels // int *comp -- outputs # of image components in image file // int req_comp -- if non-zero, # of image components requested in result // // The return value from an image loader is an 'unsigned char *' which points // to the pixel data, or NULL on an allocation failure or if the image is // corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, // with each pixel consisting of N interleaved 8-bit components; the first // pixel pointed to is top-left-most in the image. There is no padding between // image scanlines or between pixels, regardless of format. The number of // components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. // If req_comp is non-zero, *comp has the number of components that _would_ // have been output otherwise. E.g. if you set req_comp to 4, you will always // get RGBA output, but you can check *comp to see if it's trivially opaque // because e.g. there were only 3 channels in the source image. // // An output image with N components has the following components interleaved // in this order in each pixel: // // N=#comp components // 1 grey // 2 grey, alpha // 3 red, green, blue // 4 red, green, blue, alpha // // If image loading fails for any reason, the return value will be NULL, // and *x, *y, *comp will be unchanged. The function stbi_failure_reason() // can be queried for an extremely brief, end-user unfriendly explanation // of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid // compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly // more user-friendly ones. // // Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. // // =========================================================================== // // Philosophy // // stb libraries are designed with the following priorities: // // 1. easy to use // 2. easy to maintain // 3. good performance // // Sometimes I let "good performance" creep up in priority over "easy to maintain", // and for best performance I may provide less-easy-to-use APIs that give higher // performance, in addition to the easy to use ones. Nevertheless, it's important // to keep in mind that from the standpoint of you, a client of this library, // all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. // // Some secondary priorities arise directly from the first two, some of which // make more explicit reasons why performance can't be emphasized. // // - Portable ("ease of use") // - Small footprint ("easy to maintain") // - No dependencies ("ease of use") // // =========================================================================== // // I/O callbacks // // I/O callbacks allow you to read from arbitrary sources, like packaged // files or some other source. Data read from callbacks are processed // through a small internal buffer (currently 128 bytes) to try to reduce // overhead. // // The three functions you must define are "read" (reads some bytes of data), // "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). // // =========================================================================== // // SIMD support // // The JPEG decoder will try to automatically use SIMD kernels on x86 when // supported by the compiler. For ARM Neon support, you must explicitly // request it. // // (The old do-it-yourself SIMD API is no longer supported in the current // code.) // // On x86, SSE2 will automatically be used when available based on a run-time // test; if not, the generic C versions are used as a fall-back. On ARM targets, // the typical path is to have separate builds for NEON and non-NEON devices // (at least this is true for iOS and Android). Therefore, the NEON support is // toggled by a build flag: define STBI_NEON to get NEON loops. // // The output of the JPEG decoder is slightly different from versions where // SIMD support was introduced (that is, for versions before 1.49). The // difference is only +-1 in the 8-bit RGB channels, and only on a small // fraction of pixels. You can force the pre-1.49 behavior by defining // STBI_JPEG_OLD, but this will disable some of the SIMD decoding path // and hence cost some performance. // // If for some reason you do not want to use any of SIMD code, or if // you have issues compiling it, you can disable it entirely by // defining STBI_NO_SIMD. // // =========================================================================== // // HDR image support (disable by defining STBI_NO_HDR) // // stb_image now supports loading HDR images in general, and currently // the Radiance .HDR file format, although the support is provided // generically. You can still load any file through the existing interface; // if you attempt to load an HDR file, it will be automatically remapped to // LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; // both of these constants can be reconfigured through this interface: // // stbi_hdr_to_ldr_gamma(2.2f); // stbi_hdr_to_ldr_scale(1.0f); // // (note, do not use _inverse_ constants; stbi_image will invert them // appropriately). // // Additionally, there is a new, parallel interface for loading files as // (linear) floats to preserve the full dynamic range: // // float *data = stbi_loadf(filename, &x, &y, &n, 0); // // If you load LDR images through this interface, those images will // be promoted to floating point values, run through the inverse of // constants corresponding to the above: // // stbi_ldr_to_hdr_scale(1.0f); // stbi_ldr_to_hdr_gamma(2.2f); // // Finally, given a filename (or an open file or memory block--see header // file for details) containing image data, you can query for the "most // appropriate" interface to use (that is, whether the image is HDR or // not), using: // // stbi_is_hdr(char *filename); // // =========================================================================== // // iPhone PNG support: // // By default we convert iphone-formatted PNGs back to RGB, even though // they are internally encoded differently. You can disable this conversion // by by calling stbi_convert_iphone_png_to_rgb(0), in which case // you will always just get the native iphone "format" through (which // is BGR stored in RGB). // // Call stbi_set_unpremultiply_on_load(1) as well to force a divide per // pixel to remove any premultiplied alpha *only* if the image file explicitly // says there's premultiplied data (currently only happens in iPhone images, // and only if iPhone convert-to-rgb processing is on). // #ifndef STBI_NO_STDIO #include #endif // STBI_NO_STDIO #define STBI_VERSION 1 enum { STBI_default = 0, // only used for req_comp STBI_grey = 1, STBI_grey_alpha = 2, STBI_rgb = 3, STBI_rgb_alpha = 4 }; typedef unsigned char stbi_uc; #ifdef __cplusplus extern "C" { #endif #ifdef STB_IMAGE_STATIC #define STBIDEF static #else #define STBIDEF extern #endif ////////////////////////////////////////////////////////////////////////////// // // PRIMARY API - works on images of any type // // // load image by filename, open file, or memory buffer // typedef struct { int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative int (*eof) (void *user); // returns nonzero if we are at end of file/data } stbi_io_callbacks; STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); #ifndef STBI_NO_STDIO STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); // for stbi_load_from_file, file pointer is left pointing immediately after image #endif #ifndef STBI_NO_LINEAR STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); #ifndef STBI_NO_STDIO STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); #endif #endif #ifndef STBI_NO_HDR STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); STBIDEF void stbi_hdr_to_ldr_scale(float scale); #endif #ifndef STBI_NO_LINEAR STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); STBIDEF void stbi_ldr_to_hdr_scale(float scale); #endif // STBI_NO_HDR // stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); #ifndef STBI_NO_STDIO STBIDEF int stbi_is_hdr (char const *filename); STBIDEF int stbi_is_hdr_from_file(FILE *f); #endif // STBI_NO_STDIO // get a VERY brief reason for failure // NOT THREADSAFE STBIDEF const char *stbi_failure_reason (void); // free the loaded image -- this is just free() STBIDEF void stbi_image_free (void *retval_from_stbi_load); // get image dimensions & components without fully decoding STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); #ifndef STBI_NO_STDIO STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); #endif // for image formats that explicitly notate that they have premultiplied alpha, // we just return the colors as stored in the file. set this flag to force // unpremultiplication. results are undefined if the unpremultiply overflow. STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); // indicate whether we should process iphone images back to canonical format, // or just pass them through "as-is" STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); // ZLIB client - used by PNG, available for other purposes STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); #ifdef __cplusplus } #endif // // //// end header file ///////////////////////////////////////////////////// #endif // STBI_INCLUDE_STB_IMAGE_H #ifdef STB_IMAGE_IMPLEMENTATION #if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ || defined(STBI_ONLY_ZLIB) #ifndef STBI_ONLY_JPEG #define STBI_NO_JPEG #endif #ifndef STBI_ONLY_PNG #define STBI_NO_PNG #endif #ifndef STBI_ONLY_BMP #define STBI_NO_BMP #endif #ifndef STBI_ONLY_PSD #define STBI_NO_PSD #endif #ifndef STBI_ONLY_TGA #define STBI_NO_TGA #endif #ifndef STBI_ONLY_GIF #define STBI_NO_GIF #endif #ifndef STBI_ONLY_HDR #define STBI_NO_HDR #endif #ifndef STBI_ONLY_PIC #define STBI_NO_PIC #endif #ifndef STBI_ONLY_PNM #define STBI_NO_PNM #endif #endif #if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) #define STBI_NO_ZLIB #endif #include #include // ptrdiff_t on osx #include #include #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) #include // ldexp #endif #ifndef STBI_NO_STDIO #include #endif #ifndef STBI_ASSERT #include #define STBI_ASSERT(x) assert(x) #endif #ifndef _MSC_VER #ifdef __cplusplus #define stbi_inline inline #else #define stbi_inline #endif #else #define stbi_inline __forceinline #endif #ifdef _MSC_VER typedef unsigned short stbi__uint16; typedef signed short stbi__int16; typedef unsigned int stbi__uint32; typedef signed int stbi__int32; #else #include typedef uint16_t stbi__uint16; typedef int16_t stbi__int16; typedef uint32_t stbi__uint32; typedef int32_t stbi__int32; #endif // should produce compiler error if size is wrong typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #ifdef _MSC_VER #define STBI_NOTUSED(v) (void)(v) #else #define STBI_NOTUSED(v) (void)sizeof(v) #endif #ifdef _MSC_VER #define STBI_HAS_LROTL #endif #ifdef STBI_HAS_LROTL #define stbi_lrot(x,y) _lrotl(x,y) #else #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) #endif #if defined(STBI_MALLOC) && defined(STBI_FREE) && defined(STBI_REALLOC) // ok #elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) // ok #else #error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC." #endif #ifndef STBI_MALLOC #define STBI_MALLOC(sz) malloc(sz) #define STBI_REALLOC(p,sz) realloc(p,sz) #define STBI_FREE(p) free(p) #endif #if defined(__GNUC__) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) // gcc doesn't support sse2 intrinsics unless you compile with -msse2, // (but compiling with -msse2 allows the compiler to use SSE2 everywhere; // this is just broken and gcc are jerks for not fixing it properly // http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) #define STBI_NO_SIMD #endif #if !defined(STBI_NO_SIMD) && (defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86)) #define STBI_SSE2 #include #ifdef _MSC_VER #if _MSC_VER >= 1400 // not VC6 #include // __cpuid static int stbi__cpuid3(void) { int info[4]; __cpuid(info,1); return info[3]; } #else static int stbi__cpuid3(void) { int res; __asm { mov eax,1 cpuid mov res,edx } return res; } #endif #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name static int stbi__sse2_available() { int info3 = stbi__cpuid3(); return ((info3 >> 26) & 1) != 0; } #else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) static int stbi__sse2_available() { #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later // GCC 4.8+ has a nice way to do this return __builtin_cpu_supports("sse2"); #else // portable way to do this, preferably without using GCC inline ASM? // just bail for now. return 0; #endif } #endif #endif // ARM NEON #if defined(STBI_NO_SIMD) && defined(STBI_NEON) #undef STBI_NEON #endif #ifdef STBI_NEON #include // assume GCC or Clang on ARM targets #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) #endif #ifndef STBI_SIMD_ALIGN #define STBI_SIMD_ALIGN(type, name) type name #endif /////////////////////////////////////////////// // // stbi__context struct and start_xxx functions // stbi__context structure is our basic context used by all images, so it // contains all the IO context, plus some basic image information typedef struct { stbi__uint32 img_x, img_y; int img_n, img_out_n; stbi_io_callbacks io; void *io_user_data; int read_from_callbacks; int buflen; stbi_uc buffer_start[128]; stbi_uc *img_buffer, *img_buffer_end; stbi_uc *img_buffer_original; } stbi__context; static void stbi__refill_buffer(stbi__context *s); // initialize a memory-decode context static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) { s->io.read = NULL; s->read_from_callbacks = 0; s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; s->img_buffer_end = (stbi_uc *) buffer+len; } // initialize a callback-based context static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) { s->io = *c; s->io_user_data = user; s->buflen = sizeof(s->buffer_start); s->read_from_callbacks = 1; s->img_buffer_original = s->buffer_start; stbi__refill_buffer(s); } #ifndef STBI_NO_STDIO static int stbi__stdio_read(void *user, char *data, int size) { return (int) fread(data,1,size,(FILE*) user); } static void stbi__stdio_skip(void *user, int n) { fseek((FILE*) user, n, SEEK_CUR); } static int stbi__stdio_eof(void *user) { return feof((FILE*) user); } static stbi_io_callbacks stbi__stdio_callbacks = { stbi__stdio_read, stbi__stdio_skip, stbi__stdio_eof, }; static void stbi__start_file(stbi__context *s, FILE *f) { stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); } //static void stop_file(stbi__context *s) { } #endif // !STBI_NO_STDIO static void stbi__rewind(stbi__context *s) { // conceptually rewind SHOULD rewind to the beginning of the stream, // but we just rewind to the beginning of the initial buffer, because // we only use it after doing 'test', which only ever looks at at most 92 bytes s->img_buffer = s->img_buffer_original; } #ifndef STBI_NO_JPEG static int stbi__jpeg_test(stbi__context *s); static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNG static int stbi__png_test(stbi__context *s); static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_BMP static int stbi__bmp_test(stbi__context *s); static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_TGA static int stbi__tga_test(stbi__context *s); static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PSD static int stbi__psd_test(stbi__context *s); static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_HDR static int stbi__hdr_test(stbi__context *s); static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PIC static int stbi__pic_test(stbi__context *s); static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_GIF static int stbi__gif_test(stbi__context *s); static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNM static int stbi__pnm_test(stbi__context *s); static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); #endif // this is not threadsafe static const char *stbi__g_failure_reason; STBIDEF const char *stbi_failure_reason(void) { return stbi__g_failure_reason; } static int stbi__err(const char *str) { stbi__g_failure_reason = str; return 0; } static void *stbi__malloc(size_t size) { return STBI_MALLOC(size); } // stbi__err - error // stbi__errpf - error returning pointer to float // stbi__errpuc - error returning pointer to unsigned char #ifdef STBI_NO_FAILURE_STRINGS #define stbi__err(x,y) 0 #elif defined(STBI_FAILURE_USERMSG) #define stbi__err(x,y) stbi__err(y) #else #define stbi__err(x,y) stbi__err(x) #endif #define stbi__errpf(x,y) ((float *) (stbi__err(x,y)?NULL:NULL)) #define stbi__errpuc(x,y) ((unsigned char *) (stbi__err(x,y)?NULL:NULL)) STBIDEF void stbi_image_free(void *retval_from_stbi_load) { STBI_FREE(retval_from_stbi_load); } #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); #endif #ifndef STBI_NO_HDR static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); #endif static unsigned char *stbi_load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) { #ifndef STBI_NO_JPEG if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); #endif #ifndef STBI_NO_PNG if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); #endif #ifndef STBI_NO_BMP if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); #endif #ifndef STBI_NO_GIF if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); #endif #ifndef STBI_NO_PSD if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); #endif #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); #endif #ifndef STBI_NO_PNM if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); #endif #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); } #endif #ifndef STBI_NO_TGA // test tga last because it's a crappy test! if (stbi__tga_test(s)) return stbi__tga_load(s,x,y,comp,req_comp); #endif return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } #ifndef STBI_NO_STDIO static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; #if defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) f=0; #else f = fopen(filename, mode); #endif return f; } STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) { FILE *f = stbi__fopen(filename, "rb"); unsigned char *result; if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); result = stbi_load_from_file(f,x,y,comp,req_comp); fclose(f); return result; } STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { unsigned char *result; stbi__context s; stbi__start_file(&s,f); result = stbi_load_main(&s,x,y,comp,req_comp); if (result) { // need to 'unget' all the characters in the IO buffer fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); } return result; } #endif //!STBI_NO_STDIO STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi_load_main(&s,x,y,comp,req_comp); } STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi_load_main(&s,x,y,comp,req_comp); } #ifndef STBI_NO_LINEAR static float *stbi_loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) { unsigned char *data; #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) return stbi__hdr_load(s,x,y,comp,req_comp); #endif data = stbi_load_main(s, x, y, comp, req_comp); if (data) return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); } STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi_loadf_main(&s,x,y,comp,req_comp); } STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi_loadf_main(&s,x,y,comp,req_comp); } #ifndef STBI_NO_STDIO STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) { float *result; FILE *f = stbi__fopen(filename, "rb"); if (!f) return stbi__errpf("can't fopen", "Unable to open file"); result = stbi_loadf_from_file(f,x,y,comp,req_comp); fclose(f); return result; } STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_file(&s,f); return stbi_loadf_main(&s,x,y,comp,req_comp); } #endif // !STBI_NO_STDIO #endif // !STBI_NO_LINEAR // these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is // defined, for API simplicity; if STBI_NO_LINEAR is defined, it always // reports false! STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) { #ifndef STBI_NO_HDR stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__hdr_test(&s); #else STBI_NOTUSED(buffer); STBI_NOTUSED(len); return 0; #endif } #ifndef STBI_NO_STDIO STBIDEF int stbi_is_hdr (char const *filename) { FILE *f = stbi__fopen(filename, "rb"); int result=0; if (f) { result = stbi_is_hdr_from_file(f); fclose(f); } return result; } STBIDEF int stbi_is_hdr_from_file(FILE *f) { #ifndef STBI_NO_HDR stbi__context s; stbi__start_file(&s,f); return stbi__hdr_test(&s); #else return 0; #endif } #endif // !STBI_NO_STDIO STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) { #ifndef STBI_NO_HDR stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi__hdr_test(&s); #else return 0; #endif } static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; #ifndef STBI_NO_LINEAR STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } #endif STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } ////////////////////////////////////////////////////////////////////////////// // // Common code used by all image loaders // enum { STBI__SCAN_load=0, STBI__SCAN_type, STBI__SCAN_header }; static void stbi__refill_buffer(stbi__context *s) { int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); if (n == 0) { // at end of file, treat same as if from memory, but need to handle case // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file s->read_from_callbacks = 0; s->img_buffer = s->buffer_start; s->img_buffer_end = s->buffer_start+1; *s->img_buffer = 0; } else { s->img_buffer = s->buffer_start; s->img_buffer_end = s->buffer_start + n; } } stbi_inline static stbi_uc stbi__get8(stbi__context *s) { if (s->img_buffer < s->img_buffer_end) return *s->img_buffer++; if (s->read_from_callbacks) { stbi__refill_buffer(s); return *s->img_buffer++; } return 0; } stbi_inline static int stbi__at_eof(stbi__context *s) { if (s->io.read) { if (!(s->io.eof)(s->io_user_data)) return 0; // if feof() is true, check if buffer = end // special case: we've only got the special 0 character at the end if (s->read_from_callbacks == 0) return 1; } return s->img_buffer >= s->img_buffer_end; } static void stbi__skip(stbi__context *s, int n) { if (s->io.read) { int blen = (int) (s->img_buffer_end - s->img_buffer); if (blen < n) { s->img_buffer = s->img_buffer_end; (s->io.skip)(s->io_user_data, n - blen); return; } } s->img_buffer += n; } static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) { if (s->io.read) { int blen = (int) (s->img_buffer_end - s->img_buffer); if (blen < n) { int res, count; memcpy(buffer, s->img_buffer, blen); count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); res = (count == (n-blen)); s->img_buffer = s->img_buffer_end; return res; } } if (s->img_buffer+n <= s->img_buffer_end) { memcpy(buffer, s->img_buffer, n); s->img_buffer += n; return 1; } else return 0; } static int stbi__get16be(stbi__context *s) { int z = stbi__get8(s); return (z << 8) + stbi__get8(s); } static stbi__uint32 stbi__get32be(stbi__context *s) { stbi__uint32 z = stbi__get16be(s); return (z << 16) + stbi__get16be(s); } static int stbi__get16le(stbi__context *s) { int z = stbi__get8(s); return z + (stbi__get8(s) << 8); } static stbi__uint32 stbi__get32le(stbi__context *s) { stbi__uint32 z = stbi__get16le(s); return z + (stbi__get16le(s) << 16); } #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings ////////////////////////////////////////////////////////////////////////////// // // generic converter from built-in img_n to req_comp // individual types do this automatically as much as possible (e.g. jpeg // does all cases internally since it needs to colorspace convert anyway, // and it never has alpha, so very few cases ). png can automatically // interleave an alpha=255 channel, but falls back to this for other cases // // assume data buffer is malloced, so malloc a new one and free that one // only failure mode is malloc failing static stbi_uc stbi__compute_y(int r, int g, int b) { return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); } static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; unsigned char *good; if (req_comp == img_n) return data; STBI_ASSERT(req_comp >= 1 && req_comp <= 4); good = (unsigned char *) stbi__malloc(req_comp * x * y); if (good == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } for (j=0; j < (int) y; ++j) { unsigned char *src = data + j * x * img_n ; unsigned char *dest = good + j * x * req_comp; #define COMBO(a,b) ((a)*8+(b)) #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (COMBO(img_n, req_comp)) { CASE(1,2) dest[0]=src[0], dest[1]=255; break; CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; CASE(2,1) dest[0]=src[0]; break; CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; default: STBI_ASSERT(0); } #undef CASE } STBI_FREE(data); return good; } #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) { int i,k,n; float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; for (i=0; i < x*y; ++i) { for (k=0; k < n; ++k) { output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); } if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; } STBI_FREE(data); return output; } #endif #ifndef STBI_NO_HDR #define stbi__float2int(x) ((int) (x)) static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) { int i,k,n; stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; for (i=0; i < x*y; ++i) { for (k=0; k < n; ++k) { float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; if (z < 0) z = 0; if (z > 255) z = 255; output[i*comp + k] = (stbi_uc) stbi__float2int(z); } if (k < comp) { float z = data[i*comp+k] * 255 + 0.5f; if (z < 0) z = 0; if (z > 255) z = 255; output[i*comp + k] = (stbi_uc) stbi__float2int(z); } } STBI_FREE(data); return output; } #endif ////////////////////////////////////////////////////////////////////////////// // // "baseline" JPEG/JFIF decoder // // simple implementation // - doesn't support delayed output of y-dimension // - simple interface (only one output format: 8-bit interleaved RGB) // - doesn't try to recover corrupt jpegs // - doesn't allow partial loading, loading multiple at once // - still fast on x86 (copying globals into locals doesn't help x86) // - allocates lots of intermediate memory (full size of all components) // - non-interleaved case requires this anyway // - allows good upsampling (see next) // high-quality // - upsampled channels are bilinearly interpolated, even across blocks // - quality integer IDCT derived from IJG's 'slow' // performance // - fast huffman; reasonable integer IDCT // - some SIMD kernels for common paths on targets with SSE2/NEON // - uses a lot of intermediate memory, could cache poorly #ifndef STBI_NO_JPEG // huffman decoding acceleration #define FAST_BITS 9 // larger handles more cases; smaller stomps less cache typedef struct { stbi_uc fast[1 << FAST_BITS]; // weirdly, repacking this into AoS is a 10% speed loss, instead of a win stbi__uint16 code[256]; stbi_uc values[256]; stbi_uc size[257]; unsigned int maxcode[18]; int delta[17]; // old 'firstsymbol' - old 'firstcode' } stbi__huffman; typedef struct { stbi__context *s; stbi__huffman huff_dc[4]; stbi__huffman huff_ac[4]; stbi_uc dequant[4][64]; stbi__int16 fast_ac[4][1 << FAST_BITS]; // sizes for components, interleaved MCUs int img_h_max, img_v_max; int img_mcu_x, img_mcu_y; int img_mcu_w, img_mcu_h; // definition of jpeg image component struct { int id; int h,v; int tq; int hd,ha; int dc_pred; int x,y,w2,h2; stbi_uc *data; void *raw_data, *raw_coeff; stbi_uc *linebuf; short *coeff; // progressive only int coeff_w, coeff_h; // number of 8x8 coefficient blocks } img_comp[4]; stbi__uint32 code_buffer; // jpeg entropy-coded buffer int code_bits; // number of valid bits unsigned char marker; // marker seen while filling entropy buffer int nomore; // flag if we saw a marker so must stop int progressive; int spec_start; int spec_end; int succ_high; int succ_low; int eob_run; int scan_n, order[4]; int restart_interval, todo; // kernels void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); } stbi__jpeg; static int stbi__build_huffman(stbi__huffman *h, int *count) { int i,j,k=0,code; // build size list for each symbol (from JPEG spec) for (i=0; i < 16; ++i) for (j=0; j < count[i]; ++j) h->size[k++] = (stbi_uc) (i+1); h->size[k] = 0; // compute actual symbols (from jpeg spec) code = 0; k = 0; for(j=1; j <= 16; ++j) { // compute delta to add to code to compute symbol id h->delta[j] = k - code; if (h->size[k] == j) { while (h->size[k] == j) h->code[k++] = (stbi__uint16) (code++); if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); } // compute largest code + 1 for this size, preshifted as needed later h->maxcode[j] = code << (16-j); code <<= 1; } h->maxcode[j] = 0xffffffff; // build non-spec acceleration table; 255 is flag for not-accelerated memset(h->fast, 255, 1 << FAST_BITS); for (i=0; i < k; ++i) { int s = h->size[i]; if (s <= FAST_BITS) { int c = h->code[i] << (FAST_BITS-s); int m = 1 << (FAST_BITS-s); for (j=0; j < m; ++j) { h->fast[c+j] = (stbi_uc) i; } } } return 1; } // build a table that decodes both magnitude and value of small ACs in // one go. static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) { int i; for (i=0; i < (1 << FAST_BITS); ++i) { stbi_uc fast = h->fast[i]; fast_ac[i] = 0; if (fast < 255) { int rs = h->values[fast]; int run = (rs >> 4) & 15; int magbits = rs & 15; int len = h->size[fast]; if (magbits && len + magbits <= FAST_BITS) { // magnitude code followed by receive_extend code int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); int m = 1 << (magbits - 1); if (k < m) k += (-1 << magbits) + 1; // if the result is small enough, we can fit it in fast_ac table if (k >= -128 && k <= 127) fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); } } } } static void stbi__grow_buffer_unsafe(stbi__jpeg *j) { do { int b = j->nomore ? 0 : stbi__get8(j->s); if (b == 0xff) { int c = stbi__get8(j->s); if (c != 0) { j->marker = (unsigned char) c; j->nomore = 1; return; } } j->code_buffer |= b << (24 - j->code_bits); j->code_bits += 8; } while (j->code_bits <= 24); } // (1 << n) - 1 static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; // decode a jpeg huffman value from the bitstream stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) { unsigned int temp; int c,k; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); // look at the top FAST_BITS and determine what symbol ID it is, // if the code is <= FAST_BITS c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); k = h->fast[c]; if (k < 255) { int s = h->size[k]; if (s > j->code_bits) return -1; j->code_buffer <<= s; j->code_bits -= s; return h->values[k]; } // naive test is to shift the code_buffer down so k bits are // valid, then test against maxcode. To speed this up, we've // preshifted maxcode left so that it has (16-k) 0s at the // end; in other words, regardless of the number of bits, it // wants to be compared against something shifted to have 16; // that way we don't need to shift inside the loop. temp = j->code_buffer >> 16; for (k=FAST_BITS+1 ; ; ++k) if (temp < h->maxcode[k]) break; if (k == 17) { // error! code not found j->code_bits -= 16; return -1; } if (k > j->code_bits) return -1; // convert the huffman code to the symbol id c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); // convert the id to a symbol j->code_bits -= k; j->code_buffer <<= k; return h->values[c]; } // bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB k = stbi_lrot(j->code_buffer, n); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; return k + (stbi__jbias[n] & ~sgn); } // get some unsigned bits stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { unsigned int k; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); k = stbi_lrot(j->code_buffer, n); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; return k; } stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) { unsigned int k; if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); k = j->code_buffer; j->code_buffer <<= 1; --j->code_bits; return k & 0x80000000; } // given a value that's at position X in the zigzag stream, // where does it appear in the 8x8 matrix coded as row-major? static stbi_uc stbi__jpeg_dezigzag[64+15] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, // let corrupt input sample past end 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }; // decode one 64-entry block-- static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi_uc *dequant) { int diff,dc,k; int t; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); t = stbi__jpeg_huff_decode(j, hdc); if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); // 0 all the ac values now so we can do it 32-bits at a time memset(data,0,64*sizeof(data[0])); diff = t ? stbi__extend_receive(j, t) : 0; dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; data[0] = (short) (dc * dequant[0]); // decode AC components, see JPEG spec k = 1; do { unsigned int zig; int c,r,s; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); r = fac[c]; if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length j->code_buffer <<= s; j->code_bits -= s; // decode into unzigzag'd location zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) ((r >> 8) * dequant[zig]); } else { int rs = stbi__jpeg_huff_decode(j, hac); if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); s = rs & 15; r = rs >> 4; if (s == 0) { if (rs != 0xf0) break; // end block k += 16; } else { k += r; // decode into unzigzag'd location zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); } } } while (k < 64); return 1; } static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) { int diff,dc; int t; if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); if (j->succ_high == 0) { // first scan for DC coefficient, must be first memset(data,0,64*sizeof(data[0])); // 0 all the ac values now t = stbi__jpeg_huff_decode(j, hdc); diff = t ? stbi__extend_receive(j, t) : 0; dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; data[0] = (short) (dc << j->succ_low); } else { // refinement scan for DC coefficient if (stbi__jpeg_get_bit(j)) data[0] += (short) (1 << j->succ_low); } return 1; } // @OPTIMIZE: store non-zigzagged during the decode passes, // and only de-zigzag when dequantizing static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) { int k; if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); if (j->succ_high == 0) { int shift = j->succ_low; if (j->eob_run) { --j->eob_run; return 1; } k = j->spec_start; do { unsigned int zig; int c,r,s; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); r = fac[c]; if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length j->code_buffer <<= s; j->code_bits -= s; zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) ((r >> 8) << shift); } else { int rs = stbi__jpeg_huff_decode(j, hac); if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); s = rs & 15; r = rs >> 4; if (s == 0) { if (r < 15) { j->eob_run = (1 << r); if (r) j->eob_run += stbi__jpeg_get_bits(j, r); --j->eob_run; break; } k += 16; } else { k += r; zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) (stbi__extend_receive(j,s) << shift); } } } while (k <= j->spec_end); } else { // refinement scan for these AC coefficients short bit = (short) (1 << j->succ_low); if (j->eob_run) { --j->eob_run; for (k = j->spec_start; k <= j->spec_end; ++k) { short *p = &data[stbi__jpeg_dezigzag[k]]; if (*p != 0) if (stbi__jpeg_get_bit(j)) if ((*p & bit)==0) { if (*p > 0) *p += bit; else *p -= bit; } } } else { k = j->spec_start; do { int r,s; int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); s = rs & 15; r = rs >> 4; if (s == 0) { if (r < 15) { j->eob_run = (1 << r) - 1; if (r) j->eob_run += stbi__jpeg_get_bits(j, r); r = 64; // force end of block } else r = 16; // r=15 is the code for 16 0s } else { if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); // sign bit if (stbi__jpeg_get_bit(j)) s = bit; else s = -bit; } // advance by r while (k <= j->spec_end) { short *p = &data[stbi__jpeg_dezigzag[k]]; if (*p != 0) { if (stbi__jpeg_get_bit(j)) if ((*p & bit)==0) { if (*p > 0) *p += bit; else *p -= bit; } ++k; } else { if (r == 0) { if (s) data[stbi__jpeg_dezigzag[k++]] = (short) s; break; } --r; ++k; } } } while (k <= j->spec_end); } } return 1; } // take a -128..127 value and stbi__clamp it and convert to 0..255 stbi_inline static stbi_uc stbi__clamp(int x) { // trick to use a single test to catch both cases if ((unsigned int) x > 255) { if (x < 0) return 0; if (x > 255) return 255; } return (stbi_uc) x; } #define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) #define stbi__fsh(x) ((x) << 12) // derived from jidctint -- DCT_ISLOW #define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ p2 = s2; \ p3 = s6; \ p1 = (p2+p3) * stbi__f2f(0.5411961f); \ t2 = p1 + p3*stbi__f2f(-1.847759065f); \ t3 = p1 + p2*stbi__f2f( 0.765366865f); \ p2 = s0; \ p3 = s4; \ t0 = stbi__fsh(p2+p3); \ t1 = stbi__fsh(p2-p3); \ x0 = t0+t3; \ x3 = t0-t3; \ x1 = t1+t2; \ x2 = t1-t2; \ t0 = s7; \ t1 = s5; \ t2 = s3; \ t3 = s1; \ p3 = t0+t2; \ p4 = t1+t3; \ p1 = t0+t3; \ p2 = t1+t2; \ p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ t0 = t0*stbi__f2f( 0.298631336f); \ t1 = t1*stbi__f2f( 2.053119869f); \ t2 = t2*stbi__f2f( 3.072711026f); \ t3 = t3*stbi__f2f( 1.501321110f); \ p1 = p5 + p1*stbi__f2f(-0.899976223f); \ p2 = p5 + p2*stbi__f2f(-2.562915447f); \ p3 = p3*stbi__f2f(-1.961570560f); \ p4 = p4*stbi__f2f(-0.390180644f); \ t3 += p1+p4; \ t2 += p2+p3; \ t1 += p2+p4; \ t0 += p1+p3; static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) { int i,val[64],*v=val; stbi_uc *o; short *d = data; // columns for (i=0; i < 8; ++i,++d, ++v) { // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 && d[40]==0 && d[48]==0 && d[56]==0) { // no shortcut 0 seconds // (1|2|3|4|5|6|7)==0 0 seconds // all separate -0.047 seconds // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds int dcterm = d[0] << 2; v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; } else { STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) // constants scaled things up by 1<<12; let's bring them back // down, but keep 2 extra bits of precision x0 += 512; x1 += 512; x2 += 512; x3 += 512; v[ 0] = (x0+t3) >> 10; v[56] = (x0-t3) >> 10; v[ 8] = (x1+t2) >> 10; v[48] = (x1-t2) >> 10; v[16] = (x2+t1) >> 10; v[40] = (x2-t1) >> 10; v[24] = (x3+t0) >> 10; v[32] = (x3-t0) >> 10; } } for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { // no fast case since the first 1D IDCT spread components out STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) // constants scaled things up by 1<<12, plus we had 1<<2 from first // loop, plus horizontal and vertical each scale by sqrt(8) so together // we've got an extra 1<<3, so 1<<17 total we need to remove. // so we want to round that, which means adding 0.5 * 1<<17, // aka 65536. Also, we'll end up with -128 to 127 that we want // to encode as 0..255 by adding 128, so we'll add that before the shift x0 += 65536 + (128<<17); x1 += 65536 + (128<<17); x2 += 65536 + (128<<17); x3 += 65536 + (128<<17); // tried computing the shifts into temps, or'ing the temps to see // if any were out of range, but that was slower o[0] = stbi__clamp((x0+t3) >> 17); o[7] = stbi__clamp((x0-t3) >> 17); o[1] = stbi__clamp((x1+t2) >> 17); o[6] = stbi__clamp((x1-t2) >> 17); o[2] = stbi__clamp((x2+t1) >> 17); o[5] = stbi__clamp((x2-t1) >> 17); o[3] = stbi__clamp((x3+t0) >> 17); o[4] = stbi__clamp((x3-t0) >> 17); } } #ifdef STBI_SSE2 // sse2 integer IDCT. not the fastest possible implementation but it // produces bit-identical results to the generic C version so it's // fully "transparent". static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { // This is constructed to match our regular (generic) integer IDCT exactly. __m128i row0, row1, row2, row3, row4, row5, row6, row7; __m128i tmp; // dot product constant: even elems=x, odd elems=y #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) // out(1) = c1[even]*x + c1[odd]*y #define dct_rot(out0,out1, x,y,c0,c1) \ __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) // out = in << 12 (in 16-bit, out 32-bit) #define dct_widen(out, in) \ __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) // wide add #define dct_wadd(out, a, b) \ __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ __m128i out##_h = _mm_add_epi32(a##_h, b##_h) // wide sub #define dct_wsub(out, a, b) \ __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) // butterfly a/b, add bias, then shift by "s" and pack #define dct_bfly32o(out0, out1, a,b,bias,s) \ { \ __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ dct_wadd(sum, abiased, b); \ dct_wsub(dif, abiased, b); \ out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ } // 8-bit interleave step (for transposes) #define dct_interleave8(a, b) \ tmp = a; \ a = _mm_unpacklo_epi8(a, b); \ b = _mm_unpackhi_epi8(tmp, b) // 16-bit interleave step (for transposes) #define dct_interleave16(a, b) \ tmp = a; \ a = _mm_unpacklo_epi16(a, b); \ b = _mm_unpackhi_epi16(tmp, b) #define dct_pass(bias,shift) \ { \ /* even part */ \ dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ __m128i sum04 = _mm_add_epi16(row0, row4); \ __m128i dif04 = _mm_sub_epi16(row0, row4); \ dct_widen(t0e, sum04); \ dct_widen(t1e, dif04); \ dct_wadd(x0, t0e, t3e); \ dct_wsub(x3, t0e, t3e); \ dct_wadd(x1, t1e, t2e); \ dct_wsub(x2, t1e, t2e); \ /* odd part */ \ dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ __m128i sum17 = _mm_add_epi16(row1, row7); \ __m128i sum35 = _mm_add_epi16(row3, row5); \ dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ dct_wadd(x4, y0o, y4o); \ dct_wadd(x5, y1o, y5o); \ dct_wadd(x6, y2o, y5o); \ dct_wadd(x7, y3o, y4o); \ dct_bfly32o(row0,row7, x0,x7,bias,shift); \ dct_bfly32o(row1,row6, x1,x6,bias,shift); \ dct_bfly32o(row2,row5, x2,x5,bias,shift); \ dct_bfly32o(row3,row4, x3,x4,bias,shift); \ } __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); // rounding biases in column/row passes, see stbi__idct_block for explanation. __m128i bias_0 = _mm_set1_epi32(512); __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); // load row0 = _mm_load_si128((const __m128i *) (data + 0*8)); row1 = _mm_load_si128((const __m128i *) (data + 1*8)); row2 = _mm_load_si128((const __m128i *) (data + 2*8)); row3 = _mm_load_si128((const __m128i *) (data + 3*8)); row4 = _mm_load_si128((const __m128i *) (data + 4*8)); row5 = _mm_load_si128((const __m128i *) (data + 5*8)); row6 = _mm_load_si128((const __m128i *) (data + 6*8)); row7 = _mm_load_si128((const __m128i *) (data + 7*8)); // column pass dct_pass(bias_0, 10); { // 16bit 8x8 transpose pass 1 dct_interleave16(row0, row4); dct_interleave16(row1, row5); dct_interleave16(row2, row6); dct_interleave16(row3, row7); // transpose pass 2 dct_interleave16(row0, row2); dct_interleave16(row1, row3); dct_interleave16(row4, row6); dct_interleave16(row5, row7); // transpose pass 3 dct_interleave16(row0, row1); dct_interleave16(row2, row3); dct_interleave16(row4, row5); dct_interleave16(row6, row7); } // row pass dct_pass(bias_1, 17); { // pack __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 __m128i p1 = _mm_packus_epi16(row2, row3); __m128i p2 = _mm_packus_epi16(row4, row5); __m128i p3 = _mm_packus_epi16(row6, row7); // 8bit 8x8 transpose pass 1 dct_interleave8(p0, p2); // a0e0a1e1... dct_interleave8(p1, p3); // c0g0c1g1... // transpose pass 2 dct_interleave8(p0, p1); // a0c0e0g0... dct_interleave8(p2, p3); // b0d0f0h0... // transpose pass 3 dct_interleave8(p0, p2); // a0b0c0d0... dct_interleave8(p1, p3); // a4b4c4d4... // store _mm_storel_epi64((__m128i *) out, p0); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; _mm_storel_epi64((__m128i *) out, p2); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; _mm_storel_epi64((__m128i *) out, p1); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; _mm_storel_epi64((__m128i *) out, p3); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); } #undef dct_const #undef dct_rot #undef dct_widen #undef dct_wadd #undef dct_wsub #undef dct_bfly32o #undef dct_interleave8 #undef dct_interleave16 #undef dct_pass } #endif // STBI_SSE2 #ifdef STBI_NEON // NEON integer IDCT. should produce bit-identical // results to the generic C version. static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); #define dct_long_mul(out, inq, coeff) \ int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) #define dct_long_mac(out, acc, inq, coeff) \ int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) #define dct_widen(out, inq) \ int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) // wide add #define dct_wadd(out, a, b) \ int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ int32x4_t out##_h = vaddq_s32(a##_h, b##_h) // wide sub #define dct_wsub(out, a, b) \ int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ int32x4_t out##_h = vsubq_s32(a##_h, b##_h) // butterfly a/b, then shift using "shiftop" by "s" and pack #define dct_bfly32o(out0,out1, a,b,shiftop,s) \ { \ dct_wadd(sum, a, b); \ dct_wsub(dif, a, b); \ out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ } #define dct_pass(shiftop, shift) \ { \ /* even part */ \ int16x8_t sum26 = vaddq_s16(row2, row6); \ dct_long_mul(p1e, sum26, rot0_0); \ dct_long_mac(t2e, p1e, row6, rot0_1); \ dct_long_mac(t3e, p1e, row2, rot0_2); \ int16x8_t sum04 = vaddq_s16(row0, row4); \ int16x8_t dif04 = vsubq_s16(row0, row4); \ dct_widen(t0e, sum04); \ dct_widen(t1e, dif04); \ dct_wadd(x0, t0e, t3e); \ dct_wsub(x3, t0e, t3e); \ dct_wadd(x1, t1e, t2e); \ dct_wsub(x2, t1e, t2e); \ /* odd part */ \ int16x8_t sum15 = vaddq_s16(row1, row5); \ int16x8_t sum17 = vaddq_s16(row1, row7); \ int16x8_t sum35 = vaddq_s16(row3, row5); \ int16x8_t sum37 = vaddq_s16(row3, row7); \ int16x8_t sumodd = vaddq_s16(sum17, sum35); \ dct_long_mul(p5o, sumodd, rot1_0); \ dct_long_mac(p1o, p5o, sum17, rot1_1); \ dct_long_mac(p2o, p5o, sum35, rot1_2); \ dct_long_mul(p3o, sum37, rot2_0); \ dct_long_mul(p4o, sum15, rot2_1); \ dct_wadd(sump13o, p1o, p3o); \ dct_wadd(sump24o, p2o, p4o); \ dct_wadd(sump23o, p2o, p3o); \ dct_wadd(sump14o, p1o, p4o); \ dct_long_mac(x4, sump13o, row7, rot3_0); \ dct_long_mac(x5, sump24o, row5, rot3_1); \ dct_long_mac(x6, sump23o, row3, rot3_2); \ dct_long_mac(x7, sump14o, row1, rot3_3); \ dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ } // load row0 = vld1q_s16(data + 0*8); row1 = vld1q_s16(data + 1*8); row2 = vld1q_s16(data + 2*8); row3 = vld1q_s16(data + 3*8); row4 = vld1q_s16(data + 4*8); row5 = vld1q_s16(data + 5*8); row6 = vld1q_s16(data + 6*8); row7 = vld1q_s16(data + 7*8); // add DC bias row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); // column pass dct_pass(vrshrn_n_s32, 10); // 16bit 8x8 transpose { // these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. // whether compilers actually get this is another story, sadly. #define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } #define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } #define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } // pass 1 dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 dct_trn16(row2, row3); dct_trn16(row4, row5); dct_trn16(row6, row7); // pass 2 dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 dct_trn32(row1, row3); dct_trn32(row4, row6); dct_trn32(row5, row7); // pass 3 dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 dct_trn64(row1, row5); dct_trn64(row2, row6); dct_trn64(row3, row7); #undef dct_trn16 #undef dct_trn32 #undef dct_trn64 } // row pass // vrshrn_n_s32 only supports shifts up to 16, we need // 17. so do a non-rounding shift of 16 first then follow // up with a rounding shift by 1. dct_pass(vshrn_n_s32, 16); { // pack and round uint8x8_t p0 = vqrshrun_n_s16(row0, 1); uint8x8_t p1 = vqrshrun_n_s16(row1, 1); uint8x8_t p2 = vqrshrun_n_s16(row2, 1); uint8x8_t p3 = vqrshrun_n_s16(row3, 1); uint8x8_t p4 = vqrshrun_n_s16(row4, 1); uint8x8_t p5 = vqrshrun_n_s16(row5, 1); uint8x8_t p6 = vqrshrun_n_s16(row6, 1); uint8x8_t p7 = vqrshrun_n_s16(row7, 1); // again, these can translate into one instruction, but often don't. #define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } #define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } #define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } // sadly can't use interleaved stores here since we only write // 8 bytes to each scan line! // 8x8 8-bit transpose pass 1 dct_trn8_8(p0, p1); dct_trn8_8(p2, p3); dct_trn8_8(p4, p5); dct_trn8_8(p6, p7); // pass 2 dct_trn8_16(p0, p2); dct_trn8_16(p1, p3); dct_trn8_16(p4, p6); dct_trn8_16(p5, p7); // pass 3 dct_trn8_32(p0, p4); dct_trn8_32(p1, p5); dct_trn8_32(p2, p6); dct_trn8_32(p3, p7); // store vst1_u8(out, p0); out += out_stride; vst1_u8(out, p1); out += out_stride; vst1_u8(out, p2); out += out_stride; vst1_u8(out, p3); out += out_stride; vst1_u8(out, p4); out += out_stride; vst1_u8(out, p5); out += out_stride; vst1_u8(out, p6); out += out_stride; vst1_u8(out, p7); #undef dct_trn8_8 #undef dct_trn8_16 #undef dct_trn8_32 } #undef dct_long_mul #undef dct_long_mac #undef dct_widen #undef dct_wadd #undef dct_wsub #undef dct_bfly32o #undef dct_pass } #endif // STBI_NEON #define STBI__MARKER_none 0xff // if there's a pending marker from the entropy stream, return that // otherwise, fetch from the stream and get a marker. if there's no // marker, return 0xff, which is never a valid marker value static stbi_uc stbi__get_marker(stbi__jpeg *j) { stbi_uc x; if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } x = stbi__get8(j->s); if (x != 0xff) return STBI__MARKER_none; while (x == 0xff) x = stbi__get8(j->s); return x; } // in each scan, we'll have scan_n components, and the order // of the components is specified by order[] #define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) // after a restart interval, stbi__jpeg_reset the entropy decoder and // the dc prediction static void stbi__jpeg_reset(stbi__jpeg *j) { j->code_bits = 0; j->code_buffer = 0; j->nomore = 0; j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; j->marker = STBI__MARKER_none; j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; j->eob_run = 0; // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, // since we don't even allow 1<<30 pixels } static int stbi__parse_entropy_coded_data(stbi__jpeg *z) { stbi__jpeg_reset(z); if (!z->progressive) { if (z->scan_n == 1) { int i,j; STBI_SIMD_ALIGN(short, data[64]); int n = z->order[0]; // non-interleaved data, we just need to process one block at a time, // in trivial scanline order // number of blocks to do just depends on how many actual "pixels" this // component has, independent of interleaved MCU blocking and such int w = (z->img_comp[n].x+7) >> 3; int h = (z->img_comp[n].y+7) >> 3; for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { int ha = z->img_comp[n].ha; if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); // every data block is an MCU, so countdown the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); // if it's NOT a restart, then just bail, so we get corrupt data // rather than no data if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } else { // interleaved int i,j,k,x,y; STBI_SIMD_ALIGN(short, data[64]); for (j=0; j < z->img_mcu_y; ++j) { for (i=0; i < z->img_mcu_x; ++i) { // scan an interleaved mcu... process scan_n components in order for (k=0; k < z->scan_n; ++k) { int n = z->order[k]; // scan out an mcu's worth of this component; that's just determined // by the basic H and V specified for the component for (y=0; y < z->img_comp[n].v; ++y) { for (x=0; x < z->img_comp[n].h; ++x) { int x2 = (i*z->img_comp[n].h + x)*8; int y2 = (j*z->img_comp[n].v + y)*8; int ha = z->img_comp[n].ha; if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); } } } // after all interleaved components, that's an interleaved MCU, // so now count down the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } } else { if (z->scan_n == 1) { int i,j; int n = z->order[0]; // non-interleaved data, we just need to process one block at a time, // in trivial scanline order // number of blocks to do just depends on how many actual "pixels" this // component has, independent of interleaved MCU blocking and such int w = (z->img_comp[n].x+7) >> 3; int h = (z->img_comp[n].y+7) >> 3; for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); if (z->spec_start == 0) { if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) return 0; } else { int ha = z->img_comp[n].ha; if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) return 0; } // every data block is an MCU, so countdown the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } else { // interleaved int i,j,k,x,y; for (j=0; j < z->img_mcu_y; ++j) { for (i=0; i < z->img_mcu_x; ++i) { // scan an interleaved mcu... process scan_n components in order for (k=0; k < z->scan_n; ++k) { int n = z->order[k]; // scan out an mcu's worth of this component; that's just determined // by the basic H and V specified for the component for (y=0; y < z->img_comp[n].v; ++y) { for (x=0; x < z->img_comp[n].h; ++x) { int x2 = (i*z->img_comp[n].h + x); int y2 = (j*z->img_comp[n].v + y); short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) return 0; } } } // after all interleaved components, that's an interleaved MCU, // so now count down the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } } } static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) { int i; for (i=0; i < 64; ++i) data[i] *= dequant[i]; } static void stbi__jpeg_finish(stbi__jpeg *z) { if (z->progressive) { // dequantize and idct the data int i,j,n; for (n=0; n < z->s->img_n; ++n) { int w = (z->img_comp[n].x+7) >> 3; int h = (z->img_comp[n].y+7) >> 3; for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); } } } } } static int stbi__process_marker(stbi__jpeg *z, int m) { int L; switch (m) { case STBI__MARKER_none: // no marker found return stbi__err("expected marker","Corrupt JPEG"); case 0xDD: // DRI - specify restart interval if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); z->restart_interval = stbi__get16be(z->s); return 1; case 0xDB: // DQT - define quantization table L = stbi__get16be(z->s)-2; while (L > 0) { int q = stbi__get8(z->s); int p = q >> 4; int t = q & 15,i; if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); for (i=0; i < 64; ++i) z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); L -= 65; } return L==0; case 0xC4: // DHT - define huffman table L = stbi__get16be(z->s)-2; while (L > 0) { stbi_uc *v; int sizes[16],i,n=0; int q = stbi__get8(z->s); int tc = q >> 4; int th = q & 15; if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); for (i=0; i < 16; ++i) { sizes[i] = stbi__get8(z->s); n += sizes[i]; } L -= 17; if (tc == 0) { if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; v = z->huff_dc[th].values; } else { if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; v = z->huff_ac[th].values; } for (i=0; i < n; ++i) v[i] = stbi__get8(z->s); if (tc != 0) stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); L -= n; } return L==0; } // check for comment block or APP blocks if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { stbi__skip(z->s, stbi__get16be(z->s)-2); return 1; } return 0; } // after we see SOS static int stbi__process_scan_header(stbi__jpeg *z) { int i; int Ls = stbi__get16be(z->s); z->scan_n = stbi__get8(z->s); if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); for (i=0; i < z->scan_n; ++i) { int id = stbi__get8(z->s), which; int q = stbi__get8(z->s); for (which = 0; which < z->s->img_n; ++which) if (z->img_comp[which].id == id) break; if (which == z->s->img_n) return 0; // no match z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); z->order[i] = which; } { int aa; z->spec_start = stbi__get8(z->s); z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 aa = stbi__get8(z->s); z->succ_high = (aa >> 4); z->succ_low = (aa & 15); if (z->progressive) { if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) return stbi__err("bad SOS", "Corrupt JPEG"); } else { if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); z->spec_end = 63; } } return 1; } static int stbi__process_frame_header(stbi__jpeg *z, int scan) { stbi__context *s = z->s; int Lf,p,i,q, h_max=1,v_max=1,c; Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires c = stbi__get8(s); if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires s->img_n = c; for (i=0; i < c; ++i) { z->img_comp[i].data = NULL; z->img_comp[i].linebuf = NULL; } if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); for (i=0; i < s->img_n; ++i) { z->img_comp[i].id = stbi__get8(s); if (z->img_comp[i].id != i+1) // JFIF requires if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! return stbi__err("bad component ID","Corrupt JPEG"); q = stbi__get8(s); z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); } if (scan != STBI__SCAN_load) return 1; if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); for (i=0; i < s->img_n; ++i) { if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; } // compute interleaved mcu info z->img_h_max = h_max; z->img_v_max = v_max; z->img_mcu_w = h_max * 8; z->img_mcu_h = v_max * 8; z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; for (i=0; i < s->img_n; ++i) { // number of effective pixels (e.g. for non-interleaved MCU) z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; // to simplify generation, we'll allocate enough memory to decode // the bogus oversized data from using interleaved MCUs and their // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't // discard the extra data until colorspace conversion z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); if (z->img_comp[i].raw_data == NULL) { for(--i; i >= 0; --i) { STBI_FREE(z->img_comp[i].raw_data); z->img_comp[i].data = NULL; } return stbi__err("outofmem", "Out of memory"); } // align blocks for idct using mmx/sse z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); z->img_comp[i].linebuf = NULL; if (z->progressive) { z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); } else { z->img_comp[i].coeff = 0; z->img_comp[i].raw_coeff = 0; } } return 1; } // use comparisons since in some cases we handle more than one case (e.g. SOF) #define stbi__DNL(x) ((x) == 0xdc) #define stbi__SOI(x) ((x) == 0xd8) #define stbi__EOI(x) ((x) == 0xd9) #define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) #define stbi__SOS(x) ((x) == 0xda) #define stbi__SOF_progressive(x) ((x) == 0xc2) static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) { int m; z->marker = STBI__MARKER_none; // initialize cached marker to empty m = stbi__get_marker(z); if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); if (scan == STBI__SCAN_type) return 1; m = stbi__get_marker(z); while (!stbi__SOF(m)) { if (!stbi__process_marker(z,m)) return 0; m = stbi__get_marker(z); while (m == STBI__MARKER_none) { // some files have extra padding after their blocks, so ok, we'll scan if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); m = stbi__get_marker(z); } } z->progressive = stbi__SOF_progressive(m); if (!stbi__process_frame_header(z, scan)) return 0; return 1; } // decode image to YCbCr format static int stbi__decode_jpeg_image(stbi__jpeg *j) { int m; j->restart_interval = 0; if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; m = stbi__get_marker(j); while (!stbi__EOI(m)) { if (stbi__SOS(m)) { if (!stbi__process_scan_header(j)) return 0; if (!stbi__parse_entropy_coded_data(j)) return 0; if (j->marker == STBI__MARKER_none ) { // handle 0s at the end of image data from IP Kamera 9060 while (!stbi__at_eof(j->s)) { int x = stbi__get8(j->s); if (x == 255) { j->marker = stbi__get8(j->s); break; } else if (x != 0) { return stbi__err("junk before marker", "Corrupt JPEG"); } } // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } } else { if (!stbi__process_marker(j, m)) return 0; } m = stbi__get_marker(j); } if (j->progressive) stbi__jpeg_finish(j); return 1; } // static jfif-centered resampling (across block boundaries) typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, int w, int hs); #define stbi__div4(x) ((stbi_uc) ((x) >> 2)) static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { STBI_NOTUSED(out); STBI_NOTUSED(in_far); STBI_NOTUSED(w); STBI_NOTUSED(hs); return in_near; } static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate two samples vertically for every one in input int i; STBI_NOTUSED(hs); for (i=0; i < w; ++i) out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); return out; } static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate two samples horizontally for every one in input int i; stbi_uc *input = in_near; if (w == 1) { // if only one sample, can't do any interpolation out[0] = out[1] = input[0]; return out; } out[0] = input[0]; out[1] = stbi__div4(input[0]*3 + input[1] + 2); for (i=1; i < w-1; ++i) { int n = 3*input[i]+2; out[i*2+0] = stbi__div4(n+input[i-1]); out[i*2+1] = stbi__div4(n+input[i+1]); } out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); out[i*2+1] = input[w-1]; STBI_NOTUSED(in_far); STBI_NOTUSED(hs); return out; } #define stbi__div16(x) ((stbi_uc) ((x) >> 4)) static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate 2x2 samples for every one in input int i,t0,t1; if (w == 1) { out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); return out; } t1 = 3*in_near[0] + in_far[0]; out[0] = stbi__div4(t1+2); for (i=1; i < w; ++i) { t0 = t1; t1 = 3*in_near[i]+in_far[i]; out[i*2-1] = stbi__div16(3*t0 + t1 + 8); out[i*2 ] = stbi__div16(3*t1 + t0 + 8); } out[w*2-1] = stbi__div4(t1+2); STBI_NOTUSED(hs); return out; } #if defined(STBI_SSE2) || defined(STBI_NEON) static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate 2x2 samples for every one in input int i=0,t0,t1; if (w == 1) { out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); return out; } t1 = 3*in_near[0] + in_far[0]; // process groups of 8 pixels for as long as we can. // note we can't handle the last pixel in a row in this loop // because we need to handle the filter boundary conditions. for (; i < ((w-1) & ~7); i += 8) { #if defined(STBI_SSE2) // load and perform the vertical filtering pass // this uses 3*x + y = 4*x + (y - x) __m128i zero = _mm_setzero_si128(); __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); __m128i farw = _mm_unpacklo_epi8(farb, zero); __m128i nearw = _mm_unpacklo_epi8(nearb, zero); __m128i diff = _mm_sub_epi16(farw, nearw); __m128i nears = _mm_slli_epi16(nearw, 2); __m128i curr = _mm_add_epi16(nears, diff); // current row // horizontal filter works the same based on shifted vers of current // row. "prev" is current row shifted right by 1 pixel; we need to // insert the previous pixel value (from t1). // "next" is current row shifted left by 1 pixel, with first pixel // of next block of 8 pixels added in. __m128i prv0 = _mm_slli_si128(curr, 2); __m128i nxt0 = _mm_srli_si128(curr, 2); __m128i prev = _mm_insert_epi16(prv0, t1, 0); __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); // horizontal filter, polyphase implementation since it's convenient: // even pixels = 3*cur + prev = cur*4 + (prev - cur) // odd pixels = 3*cur + next = cur*4 + (next - cur) // note the shared term. __m128i bias = _mm_set1_epi16(8); __m128i curs = _mm_slli_epi16(curr, 2); __m128i prvd = _mm_sub_epi16(prev, curr); __m128i nxtd = _mm_sub_epi16(next, curr); __m128i curb = _mm_add_epi16(curs, bias); __m128i even = _mm_add_epi16(prvd, curb); __m128i odd = _mm_add_epi16(nxtd, curb); // interleave even and odd pixels, then undo scaling. __m128i int0 = _mm_unpacklo_epi16(even, odd); __m128i int1 = _mm_unpackhi_epi16(even, odd); __m128i de0 = _mm_srli_epi16(int0, 4); __m128i de1 = _mm_srli_epi16(int1, 4); // pack and write output __m128i outv = _mm_packus_epi16(de0, de1); _mm_storeu_si128((__m128i *) (out + i*2), outv); #elif defined(STBI_NEON) // load and perform the vertical filtering pass // this uses 3*x + y = 4*x + (y - x) uint8x8_t farb = vld1_u8(in_far + i); uint8x8_t nearb = vld1_u8(in_near + i); int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); int16x8_t curr = vaddq_s16(nears, diff); // current row // horizontal filter works the same based on shifted vers of current // row. "prev" is current row shifted right by 1 pixel; we need to // insert the previous pixel value (from t1). // "next" is current row shifted left by 1 pixel, with first pixel // of next block of 8 pixels added in. int16x8_t prv0 = vextq_s16(curr, curr, 7); int16x8_t nxt0 = vextq_s16(curr, curr, 1); int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); // horizontal filter, polyphase implementation since it's convenient: // even pixels = 3*cur + prev = cur*4 + (prev - cur) // odd pixels = 3*cur + next = cur*4 + (next - cur) // note the shared term. int16x8_t curs = vshlq_n_s16(curr, 2); int16x8_t prvd = vsubq_s16(prev, curr); int16x8_t nxtd = vsubq_s16(next, curr); int16x8_t even = vaddq_s16(curs, prvd); int16x8_t odd = vaddq_s16(curs, nxtd); // undo scaling and round, then store with even/odd phases interleaved uint8x8x2_t o; o.val[0] = vqrshrun_n_s16(even, 4); o.val[1] = vqrshrun_n_s16(odd, 4); vst2_u8(out + i*2, o); #endif // "previous" value for next iter t1 = 3*in_near[i+7] + in_far[i+7]; } t0 = t1; t1 = 3*in_near[i] + in_far[i]; out[i*2] = stbi__div16(3*t1 + t0 + 8); for (++i; i < w; ++i) { t0 = t1; t1 = 3*in_near[i]+in_far[i]; out[i*2-1] = stbi__div16(3*t0 + t1 + 8); out[i*2 ] = stbi__div16(3*t1 + t0 + 8); } out[w*2-1] = stbi__div4(t1+2); STBI_NOTUSED(hs); return out; } #endif static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // resample with nearest-neighbor int i,j; STBI_NOTUSED(in_far); for (i=0; i < w; ++i) for (j=0; j < hs; ++j) out[i*hs+j] = in_near[i]; return out; } #ifdef STBI_JPEG_OLD // this is the same YCbCr-to-RGB calculation that stb_image has used // historically before the algorithm changes in 1.49 #define float2fixed(x) ((int) ((x) * 65536 + 0.5)) static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) { int i; for (i=0; i < count; ++i) { int y_fixed = (y[i] << 16) + 32768; // rounding int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; r = y_fixed + cr*float2fixed(1.40200f); g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); b = y_fixed + cb*float2fixed(1.77200f); r >>= 16; g >>= 16; b >>= 16; if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } out[0] = (stbi_uc)r; out[1] = (stbi_uc)g; out[2] = (stbi_uc)b; out[3] = 255; out += step; } } #else // this is a reduced-precision calculation of YCbCr-to-RGB introduced // to make sure the code produces the same results in both SIMD and scalar #define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) { int i; for (i=0; i < count; ++i) { int y_fixed = (y[i] << 20) + (1<<19); // rounding int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; r = y_fixed + cr* float2fixed(1.40200f); g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); b = y_fixed + cb* float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } out[0] = (stbi_uc)r; out[1] = (stbi_uc)g; out[2] = (stbi_uc)b; out[3] = 255; out += step; } } #endif #if defined(STBI_SSE2) || defined(STBI_NEON) static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) { int i = 0; #ifdef STBI_SSE2 // step == 3 is pretty ugly on the final interleave, and i'm not convinced // it's useful in practice (you wouldn't use it for textures, for example). // so just accelerate step == 4 case. if (step == 4) { // this is a fairly straightforward implementation and not super-optimized. __m128i signflip = _mm_set1_epi8(-0x80); __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); __m128i xw = _mm_set1_epi16(255); // alpha channel for (; i+7 < count; i += 8) { // load __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 // unpack to short (and left-shift cr, cb by 8) __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); // color transform __m128i yws = _mm_srli_epi16(yw, 4); __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); __m128i rws = _mm_add_epi16(cr0, yws); __m128i gwt = _mm_add_epi16(cb0, yws); __m128i bws = _mm_add_epi16(yws, cb1); __m128i gws = _mm_add_epi16(gwt, cr1); // descale __m128i rw = _mm_srai_epi16(rws, 4); __m128i bw = _mm_srai_epi16(bws, 4); __m128i gw = _mm_srai_epi16(gws, 4); // back to byte, set up for transpose __m128i brb = _mm_packus_epi16(rw, bw); __m128i gxb = _mm_packus_epi16(gw, xw); // transpose to interleave channels __m128i t0 = _mm_unpacklo_epi8(brb, gxb); __m128i t1 = _mm_unpackhi_epi8(brb, gxb); __m128i o0 = _mm_unpacklo_epi16(t0, t1); __m128i o1 = _mm_unpackhi_epi16(t0, t1); // store _mm_storeu_si128((__m128i *) (out + 0), o0); _mm_storeu_si128((__m128i *) (out + 16), o1); out += 32; } } #endif #ifdef STBI_NEON // in this version, step=3 support would be easy to add. but is there demand? if (step == 4) { // this is a fairly straightforward implementation and not super-optimized. uint8x8_t signflip = vdup_n_u8(0x80); int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); for (; i+7 < count; i += 8) { // load uint8x8_t y_bytes = vld1_u8(y + i); uint8x8_t cr_bytes = vld1_u8(pcr + i); uint8x8_t cb_bytes = vld1_u8(pcb + i); int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); // expand to s16 int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); int16x8_t crw = vshll_n_s8(cr_biased, 7); int16x8_t cbw = vshll_n_s8(cb_biased, 7); // color transform int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); int16x8_t rws = vaddq_s16(yws, cr0); int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); int16x8_t bws = vaddq_s16(yws, cb1); // undo scaling, round, convert to byte uint8x8x4_t o; o.val[0] = vqrshrun_n_s16(rws, 4); o.val[1] = vqrshrun_n_s16(gws, 4); o.val[2] = vqrshrun_n_s16(bws, 4); o.val[3] = vdup_n_u8(255); // store, interleaving r/g/b/a vst4_u8(out, o); out += 8*4; } } #endif for (; i < count; ++i) { int y_fixed = (y[i] << 20) + (1<<19); // rounding int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; r = y_fixed + cr* float2fixed(1.40200f); g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); b = y_fixed + cb* float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } out[0] = (stbi_uc)r; out[1] = (stbi_uc)g; out[2] = (stbi_uc)b; out[3] = 255; out += step; } } #endif // set up the kernels static void stbi__setup_jpeg(stbi__jpeg *j) { j->idct_block_kernel = stbi__idct_block; j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; #ifdef STBI_SSE2 if (stbi__sse2_available()) { j->idct_block_kernel = stbi__idct_simd; #ifndef STBI_JPEG_OLD j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; #endif j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; } #endif #ifdef STBI_NEON j->idct_block_kernel = stbi__idct_simd; #ifndef STBI_JPEG_OLD j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; #endif j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; #endif } // clean up the temporary component buffers static void stbi__cleanup_jpeg(stbi__jpeg *j) { int i; for (i=0; i < j->s->img_n; ++i) { if (j->img_comp[i].raw_data) { STBI_FREE(j->img_comp[i].raw_data); j->img_comp[i].raw_data = NULL; j->img_comp[i].data = NULL; } if (j->img_comp[i].raw_coeff) { STBI_FREE(j->img_comp[i].raw_coeff); j->img_comp[i].raw_coeff = 0; j->img_comp[i].coeff = 0; } if (j->img_comp[i].linebuf) { STBI_FREE(j->img_comp[i].linebuf); j->img_comp[i].linebuf = NULL; } } } typedef struct { resample_row_func resample; stbi_uc *line0,*line1; int hs,vs; // expansion factor in each axis int w_lores; // horizontal pixels pre-expansion int ystep; // how far through vertical expansion we are int ypos; // which pre-expansion row we're on } stbi__resample; static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) { int n, decode_n; z->s->img_n = 0; // make stbi__cleanup_jpeg safe // validate req_comp if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); // load a jpeg image from whichever source, but leave in YCbCr format if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } // determine actual number of components to generate n = req_comp ? req_comp : z->s->img_n; if (z->s->img_n == 3 && n < 3) decode_n = 1; else decode_n = z->s->img_n; // resample and color-convert { int k; unsigned int i,j; stbi_uc *output; stbi_uc *coutput[4]; stbi__resample res_comp[4]; for (k=0; k < decode_n; ++k) { stbi__resample *r = &res_comp[k]; // allocate line buffer big enough for upsampling off the edges // with upsample factor of 4 z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } r->hs = z->img_h_max / z->img_comp[k].h; r->vs = z->img_v_max / z->img_comp[k].v; r->ystep = r->vs >> 1; r->w_lores = (z->s->img_x + r->hs-1) / r->hs; r->ypos = 0; r->line0 = r->line1 = z->img_comp[k].data; if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; else r->resample = stbi__resample_row_generic; } // can't error after this so, this is safe output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } // now go ahead and resample for (j=0; j < z->s->img_y; ++j) { stbi_uc *out = output + n * z->s->img_x * j; for (k=0; k < decode_n; ++k) { stbi__resample *r = &res_comp[k]; int y_bot = r->ystep >= (r->vs >> 1); coutput[k] = r->resample(z->img_comp[k].linebuf, y_bot ? r->line1 : r->line0, y_bot ? r->line0 : r->line1, r->w_lores, r->hs); if (++r->ystep >= r->vs) { r->ystep = 0; r->line0 = r->line1; if (++r->ypos < z->img_comp[k].y) r->line1 += z->img_comp[k].w2; } } if (n >= 3) { stbi_uc *y = coutput[0]; if (z->s->img_n == 3) { z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); } else for (i=0; i < z->s->img_x; ++i) { out[0] = out[1] = out[2] = y[i]; out[3] = 255; // not used if n==3 out += n; } } else { stbi_uc *y = coutput[0]; if (n == 1) for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; else for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; } } stbi__cleanup_jpeg(z); *out_x = z->s->img_x; *out_y = z->s->img_y; if (comp) *comp = z->s->img_n; // report original components, not output return output; } } static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi__jpeg j; j.s = s; stbi__setup_jpeg(&j); return load_jpeg_image(&j, x,y,comp,req_comp); } static int stbi__jpeg_test(stbi__context *s) { int r; stbi__jpeg j; j.s = s; stbi__setup_jpeg(&j); r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); stbi__rewind(s); return r; } static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) { if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { stbi__rewind( j->s ); return 0; } if (x) *x = j->s->img_x; if (y) *y = j->s->img_y; if (comp) *comp = j->s->img_n; return 1; } static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { stbi__jpeg j; j.s = s; return stbi__jpeg_info_raw(&j, x, y, comp); } #endif // public domain zlib decode v0.2 Sean Barrett 2006-11-18 // simple implementation // - all input must be provided in an upfront buffer // - all output is written to a single output buffer (can malloc/realloc) // performance // - fast huffman #ifndef STBI_NO_ZLIB // fast-way is faster to check than jpeg huffman, but slow way is slower #define STBI__ZFAST_BITS 9 // accelerate all cases in default tables #define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) // zlib-style huffman encoding // (jpegs packs from left, zlib from right, so can't share code) typedef struct { stbi__uint16 fast[1 << STBI__ZFAST_BITS]; stbi__uint16 firstcode[16]; int maxcode[17]; stbi__uint16 firstsymbol[16]; stbi_uc size[288]; stbi__uint16 value[288]; } stbi__zhuffman; stbi_inline static int stbi__bitreverse16(int n) { n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); return n; } stbi_inline static int stbi__bit_reverse(int v, int bits) { STBI_ASSERT(bits <= 16); // to bit reverse n bits, reverse 16 and shift // e.g. 11 bits, bit reverse and shift away 5 return stbi__bitreverse16(v) >> (16-bits); } static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) { int i,k=0; int code, next_code[16], sizes[17]; // DEFLATE spec for generating codes memset(sizes, 0, sizeof(sizes)); memset(z->fast, 0, sizeof(z->fast)); for (i=0; i < num; ++i) ++sizes[sizelist[i]]; sizes[0] = 0; for (i=1; i < 16; ++i) STBI_ASSERT(sizes[i] <= (1 << i)); code = 0; for (i=1; i < 16; ++i) { next_code[i] = code; z->firstcode[i] = (stbi__uint16) code; z->firstsymbol[i] = (stbi__uint16) k; code = (code + sizes[i]); if (sizes[i]) if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt JPEG"); z->maxcode[i] = code << (16-i); // preshift for inner loop code <<= 1; k += sizes[i]; } z->maxcode[16] = 0x10000; // sentinel for (i=0; i < num; ++i) { int s = sizelist[i]; if (s) { int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); z->size [c] = (stbi_uc ) s; z->value[c] = (stbi__uint16) i; if (s <= STBI__ZFAST_BITS) { int k = stbi__bit_reverse(next_code[s],s); while (k < (1 << STBI__ZFAST_BITS)) { z->fast[k] = fastv; k += (1 << s); } } ++next_code[s]; } } return 1; } // zlib-from-memory implementation for PNG reading // because PNG allows splitting the zlib stream arbitrarily, // and it's annoying structurally to have PNG call ZLIB call PNG, // we require PNG read all the IDATs and combine them into a single // memory buffer typedef struct { stbi_uc *zbuffer, *zbuffer_end; int num_bits; stbi__uint32 code_buffer; char *zout; char *zout_start; char *zout_end; int z_expandable; stbi__zhuffman z_length, z_distance; } stbi__zbuf; stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) { if (z->zbuffer >= z->zbuffer_end) return 0; return *z->zbuffer++; } static void stbi__fill_bits(stbi__zbuf *z) { do { STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); z->code_buffer |= stbi__zget8(z) << z->num_bits; z->num_bits += 8; } while (z->num_bits <= 24); } stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) { unsigned int k; if (z->num_bits < n) stbi__fill_bits(z); k = z->code_buffer & ((1 << n) - 1); z->code_buffer >>= n; z->num_bits -= n; return k; } static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) { int b,s,k; // not resolved by fast table, so compute it the slow way // use jpeg approach, which requires MSbits at top k = stbi__bit_reverse(a->code_buffer, 16); for (s=STBI__ZFAST_BITS+1; ; ++s) if (k < z->maxcode[s]) break; if (s == 16) return -1; // invalid code! // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; STBI_ASSERT(z->size[b] == s); a->code_buffer >>= s; a->num_bits -= s; return z->value[b]; } stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { int b,s; if (a->num_bits < 16) stbi__fill_bits(a); b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { s = b >> 9; a->code_buffer >>= s; a->num_bits -= s; return b & 511; } return stbi__zhuffman_decode_slowpath(a, z); } static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes { char *q; int cur, limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); cur = (int) (z->zout - z->zout_start); limit = (int) (z->zout_end - z->zout_start); while (cur + n > limit) limit *= 2; q = (char *) STBI_REALLOC(z->zout_start, limit); if (q == NULL) return stbi__err("outofmem", "Out of memory"); z->zout_start = q; z->zout = q + cur; z->zout_end = q + limit; return 1; } static int stbi__zlength_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; static int stbi__zlength_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; static int stbi__zdist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; static int stbi__parse_huffman_block(stbi__zbuf *a) { char *zout = a->zout; for(;;) { int z = stbi__zhuffman_decode(a, &a->z_length); if (z < 256) { if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes if (zout >= a->zout_end) { if (!stbi__zexpand(a, zout, 1)) return 0; zout = a->zout; } *zout++ = (char) z; } else { stbi_uc *p; int len,dist; if (z == 256) { a->zout = zout; return 1; } z -= 257; len = stbi__zlength_base[z]; if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); z = stbi__zhuffman_decode(a, &a->z_distance); if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); dist = stbi__zdist_base[z]; if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); if (zout + len > a->zout_end) { if (!stbi__zexpand(a, zout, len)) return 0; zout = a->zout; } p = (stbi_uc *) (zout - dist); if (dist == 1) { // run of one byte; common in images. stbi_uc v = *p; do *zout++ = v; while (--len); } else { do *zout++ = *p++; while (--len); } } } } static int stbi__compute_huffman_codes(stbi__zbuf *a) { static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; stbi__zhuffman z_codelength; stbi_uc lencodes[286+32+137];//padding for maximum single op stbi_uc codelength_sizes[19]; int i,n; int hlit = stbi__zreceive(a,5) + 257; int hdist = stbi__zreceive(a,5) + 1; int hclen = stbi__zreceive(a,4) + 4; memset(codelength_sizes, 0, sizeof(codelength_sizes)); for (i=0; i < hclen; ++i) { int s = stbi__zreceive(a,3); codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; } if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; n = 0; while (n < hlit + hdist) { int c = stbi__zhuffman_decode(a, &z_codelength); STBI_ASSERT(c >= 0 && c < 19); if (c < 16) lencodes[n++] = (stbi_uc) c; else if (c == 16) { c = stbi__zreceive(a,2)+3; memset(lencodes+n, lencodes[n-1], c); n += c; } else if (c == 17) { c = stbi__zreceive(a,3)+3; memset(lencodes+n, 0, c); n += c; } else { STBI_ASSERT(c == 18); c = stbi__zreceive(a,7)+11; memset(lencodes+n, 0, c); n += c; } } if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; return 1; } static int stbi__parse_uncomperssed_block(stbi__zbuf *a) { stbi_uc header[4]; int len,nlen,k; if (a->num_bits & 7) stbi__zreceive(a, a->num_bits & 7); // discard // drain the bit-packed data into header k = 0; while (a->num_bits > 0) { header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check a->code_buffer >>= 8; a->num_bits -= 8; } STBI_ASSERT(a->num_bits == 0); // now fill header the normal way while (k < 4) header[k++] = stbi__zget8(a); len = header[1] * 256 + header[0]; nlen = header[3] * 256 + header[2]; if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); if (a->zout + len > a->zout_end) if (!stbi__zexpand(a, a->zout, len)) return 0; memcpy(a->zout, a->zbuffer, len); a->zbuffer += len; a->zout += len; return 1; } static int stbi__parse_zlib_header(stbi__zbuf *a) { int cmf = stbi__zget8(a); int cm = cmf & 15; /* int cinfo = cmf >> 4; */ int flg = stbi__zget8(a); if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png // window = 1 << (8 + cinfo)... but who cares, we fully buffer output return 1; } // @TODO: should statically initialize these for optimal thread safety static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; static void stbi__init_zdefaults(void) { int i; // use <= to match clearly with spec for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; } static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) { int final, type; if (parse_header) if (!stbi__parse_zlib_header(a)) return 0; a->num_bits = 0; a->code_buffer = 0; do { final = stbi__zreceive(a,1); type = stbi__zreceive(a,2); if (type == 0) { if (!stbi__parse_uncomperssed_block(a)) return 0; } else if (type == 3) { return 0; } else { if (type == 1) { // use fixed code lengths if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; } else { if (!stbi__compute_huffman_codes(a)) return 0; } if (!stbi__parse_huffman_block(a)) return 0; } } while (!final); return 1; } static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) { a->zout_start = obuf; a->zout = obuf; a->zout_end = obuf + olen; a->z_expandable = exp; return stbi__parse_zlib(a, parse_header); } STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) { stbi__zbuf a; char *p = (char *) stbi__malloc(initial_size); if (p == NULL) return NULL; a.zbuffer = (stbi_uc *) buffer; a.zbuffer_end = (stbi_uc *) buffer + len; if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { if (outlen) *outlen = (int) (a.zout - a.zout_start); return a.zout_start; } else { STBI_FREE(a.zout_start); return NULL; } } STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) { return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); } STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) { stbi__zbuf a; char *p = (char *) stbi__malloc(initial_size); if (p == NULL) return NULL; a.zbuffer = (stbi_uc *) buffer; a.zbuffer_end = (stbi_uc *) buffer + len; if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { if (outlen) *outlen = (int) (a.zout - a.zout_start); return a.zout_start; } else { STBI_FREE(a.zout_start); return NULL; } } STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) { stbi__zbuf a; a.zbuffer = (stbi_uc *) ibuffer; a.zbuffer_end = (stbi_uc *) ibuffer + ilen; if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) return (int) (a.zout - a.zout_start); else return -1; } STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) { stbi__zbuf a; char *p = (char *) stbi__malloc(16384); if (p == NULL) return NULL; a.zbuffer = (stbi_uc *) buffer; a.zbuffer_end = (stbi_uc *) buffer+len; if (stbi__do_zlib(&a, p, 16384, 1, 0)) { if (outlen) *outlen = (int) (a.zout - a.zout_start); return a.zout_start; } else { STBI_FREE(a.zout_start); return NULL; } } STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) { stbi__zbuf a; a.zbuffer = (stbi_uc *) ibuffer; a.zbuffer_end = (stbi_uc *) ibuffer + ilen; if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) return (int) (a.zout - a.zout_start); else return -1; } #endif // public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 // simple implementation // - only 8-bit samples // - no CRC checking // - allocates lots of intermediate memory // - avoids problem of streaming data between subsystems // - avoids explicit window management // performance // - uses stb_zlib, a PD zlib implementation with fast huffman decoding #ifndef STBI_NO_PNG typedef struct { stbi__uint32 length; stbi__uint32 type; } stbi__pngchunk; static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) { stbi__pngchunk c; c.length = stbi__get32be(s); c.type = stbi__get32be(s); return c; } static int stbi__check_png_header(stbi__context *s) { static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; int i; for (i=0; i < 8; ++i) if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); return 1; } typedef struct { stbi__context *s; stbi_uc *idata, *expanded, *out; } stbi__png; enum { STBI__F_none=0, STBI__F_sub=1, STBI__F_up=2, STBI__F_avg=3, STBI__F_paeth=4, // synthetic filters used for first scanline to avoid needing a dummy row of 0s STBI__F_avg_first, STBI__F_paeth_first }; static stbi_uc first_row_filter[5] = { STBI__F_none, STBI__F_sub, STBI__F_none, STBI__F_avg_first, STBI__F_paeth_first }; static int stbi__paeth(int a, int b, int c) { int p = a + b - c; int pa = abs(p-a); int pb = abs(p-b); int pc = abs(p-c); if (pa <= pb && pa <= pc) return a; if (pb <= pc) return b; return c; } static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; // create the png data from post-deflated data static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) { stbi__context *s = a->s; stbi__uint32 i,j,stride = x*out_n; stbi__uint32 img_len, img_width_bytes; int k; int img_n = s->img_n; // copy it into a local for later STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); img_len = (img_width_bytes + 1) * y; if (s->img_x == x && s->img_y == y) { if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); } else { // interlaced: if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); } for (j=0; j < y; ++j) { stbi_uc *cur = a->out + stride*j; stbi_uc *prior = cur - stride; int filter = *raw++; int filter_bytes = img_n; int width = x; if (filter > 4) return stbi__err("invalid filter","Corrupt PNG"); if (depth < 8) { STBI_ASSERT(img_width_bytes <= x); cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place filter_bytes = 1; width = img_width_bytes; } // if first row, use special filter that doesn't sample previous row if (j == 0) filter = first_row_filter[filter]; // handle first byte explicitly for (k=0; k < filter_bytes; ++k) { switch (filter) { case STBI__F_none : cur[k] = raw[k]; break; case STBI__F_sub : cur[k] = raw[k]; break; case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; case STBI__F_avg_first : cur[k] = raw[k]; break; case STBI__F_paeth_first: cur[k] = raw[k]; break; } } if (depth == 8) { if (img_n != out_n) cur[img_n] = 255; // first pixel raw += img_n; cur += out_n; prior += out_n; } else { raw += 1; cur += 1; prior += 1; } // this is a little gross, so that we don't switch per-pixel or per-component if (depth < 8 || img_n == out_n) { int nk = (width - 1)*img_n; #define CASE(f) \ case f: \ for (k=0; k < nk; ++k) switch (filter) { // "none" filter turns into a memcpy here; make that explicit. case STBI__F_none: memcpy(cur, raw, nk); break; CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; } #undef CASE raw += nk; } else { STBI_ASSERT(img_n+1 == out_n); #define CASE(f) \ case f: \ for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ for (k=0; k < img_n; ++k) switch (filter) { CASE(STBI__F_none) cur[k] = raw[k]; break; CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); break; CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-out_n])>>1)); break; CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); break; CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); break; } #undef CASE } } // we make a separate pass to expand bits to pixels; for performance, // this could run two scanlines behind the above code, so it won't // intefere with filtering but will still be in the cache. if (depth < 8) { for (j=0; j < y; ++j) { stbi_uc *cur = a->out + stride*j; stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range // note that the final byte might overshoot and write more data than desired. // we can allocate enough data that this never writes out of memory, but it // could also overwrite the next scanline. can it overwrite non-empty data // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. // so we need to explicitly clamp the final ones if (depth == 4) { for (k=x*img_n; k >= 2; k-=2, ++in) { *cur++ = scale * ((*in >> 4) ); *cur++ = scale * ((*in ) & 0x0f); } if (k > 0) *cur++ = scale * ((*in >> 4) ); } else if (depth == 2) { for (k=x*img_n; k >= 4; k-=4, ++in) { *cur++ = scale * ((*in >> 6) ); *cur++ = scale * ((*in >> 4) & 0x03); *cur++ = scale * ((*in >> 2) & 0x03); *cur++ = scale * ((*in ) & 0x03); } if (k > 0) *cur++ = scale * ((*in >> 6) ); if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); } else if (depth == 1) { for (k=x*img_n; k >= 8; k-=8, ++in) { *cur++ = scale * ((*in >> 7) ); *cur++ = scale * ((*in >> 6) & 0x01); *cur++ = scale * ((*in >> 5) & 0x01); *cur++ = scale * ((*in >> 4) & 0x01); *cur++ = scale * ((*in >> 3) & 0x01); *cur++ = scale * ((*in >> 2) & 0x01); *cur++ = scale * ((*in >> 1) & 0x01); *cur++ = scale * ((*in ) & 0x01); } if (k > 0) *cur++ = scale * ((*in >> 7) ); if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); } if (img_n != out_n) { // insert alpha = 255 stbi_uc *cur = a->out + stride*j; int i; if (img_n == 1) { for (i=x-1; i >= 0; --i) { cur[i*2+1] = 255; cur[i*2+0] = cur[i]; } } else { STBI_ASSERT(img_n == 3); for (i=x-1; i >= 0; --i) { cur[i*4+3] = 255; cur[i*4+2] = cur[i*3+2]; cur[i*4+1] = cur[i*3+1]; cur[i*4+0] = cur[i*3+0]; } } } } } return 1; } static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { stbi_uc *final; int p; if (!interlaced) return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); // de-interlacing final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); for (p=0; p < 7; ++p) { int xorig[] = { 0,4,0,2,0,1,0 }; int yorig[] = { 0,0,4,0,2,0,1 }; int xspc[] = { 8,8,4,4,2,2,1 }; int yspc[] = { 8,8,8,4,4,2,2 }; int i,j,x,y; // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; if (x && y) { stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { STBI_FREE(final); return 0; } for (j=0; j < y; ++j) { for (i=0; i < x; ++i) { int out_y = j*yspc[p]+yorig[p]; int out_x = i*xspc[p]+xorig[p]; memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, a->out + (j*x+i)*out_n, out_n); } } STBI_FREE(a->out); image_data += img_len; image_data_len -= img_len; } } a->out = final; return 1; } static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) { stbi__context *s = z->s; stbi__uint32 i, pixel_count = s->img_x * s->img_y; stbi_uc *p = z->out; // compute color-based transparency, assuming we've // already got 255 as the alpha value in the output STBI_ASSERT(out_n == 2 || out_n == 4); if (out_n == 2) { for (i=0; i < pixel_count; ++i) { p[1] = (p[0] == tc[0] ? 0 : 255); p += 2; } } else { for (i=0; i < pixel_count; ++i) { if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) p[3] = 0; p += 4; } } return 1; } static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) { stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; stbi_uc *p, *temp_out, *orig = a->out; p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); if (p == NULL) return stbi__err("outofmem", "Out of memory"); // between here and free(out) below, exitting would leak temp_out = p; if (pal_img_n == 3) { for (i=0; i < pixel_count; ++i) { int n = orig[i]*4; p[0] = palette[n ]; p[1] = palette[n+1]; p[2] = palette[n+2]; p += 3; } } else { for (i=0; i < pixel_count; ++i) { int n = orig[i]*4; p[0] = palette[n ]; p[1] = palette[n+1]; p[2] = palette[n+2]; p[3] = palette[n+3]; p += 4; } } STBI_FREE(a->out); a->out = temp_out; STBI_NOTUSED(len); return 1; } static int stbi__unpremultiply_on_load = 0; static int stbi__de_iphone_flag = 0; STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) { stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; } STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) { stbi__de_iphone_flag = flag_true_if_should_convert; } static void stbi__de_iphone(stbi__png *z) { stbi__context *s = z->s; stbi__uint32 i, pixel_count = s->img_x * s->img_y; stbi_uc *p = z->out; if (s->img_out_n == 3) { // convert bgr to rgb for (i=0; i < pixel_count; ++i) { stbi_uc t = p[0]; p[0] = p[2]; p[2] = t; p += 3; } } else { STBI_ASSERT(s->img_out_n == 4); if (stbi__unpremultiply_on_load) { // convert bgr to rgb and unpremultiply for (i=0; i < pixel_count; ++i) { stbi_uc a = p[3]; stbi_uc t = p[0]; if (a) { p[0] = p[2] * 255 / a; p[1] = p[1] * 255 / a; p[2] = t * 255 / a; } else { p[0] = p[2]; p[2] = t; } p += 4; } } else { // convert bgr to rgb for (i=0; i < pixel_count; ++i) { stbi_uc t = p[0]; p[0] = p[2]; p[2] = t; p += 4; } } } } #define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { stbi_uc palette[1024], pal_img_n=0; stbi_uc has_trans=0, tc[3]; stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; int first=1,k,interlace=0, color=0, depth=0, is_iphone=0; stbi__context *s = z->s; z->expanded = NULL; z->idata = NULL; z->out = NULL; if (!stbi__check_png_header(s)) return 0; if (scan == STBI__SCAN_type) return 1; for (;;) { stbi__pngchunk c = stbi__get_chunk_header(s); switch (c.type) { case STBI__PNG_TYPE('C','g','B','I'): is_iphone = 1; stbi__skip(s, c.length); break; case STBI__PNG_TYPE('I','H','D','R'): { int comp,filter; if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); first = 0; if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only"); color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); if (!pal_img_n) { s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); if (scan == STBI__SCAN_header) return 1; } else { // if paletted, then pal_n is our final components, and // img_n is # components to decompress/filter. s->img_n = 1; if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); // if SCAN_header, have to scan to see if we have a tRNS } break; } case STBI__PNG_TYPE('P','L','T','E'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); pal_len = c.length / 3; if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); for (i=0; i < pal_len; ++i) { palette[i*4+0] = stbi__get8(s); palette[i*4+1] = stbi__get8(s); palette[i*4+2] = stbi__get8(s); palette[i*4+3] = 255; } break; } case STBI__PNG_TYPE('t','R','N','S'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); if (pal_img_n) { if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); pal_img_n = 4; for (i=0; i < c.length; ++i) palette[i*4+3] = stbi__get8(s); } else { if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); has_trans = 1; for (k=0; k < s->img_n; ++k) tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[depth]; // non 8-bit images will be larger } break; } case STBI__PNG_TYPE('I','D','A','T'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } if (ioff + c.length > idata_limit) { stbi_uc *p; if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; while (ioff + c.length > idata_limit) idata_limit *= 2; p = (stbi_uc *) STBI_REALLOC(z->idata, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); z->idata = p; } if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); ioff += c.length; break; } case STBI__PNG_TYPE('I','E','N','D'): { stbi__uint32 raw_len, bpl; if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (scan != STBI__SCAN_load) return 1; if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); // initial guess for decoded data size to avoid unnecessary reallocs bpl = (s->img_x * depth + 7) / 8; // bytes per line, per component raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); if (z->expanded == NULL) return 0; // zlib should set error STBI_FREE(z->idata); z->idata = NULL; if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) s->img_out_n = s->img_n+1; else s->img_out_n = s->img_n; if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, depth, color, interlace)) return 0; if (has_trans) if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) stbi__de_iphone(z); if (pal_img_n) { // pal_img_n == 3 or 4 s->img_n = pal_img_n; // record the actual colors we had s->img_out_n = pal_img_n; if (req_comp >= 3) s->img_out_n = req_comp; if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) return 0; } STBI_FREE(z->expanded); z->expanded = NULL; return 1; } default: // if critical, fail if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if ((c.type & (1 << 29)) == 0) { #ifndef STBI_NO_FAILURE_STRINGS // not threadsafe static char invalid_chunk[] = "XXXX PNG chunk not known"; invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); #endif return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); } stbi__skip(s, c.length); break; } // end of PNG chunk, read and skip CRC stbi__get32be(s); } } static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) { unsigned char *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); p->s->img_out_n = req_comp; if (result == NULL) return result; } *x = p->s->img_x; *y = p->s->img_y; if (n) *n = p->s->img_out_n; } STBI_FREE(p->out); p->out = NULL; STBI_FREE(p->expanded); p->expanded = NULL; STBI_FREE(p->idata); p->idata = NULL; return result; } static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi__png p; p.s = s; return stbi__do_png(&p, x,y,comp,req_comp); } static int stbi__png_test(stbi__context *s) { int r; r = stbi__check_png_header(s); stbi__rewind(s); return r; } static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) { if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { stbi__rewind( p->s ); return 0; } if (x) *x = p->s->img_x; if (y) *y = p->s->img_y; if (comp) *comp = p->s->img_n; return 1; } static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) { stbi__png p; p.s = s; return stbi__png_info_raw(&p, x, y, comp); } #endif // Microsoft/Windows BMP image #ifndef STBI_NO_BMP static int stbi__bmp_test_raw(stbi__context *s) { int r; int sz; if (stbi__get8(s) != 'B') return 0; if (stbi__get8(s) != 'M') return 0; stbi__get32le(s); // discard filesize stbi__get16le(s); // discard reserved stbi__get16le(s); // discard reserved stbi__get32le(s); // discard data offset sz = stbi__get32le(s); r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); return r; } static int stbi__bmp_test(stbi__context *s) { int r = stbi__bmp_test_raw(s); stbi__rewind(s); return r; } // returns 0..31 for the highest set bit static int stbi__high_bit(unsigned int z) { int n=0; if (z == 0) return -1; if (z >= 0x10000) n += 16, z >>= 16; if (z >= 0x00100) n += 8, z >>= 8; if (z >= 0x00010) n += 4, z >>= 4; if (z >= 0x00004) n += 2, z >>= 2; if (z >= 0x00002) n += 1, z >>= 1; return n; } static int stbi__bitcount(unsigned int a) { a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits a = (a + (a >> 8)); // max 16 per 8 bits a = (a + (a >> 16)); // max 32 per 8 bits return a & 0xff; } static int stbi__shiftsigned(int v, int shift, int bits) { int result; int z=0; if (shift < 0) v <<= -shift; else v >>= shift; result = v; z = bits; while (z < 8) { result += v >> z; z += bits; } return result; } static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi_uc *out; unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; stbi_uc pal[256][4]; int psize=0,i,j,compress=0,width; int bpp, flip_vertically, pad, target, offset, hsz; if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); stbi__get32le(s); // discard filesize stbi__get16le(s); // discard reserved stbi__get16le(s); // discard reserved offset = stbi__get32le(s); hsz = stbi__get32le(s); if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); if (hsz == 12) { s->img_x = stbi__get16le(s); s->img_y = stbi__get16le(s); } else { s->img_x = stbi__get32le(s); s->img_y = stbi__get32le(s); } if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); bpp = stbi__get16le(s); if (bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); flip_vertically = ((int) s->img_y) > 0; s->img_y = abs((int) s->img_y); if (hsz == 12) { if (bpp < 24) psize = (offset - 14 - 24) / 3; } else { compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); stbi__get32le(s); // discard sizeof stbi__get32le(s); // discard hres stbi__get32le(s); // discard vres stbi__get32le(s); // discard colorsused stbi__get32le(s); // discard max important if (hsz == 40 || hsz == 56) { if (hsz == 56) { stbi__get32le(s); stbi__get32le(s); stbi__get32le(s); stbi__get32le(s); } if (bpp == 16 || bpp == 32) { mr = mg = mb = 0; if (compress == 0) { if (bpp == 32) { mr = 0xffu << 16; mg = 0xffu << 8; mb = 0xffu << 0; ma = 0xffu << 24; fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 STBI_NOTUSED(fake_a); } else { mr = 31u << 10; mg = 31u << 5; mb = 31u << 0; } } else if (compress == 3) { mr = stbi__get32le(s); mg = stbi__get32le(s); mb = stbi__get32le(s); // not documented, but generated by photoshop and handled by mspaint if (mr == mg && mg == mb) { // ?!?!? return stbi__errpuc("bad BMP", "bad BMP"); } } else return stbi__errpuc("bad BMP", "bad BMP"); } } else { STBI_ASSERT(hsz == 108 || hsz == 124); mr = stbi__get32le(s); mg = stbi__get32le(s); mb = stbi__get32le(s); ma = stbi__get32le(s); stbi__get32le(s); // discard color space for (i=0; i < 12; ++i) stbi__get32le(s); // discard color space parameters if (hsz == 124) { stbi__get32le(s); // discard rendering intent stbi__get32le(s); // discard offset of profile data stbi__get32le(s); // discard size of profile data stbi__get32le(s); // discard reserved } } if (bpp < 16) psize = (offset - 14 - hsz) >> 2; } s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else target = s->img_n; // if they want monochrome, we'll post-convert out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); if (!out) return stbi__errpuc("outofmem", "Out of memory"); if (bpp < 16) { int z=0; if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } for (i=0; i < psize; ++i) { pal[i][2] = stbi__get8(s); pal[i][1] = stbi__get8(s); pal[i][0] = stbi__get8(s); if (hsz != 12) stbi__get8(s); pal[i][3] = 255; } stbi__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); if (bpp == 4) width = (s->img_x + 1) >> 1; else if (bpp == 8) width = s->img_x; else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } pad = (-width)&3; for (j=0; j < (int) s->img_y; ++j) { for (i=0; i < (int) s->img_x; i += 2) { int v=stbi__get8(s),v2=0; if (bpp == 4) { v2 = v & 15; v >>= 4; } out[z++] = pal[v][0]; out[z++] = pal[v][1]; out[z++] = pal[v][2]; if (target == 4) out[z++] = 255; if (i+1 == (int) s->img_x) break; v = (bpp == 8) ? stbi__get8(s) : v2; out[z++] = pal[v][0]; out[z++] = pal[v][1]; out[z++] = pal[v][2]; if (target == 4) out[z++] = 255; } stbi__skip(s, pad); } } else { int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; int z = 0; int easy=0; stbi__skip(s, offset - 14 - hsz); if (bpp == 24) width = 3 * s->img_x; else if (bpp == 16) width = 2*s->img_x; else /* bpp = 32 and pad = 0 */ width=0; pad = (-width) & 3; if (bpp == 24) { easy = 1; } else if (bpp == 32) { if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) easy = 2; } if (!easy) { if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } // right shift amt to put high bit in position #7 rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); } for (j=0; j < (int) s->img_y; ++j) { if (easy) { for (i=0; i < (int) s->img_x; ++i) { unsigned char a; out[z+2] = stbi__get8(s); out[z+1] = stbi__get8(s); out[z+0] = stbi__get8(s); z += 3; a = (easy == 2 ? stbi__get8(s) : 255); if (target == 4) out[z++] = a; } } else { for (i=0; i < (int) s->img_x; ++i) { stbi__uint32 v = (stbi__uint32) (bpp == 16 ? stbi__get16le(s) : stbi__get32le(s)); int a; out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); if (target == 4) out[z++] = STBI__BYTECAST(a); } } stbi__skip(s, pad); } } if (flip_vertically) { stbi_uc t; for (j=0; j < (int) s->img_y>>1; ++j) { stbi_uc *p1 = out + j *s->img_x*target; stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; for (i=0; i < (int) s->img_x*target; ++i) { t = p1[i], p1[i] = p2[i], p2[i] = t; } } } if (req_comp && req_comp != target) { out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); if (out == NULL) return out; // stbi__convert_format frees input on failure } *x = s->img_x; *y = s->img_y; if (comp) *comp = s->img_n; return out; } #endif // Targa Truevision - TGA // by Jonathan Dummer #ifndef STBI_NO_TGA static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) { int tga_w, tga_h, tga_comp; int sz; stbi__get8(s); // discard Offset sz = stbi__get8(s); // color type if( sz > 1 ) { stbi__rewind(s); return 0; // only RGB or indexed allowed } sz = stbi__get8(s); // image type // only RGB or grey allowed, +/- RLE if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; stbi__skip(s,9); tga_w = stbi__get16le(s); if( tga_w < 1 ) { stbi__rewind(s); return 0; // test width } tga_h = stbi__get16le(s); if( tga_h < 1 ) { stbi__rewind(s); return 0; // test height } sz = stbi__get8(s); // bits per pixel // only RGB or RGBA or grey allowed if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { stbi__rewind(s); return 0; } tga_comp = sz; if (x) *x = tga_w; if (y) *y = tga_h; if (comp) *comp = tga_comp / 8; return 1; // seems to have passed everything } static int stbi__tga_test(stbi__context *s) { int res; int sz; stbi__get8(s); // discard Offset sz = stbi__get8(s); // color type if ( sz > 1 ) return 0; // only RGB or indexed allowed sz = stbi__get8(s); // image type if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE stbi__get16be(s); // discard palette start stbi__get16be(s); // discard palette length stbi__get8(s); // discard bits per palette color entry stbi__get16be(s); // discard x origin stbi__get16be(s); // discard y origin if ( stbi__get16be(s) < 1 ) return 0; // test width if ( stbi__get16be(s) < 1 ) return 0; // test height sz = stbi__get8(s); // bits per pixel if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) res = 0; else res = 1; stbi__rewind(s); return res; } static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { // read in the TGA header stuff int tga_offset = stbi__get8(s); int tga_indexed = stbi__get8(s); int tga_image_type = stbi__get8(s); int tga_is_RLE = 0; int tga_palette_start = stbi__get16le(s); int tga_palette_len = stbi__get16le(s); int tga_palette_bits = stbi__get8(s); int tga_x_origin = stbi__get16le(s); int tga_y_origin = stbi__get16le(s); int tga_width = stbi__get16le(s); int tga_height = stbi__get16le(s); int tga_bits_per_pixel = stbi__get8(s); int tga_comp = tga_bits_per_pixel / 8; int tga_inverted = stbi__get8(s); // image data unsigned char *tga_data; unsigned char *tga_palette = NULL; int i, j; unsigned char raw_data[4]; int RLE_count = 0; int RLE_repeating = 0; int read_next_pixel = 1; // do a tiny bit of precessing if ( tga_image_type >= 8 ) { tga_image_type -= 8; tga_is_RLE = 1; } /* int tga_alpha_bits = tga_inverted & 15; */ tga_inverted = 1 - ((tga_inverted >> 5) & 1); // error check if ( //(tga_indexed) || (tga_width < 1) || (tga_height < 1) || (tga_image_type < 1) || (tga_image_type > 3) || ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) ) { return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA } // If I'm paletted, then I'll use the number of bits from the palette if ( tga_indexed ) { tga_comp = tga_palette_bits / 8; } // tga info *x = tga_width; *y = tga_height; if (comp) *comp = tga_comp; tga_data = (unsigned char*)stbi__malloc( tga_width * tga_height * tga_comp ); if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); // skip to the data's starting position (offset usually = 0) stbi__skip(s, tga_offset ); if ( !tga_indexed && !tga_is_RLE) { for (i=0; i < tga_height; ++i) { int y = tga_inverted ? tga_height -i - 1 : i; stbi_uc *tga_row = tga_data + y*tga_width*tga_comp; stbi__getn(s, tga_row, tga_width * tga_comp); } } else { // do I need to load a palette? if ( tga_indexed) { // any data to skip? (offset usually = 0) stbi__skip(s, tga_palette_start ); // load the palette tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_palette_bits / 8 ); if (!tga_palette) { STBI_FREE(tga_data); return stbi__errpuc("outofmem", "Out of memory"); } if (!stbi__getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { STBI_FREE(tga_data); STBI_FREE(tga_palette); return stbi__errpuc("bad palette", "Corrupt TGA"); } } // load the data for (i=0; i < tga_width * tga_height; ++i) { // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? if ( tga_is_RLE ) { if ( RLE_count == 0 ) { // yep, get the next byte as a RLE command int RLE_cmd = stbi__get8(s); RLE_count = 1 + (RLE_cmd & 127); RLE_repeating = RLE_cmd >> 7; read_next_pixel = 1; } else if ( !RLE_repeating ) { read_next_pixel = 1; } } else { read_next_pixel = 1; } // OK, if I need to read a pixel, do it now if ( read_next_pixel ) { // load however much data we did have if ( tga_indexed ) { // read in 1 byte, then perform the lookup int pal_idx = stbi__get8(s); if ( pal_idx >= tga_palette_len ) { // invalid index pal_idx = 0; } pal_idx *= tga_bits_per_pixel / 8; for (j = 0; j*8 < tga_bits_per_pixel; ++j) { raw_data[j] = tga_palette[pal_idx+j]; } } else { // read in the data raw for (j = 0; j*8 < tga_bits_per_pixel; ++j) { raw_data[j] = stbi__get8(s); } } // clear the reading flag for the next pixel read_next_pixel = 0; } // end of reading a pixel // copy data for (j = 0; j < tga_comp; ++j) tga_data[i*tga_comp+j] = raw_data[j]; // in case we're in RLE mode, keep counting down --RLE_count; } // do I need to invert the image? if ( tga_inverted ) { for (j = 0; j*2 < tga_height; ++j) { int index1 = j * tga_width * tga_comp; int index2 = (tga_height - 1 - j) * tga_width * tga_comp; for (i = tga_width * tga_comp; i > 0; --i) { unsigned char temp = tga_data[index1]; tga_data[index1] = tga_data[index2]; tga_data[index2] = temp; ++index1; ++index2; } } } // clear my palette, if I had one if ( tga_palette != NULL ) { STBI_FREE( tga_palette ); } } // swap RGB if (tga_comp >= 3) { unsigned char* tga_pixel = tga_data; for (i=0; i < tga_width * tga_height; ++i) { unsigned char temp = tga_pixel[0]; tga_pixel[0] = tga_pixel[2]; tga_pixel[2] = temp; tga_pixel += tga_comp; } } // convert to target component count if (req_comp && req_comp != tga_comp) tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); // the things I do to get rid of an error message, and yet keep // Microsoft's C compilers happy... [8^( tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; // OK, done return tga_data; } #endif // ************************************************************************************************* // Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB #ifndef STBI_NO_PSD static int stbi__psd_test(stbi__context *s) { int r = (stbi__get32be(s) == 0x38425053); stbi__rewind(s); return r; } static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { int pixelCount; int channelCount, compression; int channel, i, count, len; int w,h; stbi_uc *out; // Check identifier if (stbi__get32be(s) != 0x38425053) // "8BPS" return stbi__errpuc("not PSD", "Corrupt PSD image"); // Check file type version. if (stbi__get16be(s) != 1) return stbi__errpuc("wrong version", "Unsupported version of PSD image"); // Skip 6 reserved bytes. stbi__skip(s, 6 ); // Read the number of channels (R, G, B, A, etc). channelCount = stbi__get16be(s); if (channelCount < 0 || channelCount > 16) return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); // Read the rows and columns of the image. h = stbi__get32be(s); w = stbi__get32be(s); // Make sure the depth is 8 bits. if (stbi__get16be(s) != 8) return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 bit"); // Make sure the color mode is RGB. // Valid options are: // 0: Bitmap // 1: Grayscale // 2: Indexed color // 3: RGB color // 4: CMYK color // 7: Multichannel // 8: Duotone // 9: Lab color if (stbi__get16be(s) != 3) return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) stbi__skip(s,stbi__get32be(s) ); // Skip the image resources. (resolution, pen tool paths, etc) stbi__skip(s, stbi__get32be(s) ); // Skip the reserved data. stbi__skip(s, stbi__get32be(s) ); // Find out if the data is compressed. // Known values: // 0: no compression // 1: RLE compressed compression = stbi__get16be(s); if (compression > 1) return stbi__errpuc("bad compression", "PSD has an unknown compression format"); // Create the destination image. out = (stbi_uc *) stbi__malloc(4 * w*h); if (!out) return stbi__errpuc("outofmem", "Out of memory"); pixelCount = w*h; // Initialize the data to zero. //memset( out, 0, pixelCount * 4 ); // Finally, the image data. if (compression) { // RLE as used by .PSD and .TIFF // Loop until you get the number of unpacked bytes you are expecting: // Read the next source byte into n. // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. // Else if n is 128, noop. // Endloop // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, // which we're going to just skip. stbi__skip(s, h * channelCount * 2 ); // Read the RLE data by channel. for (channel = 0; channel < 4; channel++) { stbi_uc *p; p = out+channel; if (channel >= channelCount) { // Fill this channel with default data. for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; } else { // Read the RLE data. count = 0; while (count < pixelCount) { len = stbi__get8(s); if (len == 128) { // No-op. } else if (len < 128) { // Copy next len+1 bytes literally. len++; count += len; while (len) { *p = stbi__get8(s); p += 4; len--; } } else if (len > 128) { stbi_uc val; // Next -len+1 bytes in the dest are replicated from next source byte. // (Interpret len as a negative 8-bit int.) len ^= 0x0FF; len += 2; val = stbi__get8(s); count += len; while (len) { *p = val; p += 4; len--; } } } } } } else { // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) // where each channel consists of an 8-bit value for each pixel in the image. // Read the data by channel. for (channel = 0; channel < 4; channel++) { stbi_uc *p; p = out + channel; if (channel > channelCount) { // Fill this channel with default data. for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; } else { // Read the data. for (i = 0; i < pixelCount; i++) *p = stbi__get8(s), p += 4; } } } if (req_comp && req_comp != 4) { out = stbi__convert_format(out, 4, req_comp, w, h); if (out == NULL) return out; // stbi__convert_format frees input on failure } if (comp) *comp = channelCount; *y = h; *x = w; return out; } #endif // ************************************************************************************************* // Softimage PIC loader // by Tom Seddon // // See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format // See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ #ifndef STBI_NO_PIC static int stbi__pic_is4(stbi__context *s,const char *str) { int i; for (i=0; i<4; ++i) if (stbi__get8(s) != (stbi_uc)str[i]) return 0; return 1; } static int stbi__pic_test_core(stbi__context *s) { int i; if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) return 0; for(i=0;i<84;++i) stbi__get8(s); if (!stbi__pic_is4(s,"PICT")) return 0; return 1; } typedef struct { stbi_uc size,type,channel; } stbi__pic_packet; static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) { int mask=0x80, i; for (i=0; i<4; ++i, mask>>=1) { if (channel & mask) { if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); dest[i]=stbi__get8(s); } } return dest; } static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) { int mask=0x80,i; for (i=0;i<4; ++i, mask>>=1) if (channel&mask) dest[i]=src[i]; } static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) { int act_comp=0,num_packets=0,y,chained; stbi__pic_packet packets[10]; // this will (should...) cater for even some bizarre stuff like having data // for the same channel in multiple packets. do { stbi__pic_packet *packet; if (num_packets==sizeof(packets)/sizeof(packets[0])) return stbi__errpuc("bad format","too many packets"); packet = &packets[num_packets++]; chained = stbi__get8(s); packet->size = stbi__get8(s); packet->type = stbi__get8(s); packet->channel = stbi__get8(s); act_comp |= packet->channel; if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); } while (chained); *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? for(y=0; ytype) { default: return stbi__errpuc("bad format","packet has bad compression type"); case 0: {//uncompressed int x; for(x=0;xchannel,dest)) return 0; break; } case 1://Pure RLE { int left=width, i; while (left>0) { stbi_uc count,value[4]; count=stbi__get8(s); if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); if (count > left) count = (stbi_uc) left; if (!stbi__readval(s,packet->channel,value)) return 0; for(i=0; ichannel,dest,value); left -= count; } } break; case 2: {//Mixed RLE int left=width; while (left>0) { int count = stbi__get8(s), i; if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); if (count >= 128) { // Repeated stbi_uc value[4]; int i; if (count==128) count = stbi__get16be(s); else count -= 127; if (count > left) return stbi__errpuc("bad file","scanline overrun"); if (!stbi__readval(s,packet->channel,value)) return 0; for(i=0;ichannel,dest,value); } else { // Raw ++count; if (count>left) return stbi__errpuc("bad file","scanline overrun"); for(i=0;ichannel,dest)) return 0; } left-=count; } break; } } } } return result; } static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) { stbi_uc *result; int i, x,y; for (i=0; i<92; ++i) stbi__get8(s); x = stbi__get16be(s); y = stbi__get16be(s); if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); stbi__get32be(s); //skip `ratio' stbi__get16be(s); //skip `fields' stbi__get16be(s); //skip `pad' // intermediate buffer is RGBA result = (stbi_uc *) stbi__malloc(x*y*4); memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { STBI_FREE(result); result=0; } *px = x; *py = y; if (req_comp == 0) req_comp = *comp; result=stbi__convert_format(result,4,req_comp,x,y); return result; } static int stbi__pic_test(stbi__context *s) { int r = stbi__pic_test_core(s); stbi__rewind(s); return r; } #endif // ************************************************************************************************* // GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb #ifndef STBI_NO_GIF typedef struct { stbi__int16 prefix; stbi_uc first; stbi_uc suffix; } stbi__gif_lzw; typedef struct { int w,h; stbi_uc *out; // output buffer (always 4 components) int flags, bgindex, ratio, transparent, eflags; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; stbi__gif_lzw codes[4096]; stbi_uc *color_table; int parse, step; int lflags; int start_x, start_y; int max_x, max_y; int cur_x, cur_y; int line_size; } stbi__gif; static int stbi__gif_test_raw(stbi__context *s) { int sz; if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; sz = stbi__get8(s); if (sz != '9' && sz != '7') return 0; if (stbi__get8(s) != 'a') return 0; return 1; } static int stbi__gif_test(stbi__context *s) { int r = stbi__gif_test_raw(s); stbi__rewind(s); return r; } static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) { int i; for (i=0; i < num_entries; ++i) { pal[i][2] = stbi__get8(s); pal[i][1] = stbi__get8(s); pal[i][0] = stbi__get8(s); pal[i][3] = transp == i ? 0 : 255; } } static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) { stbi_uc version; if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return stbi__err("not GIF", "Corrupt GIF"); version = stbi__get8(s); if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); stbi__g_failure_reason = ""; g->w = stbi__get16le(s); g->h = stbi__get16le(s); g->flags = stbi__get8(s); g->bgindex = stbi__get8(s); g->ratio = stbi__get8(s); g->transparent = -1; if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments if (is_info) return 1; if (g->flags & 0x80) stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); return 1; } static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { stbi__gif g; if (!stbi__gif_header(s, &g, comp, 1)) { stbi__rewind( s ); return 0; } if (x) *x = g.w; if (y) *y = g.h; return 1; } static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty if (g->codes[code].prefix >= 0) stbi__out_gif_code(g, g->codes[code].prefix); if (g->cur_y >= g->max_y) return; p = &g->out[g->cur_x + g->cur_y]; c = &g->color_table[g->codes[code].suffix * 4]; if (c[3] >= 128) { p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; p[3] = c[3]; } g->cur_x += 4; if (g->cur_x >= g->max_x) { g->cur_x = g->start_x; g->cur_y += g->step; while (g->cur_y >= g->max_y && g->parse > 0) { g->step = (1 << g->parse) * g->line_size; g->cur_y = g->start_y + (g->step >> 1); --g->parse; } } } static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) { stbi_uc lzw_cs; stbi__int32 len, code; stbi__uint32 first; stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; stbi__gif_lzw *p; lzw_cs = stbi__get8(s); clear = 1 << lzw_cs; first = 1; codesize = lzw_cs + 1; codemask = (1 << codesize) - 1; bits = 0; valid_bits = 0; for (code = 0; code < clear; code++) { g->codes[code].prefix = -1; g->codes[code].first = (stbi_uc) code; g->codes[code].suffix = (stbi_uc) code; } // support no starting clear code avail = clear+2; oldcode = -1; len = 0; for(;;) { if (valid_bits < codesize) { if (len == 0) { len = stbi__get8(s); // start new block if (len == 0) return g->out; } --len; bits |= (stbi__int32) stbi__get8(s) << valid_bits; valid_bits += 8; } else { stbi__int32 code = bits & codemask; bits >>= codesize; valid_bits -= codesize; // @OPTIMIZE: is there some way we can accelerate the non-clear path? if (code == clear) { // clear code codesize = lzw_cs + 1; codemask = (1 << codesize) - 1; avail = clear + 2; oldcode = -1; first = 0; } else if (code == clear + 1) { // end of stream code stbi__skip(s, len); while ((len = stbi__get8(s)) > 0) stbi__skip(s,len); return g->out; } else if (code <= avail) { if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); if (oldcode >= 0) { p = &g->codes[avail++]; if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); p->prefix = (stbi__int16) oldcode; p->first = g->codes[oldcode].first; p->suffix = (code == avail) ? p->first : g->codes[code].first; } else if (code == avail) return stbi__errpuc("illegal code in raster", "Corrupt GIF"); stbi__out_gif_code(g, (stbi__uint16) code); if ((avail & codemask) == 0 && avail <= 0x0FFF) { codesize++; codemask = (1 << codesize) - 1; } oldcode = code; } else { return stbi__errpuc("illegal code in raster", "Corrupt GIF"); } } } } static void stbi__fill_gif_background(stbi__gif *g) { int i; stbi_uc *c = g->pal[g->bgindex]; // @OPTIMIZE: write a dword at a time for (i = 0; i < g->w * g->h * 4; i += 4) { stbi_uc *p = &g->out[i]; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; p[3] = c[3]; } } // this function is designed to support animated gifs, although stb_image doesn't support it static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) { int i; stbi_uc *old_out = 0; if (g->out == 0) { if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); stbi__fill_gif_background(g); } else { // animated-gif-only path if (((g->eflags & 0x1C) >> 2) == 3) { old_out = g->out; g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); memcpy(g->out, old_out, g->w*g->h*4); } } for (;;) { switch (stbi__get8(s)) { case 0x2C: /* Image Descriptor */ { stbi__int32 x, y, w, h; stbi_uc *o; x = stbi__get16le(s); y = stbi__get16le(s); w = stbi__get16le(s); h = stbi__get16le(s); if (((x + w) > (g->w)) || ((y + h) > (g->h))) return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); g->line_size = g->w * 4; g->start_x = x * 4; g->start_y = y * g->line_size; g->max_x = g->start_x + w * 4; g->max_y = g->start_y + h * g->line_size; g->cur_x = g->start_x; g->cur_y = g->start_y; g->lflags = stbi__get8(s); if (g->lflags & 0x40) { g->step = 8 * g->line_size; // first interlaced spacing g->parse = 3; } else { g->step = g->line_size; g->parse = 0; } if (g->lflags & 0x80) { stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); g->color_table = (stbi_uc *) g->lpal; } else if (g->flags & 0x80) { for (i=0; i < 256; ++i) // @OPTIMIZE: stbi__jpeg_reset only the previous transparent g->pal[i][3] = 255; if (g->transparent >= 0 && (g->eflags & 0x01)) g->pal[g->transparent][3] = 0; g->color_table = (stbi_uc *) g->pal; } else return stbi__errpuc("missing color table", "Corrupt GIF"); o = stbi__process_gif_raster(s, g); if (o == NULL) return NULL; if (req_comp && req_comp != 4) o = stbi__convert_format(o, 4, req_comp, g->w, g->h); return o; } case 0x21: // Comment Extension. { int len; if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { g->eflags = stbi__get8(s); stbi__get16le(s); // delay g->transparent = stbi__get8(s); } else { stbi__skip(s, len); break; } } while ((len = stbi__get8(s)) != 0) stbi__skip(s, len); break; } case 0x3B: // gif stream termination code return (stbi_uc *) s; // using '1' causes warning on some compilers default: return stbi__errpuc("unknown code", "Corrupt GIF"); } } } static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi_uc *u = 0; stbi__gif g; memset(&g, 0, sizeof(g)); u = stbi__gif_load_next(s, &g, comp, req_comp); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker if (u) { *x = g.w; *y = g.h; } return u; } static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) { return stbi__gif_info_raw(s,x,y,comp); } #endif // ************************************************************************************************* // Radiance RGBE HDR loader // originally by Nicolas Schulz #ifndef STBI_NO_HDR static int stbi__hdr_test_core(stbi__context *s) { const char *signature = "#?RADIANCE\n"; int i; for (i=0; signature[i]; ++i) if (stbi__get8(s) != signature[i]) return 0; return 1; } static int stbi__hdr_test(stbi__context* s) { int r = stbi__hdr_test_core(s); stbi__rewind(s); return r; } #define STBI__HDR_BUFLEN 1024 static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) { int len=0; char c = '\0'; c = (char) stbi__get8(z); while (!stbi__at_eof(z) && c != '\n') { buffer[len++] = c; if (len == STBI__HDR_BUFLEN-1) { // flush to end of line while (!stbi__at_eof(z) && stbi__get8(z) != '\n') ; break; } c = (char) stbi__get8(z); } buffer[len] = 0; return buffer; } static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) { if ( input[3] != 0 ) { float f1; // Exponent f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); if (req_comp <= 2) output[0] = (input[0] + input[1] + input[2]) * f1 / 3; else { output[0] = input[0] * f1; output[1] = input[1] * f1; output[2] = input[2] * f1; } if (req_comp == 2) output[1] = 1; if (req_comp == 4) output[3] = 1; } else { switch (req_comp) { case 4: output[3] = 1; /* fallthrough */ case 3: output[0] = output[1] = output[2] = 0; break; case 2: output[1] = 1; /* fallthrough */ case 1: output[0] = 0; break; } } } static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { char buffer[STBI__HDR_BUFLEN]; char *token; int valid = 0; int width, height; stbi_uc *scanline; float *hdr_data; int len; unsigned char count, value; int i, j, k, c1,c2, z; // Check identifier if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) return stbi__errpf("not HDR", "Corrupt HDR image"); // Parse header for(;;) { token = stbi__hdr_gettoken(s,buffer); if (token[0] == 0) break; if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; } if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); // Parse width and height // can't use sscanf() if we're not using stdio! token = stbi__hdr_gettoken(s,buffer); if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); token += 3; height = (int) strtol(token, &token, 10); while (*token == ' ') ++token; if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); token += 3; width = (int) strtol(token, NULL, 10); *x = width; *y = height; if (comp) *comp = 3; if (req_comp == 0) req_comp = 3; // Read data hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); // Load image data // image data is stored as some number of sca if ( width < 8 || width >= 32768) { // Read flat data for (j=0; j < height; ++j) { for (i=0; i < width; ++i) { stbi_uc rgbe[4]; main_decode_loop: stbi__getn(s, rgbe, 4); stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); } } } else { // Read RLE-encoded data scanline = NULL; for (j = 0; j < height; ++j) { c1 = stbi__get8(s); c2 = stbi__get8(s); len = stbi__get8(s); if (c1 != 2 || c2 != 2 || (len & 0x80)) { // not run-length encoded, so we have to actually use THIS data as a decoded // pixel (note this can't be a valid pixel--one of RGB must be >= 128) stbi_uc rgbe[4]; rgbe[0] = (stbi_uc) c1; rgbe[1] = (stbi_uc) c2; rgbe[2] = (stbi_uc) len; rgbe[3] = (stbi_uc) stbi__get8(s); stbi__hdr_convert(hdr_data, rgbe, req_comp); i = 1; j = 0; STBI_FREE(scanline); goto main_decode_loop; // yes, this makes no sense } len <<= 8; len |= stbi__get8(s); if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); for (k = 0; k < 4; ++k) { i = 0; while (i < width) { count = stbi__get8(s); if (count > 128) { // Run value = stbi__get8(s); count -= 128; for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = stbi__get8(s); } } } for (i=0; i < width; ++i) stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); } STBI_FREE(scanline); } return hdr_data; } static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) { char buffer[STBI__HDR_BUFLEN]; char *token; int valid = 0; if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { stbi__rewind( s ); return 0; } for(;;) { token = stbi__hdr_gettoken(s,buffer); if (token[0] == 0) break; if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; } if (!valid) { stbi__rewind( s ); return 0; } token = stbi__hdr_gettoken(s,buffer); if (strncmp(token, "-Y ", 3)) { stbi__rewind( s ); return 0; } token += 3; *y = (int) strtol(token, &token, 10); while (*token == ' ') ++token; if (strncmp(token, "+X ", 3)) { stbi__rewind( s ); return 0; } token += 3; *x = (int) strtol(token, NULL, 10); *comp = 3; return 1; } #endif // STBI_NO_HDR #ifndef STBI_NO_BMP static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) { int hsz; if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') { stbi__rewind( s ); return 0; } stbi__skip(s,12); hsz = stbi__get32le(s); if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) { stbi__rewind( s ); return 0; } if (hsz == 12) { *x = stbi__get16le(s); *y = stbi__get16le(s); } else { *x = stbi__get32le(s); *y = stbi__get32le(s); } if (stbi__get16le(s) != 1) { stbi__rewind( s ); return 0; } *comp = stbi__get16le(s) / 8; return 1; } #endif #ifndef STBI_NO_PSD static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) { int channelCount; if (stbi__get32be(s) != 0x38425053) { stbi__rewind( s ); return 0; } if (stbi__get16be(s) != 1) { stbi__rewind( s ); return 0; } stbi__skip(s, 6); channelCount = stbi__get16be(s); if (channelCount < 0 || channelCount > 16) { stbi__rewind( s ); return 0; } *y = stbi__get32be(s); *x = stbi__get32be(s); if (stbi__get16be(s) != 8) { stbi__rewind( s ); return 0; } if (stbi__get16be(s) != 3) { stbi__rewind( s ); return 0; } *comp = 4; return 1; } #endif #ifndef STBI_NO_PIC static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) { int act_comp=0,num_packets=0,chained; stbi__pic_packet packets[10]; stbi__skip(s, 92); *x = stbi__get16be(s); *y = stbi__get16be(s); if (stbi__at_eof(s)) return 0; if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { stbi__rewind( s ); return 0; } stbi__skip(s, 8); do { stbi__pic_packet *packet; if (num_packets==sizeof(packets)/sizeof(packets[0])) return 0; packet = &packets[num_packets++]; chained = stbi__get8(s); packet->size = stbi__get8(s); packet->type = stbi__get8(s); packet->channel = stbi__get8(s); act_comp |= packet->channel; if (stbi__at_eof(s)) { stbi__rewind( s ); return 0; } if (packet->size != 8) { stbi__rewind( s ); return 0; } } while (chained); *comp = (act_comp & 0x10 ? 4 : 3); return 1; } #endif // ************************************************************************************************* // Portable Gray Map and Portable Pixel Map loader // by Ken Miller // // PGM: http://netpbm.sourceforge.net/doc/pgm.html // PPM: http://netpbm.sourceforge.net/doc/ppm.html // // Known limitations: // Does not support comments in the header section // Does not support ASCII image data (formats P2 and P3) // Does not support 16-bit-per-channel #ifndef STBI_NO_PNM static int stbi__pnm_test(stbi__context *s) { char p, t; p = (char) stbi__get8(s); t = (char) stbi__get8(s); if (p != 'P' || (t != '5' && t != '6')) { stbi__rewind( s ); return 0; } return 1; } static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi_uc *out; if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) return 0; *x = s->img_x; *y = s->img_y; *comp = s->img_n; out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); if (!out) return stbi__errpuc("outofmem", "Out of memory"); stbi__getn(s, out, s->img_n * s->img_x * s->img_y); if (req_comp && req_comp != s->img_n) { out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); if (out == NULL) return out; // stbi__convert_format frees input on failure } return out; } static int stbi__pnm_isspace(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; } static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) { while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) *c = (char) stbi__get8(s); } static int stbi__pnm_isdigit(char c) { return c >= '0' && c <= '9'; } static int stbi__pnm_getinteger(stbi__context *s, char *c) { int value = 0; while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { value = value*10 + (*c - '0'); *c = (char) stbi__get8(s); } return value; } static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) { int maxv; char c, p, t; stbi__rewind( s ); // Get identifier p = (char) stbi__get8(s); t = (char) stbi__get8(s); if (p != 'P' || (t != '5' && t != '6')) { stbi__rewind( s ); return 0; } *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm c = (char) stbi__get8(s); stbi__pnm_skip_whitespace(s, &c); *x = stbi__pnm_getinteger(s, &c); // read width stbi__pnm_skip_whitespace(s, &c); *y = stbi__pnm_getinteger(s, &c); // read height stbi__pnm_skip_whitespace(s, &c); maxv = stbi__pnm_getinteger(s, &c); // read max value if (maxv > 255) return stbi__err("max value > 255", "PPM image not 8-bit"); else return 1; } #endif static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) { #ifndef STBI_NO_JPEG if (stbi__jpeg_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PNG if (stbi__png_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_GIF if (stbi__gif_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_BMP if (stbi__bmp_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PSD if (stbi__psd_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PIC if (stbi__pic_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PNM if (stbi__pnm_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_HDR if (stbi__hdr_info(s, x, y, comp)) return 1; #endif // test tga last because it's a crappy test! #ifndef STBI_NO_TGA if (stbi__tga_info(s, x, y, comp)) return 1; #endif return stbi__err("unknown image type", "Image not of any known type, or corrupt"); } #ifndef STBI_NO_STDIO STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) { FILE *f = stbi__fopen(filename, "rb"); int result; if (!f) return stbi__err("can't fopen", "Unable to open file"); result = stbi_info_from_file(f, x, y, comp); fclose(f); return result; } STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) { int r; stbi__context s; long pos = ftell(f); stbi__start_file(&s, f); r = stbi__info_main(&s,x,y,comp); fseek(f,pos,SEEK_SET); return r; } #endif // !STBI_NO_STDIO STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__info_main(&s,x,y,comp); } STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); return stbi__info_main(&s,x,y,comp); } #endif // STB_IMAGE_IMPLEMENTATION /* revision history: 2.02 (2015-01-19) fix incorrect assert, fix warning 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) progressive JPEG (stb) PGM/PPM support (Ken Miller) STBI_MALLOC,STBI_REALLOC,STBI_FREE GIF bugfix -- seemingly never worked STBI_NO_*, STBI_ONLY_* 1.48 (2014-12-14) fix incorrectly-named assert() 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) optimize PNG (ryg) fix bug in interlaced PNG with user-specified channel count (stb) 1.46 (2014-08-26) fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG 1.45 (2014-08-16) fix MSVC-ARM internal compiler error by wrapping malloc 1.44 (2014-08-07) various warning fixes from Ronny Chevalier 1.43 (2014-07-15) fix MSVC-only compiler problem in code changed in 1.42 1.42 (2014-07-09) don't define _CRT_SECURE_NO_WARNINGS (affects user code) fixes to stbi__cleanup_jpeg path added STBI_ASSERT to avoid requiring assert.h 1.41 (2014-06-25) fix search&replace from 1.36 that messed up comments/error messages 1.40 (2014-06-22) fix gcc struct-initialization warning 1.39 (2014-06-15) fix to TGA optimization when req_comp != number of components in TGA; fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) add support for BMP version 5 (more ignored fields) 1.38 (2014-06-06) suppress MSVC warnings on integer casts truncating values fix accidental rename of 'skip' field of I/O 1.37 (2014-06-04) remove duplicate typedef 1.36 (2014-06-03) convert to header file single-file library if de-iphone isn't set, load iphone images color-swapped instead of returning NULL 1.35 (2014-05-27) various warnings fix broken STBI_SIMD path fix bug where stbi_load_from_file no longer left file pointer in correct place fix broken non-easy path for 32-bit BMP (possibly never used) TGA optimization by Arseny Kapoulkine 1.34 (unknown) use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case 1.33 (2011-07-14) make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements 1.32 (2011-07-13) support for "info" function for all supported filetypes (SpartanJ) 1.31 (2011-06-20) a few more leak fixes, bug in PNG handling (SpartanJ) 1.30 (2011-06-11) added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) removed deprecated format-specific test/load functions removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) fix inefficiency in decoding 32-bit BMP (David Woo) 1.29 (2010-08-16) various warning fixes from Aurelien Pocheville 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ) 1.27 (2010-08-01) cast-to-stbi_uc to fix warnings 1.26 (2010-07-24) fix bug in file buffering for PNG reported by SpartanJ 1.25 (2010-07-17) refix trans_data warning (Won Chun) 1.24 (2010-07-12) perf improvements reading from files on platforms with lock-heavy fgetc() minor perf improvements for jpeg deprecated type-specific functions so we'll get feedback if they're needed attempt to fix trans_data warning (Won Chun) 1.23 fixed bug in iPhone support 1.22 (2010-07-10) removed image *writing* support stbi_info support from Jetro Lauha GIF support from Jean-Marc Lienher iPhone PNG-extensions from James Brown warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) 1.21 fix use of 'stbi_uc' in header (reported by jon blow) 1.20 added support for Softimage PIC, by Tom Seddon 1.19 bug in interlaced PNG corruption check (found by ryg) 1.18 2008-08-02 fix a threading bug (local mutable static) 1.17 support interlaced PNG 1.16 major bugfix - stbi__convert_format converted one too many pixels 1.15 initialize some fields for thread safety 1.14 fix threadsafe conversion bug header-file-only version (#define STBI_HEADER_FILE_ONLY before including) 1.13 threadsafe 1.12 const qualifiers in the API 1.11 Support installable IDCT, colorspace conversion routines 1.10 Fixes for 64-bit (don't use "unsigned long") optimized upsampling by Fabian "ryg" Giesen 1.09 Fix format-conversion for PSD code (bad global variables!) 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz 1.07 attempt to fix C++ warning/errors again 1.06 attempt to fix C++ warning/errors again 1.05 fix TGA loading to return correct *comp and use good luminance calc 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR 1.02 support for (subset of) HDR files, float interface for preferred access to them 1.01 fix bug: possible bug in handling right-side up bmps... not sure fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all 1.00 interface to zlib that skips zlib header 0.99 correct handling of alpha in palette 0.98 TGA loader by lonesock; dynamically add loaders (untested) 0.97 jpeg errors on too large a file; also catch another malloc failure 0.96 fix detection of invalid v value - particleman@mollyrocket forum 0.95 during header scan, seek to markers in case of padding 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same 0.93 handle jpegtran output; verbose errors 0.92 read 4,8,16,24,32-bit BMP files of several formats 0.91 output 24-bit Windows 3.0 BMP files 0.90 fix a few more warnings; bump version number to approach 1.0 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd 0.60 fix compiling as c++ 0.59 fix warnings: merge Dave Moore's -Wall fixes 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available 0.56 fix bug: zlib uncompressed mode len vs. nlen 0.55 fix bug: restart_interval not initialized to 0 0.54 allow NULL for 'int *comp' 0.53 fix bug in png 3->4; speedup png decoding 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments 0.51 obey req_comp requests, 1-component jpegs return as 1-component, on 'test' only check type, not whether we support this variant 0.50 first released version */ yquake2-QUAKE2_5_32/src/client/refresh/files/sp2.c0000644000175000017500000000411612615162034022276 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * .sp2 sprites * * ======================================================================= */ #include "../header/local.h" extern int modfilelen; void LoadSP2(model_t *mod, void *buffer) { dsprite_t *sprin, *sprout; int i; sprin = (dsprite_t *)buffer; sprout = Hunk_Alloc(modfilelen); sprout->ident = LittleLong(sprin->ident); sprout->version = LittleLong(sprin->version); sprout->numframes = LittleLong(sprin->numframes); if (sprout->version != SPRITE_VERSION) { VID_Error(ERR_DROP, "%s has wrong version number (%i should be %i)", mod->name, sprout->version, SPRITE_VERSION); } if (sprout->numframes > MAX_MD2SKINS) { VID_Error(ERR_DROP, "%s has too many frames (%i > %i)", mod->name, sprout->numframes, MAX_MD2SKINS); } /* byte swap everything */ for (i = 0; i < sprout->numframes; i++) { sprout->frames[i].width = LittleLong(sprin->frames[i].width); sprout->frames[i].height = LittleLong(sprin->frames[i].height); sprout->frames[i].origin_x = LittleLong(sprin->frames[i].origin_x); sprout->frames[i].origin_y = LittleLong(sprin->frames[i].origin_y); memcpy(sprout->frames[i].name, sprin->frames[i].name, MAX_SKINNAME); mod->skins[i] = R_FindImage(sprout->frames[i].name, it_sprite); } mod->type = mod_sprite; } yquake2-QUAKE2_5_32/src/client/refresh/r_warp.c0000644000175000017500000002724712615162034021774 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Warps. Used on water surfaces und for skybox rotation. * * ======================================================================= */ #include "header/local.h" #define TURBSCALE (256.0 / (2 * M_PI)) #define SUBDIVIDE_SIZE 64 #define ON_EPSILON 0.1 /* point on plane side epsilon */ #define MAX_CLIP_VERTS 64 extern model_t *loadmodel; char skyname[MAX_QPATH]; float skyrotate; vec3_t skyaxis; image_t *sky_images[6]; msurface_t *warpface; int skytexorder[6] = {0, 2, 1, 3, 4, 5}; /* 3dstudio environment map names */ char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; float r_turbsin[] = { #include "constants/warpsin.h" }; 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; 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 */ }; int vec_to_st[6][3] = { {-2, 3, 1}, {2, 3, -1}, {1, 3, 2}, {-1, 3, -2}, {-2, -1, 3}, {-2, 1, -3} }; float skymins[2][6], skymaxs[2][6]; float sky_min, sky_max; void R_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 < numverts; i++) { for (j = 0; j < 3; j++, v++) { if (*v < mins[j]) { mins[j] = *v; } if (*v > maxs[j]) { maxs[j] = *v; } } } } void R_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; vec3_t total; float total_s, total_t; if (numverts > 60) { VID_Error(ERR_DROP, "numverts = %i", numverts); } R_BoundPoly(numverts, verts, mins, maxs); for (i = 0; i < 3; i++) { m = (mins[i] + maxs[i]) * 0.5; m = SUBDIVIDE_SIZE * floor(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 < numverts; j++, v += 3) { dist[j] = *v - m; } /* wrap cases */ dist[j] = dist[0]; v -= i; VectorCopy(verts, v); f = b = 0; v = verts; for (j = 0; j < numverts; j++, v += 3) { if (dist[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++; } } R_SubdividePolygon(f, front[0]); R_SubdividePolygon(b, back[0]); return; } /* add a point in the center to help keep warp valid */ poly = Hunk_Alloc(sizeof(glpoly_t) + ((numverts - 4) + 2) * VERTEXSIZE * sizeof(float)); poly->next = warpface->polys; warpface->polys = poly; poly->numverts = numverts + 2; VectorClear(total); total_s = 0; total_t = 0; for (i = 0; i < numverts; i++, verts += 3) { VectorCopy(verts, poly->verts[i + 1]); s = DotProduct(verts, warpface->texinfo->vecs[0]); t = DotProduct(verts, warpface->texinfo->vecs[1]); total_s += s; total_t += t; VectorAdd(total, verts, total); poly->verts[i + 1][3] = s; poly->verts[i + 1][4] = t; } VectorScale(total, (1.0 / numverts), poly->verts[0]); poly->verts[0][3] = total_s / numverts; poly->verts[0][4] = total_t / numverts; /* copy first vertex to last */ memcpy(poly->verts[i + 1], poly->verts[1], sizeof(poly->verts[0])); } /* * Breaks a polygon up along axial 64 unit * boundaries so that turbulent and sky warps * can be done reasonably. */ void R_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; i < fa->numedges; 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++; } R_SubdividePolygon(numverts, verts[0]); } /* * Does a water warp on the pre-fragmented glpoly_t chain */ void R_EmitWaterPolys(msurface_t *fa) { glpoly_t *p, *bp; float *v; int i; float s, t, os, ot; float scroll; float rdt = r_newrefdef.time; if (fa->texinfo->flags & SURF_FLOWING) { scroll = -64 * ((r_newrefdef.time * 0.5) - (int)(r_newrefdef.time * 0.5)); } else { scroll = 0; } for (bp = fa->polys; bp; bp = bp->next) { p = bp; glBegin(GL_TRIANGLE_FAN); for (i = 0, v = p->verts[0]; i < p->numverts; i++, v += VERTEXSIZE) { os = v[3]; ot = v[4]; s = os + r_turbsin[(int)((ot * 0.125 + r_newrefdef.time) * TURBSCALE) & 255]; s += scroll; s *= (1.0 / 64); t = ot + r_turbsin[(int)((os * 0.125 + rdt) * TURBSCALE) & 255]; t *= (1.0 / 64); glTexCoord2f(s, t); glVertex3fv(v); } glEnd(); } } void R_DrawSkyPolygon(int nump, vec3_t vecs) { int i, j; vec3_t v, av; float s, t, dv; int axis; float *vp; c_sky++; /* decide which face it maps to */ VectorCopy(vec3_origin, v); for (i = 0, vp = vecs; i < nump; i++, vp += 3) { VectorAdd(vp, v, v); } av[0] = fabs(v[0]); av[1] = fabs(v[1]); av[2] = fabs(v[2]); if ((av[0] > av[1]) && (av[0] > av[2])) { if (v[0] < 0) { axis = 1; } else { axis = 0; } } else if ((av[1] > av[2]) && (av[1] > av[0])) { if (v[1] < 0) { axis = 3; } else { axis = 2; } } else { if (v[2] < 0) { axis = 5; } else { axis = 4; } } /* project new texture coords */ for (i = 0; i < nump; i++, vecs += 3) { j = vec_to_st[axis][2]; if (j > 0) { dv = vecs[j - 1]; } else { dv = -vecs[-j - 1]; } if (dv < 0.001) { continue; /* don't divide by zero */ } j = vec_to_st[axis][0]; if (j < 0) { s = -vecs[-j - 1] / dv; } else { s = vecs[j - 1] / dv; } j = vec_to_st[axis][1]; if (j < 0) { t = -vecs[-j - 1] / dv; } else { t = 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; } } } void R_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) { VID_Error(ERR_DROP, "R_ClipSkyPolygon: MAX_CLIP_VERTS"); } if (stage == 6) { /* fully clipped, so draw it */ R_DrawSkyPolygon(nump, vecs); return; } front = back = false; norm = skyclip[stage]; for (i = 0, v = vecs; i < nump; i++, v += 3) { d = DotProduct(v, norm); if (d > ON_EPSILON) { front = true; sides[i] = SIDE_FRONT; } else if (d < -ON_EPSILON) { back = true; sides[i] = SIDE_BACK; } else { sides[i] = SIDE_ON; } dists[i] = d; } if (!front || !back) { /* not clipped */ R_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 < nump; i++, v += 3) { switch (sides[i]) { case SIDE_FRONT: VectorCopy(v, newv[0][newc[0]]); newc[0]++; break; case SIDE_BACK: VectorCopy(v, newv[1][newc[1]]); newc[1]++; break; case SIDE_ON: VectorCopy(v, newv[0][newc[0]]); newc[0]++; VectorCopy(v, newv[1][newc[1]]); newc[1]++; break; } if ((sides[i] == SIDE_ON) || (sides[i + 1] == SIDE_ON) || (sides[i + 1] == sides[i])) { continue; } d = dists[i] / (dists[i] - dists[i + 1]); for (j = 0; j < 3; j++) { e = v[j] + d * (v[j + 3] - v[j]); newv[0][newc[0]][j] = e; newv[1][newc[1]][j] = e; } newc[0]++; newc[1]++; } /* continue */ R_ClipSkyPolygon(newc[0], newv[0][0], stage + 1); R_ClipSkyPolygon(newc[1], newv[1][0], stage + 1); } void R_AddSkySurface(msurface_t *fa) { int i; vec3_t verts[MAX_CLIP_VERTS]; glpoly_t *p; /* calculate vertex values for sky box */ for (p = fa->polys; p; p = p->next) { for (i = 0; i < p->numverts; i++) { VectorSubtract(p->verts[i], r_origin, verts[i]); } R_ClipSkyPolygon(p->numverts, verts[0], 0); } } void R_ClearSkyBox(void) { int i; for (i = 0; i < 6; i++) { skymins[0][i] = skymins[1][i] = 9999; skymaxs[0][i] = skymaxs[1][i] = -9999; } } void R_MakeSkyVec(float s, float t, int axis) { vec3_t v, b; int j, k; if (gl_farsee->value == 0) { b[0] = s * 2300; b[1] = t * 2300; b[2] = 2300; } else { b[0] = s * 4096; b[1] = t * 4096; b[2] = 4096; } for (j = 0; j < 3; j++) { k = st_to_vec[axis][j]; if (k < 0) { v[j] = -b[-k - 1]; } else { v[j] = b[k - 1]; } } /* avoid bilerp seam */ s = (s + 1) * 0.5; t = (t + 1) * 0.5; if (s < sky_min) { s = sky_min; } else if (s > sky_max) { s = sky_max; } if (t < sky_min) { t = sky_min; } else if (t > sky_max) { t = sky_max; } t = 1.0 - t; glTexCoord2f(s, t); glVertex3fv(v); } void R_DrawSkyBox(void) { int i; if (skyrotate) { /* check for no sky at all */ for (i = 0; i < 6; i++) { if ((skymins[0][i] < skymaxs[0][i]) && (skymins[1][i] < skymaxs[1][i])) { break; } } if (i == 6) { return; /* nothing visible */ } } glPushMatrix(); glTranslatef(r_origin[0], r_origin[1], r_origin[2]); glRotatef(r_newrefdef.time * skyrotate, skyaxis[0], skyaxis[1], skyaxis[2]); for (i = 0; i < 6; i++) { if (skyrotate) { skymins[0][i] = -1; skymins[1][i] = -1; skymaxs[0][i] = 1; skymaxs[1][i] = 1; } if ((skymins[0][i] >= skymaxs[0][i]) || (skymins[1][i] >= skymaxs[1][i])) { continue; } R_Bind(sky_images[skytexorder[i]]->texnum); glBegin(GL_QUADS); R_MakeSkyVec(skymins[0][i], skymins[1][i], i); R_MakeSkyVec(skymins[0][i], skymaxs[1][i], i); R_MakeSkyVec(skymaxs[0][i], skymaxs[1][i], i); R_MakeSkyVec(skymaxs[0][i], skymins[1][i], i); glEnd(); } glPopMatrix(); } void R_SetSky(char *name, float rotate, vec3_t axis) { int i; char pathname[MAX_QPATH]; Q_strlcpy(skyname, name, sizeof(skyname)); skyrotate = rotate; VectorCopy(axis, skyaxis); for (i = 0; i < 6; i++) { if (qglColorTableEXT && gl_ext_palettedtexture->value) { Com_sprintf(pathname, sizeof(pathname), "env/%s%s.pcx", skyname, suf[i]); } else { Com_sprintf(pathname, sizeof(pathname), "env/%s%s.tga", skyname, suf[i]); } sky_images[i] = R_FindImage(pathname, it_sky); if (!sky_images[i]) { sky_images[i] = r_notexture; } sky_min = 1.0 / 512; sky_max = 511.0 / 512; } } yquake2-QUAKE2_5_32/src/client/refresh/r_mesh.c0000644000175000017500000004145012615162034021747 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Mesh handling * * ======================================================================= */ #include "header/local.h" #define NUMVERTEXNORMALS 162 #define SHADEDOT_QUANT 16 float r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "constants/anorms.h" }; /* precalculated dot products for quantized angles */ float r_avertexnormal_dots[SHADEDOT_QUANT][256] = #include "constants/anormtab.h" ; typedef float vec4_t[4]; static vec4_t s_lerped[MAX_VERTS]; vec3_t shadevector; float shadelight[3]; float *shadedots = r_avertexnormal_dots[0]; extern vec3_t lightspot; extern qboolean have_stencil; void R_LerpVerts(int nverts, dtrivertx_t *v, dtrivertx_t *ov, dtrivertx_t *verts, float *lerp, float move[3], float frontv[3], float backv[3]) { int i; if (currententity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) { for (i = 0; i < nverts; i++, v++, ov++, lerp += 4) { float *normal = r_avertexnormals[verts[i].lightnormalindex]; lerp[0] = move[0] + ov->v[0] * backv[0] + v->v[0] * frontv[0] + normal[0] * POWERSUIT_SCALE; lerp[1] = move[1] + ov->v[1] * backv[1] + v->v[1] * frontv[1] + normal[1] * POWERSUIT_SCALE; lerp[2] = move[2] + ov->v[2] * backv[2] + v->v[2] * frontv[2] + normal[2] * POWERSUIT_SCALE; } } else { for (i = 0; i < nverts; i++, v++, ov++, lerp += 4) { lerp[0] = move[0] + ov->v[0] * backv[0] + v->v[0] * frontv[0]; lerp[1] = move[1] + ov->v[1] * backv[1] + v->v[1] * frontv[1]; lerp[2] = move[2] + ov->v[2] * backv[2] + v->v[2] * frontv[2]; } } } /* * Interpolates between two frames and origins */ void R_DrawAliasFrameLerp(dmdl_t *paliashdr, float backlerp) { float l; daliasframe_t *frame, *oldframe; dtrivertx_t *v, *ov, *verts; int *order; int count; float frontlerp; float alpha; vec3_t move, delta, vectors[3]; vec3_t frontv, backv; int i; int index_xyz; float *lerp; frame = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames + currententity->frame * paliashdr->framesize); verts = v = frame->verts; oldframe = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames + currententity->oldframe * paliashdr->framesize); ov = oldframe->verts; order = (int *)((byte *)paliashdr + paliashdr->ofs_glcmds); if (currententity->flags & RF_TRANSLUCENT) { alpha = currententity->alpha; } else { alpha = 1.0; } if (currententity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) { glDisable(GL_TEXTURE_2D); } frontlerp = 1.0 - backlerp; /* move should be the delta back to the previous frame * backlerp */ VectorSubtract(currententity->oldorigin, currententity->origin, delta); AngleVectors(currententity->angles, vectors[0], vectors[1], vectors[2]); move[0] = DotProduct(delta, vectors[0]); /* forward */ move[1] = -DotProduct(delta, vectors[1]); /* left */ move[2] = DotProduct(delta, vectors[2]); /* up */ VectorAdd(move, oldframe->translate, move); for (i = 0; i < 3; i++) { move[i] = backlerp * move[i] + frontlerp * frame->translate[i]; } for (i = 0; i < 3; i++) { frontv[i] = frontlerp * frame->scale[i]; backv[i] = backlerp * oldframe->scale[i]; } lerp = s_lerped[0]; R_LerpVerts(paliashdr->num_xyz, v, ov, verts, lerp, move, frontv, backv); if (gl_vertex_arrays->value) { float colorArray[MAX_VERTS * 4]; glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 16, s_lerped); if (currententity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) { glColor4f(shadelight[0], shadelight[1], shadelight[2], alpha); } else { glEnableClientState(GL_COLOR_ARRAY); glColorPointer(3, GL_FLOAT, 0, colorArray); /* pre light everything */ for (i = 0; i < paliashdr->num_xyz; i++) { float l = shadedots[verts[i].lightnormalindex]; colorArray[i * 3 + 0] = l * shadelight[0]; colorArray[i * 3 + 1] = l * shadelight[1]; colorArray[i * 3 + 2] = l * shadelight[2]; } } if (qglLockArraysEXT != 0) { qglLockArraysEXT(0, paliashdr->num_xyz); } while (1) { /* get the vertex count and primitive type */ count = *order++; if (!count) { break; /* done */ } if (count < 0) { count = -count; glBegin(GL_TRIANGLE_FAN); } else { glBegin(GL_TRIANGLE_STRIP); } if (currententity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) { do { index_xyz = order[2]; order += 3; glVertex3fv(s_lerped[index_xyz]); } while (--count); } else { do { /* texture coordinates come from the draw list */ glTexCoord2f(((float *)order)[0], ((float *)order)[1]); index_xyz = order[2]; order += 3; glArrayElement(index_xyz); } while (--count); } glEnd(); } if (qglUnlockArraysEXT != 0) { qglUnlockArraysEXT(); } } else { while (1) { /* get the vertex count and primitive type */ count = *order++; if (!count) { break; /* done */ } if (count < 0) { count = -count; glBegin(GL_TRIANGLE_FAN); } else { glBegin(GL_TRIANGLE_STRIP); } if (currententity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE)) { do { index_xyz = order[2]; order += 3; glColor4f(shadelight[0], shadelight[1], shadelight[2], alpha); glVertex3fv(s_lerped[index_xyz]); } while (--count); } else { do { /* texture coordinates come from the draw list */ glTexCoord2f(((float *)order)[0], ((float *)order)[1]); index_xyz = order[2]; order += 3; /* normals and vertexes come from the frame list */ l = shadedots[verts[index_xyz].lightnormalindex]; glColor4f(l * shadelight[0], l * shadelight[1], l * shadelight[2], alpha); glVertex3fv(s_lerped[index_xyz]); } while (--count); } glEnd(); } } if (currententity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) { glEnable(GL_TEXTURE_2D); } } void R_DrawAliasShadow(dmdl_t *paliashdr, int posenum) { int *order; vec3_t point; float height = 0, lheight; int count; lheight = currententity->origin[2] - lightspot[2]; order = (int *)((byte *)paliashdr + paliashdr->ofs_glcmds); height = -lheight + 0.1f; /* stencilbuffer shadows */ if (have_stencil && gl_stencilshadow->value) { glEnable(GL_STENCIL_TEST); glStencilFunc(GL_EQUAL, 1, 2); glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); } while (1) { /* get the vertex count and primitive type */ count = *order++; if (!count) { break; /* done */ } if (count < 0) { count = -count; glBegin(GL_TRIANGLE_FAN); } else { glBegin(GL_TRIANGLE_STRIP); } do { /* normals and vertexes come from the frame list */ memcpy(point, s_lerped[order[2]], sizeof(point)); point[0] -= shadevector[0] * (point[2] + lheight); point[1] -= shadevector[1] * (point[2] + lheight); point[2] = height; glVertex3fv(point); order += 3; } while (--count); glEnd(); } /* stencilbuffer shadows */ if (have_stencil && gl_stencilshadow->value) { glDisable(GL_STENCIL_TEST); } } static qboolean R_CullAliasModel(vec3_t bbox[8], entity_t *e) { int i; vec3_t mins, maxs; dmdl_t *paliashdr; vec3_t vectors[3]; vec3_t thismins, oldmins, thismaxs, oldmaxs; daliasframe_t *pframe, *poldframe; vec3_t angles; paliashdr = (dmdl_t *)currentmodel->extradata; if ((e->frame >= paliashdr->num_frames) || (e->frame < 0)) { VID_Printf(PRINT_ALL, "R_CullAliasModel %s: no such frame %d\n", currentmodel->name, e->frame); e->frame = 0; } if ((e->oldframe >= paliashdr->num_frames) || (e->oldframe < 0)) { VID_Printf(PRINT_ALL, "R_CullAliasModel %s: no such oldframe %d\n", currentmodel->name, e->oldframe); e->oldframe = 0; } pframe = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames + e->frame * paliashdr->framesize); poldframe = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames + e->oldframe * paliashdr->framesize); /* compute axially aligned mins and maxs */ if (pframe == poldframe) { for (i = 0; i < 3; i++) { mins[i] = pframe->translate[i]; maxs[i] = mins[i] + pframe->scale[i] * 255; } } else { for (i = 0; i < 3; i++) { thismins[i] = pframe->translate[i]; thismaxs[i] = thismins[i] + pframe->scale[i] * 255; oldmins[i] = poldframe->translate[i]; oldmaxs[i] = oldmins[i] + poldframe->scale[i] * 255; if (thismins[i] < oldmins[i]) { mins[i] = thismins[i]; } else { mins[i] = oldmins[i]; } if (thismaxs[i] > oldmaxs[i]) { maxs[i] = thismaxs[i]; } else { maxs[i] = oldmaxs[i]; } } } /* compute a full bounding box */ for (i = 0; i < 8; i++) { vec3_t tmp; if (i & 1) { tmp[0] = mins[0]; } else { tmp[0] = maxs[0]; } if (i & 2) { tmp[1] = mins[1]; } else { tmp[1] = maxs[1]; } if (i & 4) { tmp[2] = mins[2]; } else { tmp[2] = maxs[2]; } VectorCopy(tmp, bbox[i]); } /* rotate the bounding box */ VectorCopy(e->angles, angles); angles[YAW] = -angles[YAW]; AngleVectors(angles, vectors[0], vectors[1], vectors[2]); for (i = 0; i < 8; i++) { vec3_t tmp; VectorCopy(bbox[i], tmp); bbox[i][0] = DotProduct(vectors[0], tmp); bbox[i][1] = -DotProduct(vectors[1], tmp); bbox[i][2] = DotProduct(vectors[2], tmp); VectorAdd(e->origin, bbox[i], bbox[i]); } int p, f, aggregatemask = ~0; for (p = 0; p < 8; p++) { int mask = 0; for (f = 0; f < 4; f++) { float dp = DotProduct(frustum[f].normal, bbox[p]); if ((dp - frustum[f].dist) < 0) { mask |= (1 << f); } } aggregatemask &= mask; } if (aggregatemask) { return true; } return false; } void R_DrawAliasModel(entity_t *e) { int i; dmdl_t *paliashdr; float an; vec3_t bbox[8]; image_t *skin; if (!(e->flags & RF_WEAPONMODEL)) { if (R_CullAliasModel(bbox, e)) { return; } } if (e->flags & RF_WEAPONMODEL) { if (gl_lefthand->value == 2) { return; } } paliashdr = (dmdl_t *)currentmodel->extradata; /* get lighting information */ if (currententity->flags & (RF_SHELL_HALF_DAM | RF_SHELL_GREEN | RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_DOUBLE)) { VectorClear(shadelight); if (currententity->flags & RF_SHELL_HALF_DAM) { shadelight[0] = 0.56; shadelight[1] = 0.59; shadelight[2] = 0.45; } if (currententity->flags & RF_SHELL_DOUBLE) { shadelight[0] = 0.9; shadelight[1] = 0.7; } if (currententity->flags & RF_SHELL_RED) { shadelight[0] = 1.0; } if (currententity->flags & RF_SHELL_GREEN) { shadelight[1] = 1.0; } if (currententity->flags & RF_SHELL_BLUE) { shadelight[2] = 1.0; } } else if (currententity->flags & RF_FULLBRIGHT) { for (i = 0; i < 3; i++) { shadelight[i] = 1.0; } } else { R_LightPoint(currententity->origin, shadelight); /* player lighting hack for communication back to server */ if (currententity->flags & RF_WEAPONMODEL) { /* pick the greatest component, which should be the same as the mono value returned by software */ if (shadelight[0] > shadelight[1]) { if (shadelight[0] > shadelight[2]) { gl_lightlevel->value = 150 * shadelight[0]; } else { gl_lightlevel->value = 150 * shadelight[2]; } } else { if (shadelight[1] > shadelight[2]) { gl_lightlevel->value = 150 * shadelight[1]; } else { gl_lightlevel->value = 150 * shadelight[2]; } } } } if (currententity->flags & RF_MINLIGHT) { for (i = 0; i < 3; i++) { if (shadelight[i] > 0.1) { break; } } if (i == 3) { shadelight[0] = 0.1; shadelight[1] = 0.1; shadelight[2] = 0.1; } } if (currententity->flags & RF_GLOW) { /* bonus items will pulse with time */ float scale; float min; scale = 0.1 * sin(r_newrefdef.time * 7); for (i = 0; i < 3; i++) { min = shadelight[i] * 0.8; shadelight[i] += scale; if (shadelight[i] < min) { shadelight[i] = min; } } } /* Apply gl_overbrightbits to the mesh. If we don't do this they will appear slightly dimmer relative to walls. Also note that gl_overbrightbits is only applied to walls when gl_ext_mtexcombine is set to 1, so we'll also want to check that; otherwise we'll end up in the reverse situation and the meshes will appear too bright. */ if (gl_config.mtexcombine && gl_overbrightbits->value) { for (i = 0; i < 3; ++i) { shadelight[i] *= gl_overbrightbits->value; } } /* ir goggles color override */ if (r_newrefdef.rdflags & RDF_IRGOGGLES && currententity->flags & RF_IR_VISIBLE) { shadelight[0] = 1.0; shadelight[1] = 0.0; shadelight[2] = 0.0; } shadedots = r_avertexnormal_dots[((int)(currententity->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; an = currententity->angles[1] / 180 * M_PI; shadevector[0] = cos(-an); shadevector[1] = sin(-an); shadevector[2] = 1; VectorNormalize(shadevector); /* locate the proper data */ c_alias_polys += paliashdr->num_tris; /* draw all the triangles */ if (currententity->flags & RF_DEPTHHACK) { /* hack the depth range to prevent view model from poking into walls */ glDepthRange(gldepthmin, gldepthmin + 0.3 * (gldepthmax - gldepthmin)); } if ((currententity->flags & RF_WEAPONMODEL) && (gl_lefthand->value == 1.0F)) { extern void R_MYgluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glScalef(-1, 1, 1); R_MYgluPerspective(r_newrefdef.fov_y, (float)r_newrefdef.width / r_newrefdef.height, 4, 4096); glMatrixMode(GL_MODELVIEW); glCullFace(GL_BACK); } glPushMatrix(); e->angles[PITCH] = -e->angles[PITCH]; R_RotateForEntity(e); e->angles[PITCH] = -e->angles[PITCH]; /* select skin */ if (currententity->skin) { skin = currententity->skin; /* custom player skin */ } else { if (currententity->skinnum >= MAX_MD2SKINS) { skin = currentmodel->skins[0]; } else { skin = currentmodel->skins[currententity->skinnum]; if (!skin) { skin = currentmodel->skins[0]; } } } if (!skin) { skin = r_notexture; /* fallback... */ } R_Bind(skin->texnum); /* draw it */ glShadeModel(GL_SMOOTH); R_TexEnv(GL_MODULATE); if (currententity->flags & RF_TRANSLUCENT) { glEnable(GL_BLEND); } if ((currententity->frame >= paliashdr->num_frames) || (currententity->frame < 0)) { VID_Printf(PRINT_ALL, "R_DrawAliasModel %s: no such frame %d\n", currentmodel->name, currententity->frame); currententity->frame = 0; currententity->oldframe = 0; } if ((currententity->oldframe >= paliashdr->num_frames) || (currententity->oldframe < 0)) { VID_Printf(PRINT_ALL, "R_DrawAliasModel %s: no such oldframe %d\n", currentmodel->name, currententity->oldframe); currententity->frame = 0; currententity->oldframe = 0; } if (!gl_lerpmodels->value) { currententity->backlerp = 0; } R_DrawAliasFrameLerp(paliashdr, currententity->backlerp); R_TexEnv(GL_REPLACE); glShadeModel(GL_FLAT); glPopMatrix(); if ((currententity->flags & RF_WEAPONMODEL) && (gl_lefthand->value == 1.0F)) { glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glCullFace(GL_FRONT); } if (currententity->flags & RF_TRANSLUCENT) { glDisable(GL_BLEND); } if (currententity->flags & RF_DEPTHHACK) { glDepthRange(gldepthmin, gldepthmax); } if (gl_shadows->value && !(currententity->flags & (RF_TRANSLUCENT | RF_WEAPONMODEL | RF_NOSHADOW))) { glPushMatrix(); /* don't rotate shadows on ungodly axes */ glTranslatef(e->origin[0], e->origin[1], e->origin[2]); glRotatef(e->angles[1], 0, 0, 1); glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glColor4f(0, 0, 0, 0.5); R_DrawAliasShadow(paliashdr, currententity->frame); glEnable(GL_TEXTURE_2D); glDisable(GL_BLEND); glPopMatrix(); } glColor4f(1, 1, 1, 1); } yquake2-QUAKE2_5_32/src/client/cl_inventory.c0000644000175000017500000000616612615162034021554 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements the inventory screen * * ======================================================================= */ #include "header/client.h" void CL_ParseInventory(void) { int i; for (i = 0; i < MAX_ITEMS; i++) { cl.inventory[i] = MSG_ReadShort(&net_message); } } static void Inv_DrawStringScaled(int x, int y, char *string, float factor) { while (*string) { Draw_CharScaled(x, y, *string, factor); x += factor*8; string++; } } static void SetStringHighBit(char *s) { while (*s) { *s++ |= 128; } } #define DISPLAY_ITEMS 17 void CL_DrawInventory(void) { int i, j; int num, selected_num, item; int index[MAX_ITEMS]; char string[1024]; int x, y; char binding[1024]; const char *bind; int selected; int top; selected = cl.frame.playerstate.stats[STAT_SELECTED_ITEM]; num = 0; selected_num = 0; float scale = SCR_GetHUDScale(); for (i = 0; i < MAX_ITEMS; i++) { if (i == selected) { selected_num = num; } if (cl.inventory[i]) { index[num] = i; num++; } } /* determine scroll point */ top = selected_num - DISPLAY_ITEMS / 2; if (num - top < DISPLAY_ITEMS) { top = num - DISPLAY_ITEMS; } if (top < 0) { top = 0; } x = (viddef.width - scale*256) / 2; y = (viddef.height - scale*240) / 2; /* repaint everything next frame */ SCR_DirtyScreen(); Draw_PicScaled(x, y + scale*8, "inventory", scale); y += scale*24; x += scale*24; Inv_DrawStringScaled(x, y, "hotkey ### item", scale); Inv_DrawStringScaled(x, y + scale*8, "------ --- ----", scale); y += scale*16; for (i = top; i < num && i < top + DISPLAY_ITEMS; i++) { item = index[i]; /* search for a binding */ Com_sprintf(binding, sizeof(binding), "use %s", cl.configstrings[CS_ITEMS + item]); bind = ""; for (j = 0; j < 256; j++) { if (keybindings[j] && !Q_stricmp(keybindings[j], binding)) { bind = Key_KeynumToString(j); break; } } Com_sprintf(string, sizeof(string), "%6s %3i %s", bind, cl.inventory[item], cl.configstrings[CS_ITEMS + item]); if (item != selected) { SetStringHighBit(string); } else { /* draw a blinky cursor by the selected item */ if ((int)(cls.realtime * 10) & 1) { Draw_CharScaled(x - scale*8, y, 15, scale); } } Inv_DrawStringScaled(x, y, string, scale); y += scale*8; } } yquake2-QUAKE2_5_32/src/client/cl_tempentities.c0000644000175000017500000011176412615162034022232 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements all temporary (dynamic created) entities * * ======================================================================= */ #include "header/client.h" typedef enum { ex_free, ex_explosion, ex_misc, ex_flash, ex_mflash, ex_poly, ex_poly2 } exptype_t; typedef struct { exptype_t type; entity_t ent; int frames; float light; vec3_t lightcolor; float start; int baseframe; } explosion_t; #define MAX_EXPLOSIONS 64 #define MAX_BEAMS 64 #define MAX_LASERS 64 explosion_t cl_explosions[MAX_EXPLOSIONS]; typedef struct { int entity; int dest_entity; struct model_s *model; int endtime; vec3_t offset; vec3_t start, end; } beam_t; beam_t cl_beams[MAX_BEAMS]; beam_t cl_playerbeams[MAX_BEAMS]; typedef struct { entity_t ent; int endtime; } laser_t; laser_t cl_lasers[MAX_LASERS]; cl_sustain_t cl_sustains[MAX_SUSTAINS]; extern void CL_TeleportParticles(vec3_t org); void CL_BlasterParticles(vec3_t org, vec3_t dir); void CL_BFGExplosionParticles(vec3_t org); void CL_BlueBlasterParticles(vec3_t org, vec3_t dir); void CL_ExplosionParticles(vec3_t org); void CL_Explosion_Particle(vec3_t org, float size, qboolean large, qboolean rocket); #define EXPLOSION_PARTICLES(x) CL_ExplosionParticles((x)); struct sfx_s *cl_sfx_ric1; struct sfx_s *cl_sfx_ric2; struct sfx_s *cl_sfx_ric3; struct sfx_s *cl_sfx_lashit; struct sfx_s *cl_sfx_spark5; struct sfx_s *cl_sfx_spark6; struct sfx_s *cl_sfx_spark7; struct sfx_s *cl_sfx_railg; struct sfx_s *cl_sfx_rockexp; struct sfx_s *cl_sfx_grenexp; struct sfx_s *cl_sfx_watrexp; struct sfx_s *cl_sfx_plasexp; struct sfx_s *cl_sfx_footsteps[4]; struct model_s *cl_mod_explode; struct model_s *cl_mod_smoke; struct model_s *cl_mod_flash; struct model_s *cl_mod_parasite_segment; struct model_s *cl_mod_grapple_cable; struct model_s *cl_mod_parasite_tip; struct model_s *cl_mod_explo4; struct model_s *cl_mod_bfg_explo; struct model_s *cl_mod_powerscreen; struct model_s *cl_mod_plasmaexplo; struct sfx_s *cl_sfx_lightning; struct sfx_s *cl_sfx_disrexp; struct model_s *cl_mod_lightning; struct model_s *cl_mod_heatbeam; struct model_s *cl_mod_monster_heatbeam; struct model_s *cl_mod_explo4_big; void CL_RegisterTEntSounds(void) { int i; char name[MAX_QPATH]; cl_sfx_ric1 = S_RegisterSound("world/ric1.wav"); cl_sfx_ric2 = S_RegisterSound("world/ric2.wav"); cl_sfx_ric3 = S_RegisterSound("world/ric3.wav"); cl_sfx_lashit = S_RegisterSound("weapons/lashit.wav"); cl_sfx_spark5 = S_RegisterSound("world/spark5.wav"); cl_sfx_spark6 = S_RegisterSound("world/spark6.wav"); cl_sfx_spark7 = S_RegisterSound("world/spark7.wav"); cl_sfx_railg = S_RegisterSound("weapons/railgf1a.wav"); cl_sfx_rockexp = S_RegisterSound("weapons/rocklx1a.wav"); cl_sfx_grenexp = S_RegisterSound("weapons/grenlx1a.wav"); cl_sfx_watrexp = S_RegisterSound("weapons/xpld_wat.wav"); S_RegisterSound("player/land1.wav"); S_RegisterSound("player/fall2.wav"); S_RegisterSound("player/fall1.wav"); for (i = 0; i < 4; i++) { Com_sprintf(name, sizeof(name), "player/step%i.wav", i + 1); cl_sfx_footsteps[i] = S_RegisterSound(name); } cl_sfx_lightning = S_RegisterSound("weapons/tesla.wav"); cl_sfx_disrexp = S_RegisterSound("weapons/disrupthit.wav"); } void CL_RegisterTEntModels(void) { cl_mod_explode = R_RegisterModel("models/objects/explode/tris.md2"); cl_mod_smoke = R_RegisterModel("models/objects/smoke/tris.md2"); cl_mod_flash = R_RegisterModel("models/objects/flash/tris.md2"); cl_mod_parasite_segment = R_RegisterModel("models/monsters/parasite/segment/tris.md2"); cl_mod_grapple_cable = R_RegisterModel("models/ctf/segment/tris.md2"); cl_mod_parasite_tip = R_RegisterModel("models/monsters/parasite/tip/tris.md2"); cl_mod_explo4 = R_RegisterModel("models/objects/r_explode/tris.md2"); cl_mod_bfg_explo = R_RegisterModel("sprites/s_bfg2.sp2"); cl_mod_powerscreen = R_RegisterModel("models/items/armor/effect/tris.md2"); R_RegisterModel("models/objects/laser/tris.md2"); R_RegisterModel("models/objects/grenade2/tris.md2"); R_RegisterModel("models/weapons/v_machn/tris.md2"); R_RegisterModel("models/weapons/v_handgr/tris.md2"); R_RegisterModel("models/weapons/v_shotg2/tris.md2"); R_RegisterModel("models/objects/gibs/bone/tris.md2"); R_RegisterModel("models/objects/gibs/sm_meat/tris.md2"); R_RegisterModel("models/objects/gibs/bone2/tris.md2"); Draw_FindPic("w_machinegun"); Draw_FindPic("a_bullets"); Draw_FindPic("i_health"); Draw_FindPic("a_grenades"); cl_mod_explo4_big = R_RegisterModel("models/objects/r_explode2/tris.md2"); cl_mod_lightning = R_RegisterModel("models/proj/lightning/tris.md2"); cl_mod_heatbeam = R_RegisterModel("models/proj/beam/tris.md2"); cl_mod_monster_heatbeam = R_RegisterModel("models/proj/widowbeam/tris.md2"); } void CL_ClearTEnts(void) { memset(cl_beams, 0, sizeof(cl_beams)); memset(cl_explosions, 0, sizeof(cl_explosions)); memset(cl_lasers, 0, sizeof(cl_lasers)); memset(cl_playerbeams, 0, sizeof(cl_playerbeams)); memset(cl_sustains, 0, sizeof(cl_sustains)); } explosion_t * CL_AllocExplosion(void) { int i; float time; int index; for (i = 0; i < MAX_EXPLOSIONS; i++) { if (cl_explosions[i].type == ex_free) { memset(&cl_explosions[i], 0, sizeof(cl_explosions[i])); return &cl_explosions[i]; } } /* find the oldest explosion */ time = (float)cl.time; index = 0; for (i = 0; i < MAX_EXPLOSIONS; i++) { if (cl_explosions[i].start < time) { time = cl_explosions[i].start; index = i; } } memset(&cl_explosions[index], 0, sizeof(cl_explosions[index])); return &cl_explosions[index]; } void CL_SmokeAndFlash(vec3_t origin) { explosion_t *ex; ex = CL_AllocExplosion(); VectorCopy(origin, ex->ent.origin); ex->type = ex_misc; ex->frames = 4; ex->ent.flags = RF_TRANSLUCENT; ex->start = cl.frame.servertime - 100.0f; ex->ent.model = cl_mod_smoke; ex = CL_AllocExplosion(); VectorCopy(origin, ex->ent.origin); ex->type = ex_flash; ex->ent.flags = RF_FULLBRIGHT; ex->frames = 2; ex->start = cl.frame.servertime - 100.0f; ex->ent.model = cl_mod_flash; } void CL_ParseParticles(void) { int color, count; vec3_t pos, dir; MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); color = MSG_ReadByte(&net_message); count = MSG_ReadByte(&net_message); CL_ParticleEffect(pos, dir, color, count); } void CL_ParseBeam(struct model_s *model) { int ent; vec3_t start, end; beam_t *b; int i; ent = MSG_ReadShort(&net_message); MSG_ReadPos(&net_message, start); MSG_ReadPos(&net_message, end); /* override any beam with the same entity */ for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++) { if (b->entity == ent) { b->entity = ent; b->model = model; b->endtime = cl.time + 200; VectorCopy(start, b->start); VectorCopy(end, b->end); VectorClear(b->offset); 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 = model; b->endtime = cl.time + 200; VectorCopy(start, b->start); VectorCopy(end, b->end); VectorClear(b->offset); return; } } Com_Printf("beam list overflow!\n"); return; } void CL_ParseBeam2(struct model_s *model) { int ent; vec3_t start, end, offset; beam_t *b; int i; ent = MSG_ReadShort(&net_message); MSG_ReadPos(&net_message, start); MSG_ReadPos(&net_message, end); MSG_ReadPos(&net_message, offset); /* override any beam with the same entity */ for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++) { if (b->entity == ent) { b->entity = ent; b->model = model; b->endtime = cl.time + 200; VectorCopy(start, b->start); VectorCopy(end, b->end); VectorCopy(offset, b->offset); 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 = model; b->endtime = cl.time + 200; VectorCopy(start, b->start); VectorCopy(end, b->end); VectorCopy(offset, b->offset); return; } } Com_Printf("beam list overflow!\n"); return; } /* * adds to the cl_playerbeam array instead of the cl_beams array */ void CL_ParsePlayerBeam(struct model_s *model) { int ent; vec3_t start, end, offset; beam_t *b; int i; ent = MSG_ReadShort(&net_message); MSG_ReadPos(&net_message, start); MSG_ReadPos(&net_message, end); /* network optimization */ if (model == cl_mod_heatbeam) { VectorSet(offset, 2, 7, -3); } else if (model == cl_mod_monster_heatbeam) { model = cl_mod_heatbeam; VectorSet(offset, 0, 0, 0); } else { MSG_ReadPos(&net_message, offset); } /* Override any beam with the same entity For player beams, we only want one per player (entity) so... */ for (i = 0, b = cl_playerbeams; i < MAX_BEAMS; i++, b++) { if (b->entity == ent) { b->entity = ent; b->model = model; b->endtime = cl.time + 200; VectorCopy(start, b->start); VectorCopy(end, b->end); VectorCopy(offset, b->offset); return; } } /* find a free beam */ for (i = 0, b = cl_playerbeams; i < MAX_BEAMS; i++, b++) { if (!b->model || (b->endtime < cl.time)) { b->entity = ent; b->model = model; b->endtime = cl.time + 100; /* this needs to be 100 to prevent multiple heatbeams */ VectorCopy(start, b->start); VectorCopy(end, b->end); VectorCopy(offset, b->offset); return; } } Com_Printf("beam list overflow!\n"); return; } int CL_ParseLightning(struct model_s *model) { int srcEnt, destEnt; vec3_t start, end; beam_t *b; int i; srcEnt = MSG_ReadShort(&net_message); destEnt = MSG_ReadShort(&net_message); MSG_ReadPos(&net_message, start); MSG_ReadPos(&net_message, end); /* override any beam with the same source AND destination entities */ for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++) { if ((b->entity == srcEnt) && (b->dest_entity == destEnt)) { b->entity = srcEnt; b->dest_entity = destEnt; b->model = model; b->endtime = cl.time + 200; VectorCopy(start, b->start); VectorCopy(end, b->end); VectorClear(b->offset); return srcEnt; } } /* find a free beam */ for (i = 0, b = cl_beams; i < MAX_BEAMS; i++, b++) { if (!b->model || (b->endtime < cl.time)) { b->entity = srcEnt; b->dest_entity = destEnt; b->model = model; b->endtime = cl.time + 200; VectorCopy(start, b->start); VectorCopy(end, b->end); VectorClear(b->offset); return srcEnt; } } Com_Printf("beam list overflow!\n"); return srcEnt; } void CL_ParseLaser(int colors) { vec3_t start; vec3_t end; laser_t *l; int i; MSG_ReadPos(&net_message, start); MSG_ReadPos(&net_message, end); for (i = 0, l = cl_lasers; i < MAX_LASERS; i++, l++) { if (l->endtime < cl.time) { l->ent.flags = RF_TRANSLUCENT | RF_BEAM; VectorCopy(start, l->ent.origin); VectorCopy(end, l->ent.oldorigin); l->ent.alpha = 0.30f; l->ent.skinnum = (colors >> ((randk() % 4) * 8)) & 0xff; l->ent.model = NULL; l->ent.frame = 4; l->endtime = cl.time + 100; return; } } } void CL_ParseSteam(void) { vec3_t pos, dir; int id, i; int r; int cnt; int color; int magnitude; cl_sustain_t *s, *free_sustain; id = MSG_ReadShort(&net_message); /* an id of -1 is an instant effect */ if (id != -1) /* sustains */ { free_sustain = NULL; for (i = 0, s = cl_sustains; i < MAX_SUSTAINS; i++, s++) { if (s->id == 0) { free_sustain = s; break; } } if (free_sustain) { s->id = id; s->count = MSG_ReadByte(&net_message); MSG_ReadPos(&net_message, s->org); MSG_ReadDir(&net_message, s->dir); r = MSG_ReadByte(&net_message); s->color = r & 0xff; s->magnitude = MSG_ReadShort(&net_message); s->endtime = cl.time + MSG_ReadLong(&net_message); s->think = CL_ParticleSteamEffect2; s->thinkinterval = 100; s->nextthink = cl.time; } else { MSG_ReadByte(&net_message); MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); MSG_ReadByte(&net_message); MSG_ReadShort(&net_message); MSG_ReadLong(&net_message); /* really interval */ } } else { /* instant */ cnt = MSG_ReadByte(&net_message); MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); r = MSG_ReadByte(&net_message); magnitude = MSG_ReadShort(&net_message); color = r & 0xff; CL_ParticleSteamEffect(pos, dir, color, cnt, magnitude); } } void CL_ParseWidow(void) { vec3_t pos; int id, i; cl_sustain_t *s, *free_sustain; id = MSG_ReadShort(&net_message); free_sustain = NULL; for (i = 0, s = cl_sustains; i < MAX_SUSTAINS; i++, s++) { if (s->id == 0) { free_sustain = s; break; } } if (free_sustain) { s->id = id; MSG_ReadPos(&net_message, s->org); s->endtime = cl.time + 2100; s->think = CL_Widowbeamout; s->thinkinterval = 1; s->nextthink = cl.time; } else { /* no free sustains */ MSG_ReadPos(&net_message, pos); } } void CL_ParseNuke(void) { vec3_t pos; int i; cl_sustain_t *s, *free_sustain; free_sustain = NULL; for (i = 0, s = cl_sustains; i < MAX_SUSTAINS; i++, s++) { if (s->id == 0) { free_sustain = s; break; } } if (free_sustain) { s->id = 21000; MSG_ReadPos(&net_message, s->org); s->endtime = cl.time + 1000; s->think = CL_Nukeblast; s->thinkinterval = 1; s->nextthink = cl.time; } else { /* no free sustains */ MSG_ReadPos(&net_message, pos); } } static byte splash_color[] = {0x00, 0xe0, 0xb0, 0x50, 0xd0, 0xe0, 0xe8}; void CL_ParseTEnt(void) { int type; vec3_t pos, pos2, dir; explosion_t *ex; int cnt; int color; int r; int ent; int magnitude; type = MSG_ReadByte(&net_message); switch (type) { case TE_BLOOD: /* bullet hitting flesh */ MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); CL_ParticleEffect(pos, dir, 0xe8, 60); break; case TE_GUNSHOT: /* bullet hitting wall */ case TE_SPARKS: case TE_BULLET_SPARKS: MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); if (type == TE_GUNSHOT) { CL_ParticleEffect(pos, dir, 0, 40); } else { CL_ParticleEffect(pos, dir, 0xe0, 6); } if (type != TE_SPARKS) { CL_SmokeAndFlash(pos); /* impact sound */ cnt = randk() & 15; if (cnt == 1) { S_StartSound(pos, 0, 0, cl_sfx_ric1, 1, ATTN_NORM, 0); } else if (cnt == 2) { S_StartSound(pos, 0, 0, cl_sfx_ric2, 1, ATTN_NORM, 0); } else if (cnt == 3) { S_StartSound(pos, 0, 0, cl_sfx_ric3, 1, ATTN_NORM, 0); } } break; case TE_SCREEN_SPARKS: case TE_SHIELD_SPARKS: MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); if (type == TE_SCREEN_SPARKS) { CL_ParticleEffect(pos, dir, 0xd0, 40); } else { CL_ParticleEffect(pos, dir, 0xb0, 40); } S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); break; case TE_SHOTGUN: /* bullet hitting wall */ MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); CL_ParticleEffect(pos, dir, 0, 20); CL_SmokeAndFlash(pos); break; case TE_SPLASH: /* bullet hitting water */ cnt = MSG_ReadByte(&net_message); MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); r = MSG_ReadByte(&net_message); if (r > 6) { color = 0x00; } else { color = splash_color[r]; } CL_ParticleEffect(pos, dir, color, cnt); if (r == SPLASH_SPARKS) { r = randk() & 3; if (r == 0) { S_StartSound(pos, 0, 0, cl_sfx_spark5, 1, ATTN_STATIC, 0); } else if (r == 1) { S_StartSound(pos, 0, 0, cl_sfx_spark6, 1, ATTN_STATIC, 0); } else { S_StartSound(pos, 0, 0, cl_sfx_spark7, 1, ATTN_STATIC, 0); } } break; case TE_LASER_SPARKS: cnt = MSG_ReadByte(&net_message); MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); color = MSG_ReadByte(&net_message); CL_ParticleEffect2(pos, dir, color, cnt); break; case TE_BLUEHYPERBLASTER: MSG_ReadPos(&net_message, pos); MSG_ReadPos(&net_message, dir); CL_BlasterParticles(pos, dir); break; case TE_BLASTER: /* blaster hitting wall */ MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); CL_BlasterParticles(pos, dir); ex = CL_AllocExplosion(); VectorCopy(pos, ex->ent.origin); ex->ent.angles[0] = (float)acos(dir[2]) / M_PI * 180; if (dir[0]) { ex->ent.angles[1] = (float)atan2(dir[1], dir[0]) / M_PI * 180; } else if (dir[1] > 0) { ex->ent.angles[1] = 90; } else if (dir[1] < 0) { ex->ent.angles[1] = 270; } else { ex->ent.angles[1] = 0; } ex->type = ex_misc; ex->ent.flags = 0; ex->start = cl.frame.servertime - 100.0f; ex->light = 150; ex->lightcolor[0] = 1; ex->lightcolor[1] = 1; ex->ent.model = cl_mod_explode; ex->frames = 4; S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); break; case TE_RAILTRAIL: /* railgun effect */ MSG_ReadPos(&net_message, pos); MSG_ReadPos(&net_message, pos2); CL_RailTrail(pos, pos2); S_StartSound(pos2, 0, 0, cl_sfx_railg, 1, ATTN_NORM, 0); break; case TE_EXPLOSION2: case TE_GRENADE_EXPLOSION: case TE_GRENADE_EXPLOSION_WATER: MSG_ReadPos(&net_message, pos); ex = CL_AllocExplosion(); VectorCopy(pos, ex->ent.origin); ex->type = ex_poly; ex->ent.flags = RF_FULLBRIGHT | RF_NOSHADOW; ex->start = cl.frame.servertime - 100.0f; ex->light = 350; ex->lightcolor[0] = 1.0; ex->lightcolor[1] = 0.5; ex->lightcolor[2] = 0.5; ex->ent.model = cl_mod_explo4; ex->frames = 19; ex->baseframe = 30; ex->ent.angles[1] = (float)(randk() % 360); EXPLOSION_PARTICLES(pos); if (type == TE_GRENADE_EXPLOSION_WATER) { S_StartSound(pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0); } else { S_StartSound(pos, 0, 0, cl_sfx_grenexp, 1, ATTN_NORM, 0); } break; case TE_PLASMA_EXPLOSION: MSG_ReadPos(&net_message, pos); ex = CL_AllocExplosion(); VectorCopy(pos, ex->ent.origin); ex->type = ex_poly; ex->ent.flags = RF_FULLBRIGHT | RF_NOSHADOW; ex->start = cl.frame.servertime - 100.0f; ex->light = 350; ex->lightcolor[0] = 1.0; ex->lightcolor[1] = 0.5; ex->lightcolor[2] = 0.5; ex->ent.angles[1] = (float)(randk() % 360); ex->ent.model = cl_mod_explo4; if (frandk() < 0.5) { ex->baseframe = 15; } ex->frames = 15; EXPLOSION_PARTICLES(pos); S_StartSound(pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0); break; case TE_EXPLOSION1_BIG: case TE_EXPLOSION1_NP: case TE_EXPLOSION1: case TE_ROCKET_EXPLOSION: case TE_ROCKET_EXPLOSION_WATER: MSG_ReadPos(&net_message, pos); ex = CL_AllocExplosion(); VectorCopy(pos, ex->ent.origin); ex->type = ex_poly; ex->ent.flags = RF_FULLBRIGHT | RF_NOSHADOW; ex->start = cl.frame.servertime - 100.0f; ex->light = 350; ex->lightcolor[0] = 1.0; ex->lightcolor[1] = 0.5; ex->lightcolor[2] = 0.5; ex->ent.angles[1] = (float)(randk() % 360); if (type != TE_EXPLOSION1_BIG) { ex->ent.model = cl_mod_explo4; } else { ex->ent.model = cl_mod_explo4_big; } if (frandk() < 0.5) { ex->baseframe = 15; } ex->frames = 15; if ((type != TE_EXPLOSION1_BIG) && (type != TE_EXPLOSION1_NP)) { EXPLOSION_PARTICLES(pos); } if (type == TE_ROCKET_EXPLOSION_WATER) { S_StartSound(pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0); } else { S_StartSound(pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0); } break; case TE_BFG_EXPLOSION: MSG_ReadPos(&net_message, pos); ex = CL_AllocExplosion(); VectorCopy(pos, ex->ent.origin); ex->type = ex_poly; ex->ent.flags = RF_FULLBRIGHT | RF_NOSHADOW; ex->start = cl.frame.servertime - 100.0f; ex->light = 350; ex->lightcolor[0] = 0.0; ex->lightcolor[1] = 1.0; ex->lightcolor[2] = 0.0; ex->ent.model = cl_mod_bfg_explo; ex->ent.flags |= RF_TRANSLUCENT; ex->ent.alpha = 0.30f; ex->frames = 4; break; case TE_BFG_BIGEXPLOSION: MSG_ReadPos(&net_message, pos); CL_BFGExplosionParticles(pos); break; case TE_BFG_LASER: CL_ParseLaser(0xd0d1d2d3); break; case TE_BUBBLETRAIL: MSG_ReadPos(&net_message, pos); MSG_ReadPos(&net_message, pos2); CL_BubbleTrail(pos, pos2); break; case TE_PARASITE_ATTACK: case TE_MEDIC_CABLE_ATTACK: CL_ParseBeam(cl_mod_parasite_segment); break; case TE_BOSSTPORT: /* boss teleporting to station */ MSG_ReadPos(&net_message, pos); CL_BigTeleportParticles(pos); S_StartSound(pos, 0, 0, S_RegisterSound( "misc/bigtele.wav"), 1, ATTN_NONE, 0); break; case TE_GRAPPLE_CABLE: CL_ParseBeam2(cl_mod_grapple_cable); break; case TE_WELDING_SPARKS: cnt = MSG_ReadByte(&net_message); MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); color = MSG_ReadByte(&net_message); CL_ParticleEffect2(pos, dir, color, cnt); ex = CL_AllocExplosion(); VectorCopy(pos, ex->ent.origin); ex->type = ex_flash; ex->ent.flags = RF_BEAM; ex->start = cl.frame.servertime - 0.1f; ex->light = 100 + (float)(randk() % 75); ex->lightcolor[0] = 1.0f; ex->lightcolor[1] = 1.0f; ex->lightcolor[2] = 0.3f; ex->ent.model = cl_mod_flash; ex->frames = 2; break; case TE_GREENBLOOD: MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); CL_ParticleEffect2(pos, dir, 0xdf, 30); break; case TE_TUNNEL_SPARKS: cnt = MSG_ReadByte(&net_message); MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); color = MSG_ReadByte(&net_message); CL_ParticleEffect3(pos, dir, color, cnt); break; case TE_BLASTER2: case TE_FLECHETTE: MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); if (type == TE_BLASTER2) { CL_BlasterParticles2(pos, dir, 0xd0); } else { CL_BlasterParticles2(pos, dir, 0x6f); } ex = CL_AllocExplosion(); VectorCopy(pos, ex->ent.origin); ex->ent.angles[0] = (float)acos(dir[2]) / M_PI * 180; if (dir[0]) { ex->ent.angles[1] = (float)atan2(dir[1], dir[0]) / M_PI * 180; } else if (dir[1] > 0) { ex->ent.angles[1] = 90; } else if (dir[1] < 0) { ex->ent.angles[1] = 270; } else { ex->ent.angles[1] = 0; } ex->type = ex_misc; ex->ent.flags = RF_FULLBRIGHT | RF_TRANSLUCENT; if (type == TE_BLASTER2) { ex->ent.skinnum = 1; } else /* flechette */ { ex->ent.skinnum = 2; } ex->start = cl.frame.servertime - 100.0f; ex->light = 150; if (type == TE_BLASTER2) { ex->lightcolor[1] = 1; } else { /* flechette */ ex->lightcolor[0] = 0.19f; ex->lightcolor[1] = 0.41f; ex->lightcolor[2] = 0.75f; } ex->ent.model = cl_mod_explode; ex->frames = 4; S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); break; case TE_LIGHTNING: ent = CL_ParseLightning(cl_mod_lightning); S_StartSound(NULL, ent, CHAN_WEAPON, cl_sfx_lightning, 1, ATTN_NORM, 0); break; case TE_DEBUGTRAIL: MSG_ReadPos(&net_message, pos); MSG_ReadPos(&net_message, pos2); CL_DebugTrail(pos, pos2); break; case TE_PLAIN_EXPLOSION: MSG_ReadPos(&net_message, pos); ex = CL_AllocExplosion(); VectorCopy(pos, ex->ent.origin); ex->type = ex_poly; ex->ent.flags = RF_FULLBRIGHT | RF_NOSHADOW; ex->start = cl.frame.servertime - 100.0f; ex->light = 350; ex->lightcolor[0] = 1.0; ex->lightcolor[1] = 0.5; ex->lightcolor[2] = 0.5; ex->ent.angles[1] = randk() % 360; ex->ent.model = cl_mod_explo4; if (frandk() < 0.5) { ex->baseframe = 15; } ex->frames = 15; if (type == TE_ROCKET_EXPLOSION_WATER) { S_StartSound(pos, 0, 0, cl_sfx_watrexp, 1, ATTN_NORM, 0); } else { S_StartSound(pos, 0, 0, cl_sfx_rockexp, 1, ATTN_NORM, 0); } break; case TE_FLASHLIGHT: MSG_ReadPos(&net_message, pos); ent = MSG_ReadShort(&net_message); CL_Flashlight(ent, pos); break; case TE_FORCEWALL: MSG_ReadPos(&net_message, pos); MSG_ReadPos(&net_message, pos2); color = MSG_ReadByte(&net_message); CL_ForceWall(pos, pos2, color); break; case TE_HEATBEAM: CL_ParsePlayerBeam(cl_mod_heatbeam); break; case TE_MONSTER_HEATBEAM: CL_ParsePlayerBeam(cl_mod_monster_heatbeam); break; case TE_HEATBEAM_SPARKS: cnt = 50; MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); r = 8; magnitude = 60; color = r & 0xff; CL_ParticleSteamEffect(pos, dir, color, cnt, magnitude); S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); break; case TE_HEATBEAM_STEAM: cnt = 20; MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); color = 0xe0; magnitude = 60; CL_ParticleSteamEffect(pos, dir, color, cnt, magnitude); S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); break; case TE_STEAM: CL_ParseSteam(); break; case TE_BUBBLETRAIL2: MSG_ReadPos(&net_message, pos); MSG_ReadPos(&net_message, pos2); CL_BubbleTrail2(pos, pos2, 8); S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); break; case TE_MOREBLOOD: MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); CL_ParticleEffect(pos, dir, 0xe8, 250); break; case TE_CHAINFIST_SMOKE: dir[0] = 0; dir[1] = 0; dir[2] = 1; MSG_ReadPos(&net_message, pos); CL_ParticleSmokeEffect(pos, dir, 0, 20, 20); break; case TE_ELECTRIC_SPARKS: MSG_ReadPos(&net_message, pos); MSG_ReadDir(&net_message, dir); CL_ParticleEffect(pos, dir, 0x75, 40); S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); break; case TE_TRACKER_EXPLOSION: MSG_ReadPos(&net_message, pos); CL_ColorFlash(pos, 0, 150, -1, -1, -1); CL_ColorExplosionParticles(pos, 0, 1); S_StartSound(pos, 0, 0, cl_sfx_disrexp, 1, ATTN_NORM, 0); break; case TE_TELEPORT_EFFECT: case TE_DBALL_GOAL: MSG_ReadPos(&net_message, pos); CL_TeleportParticles(pos); break; case TE_WIDOWBEAMOUT: CL_ParseWidow(); break; case TE_NUKEBLAST: CL_ParseNuke(); break; case TE_WIDOWSPLASH: MSG_ReadPos(&net_message, pos); CL_WidowSplash(pos); break; default: Com_Error(ERR_DROP, "CL_ParseTEnt: bad type"); } } void CL_AddBeams(void) { int i, j; beam_t *b; vec3_t dist, org; float d; entity_t ent; float yaw, pitch; float forward; float len, steps; float model_length; /* update beams */ 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.playernum + 1) /* entity 0 is the world */ { VectorCopy(cl.refdef.vieworg, b->start); b->start[2] -= 22; /* adjust for view height */ } VectorAdd(b->start, b->offset, org); /* calculate pitch and yaw */ VectorSubtract(b->end, org, dist); if ((dist[1] == 0) && (dist[0] == 0)) { yaw = 0; if (dist[2] > 0) { pitch = 90; } else { pitch = 270; } } else { if (dist[0]) { yaw = ((float)atan2(dist[1], dist[0]) * 180 / M_PI); } else if (dist[1] > 0) { yaw = 90; } else { yaw = 270; } if (yaw < 0) { yaw += 360; } forward = (float)sqrt(dist[0] * dist[0] + dist[1] * dist[1]); pitch = ((float)atan2(dist[2], forward) * -180.0 / M_PI); if (pitch < 0) { pitch += 360.0; } } /* add new entities for the beams */ d = VectorNormalize(dist); memset(&ent, 0, sizeof(ent)); if (b->model == cl_mod_lightning) { model_length = 35.0; d -= 20.0; /* correction so it doesn't end in middle of tesla */ } else { model_length = 30.0; } steps = (float)ceil(d / model_length); len = (d - model_length) / (steps - 1); /* special case for lightning model .. if the real length is shorter than the model, flip it around & draw it from the end to the start. This prevents the model from going through the tesla mine (instead it goes through the target) */ if ((b->model == cl_mod_lightning) && (d <= model_length)) { VectorCopy(b->end, ent.origin); ent.model = b->model; ent.flags = RF_FULLBRIGHT; ent.angles[0] = pitch; ent.angles[1] = yaw; ent.angles[2] = (float)(randk() % 360); V_AddEntity(&ent); return; } while (d > 0) { VectorCopy(org, ent.origin); ent.model = b->model; if (b->model == cl_mod_lightning) { ent.flags = RF_FULLBRIGHT; ent.angles[0] = -pitch; ent.angles[1] = yaw + 180.0f; ent.angles[2] = (float)(randk() % 360); } else { ent.angles[0] = pitch; ent.angles[1] = yaw; ent.angles[2] = (float)(randk() % 360); } V_AddEntity(&ent); for (j = 0; j < 3; j++) { org[j] += dist[j] * len; } d -= model_length; } } } extern cvar_t *hand; void CL_AddPlayerBeams(void) { int i, j; beam_t *b; vec3_t dist, org; float d; entity_t ent; float yaw, pitch; float forward; float len, steps; int framenum; float model_length; float hand_multiplier; frame_t *oldframe; player_state_t *ps, *ops; framenum = 0; if (hand) { if (hand->value == 2) { hand_multiplier = 0; } else if (hand->value == 1) { hand_multiplier = -1; } else { hand_multiplier = 1; } } else { hand_multiplier = 1; } /* update beams */ for (i = 0, b = cl_playerbeams; i < MAX_BEAMS; i++, b++) { vec3_t f, r, u; if (!b->model || (b->endtime < cl.time)) { continue; } if (cl_mod_heatbeam && (b->model == cl_mod_heatbeam)) { /* if coming from the player, update the start position */ if (b->entity == cl.playernum + 1) { /* set up gun position */ ps = &cl.frame.playerstate; j = (cl.frame.serverframe - 1) & UPDATE_MASK; oldframe = &cl.frames[j]; if ((oldframe->serverframe != cl.frame.serverframe - 1) || !oldframe->valid) { oldframe = &cl.frame; /* previous frame was dropped or invalid */ } ops = &oldframe->playerstate; for (j = 0; j < 3; j++) { b->start[j] = cl.refdef.vieworg[j] + ops->gunoffset[j] + cl.lerpfrac * (ps->gunoffset[j] - ops->gunoffset[j]); } VectorMA(b->start, (hand_multiplier * b->offset[0]), cl.v_right, org); VectorMA(org, b->offset[1], cl.v_forward, org); VectorMA(org, b->offset[2], cl.v_up, org); if ((hand) && (hand->value == 2)) { VectorMA(org, -1, cl.v_up, org); } VectorCopy(cl.v_right, r); VectorCopy(cl.v_forward, f); VectorCopy(cl.v_up, u); } else { VectorCopy(b->start, org); } } else { /* if coming from the player, update the start position */ if (b->entity == cl.playernum + 1) /* entity 0 is the world */ { VectorCopy(cl.refdef.vieworg, b->start); b->start[2] -= 22; /* adjust for view height */ } VectorAdd(b->start, b->offset, org); } /* calculate pitch and yaw */ VectorSubtract(b->end, org, dist); if (cl_mod_heatbeam && (b->model == cl_mod_heatbeam) && (b->entity == cl.playernum + 1)) { vec_t len; len = VectorLength(dist); VectorScale(f, len, dist); VectorMA(dist, (hand_multiplier * b->offset[0]), r, dist); VectorMA(dist, b->offset[1], f, dist); VectorMA(dist, b->offset[2], u, dist); if ((hand) && (hand->value == 2)) { VectorMA(org, -1, cl.v_up, org); } } if ((dist[1] == 0) && (dist[0] == 0)) { yaw = 0; if (dist[2] > 0) { pitch = 90; } else { pitch = 270; } } else { if (dist[0]) { yaw = ((float)atan2(dist[1], dist[0]) * 180 / M_PI); } else if (dist[1] > 0) { yaw = 90; } else { yaw = 270; } if (yaw < 0) { yaw += 360; } forward = sqrt(dist[0] * dist[0] + dist[1] * dist[1]); pitch = ((float)atan2(dist[2], forward) * -180.0 / M_PI); if (pitch < 0) { pitch += 360.0; } } if (cl_mod_heatbeam && (b->model == cl_mod_heatbeam)) { if (b->entity != cl.playernum + 1) { framenum = 2; ent.angles[0] = -pitch; ent.angles[1] = yaw + 180.0f; ent.angles[2] = 0; AngleVectors(ent.angles, f, r, u); /* if it's a non-origin offset, it's a player, so use the hardcoded player offset */ if (!VectorCompare(b->offset, vec3_origin)) { VectorMA(org, -(b->offset[0]) + 1, r, org); VectorMA(org, -(b->offset[1]), f, org); VectorMA(org, -(b->offset[2]) - 10, u, org); } else { /* if it's a monster, do the particle effect */ CL_MonsterPlasma_Shell(b->start); } } else { framenum = 1; } } /* if it's the heatbeam, draw the particle effect */ if ((cl_mod_heatbeam && (b->model == cl_mod_heatbeam) && (b->entity == cl.playernum + 1))) { CL_Heatbeam(org, dist); } /* add new entities for the beams */ d = VectorNormalize(dist); memset(&ent, 0, sizeof(ent)); if (b->model == cl_mod_heatbeam) { model_length = 32.0; } else if (b->model == cl_mod_lightning) { model_length = 35.0; d -= 20.0; /* correction so it doesn't end in middle of tesla */ } else { model_length = 30.0; } steps = ceil(d / model_length); len = (d - model_length) / (steps - 1); /* special case for lightning model .. if the real length is shorter than the model, flip it around & draw it from the end to the start. This prevents the model from going through the tesla mine (instead it goes through the target) */ if ((b->model == cl_mod_lightning) && (d <= model_length)) { VectorCopy(b->end, ent.origin); ent.model = b->model; ent.flags = RF_FULLBRIGHT; ent.angles[0] = pitch; ent.angles[1] = yaw; ent.angles[2] = (float)(randk() % 360); V_AddEntity(&ent); return; } while (d > 0) { VectorCopy(org, ent.origin); ent.model = b->model; if (cl_mod_heatbeam && (b->model == cl_mod_heatbeam)) { ent.flags = RF_FULLBRIGHT; ent.angles[0] = -pitch; ent.angles[1] = yaw + 180.0f; ent.angles[2] = (float)((cl.time) % 360); ent.frame = framenum; } else if (b->model == cl_mod_lightning) { ent.flags = RF_FULLBRIGHT; ent.angles[0] = -pitch; ent.angles[1] = yaw + 180.0f; ent.angles[2] = (float)(randk() % 360); } else { ent.angles[0] = pitch; ent.angles[1] = yaw; ent.angles[2] = (float)(randk() % 360); } V_AddEntity(&ent); for (j = 0; j < 3; j++) { org[j] += dist[j] * len; } d -= model_length; } } } void CL_AddExplosions(void) { entity_t *ent; int i; explosion_t *ex; float frac; int f; memset(&ent, 0, sizeof(ent)); for (i = 0, ex = cl_explosions; i < MAX_EXPLOSIONS; i++, ex++) { if (ex->type == ex_free) { continue; } frac = (cl.time - ex->start) / 100.0; f = (int)floor(frac); ent = &ex->ent; switch (ex->type) { case ex_mflash: if (f >= ex->frames - 1) { ex->type = ex_free; } break; case ex_misc: if (f >= ex->frames - 1) { ex->type = ex_free; break; } ent->alpha = 1.0f - frac / (ex->frames - 1); break; case ex_flash: if (f >= 1) { ex->type = ex_free; break; } ent->alpha = 1.0; break; case ex_poly: if (f >= ex->frames - 1) { ex->type = ex_free; break; } ent->alpha = (16.0f - (float)f) / 16.0f; if (f < 10) { ent->skinnum = (f >> 1); if (ent->skinnum < 0) { ent->skinnum = 0; } } else { ent->flags |= RF_TRANSLUCENT; if (f < 13) { ent->skinnum = 5; } else { ent->skinnum = 6; } } break; case ex_poly2: if (f >= ex->frames - 1) { ex->type = ex_free; break; } ent->alpha = (5.0 - (float)f) / 5.0; ent->skinnum = 0; ent->flags |= RF_TRANSLUCENT; break; default: break; } if (ex->type == ex_free) { continue; } if (ex->light) { V_AddLight(ent->origin, ex->light * ent->alpha, ex->lightcolor[0], ex->lightcolor[1], ex->lightcolor[2]); } VectorCopy(ent->origin, ent->oldorigin); if (f < 0) { f = 0; } ent->frame = ex->baseframe + f + 1; ent->oldframe = ex->baseframe + f; ent->backlerp = 1.0f - cl.lerpfrac; V_AddEntity(ent); } } void CL_AddLasers(void) { laser_t *l; int i; for (i = 0, l = cl_lasers; i < MAX_LASERS; i++, l++) { if (l->endtime >= cl.time) { V_AddEntity(&l->ent); } } } void CL_ProcessSustain() { cl_sustain_t *s; int i; for (i = 0, s = cl_sustains; i < MAX_SUSTAINS; i++, s++) { if (s->id) { if ((s->endtime >= cl.time) && (cl.time >= s->nextthink)) { s->think(s); } else if (s->endtime < cl.time) { s->id = 0; } } } } void CL_AddTEnts(void) { CL_AddBeams(); CL_AddPlayerBeams(); CL_AddExplosions(); CL_AddLasers(); CL_ProcessSustain(); } yquake2-QUAKE2_5_32/src/client/cl_cin.c0000644000175000017500000002606512615162034020270 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements the .cin video codec and the corresponding .pcx * bitmap decoder. .cin files are just a bunch of .pcx images. * * ======================================================================= */ #include "header/client.h" cvar_t *cin_force43; typedef struct { byte *data; int count; } cblock_t; typedef struct { qboolean restart_sound; int s_rate; int s_width; int s_channels; int width; int height; byte *pic; byte *pic_pending; /* order 1 huffman stuff */ int *hnodes1; /* [256][256][2]; */ int numhnodes1[256]; int h_used[512]; int h_count[512]; } cinematics_t; cinematics_t cin; void SCR_LoadPCX(char *filename, byte **pic, byte **palette, int *width, int *height) { byte *raw; pcx_t *pcx; int x, y; int len; int dataByte, runLength; byte *out, *pix; *pic = NULL; /* load the file */ len = FS_LoadFile(filename, (void **)&raw); if (!raw) { return; } /* parse the PCX file */ pcx = (pcx_t *)raw; raw = &pcx->data; if ((pcx->manufacturer != 0x0a) || (pcx->version != 5) || (pcx->encoding != 1) || (pcx->bits_per_pixel != 8) || (pcx->xmax >= 640) || (pcx->ymax >= 480)) { Com_Printf("Bad pcx file %s\n", filename); return; } out = Z_Malloc((pcx->ymax + 1) * (pcx->xmax + 1)); *pic = out; pix = out; if (palette) { *palette = Z_Malloc(768); memcpy(*palette, (byte *)pcx + len - 768, 768); } if (width) { *width = pcx->xmax + 1; } if (height) { *height = pcx->ymax + 1; } for (y = 0; y <= pcx->ymax; y++, pix += pcx->xmax + 1) { for (x = 0; x <= pcx->xmax; ) { dataByte = *raw++; if ((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; dataByte = *raw++; } else { runLength = 1; } while (runLength-- > 0) { pix[x++] = dataByte; } } } if (raw - (byte *)pcx > len) { Com_Printf("PCX file %s was malformed", filename); Z_Free(*pic); *pic = NULL; } FS_FreeFile(pcx); } void SCR_StopCinematic(void) { cl.cinematictime = 0; /* done */ if (cin.pic) { Z_Free(cin.pic); cin.pic = NULL; } if (cin.pic_pending) { Z_Free(cin.pic_pending); cin.pic_pending = NULL; } if (cl.cinematicpalette_active) { R_SetPalette(NULL); cl.cinematicpalette_active = false; } if (cl.cinematic_file) { FS_FCloseFile(cl.cinematic_file); cl.cinematic_file = 0; } if (cin.hnodes1) { Z_Free(cin.hnodes1); cin.hnodes1 = NULL; } /* switch back down to 11 khz sound if necessary */ if (cin.restart_sound) { cin.restart_sound = false; CL_Snd_Restart_f(); } } void SCR_FinishCinematic(void) { /* tell the server to advance to the next map / cinematic */ MSG_WriteByte(&cls.netchan.message, clc_stringcmd); SZ_Print(&cls.netchan.message, va("nextserver %i\n", cl.servercount)); } int SmallestNode1(int numhnodes) { int i; int best, bestnode; best = 99999999; bestnode = -1; for (i = 0; i < numhnodes; i++) { if (cin.h_used[i]) { continue; } if (!cin.h_count[i]) { continue; } if (cin.h_count[i] < best) { best = cin.h_count[i]; bestnode = i; } } if (bestnode == -1) { return -1; } cin.h_used[bestnode] = true; return bestnode; } /* * Reads the 64k counts table and initializes the node trees */ void Huff1TableInit(void) { int prev; int j; int *node, *nodebase; byte counts[256]; int numhnodes; cin.hnodes1 = Z_Malloc(256 * 256 * 2 * 4); memset(cin.hnodes1, 0, 256 * 256 * 2 * 4); for (prev = 0; prev < 256; prev++) { memset(cin.h_count, 0, sizeof(cin.h_count)); memset(cin.h_used, 0, sizeof(cin.h_used)); /* read a row of counts */ FS_Read(counts, sizeof(counts), cl.cinematic_file); for (j = 0; j < 256; j++) { cin.h_count[j] = counts[j]; } /* build the nodes */ numhnodes = 256; nodebase = cin.hnodes1 + prev * 256 * 2; while (numhnodes != 511) { node = nodebase + (numhnodes - 256) * 2; /* pick two lowest counts */ node[0] = SmallestNode1(numhnodes); if (node[0] == -1) { break; /* no more counts */ } node[1] = SmallestNode1(numhnodes); if (node[1] == -1) { break; } cin.h_count[numhnodes] = cin.h_count[node[0]] + cin.h_count[node[1]]; numhnodes++; } cin.numhnodes1[prev] = numhnodes - 1; } } cblock_t Huff1Decompress(cblock_t in) { byte *input; byte *out_p; int nodenum; int count; cblock_t out; int inbyte; int *hnodes, *hnodesbase; /* get decompressed count */ count = in.data[0] + (in.data[1] << 8) + (in.data[2] << 16) + (in.data[3] << 24); input = in.data + 4; out_p = out.data = Z_Malloc(count); /* read bits */ hnodesbase = cin.hnodes1 - 256 * 2; /* nodes 0-255 aren't stored */ hnodes = hnodesbase; nodenum = cin.numhnodes1[0]; while (count) { inbyte = *input++; int i = 0; for (i = 0; i < 8; i++) { if (nodenum < 256) { hnodes = hnodesbase + (nodenum << 9); *out_p++ = nodenum; if (!--count) { break; } nodenum = cin.numhnodes1[nodenum]; } nodenum = hnodes[nodenum * 2 + (inbyte & 1)]; inbyte >>= 1; } } if ((input - in.data != in.count) && (input - in.data != in.count + 1)) { Com_Printf("Decompression overread by %i", (input - in.data) - in.count); } out.count = out_p - out.data; return out; } byte * SCR_ReadNextFrame(void) { int r; int command; byte samples[22050 / 14 * 4]; byte compressed[0x20000]; int size; byte *pic; cblock_t in, huf1; int start, end, count; /* read the next frame */ r = FS_FRead(&command, 4, 1, cl.cinematic_file); if (r == 0) { /* we'll give it one more chance */ r = FS_FRead(&command, 4, 1, cl.cinematic_file); } if (r != 4) { return NULL; } command = LittleLong(command); if (command == 2) { return NULL; /* last frame marker */ } if (command == 1) { /* read palette */ FS_Read(cl.cinematicpalette, sizeof(cl.cinematicpalette), cl.cinematic_file); cl.cinematicpalette_active = 0; } /* decompress the next frame */ FS_Read(&size, 4, cl.cinematic_file); size = LittleLong(size); if (((unsigned long)size > sizeof(compressed)) || (size < 1)) { Com_Error(ERR_DROP, "Bad compressed frame size"); } FS_Read(compressed, size, cl.cinematic_file); /* read sound */ start = cl.cinematicframe * cin.s_rate / 14; end = (cl.cinematicframe + 1) * cin.s_rate / 14; count = end - start; FS_Read(samples, count * cin.s_width * cin.s_channels, cl.cinematic_file); if (cin.s_width == 2) { for (r = 0; r < count * cin.s_channels; r++) { ((short *)samples)[r] = LittleShort(((short *)samples)[r]); } } S_RawSamples(count, cin.s_rate, cin.s_width, cin.s_channels, samples, Cvar_VariableValue("s_volume")); in.data = compressed; in.count = size; huf1 = Huff1Decompress(in); pic = huf1.data; cl.cinematicframe++; return pic; } void SCR_RunCinematic(void) { int frame; if (cl.cinematictime <= 0) { SCR_StopCinematic(); return; } if (cl.cinematicframe == -1) { return; /* static image */ } if (cls.key_dest != key_game) { /* pause if menu or console is up */ cl.cinematictime = cls.realtime - cl.cinematicframe * 1000 / 14; return; } frame = (cls.realtime - cl.cinematictime) * 14.0 / 1000; if (frame <= cl.cinematicframe) { return; } if (frame > cl.cinematicframe + 1) { Com_Printf("Dropped frame: %i > %i\n", frame, cl.cinematicframe + 1); cl.cinematictime = cls.realtime - cl.cinematicframe * 1000 / 14; } if (cin.pic) { Z_Free(cin.pic); } cin.pic = cin.pic_pending; cin.pic_pending = NULL; cin.pic_pending = SCR_ReadNextFrame(); if (!cin.pic_pending) { SCR_StopCinematic(); SCR_FinishCinematic(); cl.cinematictime = 1; /* the black screen behind loading */ SCR_BeginLoadingPlaque(); cl.cinematictime = 0; return; } } /* * Returns true if a cinematic is active, meaning the * view rendering should be skipped */ qboolean SCR_DrawCinematic(void) { int x, y, w, h; if (cl.cinematictime <= 0) { return false; } /* blank screen and pause if menu is up */ if (cls.key_dest == key_menu) { R_SetPalette(NULL); cl.cinematicpalette_active = false; return true; } if (!cl.cinematicpalette_active) { R_SetPalette(cl.cinematicpalette); cl.cinematicpalette_active = true; } if (!cin.pic) { return true; } if (cin_force43->value) { w = viddef.height * 4 / 3; if (w > viddef.width) { w = viddef.width; } w &= ~3; h = w * 3 / 4; x = (viddef.width - w) / 2; y = (viddef.height - h) / 2; } else { x = y = 0; w = viddef.width; h = viddef.height; } if (x > 0) { Draw_Fill(0, 0, x, viddef.height, 0); } if (x + w < viddef.width) { Draw_Fill(x + w, 0, viddef.width - (x + w), viddef.height, 0); } if (y > 0) { Draw_Fill(x, 0, w, y, 0); } if (y + h < viddef.height) { Draw_Fill(x, y + h, w, viddef.height - (y + h), 0); } Draw_StretchRaw(x, y, w, h, cin.width, cin.height, cin.pic); return true; } void SCR_PlayCinematic(char *arg) { int width, height; byte *palette; char name[MAX_OSPATH], *dot; /* make sure background music is not playing */ #ifdef CDA CDAudio_Stop(); #endif #ifdef OGG OGG_Stop(); #endif cl.cinematicframe = 0; dot = strstr(arg, "."); /* static pcx image */ if (dot && !strcmp(dot, ".pcx")) { Com_sprintf(name, sizeof(name), "pics/%s", arg); SCR_LoadPCX(name, &cin.pic, &palette, &cin.width, &cin.height); cl.cinematicframe = -1; cl.cinematictime = 1; SCR_EndLoadingPlaque(); cls.state = ca_active; if (!cin.pic) { Com_Printf("%s not found.\n", name); cl.cinematictime = 0; } else { memcpy(cl.cinematicpalette, palette, sizeof(cl.cinematicpalette)); Z_Free(palette); } return; } Com_sprintf(name, sizeof(name), "video/%s", arg); FS_FOpenFile(name, &cl.cinematic_file, false); if (!cl.cinematic_file) { SCR_FinishCinematic(); cl.cinematictime = 0; /* done */ return; } SCR_EndLoadingPlaque(); cls.state = ca_active; FS_Read(&width, 4, cl.cinematic_file); FS_Read(&height, 4, cl.cinematic_file); cin.width = LittleLong(width); cin.height = LittleLong(height); FS_Read(&cin.s_rate, 4, cl.cinematic_file); cin.s_rate = LittleLong(cin.s_rate); FS_Read(&cin.s_width, 4, cl.cinematic_file); cin.s_width = LittleLong(cin.s_width); FS_Read(&cin.s_channels, 4, cl.cinematic_file); cin.s_channels = LittleLong(cin.s_channels); Huff1TableInit(); cl.cinematicframe = 0; cin.pic = SCR_ReadNextFrame(); cl.cinematictime = Sys_Milliseconds(); } yquake2-QUAKE2_5_32/src/client/cl_screen.c0000644000175000017500000007126412615162034020777 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements the 2D stuff. For example the HUD and the * networkgraph. * * ======================================================================= */ #include "header/client.h" float scr_con_current; /* aproaches scr_conlines at scr_conspeed */ float scr_conlines; /* 0.0 to 1.0 lines of console to display */ qboolean scr_initialized; /* ready to draw */ int scr_draw_loading; vrect_t scr_vrect; /* position of render window on screen */ cvar_t *scr_viewsize; cvar_t *scr_conspeed; cvar_t *scr_centertime; cvar_t *scr_showturtle; cvar_t *scr_showpause; cvar_t *scr_netgraph; cvar_t *scr_timegraph; cvar_t *scr_debuggraph; cvar_t *scr_graphheight; cvar_t *scr_graphscale; cvar_t *scr_graphshift; cvar_t *scr_drawall; cvar_t *gl_hudscale; /* named for consistency with R1Q2 */ cvar_t *gl_consolescale; cvar_t *gl_menuscale; typedef struct { int x1, y1, x2, y2; } dirty_t; dirty_t scr_dirty, scr_old_dirty[2]; char crosshair_pic[MAX_QPATH]; int crosshair_width, crosshair_height; extern cvar_t *cl_drawfps; extern cvar_t *crosshair_scale; void SCR_TimeRefresh_f(void); void SCR_Loading_f(void); /* * A new packet was just parsed */ void CL_AddNetgraph(void) { int i; int in; int ping; /* if using the debuggraph for something else, don't add the net lines */ if (scr_debuggraph->value || scr_timegraph->value) { return; } for (i = 0; i < cls.netchan.dropped; i++) { SCR_DebugGraph(30, 0x40); } for (i = 0; i < cl.surpressCount; i++) { SCR_DebugGraph(30, 0xdf); } /* see what the latency was on this packet */ in = cls.netchan.incoming_acknowledged & (CMD_BACKUP - 1); ping = cls.realtime - cl.cmd_time[in]; ping /= 30; if (ping > 30) { ping = 30; } SCR_DebugGraph((float)ping, 0xd0); } typedef struct { float value; int color; } graphsamp_t; static int current; static graphsamp_t values[2024]; void SCR_DebugGraph(float value, int color) { values[current & 2023].value = value; values[current & 2023].color = color; current++; } void SCR_DrawDebugGraph(void) { int a, x, y, w, i, h; float v; int color; /* draw the graph */ w = scr_vrect.width; x = scr_vrect.x; y = scr_vrect.y + scr_vrect.height; Draw_Fill(x, y - scr_graphheight->value, w, scr_graphheight->value, 8); for (a = 0; a < w; a++) { i = (current - 1 - a + 1024) & 1023; v = values[i].value; color = values[i].color; v = v * scr_graphscale->value + scr_graphshift->value; if (v < 0) { v += scr_graphheight->value * (1 + (int)(-v / scr_graphheight->value)); } h = (int)v % (int)scr_graphheight->value; Draw_Fill(x + w - 1 - a, y - h, 1, h, color); } } char scr_centerstring[1024]; float scr_centertime_start; /* for slow victory printing */ float scr_centertime_off; int scr_center_lines; int scr_erase_center; /* * Called for important messages that should stay * in the center of the screen for a few moments */ void SCR_CenterPrint(char *str) { char *s; char line[64]; int i, j, l; Q_strlcpy(scr_centerstring, str, sizeof(scr_centerstring)); scr_centertime_off = scr_centertime->value; scr_centertime_start = cl.time; /* count the number of lines for centering */ scr_center_lines = 1; s = str; while (*s) { if (*s == '\n') { scr_center_lines++; } s++; } /* echo it to the console */ Com_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"); s = str; do { /* scan the width of the line */ for (l = 0; l < 40; l++) { if ((s[l] == '\n') || !s[l]) { break; } } for (i = 0; i < (40 - l) / 2; i++) { line[i] = ' '; } for (j = 0; j < l; j++) { line[i++] = s[j]; } line[i] = '\n'; line[i + 1] = 0; Com_Printf("%s", line); while (*s && *s != '\n') { s++; } if (!*s) { break; } s++; /* skip the \n */ } while (1); Com_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_ClearNotify(); } void SCR_DrawCenterString(void) { char *start; int l; int j; int x, y; int remaining; float scale; const int char_unscaled_width = 8; const int char_unscaled_height = 8; /* the finale prints the characters one at a time */ remaining = 9999; scr_erase_center = 0; start = scr_centerstring; scale = SCR_GetConsoleScale(); if (scr_center_lines <= 4) { y = (viddef.height * 0.35) / scale; } else { y = 48 / scale; } do { /* scan the width of the line */ for (l = 0; l < 40; l++) { if ((start[l] == '\n') || !start[l]) { break; } } x = ((viddef.width / scale) - (l * char_unscaled_width)) / 2; SCR_AddDirtyPoint(x, y); for (j = 0; j < l; j++, x += char_unscaled_width) { Draw_CharScaled(x * scale, y * scale, start[j], scale); if (!remaining--) { return; } } SCR_AddDirtyPoint(x, y + char_unscaled_height); y += char_unscaled_height; while (*start && *start != '\n') { start++; } if (!*start) { break; } start++; /* skip the \n */ } while (1); } void SCR_CheckDrawCenterString(void) { scr_centertime_off -= cls.frametime; if (scr_centertime_off <= 0) { return; } SCR_DrawCenterString(); } /* * Sets scr_vrect, the coordinates of the rendered window */ static void SCR_CalcVrect(void) { int size; /* bound viewsize */ if (scr_viewsize->value < 40) { Cvar_Set("viewsize", "40"); } if (scr_viewsize->value > 100) { Cvar_Set("viewsize", "100"); } size = scr_viewsize->value; scr_vrect.width = viddef.width * size / 100; scr_vrect.width &= ~1; scr_vrect.height = viddef.height * size / 100; scr_vrect.height &= ~1; scr_vrect.x = (viddef.width - scr_vrect.width) / 2; scr_vrect.y = (viddef.height - scr_vrect.height) / 2; } /* * Keybinding command */ void SCR_SizeUp_f(void) { Cvar_SetValue("viewsize", (float)scr_viewsize->value + 10); } /* *Keybinding command */ void SCR_SizeDown_f(void) { Cvar_SetValue("viewsize", (float)scr_viewsize->value - 10); } /* * Set a specific sky and rotation speed */ void SCR_Sky_f(void) { float rotate; vec3_t axis; if (Cmd_Argc() < 2) { Com_Printf("Usage: sky \n"); return; } if (Cmd_Argc() > 2) { rotate = (float)strtod(Cmd_Argv(2), (char **)NULL); } else { rotate = 0; } if (Cmd_Argc() == 6) { axis[0] = (float)strtod(Cmd_Argv(3), (char **)NULL); axis[1] = (float)strtod(Cmd_Argv(4), (char **)NULL); axis[2] = (float)strtod(Cmd_Argv(5), (char **)NULL); } else { axis[0] = 0; axis[1] = 0; axis[2] = 1; } R_SetSky(Cmd_Argv(1), rotate, axis); } void SCR_Init(void) { scr_viewsize = Cvar_Get("viewsize", "100", CVAR_ARCHIVE); scr_conspeed = Cvar_Get("scr_conspeed", "3", 0); scr_centertime = Cvar_Get("scr_centertime", "2.5", 0); scr_showturtle = Cvar_Get("scr_showturtle", "0", 0); scr_showpause = Cvar_Get("scr_showpause", "1", 0); scr_netgraph = Cvar_Get("netgraph", "0", 0); scr_timegraph = Cvar_Get("timegraph", "0", 0); scr_debuggraph = Cvar_Get("debuggraph", "0", 0); scr_graphheight = Cvar_Get("graphheight", "32", 0); scr_graphscale = Cvar_Get("graphscale", "1", 0); scr_graphshift = Cvar_Get("graphshift", "0", 0); scr_drawall = Cvar_Get("scr_drawall", "0", 0); gl_hudscale = Cvar_Get("gl_hudscale", "-1", CVAR_ARCHIVE); gl_consolescale = Cvar_Get("gl_consolescale", "1", CVAR_ARCHIVE); gl_menuscale = Cvar_Get("gl_menuscale", "1", CVAR_ARCHIVE); /* register our commands */ Cmd_AddCommand("timerefresh", SCR_TimeRefresh_f); Cmd_AddCommand("loading", SCR_Loading_f); Cmd_AddCommand("sizeup", SCR_SizeUp_f); Cmd_AddCommand("sizedown", SCR_SizeDown_f); Cmd_AddCommand("sky", SCR_Sky_f); scr_initialized = true; } void SCR_DrawNet(void) { float scale = SCR_GetMenuScale(); if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged < CMD_BACKUP - 1) { return; } Draw_PicScaled(scr_vrect.x + 64 * scale, scr_vrect.y, "net", scale); } void SCR_DrawPause(void) { int w, h; float scale = SCR_GetMenuScale(); if (!scr_showpause->value) /* turn off for screenshots */ { return; } if (!cl_paused->value) { return; } Draw_GetPicSize(&w, &h, "pause"); Draw_PicScaled((viddef.width - w * scale) / 2, viddef.height / 2 + 8 * scale, "pause", scale); } void SCR_DrawLoading(void) { int w, h; float scale = SCR_GetMenuScale(); if (!scr_draw_loading) { return; } scr_draw_loading = false; Draw_GetPicSize(&w, &h, "loading"); Draw_PicScaled((viddef.width - w * scale) / 2, (viddef.height - h * scale) / 2, "loading", scale); } /* * Scroll it up or down */ void SCR_RunConsole(void) { /* decide on the height of the console */ if (cls.key_dest == key_console) { scr_conlines = 0.5; /* half screen */ } else { scr_conlines = 0; /* none visible */ } if (scr_conlines < scr_con_current) { scr_con_current -= scr_conspeed->value * cls.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 * cls.frametime; if (scr_conlines < scr_con_current) { scr_con_current = scr_conlines; } } } void SCR_DrawConsole(void) { Con_CheckResize(); if ((cls.state == ca_disconnected) || (cls.state == ca_connecting)) { /* forced full screen console */ Con_DrawConsole(1.0); return; } if ((cls.state != ca_active) || !cl.refresh_prepped) { /* connected, but can't render */ Con_DrawConsole(0.5); Draw_Fill(0, viddef.height / 2, viddef.width, viddef.height / 2, 0); return; } if (scr_con_current) { Con_DrawConsole(scr_con_current); } else { if ((cls.key_dest == key_game) || (cls.key_dest == key_message)) { Con_DrawNotify(); /* only draw notify in game */ } } } void SCR_BeginLoadingPlaque(void) { S_StopAllSounds(); cl.sound_prepped = false; /* don't play ambients */ #ifdef CDA CDAudio_Stop(); #endif #ifdef OGG OGG_Stop(); #endif if (cls.disable_screen) { return; } if (developer->value) { return; } if (cls.state == ca_disconnected) { /* if at console, don't bring up the plaque */ return; } if (cls.key_dest == key_console) { return; } if (cl.cinematictime > 0) { scr_draw_loading = 2; /* clear to balack first */ } else { scr_draw_loading = 1; } SCR_UpdateScreen(); SCR_StopCinematic(); cls.disable_screen = Sys_Milliseconds(); cls.disable_servercount = cl.servercount; } void SCR_EndLoadingPlaque(void) { cls.disable_screen = 0; Con_ClearNotify(); } void SCR_Loading_f(void) { SCR_BeginLoadingPlaque(); } void SCR_TimeRefresh_f(void) { int i; int start, stop; float time; if (cls.state != ca_active) { return; } start = Sys_Milliseconds(); if (Cmd_Argc() == 2) { /* run without page flipping */ int j; for (j = 0; j < 1000; j++) { R_BeginFrame(0); for (i = 0; i < 128; i++) { cl.refdef.viewangles[1] = i / 128.0f * 360.0f; R_RenderFrame(&cl.refdef); } GLimp_EndFrame(); } } else { for (i = 0; i < 128; i++) { cl.refdef.viewangles[1] = i / 128.0f * 360.0f; R_BeginFrame(0); R_RenderFrame(&cl.refdef); GLimp_EndFrame(); } } stop = Sys_Milliseconds(); time = (stop - start) / 1000.0f; Com_Printf("%f seconds (%f fps)\n", time, 128 / time); } void SCR_AddDirtyPoint(int x, int y) { if (x < scr_dirty.x1) { scr_dirty.x1 = x; } if (x > scr_dirty.x2) { scr_dirty.x2 = x; } if (y < scr_dirty.y1) { scr_dirty.y1 = y; } if (y > scr_dirty.y2) { scr_dirty.y2 = y; } } void SCR_DirtyScreen(void) { SCR_AddDirtyPoint(0, 0); SCR_AddDirtyPoint(viddef.width - 1, viddef.height - 1); } /* * Clear any parts of the tiled background that were drawn on last frame */ void SCR_TileClear(void) { int i; int top, bottom, left, right; dirty_t clear; if (scr_con_current == 1.0) { return; /* full screen console */ } if (scr_viewsize->value == 100) { return; /* full screen rendering */ } if (cl.cinematictime > 0) { return; /* full screen cinematic */ } /* erase rect will be the union of the past three frames so tripple buffering works properly */ clear = scr_dirty; for (i = 0; i < 2; i++) { if (scr_old_dirty[i].x1 < clear.x1) { clear.x1 = scr_old_dirty[i].x1; } if (scr_old_dirty[i].x2 > clear.x2) { clear.x2 = scr_old_dirty[i].x2; } if (scr_old_dirty[i].y1 < clear.y1) { clear.y1 = scr_old_dirty[i].y1; } if (scr_old_dirty[i].y2 > clear.y2) { clear.y2 = scr_old_dirty[i].y2; } } scr_old_dirty[1] = scr_old_dirty[0]; scr_old_dirty[0] = scr_dirty; scr_dirty.x1 = 9999; scr_dirty.x2 = -9999; scr_dirty.y1 = 9999; scr_dirty.y2 = -9999; /* don't bother with anything convered by the console */ top = (int)(scr_con_current * viddef.height); if (top >= clear.y1) { clear.y1 = top; } if (clear.y2 <= clear.y1) { return; /* nothing disturbed */ } top = scr_vrect.y; bottom = top + scr_vrect.height - 1; left = scr_vrect.x; right = left + scr_vrect.width - 1; if (clear.y1 < top) { /* clear above view screen */ i = clear.y2 < top - 1 ? clear.y2 : top - 1; Draw_TileClear(clear.x1, clear.y1, clear.x2 - clear.x1 + 1, i - clear.y1 + 1, "backtile"); clear.y1 = top; } if (clear.y2 > bottom) { /* clear below view screen */ i = clear.y1 > bottom + 1 ? clear.y1 : bottom + 1; Draw_TileClear(clear.x1, i, clear.x2 - clear.x1 + 1, clear.y2 - i + 1, "backtile"); clear.y2 = bottom; } if (clear.x1 < left) { /* clear left of view screen */ i = clear.x2 < left - 1 ? clear.x2 : left - 1; Draw_TileClear(clear.x1, clear.y1, i - clear.x1 + 1, clear.y2 - clear.y1 + 1, "backtile"); clear.x1 = left; } if (clear.x2 > right) { /* clear left of view screen */ i = clear.x1 > right + 1 ? clear.x1 : right + 1; Draw_TileClear(i, clear.y1, clear.x2 - i + 1, clear.y2 - clear.y1 + 1, "backtile"); clear.x2 = right; } } #define STAT_MINUS 10 char *sb_nums[2][11] = { { "num_0", "num_1", "num_2", "num_3", "num_4", "num_5", "num_6", "num_7", "num_8", "num_9", "num_minus" }, { "anum_0", "anum_1", "anum_2", "anum_3", "anum_4", "anum_5", "anum_6", "anum_7", "anum_8", "anum_9", "anum_minus" } }; #define ICON_WIDTH 24 #define ICON_HEIGHT 24 #define CHAR_WIDTH 16 #define ICON_SPACE 8 /* * Allow embedded \n in the string */ void SizeHUDString(char *string, int *w, int *h) { int lines, width, current; lines = 1; width = 0; current = 0; while (*string) { if (*string == '\n') { lines++; current = 0; } else { current++; if (current > width) { width = current; } } string++; } *w = width * 8; *h = lines * 8; } void DrawHUDStringScaled(char *string, int x, int y, int centerwidth, int xor, float factor) { int margin; char line[1024]; int width; int i; margin = x; while (*string) { /* scan out one line of text from the string */ width = 0; while (*string && *string != '\n') { line[width++] = *string++; } line[width] = 0; if (centerwidth) { x = margin + (centerwidth - width * 8)*factor / 2; } else { x = margin; } for (i = 0; i < width; i++) { Draw_CharScaled(x, y, line[i] ^ xor, factor); x += 8*factor; } if (*string) { string++; /* skip the \n */ y += 8*factor; } } } void DrawHUDString(char *string, int x, int y, int centerwidth, int xor) { DrawHUDStringScaled(string, x, y, centerwidth, xor, 1.0f); } void SCR_DrawFieldScaled(int x, int y, int color, int width, int value, float factor) { char num[16], *ptr; int l; int frame; if (width < 1) { return; } /* draw number string */ if (width > 5) { width = 5; } SCR_AddDirtyPoint(x, y); SCR_AddDirtyPoint(x + (width * CHAR_WIDTH + 2)*factor, y + 23); Com_sprintf(num, sizeof(num), "%i", value); l = (int)strlen(num); if (l > width) { l = width; } x += (2 + CHAR_WIDTH * (width - l)) * factor; ptr = num; while (*ptr && l) { if (*ptr == '-') { frame = STAT_MINUS; } else { frame = *ptr - '0'; } Draw_PicScaled(x, y, sb_nums[color][frame], factor); x += CHAR_WIDTH*factor; ptr++; l--; } } void SCR_DrawField(int x, int y, int color, int width, int value) { SCR_DrawFieldScaled(x, y, color, width, value, 1.0f); } /* * Allows rendering code to cache all needed sbar graphics */ void SCR_TouchPics(void) { int i, j; for (i = 0; i < 2; i++) { for (j = 0; j < 11; j++) { Draw_FindPic(sb_nums[i][j]); } } if (crosshair->value) { if ((crosshair->value > 3) || (crosshair->value < 0)) { crosshair->value = 3; } Com_sprintf(crosshair_pic, sizeof(crosshair_pic), "ch%i", (int)(crosshair->value)); Draw_GetPicSize(&crosshair_width, &crosshair_height, crosshair_pic); if (!crosshair_width) { crosshair_pic[0] = 0; } } } void SCR_ExecuteLayoutString(char *s) { int x, y; int value; char *token; int width; int index; clientinfo_t *ci; float scale = SCR_GetHUDScale(); if ((cls.state != ca_active) || !cl.refresh_prepped) { return; } if (!s[0]) { return; } x = 0; y = 0; while (s) { token = COM_Parse(&s); if (!strcmp(token, "xl")) { token = COM_Parse(&s); x = scale*(int)strtol(token, (char **)NULL, 10); continue; } if (!strcmp(token, "xr")) { token = COM_Parse(&s); x = viddef.width + scale*(int)strtol(token, (char **)NULL, 10); continue; } if (!strcmp(token, "xv")) { token = COM_Parse(&s); x = viddef.width / 2 - scale*160 + scale*(int)strtol(token, (char **)NULL, 10); continue; } if (!strcmp(token, "yt")) { token = COM_Parse(&s); y = scale*(int)strtol(token, (char **)NULL, 10); continue; } if (!strcmp(token, "yb")) { token = COM_Parse(&s); y = viddef.height + scale*(int)strtol(token, (char **)NULL, 10); continue; } if (!strcmp(token, "yv")) { token = COM_Parse(&s); y = viddef.height / 2 - scale*120 + scale*(int)strtol(token, (char **)NULL, 10); continue; } if (!strcmp(token, "pic")) { /* draw a pic from a stat number */ token = COM_Parse(&s); index = (int)strtol(token, (char **)NULL, 10); if ((index < 0) || (index >= sizeof(cl.frame.playerstate.stats))) { Com_Error(ERR_DROP, "bad stats index %d (0x%x)", index, index); } value = cl.frame.playerstate.stats[index]; if (value >= MAX_IMAGES) { Com_Error(ERR_DROP, "Pic >= MAX_IMAGES"); } if (cl.configstrings[CS_IMAGES + value]) { SCR_AddDirtyPoint(x, y); SCR_AddDirtyPoint(x + 23*scale, y + 23*scale); Draw_PicScaled(x, y, cl.configstrings[CS_IMAGES + value], scale); } continue; } if (!strcmp(token, "client")) { /* draw a deathmatch client block */ int score, ping, time; token = COM_Parse(&s); x = viddef.width / 2 - scale*160 + scale*(int)strtol(token, (char **)NULL, 10); token = COM_Parse(&s); y = viddef.height / 2 - scale*120 + scale*(int)strtol(token, (char **)NULL, 10); SCR_AddDirtyPoint(x, y); SCR_AddDirtyPoint(x + scale*159, y + scale*31); token = COM_Parse(&s); value = (int)strtol(token, (char **)NULL, 10); if ((value >= MAX_CLIENTS) || (value < 0)) { Com_Error(ERR_DROP, "client >= MAX_CLIENTS"); } ci = &cl.clientinfo[value]; token = COM_Parse(&s); score = (int)strtol(token, (char **)NULL, 10); token = COM_Parse(&s); ping = (int)strtol(token, (char **)NULL, 10); token = COM_Parse(&s); time = (int)strtol(token, (char **)NULL, 10); DrawAltStringScaled(x + scale*32, y, ci->name, scale); DrawAltStringScaled(x + scale*32, y + scale*8, "Score: ", scale); DrawAltStringScaled(x + scale*(32 + 7 * 8), y + scale*8, va("%i", score), scale); DrawStringScaled(x + scale*32, y + scale*16, va("Ping: %i", ping), scale); DrawStringScaled(x + scale*32, y + scale*24, va("Time: %i", time), scale); if (!ci->icon) { ci = &cl.baseclientinfo; } Draw_PicScaled(x, y, ci->iconname, scale); continue; } if (!strcmp(token, "ctf")) { /* draw a ctf client block */ int score, ping; char block[80]; token = COM_Parse(&s); x = viddef.width / 2 - scale*160 + scale*(int)strtol(token, (char **)NULL, 10); token = COM_Parse(&s); y = viddef.height / 2 - scale*120 + scale*(int)strtol(token, (char **)NULL, 10); SCR_AddDirtyPoint(x, y); SCR_AddDirtyPoint(x + scale*159, y + scale*31); token = COM_Parse(&s); value = (int)strtol(token, (char **)NULL, 10); if ((value >= MAX_CLIENTS) || (value < 0)) { Com_Error(ERR_DROP, "client >= MAX_CLIENTS"); } ci = &cl.clientinfo[value]; token = COM_Parse(&s); score = (int)strtol(token, (char **)NULL, 10); token = COM_Parse(&s); ping = (int)strtol(token, (char **)NULL, 10); if (ping > 999) { ping = 999; } sprintf(block, "%3d %3d %-12.12s", score, ping, ci->name); if (value == cl.playernum) { DrawAltStringScaled(x, y, block, scale); } else { DrawAltStringScaled(x, y, block, scale); } continue; } if (!strcmp(token, "picn")) { /* draw a pic from a name */ token = COM_Parse(&s); SCR_AddDirtyPoint(x, y); SCR_AddDirtyPoint(x + scale*23, y + scale*23); Draw_PicScaled(x, y, (char *)token, scale); continue; } if (!strcmp(token, "num")) { /* draw a number */ token = COM_Parse(&s); width = (int)strtol(token, (char **)NULL, 10); token = COM_Parse(&s); value = cl.frame.playerstate.stats[(int)strtol(token, (char **)NULL, 10)]; SCR_DrawFieldScaled(x, y, 0, width, value, scale); continue; } if (!strcmp(token, "hnum")) { /* health number */ int color; width = 3; value = cl.frame.playerstate.stats[STAT_HEALTH]; if (value > 25) { color = 0; /* green */ } else if (value > 0) { color = (cl.frame.serverframe >> 2) & 1; /* flash */ } else { color = 1; } if (cl.frame.playerstate.stats[STAT_FLASHES] & 1) { Draw_PicScaled(x, y, "field_3", scale); } SCR_DrawFieldScaled(x, y, color, width, value, scale); continue; } if (!strcmp(token, "anum")) { /* ammo number */ int color; width = 3; value = cl.frame.playerstate.stats[STAT_AMMO]; if (value > 5) { color = 0; /* green */ } else if (value >= 0) { color = (cl.frame.serverframe >> 2) & 1; /* flash */ } else { continue; /* negative number = don't show */ } if (cl.frame.playerstate.stats[STAT_FLASHES] & 4) { Draw_PicScaled(x, y, "field_3", scale); } SCR_DrawFieldScaled(x, y, color, width, value, scale); continue; } if (!strcmp(token, "rnum")) { /* armor number */ int color; width = 3; value = cl.frame.playerstate.stats[STAT_ARMOR]; if (value < 1) { continue; } color = 0; /* green */ if (cl.frame.playerstate.stats[STAT_FLASHES] & 2) { Draw_PicScaled(x, y, "field_3", scale); } SCR_DrawFieldScaled(x, y, color, width, value, scale); continue; } if (!strcmp(token, "stat_string")) { token = COM_Parse(&s); index = (int)strtol(token, (char **)NULL, 10); if ((index < 0) || (index >= MAX_CONFIGSTRINGS)) { Com_Error(ERR_DROP, "Bad stat_string index"); } index = cl.frame.playerstate.stats[index]; if ((index < 0) || (index >= MAX_CONFIGSTRINGS)) { Com_Error(ERR_DROP, "Bad stat_string index"); } DrawStringScaled(x, y, cl.configstrings[index], scale); continue; } if (!strcmp(token, "cstring")) { token = COM_Parse(&s); DrawHUDStringScaled(token, x, y, 320, 0, scale); // FIXME: or scale 320 here? continue; } if (!strcmp(token, "string")) { token = COM_Parse(&s); DrawStringScaled(x, y, token, scale); continue; } if (!strcmp(token, "cstring2")) { token = COM_Parse(&s); DrawHUDStringScaled(token, x, y, 320, 0x80, scale); // FIXME: or scale 320 here? continue; } if (!strcmp(token, "string2")) { token = COM_Parse(&s); DrawAltStringScaled(x, y, token, scale); continue; } if (!strcmp(token, "if")) { /* draw a number */ token = COM_Parse(&s); value = cl.frame.playerstate.stats[(int)strtol(token, (char **)NULL, 10)]; if (!value) { /* skip to endif */ while (s && strcmp(token, "endif")) { token = COM_Parse(&s); } } continue; } } } /* * The status bar is a small layout program that * is based on the stats array */ void SCR_DrawStats(void) { SCR_ExecuteLayoutString(cl.configstrings[CS_STATUSBAR]); } #define STAT_LAYOUTS 13 void SCR_DrawLayout(void) { if (!cl.frame.playerstate.stats[STAT_LAYOUTS]) { return; } SCR_ExecuteLayoutString(cl.layout); } /* * This is called every frame, and can also be called * explicitly to flush text to the screen. */ void SCR_UpdateScreen(void) { int numframes; int i; float separation[2] = {0, 0}; float scale = SCR_GetMenuScale(); /* if the screen is disabled (loading plaque is up, or vid mode changing) do nothing at all */ if (cls.disable_screen) { if (Sys_Milliseconds() - cls.disable_screen > 120000) { cls.disable_screen = 0; Com_Printf("Loading plaque timed out.\n"); } return; } if (!scr_initialized || !con.initialized) { return; /* not initialized yet */ } separation[0] = 0; separation[1] = 0; numframes = 1; for (i = 0; i < numframes; i++) { R_BeginFrame(separation[i]); if (scr_draw_loading == 2) { /* loading plaque over black screen */ int w, h; R_SetPalette(NULL); scr_draw_loading = false; Draw_GetPicSize(&w, &h, "loading"); Draw_PicScaled((viddef.width - w * scale) / 2, (viddef.height - h * scale) / 2, "loading", scale); } /* if a cinematic is supposed to be running, handle menus and console specially */ else if (cl.cinematictime > 0) { if (cls.key_dest == key_menu) { if (cl.cinematicpalette_active) { R_SetPalette(NULL); cl.cinematicpalette_active = false; } M_Draw(); } else if (cls.key_dest == key_console) { if (cl.cinematicpalette_active) { R_SetPalette(NULL); cl.cinematicpalette_active = false; } SCR_DrawConsole(); } else { SCR_DrawCinematic(); } } else { /* make sure the game palette is active */ if (cl.cinematicpalette_active) { R_SetPalette(NULL); cl.cinematicpalette_active = false; } /* do 3D refresh drawing, and then update the screen */ SCR_CalcVrect(); /* clear any dirty part of the background */ SCR_TileClear(); V_RenderView(separation[i]); SCR_DrawStats(); if (cl.frame.playerstate.stats[STAT_LAYOUTS] & 1) { SCR_DrawLayout(); } if (cl.frame.playerstate.stats[STAT_LAYOUTS] & 2) { CL_DrawInventory(); } SCR_DrawNet(); SCR_CheckDrawCenterString(); if (cl_drawfps->value) { char s[8]; sprintf(s, "%3.0ffps", 1 / cls.frametime); DrawString(viddef.width - 64, 0, s); } if (scr_timegraph->value) { SCR_DebugGraph(cls.frametime * 300, 0); } if (scr_debuggraph->value || scr_timegraph->value || scr_netgraph->value) { SCR_DrawDebugGraph(); } SCR_DrawPause(); SCR_DrawConsole(); M_Draw(); SCR_DrawLoading(); } } GLimp_EndFrame(); } float SCR_GetDefaultScale(void) { int i = viddef.width / 640; int j = viddef.height / 240; if (i > j) { i = j; } if (i < 1) { i = 1; } return i; } void SCR_DrawCrosshair(void) { float scale; if (!crosshair->value) { return; } if (crosshair->modified) { crosshair->modified = false; SCR_TouchPics(); } if (!crosshair_pic[0]) { return; } if (crosshair_scale->value < 0) { scale = SCR_GetDefaultScale(); } else { scale = crosshair_scale->value; } Draw_PicScaled(scr_vrect.x + ((scr_vrect.width - crosshair_width) >> 1), scr_vrect.y + ((scr_vrect.height - crosshair_height) >> 1), crosshair_pic, scale); } float SCR_GetHUDScale(void) { float scale; if (gl_hudscale->value < 0) { scale = SCR_GetDefaultScale(); } else { scale = gl_hudscale->value; } return scale; } float SCR_GetConsoleScale(void) { float scale; if (gl_consolescale->value < 0) { scale = SCR_GetDefaultScale(); } else { scale = gl_consolescale->value; } return scale; } float SCR_GetMenuScale(void) { float scale; if (gl_menuscale->value < 0) { scale = SCR_GetDefaultScale(); } else { scale = gl_menuscale->value; } return scale; } yquake2-QUAKE2_5_32/src/client/cl_prediction.c0000644000175000017500000001454412615162034021656 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements interpolation between two frames. This is used * to smooth down network play * * ======================================================================= */ #include "header/client.h" void CL_CheckPredictionError(void) { int frame; int delta[3]; int i; int len; if (!cl_predict->value || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION)) { return; } /* calculate the last usercmd_t we sent that the server has processed */ frame = cls.netchan.incoming_acknowledged; frame &= (CMD_BACKUP - 1); /* compare what the server returned with what we had predicted it to be */ VectorSubtract(cl.frame.playerstate.pmove.origin, cl.predicted_origins[frame], delta); /* save the prediction error for interpolation */ len = abs(delta[0]) + abs(delta[1]) + abs(delta[2]); /* 80 world units */ if (len > 640) { /* a teleport or something */ VectorClear(cl.prediction_error); } else { if (cl_showmiss->value && (delta[0] || delta[1] || delta[2])) { Com_Printf("prediction miss on %i: %i\n", cl.frame.serverframe, delta[0] + delta[1] + delta[2]); } VectorCopy(cl.frame.playerstate.pmove.origin, cl.predicted_origins[frame]); /* save for error itnerpolation */ for (i = 0; i < 3; i++) { cl.prediction_error[i] = delta[i] * 0.125f; } } } void CL_ClipMoveToEntities(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, trace_t *tr) { int i, x, zd, zu; trace_t trace; int headnode; float *angles; entity_state_t *ent; int num; cmodel_t *cmodel; vec3_t bmins, bmaxs; for (i = 0; i < cl.frame.num_entities; i++) { num = (cl.frame.parse_entities + i) & (MAX_PARSE_ENTITIES - 1); ent = &cl_parse_entities[num]; if (!ent->solid) { continue; } if (ent->number == cl.playernum + 1) { continue; } if (ent->solid == 31) { /* special value for bmodel */ cmodel = cl.model_clip[ent->modelindex]; if (!cmodel) { continue; } headnode = cmodel->headnode; angles = ent->angles; } else { /* encoded bbox */ x = 8 * (ent->solid & 31); zd = 8 * ((ent->solid >> 5) & 31); zu = 8 * ((ent->solid >> 10) & 63) - 32; bmins[0] = bmins[1] = -(float)x; bmaxs[0] = bmaxs[1] = (float)x; bmins[2] = -(float)zd; bmaxs[2] = (float)zu; headnode = CM_HeadnodeForBox(bmins, bmaxs); angles = vec3_origin; /* boxes don't rotate */ } if (tr->allsolid) { return; } trace = CM_TransformedBoxTrace(start, end, mins, maxs, headnode, MASK_PLAYERSOLID, ent->origin, angles); if (trace.allsolid || trace.startsolid || (trace.fraction < tr->fraction)) { trace.ent = (struct edict_s *)ent; if (tr->startsolid) { *tr = trace; tr->startsolid = true; } else { *tr = trace; } } else if (trace.startsolid) { tr->startsolid = true; } } } trace_t CL_PMTrace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end) { trace_t t; /* check against world */ t = CM_BoxTrace(start, end, mins, maxs, 0, MASK_PLAYERSOLID); if (t.fraction < 1.0) { t.ent = (struct edict_s *)1; } /* check all other solid models */ CL_ClipMoveToEntities(start, mins, maxs, end, &t); return t; } int CL_PMpointcontents(vec3_t point) { int i; entity_state_t *ent; int num; cmodel_t *cmodel; int contents; contents = CM_PointContents(point, 0); for (i = 0; i < cl.frame.num_entities; i++) { num = (cl.frame.parse_entities + i) & (MAX_PARSE_ENTITIES - 1); ent = &cl_parse_entities[num]; if (ent->solid != 31) /* special value for bmodel */ { continue; } cmodel = cl.model_clip[ent->modelindex]; if (!cmodel) { continue; } contents |= CM_TransformedPointContents(point, cmodel->headnode, ent->origin, ent->angles); } return contents; } /* * Sets cl.predicted_origin and cl.predicted_angles */ void CL_PredictMovement(void) { int ack, current; int frame; int oldframe; usercmd_t *cmd; pmove_t pm; int i; int step; int oldz; if (cls.state != ca_active) { return; } if (cl_paused->value) { return; } if (!cl_predict->value || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION)) { /* just set angles */ for (i = 0; i < 3; i++) { cl.predicted_angles[i] = cl.viewangles[i] + SHORT2ANGLE( cl.frame.playerstate.pmove.delta_angles[i]); } return; } ack = cls.netchan.incoming_acknowledged; current = cls.netchan.outgoing_sequence; /* if we are too far out of date, just freeze */ if (current - ack >= CMD_BACKUP) { if (cl_showmiss->value) { Com_Printf("exceeded CMD_BACKUP\n"); } return; } /* copy current state to pmove */ pm.trace = CL_PMTrace; pm.pointcontents = CL_PMpointcontents; pm_airaccelerate = strtod(cl.configstrings[CS_AIRACCEL], (char **)NULL); pm.s = cl.frame.playerstate.pmove; VectorSet(pm.mins, -16, -16, -24); VectorSet(pm.maxs, 16, 16, 32); /* run frames */ while (++ack < current) { frame = ack & (CMD_BACKUP - 1); cmd = &cl.cmds[frame]; pm.cmd = *cmd; Pmove(&pm); /* save for debug checking */ VectorCopy(pm.s.origin, cl.predicted_origins[frame]); } oldframe = (ack - 2) & (CMD_BACKUP - 1); oldz = cl.predicted_origins[oldframe][2]; step = pm.s.origin[2] - oldz; if ((step > 63) && (step < 160) && (pm.s.pm_flags & PMF_ON_GROUND)) { cl.predicted_step = step * 0.125f; cl.predicted_step_time = cls.realtime - cls.frametime * 500; } /* copy results out for rendering */ cl.predicted_origin[0] = pm.s.origin[0] * 0.125f; cl.predicted_origin[1] = pm.s.origin[1] * 0.125f; cl.predicted_origin[2] = pm.s.origin[2] * 0.125f; VectorCopy(pm.viewangles, cl.predicted_angles); } yquake2-QUAKE2_5_32/src/client/cl_keyboard.c0000644000175000017500000005476512615162034021327 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 layer of the keyboard implementation. This file processes all * keyboard events which are generated by the low level keyboard layer. * Remeber, that the mouse is handled by the refresher and not by the * client! * * ======================================================================= */ #include "header/client.h" #include "refresh/header/local.h" static cvar_t *cfg_unbindall; /* * key up events are sent even if in console mode */ char key_lines[NUM_KEY_LINES][MAXCMDLINE]; int key_linepos; int anykeydown; int edit_line = 0; int history_line = 0; int key_waiting; char *keybindings[K_LAST]; qboolean consolekeys[K_LAST]; /* if true, can't be rebound while in console */ qboolean menubound[K_LAST]; /* if true, can't be rebound while in menu */ int key_repeats[K_LAST]; /* if > 1, it is autorepeating */ qboolean keydown[K_LAST]; qboolean Cmd_IsComplete(char *cmd); typedef struct { char *name; int keynum; } keyname_t; /* Translates internal key representations * into human readable strings. */ keyname_t keynames[] = { {"TAB", K_TAB}, {"ENTER", K_ENTER}, {"ESCAPE", K_ESCAPE}, {"SPACE", K_SPACE}, {"BACKSPACE", K_BACKSPACE}, {"COMMAND", K_COMMAND}, {"CAPSLOCK", K_CAPSLOCK}, {"POWER", K_POWER}, {"PAUSE", K_PAUSE}, {"UPARROW", K_UPARROW}, {"DOWNARROW", K_DOWNARROW}, {"LEFTARROW", K_LEFTARROW}, {"RIGHTARROW", K_RIGHTARROW}, {"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}, {"END", K_END}, {"MOUSE1", K_MOUSE1}, {"MOUSE2", K_MOUSE2}, {"MOUSE3", K_MOUSE3}, {"MOUSE4", K_MOUSE4}, {"MOUSE5", K_MOUSE5}, {"AUX1", K_AUX1}, {"AUX2", 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}, {"KP_HOME", K_KP_HOME}, {"KP_UPARROW", K_KP_UPARROW}, {"KP_PGUP", K_KP_PGUP}, {"KP_LEFTARROW", K_KP_LEFTARROW}, {"KP_5", K_KP_5}, {"KP_RIGHTARROW", K_KP_RIGHTARROW}, {"KP_END", K_KP_END}, {"KP_DOWNARROW", K_KP_DOWNARROW}, {"KP_PGDN", K_KP_PGDN}, {"KP_ENTER", K_KP_ENTER}, {"KP_INS", K_KP_INS}, {"KP_DEL", K_KP_DEL}, {"KP_SLASH", K_KP_SLASH}, {"KP_MINUS", K_KP_MINUS}, {"KP_PLUS", K_KP_PLUS}, {"MWHEELUP", K_MWHEELUP}, {"MWHEELDOWN", K_MWHEELDOWN}, {"PAUSE", K_PAUSE}, {"SEMICOLON", ';'}, /* because a raw semicolon seperates commands */ {NULL, 0} }; /* ------------------------------------------------------------------ */ void CompleteCommand(void) { char *cmd, *s; s = key_lines[edit_line] + 1; if ((*s == '\\') || (*s == '/')) { s++; } cmd = Cmd_CompleteCommand(s); if (cmd) { key_lines[edit_line][1] = '/'; strcpy(key_lines[edit_line] + 2, cmd); key_linepos = strlen(cmd) + 2; if (Cmd_IsComplete(cmd)) { key_lines[edit_line][key_linepos] = ' '; key_linepos++; key_lines[edit_line][key_linepos] = 0; } else { key_lines[edit_line][key_linepos] = 0; } return; } } /* * Interactive line editing and console scrollback */ void Key_Console(int key) { /* * Ignore keypad in console to prevent duplicate * entries through key presses processed as a * normal char event and additionally as key * event. */ switch (key) { case K_KP_SLASH: case K_KP_MINUS: case K_KP_PLUS: case K_KP_HOME: case K_KP_UPARROW: case K_KP_PGUP: case K_KP_LEFTARROW: case K_KP_5: case K_KP_RIGHTARROW: case K_KP_END: case K_KP_DOWNARROW: case K_KP_PGDN: case K_KP_INS: case K_KP_DEL: return; break; default: break; } if (key == 'l') { if (keydown[K_CTRL]) { Cbuf_AddText("clear\n"); return; } } if ((key == K_ENTER) || (key == K_KP_ENTER)) { /* slash text are commands, else chat */ if ((key_lines[edit_line][1] == '\\') || (key_lines[edit_line][1] == '/')) { Cbuf_AddText(key_lines[edit_line] + 2); /* skip the > */ } else { Cbuf_AddText(key_lines[edit_line] + 1); /* valid command */ } Cbuf_AddText("\n"); Com_Printf("%s\n", key_lines[edit_line]); edit_line = (edit_line + 1) & (NUM_KEY_LINES-1); 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 */ } return; } if (key == K_TAB) { /* command completion */ CompleteCommand(); return; } if ((key == K_BACKSPACE) || (key == K_LEFTARROW) || (key == K_KP_LEFTARROW) || ((key == 'h') && (keydown[K_CTRL]))) { if (key_linepos > 1) { key_linepos--; } return; } if (key == K_DEL) { memmove(key_lines[edit_line] + key_linepos, key_lines[edit_line] + key_linepos + 1, sizeof(key_lines[edit_line]) - key_linepos - 1); return; } if ((key == K_UPARROW) || (key == K_KP_UPARROW) || ((key == 'p') && keydown[K_CTRL])) { do { history_line = (history_line - 1) & (NUM_KEY_LINES-1); } while (history_line != edit_line && !key_lines[history_line][1]); if (history_line == edit_line) { history_line = (edit_line + 1) & (NUM_KEY_LINES-1); } strcpy(key_lines[edit_line], key_lines[history_line]); key_linepos = (int)strlen(key_lines[edit_line]); return; } if ((key == K_DOWNARROW) || (key == K_KP_DOWNARROW) || ((key == 'n') && keydown[K_CTRL])) { if (history_line == edit_line) { return; } do { history_line = (history_line + 1) & (NUM_KEY_LINES-1); } while (history_line != edit_line && !key_lines[history_line][1]); if (history_line == edit_line) { key_lines[edit_line][0] = ']'; key_linepos = 1; } else { strcpy(key_lines[edit_line], key_lines[history_line]); key_linepos = (int)strlen(key_lines[edit_line]); } return; } if ((key == K_PGUP) || (key == K_KP_PGUP) || (key == K_MWHEELUP) || (key == K_MOUSE4)) { con.display -= 2; return; } if ((key == K_PGDN) || (key == K_KP_PGDN) || (key == K_MWHEELDOWN) || (key == K_MOUSE5)) { con.display += 2; if (con.display > con.current) { con.display = con.current; } return; } if ((key == K_HOME) || (key == K_KP_HOME)) { if (keydown[K_CTRL]) { con.display = con.current - con.totallines + 10; } else { key_linepos = 1; } return; } if ((key == K_END) || (key == K_KP_END)) { if (keydown[K_CTRL]) { con.display = con.current; } else { key_linepos = (int)strlen(key_lines[edit_line]); } return; } if ((key < 32) || (key > 127)) { return; /* non printable character */ } if (key_linepos < MAXCMDLINE - 1) { int last; int length; length = strlen(key_lines[edit_line]); if (length >= MAXCMDLINE - 1) { return; } last = key_lines[edit_line][key_linepos]; memmove(key_lines[edit_line] + key_linepos + 1, key_lines[edit_line] + key_linepos, length - key_linepos); key_lines[edit_line][key_linepos] = key; key_linepos++; if (!last) { key_lines[edit_line][key_linepos] = 0; } } } qboolean chat_team; char chat_buffer[MAXCMDLINE]; int chat_bufferlen = 0; int chat_cursorpos = 0; void Key_Message(int key) { char last; if ((key == K_ENTER) || (key == K_KP_ENTER)) { if (chat_team) { Cbuf_AddText("say_team \""); } else { Cbuf_AddText("say \""); } Cbuf_AddText(chat_buffer); Cbuf_AddText("\"\n"); cls.key_dest = key_game; chat_bufferlen = 0; chat_buffer[0] = 0; chat_cursorpos = 0; return; } if (key == K_ESCAPE) { cls.key_dest = key_game; chat_cursorpos = 0; chat_bufferlen = 0; chat_buffer[0] = 0; return; } if (key == K_BACKSPACE) { if (chat_cursorpos) { memmove(chat_buffer + chat_cursorpos - 1, chat_buffer + chat_cursorpos, chat_bufferlen - chat_cursorpos + 1); chat_cursorpos--; chat_bufferlen--; } return; } if (key == K_DEL) { if (chat_bufferlen && (chat_cursorpos != chat_bufferlen)) { memmove(chat_buffer + chat_cursorpos, chat_buffer + chat_cursorpos + 1, chat_bufferlen - chat_cursorpos + 1); chat_bufferlen--; } return; } if (key == K_LEFTARROW) { if (chat_cursorpos > 0) { chat_cursorpos--; } return; } if (key == K_HOME) { chat_cursorpos = 0; return; } if (key == K_END) { chat_cursorpos = chat_bufferlen; return; } if (key == K_RIGHTARROW) { if (chat_buffer[chat_cursorpos]) { chat_cursorpos++; } return; } if ((key < 32) || (key > 127)) { return; /* non printable charcter */ } if (chat_bufferlen == sizeof(chat_buffer) - 1) { return; /* all full, this should never happen on modern systems */ } memmove(chat_buffer + chat_cursorpos + 1, chat_buffer + chat_cursorpos, chat_bufferlen - chat_cursorpos + 1); last = chat_buffer[chat_cursorpos]; chat_buffer[chat_cursorpos] = key; chat_bufferlen++; chat_cursorpos++; if (!last) { chat_buffer[chat_cursorpos] = 0; } } /* * 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_stricmp(str, kn->name)) { return kn->keynum; } } return -1; } /* * Returns a string (either a single ascii char, * or a K_* name) for the given keynum. */ char * Key_KeynumToString(int keynum) { keyname_t *kn; static char tinystr[2] = {0}; if (keynum == -1) { return ""; } if ((keynum > 32) && (keynum < 127)) { /* printable ascii */ tinystr[0] = keynum; return tinystr; } for (kn = keynames; kn->name; kn++) { if (keynum == kn->keynum) { return kn->name; } } return ""; } void Key_SetBinding(int keynum, char *binding) { char *new; int l; if (keynum == -1) { return; } /* free old bindings */ if (keybindings[keynum]) { Z_Free(keybindings[keynum]); keybindings[keynum] = NULL; } /* allocate memory for new binding */ l = strlen(binding); new = Z_Malloc(l + 1); strcpy(new, binding); new[l] = 0; keybindings[keynum] = new; } void Key_Unbind_f(void) { int b; if (Cmd_Argc() != 2) { Com_Printf("unbind : remove commands from a key\n"); return; } b = Key_StringToKeynum(Cmd_Argv(1)); if (b == -1) { Com_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 < K_LAST; i++) { if (keybindings[i]) { Key_SetBinding(i, ""); } } } /* ugly hack, set in Cmd_ExecuteString() when yq2.cfg is executed * (=> default.cfg is done) */ extern qboolean doneWithDefaultCfg; void Key_Bind_f(void) { int i, c, b; char cmd[1024]; c = Cmd_Argc(); if (c < 2) { Com_Printf("bind [command] : attach a command to a key\n"); return; } b = Key_StringToKeynum(Cmd_Argv(1)); if (b == -1) { Com_Printf("\"%s\" isn't a valid key\n", Cmd_Argv(1)); return; } /* don't allow binding escape or the special console keys */ if(b == K_ESCAPE || b == '^' || b == '`' || b == '~') { if(doneWithDefaultCfg) { /* don't warn about this when it's from default.cfg, we can't change that anyway */ Com_Printf("You can't bind the special key \"%s\"!\n", Cmd_Argv(1)); } return; } if (c == 2) { if (keybindings[b]) { Com_Printf("\"%s\" = \"%s\"\n", Cmd_Argv(1), keybindings[b]); } else { Com_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++) { strcat(cmd, Cmd_Argv(i)); if (i != (c - 1)) { strcat(cmd, " "); } } Key_SetBinding(b, cmd); } /* * Writes lines containing "bind key value" */ void Key_WriteBindings(FILE *f) { int i; if (cfg_unbindall->value) { fprintf(f, "unbindall\n"); } for (i = 0; i < K_LAST; i++) { if (keybindings[i] && keybindings[i][0]) { fprintf(f, "bind %s \"%s\"\n", Key_KeynumToString(i), keybindings[i]); } } } void Key_WriteConsoleHistory() { int i; char path[MAX_OSPATH]; Com_sprintf(path, sizeof(path), "%sconsole_history.txt", Sys_GetHomeDir()); FILE* f = fopen(path, "w"); if(f==NULL) { Com_Printf("Opening console history %s for writing failed!\n", path); return; } // save the oldest lines first by starting at edit_line // and going forward (and wrapping around) const char* lastWrittenLine = ""; for(i=0; i= 0) { key_lines[i][lastCharIdx] = '\0'; --lastCharIdx; } } fclose(f); } void Key_Bindlist_f(void) { int i; for (i = 0; i < K_LAST; i++) { if (keybindings[i] && keybindings[i][0]) { Com_Printf("%s \"%s\"\n", Key_KeynumToString(i), keybindings[i]); } } } void Key_Init(void) { int i; for (i = 0; i < NUM_KEY_LINES; i++) { key_lines[i][0] = ']'; key_lines[i][1] = 0; } // can't call Key_ReadConsoleHistory() here because FS_Gamedir() isn't set yet key_linepos = 1; /* init 128 bit ascii characters in console mode */ for (i = 32; i < 128; i++) { consolekeys[i] = true; } consolekeys[K_ENTER] = true; consolekeys[K_KP_ENTER] = true; consolekeys[K_TAB] = true; consolekeys[K_LEFTARROW] = true; consolekeys[K_KP_LEFTARROW] = true; consolekeys[K_RIGHTARROW] = true; consolekeys[K_KP_RIGHTARROW] = true; consolekeys[K_UPARROW] = true; consolekeys[K_KP_UPARROW] = true; consolekeys[K_DOWNARROW] = true; consolekeys[K_KP_DOWNARROW] = true; consolekeys[K_BACKSPACE] = true; consolekeys[K_HOME] = true; consolekeys[K_KP_HOME] = true; consolekeys[K_END] = true; consolekeys[K_KP_END] = true; consolekeys[K_PGUP] = true; consolekeys[K_KP_PGUP] = true; consolekeys[K_PGDN] = true; consolekeys[K_KP_PGDN] = true; consolekeys[K_SHIFT] = true; consolekeys[K_INS] = true; consolekeys[K_KP_INS] = true; consolekeys[K_KP_DEL] = true; consolekeys[K_KP_SLASH] = true; consolekeys[K_KP_PLUS] = true; consolekeys[K_KP_MINUS] = true; consolekeys[K_KP_5] = true; consolekeys['`'] = false; consolekeys['~'] = false; consolekeys['^'] = false; menubound[K_ESCAPE] = true; for (i = 0; i < 12; i++) { menubound[K_F1 + i] = true; } /* register our variables */ cfg_unbindall = Cvar_Get("cfg_unbindall", "1", CVAR_ARCHIVE); /* register our functions */ Cmd_AddCommand("bind", Key_Bind_f); Cmd_AddCommand("unbind", Key_Unbind_f); Cmd_AddCommand("unbindall", Key_Unbindall_f); Cmd_AddCommand("bindlist", Key_Bindlist_f); } /* * Called every frame for every detected keypress. * ASCII input for the console, the menu and the * chat window are handled by this function. * Anything else is handled by Key_Event(). */ void Char_Event(int key) { /* console key is hardcoded, so the user can never unbind it */ if ((key == '^') || (key == '~') || (key == '`')) { Con_ToggleConsole_f(); return; } switch (cls.key_dest) { /* Chat */ case key_message: Key_Message(key); break; /* Menu */ case key_menu: M_Keydown(key); break; /* Console */ case key_game: case key_console: Key_Console(key); break; } } /* * Called every frame for every detected keypress. * This is only for movement and special characters, * anything else is handled by Char_Event(). */ void Key_Event(int key, qboolean down, qboolean special) { char cmd[1024]; char *kb; cvar_t *fullscreen; unsigned int time = Sys_Milliseconds(); /* Track if key is down */ keydown[key] = down; /* Ignore most autorepeats */ if (down) { key_repeats[key]++; if ((key != K_BACKSPACE) && (key != K_PAUSE) && (key != K_PGUP) && (key != K_KP_PGUP) && (key != K_PGDN) && (key != K_KP_PGDN) && (key_repeats[key] > 1)) { return; } } else { key_repeats[key] = 0; } /* Fullscreen switch through Alt + Return */ if (down && keydown[K_ALT] && key == K_ENTER) { fullscreen = Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); if (!fullscreen->value) { fullscreen->value = 1; fullscreen->modified = true; } else { fullscreen->value = 0; fullscreen->modified = true; } return; } /* Toogle console though Shift + Escape */ if (down && keydown[K_SHIFT] && key == K_ESCAPE) { Con_ToggleConsole_f(); return; } /* Key is unbound */ if ((key >= 200) && !keybindings[key] && (cls.key_dest != key_console)) { Com_Printf("%s is unbound, hit F4 to set.\n", Key_KeynumToString(key)); } /* While in attract loop all keys besides F1 to F12 (to allow quick load and the like) are treated like escape. */ if (cl.attractloop && (cls.key_dest != key_menu) && !((key >= K_F1) && (key <= K_F12))) { key = K_ESCAPE; } /* Escape has a special meaning. Depending on the situation it - pauses the game and breaks into the menu - stops the attract loop and breaks into the menu - closes the console and breaks into the menu - moves one menu level up - closes the menu - closes the help computer - closes the chat window */ if (!cls.disable_screen) { if (key == K_ESCAPE) { if (!down) { return; } /* Close the help computer */ if (cl.frame.playerstate.stats[STAT_LAYOUTS] && (cls.key_dest == key_game)) { Cbuf_AddText("cmd putaway\n"); return; } switch (cls.key_dest) { /* Close chat window */ case key_message: Key_Message(key); break; /* Close menu or one layer up */ case key_menu: M_Keydown(key); break; /* Pause game and / or leave console, break into the menu. */ case key_game: case key_console: M_Menu_Main_f(); break; } return; } } /* This is one of the most ugly constructs I've found so far in Quake II. When the game is in the intermission, the player can press any key to end it and advance into the next level. It should be easy to figure out at server level if a button is pressed. But somehow the developers decided, that they'll need special move state BUTTON_ANY to solve this problem. So there's this global variable anykeydown. If it's not 0, CL_FinishMove() encodes BUTTON_ANY into the button state. The server reads this value and sends it to gi->ClientThink() where it's used to determine if the intermission shall end. Needless to say that this is the only consumer of BUTTON_ANY. Since we cannot alter the network protocol nor the server <-> game API, I'll leave things alone and try to forget. */ if (down) { if (key_repeats[key] == 1) { anykeydown++; } } else { anykeydown--; if (anykeydown < 0) { anykeydown = 0; } } /* 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] == '+')) { Com_sprintf(cmd, sizeof(cmd), "-%s %i %i\n", kb + 1, key, time); Cbuf_AddText(cmd); } return; } else if (((cls.key_dest == key_menu) && menubound[key]) || ((cls.key_dest == key_console) && !consolekeys[key]) || ((cls.key_dest == key_game) && ((cls.state == ca_active) || !consolekeys[key]))) { kb = keybindings[key]; if (kb) { if (kb[0] == '+') { /* button commands add keynum and time as a parm */ Com_sprintf(cmd, sizeof(cmd), "%s %i %i\n", kb, key, time); Cbuf_AddText(cmd); } else { Cbuf_AddText(kb); Cbuf_AddText("\n"); } } return; } /* All input subsystems handled after this point only care for key down events. */ if (!down) { return; } /* Everything that's not a special char is processed by Char_Event(). */ if (!special) { return; } /* Send key to the active input subsystem */ switch (cls.key_dest) { /* Chat */ case key_message: Key_Message(key); break; /* Menu */ case key_menu: M_Keydown(key); break; /* Console */ case key_game: case key_console: Key_Console(key); break; } } /* * Marks all keys as "up" */ void Key_MarkAllUp(void) { int key; for (key = 0; key < K_LAST; key++) { key_repeats[key] = 0; keydown[key] = 0; } } yquake2-QUAKE2_5_32/src/client/cl_input.c0000644000175000017500000003225412615162034020653 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements the input handling like mouse events and * keyboard strokes. * * ======================================================================= */ #include "header/client.h" #include "../backends/generic/header/input.h" cvar_t *cl_nodelta; extern unsigned sys_frame_time; unsigned frame_msec; unsigned old_sys_frame_time; /* * 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 * * * Key_Event (int key, qboolean down, unsigned time); * * +mlook src time */ kbutton_t in_klook; kbutton_t in_left, in_right, in_forward, in_back; kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; kbutton_t in_strafe, in_speed, in_use, in_attack; kbutton_t in_up, in_down; int in_impulse; void KeyDown(kbutton_t *b) { int k; char *c; c = Cmd_Argv(1); if (c[0]) { k = (int)strtol(c, (char **)NULL, 10); } 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 { Com_Printf("Three keys down for a button!\n"); return; } if (b->state & 1) { return; /* still down */ } /* save timestamp */ c = Cmd_Argv(2); b->downtime = (int)strtol(c, (char **)NULL, 10); if (!b->downtime) { b->downtime = sys_frame_time - 100; } b->state |= 1 + 2; /* down + impulse down */ } void KeyUp(kbutton_t *b) { int k; char *c; unsigned uptime; c = Cmd_Argv(1); if (c[0]) { k = (int)strtol(c, (char **)NULL, 10); } 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) */ } /* save timestamp */ c = Cmd_Argv(2); uptime = (int)strtol(c, (char **)NULL, 10); if (uptime) { b->msec += uptime - b->downtime; } else { b->msec += 10; } b->state &= ~1; /* now up */ b->state |= 4; /* impulse up */ } void IN_KLookDown(void) { KeyDown(&in_klook); } void IN_KLookUp(void) { KeyUp(&in_klook); } void IN_UpDown(void) { KeyDown(&in_up); } void IN_UpUp(void) { KeyUp(&in_up); } void IN_DownDown(void) { KeyDown(&in_down); } void IN_DownUp(void) { KeyUp(&in_down); } void IN_LeftDown(void) { KeyDown(&in_left); } void IN_LeftUp(void) { KeyUp(&in_left); } void IN_RightDown(void) { KeyDown(&in_right); } void IN_RightUp(void) { KeyUp(&in_right); } void IN_ForwardDown(void) { KeyDown(&in_forward); } void IN_ForwardUp(void) { KeyUp(&in_forward); } void IN_BackDown(void) { KeyDown(&in_back); } void IN_BackUp(void) { KeyUp(&in_back); } void IN_LookupDown(void) { KeyDown(&in_lookup); } void IN_LookupUp(void) { KeyUp(&in_lookup); } void IN_LookdownDown(void) { KeyDown(&in_lookdown); } void IN_LookdownUp(void) { KeyUp(&in_lookdown); } void IN_MoveleftDown(void) { KeyDown(&in_moveleft); } void IN_MoveleftUp(void) { KeyUp(&in_moveleft); } void IN_MoverightDown(void) { KeyDown(&in_moveright); } void IN_MoverightUp(void) { KeyUp(&in_moveright); } void IN_SpeedDown(void) { KeyDown(&in_speed); } void IN_SpeedUp(void) { KeyUp(&in_speed); } void IN_StrafeDown(void) { KeyDown(&in_strafe); } void IN_StrafeUp(void) { KeyUp(&in_strafe); } void IN_AttackDown(void) { KeyDown(&in_attack); } void IN_AttackUp(void) { KeyUp(&in_attack); } void IN_UseDown(void) { KeyDown(&in_use); } void IN_UseUp(void) { KeyUp(&in_use); } void IN_Impulse(void) { in_impulse = (int)strtol(Cmd_Argv(1), (char **)NULL, 10); } /* * Returns the fraction of the * frame that the key was down */ float CL_KeyState(kbutton_t *key) { float val; int msec; key->state &= 1; /* clear impulses */ msec = key->msec; key->msec = 0; if (key->state) { /* still down */ msec += sys_frame_time - key->downtime; key->downtime = sys_frame_time; } val = (float)msec / frame_msec; if (val < 0) { val = 0; } if (val > 1) { val = 1; } return val; } cvar_t *cl_upspeed; cvar_t *cl_forwardspeed; cvar_t *cl_sidespeed; cvar_t *cl_yawspeed; cvar_t *cl_pitchspeed; cvar_t *cl_run; cvar_t *cl_anglespeedkey; /* * Moves the local angle positions */ void CL_AdjustAngles(void) { float speed; float up, down; if (in_speed.state & 1) { speed = cls.frametime * cl_anglespeedkey->value; } else { speed = cls.frametime; } 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); } if (in_klook.state & 1) { 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; } /* * Send the intended movement message to the server */ void CL_BaseMove(usercmd_t *cmd) { CL_AdjustAngles(); memset(cmd, 0, sizeof(*cmd)); VectorCopy(cl.viewangles, cmd->angles); if (in_strafe.state & 1) { cmd->sidemove += cl_sidespeed->value * CL_KeyState(&in_right); cmd->sidemove -= cl_sidespeed->value * CL_KeyState(&in_left); } cmd->sidemove += cl_sidespeed->value * CL_KeyState(&in_moveright); cmd->sidemove -= cl_sidespeed->value * CL_KeyState(&in_moveleft); cmd->upmove += cl_upspeed->value * CL_KeyState(&in_up); cmd->upmove -= cl_upspeed->value * CL_KeyState(&in_down); if (!(in_klook.state & 1)) { cmd->forwardmove += cl_forwardspeed->value * CL_KeyState(&in_forward); cmd->forwardmove -= cl_forwardspeed->value * CL_KeyState(&in_back); } /* adjust for speed key / running */ if ((in_speed.state & 1) ^ (int)(cl_run->value)) { cmd->forwardmove *= 2; cmd->sidemove *= 2; cmd->upmove *= 2; } } void CL_ClampPitch(void) { float pitch; pitch = SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]); if (pitch > 180) { pitch -= 360; } if (cl.viewangles[PITCH] + pitch < -360) { cl.viewangles[PITCH] += 360; /* wrapped */ } if (cl.viewangles[PITCH] + pitch > 360) { cl.viewangles[PITCH] -= 360; /* wrapped */ } if (cl.viewangles[PITCH] + pitch > 89) { cl.viewangles[PITCH] = 89 - pitch; } if (cl.viewangles[PITCH] + pitch < -89) { cl.viewangles[PITCH] = -89 - pitch; } } void CL_FinishMove(usercmd_t *cmd) { int ms; int i; /* figure button bits */ if (in_attack.state & 3) { cmd->buttons |= BUTTON_ATTACK; } in_attack.state &= ~2; if (in_use.state & 3) { cmd->buttons |= BUTTON_USE; } in_use.state &= ~2; if (anykeydown && (cls.key_dest == key_game)) { cmd->buttons |= BUTTON_ANY; } /* send milliseconds of time to apply the move */ ms = cls.frametime * 1000; if (ms > 250) { ms = 100; /* time was unreasonable */ } cmd->msec = ms; CL_ClampPitch(); for (i = 0; i < 3; i++) { cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]); } cmd->impulse = in_impulse; in_impulse = 0; /* send the ambient light level at the player's current position */ cmd->lightlevel = (byte)cl_lightlevel->value; } usercmd_t CL_CreateCmd(void) { usercmd_t cmd; frame_msec = sys_frame_time - old_sys_frame_time; if (frame_msec < 1) { frame_msec = 1; } if (frame_msec > 200) { frame_msec = 200; } /* get basic movement from keyboard */ CL_BaseMove(&cmd); /* allow mice or other external controllers to add to the move */ IN_Move(&cmd); CL_FinishMove(&cmd); old_sys_frame_time = sys_frame_time; return cmd; } void IN_CenterView(void) { cl.viewangles[PITCH] = -SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]); } /* * Centers the view */ static void IN_ForceCenterView(void) { cl.viewangles[PITCH] = 0; } void CL_InitInput(void) { Cmd_AddCommand("centerview", IN_CenterView); Cmd_AddCommand("force_centerview", IN_ForceCenterView); 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("impulse", IN_Impulse); Cmd_AddCommand("+klook", IN_KLookDown); Cmd_AddCommand("-klook", IN_KLookUp); cl_nodelta = Cvar_Get("cl_nodelta", "0", 0); } void CL_SendCmd(void) { sizebuf_t buf; byte data[128]; int i; usercmd_t *cmd, *oldcmd; usercmd_t nullcmd; int checksumIndex; /* build a command even if not connected */ /* save this command off for prediction */ i = cls.netchan.outgoing_sequence & (CMD_BACKUP - 1); cmd = &cl.cmds[i]; cl.cmd_time[i] = cls.realtime; /* for netgraph ping calculation */ *cmd = CL_CreateCmd(); cl.cmd = *cmd; if ((cls.state == ca_disconnected) || (cls.state == ca_connecting)) { return; } if (cls.state == ca_connected) { if (cls.netchan.message.cursize || (curtime - cls.netchan.last_sent > 100)) { byte zero_data = 0; Netchan_Transmit(&cls.netchan, 0, &zero_data); } return; } /* send a userinfo update if needed */ if (userinfo_modified) { CL_FixUpGender(); userinfo_modified = false; MSG_WriteByte(&cls.netchan.message, clc_userinfo); MSG_WriteString(&cls.netchan.message, Cvar_Userinfo()); } SZ_Init(&buf, data, sizeof(data)); if (cmd->buttons && (cl.cinematictime > 0) && !cl.attractloop && (cls.realtime - cl.cinematictime > 1000)) { /* skip the rest of the cinematic */ SCR_FinishCinematic(); } /* begin a client move command */ MSG_WriteByte(&buf, clc_move); /* save the position for a checksum byte */ checksumIndex = buf.cursize; MSG_WriteByte(&buf, 0); /* let the server know what the last frame we got was, so the next message can be delta compressed */ if (cl_nodelta->value || !cl.frame.valid || cls.demowaiting) { MSG_WriteLong(&buf, -1); /* no compression */ } else { MSG_WriteLong(&buf, cl.frame.serverframe); } /* send this and the previous cmds in the message, so if the last packet was dropped, it can be recovered */ i = (cls.netchan.outgoing_sequence - 2) & (CMD_BACKUP - 1); cmd = &cl.cmds[i]; memset(&nullcmd, 0, sizeof(nullcmd)); MSG_WriteDeltaUsercmd(&buf, &nullcmd, cmd); oldcmd = cmd; i = (cls.netchan.outgoing_sequence - 1) & (CMD_BACKUP - 1); cmd = &cl.cmds[i]; MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd); oldcmd = cmd; i = (cls.netchan.outgoing_sequence) & (CMD_BACKUP - 1); cmd = &cl.cmds[i]; MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd); /* calculate a checksum over the move commands */ buf.data[checksumIndex] = COM_BlockSequenceCRCByte( buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1, cls.netchan.outgoing_sequence); /* deliver the message */ Netchan_Transmit(&cls.netchan, buf.cursize, buf.data); } yquake2-QUAKE2_5_32/src/client/cl_parse.c0000644000175000017500000007010112615162034020617 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements the entity and network protocol parsing * * ======================================================================= */ #include "header/client.h" void CL_DownloadFileName(char *dest, int destlen, char *fn); void CL_ParseDownload(void); int bitcounts[32]; /* just for protocol profiling */ char *svc_strings[256] = { "svc_bad", "svc_muzzleflash", "svc_muzzlflash2", "svc_temp_entity", "svc_layout", "svc_inventory", "svc_nop", "svc_disconnect", "svc_reconnect", "svc_sound", "svc_print", "svc_stufftext", "svc_serverdata", "svc_configstring", "svc_spawnbaseline", "svc_centerprint", "svc_download", "svc_playerinfo", "svc_packetentities", "svc_deltapacketentities", "svc_frame" }; void CL_RegisterSounds(void) { int i; S_BeginRegistration(); CL_RegisterTEntSounds(); for (i = 1; i < MAX_SOUNDS; i++) { if (!cl.configstrings[CS_SOUNDS + i][0]) { break; } cl.sound_precache[i] = S_RegisterSound(cl.configstrings[CS_SOUNDS + i]); Sys_SendKeyEvents(); } S_EndRegistration(); } /* * Returns the entity number and the header bits */ int CL_ParseEntityBits(unsigned *bits) { unsigned b, total; int i; int number; total = MSG_ReadByte(&net_message); if (total & U_MOREBITS1) { b = MSG_ReadByte(&net_message); total |= b << 8; } if (total & U_MOREBITS2) { b = MSG_ReadByte(&net_message); total |= b << 16; } if (total & U_MOREBITS3) { b = MSG_ReadByte(&net_message); total |= b << 24; } /* count the bits for net profiling */ for (i = 0; i < 32; i++) { if (total & (1 << i)) { bitcounts[i]++; } } if (total & U_NUMBER16) { number = MSG_ReadShort(&net_message); } else { number = MSG_ReadByte(&net_message); } *bits = total; return number; } /* * Can go from either a baseline or a previous packet_entity */ void CL_ParseDelta(entity_state_t *from, entity_state_t *to, int number, int bits) { /* set everything to the state we are delta'ing from */ *to = *from; VectorCopy(from->origin, to->old_origin); to->number = number; if (bits & U_MODEL) { to->modelindex = MSG_ReadByte(&net_message); } if (bits & U_MODEL2) { to->modelindex2 = MSG_ReadByte(&net_message); } if (bits & U_MODEL3) { to->modelindex3 = MSG_ReadByte(&net_message); } if (bits & U_MODEL4) { to->modelindex4 = MSG_ReadByte(&net_message); } if (bits & U_FRAME8) { to->frame = MSG_ReadByte(&net_message); } if (bits & U_FRAME16) { to->frame = MSG_ReadShort(&net_message); } /* used for laser colors */ if ((bits & U_SKIN8) && (bits & U_SKIN16)) { to->skinnum = MSG_ReadLong(&net_message); } else if (bits & U_SKIN8) { to->skinnum = MSG_ReadByte(&net_message); } else if (bits & U_SKIN16) { to->skinnum = MSG_ReadShort(&net_message); } if ((bits & (U_EFFECTS8 | U_EFFECTS16)) == (U_EFFECTS8 | U_EFFECTS16)) { to->effects = MSG_ReadLong(&net_message); } else if (bits & U_EFFECTS8) { to->effects = MSG_ReadByte(&net_message); } else if (bits & U_EFFECTS16) { to->effects = MSG_ReadShort(&net_message); } if ((bits & (U_RENDERFX8 | U_RENDERFX16)) == (U_RENDERFX8 | U_RENDERFX16)) { to->renderfx = MSG_ReadLong(&net_message); } else if (bits & U_RENDERFX8) { to->renderfx = MSG_ReadByte(&net_message); } else if (bits & U_RENDERFX16) { to->renderfx = MSG_ReadShort(&net_message); } if (bits & U_ORIGIN1) { to->origin[0] = MSG_ReadCoord(&net_message); } if (bits & U_ORIGIN2) { to->origin[1] = MSG_ReadCoord(&net_message); } if (bits & U_ORIGIN3) { to->origin[2] = MSG_ReadCoord(&net_message); } if (bits & U_ANGLE1) { to->angles[0] = MSG_ReadAngle(&net_message); } if (bits & U_ANGLE2) { to->angles[1] = MSG_ReadAngle(&net_message); } if (bits & U_ANGLE3) { to->angles[2] = MSG_ReadAngle(&net_message); } if (bits & U_OLDORIGIN) { MSG_ReadPos(&net_message, to->old_origin); } if (bits & U_SOUND) { to->sound = MSG_ReadByte(&net_message); } if (bits & U_EVENT) { to->event = MSG_ReadByte(&net_message); } else { to->event = 0; } if (bits & U_SOLID) { to->solid = MSG_ReadShort(&net_message); } } /* * Parses deltas from the given base and adds the resulting entity to * the current frame */ void CL_DeltaEntity(frame_t *frame, int newnum, entity_state_t *old, int bits) { centity_t *ent; entity_state_t *state; ent = &cl_entities[newnum]; state = &cl_parse_entities[cl.parse_entities & (MAX_PARSE_ENTITIES - 1)]; cl.parse_entities++; frame->num_entities++; CL_ParseDelta(old, state, newnum, bits); /* some data changes will force no lerping */ if ((state->modelindex != ent->current.modelindex) || (state->modelindex2 != ent->current.modelindex2) || (state->modelindex3 != ent->current.modelindex3) || (state->modelindex4 != ent->current.modelindex4) || (state->event == EV_PLAYER_TELEPORT) || (state->event == EV_OTHER_TELEPORT) || (abs((int)(state->origin[0] - ent->current.origin[0])) > 512) || (abs((int)(state->origin[1] - ent->current.origin[1])) > 512) || (abs((int)(state->origin[2] - ent->current.origin[2])) > 512) ) { ent->serverframe = -99; } /* wasn't in last update, so initialize some things */ if (ent->serverframe != cl.frame.serverframe - 1) { ent->trailcount = 1024; /* for diminishing rocket / grenade trails */ /* duplicate the current state so lerping doesn't hurt anything */ ent->prev = *state; if (state->event == EV_OTHER_TELEPORT) { VectorCopy(state->origin, ent->prev.origin); VectorCopy(state->origin, ent->lerp_origin); } else { VectorCopy(state->old_origin, ent->prev.origin); VectorCopy(state->old_origin, ent->lerp_origin); } } else { /* shuffle the last state to previous */ ent->prev = ent->current; } ent->serverframe = cl.frame.serverframe; ent->current = *state; } /* * An svc_packetentities has just been * parsed, deal with the rest of the * data stream. */ void CL_ParsePacketEntities(frame_t *oldframe, frame_t *newframe) { unsigned int newnum; unsigned bits; entity_state_t *oldstate = NULL; int oldindex, oldnum; newframe->parse_entities = cl.parse_entities; newframe->num_entities = 0; /* delta from the entities present in oldframe */ oldindex = 0; if (!oldframe) { oldnum = 99999; } else { if (oldindex >= oldframe->num_entities) { oldnum = 99999; } else { oldstate = &cl_parse_entities[(oldframe->parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)]; oldnum = oldstate->number; } } while (1) { newnum = CL_ParseEntityBits(&bits); if (newnum >= MAX_EDICTS) { Com_Error(ERR_DROP, "CL_ParsePacketEntities: bad number:%i", newnum); } if (net_message.readcount > net_message.cursize) { Com_Error(ERR_DROP, "CL_ParsePacketEntities: end of message"); } if (!newnum) { break; } while (oldnum < newnum) { /* one or more entities from the old packet are unchanged */ if (cl_shownet->value == 3) { Com_Printf(" unchanged: %i\n", oldnum); } CL_DeltaEntity(newframe, oldnum, oldstate, 0); oldindex++; if (oldindex >= oldframe->num_entities) { oldnum = 99999; } else { oldstate = &cl_parse_entities[(oldframe->parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)]; oldnum = oldstate->number; } } if (bits & U_REMOVE) { /* the entity present in oldframe is not in the current frame */ if (cl_shownet->value == 3) { Com_Printf(" remove: %i\n", newnum); } if (oldnum != newnum) { Com_Printf("U_REMOVE: oldnum != newnum\n"); } oldindex++; if (oldindex >= oldframe->num_entities) { oldnum = 99999; } else { oldstate = &cl_parse_entities[(oldframe->parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)]; oldnum = oldstate->number; } continue; } if (oldnum == newnum) { /* delta from previous state */ if (cl_shownet->value == 3) { Com_Printf(" delta: %i\n", newnum); } CL_DeltaEntity(newframe, newnum, oldstate, bits); oldindex++; if (oldindex >= oldframe->num_entities) { oldnum = 99999; } else { oldstate = &cl_parse_entities[(oldframe->parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)]; oldnum = oldstate->number; } continue; } if (oldnum > newnum) { /* delta from baseline */ if (cl_shownet->value == 3) { Com_Printf(" baseline: %i\n", newnum); } CL_DeltaEntity(newframe, newnum, &cl_entities[newnum].baseline, bits); continue; } } /* any remaining entities in the old frame are copied over */ while (oldnum != 99999) { /* one or more entities from the old packet are unchanged */ if (cl_shownet->value == 3) { Com_Printf(" unchanged: %i\n", oldnum); } CL_DeltaEntity(newframe, oldnum, oldstate, 0); oldindex++; if (oldindex >= oldframe->num_entities) { oldnum = 99999; } else { oldstate = &cl_parse_entities[(oldframe->parse_entities + oldindex) & (MAX_PARSE_ENTITIES - 1)]; oldnum = oldstate->number; } } } void CL_ParsePlayerstate(frame_t *oldframe, frame_t *newframe) { int flags; player_state_t *state; int i; int statbits; state = &newframe->playerstate; /* clear to old value before delta parsing */ if (oldframe) { *state = oldframe->playerstate; } else { memset(state, 0, sizeof(*state)); } flags = MSG_ReadShort(&net_message); /* parse the pmove_state_t */ if (flags & PS_M_TYPE) { state->pmove.pm_type = MSG_ReadByte(&net_message); } if (flags & PS_M_ORIGIN) { state->pmove.origin[0] = MSG_ReadShort(&net_message); state->pmove.origin[1] = MSG_ReadShort(&net_message); state->pmove.origin[2] = MSG_ReadShort(&net_message); } if (flags & PS_M_VELOCITY) { state->pmove.velocity[0] = MSG_ReadShort(&net_message); state->pmove.velocity[1] = MSG_ReadShort(&net_message); state->pmove.velocity[2] = MSG_ReadShort(&net_message); } if (flags & PS_M_TIME) { state->pmove.pm_time = MSG_ReadByte(&net_message); } if (flags & PS_M_FLAGS) { state->pmove.pm_flags = MSG_ReadByte(&net_message); } if (flags & PS_M_GRAVITY) { state->pmove.gravity = MSG_ReadShort(&net_message); } if (flags & PS_M_DELTA_ANGLES) { state->pmove.delta_angles[0] = MSG_ReadShort(&net_message); state->pmove.delta_angles[1] = MSG_ReadShort(&net_message); state->pmove.delta_angles[2] = MSG_ReadShort(&net_message); } if (cl.attractloop) { state->pmove.pm_type = PM_FREEZE; /* demo playback */ } /* parse the rest of the player_state_t */ if (flags & PS_VIEWOFFSET) { state->viewoffset[0] = MSG_ReadChar(&net_message) * 0.25f; state->viewoffset[1] = MSG_ReadChar(&net_message) * 0.25f; state->viewoffset[2] = MSG_ReadChar(&net_message) * 0.25f; } if (flags & PS_VIEWANGLES) { state->viewangles[0] = MSG_ReadAngle16(&net_message); state->viewangles[1] = MSG_ReadAngle16(&net_message); state->viewangles[2] = MSG_ReadAngle16(&net_message); } if (flags & PS_KICKANGLES) { state->kick_angles[0] = MSG_ReadChar(&net_message) * 0.25f; state->kick_angles[1] = MSG_ReadChar(&net_message) * 0.25f; state->kick_angles[2] = MSG_ReadChar(&net_message) * 0.25f; } if (flags & PS_WEAPONINDEX) { state->gunindex = MSG_ReadByte(&net_message); } if (flags & PS_WEAPONFRAME) { state->gunframe = MSG_ReadByte(&net_message); state->gunoffset[0] = MSG_ReadChar(&net_message) * 0.25f; state->gunoffset[1] = MSG_ReadChar(&net_message) * 0.25f; state->gunoffset[2] = MSG_ReadChar(&net_message) * 0.25f; state->gunangles[0] = MSG_ReadChar(&net_message) * 0.25f; state->gunangles[1] = MSG_ReadChar(&net_message) * 0.25f; state->gunangles[2] = MSG_ReadChar(&net_message) * 0.25f; } if (flags & PS_BLEND) { state->blend[0] = MSG_ReadByte(&net_message) / 255.0f; state->blend[1] = MSG_ReadByte(&net_message) / 255.0f; state->blend[2] = MSG_ReadByte(&net_message) / 255.0f; state->blend[3] = MSG_ReadByte(&net_message) / 255.0f; } if (flags & PS_FOV) { state->fov = (float)MSG_ReadByte(&net_message); } if (flags & PS_RDFLAGS) { state->rdflags = MSG_ReadByte(&net_message); } /* parse stats */ statbits = MSG_ReadLong(&net_message); for (i = 0; i < MAX_STATS; i++) { if (statbits & (1 << i)) { state->stats[i] = MSG_ReadShort(&net_message); } } } void CL_FireEntityEvents(frame_t *frame) { entity_state_t *s1; int pnum, num; for (pnum = 0; pnum < frame->num_entities; pnum++) { num = (frame->parse_entities + pnum) & (MAX_PARSE_ENTITIES - 1); s1 = &cl_parse_entities[num]; if (s1->event) { CL_EntityEvent(s1); } if (s1->effects & EF_TELEPORTER) { CL_TeleporterParticles(s1); } } } void CL_ParseFrame(void) { int cmd; int len; frame_t *old; memset(&cl.frame, 0, sizeof(cl.frame)); cl.frame.serverframe = MSG_ReadLong(&net_message); cl.frame.deltaframe = MSG_ReadLong(&net_message); cl.frame.servertime = cl.frame.serverframe * 100; /* BIG HACK to let old demos continue to work */ if (cls.serverProtocol != 26) { cl.surpressCount = MSG_ReadByte(&net_message); } if (cl_shownet->value == 3) { Com_Printf(" frame:%i delta:%i\n", cl.frame.serverframe, cl.frame.deltaframe); } /* If the frame is delta compressed from data that we no longer have available, we must suck up the rest of the frame, but not use it, then ask for a non-compressed message */ if (cl.frame.deltaframe <= 0) { cl.frame.valid = true; /* uncompressed frame */ old = NULL; cls.demowaiting = false; /* we can start recording now */ } else { old = &cl.frames[cl.frame.deltaframe & UPDATE_MASK]; if (!old->valid) { /* should never happen */ Com_Printf("Delta from invalid frame (not supposed to happen!).\n"); } if (old->serverframe != cl.frame.deltaframe) { /* The frame that the server did the delta from is too old, so we can't reconstruct it properly. */ Com_Printf("Delta frame too old.\n"); } else if (cl.parse_entities - old->parse_entities > MAX_PARSE_ENTITIES - 128) { Com_Printf("Delta parse_entities too old.\n"); } else { cl.frame.valid = true; /* valid delta parse */ } } /* clamp time */ if (cl.time > cl.frame.servertime) { cl.time = cl.frame.servertime; } else if (cl.time < cl.frame.servertime - 100) { cl.time = cl.frame.servertime - 100; } /* read areabits */ len = MSG_ReadByte(&net_message); MSG_ReadData(&net_message, &cl.frame.areabits, len); /* read playerinfo */ cmd = MSG_ReadByte(&net_message); SHOWNET(svc_strings[cmd]); if (cmd != svc_playerinfo) { Com_Error(ERR_DROP, "CL_ParseFrame: 0x%X not playerinfo", cmd); } CL_ParsePlayerstate(old, &cl.frame); /* read packet entities */ cmd = MSG_ReadByte(&net_message); SHOWNET(svc_strings[cmd]); if (cmd != svc_packetentities) { Com_Error(ERR_DROP, "CL_ParseFrame: 0x%X not packetentities", cmd); } CL_ParsePacketEntities(old, &cl.frame); /* save the frame off in the backup array for later delta comparisons */ cl.frames[cl.frame.serverframe & UPDATE_MASK] = cl.frame; if (cl.frame.valid) { /* getting a valid frame message ends the connection process */ if (cls.state != ca_active) { cls.state = ca_active; cl.force_refdef = true; cl.predicted_origin[0] = cl.frame.playerstate.pmove.origin[0] * 0.125f; cl.predicted_origin[1] = cl.frame.playerstate.pmove.origin[1] * 0.125f; cl.predicted_origin[2] = cl.frame.playerstate.pmove.origin[2] * 0.125f; VectorCopy(cl.frame.playerstate.viewangles, cl.predicted_angles); if ((cls.disable_servercount != cl.servercount) && cl.refresh_prepped) { SCR_EndLoadingPlaque(); /* get rid of loading plaque */ } cl.sound_prepped = true; } /* fire entity events */ CL_FireEntityEvents(&cl.frame); if (!(!cl_predict->value || (cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION))) { CL_CheckPredictionError(); } } } void CL_ParseServerData(void) { extern cvar_t *fs_gamedirvar; char *str; int i; Com_DPrintf("Serverdata packet received.\n"); /* wipe the client_state_t struct */ CL_ClearState(); cls.state = ca_connected; /* parse protocol version number */ i = MSG_ReadLong(&net_message); cls.serverProtocol = i; /* another demo hack */ if (Com_ServerState() && (PROTOCOL_VERSION == 34)) { } else if (i != PROTOCOL_VERSION) { Com_Error(ERR_DROP, "Server returned version %i, not %i", i, PROTOCOL_VERSION); } cl.servercount = MSG_ReadLong(&net_message); cl.attractloop = MSG_ReadByte(&net_message); /* game directory */ str = MSG_ReadString(&net_message); Q_strlcpy(cl.gamedir, str, sizeof(cl.gamedir)); /* set gamedir */ if ((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string))) { Cvar_Set("game", str); } /* parse player entity number */ cl.playernum = MSG_ReadShort(&net_message); /* get the full level name */ str = MSG_ReadString(&net_message); if (cl.playernum == -1) { /* playing a cinematic or showing a pic, not a level */ SCR_PlayCinematic(str); } else { /* seperate the printfs so the server * message can have a color */ Com_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"); Com_Printf("%c%s\n", 2, str); /* need to prep refresh at next oportunity */ cl.refresh_prepped = false; } } void CL_ParseBaseline(void) { entity_state_t *es; unsigned bits; int newnum; entity_state_t nullstate; memset(&nullstate, 0, sizeof(nullstate)); newnum = CL_ParseEntityBits(&bits); es = &cl_entities[newnum].baseline; CL_ParseDelta(&nullstate, es, newnum, bits); } void CL_LoadClientinfo(clientinfo_t *ci, char *s) { int i; char *t; char model_name[MAX_QPATH]; char skin_name[MAX_QPATH]; char model_filename[MAX_QPATH]; char skin_filename[MAX_QPATH]; char weapon_filename[MAX_QPATH]; Q_strlcpy(ci->cinfo, s, sizeof(ci->cinfo)); s = ci->cinfo; /* isolate the player's name */ Q_strlcpy(ci->name, s, sizeof(ci->name)); t = strstr(s, "\\"); if (t) { ci->name[t - s] = 0; s = t + 1; } if (cl_noskins->value || (*s == 0)) { strcpy(model_filename, "players/male/tris.md2"); strcpy(weapon_filename, "players/male/weapon.md2"); strcpy(skin_filename, "players/male/grunt.pcx"); strcpy(ci->iconname, "/players/male/grunt_i.pcx"); ci->model = R_RegisterModel(model_filename); memset(ci->weaponmodel, 0, sizeof(ci->weaponmodel)); ci->weaponmodel[0] = R_RegisterModel(weapon_filename); ci->skin = R_RegisterSkin(skin_filename); ci->icon = Draw_FindPic(ci->iconname); } else { /* isolate the model name */ strcpy(model_name, s); t = strstr(model_name, "/"); if (!t) { t = strstr(model_name, "\\"); } if (!t) { t = model_name; } *t = 0; /* isolate the skin name */ strcpy(skin_name, s + strlen(model_name) + 1); /* model file */ Com_sprintf(model_filename, sizeof(model_filename), "players/%s/tris.md2", model_name); ci->model = R_RegisterModel(model_filename); if (!ci->model) { strcpy(model_name, "male"); Com_sprintf(model_filename, sizeof(model_filename), "players/male/tris.md2"); ci->model = R_RegisterModel(model_filename); } /* skin file */ Com_sprintf(skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name); ci->skin = R_RegisterSkin(skin_filename); /* if we don't have the skin and the model wasn't male, * see if the male has it (this is for CTF's skins) */ if (!ci->skin && Q_stricmp(model_name, "male")) { /* change model to male */ strcpy(model_name, "male"); Com_sprintf(model_filename, sizeof(model_filename), "players/male/tris.md2"); ci->model = R_RegisterModel(model_filename); /* see if the skin exists for the male model */ Com_sprintf(skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name); ci->skin = R_RegisterSkin(skin_filename); } /* if we still don't have a skin, it means that the male model didn't have * it, so default to grunt */ if (!ci->skin) { /* see if the skin exists for the male model */ Com_sprintf(skin_filename, sizeof(skin_filename), "players/%s/grunt.pcx", model_name); ci->skin = R_RegisterSkin(skin_filename); } /* weapon file */ for (i = 0; i < num_cl_weaponmodels; i++) { Com_sprintf(weapon_filename, sizeof(weapon_filename), "players/%s/%s", model_name, cl_weaponmodels[i]); ci->weaponmodel[i] = R_RegisterModel(weapon_filename); if (!ci->weaponmodel[i] && (strcmp(model_name, "cyborg") == 0)) { /* try male */ Com_sprintf(weapon_filename, sizeof(weapon_filename), "players/male/%s", cl_weaponmodels[i]); ci->weaponmodel[i] = R_RegisterModel(weapon_filename); } if (!cl_vwep->value) { break; /* only one when vwep is off */ } } /* icon file */ Com_sprintf(ci->iconname, sizeof(ci->iconname), "/players/%s/%s_i.pcx", model_name, skin_name); ci->icon = Draw_FindPic(ci->iconname); } /* must have loaded all data types to be valid */ if (!ci->skin || !ci->icon || !ci->model || !ci->weaponmodel[0]) { ci->skin = NULL; ci->icon = NULL; ci->model = NULL; ci->weaponmodel[0] = NULL; return; } } /* * Load the skin, icon, and model for a client */ void CL_ParseClientinfo(int player) { char *s; clientinfo_t *ci; s = cl.configstrings[player + CS_PLAYERSKINS]; ci = &cl.clientinfo[player]; CL_LoadClientinfo(ci, s); } void CL_ParseConfigString(void) { int i, length; char *s; char olds[MAX_QPATH]; i = MSG_ReadShort(&net_message); if ((i < 0) || (i >= MAX_CONFIGSTRINGS)) { Com_Error(ERR_DROP, "configstring > MAX_CONFIGSTRINGS"); } s = MSG_ReadString(&net_message); Q_strlcpy(olds, cl.configstrings[i], sizeof(olds)); length = strlen(s); if (length > sizeof(cl.configstrings) - sizeof(cl.configstrings[0])*i - 1) { Com_Error(ERR_DROP, "CL_ParseConfigString: oversize configstring"); } strcpy(cl.configstrings[i], s); /* do something apropriate */ if ((i >= CS_LIGHTS) && (i < CS_LIGHTS + MAX_LIGHTSTYLES)) { CL_SetLightstyle(i - CS_LIGHTS); } else if (i == CS_CDTRACK) { if (cl.refresh_prepped) { #ifdef CDA CDAudio_Play((int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10), true); #endif #ifdef OGG /* OGG/Vorbis */ if ((int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10) < 10) { char tmp[3] = "0"; OGG_ParseCmd(strcat(tmp, cl.configstrings[CS_CDTRACK])); } else { OGG_ParseCmd(cl.configstrings[CS_CDTRACK]); } #endif } } else if ((i >= CS_MODELS) && (i < CS_MODELS + MAX_MODELS)) { if (cl.refresh_prepped) { cl.model_draw[i - CS_MODELS] = R_RegisterModel(cl.configstrings[i]); if (cl.configstrings[i][0] == '*') { cl.model_clip[i - CS_MODELS] = CM_InlineModel(cl.configstrings[i]); } else { cl.model_clip[i - CS_MODELS] = NULL; } } } else if ((i >= CS_SOUNDS) && (i < CS_SOUNDS + MAX_MODELS)) { if (cl.refresh_prepped) { cl.sound_precache[i - CS_SOUNDS] = S_RegisterSound(cl.configstrings[i]); } } else if ((i >= CS_IMAGES) && (i < CS_IMAGES + MAX_MODELS)) { if (cl.refresh_prepped) { cl.image_precache[i - CS_IMAGES] = Draw_FindPic(cl.configstrings[i]); } } else if ((i >= CS_PLAYERSKINS) && (i < CS_PLAYERSKINS + MAX_CLIENTS)) { if (cl.refresh_prepped && strcmp(olds, s)) { CL_ParseClientinfo(i - CS_PLAYERSKINS); } } } void CL_ParseStartSoundPacket(void) { vec3_t pos_v; float *pos; int channel, ent; int sound_num; float volume; float attenuation; int flags; float ofs; flags = MSG_ReadByte(&net_message); sound_num = MSG_ReadByte(&net_message); if (flags & SND_VOLUME) { volume = MSG_ReadByte(&net_message) / 255.0f; } else { volume = DEFAULT_SOUND_PACKET_VOLUME; } if (flags & SND_ATTENUATION) { attenuation = MSG_ReadByte(&net_message) / 64.0f; } else { attenuation = DEFAULT_SOUND_PACKET_ATTENUATION; } if (flags & SND_OFFSET) { ofs = MSG_ReadByte(&net_message) / 1000.0f; } else { ofs = 0; } if (flags & SND_ENT) { /* entity reletive */ channel = MSG_ReadShort(&net_message); ent = channel >> 3; if (ent > MAX_EDICTS) { Com_Error(ERR_DROP, "CL_ParseStartSoundPacket: ent = %i", ent); } channel &= 7; } else { ent = 0; channel = 0; } if (flags & SND_POS) { /* positioned in space */ MSG_ReadPos(&net_message, pos_v); pos = pos_v; } else { /* use entity number */ pos = NULL; } if (!cl.sound_precache[sound_num]) { return; } S_StartSound(pos, ent, channel, cl.sound_precache[sound_num], volume, attenuation, ofs); } void SHOWNET(char *s) { if (cl_shownet->value >= 2) { Com_Printf("%3i:%s\n", net_message.readcount - 1, s); } } void CL_ParseServerMessage(void) { int cmd; char *s; int i; /* if recording demos, copy the message out */ if (cl_shownet->value == 1) { Com_Printf("%i ", net_message.cursize); } else if (cl_shownet->value >= 2) { Com_Printf("------------------\n"); } /* parse the message */ while (1) { if (net_message.readcount > net_message.cursize) { Com_Error(ERR_DROP, "CL_ParseServerMessage: Bad server message"); break; } cmd = MSG_ReadByte(&net_message); if (cmd == -1) { SHOWNET("END OF MESSAGE"); break; } if (cl_shownet->value >= 2) { if (!svc_strings[cmd]) { Com_Printf("%3i:BAD CMD %i\n", net_message.readcount - 1, cmd); } else { SHOWNET(svc_strings[cmd]); } } /* other commands */ switch (cmd) { default: Com_Error(ERR_DROP, "CL_ParseServerMessage: Illegible server message\n"); break; case svc_nop: break; case svc_disconnect: Com_Error(ERR_DISCONNECT, "Server disconnected\n"); break; case svc_reconnect: Com_Printf("Server disconnected, reconnecting\n"); if (cls.download) { /* close download */ fclose(cls.download); cls.download = NULL; } cls.state = ca_connecting; cls.connect_time = -99999; /* CL_CheckForResend() will fire immediately */ break; case svc_print: i = MSG_ReadByte(&net_message); if (i == PRINT_CHAT) { S_StartLocalSound("misc/talk.wav"); con.ormask = 128; } Com_Printf("%s", MSG_ReadString(&net_message)); con.ormask = 0; break; case svc_centerprint: SCR_CenterPrint(MSG_ReadString(&net_message)); break; case svc_stufftext: s = MSG_ReadString(&net_message); Com_DPrintf("stufftext: %s\n", s); Cbuf_AddText(s); break; case svc_serverdata: Cbuf_Execute(); /* make sure any stuffed commands are done */ CL_ParseServerData(); break; case svc_configstring: CL_ParseConfigString(); break; case svc_sound: CL_ParseStartSoundPacket(); break; case svc_spawnbaseline: CL_ParseBaseline(); break; case svc_temp_entity: CL_ParseTEnt(); break; case svc_muzzleflash: CL_AddMuzzleFlash(); break; case svc_muzzleflash2: CL_AddMuzzleFlash2(); break; case svc_download: CL_ParseDownload(); break; case svc_frame: CL_ParseFrame(); break; case svc_inventory: CL_ParseInventory(); break; case svc_layout: s = MSG_ReadString(&net_message); Q_strlcpy(cl.layout, s, sizeof(cl.layout)); break; case svc_playerinfo: case svc_packetentities: case svc_deltapacketentities: Com_Error(ERR_DROP, "Out of place frame data"); break; } } CL_AddNetgraph(); /* we don't know if it is ok to save a demo message until after we have parsed the frame */ if (cls.demorecording && !cls.demowaiting) { CL_WriteDemoMessage(); } } yquake2-QUAKE2_5_32/src/client/header/0000755000175000017500000000000012615162034020114 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/client/header/keyboard.h0000644000175000017500000001105412615162034022066 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 header file for the upper level key event processing * * ======================================================================= */ #ifndef CL_HEADER_KEYBOARD_H #define CL_HEADER_KEYBOARD_H /* max length of a console command line */ #define MAXCMDLINE 256 /* number of console command lines saved in history, * must be a power of two, because we use & (NUM_KEY_LINES-1) * instead of % so -1 wraps to NUM_KEY_LINES-1 */ #define NUM_KEY_LINES 32 /* these are the key numbers that should be passed to Key_Event they must be mached by the low level key event processing! */ enum QKEYS { K_TAB = 9, K_ENTER = 13, K_ESCAPE = 27, K_SPACE = 32, K_BACKSPACE = 127, K_COMMAND = 128, K_CAPSLOCK, K_POWER, K_PAUSE, K_UPARROW, K_DOWNARROW, K_LEFTARROW, K_RIGHTARROW, K_ALT, K_CTRL, K_SHIFT, K_INS, K_DEL, K_PGDN, K_PGUP, K_HOME, K_END, K_F1, K_F2, K_F3, K_F4, K_F5, K_F6, K_F7, K_F8, K_F9, K_F10, K_F11, K_F12, K_F13, K_F14, K_F15, K_KP_HOME, K_KP_UPARROW, K_KP_PGUP, K_KP_LEFTARROW, K_KP_5, K_KP_RIGHTARROW, K_KP_END, K_KP_DOWNARROW, K_KP_PGDN, K_KP_ENTER, K_KP_INS, K_KP_DEL, K_KP_SLASH, K_KP_MINUS, K_KP_PLUS, K_KP_NUMLOCK, K_KP_STAR, K_KP_EQUALS, K_MOUSE1, K_MOUSE2, K_MOUSE3, K_MOUSE4, K_MOUSE5, K_MWHEELDOWN, K_MWHEELUP, K_JOY1, K_JOY2, K_JOY3, K_JOY4, K_JOY5, K_JOY6, K_JOY7, K_JOY8, K_JOY9, K_JOY10, K_JOY11, K_JOY12, K_JOY13, K_JOY14, K_JOY15, K_JOY16, K_JOY17, K_JOY18, K_JOY19, K_JOY20, K_JOY21, K_JOY22, K_JOY23, K_JOY24, K_JOY25, K_JOY26, K_JOY27, K_JOY28, K_JOY29, K_JOY30, K_JOY31, K_JOY32, K_AUX1, K_AUX2, K_AUX3, K_AUX4, K_AUX5, K_AUX6, K_AUX7, K_AUX8, K_AUX9, K_AUX10, K_AUX11, K_AUX12, K_AUX13, K_AUX14, K_AUX15, K_AUX16, K_AUX17, K_AUX18, K_AUX19, K_AUX20, K_AUX21, K_AUX22, K_AUX23, K_AUX24, K_AUX25, K_AUX26, K_AUX27, K_AUX28, K_AUX29, K_AUX30, K_AUX31, K_AUX32, K_WORLD_0, K_WORLD_1, K_WORLD_2, K_WORLD_3, K_WORLD_4, K_WORLD_5, K_WORLD_6, K_WORLD_7, K_WORLD_8, K_WORLD_9, K_WORLD_10, K_WORLD_11, K_WORLD_12, K_WORLD_13, K_WORLD_14, K_WORLD_15, K_WORLD_16, K_WORLD_17, K_WORLD_18, K_WORLD_19, K_WORLD_20, K_WORLD_21, K_WORLD_22, K_WORLD_23, K_WORLD_24, K_WORLD_25, K_WORLD_26, K_WORLD_27, K_WORLD_28, K_WORLD_29, K_WORLD_30, K_WORLD_31, K_WORLD_32, K_WORLD_33, K_WORLD_34, K_WORLD_35, K_WORLD_36, K_WORLD_37, K_WORLD_38, K_WORLD_39, K_WORLD_40, K_WORLD_41, K_WORLD_42, K_WORLD_43, K_WORLD_44, K_WORLD_45, K_WORLD_46, K_WORLD_47, K_WORLD_48, K_WORLD_49, K_WORLD_50, K_WORLD_51, K_WORLD_52, K_WORLD_53, K_WORLD_54, K_WORLD_55, K_WORLD_56, K_WORLD_57, K_WORLD_58, K_WORLD_59, K_WORLD_60, K_WORLD_61, K_WORLD_62, K_WORLD_63, K_WORLD_64, K_WORLD_65, K_WORLD_66, K_WORLD_67, K_WORLD_68, K_WORLD_69, K_WORLD_70, K_WORLD_71, K_WORLD_72, K_WORLD_73, K_WORLD_74, K_WORLD_75, K_WORLD_76, K_WORLD_77, K_WORLD_78, K_WORLD_79, K_WORLD_80, K_WORLD_81, K_WORLD_82, K_WORLD_83, K_WORLD_84, K_WORLD_85, K_WORLD_86, K_WORLD_87, K_WORLD_88, K_WORLD_89, K_WORLD_90, K_WORLD_91, K_WORLD_92, K_WORLD_93, K_WORLD_94, K_WORLD_95, K_SUPER, K_COMPOSE, K_MODE, K_HELP, K_PRINT, K_SYSREQ, K_SCROLLOCK, K_BREAK, K_MENU, K_EURO, K_UNDO, K_LAST }; extern char *keybindings[K_LAST]; extern int key_repeats[K_LAST]; extern int anykeydown; extern char chat_buffer[]; extern int chat_bufferlen; extern int chat_cursorpos; extern qboolean chat_team; void Char_Event(int key); void Key_Event(int key, qboolean down, qboolean special); void Key_Init(void); void Key_WriteBindings(FILE *f); void Key_ReadConsoleHistory(); void Key_WriteConsoleHistory(); void Key_SetBinding(int keynum, char *binding); void Key_MarkAllUp(void); int Key_GetKey(void); #endif yquake2-QUAKE2_5_32/src/client/header/vid.h0000644000175000017500000000310412615162034021045 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * ABI to the video oute driver * * ======================================================================= */ #ifndef CL_VID_H #define CL_VID_H typedef struct vrect_s { int x,y,width,height; } vrect_t; typedef struct { int width, height; /* coordinates from main game */ } viddef_t; extern viddef_t viddef; /* global video state */ /* Video module initialisation, etc */ void VID_Init(void); void VID_Shutdown(void); void VID_CheckChanges(void); void VID_MenuInit(void); void VID_MenuDraw(void); const char *VID_MenuKey(int); void VID_Printf(int print_level, char *fmt, ...); void VID_Error(int err_level, char *fmt, ...); void VID_NewWindow(int width, int height); qboolean VID_GetModeInfo(int *width, int *height, int mode); #endif yquake2-QUAKE2_5_32/src/client/header/screen.h0000644000175000017500000000374612615162034021556 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Header for the 2D client stuff and the .cinf file format * * ======================================================================= */ #ifndef CL_SCREEN_H #define CL_SCREEN_H void SCR_Init(void); void SCR_UpdateScreen(void); void SCR_SizeUp(void); void SCR_SizeDown(void); void SCR_CenterPrint(char *str); void SCR_BeginLoadingPlaque(void); void SCR_EndLoadingPlaque(void); void SCR_DebugGraph(float value, int color); void SCR_TouchPics(void); void SCR_RunConsole(void); extern float scr_con_current; extern float scr_conlines; /* lines of console to display */ extern int sb_lines; extern cvar_t *scr_viewsize; extern cvar_t *crosshair; extern vrect_t scr_vrect; /* position of render window */ extern char crosshair_pic[MAX_QPATH]; extern int crosshair_width, crosshair_height; void SCR_AddDirtyPoint(int x, int y); void SCR_DirtyScreen(void); void SCR_PlayCinematic(char *name); qboolean SCR_DrawCinematic(void); void SCR_RunCinematic(void); void SCR_StopCinematic(void); void SCR_FinishCinematic(void); void SCR_DrawCrosshair(void); float SCR_GetHUDScale(void); float SCR_GetConsoleScale(void); float SCR_GetMenuScale(void); #endif yquake2-QUAKE2_5_32/src/client/header/client.h0000644000175000017500000003475712615162034021563 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Main header for the client * * ======================================================================= */ #ifndef CL_CLIENT_H #define CL_CLIENT_H #define MAX_CLIENTWEAPONMODELS 20 #define CMD_BACKUP 256 /* allow a lot of command backups for very fast systems */ /* the cl_parse_entities must be large enough to hold UPDATE_BACKUP frames of entities, so that when a delta compressed message arives from the server it can be un-deltad from the original */ #define MAX_PARSE_ENTITIES 1024 #define MAX_SUSTAINS 32 #define PARTICLE_GRAVITY 40 #define BLASTER_PARTICLE_COLOR 0xe0 #define INSTANT_PARTICLE -10000.0 #include #include #include #include #include #include #include "ref.h" #include "vid.h" #include "screen.h" #include "keyboard.h" #include "console.h" #include "../sound/header/cdaudio.h" #include "../sound/header/sound.h" #include "../sound/header/vorbis.h" typedef struct { qboolean valid; /* cleared if delta parsing was invalid */ int serverframe; int servertime; /* server time the message is valid for (in msec) */ int deltaframe; byte areabits[MAX_MAP_AREAS/8]; /* portalarea visibility bits */ player_state_t playerstate; int num_entities; int parse_entities; /* non-masked index into cl_parse_entities array */ } frame_t; typedef struct { entity_state_t baseline; /* delta from this if not from a previous frame */ entity_state_t current; entity_state_t prev; /* will always be valid, but might just be a copy of current */ int serverframe; /* if not current, this ent isn't in the frame */ int trailcount; /* for diminishing grenade trails */ vec3_t lerp_origin; /* for trails (variable hz) */ int fly_stoptime; } centity_t; typedef struct { char name[MAX_QPATH]; char cinfo[MAX_QPATH]; struct image_s *skin; struct image_s *icon; char iconname[MAX_QPATH]; struct model_s *model; struct model_s *weaponmodel[MAX_CLIENTWEAPONMODELS]; } clientinfo_t; extern char cl_weaponmodels[MAX_CLIENTWEAPONMODELS][MAX_QPATH]; extern int num_cl_weaponmodels; /* the client_state_t structure is wiped completely at every server map change */ typedef struct { int timeoutcount; int timedemo_frames; int timedemo_start; qboolean refresh_prepped; /* false if on new level or new ref dll */ qboolean sound_prepped; /* ambient sounds can start */ qboolean force_refdef; /* vid has changed, so we can't use a paused refdef */ int parse_entities; /* index (not anded off) into cl_parse_entities[] */ usercmd_t cmd; usercmd_t cmds[CMD_BACKUP]; /* each mesage will send several old cmds */ int cmd_time[CMD_BACKUP]; /* time sent, for calculating pings */ short predicted_origins[CMD_BACKUP][3]; /* for debug comparing against server */ float predicted_step; /* for stair up smoothing */ unsigned predicted_step_time; vec3_t predicted_origin; /* generated by CL_PredictMovement */ vec3_t predicted_angles; vec3_t prediction_error; frame_t frame; /* received from server */ int surpressCount; /* number of messages rate supressed */ frame_t frames[UPDATE_BACKUP]; /* the client maintains its own idea of view angles, which are sent to the server each frame. It is cleared to 0 upon entering each level. the server sends a delta each frame which is added to the locally tracked view angles to account for standing on rotating objects, and teleport direction changes */ vec3_t viewangles; int time; /* this is the time value that the client is rendering at. always <= cls.realtime */ float lerpfrac; /* between oldframe and frame */ refdef_t refdef; vec3_t v_forward, v_right, v_up; /* set when refdef.angles is set */ /* transient data from server */ char layout[1024]; /* general 2D overlay */ int inventory[MAX_ITEMS]; /* non-gameserver infornamtion */ fileHandle_t cinematic_file; int cinematictime; /* cls.realtime for first cinematic frame */ int cinematicframe; unsigned char cinematicpalette[768]; qboolean cinematicpalette_active; /* server state information */ qboolean attractloop; /* running the attract loop, any key will menu */ int servercount; /* server identification for prespawns */ char gamedir[MAX_QPATH]; int playernum; char configstrings[MAX_CONFIGSTRINGS][MAX_QPATH]; /* locally derived information from server state */ struct model_s *model_draw[MAX_MODELS]; struct cmodel_s *model_clip[MAX_MODELS]; struct sfx_s *sound_precache[MAX_SOUNDS]; struct image_s *image_precache[MAX_IMAGES]; clientinfo_t clientinfo[MAX_CLIENTS]; clientinfo_t baseclientinfo; } client_state_t; extern client_state_t cl; /* the client_static_t structure is persistant through an arbitrary number of server connections */ typedef enum { ca_uninitialized, ca_disconnected, /* not talking to a server */ ca_connecting, /* sending request packets to the server */ ca_connected, /* netchan_t established, waiting for svc_serverdata */ ca_active /* game views should be displayed */ } connstate_t; typedef enum { dl_none, dl_model, dl_sound, dl_skin, dl_single } dltype_t; typedef enum {key_game, key_console, key_message, key_menu} keydest_t; typedef struct { connstate_t state; keydest_t key_dest; int framecount; int realtime; /* always increasing, no clamping, etc */ float frametime; /* seconds since last frame */ /* screen rendering information */ float disable_screen; /* showing loading plaque between levels */ /* or changing rendering dlls */ /* if time gets > 30 seconds ahead, break it */ int disable_servercount; /* when we receive a frame and cl.servercount */ /* > cls.disable_servercount, clear disable_screen */ /* connection information */ char servername[256]; /* name of server from original connect */ float connect_time; /* for connection retransmits */ int quakePort; /* a 16 bit value that allows quake servers */ /* to work around address translating routers */ netchan_t netchan; int serverProtocol; /* in case we are doing some kind of version hack */ int challenge; /* from the server to use for connecting */ FILE *download; /* file transfer from server */ char downloadtempname[MAX_OSPATH]; char downloadname[MAX_OSPATH]; int downloadnumber; dltype_t downloadtype; int downloadpercent; /* demo recording info must be here, so it isn't cleared on level change */ qboolean demorecording; qboolean demowaiting; /* don't record until a non-delta message is received */ FILE *demofile; } client_static_t; extern client_static_t cls; /* cvars */ extern cvar_t *cl_stereo_separation; extern cvar_t *cl_stereo; extern cvar_t *cl_gun; extern cvar_t *cl_add_blend; extern cvar_t *cl_add_lights; extern cvar_t *cl_add_particles; extern cvar_t *cl_add_entities; extern cvar_t *cl_predict; extern cvar_t *cl_footsteps; extern cvar_t *cl_noskins; extern cvar_t *cl_autoskins; extern cvar_t *cl_upspeed; extern cvar_t *cl_forwardspeed; extern cvar_t *cl_sidespeed; extern cvar_t *cl_yawspeed; extern cvar_t *cl_pitchspeed; extern cvar_t *cl_run; extern cvar_t *cl_anglespeedkey; extern cvar_t *cl_shownet; extern cvar_t *cl_showmiss; extern cvar_t *cl_showclamp; extern cvar_t *lookspring; extern cvar_t *lookstrafe; extern cvar_t *sensitivity; extern cvar_t *m_pitch; extern cvar_t *m_yaw; extern cvar_t *m_forward; extern cvar_t *m_side; extern cvar_t *freelook; extern cvar_t *cl_lightlevel; extern cvar_t *cl_paused; extern cvar_t *cl_timedemo; extern cvar_t *cl_vwep; extern cvar_t *horplus; extern cvar_t *cin_force43; typedef struct { int key; /* so entities can reuse same entry */ vec3_t color; 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 */ } cdlight_t; extern centity_t cl_entities[MAX_EDICTS]; extern cdlight_t cl_dlights[MAX_DLIGHTS]; extern entity_state_t cl_parse_entities[MAX_PARSE_ENTITIES]; extern netadr_t net_from; extern sizebuf_t net_message; void DrawString (int x, int y, char *s); void DrawStringScaled(int x, int y, char *s, float factor); void DrawAltString (int x, int y, char *s); /* toggle high bit */ void DrawAltStringScaled(int x, int y, char *s, float factor); qboolean CL_CheckOrDownloadFile (char *filename); void CL_AddNetgraph (void); typedef struct cl_sustain { int id; int type; int endtime; int nextthink; int thinkinterval; vec3_t org; vec3_t dir; int color; int count; int magnitude; void (*think)(struct cl_sustain *self); } cl_sustain_t; void CL_ParticleSteamEffect2(cl_sustain_t *self); void CL_TeleporterParticles (entity_state_t *ent); void CL_ParticleEffect (vec3_t org, vec3_t dir, int color, int count); void CL_ParticleEffect2 (vec3_t org, vec3_t dir, int color, int count); void CL_ParticleEffect3 (vec3_t org, vec3_t dir, int color, int count); typedef struct particle_s { struct particle_s *next; float time; vec3_t org; vec3_t vel; vec3_t accel; float color; float colorvel; float alpha; float alphavel; } cparticle_t; void CL_ClearEffects (void); void CL_ClearTEnts (void); void CL_BlasterTrail (vec3_t start, vec3_t end); void CL_QuadTrail (vec3_t start, vec3_t end); void CL_RailTrail (vec3_t start, vec3_t end); void CL_BubbleTrail (vec3_t start, vec3_t end); void CL_FlagTrail (vec3_t start, vec3_t end, int color); void CL_IonripperTrail (vec3_t start, vec3_t end); void CL_BlasterParticles2 (vec3_t org, vec3_t dir, unsigned int color); void CL_BlasterTrail2 (vec3_t start, vec3_t end); void CL_DebugTrail (vec3_t start, vec3_t end); void CL_SmokeTrail (vec3_t start, vec3_t end, int colorStart, int colorRun, int spacing); void CL_Flashlight (int ent, vec3_t pos); void CL_ForceWall (vec3_t start, vec3_t end, int color); void CL_FlameEffects (centity_t *ent, vec3_t origin); void CL_GenericParticleEffect (vec3_t org, vec3_t dir, int color, int count, int numcolors, int dirspread, float alphavel); void CL_BubbleTrail2 (vec3_t start, vec3_t end, int dist); void CL_Heatbeam (vec3_t start, vec3_t end); void CL_ParticleSteamEffect (vec3_t org, vec3_t dir, int color, int count, int magnitude); void CL_TrackerTrail (vec3_t start, vec3_t end, int particleColor); void CL_Tracker_Explode(vec3_t origin); void CL_TagTrail (vec3_t start, vec3_t end, int color); void CL_ColorFlash (vec3_t pos, int ent, float intensity, float r, float g, float b); void CL_Tracker_Shell(vec3_t origin); void CL_MonsterPlasma_Shell(vec3_t origin); void CL_ColorExplosionParticles (vec3_t org, int color, int run); void CL_ParticleSmokeEffect (vec3_t org, vec3_t dir, int color, int count, int magnitude); void CL_Widowbeamout (cl_sustain_t *self); void CL_Nukeblast (cl_sustain_t *self); void CL_WidowSplash (vec3_t org); int CL_ParseEntityBits (unsigned *bits); void CL_ParseDelta (entity_state_t *from, entity_state_t *to, int number, int bits); void CL_ParseFrame (void); void CL_ParseTEnt (void); void CL_ParseConfigString (void); void CL_AddMuzzleFlash (void); void CL_AddMuzzleFlash2 (void); void SmokeAndFlash(vec3_t origin); void CL_SetLightstyle (int i); void CL_RunParticles (void); void CL_RunDLights (void); void CL_RunLightStyles (void); void CL_AddEntities (void); void CL_AddDLights (void); void CL_AddTEnts (void); void CL_AddLightStyles (void); void CL_PrepRefresh (void); void CL_RegisterSounds (void); void CL_Quit_f (void); void IN_Accumulate (void); void CL_ParseLayout (void); void CL_Init (void); void CL_FixUpGender(void); void CL_Disconnect (void); void CL_Disconnect_f (void); void CL_GetChallengePacket (void); void CL_PingServers_f (void); void CL_Snd_Restart_f (void); void CL_RequestNextDownload (void); typedef struct { int down[2]; /* key nums holding it down */ unsigned downtime; /* msec timestamp */ unsigned msec; /* msec down this frame */ int state; } kbutton_t; extern kbutton_t in_mlook, in_klook; extern kbutton_t in_strafe; extern kbutton_t in_speed; void CL_InitInput (void); void CL_SendCmd (void); void CL_SendMove (usercmd_t *cmd); void CL_ClearState (void); void CL_ReadPackets (void); int CL_ReadFromServer (void); void CL_WriteToServer (usercmd_t *cmd); void CL_BaseMove (usercmd_t *cmd); void IN_CenterView (void); float CL_KeyState (kbutton_t *key); char *Key_KeynumToString (int keynum); void CL_WriteDemoMessage (void); void CL_Stop_f (void); void CL_Record_f (void); extern char *svc_strings[256]; void CL_ParseServerMessage (void); void CL_LoadClientinfo (clientinfo_t *ci, char *s); void SHOWNET(char *s); void CL_ParseClientinfo (int player); void CL_Download_f (void); extern int gun_frame; extern struct model_s *gun_model; void V_Init (void); void V_RenderView( float stereo_separation ); void V_AddEntity (entity_t *ent); void V_AddParticle (vec3_t org, unsigned int color, float alpha); void V_AddLight (vec3_t org, float intensity, float r, float g, float b); void V_AddLightStyle (int style, float r, float g, float b); void CL_RegisterTEntSounds (void); void CL_RegisterTEntModels (void); void CL_SmokeAndFlash(vec3_t origin); void CL_InitPrediction (void); void CL_PredictMove (void); void CL_CheckPredictionError (void); cdlight_t *CL_AllocDlight (int key); void CL_BigTeleportParticles (vec3_t org); void CL_RocketTrail (vec3_t start, vec3_t end, centity_t *old); void CL_DiminishingTrail (vec3_t start, vec3_t end, centity_t *old, int flags); void CL_FlyEffect (centity_t *ent, vec3_t origin); void CL_BfgParticles (entity_t *ent); void CL_AddParticles (void); void CL_EntityEvent (entity_state_t *ent); void CL_TrapParticles (entity_t *ent); void M_Init (void); void M_Keydown (int key); void M_Draw (void); void M_Menu_Main_f (void); void M_ForceMenuOff (void); void M_AddToServerList (netadr_t adr, char *info); void CL_ParseInventory (void); void CL_KeyInventory (int key); void CL_DrawInventory (void); void CL_PredictMovement (void); #endif yquake2-QUAKE2_5_32/src/client/header/console.h0000644000175000017500000000364412615162034021736 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Header file for the console * * ======================================================================= */ #ifndef CL_HEADER_CONSOLE_H #define CL_HEADER_CONSOLE_H #define NUM_CON_TIMES 4 #define CON_TEXTSIZE 32768 typedef struct { qboolean initialized; char text[CON_TEXTSIZE]; int current; /* line where next message will be printed */ int x; /* offset in current line for next print */ int display; /* bottom of console displays this line */ int ormask; /* high bit mask for colored characters */ int linewidth; /* characters across screen */ int totallines; /* total lines in console scrollback */ float cursorspeed; int vislines; float times[NUM_CON_TIMES]; /* cls.realtime time the line was generated */ } console_t; extern console_t con; void Con_DrawCharacter (int cx, int line, int num); void Con_CheckResize (void); void Con_Init (void); void Con_DrawConsole (float frac); void Con_Print (char *txt); void Con_CenteredPrint (char *text); void Con_Clear_f (void); void Con_DrawNotify (void); void Con_ClearNotify (void); void Con_ToggleConsole_f (void); #endif yquake2-QUAKE2_5_32/src/client/header/ref.h0000644000175000017500000001003712615162034021042 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * ABI between client and refresher * * ======================================================================= */ #ifndef CL_REF_H #define CL_REF_H #include "../../common/header/common.h" #include "vid.h" #define MAX_DLIGHTS 32 #define MAX_ENTITIES 128 #define MAX_PARTICLES 4096 #define MAX_LIGHTSTYLES 256 #define POWERSUIT_SCALE 4.0F #define SHELL_RED_COLOR 0xF2 #define SHELL_GREEN_COLOR 0xD0 #define SHELL_BLUE_COLOR 0xF3 #define SHELL_RG_COLOR 0xDC #define SHELL_RB_COLOR 0x68 #define SHELL_BG_COLOR 0x78 #define SHELL_DOUBLE_COLOR 0xDF #define SHELL_HALF_DAM_COLOR 0x90 #define SHELL_CYAN_COLOR 0x72 #define SHELL_WHITE_COLOR 0xD7 #define ENTITY_FLAGS 68 #define API_VERSION 3 typedef struct entity_s { struct model_s *model; /* opaque type outside refresh */ float angles[3]; /* most recent data */ float origin[3]; /* also used as RF_BEAM's "from" */ int frame; /* also used as RF_BEAM's diameter */ /* previous data for lerping */ float oldorigin[3]; /* also used as RF_BEAM's "to" */ int oldframe; /* misc */ float backlerp; /* 0.0 = current, 1.0 = old */ int skinnum; /* also used as RF_BEAM's palette index */ int lightstyle; /* for flashing entities */ float alpha; /* ignore if RF_TRANSLUCENT isn't set */ struct image_s *skin; /* NULL for inline skin */ int flags; } entity_t; typedef struct { vec3_t origin; vec3_t color; float intensity; } dlight_t; typedef struct { vec3_t origin; int color; float alpha; } particle_t; typedef struct { float rgb[3]; /* 0.0 - 2.0 */ float white; /* highest of rgb */ } lightstyle_t; typedef struct { int x, y, width, height; /* in virtual screen coordinates */ float fov_x, fov_y; float vieworg[3]; float viewangles[3]; float blend[4]; /* rgba 0-1 full screen blend */ float time; /* time is uesed to auto animate */ int rdflags; /* RDF_UNDERWATER, etc */ byte *areabits; /* if not NULL, only areas with set bits will be drawn */ lightstyle_t *lightstyles; /* [MAX_LIGHTSTYLES] */ int num_entities; entity_t *entities; int num_dlights; dlight_t *dlights; int num_particles; particle_t *particles; } refdef_t; // Soon to be deleted //void R_GetRefAPI(void); /* * Refresh API */ void R_BeginRegistration(char *map); void R_Clear(void); struct model_s *R_RegisterModel(char *name); struct image_s *R_RegisterSkin(char *name); void R_SetSky(char *name, float rotate, vec3_t axis); void R_EndRegistration(void); struct image_s *Draw_FindPic(char *name); void R_RenderFrame(refdef_t *fd); void Draw_GetPicSize(int *w, int *h, char *name); void Draw_Pic(int x, int y, char *name); void Draw_StretchPic(int x, int y, int w, int h, char *name); void Draw_PicScaled(int x, int y, char *pic, float factor); void Draw_Char(int x, int y, int c); void Draw_CharScaled(int x, int y, int num, float scale); void Draw_TileClear(int x, int y, int w, int h, char *name); void Draw_Fill(int x, int y, int w, int h, int c); void Draw_FadeScreen(void); void Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, byte *data); int R_Init(void *hinstance, void *hWnd); void R_Shutdown(void); void R_SetPalette(const unsigned char *palette); void R_BeginFrame(float camera_separation); void GLimp_EndFrame(void); #endif yquake2-QUAKE2_5_32/src/client/cl_download.c0000644000175000017500000003310212615162034021314 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements the game media download from the server * * ======================================================================= */ #include "header/client.h" extern cvar_t *allow_download; extern cvar_t *allow_download_players; extern cvar_t *allow_download_models; extern cvar_t *allow_download_sounds; extern cvar_t *allow_download_maps; extern int precache_check; extern int precache_spawncount; extern int precache_tex; extern int precache_model_skin; extern byte *precache_model; static const char *env_suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; #define PLAYER_MULT 5 /* ENV_CNT is map load, ENV_CNT+1 is first env map */ #define ENV_CNT (CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT) #define TEXTURE_CNT (ENV_CNT + 13) void CL_RequestNextDownload(void) { unsigned int map_checksum; /* for detecting cheater maps */ char fn[MAX_OSPATH]; dmdl_t *pheader; if (cls.state != ca_connected) { return; } if (!allow_download->value && (precache_check < ENV_CNT)) { precache_check = ENV_CNT; } if (precache_check == CS_MODELS) { precache_check = CS_MODELS + 2; if (allow_download_maps->value) { if (!CL_CheckOrDownloadFile(cl.configstrings[CS_MODELS + 1])) { return; /* started a download */ } } } if ((precache_check >= CS_MODELS) && (precache_check < CS_MODELS + MAX_MODELS)) { if (allow_download_models->value) { while (precache_check < CS_MODELS + MAX_MODELS && cl.configstrings[precache_check][0]) { if ((cl.configstrings[precache_check][0] == '*') || (cl.configstrings[precache_check][0] == '#')) { precache_check++; continue; } if (precache_model_skin == 0) { if (!CL_CheckOrDownloadFile(cl.configstrings[precache_check])) { precache_model_skin = 1; return; /* started a download */ } precache_model_skin = 1; } /* checking for skins in the model */ if (!precache_model) { FS_LoadFile(cl.configstrings[precache_check], (void **)&precache_model); if (!precache_model) { precache_model_skin = 0; precache_check++; continue; /* couldn't load it */ } if (LittleLong(*(unsigned *)precache_model) != IDALIASHEADER) { /* not an alias model */ FS_FreeFile(precache_model); precache_model = 0; precache_model_skin = 0; precache_check++; continue; } pheader = (dmdl_t *)precache_model; if (LittleLong(pheader->version) != ALIAS_VERSION) { precache_check++; precache_model_skin = 0; continue; /* couldn't load it */ } } pheader = (dmdl_t *)precache_model; while (precache_model_skin - 1 < LittleLong(pheader->num_skins)) { if (!CL_CheckOrDownloadFile((char *)precache_model + LittleLong(pheader->ofs_skins) + (precache_model_skin - 1) * MAX_SKINNAME)) { precache_model_skin++; return; /* started a download */ } precache_model_skin++; } if (precache_model) { FS_FreeFile(precache_model); precache_model = 0; } precache_model_skin = 0; precache_check++; } } precache_check = CS_SOUNDS; } if ((precache_check >= CS_SOUNDS) && (precache_check < CS_SOUNDS + MAX_SOUNDS)) { if (allow_download_sounds->value) { if (precache_check == CS_SOUNDS) { precache_check++; } while (precache_check < CS_SOUNDS + MAX_SOUNDS && cl.configstrings[precache_check][0]) { if (cl.configstrings[precache_check][0] == '*') { precache_check++; continue; } Com_sprintf(fn, sizeof(fn), "sound/%s", cl.configstrings[precache_check++]); if (!CL_CheckOrDownloadFile(fn)) { return; /* started a download */ } } } precache_check = CS_IMAGES; } if ((precache_check >= CS_IMAGES) && (precache_check < CS_IMAGES + MAX_IMAGES)) { if (precache_check == CS_IMAGES) { precache_check++; } while (precache_check < CS_IMAGES + MAX_IMAGES && cl.configstrings[precache_check][0]) { Com_sprintf(fn, sizeof(fn), "pics/%s.pcx", cl.configstrings[precache_check++]); if (!CL_CheckOrDownloadFile(fn)) { return; /* started a download */ } } precache_check = CS_PLAYERSKINS; } /* skins are special, since a player has three things to download: model, weapon model and skin so precache_check is now *3 */ if ((precache_check >= CS_PLAYERSKINS) && (precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT)) { if (allow_download_players->value) { while (precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT) { int i, n; char model[MAX_QPATH], skin[MAX_QPATH], *p; i = (precache_check - CS_PLAYERSKINS) / PLAYER_MULT; n = (precache_check - CS_PLAYERSKINS) % PLAYER_MULT; if (!cl.configstrings[CS_PLAYERSKINS + i][0]) { precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT; continue; } if ((p = strchr(cl.configstrings[CS_PLAYERSKINS + i], '\\')) != NULL) { p++; } else { p = cl.configstrings[CS_PLAYERSKINS + i]; } strcpy(model, p); p = strchr(model, '/'); if (!p) { p = strchr(model, '\\'); } if (p) { *p++ = 0; strcpy(skin, p); } else { *skin = 0; } switch (n) { case 0: /* model */ Com_sprintf(fn, sizeof(fn), "players/%s/tris.md2", model); if (!CL_CheckOrDownloadFile(fn)) { precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 1; return; } n++; case 1: /* weapon model */ Com_sprintf(fn, sizeof(fn), "players/%s/weapon.md2", model); if (!CL_CheckOrDownloadFile(fn)) { precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 2; return; } n++; case 2: /* weapon skin */ Com_sprintf(fn, sizeof(fn), "players/%s/weapon.pcx", model); if (!CL_CheckOrDownloadFile(fn)) { precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 3; return; } n++; case 3: /* skin */ Com_sprintf(fn, sizeof(fn), "players/%s/%s.pcx", model, skin); if (!CL_CheckOrDownloadFile(fn)) { precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 4; return; } n++; case 4: /* skin_i */ Com_sprintf(fn, sizeof(fn), "players/%s/%s_i.pcx", model, skin); if (!CL_CheckOrDownloadFile(fn)) { precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 5; return; /* started a download */ } /* move on to next model */ precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT; } } } /* precache phase completed */ precache_check = ENV_CNT; } if (precache_check == ENV_CNT) { precache_check = ENV_CNT + 1; CM_LoadMap(cl.configstrings[CS_MODELS + 1], true, &map_checksum); if (map_checksum != (int)strtol(cl.configstrings[CS_MAPCHECKSUM], (char **)NULL, 10)) { Com_Error(ERR_DROP, "Local map version differs from server: %i != '%s'\n", map_checksum, cl.configstrings[CS_MAPCHECKSUM]); return; } } if ((precache_check > ENV_CNT) && (precache_check < TEXTURE_CNT)) { if (allow_download->value && allow_download_maps->value) { while (precache_check < TEXTURE_CNT) { int n = precache_check++ - ENV_CNT - 1; if (n & 1) { Com_sprintf(fn, sizeof(fn), "env/%s%s.pcx", cl.configstrings[CS_SKY], env_suf[n / 2]); } else { Com_sprintf(fn, sizeof(fn), "env/%s%s.tga", cl.configstrings[CS_SKY], env_suf[n / 2]); } if (!CL_CheckOrDownloadFile(fn)) { return; } } } precache_check = TEXTURE_CNT; } if (precache_check == TEXTURE_CNT) { precache_check = TEXTURE_CNT + 1; precache_tex = 0; } /* confirm existance of textures, download any that don't exist */ if (precache_check == TEXTURE_CNT + 1) { extern int numtexinfo; extern mapsurface_t map_surfaces[]; if (allow_download->value && allow_download_maps->value) { while (precache_tex < numtexinfo) { char fn[MAX_OSPATH]; sprintf(fn, "textures/%s.wal", map_surfaces[precache_tex++].rname); if (!CL_CheckOrDownloadFile(fn)) { return; /* started a download */ } } } precache_check = TEXTURE_CNT + 999; } CL_RegisterSounds(); CL_PrepRefresh(); MSG_WriteByte(&cls.netchan.message, clc_stringcmd); MSG_WriteString(&cls.netchan.message, va("begin %i\n", precache_spawncount)); } void CL_DownloadFileName(char *dest, int destlen, char *fn) { if (strncmp(fn, "players", 7) == 0) { Com_sprintf(dest, destlen, "%s/%s", BASEDIRNAME, fn); } else { Com_sprintf(dest, destlen, "%s/%s", FS_Gamedir(), fn); } } /* * Returns true if the file exists, otherwise it attempts * to start a download from the server. */ qboolean CL_CheckOrDownloadFile(char *filename) { FILE *fp; char name[MAX_OSPATH]; char *ptr; /* fix backslashes - this is mostly für UNIX comaptiblity */ while ((ptr = strchr(filename, '\\'))) { *ptr = '/'; } if (FS_LoadFile(filename, NULL) != -1) { /* it exists, no need to download */ return true; } if (strstr(filename, "..") || strstr(filename, ":") || (*filename == '.') || (*filename == '/')) { Com_Printf("Refusing to download a path with ..: %s\n", filename); return true; } strcpy(cls.downloadname, filename); /* download to a temp name, and only rename to the real name when done, so if interrupted a runt file wont be left */ COM_StripExtension(cls.downloadname, cls.downloadtempname); strcat(cls.downloadtempname, ".tmp"); /* check to see if we already have a tmp for this file, if so, try to resume and open the file if not opened yet */ CL_DownloadFileName(name, sizeof(name), cls.downloadtempname); fp = fopen(name, "r+b"); if (fp) { /* it exists */ int len; fseek(fp, 0, SEEK_END); len = ftell(fp); cls.download = fp; /* give the server an offset to start the download */ Com_Printf("Resuming %s\n", cls.downloadname); MSG_WriteByte(&cls.netchan.message, clc_stringcmd); MSG_WriteString(&cls.netchan.message, va("download %s %i", cls.downloadname, len)); } else { Com_Printf("Downloading %s\n", cls.downloadname); MSG_WriteByte(&cls.netchan.message, clc_stringcmd); MSG_WriteString(&cls.netchan.message, va("download %s", cls.downloadname)); } cls.downloadnumber++; return false; } /* * Request a download from the server */ void CL_Download_f(void) { char filename[MAX_OSPATH]; if (Cmd_Argc() != 2) { Com_Printf("Usage: download \n"); return; } Com_sprintf(filename, sizeof(filename), "%s", Cmd_Argv(1)); if (strstr(filename, "..")) { Com_Printf("Refusing to download a path with ..\n"); return; } if (FS_LoadFile(filename, NULL) != -1) { /* it exists, no need to download */ Com_Printf("File already exists.\n"); return; } strcpy(cls.downloadname, filename); Com_Printf("Downloading %s\n", cls.downloadname); /* download to a temp name, and only rename to the real name when done, so if interrupted a runt file wont be left */ COM_StripExtension(cls.downloadname, cls.downloadtempname); strcat(cls.downloadtempname, ".tmp"); MSG_WriteByte(&cls.netchan.message, clc_stringcmd); MSG_WriteString(&cls.netchan.message, va("download %s", cls.downloadname)); cls.downloadnumber++; } /* * A download message has been received from the server */ void CL_ParseDownload(void) { int size, percent; char name[MAX_OSPATH]; int r; /* read the data */ size = MSG_ReadShort(&net_message); percent = MSG_ReadByte(&net_message); if (size == -1) { Com_Printf("Server does not have this file.\n"); if (cls.download) { /* if here, we tried to resume a * file but the server said no */ fclose(cls.download); cls.download = NULL; } CL_RequestNextDownload(); return; } /* open the file if not opened yet */ if (!cls.download) { CL_DownloadFileName(name, sizeof(name), cls.downloadtempname); FS_CreatePath(name); cls.download = fopen(name, "wb"); if (!cls.download) { net_message.readcount += size; Com_Printf("Failed to open %s\n", cls.downloadtempname); CL_RequestNextDownload(); return; } } fwrite(net_message.data + net_message.readcount, 1, size, cls.download); net_message.readcount += size; if (percent != 100) { /* request next block */ cls.downloadpercent = percent; MSG_WriteByte(&cls.netchan.message, clc_stringcmd); SZ_Print(&cls.netchan.message, "nextdl"); } else { char oldn[MAX_OSPATH]; char newn[MAX_OSPATH]; fclose(cls.download); /* rename the temp file to it's final name */ CL_DownloadFileName(oldn, sizeof(oldn), cls.downloadtempname); CL_DownloadFileName(newn, sizeof(newn), cls.downloadname); r = rename(oldn, newn); if (r) { Com_Printf("failed to rename.\n"); } cls.download = NULL; cls.downloadpercent = 0; /* get another file if needed */ CL_RequestNextDownload(); } } yquake2-QUAKE2_5_32/src/client/sound/0000755000175000017500000000000012615162034020014 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/client/sound/ogg.c0000644000175000017500000003775612615162034020756 0ustar greffrathgreffrath/* * This program is free software; you can 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 implements an interface to libvorbis for decoding * OGG/Vorbis files. Strongly spoken this file isn't part of the sound * system but part of the main client. It justs converts Vorbis streams * into normal, raw Wave stream which are injected into the backends as * if they were normal "raw" samples. At this moment only background * music playback and in theory .cin movie file playback is supported. * * ======================================================================= */ #ifdef OGG #define OV_EXCLUDE_STATIC_CALLBACKS #ifndef _WIN32 #include #endif #include #include #include "../header/client.h" #include "header/local.h" #include "header/vorbis.h" qboolean ogg_first_init = true; /* First initialization flag. */ qboolean ogg_started = false; /* Initialization flag. */ int ogg_bigendian = 0; byte *ogg_buffer; /* File buffer. */ char **ogg_filelist; /* List of Ogg Vorbis files. */ char ovBuf[4096]; /* Buffer for sound. */ int ogg_curfile; /* Index of currently played file. */ int ogg_numfiles; /* Number of Ogg Vorbis files. */ int ovSection; /* Position in Ogg Vorbis file. */ ogg_status_t ogg_status; /* Status indicator. */ cvar_t *ogg_autoplay; /* Play this song when started. */ cvar_t *ogg_check; /* Check Ogg files or not. */ cvar_t *ogg_playlist; /* Playlist. */ cvar_t *ogg_sequence; /* Sequence play indicator. */ cvar_t *ogg_volume; /* Music volume. */ cvar_t *ogg_ignoretrack0; /* Toggle track 0 playing */ OggVorbis_File ovFile; /* Ogg Vorbis file. */ vorbis_info *ogg_info; /* Ogg Vorbis file information */ int ogg_numbufs; /* Number of buffers for OpenAL */ /* * Initialize the Ogg Vorbis subsystem. */ void OGG_Init(void) { cvar_t *cv; /* Cvar pointer. */ if (ogg_started) { return; } Com_Printf("Starting Ogg Vorbis.\n"); /* Skip initialization if disabled. */ cv = Cvar_Get("ogg_enable", "1", CVAR_ARCHIVE); if (cv->value != 1) { Com_Printf("Ogg Vorbis not initializing.\n"); return; } if (bigendien == true) { ogg_bigendian = 1; } /* Cvars. */ ogg_autoplay = Cvar_Get("ogg_autoplay", "?", CVAR_ARCHIVE); ogg_check = Cvar_Get("ogg_check", "0", CVAR_ARCHIVE); ogg_playlist = Cvar_Get("ogg_playlist", "playlist", CVAR_ARCHIVE); ogg_sequence = Cvar_Get("ogg_sequence", "loop", CVAR_ARCHIVE); ogg_volume = Cvar_Get("ogg_volume", "0.7", CVAR_ARCHIVE); ogg_ignoretrack0 = Cvar_Get("ogg_ignoretrack0", "0", CVAR_ARCHIVE); /* Console commands. */ Cmd_AddCommand("ogg_list", OGG_ListCmd); Cmd_AddCommand("ogg_pause", OGG_PauseCmd); Cmd_AddCommand("ogg_play", OGG_PlayCmd); Cmd_AddCommand("ogg_reinit", OGG_Reinit); Cmd_AddCommand("ogg_resume", OGG_ResumeCmd); Cmd_AddCommand("ogg_seek", OGG_SeekCmd); Cmd_AddCommand("ogg_status", OGG_StatusCmd); Cmd_AddCommand("ogg_stop", OGG_Stop); /* Build list of files. */ ogg_numfiles = 0; if (ogg_playlist->string[0] != '\0') { OGG_LoadPlaylist(ogg_playlist->string); } if (ogg_numfiles == 0) { OGG_LoadFileList(); } /* Check if we have Ogg Vorbis files. */ if (ogg_numfiles <= 0) { Com_Printf("No Ogg Vorbis files found.\n"); ogg_started = true; /* For OGG_Shutdown(). */ OGG_Shutdown(); return; } /* Initialize variables. */ if (ogg_first_init) { ogg_buffer = NULL; ogg_curfile = -1; ogg_info = NULL; ogg_status = STOP; ogg_first_init = false; } ogg_started = true; Com_Printf("%d Ogg Vorbis files found.\n", ogg_numfiles); /* Autoplay support. */ if (ogg_autoplay->string[0] != '\0') { OGG_ParseCmd(ogg_autoplay->string); } } /* * Shutdown the Ogg Vorbis subsystem. */ void OGG_Shutdown(void) { if (!ogg_started) { return; } Com_Printf("Shutting down Ogg Vorbis.\n"); OGG_Stop(); /* Free the list of files. */ FS_FreeList(ogg_filelist, ogg_numfiles + 1); /* Remove console commands. */ Cmd_RemoveCommand("ogg_list"); Cmd_RemoveCommand("ogg_pause"); Cmd_RemoveCommand("ogg_play"); Cmd_RemoveCommand("ogg_reinit"); Cmd_RemoveCommand("ogg_resume"); Cmd_RemoveCommand("ogg_seek"); Cmd_RemoveCommand("ogg_status"); Cmd_RemoveCommand("ogg_stop"); ogg_started = false; } /* * Reinitialize the Ogg Vorbis subsystem. */ void OGG_Reinit(void) { OGG_Shutdown(); OGG_Init(); } /* * Check if the file is a valid Ogg Vorbis file. */ qboolean OGG_Check(char *name) { qboolean res; /* Return value. */ byte *buffer; /* File buffer. */ int size; /* File size. */ OggVorbis_File ovf; /* Ogg Vorbis file. */ if (ogg_check->value == 0) { return true; } res = false; if ((size = FS_LoadFile(name, (void **)&buffer)) > 0) { if (ov_test(NULL, &ovf, (char *)buffer, size) == 0) { res = true; ov_clear(&ovf); } FS_FreeFile(buffer); } return res; } /* * Change position in the file. */ void OGG_Seek(ogg_seek_t type, double offset) { double pos; /* Position in file (in seconds). */ double total; /* Length of file (in seconds). */ /* Check if the file is seekable. */ if (ov_seekable(&ovFile) == 0) { Com_Printf("OGG_Seek: file is not seekable.\n"); return; } /* Get file information. */ pos = ov_time_tell(&ovFile); total = ov_time_total(&ovFile, -1); switch (type) { case ABS: if ((offset >= 0) && (offset <= total)) { if (ov_time_seek(&ovFile, offset) != 0) { Com_Printf("OGG_Seek: could not seek.\n"); } else { Com_Printf("%0.2f -> %0.2f of %0.2f.\n", pos, offset, total); } } else { Com_Printf("OGG_Seek: invalid offset.\n"); } break; case REL: if ((pos + offset >= 0) && (pos + offset <= total)) { if (ov_time_seek(&ovFile, pos + offset) != 0) { Com_Printf("OGG_Seek: could not seek.\n"); } else { Com_Printf("%0.2f -> %0.2f of %0.2f.\n", pos, pos + offset, total); } } else { Com_Printf("OGG_Seek: invalid offset.\n"); } break; } } /* * Load list of Ogg Vorbis files in "music". */ void OGG_LoadFileList(void) { char **list; /* List of .ogg files. */ int i; /* Loop counter. */ int j; /* Real position in list. */ /* Get file list. */ list = FS_ListFiles2(va("%s/*.ogg", OGG_DIR), &ogg_numfiles, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM); ogg_numfiles--; /* Check if there are posible Ogg files. */ if (list == NULL) { return; } /* Allocate list of files. */ ogg_filelist = malloc(sizeof(char *) * ogg_numfiles); /* Add valid Ogg Vorbis file to the list. */ for (i = 0, j = 0; i < ogg_numfiles; i++) { if (!OGG_Check(list[i])) { free(list[i]); continue; } ogg_filelist[j++] = list[i]; } /* Free the file list. */ free(list); /* Adjust the list size (remove space for invalid music files). */ ogg_numfiles = j; ogg_filelist = realloc(ogg_filelist, sizeof(char *) * ogg_numfiles); } /* * Load playlist. */ void OGG_LoadPlaylist(char *playlist) { byte *buffer; /* Buffer to read the file. */ char *ptr; /* Pointer for parsing the file. */ int i; /* Loop counter. */ int size; /* Length of buffer and strings. */ /* Open playlist. */ if ((size = FS_LoadFile(va("%s/%s.lst", OGG_DIR, ogg_playlist->string), (void **)&buffer)) < 0) { Com_Printf("OGG_LoadPlaylist: could not open playlist: %s.\n", strerror(errno)); return; } /* Count the files in playlist. */ for (ptr = strtok((char *)buffer, "\n"); ptr != NULL; ptr = strtok(NULL, "\n")) { if ((byte *)ptr != buffer) { ptr[-1] = '\n'; } if (OGG_Check(va("%s/%s", OGG_DIR, ptr))) { ogg_numfiles++; } } /* Allocate file list. */ ogg_filelist = malloc(sizeof(char *) * ogg_numfiles); i = 0; for (ptr = strtok((char *)buffer, "\n"); ptr != NULL; ptr = strtok(NULL, "\n")) { if (OGG_Check(va("%s/%s", OGG_DIR, ptr))) { ogg_filelist[i++] = strdup(va("%s/%s", OGG_DIR, ptr)); } } /* Free file buffer. */ FS_FreeFile(buffer); } /* * Play Ogg Vorbis file (with absolute or relative index). */ qboolean OGG_Open(ogg_seek_t type, int offset) { int size; /* File size. */ int pos = -1; /* Absolute position. */ int res; /* Error indicator. */ switch (type) { case ABS: /* Absolute index. */ if ((offset < 0) || (offset >= ogg_numfiles)) { Com_Printf("OGG_Open: %d out of range.\n", offset + 1); return false; } else { pos = offset; } break; case REL: /* Simulate a loopback. */ if ((ogg_curfile == -1) && (offset < 0)) { offset++; } while (ogg_curfile + offset < 0) { offset += ogg_numfiles; } while (ogg_curfile + offset >= ogg_numfiles) { offset -= ogg_numfiles; } pos = ogg_curfile + offset; break; } /* Check running music. */ if (ogg_status == PLAY) { if (ogg_curfile == pos) { return true; } else { OGG_Stop(); } } /* Find file. */ if ((size = FS_LoadFile(ogg_filelist[pos], (void **)&ogg_buffer)) == -1) { Com_Printf("OGG_Open: could not open %d (%s): %s.\n", pos, ogg_filelist[pos], strerror(errno)); return false; } /* Open ogg vorbis file. */ if ((res = ov_open(NULL, &ovFile, (char *)ogg_buffer, size)) < 0) { Com_Printf("OGG_Open: '%s' is not a valid Ogg Vorbis file (error %i).\n", ogg_filelist[pos], res); FS_FreeFile(ogg_buffer); ogg_buffer = NULL; return false; } ogg_info = ov_info(&ovFile, 0); if (!ogg_info) { Com_Printf("OGG_Open: Unable to get stream information for %s.\n", ogg_filelist[pos]); ov_clear(&ovFile); FS_FreeFile(ogg_buffer); ogg_buffer = NULL; return false; } /* Play file. */ ovSection = 0; ogg_curfile = pos; ogg_status = PLAY; return true; } /* * Play Ogg Vorbis file (with name only). */ qboolean OGG_OpenName(char *filename) { char *name; /* File name. */ int i; /* Loop counter. */ /* If the track name is '00', and ogg_ignoretrack0 is set to 0, stop playback */ if ((!strncmp(filename, "00", sizeof(char) * 3)) && ogg_ignoretrack0->value == 0) { OGG_PauseCmd(); return false; } name = va("%s/%s.ogg", OGG_DIR, filename); for (i = 0; i < ogg_numfiles; i++) { if (strcmp(name, ogg_filelist[i]) == 0) { break; } } if (i < ogg_numfiles) { return OGG_Open(ABS, i); } else { Com_Printf("OGG_OpenName: '%s' not in the list.\n", filename); return false; } } /* * Play a portion of the currently opened file. */ int OGG_Read(void) { int res; /* Number of bytes read. */ /* Read and resample. */ res = ov_read(&ovFile, ovBuf, sizeof(ovBuf), ogg_bigendian, OGG_SAMPLEWIDTH, 1, &ovSection); S_RawSamples(res / (OGG_SAMPLEWIDTH * ogg_info->channels), ogg_info->rate, OGG_SAMPLEWIDTH, ogg_info->channels, (byte *)ovBuf, ogg_volume->value); /* Check for end of file. */ if (res == 0) { OGG_Stop(); OGG_Sequence(); } return res; } /* * Play files in sequence. */ void OGG_Sequence(void) { if (strcmp(ogg_sequence->string, "next") == 0) { OGG_Open(REL, 1); } else if (strcmp(ogg_sequence->string, "prev") == 0) { OGG_Open(REL, -1); } else if (strcmp(ogg_sequence->string, "random") == 0) { OGG_Open(ABS, randk() % ogg_numfiles); } else if (strcmp(ogg_sequence->string, "loop") == 0) { OGG_Open(REL, 0); } else if (strcmp(ogg_sequence->string, "none") != 0) { Com_Printf("Invalid value of ogg_sequence: %s\n", ogg_sequence->string); Cvar_Set("ogg_sequence", "none"); } } /* * Stop playing the current file. */ void OGG_Stop(void) { if (ogg_status == STOP) { return; } #ifdef USE_OPENAL if (sound_started == SS_OAL) { AL_UnqueueRawSamples(); } #endif ov_clear(&ovFile); ogg_status = STOP; ogg_info = NULL; ogg_numbufs = 0; if (ogg_buffer != NULL) { FS_FreeFile(ogg_buffer); ogg_buffer = NULL; } } /* * Stream music. */ void OGG_Stream(void) { if (!ogg_started) { return; } if (ogg_status == PLAY) { #ifdef USE_OPENAL if (sound_started == SS_OAL) { /* Calculate the number of buffers used for storing decoded OGG/Vorbis data. We take the number of active buffers at startup (at this point most of the samples should be precached and loaded into buffers) and add 64. Empircal testing showed, that at most times at least 52 buffers remain available for OGG/Vorbis, enough for about 3 seconds playback. The music won't stutter as long as the framerate stayes over 1 FPS. */ if (ogg_numbufs == 0) { ogg_numbufs = active_buffers + 64; } /* active_buffers are all active OpenAL buffers, buffering normal sfx _and_ ogg/vorbis samples. */ while (active_buffers <= ogg_numbufs) { OGG_Read(); } } else /* using SDL */ #endif { if (sound_started == SS_SDL) { /* Read that number samples into the buffer, that were played since the last call to this function. This keeps the buffer at all times at an "optimal" fill level. */ while (paintedtime + MAX_RAW_SAMPLES - 2048 > s_rawend) { OGG_Read(); } } } /* using SDL */ } /* ogg_status == PLAY */ } /* * List Ogg Vorbis files. */ void OGG_ListCmd(void) { int i; for (i = 0; i < ogg_numfiles; i++) { Com_Printf("%d %s\n", i + 1, ogg_filelist[i]); } Com_Printf("%d Ogg Vorbis files.\n", ogg_numfiles); } /* * Parse play controls. */ void OGG_ParseCmd(char *arg) { int n; cvar_t *ogg_enable; ogg_enable = Cvar_Get("ogg_enable", "1", CVAR_ARCHIVE); switch (arg[0]) { case '#': n = (int)strtol(arg + 1, (char **)NULL, 10) - 1; OGG_Open(ABS, n); break; case '?': OGG_Open(ABS, randk() % ogg_numfiles); break; case '>': if (strlen(arg) > 1) { OGG_Open(REL, (int)strtol(arg + 1, (char **)NULL, 10)); } else { OGG_Open(REL, 1); } break; case '<': if (strlen(arg) > 1) { OGG_Open(REL, -(int)strtol(arg + 1, (char **)NULL, 10)); } else { OGG_Open(REL, -1); } break; default: if (ogg_enable->value != 0) { OGG_OpenName(arg); } break; } } /* * Pause current song. */ void OGG_PauseCmd(void) { if (ogg_status == PLAY) { ogg_status = PAUSE; ogg_numbufs = 0; } } /* * Play control. */ void OGG_PlayCmd(void) { if (Cmd_Argc() < 2) { Com_Printf("Usage: ogg_play {filename | #n | ? | >n | n}\n"); return; } switch (Cmd_Argv(1)[0]) { case '>': OGG_Seek(REL, strtod(Cmd_Argv(1) + 1, (char **)NULL)); break; case '<': OGG_Seek(REL, -strtod(Cmd_Argv(1) + 1, (char **)NULL)); break; default: OGG_Seek(ABS, strtod(Cmd_Argv(1), (char **)NULL)); break; } } /* * Display status. */ void OGG_StatusCmd(void) { switch (ogg_status) { case PLAY: Com_Printf("Playing file %d (%s) at %0.2f seconds.\n", ogg_curfile + 1, ogg_filelist[ogg_curfile], ov_time_tell(&ovFile)); break; case PAUSE: Com_Printf("Paused file %d (%s) at %0.2f seconds.\n", ogg_curfile + 1, ogg_filelist[ogg_curfile], ov_time_tell(&ovFile)); break; case STOP: if (ogg_curfile == -1) { Com_Printf("Stopped.\n"); } else { Com_Printf("Stopped file %d (%s).\n", ogg_curfile + 1, ogg_filelist[ogg_curfile]); } break; } } #endif /* OGG */ yquake2-QUAKE2_5_32/src/client/sound/header/0000755000175000017500000000000012615162034021244 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/client/sound/header/cdaudio.h0000644000175000017500000000241612615162034023030 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Function prototypes for the Audio-CD playback * * ======================================================================= */ #ifdef CDA #ifndef CL_SOUND_CDAUDIO_H #define CL_SOUND_CDAUDIO_H int CDAudio_Init(void); void CDAudio_Shutdown(void); void CDAudio_Play(int track, qboolean looping); void CDAudio_Stop(void); void CDAudio_Update(void); void CDAudio_Activate (qboolean active); void CDAudio_RandomPlay(void); #endif #endif yquake2-QUAKE2_5_32/src/client/sound/header/vorbis.h0000644000175000017500000000366712615162034022735 0ustar greffrathgreffrath/* * This program is free software; you can 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 header file for the OGG/Vorbis playback * * ======================================================================= */ #ifdef OGG #ifndef CL_SOUND_VORBIS_H #define CL_SOUND_VORBIS_H /* The OGG codec can return the samples in a number * of different formats, we use the standard signed * short format. */ #define OGG_SAMPLEWIDTH 2 #define OGG_DIR "music" typedef enum { PLAY, PAUSE, STOP } ogg_status_t; typedef enum { ABS, REL } ogg_seek_t; void OGG_Init(void); void OGG_Shutdown(void); void OGG_Reinit(void); qboolean OGG_Check(char *name); void OGG_Seek(ogg_seek_t type, double offset); void OGG_LoadFileList(void); void OGG_LoadPlaylist(char *name); qboolean OGG_Open(ogg_seek_t type, int offset); qboolean OGG_OpenName(char *filename); int OGG_Read(void); void OGG_Sequence(void); void OGG_Stop(void); void OGG_Stream(void); void S_RawSamplesVol(int samples, int rate, int width, int channels, byte *data, float volume); /* Console commands. */ void OGG_ListCmd(void); void OGG_ParseCmd(char *arg); void OGG_PauseCmd(void); void OGG_PlayCmd(void); void OGG_ResumeCmd(void); void OGG_SeekCmd(void); void OGG_StatusCmd(void); #endif #endif yquake2-QUAKE2_5_32/src/client/sound/header/sound.h0000644000175000017500000000372612615162034022555 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Public header for the sound system. This is included in the main * client source and in the low level sound backend. * * ======================================================================= */ #ifndef CL_SOUND_SOUND_H #define CL_SOUND_SOUND_H struct sfx_s; void S_Init(void); void S_Shutdown(void); /* if origin is NULL, the sound will be dynamically sourced from the entity */ void S_StartSound(vec3_t origin, int entnum, int entchannel, struct sfx_s *sfx, float fvol, float attenuation, float timeofs); void S_StartLocalSound(char *s); void S_RawSamples(int samples, int rate, int width, int channels, byte *data, float volume); void S_StopAllSounds(void); void S_Update(vec3_t origin, vec3_t v_forward, vec3_t v_right, vec3_t v_up); void S_Activate(qboolean active); void S_BeginRegistration(void); struct sfx_s *S_RegisterSound(char *sample); void S_EndRegistration(void); struct sfx_s *S_FindName(char *name, qboolean create); /* the sound code makes callbacks to the client for entitiy position information, so entities can be dynamically re-spatialized */ void CL_GetEntitySoundOrigin(int ent, vec3_t org); #endif yquake2-QUAKE2_5_32/src/client/sound/header/local.h0000644000175000017500000001577712615162034022530 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Local defines for the sound system. * * ======================================================================= */ #ifndef CL_SOUND_LOCAL_H #define CL_SOUND_LOCAL_H #define MAX_CHANNELS 32 #define MAX_RAW_SAMPLES 8192 /* * Holds one sample with 2 channels */ typedef struct { int left; int right; } portable_samplepair_t; /* * Holds a cached SFX and * it's meta data */ typedef struct { int length; int loopstart; int speed; int width; #if USE_OPENAL int size; int bufnum; #endif int stereo; byte data[1]; } sfxcache_t; /* * Holds a SFX */ typedef struct sfx_s { char name[MAX_QPATH]; int registration_sequence; sfxcache_t *cache; char *truename; } sfx_t; /* A playsound_t will be generated by each call * to S_StartSound. When the mixer reaches * playsound->begin, the playsound will * be assigned to a channel. */ typedef struct playsound_s { struct playsound_s *prev, *next; sfx_t *sfx; float volume; float attenuation; int entnum; int entchannel; qboolean fixed_origin; vec3_t origin; unsigned begin; } playsound_t; /* * Interface to pass data and metadata * between the frontend and the backends. * Mainly used by the SDL backend, since * the OpenAL backend has it's own AL * based magic. */ typedef struct { 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; } sound_t; /* * Hold all information for one * playback channel. */ 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; /* only use if fixed_origin is set */ vec_t dist_mult; /* distance multiplier (attenuation/clipK) */ int master_vol; /* 0-255 master volume */ qboolean fixed_origin; /* use origin instead of fetching entnum's origin */ qboolean autosound; /* from an entity->sound, cleared each frame */ #if USE_OPENAL int autoframe; float oal_vol; int srcnum; #endif } channel_t; /* * Information read from * wave file header. */ typedef struct { int rate; int width; int channels; int loopstart; int samples; int dataofs; /* chunk starts this many bytes from file start */ } wavinfo_t; /* * Type of active sound backend */ typedef enum { SS_NOT = 0, /* soundsystem not started */ SS_SDL, /* soundsystem started, using SDL */ SS_OAL /* soundsystem started, using OpenAL */ } sndstarted_t; /* * cvars */ extern cvar_t *s_volume; extern cvar_t *s_nosound; extern cvar_t *s_loadas8bit; extern cvar_t *s_khz; extern cvar_t *s_show; extern cvar_t *s_mixahead; extern cvar_t *s_testsound; extern cvar_t *s_ambient; extern cvar_t* s_underwater; extern cvar_t* s_underwater_gain_hf; /* * Globals */ extern channel_t channels[MAX_CHANNELS]; extern int paintedtime; extern int s_numchannels; extern int s_rawend; extern playsound_t s_pendingplays; extern portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES]; extern sndstarted_t sound_started; extern sound_t sound; extern vec3_t listener_origin; extern vec3_t listener_forward; extern vec3_t listener_right; extern vec3_t listener_up; extern qboolean snd_is_underwater; extern qboolean snd_is_underwater_enabled; /* * Returns the header infos * of a wave file */ wavinfo_t GetWavinfo(char *name, byte *wav, int wavlength); /* * Loads one sample into * the cache */ sfxcache_t *S_LoadSound(sfx_t *s); /* * Plays one sound sample */ void S_IssuePlaysound(playsound_t *ps); /* * picks a channel based on priorities, * empty slots, number of channels */ channel_t *S_PickChannel(int entnum, int entchannel); /* * Builds a list of all * sound still in flight */ void S_BuildSoundList(int *sounds); /* ----------------------------------------------------------------- */ /* * Initalizes the SDL backend */ qboolean SDL_BackendInit(void); /* * Shuts the SDL backend down */ void SDL_BackendShutdown(void); /* * Print informations about * the SDL backend */ void SDL_SoundInfo(void); /* * Alters start position of * sound playback */ int SDL_DriftBeginofs(float); /* * Clears all playback buffers */ void SDL_ClearBuffer(void); /* * Caches an sample for use * the SDL backend */ qboolean SDL_Cache(sfx_t *sfx, wavinfo_t *info, byte *data); /* * Performs all sound calculations * for the SDL backendend and fills * the buffer */ void SDL_Update(void); /* * Queues raw samples for * playback */ void SDL_RawSamples(int samples, int rate, int width, int channels, byte *data, float volume); /* * Spartializes a sample */ void SDL_Spatialize(channel_t *ch); /* ----------------------------------------------------------------- */ #if USE_OPENAL /* Only begin attenuating sound volumes when outside the FULLVOLUME range */ #define SOUND_FULLVOLUME 1.0 #define SOUND_LOOPATTENUATE 0.003 /* number of buffers in flight (needed for ogg) */ extern int active_buffers; /* * Informations about the * OpenAL backend */ void AL_SoundInfo(void); /* Initializes the OpenAL backend */ qboolean AL_Init(void); /* * Shuts the OpenAL backend down */ void AL_Shutdown(void); /* * Upload ("cache") one sample * into OpenAL */ sfxcache_t *AL_UploadSfx(sfx_t *s, wavinfo_t *s_info, byte *data); /* * Deletes one sample from OpenAL */ void AL_DeleteSfx(sfx_t *s); /* * Stops playback of a channel */ void AL_StopChannel(channel_t *ch); /* * Starts playback of a channel */ void AL_PlayChannel(channel_t *ch); /* * Stops playback of all channels */ void AL_StopAllChannels(void); /* * Perform caculations and fill * OpenALs buffer */ void AL_Update(void); /* * Plays raw samples */ void AL_RawSamples(int samples, int rate, int width, int channels, byte *data, float volume); /* * Unqueues any raw samples * still in flight */ void AL_UnqueueRawSamples(); #endif /* USE_OPENAL */ #endif /* CL_SOUND_LOCAL_H */ yquake2-QUAKE2_5_32/src/client/sound/sound.c0000644000175000017500000004544512615162034021324 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 upper layer of the Quake II sound system. This is merely more * than an interface between the client and a backend. Currently only * two backends are supported: * - OpenAL, renders sound with OpenAL. * - SDL, has the same features than the original sound system. * * ======================================================================= */ #include "../header/client.h" #include "../../backends/generic/header/qal.h" #include "header/local.h" #include "header/vorbis.h" /* During registration it is possible to have more sounds than could actually be referenced during gameplay, because we don't want to free anything until we are sure we won't need it. */ #define MAX_SFX (MAX_SOUNDS * 2) #define MAX_PLAYSOUNDS 128 vec3_t listener_origin; vec3_t listener_forward; vec3_t listener_right; vec3_t listener_up; playsound_t s_playsounds[MAX_PLAYSOUNDS]; playsound_t s_freeplays; playsound_t s_pendingplays; cvar_t *s_volume; cvar_t *s_testsound; cvar_t *s_loadas8bit; cvar_t *s_khz; cvar_t *s_mixahead; cvar_t *s_show; cvar_t *s_ambient; cvar_t* s_underwater; cvar_t* s_underwater_gain_hf; channel_t channels[MAX_CHANNELS]; int num_sfx; int paintedtime; int s_numchannels; int s_rawend; int s_registration_sequence; portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES]; qboolean snd_initialized = false; sfx_t known_sfx[MAX_SFX]; sndstarted_t sound_started = SS_NOT; sound_t sound; static qboolean s_registering; qboolean snd_is_underwater; qboolean snd_is_underwater_enabled; /* ----------------------------------------------------------------- */ /* * Loads one sample into memory */ sfxcache_t * S_LoadSound(sfx_t *s) { char namebuffer[MAX_QPATH]; byte *data; wavinfo_t info; sfxcache_t *sc; int size; char *name; if (s->name[0] == '*') { return NULL; } /* see if still in memory */ sc = s->cache; if (sc) { return sc; } /* load it */ if (s->truename) { name = s->truename; } else { name = s->name; } if (name[0] == '#') { strcpy(namebuffer, &name[1]); } else { Com_sprintf(namebuffer, sizeof(namebuffer), "sound/%s", name); } size = FS_LoadFile(namebuffer, (void **)&data); if (!data) { s->cache = NULL; Com_DPrintf("Couldn't load %s\n", namebuffer); return NULL; } info = GetWavinfo(s->name, data, size); if (info.channels != 1) { Com_Printf("%s is a stereo sample\n", s->name); FS_FreeFile(data); return NULL; } #if USE_OPENAL if (sound_started == SS_OAL) { sc = AL_UploadSfx(s, &info, data + info.dataofs); } else #endif { if (sound_started == SS_SDL) { if (!SDL_Cache(s, &info, data + info.dataofs)) { Com_Printf("Pansen!\n"); FS_FreeFile(data); return NULL; } } } FS_FreeFile(data); return sc; } /* * Returns the name of a sound */ sfx_t * S_FindName(char *name, qboolean create) { int i; sfx_t *sfx; if (!name) { Com_Error(ERR_FATAL, "S_FindName: NULL\n"); } if (!name[0]) { Com_Error(ERR_FATAL, "S_FindName: empty name\n"); } if (strlen(name) >= MAX_QPATH) { Com_Error(ERR_FATAL, "Sound name too long: %s", name); } /* see if already loaded */ for (i = 0; i < num_sfx; i++) { if (!strcmp(known_sfx[i].name, name)) { return &known_sfx[i]; } } if (!create) { return NULL; } /* find a free sfx */ for (i = 0; i < num_sfx; i++) { if (!known_sfx[i].name[0]) { break; } } if (i == num_sfx) { if (num_sfx == MAX_SFX) { Com_Error(ERR_FATAL, "S_FindName: out of sfx_t"); } num_sfx++; } sfx = &known_sfx[i]; sfx->truename = NULL; strcpy(sfx->name, name); sfx->registration_sequence = s_registration_sequence; return sfx; } /* * Registers an alias name * for a sound */ sfx_t * S_AliasName(char *aliasname, char *truename) { sfx_t *sfx; char *s; int i; s = Z_Malloc(MAX_QPATH); strcpy(s, truename); /* find a free sfx */ for (i = 0; i < num_sfx; i++) { if (!known_sfx[i].name[0]) { break; } } if (i == num_sfx) { if (num_sfx == MAX_SFX) { Com_Error(ERR_FATAL, "S_FindName: out of sfx_t"); } num_sfx++; } sfx = &known_sfx[i]; sfx->cache = NULL; strcpy(sfx->name, aliasname); sfx->registration_sequence = s_registration_sequence; sfx->truename = s; return sfx; } /* * Called before registering * of sound starts */ void S_BeginRegistration(void) { s_registration_sequence++; s_registering = true; } /* * Registers a sound */ sfx_t * S_RegisterSound(char *name) { sfx_t *sfx; if (!sound_started) { return NULL; } sfx = S_FindName(name, true); sfx->registration_sequence = s_registration_sequence; if (!s_registering) { S_LoadSound(sfx); } return sfx; } struct sfx_s * S_RegisterSexedSound(entity_state_t *ent, char *base) { int n; char *p; int len; struct sfx_s *sfx; char model[MAX_QPATH]; char sexedFilename[MAX_QPATH]; char maleFilename[MAX_QPATH]; /* determine what model the client is using */ model[0] = 0; n = CS_PLAYERSKINS + ent->number - 1; if (cl.configstrings[n][0]) { p = strchr(cl.configstrings[n], '\\'); if (p) { p += 1; strcpy(model, p); p = strchr(model, '/'); if (p) { p[0] = 0; } } } /* if we can't figure it out, they're male */ if (!model[0]) { strcpy(model, "male"); } /* see if we already know of the model specific sound */ Com_sprintf(sexedFilename, sizeof(sexedFilename), "#players/%s/%s", model, base + 1); sfx = S_FindName(sexedFilename, false); if (!sfx) { /* no, so see if it exists */ len = FS_LoadFile(&sexedFilename[1], NULL); if (len != -1) { /* yes, close the file and register it */ sfx = S_RegisterSound(sexedFilename); } else { /* no, revert to the male sound in the pak0.pak */ Com_sprintf(maleFilename, sizeof(maleFilename), "player/male/%s", base + 1); sfx = S_AliasName(sexedFilename, maleFilename); } } return sfx; } /* * Called after registering of * sound has ended */ void S_EndRegistration(void) { int i; sfx_t *sfx; /* free any sounds not from this registration sequence */ for (i = 0, sfx = known_sfx; i < num_sfx; i++, sfx++) { if (!sfx->name[0]) { continue; } if (sfx->registration_sequence != s_registration_sequence) { /* it is possible to have a leftover */ if (sfx->cache) { Z_Free(sfx->cache); /* from a server that didn't finish loading */ } if (sfx->truename) { Z_Free(sfx->truename); } sfx->cache = NULL; sfx->name[0] = 0; } } /* load everything in */ for (i = 0, sfx = known_sfx; i < num_sfx; i++, sfx++) { if (!sfx->name[0]) { continue; } S_LoadSound(sfx); } s_registering = false; } /* ----------------------------------------------------------------- */ /* * Picks a free channel */ channel_t * S_PickChannel(int entnum, int entchannel) { int ch_idx; int first_to_die; int life_left; channel_t *ch; if (entchannel < 0) { Com_Error(ERR_DROP, "S_PickChannel: entchannel<0"); } /* Check for replacement sound, or find the best one to replace */ first_to_die = -1; life_left = 0x7fffffff; for (ch_idx = 0; ch_idx < s_numchannels; ch_idx++) { /* channel 0 never overrides */ if ((entchannel != 0) && (channels[ch_idx].entnum == entnum) && (channels[ch_idx].entchannel == entchannel)) { /* always override sound from same entity */ first_to_die = ch_idx; break; } /* don't let monster sounds override player sounds */ if ((channels[ch_idx].entnum == cl.playernum + 1) && (entnum != cl.playernum + 1) && 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; } ch = &channels[first_to_die]; #if USE_OPENAL if ((sound_started == SS_OAL) && ch->sfx) { /* Make sure the channel is dead */ AL_StopChannel(ch); } #endif memset(ch, 0, sizeof(*ch)); return ch; } /* * Picks a free playsound */ playsound_t * S_AllocPlaysound(void) { playsound_t *ps; ps = s_freeplays.next; if (ps == &s_freeplays) { /* no free playsounds, this results in stuttering an cracking */ return NULL; } /* unlink from freelist */ ps->prev->next = ps->next; ps->next->prev = ps->prev; return ps; } /* * Frees a playsound */ void S_FreePlaysound(playsound_t *ps) { /* unlink from channel */ ps->prev->next = ps->next; ps->next->prev = ps->prev; /* add to free list */ ps->next = s_freeplays.next; s_freeplays.next->prev = ps; ps->prev = &s_freeplays; s_freeplays.next = ps; } /* * Take the next playsound and begin it on the channel * This is never called directly by S_Play*, but only * by the update loop. */ void S_IssuePlaysound(playsound_t *ps) { channel_t *ch; sfxcache_t *sc; if (!ps) { return; } if (s_show->value) { Com_Printf("Issue %i\n", ps->begin); } /* pick a channel to play on */ ch = S_PickChannel(ps->entnum, ps->entchannel); if (!ch) { S_FreePlaysound(ps); return; } sc = S_LoadSound(ps->sfx); if (!sc) { Com_Printf("S_IssuePlaysound: couldn't load %s\n", ps->sfx->name); S_FreePlaysound(ps); return; } /* spatialize */ if (ps->attenuation == ATTN_STATIC) { ch->dist_mult = ps->attenuation * 0.001f; } else { ch->dist_mult = ps->attenuation * 0.0005f; } ch->entnum = ps->entnum; ch->entchannel = ps->entchannel; ch->sfx = ps->sfx; VectorCopy(ps->origin, ch->origin); ch->fixed_origin = ps->fixed_origin; #if USE_OPENAL if (sound_started == SS_OAL) { /* This is clamped to 1.0 in AL_PlayChannel() */ ch->oal_vol = ps->volume * (s_volume->value); AL_PlayChannel(ch); } else #endif { if (sound_started == SS_SDL) { ch->master_vol = (int)ps->volume; SDL_Spatialize(ch); } } ch->pos = 0; ch->end = paintedtime + sc->length; /* free the playsound */ S_FreePlaysound(ps); } /* * Validates the parms and queues the sound up. * If pos is NULL, the sound will be dynamically * sourced from the entity. Entchannel 0 will never * override a playing sound. */ void S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float fvol, float attenuation, float timeofs) { sfxcache_t *sc; playsound_t *ps, *sort; if (!sound_started) { return; } if (!sfx) { return; } if (sfx->name[0] == '*') { sfx = S_RegisterSexedSound(&cl_entities[entnum].current, sfx->name); if (!sfx) { return; } } /* make sure the sound is loaded */ sc = S_LoadSound(sfx); if (!sc) { /* couldn't load the sound's data */ return; } /* make the playsound_t */ ps = S_AllocPlaysound(); if (!ps) { return; } if (origin) { VectorCopy(origin, ps->origin); ps->fixed_origin = true; } else { ps->fixed_origin = false; } ps->entnum = entnum; ps->entchannel = entchannel; ps->attenuation = attenuation; ps->sfx = sfx; #if USE_OPENAL if (sound_started == SS_OAL) { ps->begin = paintedtime + timeofs * 1000; ps->volume = fvol; } else #endif { if (sound_started == SS_SDL) { ps->begin = SDL_DriftBeginofs(timeofs); ps->volume = fvol * 255; } } /* sort into the pending sound list */ for (sort = s_pendingplays.next; sort != &s_pendingplays && sort->begin < ps->begin; sort = sort->next) { } ps->next = sort; ps->prev = sort->prev; ps->next->prev = ps; ps->prev->next = ps; } /* * Plays a sound when we're not * in a level. Used by the menu * system. */ void S_StartLocalSound(char *sound) { sfx_t *sfx; if (!sound_started) { return; } sfx = S_RegisterSound(sound); if (!sfx) { Com_Printf("S_StartLocalSound: can't cache %s\n", sound); return; } S_StartSound(NULL, cl.playernum + 1, 0, sfx, 1, 1, 0); } /* * Stops all sounds */ void S_StopAllSounds(void) { int i; if (!sound_started) { return; } /* clear all the playsounds */ memset(s_playsounds, 0, sizeof(s_playsounds)); s_freeplays.next = s_freeplays.prev = &s_freeplays; s_pendingplays.next = s_pendingplays.prev = &s_pendingplays; for (i = 0; i < MAX_PLAYSOUNDS; i++) { s_playsounds[i].prev = &s_freeplays; s_playsounds[i].next = s_freeplays.next; s_playsounds[i].prev->next = &s_playsounds[i]; s_playsounds[i].next->prev = &s_playsounds[i]; } #if USE_OPENAL if (sound_started == SS_OAL) { AL_StopAllChannels(); } else #endif { if (sound_started == SS_SDL) { SDL_ClearBuffer(); } } /* clear all the channels */ memset(channels, 0, sizeof(channels)); } /* * Builds a list of all sounds */ void S_BuildSoundList(int *sounds) { int i; int num; entity_state_t *ent; for (i = 0; i < cl.frame.num_entities; i++) { if (i >= MAX_EDICTS) { break; } num = (cl.frame.parse_entities + i) & (MAX_PARSE_ENTITIES - 1); ent = &cl_parse_entities[num]; if ((s_ambient->value == 2) && !ent->modelindex) { sounds[i] = 0; } else if ((s_ambient->value == 3) && (ent->number != cl.playernum + 1)) { sounds[i] = 0; } else { sounds[i] = ent->sound; } } } /* * Cinematic streaming and voice over network. * This could be used for chat over network, but * that would be terrible slow. */ void S_RawSamples(int samples, int rate, int width, int channels, byte *data, float volume) { if (!sound_started) { return; } if (s_rawend < paintedtime) { s_rawend = paintedtime; } #if USE_OPENAL if (sound_started == SS_OAL) { volume = volume * (s_volume->value); AL_RawSamples(samples, rate, width, channels, data, volume); } else #endif { if (sound_started == SS_SDL) { SDL_RawSamples(samples, rate, width, channels, data, volume); } } } /* * Calls the update functions of the * backend. Those perform their * calculations and fill their buffers. */ void S_Update(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) { if (!sound_started) { return; } VectorCopy(origin, listener_origin); VectorCopy(forward, listener_forward); VectorCopy(right, listener_right); VectorCopy(up, listener_up); #if USE_OPENAL if (sound_started == SS_OAL) { AL_Update(); } else #endif { if (sound_started == SS_SDL) { SDL_Update(); } } } /* * Plays one sample. Called * by the "play" cmd. */ void S_Play(void) { int i; char name[256]; sfx_t *sfx; i = 1; while (i < Cmd_Argc()) { if (!strrchr(Cmd_Argv(i), '.')) { Q_strlcpy(name, Cmd_Argv(i), sizeof(name) - 4); Q_strlcat(name, ".wav", sizeof(name)); } else { Q_strlcpy(name, Cmd_Argv(i), sizeof(name)); } if (strstr(name, "..") || (name[0] == '/') || (name[0] == '\\')) { Com_Printf("Bad filename %s\n", name); return; } sfx = S_RegisterSound(name); S_StartSound(NULL, cl.playernum + 1, 0, sfx, 1.0, 1.0, 0); i++; } } /* * List all loaded sounds */ void S_SoundList(void) { int i; sfx_t *sfx; sfxcache_t *sc; int size, total; int numsounds; total = 0; numsounds = 0; for (sfx = known_sfx, i = 0; i < num_sfx; i++, sfx++) { if (!sfx->name[0]) { continue; } sc = sfx->cache; if (sc) { size = sc->length * sc->width * (sc->stereo + 1); total += size; Com_Printf("%s(%2db) %8i : %s\n", sc->loopstart != -1 ? "L" : " ", sc->width * 8, size, sfx->name); } else { if (sfx->name[0] == '*') { Com_Printf(" placeholder : %s\n", sfx->name); } else { Com_Printf(" not loaded : %s\n", sfx->name); } } numsounds++; } Com_Printf("Total resident: %i bytes (%.2f MB) in %d sounds\n", total, (float)total / 1024 / 1024, numsounds); } /* ----------------------------------------------------------------- */ /* * Prints information about the * active sound backend */ void S_SoundInfo_f(void) { if (!sound_started) { Com_Printf("Sound system not started\n"); return; } #if USE_OPENAL if (sound_started == SS_OAL) { QAL_SoundInfo(); } else #endif { SDL_SoundInfo(); } } /* * Initializes the sound system * and it's requested backend */ void S_Init(void) { cvar_t *cv; Com_Printf("\n------- sound initialization -------\n"); cv = Cvar_Get("s_initsound", "1", 0); if (!cv->value) { Com_Printf("Not initializing.\n"); Com_Printf("------------------------------------\n\n"); return; } s_volume = Cvar_Get("s_volume", "0.7", CVAR_ARCHIVE); s_khz = Cvar_Get("s_khz", "44", CVAR_ARCHIVE); s_loadas8bit = Cvar_Get("s_loadas8bit", "0", CVAR_ARCHIVE); s_mixahead = Cvar_Get("s_mixahead", "0.14", CVAR_ARCHIVE); s_show = Cvar_Get("s_show", "0", 0); s_testsound = Cvar_Get("s_testsound", "0", 0); s_ambient = Cvar_Get("s_ambient", "1", 0); s_underwater = Cvar_Get("s_underwater", "1", CVAR_ARCHIVE); s_underwater_gain_hf = Cvar_Get("s_underwater_gain_hf", "0.25", CVAR_ARCHIVE); Cmd_AddCommand("play", S_Play); Cmd_AddCommand("stopsound", S_StopAllSounds); Cmd_AddCommand("soundlist", S_SoundList); Cmd_AddCommand("soundinfo", S_SoundInfo_f); #ifdef OGG Cmd_AddCommand("ogg_init", OGG_Init); Cmd_AddCommand("ogg_shutdown", OGG_Shutdown); #endif #if USE_OPENAL cv = Cvar_Get("s_openal", "1", CVAR_ARCHIVE); if (cv->value && AL_Init()) { sound_started = SS_OAL; } else #endif { if (SDL_BackendInit()) { sound_started = SS_SDL; } else { sound_started = SS_NOT; return; } } num_sfx = 0; paintedtime = 0; #ifdef OGG OGG_Init(); #endif Com_Printf("Sound sampling rate: %i\n", sound.speed); S_StopAllSounds(); Com_Printf("------------------------------------\n\n"); } /* * Shutdown the sound system * and it's backend */ void S_Shutdown(void) { int i; sfx_t *sfx; if (!sound_started) { return; } S_StopAllSounds(); #ifdef OGG OGG_Shutdown(); #endif /* free all sounds */ for (i = 0, sfx = known_sfx; i < num_sfx; i++, sfx++) { if (!sfx->name[0]) { continue; } #if USE_OPENAL if (sound_started == SS_OAL) { AL_DeleteSfx(sfx); } #endif if (sfx->cache) { Z_Free(sfx->cache); } if (sfx->truename) { Z_Free(sfx->truename); } } memset(known_sfx, 0, sizeof(known_sfx)); num_sfx = 0; #if USE_OPENAL if (sound_started == SS_OAL) { AL_Shutdown(); } else #endif { if (sound_started == SS_SDL) { SDL_BackendShutdown(); } } sound_started = SS_NOT; s_numchannels = 0; Cmd_RemoveCommand("soundlist"); Cmd_RemoveCommand("soundinfo"); Cmd_RemoveCommand("play"); Cmd_RemoveCommand("stopsound"); #ifdef OGG Cmd_RemoveCommand("ogg_init"); Cmd_RemoveCommand("ogg_shutdown"); #endif } yquake2-QUAKE2_5_32/src/client/sound/wave.c0000644000175000017500000000714312615162034021127 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements a subset of the WAVE audio file format. * * ======================================================================= */ #include "../header/client.h" #include "header/local.h" 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; data_p += 4; if (data_p >= iff_end) { data_p = NULL; return; } iff_chunk_len = GetLittleLong(); if (iff_chunk_len < 0) { data_p = NULL; return; } data_p -= 8; last_chunk = data_p + 8 + ((iff_chunk_len + 1) & ~1); if (!strncmp((const char *)data_p, name, 4)) { return; } } } void FindChunk(char *name) { last_chunk = iff_data; FindNextChunk(name); } wavinfo_t GetWavinfo(char *name, byte *wav, int wavlength) { wavinfo_t info; int i; int format; int samples; memset(&info, 0, sizeof(info)); if (!wav) { return info; } iff_data = wav; iff_end = wav + wavlength; /* find "RIFF" chunk */ FindChunk("RIFF"); if (!(data_p && !strncmp((const char *)data_p + 8, "WAVE", 4))) { Com_Printf("Missing RIFF/WAVE chunks\n"); return info; } /* get "fmt " chunk */ iff_data = data_p + 12; FindChunk("fmt "); if (!data_p) { Com_Printf("Missing fmt chunk\n"); return info; } data_p += 8; format = GetLittleShort(); if (format != 1) { Com_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(); /* if the next chunk is a LIST chunk, look for a cue length marker */ FindNextChunk("LIST"); if (data_p) { if (((data_p - wav) + 32 <= wavlength) && !strncmp((const 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; } } } else { info.loopstart = -1; } /* find data chunk */ FindChunk("data"); if (!data_p) { Com_Printf("Missing data chunk\n"); return info; } data_p += 4; samples = GetLittleLong() / info.width; if (info.samples) { if (samples < info.samples) { Com_Error(ERR_DROP, "Sound %s has a bad loop length", name); } } else { info.samples = samples; } info.dataofs = (int)(data_p - wav); return info; } yquake2-QUAKE2_5_32/src/client/sound/openal.c0000644000175000017500000004026412615162034021444 0ustar greffrathgreffrath/* * Copyright (C) 1997-2005 Id Software, Inc. * (C) 2010 skuller.net * (C) 2005 Stuart Dalton (badcdev@gmail.com) * * This program is free software; you can 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 is the OpenAL backend for the Quake II Soundsystem. Most of these * functions were optained from the Q2Pro project, and some are from zeq2. * We adapted them to work with Yamagi Quake II. The OpenAL backend is * split into two layers. This is the upper layer, doing the actual work. * The lower layer implements the interface between Q2 and the OpenAL * implementation. This backend is overmuch complicated due to the * restrictions of the frontend. * * ======================================================================= */ #ifdef USE_OPENAL #include "../header/client.h" #include "../../backends/generic/header/qal.h" #include "header/local.h" #include "header/vorbis.h" /* translates from AL coordinate system to quake */ #define AL_UnpackVector(v) - v[1], v[2], -v[0] #define AL_CopyVector(a, b) ((b)[0] = -(a)[1], (b)[1] = (a)[2], (b)[2] = -(a)[0]) /* The OpenAL implementation should support at least this number of sources */ #define MIN_CHANNELS 16 /* Globals */ cvar_t *s_openal_maxgain; int active_buffers; qboolean streamPlaying; static ALuint s_srcnums[MAX_CHANNELS - 1]; static ALuint streamSource; static int s_framecount; static ALuint underwaterFilter; /* ----------------------------------------------------------------- */ /* * Silence / stop all OpenAL streams */ static void AL_StreamDie(void) { int numBuffers; streamPlaying = false; qalSourceStop(streamSource); /* Un-queue any buffers, and delete them */ qalGetSourcei(streamSource, AL_BUFFERS_QUEUED, &numBuffers); while (numBuffers--) { ALuint buffer; qalSourceUnqueueBuffers(streamSource, 1, &buffer); qalDeleteBuffers(1, &buffer); active_buffers--; } } /* * Updates stream sources by removing all played * buffers and restarting playback if necessary. */ static void AL_StreamUpdate(void) { int numBuffers; ALint state; /* Un-queue any buffers, and delete them */ qalGetSourcei(streamSource, AL_BUFFERS_PROCESSED, &numBuffers); while (numBuffers--) { ALuint buffer; qalSourceUnqueueBuffers(streamSource, 1, &buffer); qalDeleteBuffers(1, &buffer); active_buffers--; } /* Start the streamSource playing if necessary */ qalGetSourcei(streamSource, AL_BUFFERS_QUEUED, &numBuffers); qalGetSourcei(streamSource, AL_SOURCE_STATE, &state); if (state == AL_STOPPED) { streamPlaying = false; } if (!streamPlaying && numBuffers) { qalSourcePlay(streamSource); streamPlaying = true; } } /* ----------------------------------------------------------------- */ /* * Uploads a sample to OpenAL and places * a dummy entry in the frontends cache. * This is not nice but necessary for the * frontend to work. */ sfxcache_t * AL_UploadSfx(sfx_t *s, wavinfo_t *s_info, byte *data) { sfxcache_t *sc; ALsizei size; ALenum format; ALuint name; size = s_info->samples * s_info->width; format = s_info->width == 2 ? AL_FORMAT_MONO16 : AL_FORMAT_MONO8; if (!size) { return NULL; } qalGetError(); qalGenBuffers(1, &name); qalBufferData(name, format, data, size, s_info->rate); active_buffers++; if (qalGetError() != AL_NO_ERROR) { return NULL; } /* allocate placeholder sfxcache */ sc = s->cache = Z_TagMalloc(sizeof(*sc), 0); sc->length = s_info->samples * 1000 / s_info->rate; sc->loopstart = s_info->loopstart; sc->width = s_info->width; sc->size = size; sc->bufnum = name; return sc; } /* * Deletes a sample from OpenALs internal * cache. The dummy entry in the frontends * cache is deleted by the frontend. */ void AL_DeleteSfx(sfx_t *s) { sfxcache_t *sc; ALuint name; sc = s->cache; if (!sc) { return; } name = sc->bufnum; qalDeleteBuffers(1, &name); active_buffers--; } /* ----------------------------------------------------------------- */ /* * Performance stereo spatialization * of a channel in the frontends * sense. */ static void AL_Spatialize(channel_t *ch) { vec3_t origin; if ((ch->entnum == -1) || (ch->entnum == cl.playernum + 1) || !ch->dist_mult) { /* from view entity (player) => nothing to do, * position is still (0,0,0) and relative, * as set in AL_PlayChannel() */ return; } else if (ch->fixed_origin) { VectorCopy(ch->origin, origin); qalSource3f(ch->srcnum, AL_POSITION, AL_UnpackVector(origin)); return; } else { CL_GetEntitySoundOrigin(ch->entnum, origin); qalSource3f(ch->srcnum, AL_POSITION, AL_UnpackVector(origin)); return; } } /* * Plays a channel (in the frontends * sense) with OpenAL. */ void AL_PlayChannel(channel_t *ch) { sfxcache_t *sc; float vol; /* Debug */ if (s_show->value > 1) { Com_Printf("%s: %s\n", __func__, ch->sfx->name); } /* Clamp volume */ vol = ch->oal_vol + s_volume->value; if (vol > 1.0f) { vol = 1.0f; } sc = ch->sfx->cache; ch->srcnum = s_srcnums[ch - channels]; qalGetError(); qalSourcef(ch->srcnum, AL_REFERENCE_DISTANCE, SOUND_FULLVOLUME); qalSourcef(ch->srcnum, AL_MAX_DISTANCE, 8192); qalSourcef(ch->srcnum, AL_ROLLOFF_FACTOR, ch->dist_mult * (8192 - SOUND_FULLVOLUME)); qalSourcef(ch->srcnum, AL_GAIN, vol); qalSourcei(ch->srcnum, AL_BUFFER, sc->bufnum); qalSourcei(ch->srcnum, AL_LOOPING, ch->autosound ? AL_TRUE : AL_FALSE); if ((ch->entnum == -1) || (ch->entnum == cl.playernum + 1) || !ch->dist_mult) { /* anything coming from the view entity will always * be full volume and at the listeners position */ qalSource3f(ch->srcnum, AL_POSITION, 0.0f, 0.0f, 0.0f); qalSourcei(ch->srcnum, AL_SOURCE_RELATIVE, AL_TRUE); } else { /* all other sources are *not* relative */ qalSourcei(ch->srcnum, AL_SOURCE_RELATIVE, AL_FALSE); } /* Spatialize it */ AL_Spatialize(ch); /* Play it */ qalSourcePlay(ch->srcnum); /* Do not play broken channels */ if (qalGetError() != AL_NO_ERROR) { AL_StopChannel(ch); } } /* * Stops playback of a "channel" * in the frontends sense. */ void AL_StopChannel(channel_t *ch) { /* Debug output */ if (s_show->value > 1) { Com_Printf("%s: %s\n", __func__, ch->sfx->name); } /* stop it */ qalSourceStop(ch->srcnum); qalSourcei(ch->srcnum, AL_BUFFER, AL_NONE); memset(ch, 0, sizeof(*ch)); } /* * Stops playback of all channels. */ void AL_StopAllChannels(void) { int i; channel_t *ch; ch = channels; /* It doesn't matter if a channel is active or not. */ for (i = 0; i < s_numchannels; i++, ch++) { if (!ch->sfx) { continue; } AL_StopChannel(ch); } s_rawend = 0; /* Remove all pending samples */ AL_StreamDie(); } /* ----------------------------------------------------------------- */ /* * Returns the channel which contains * the looping sound for the entity * "entnum". */ static channel_t * AL_FindLoopingSound(int entnum, sfx_t *sfx) { int i; channel_t *ch; ch = channels; for (i = 0; i < s_numchannels; i++, ch++) { if (!ch->sfx) { continue; } if (!ch->autosound) { continue; } if (ch->entnum != entnum) { continue; } if (ch->sfx != sfx) { continue; } return ch; } return NULL; } /* * Plays an looping sound with OpenAL */ static void AL_AddLoopSounds(void) { int i; int sounds[MAX_EDICTS]; channel_t *ch; sfx_t *sfx; sfxcache_t *sc; int num; entity_state_t *ent; if ((cls.state != ca_active) || cl_paused->value || !s_ambient->value) { return; } S_BuildSoundList(sounds); for (i = 0; i < cl.frame.num_entities; i++) { if (!sounds[i]) { continue; } sfx = cl.sound_precache[sounds[i]]; if (!sfx) { continue; /* bad sound effect */ } sc = sfx->cache; if (!sc) { continue; } num = (cl.frame.parse_entities + i) & (MAX_PARSE_ENTITIES - 1); ent = &cl_parse_entities[num]; ch = AL_FindLoopingSound(ent->number, sfx); if (ch) { ch->autoframe = s_framecount; ch->end = paintedtime + sc->length; continue; } /* allocate a channel */ ch = S_PickChannel(0, 0); if (!ch) { continue; } ch->autosound = true; /* remove next frame */ ch->autoframe = s_framecount; ch->sfx = sfx; ch->entnum = ent->number; ch->master_vol = 1; ch->dist_mult = SOUND_LOOPATTENUATE; ch->end = paintedtime + sc->length; AL_PlayChannel(ch); } } /* * Starts all pending playsounds. */ static void AL_IssuePlaysounds(void) { playsound_t *ps; /* start any playsounds */ while (1) { ps = s_pendingplays.next; if (ps == &s_pendingplays) { break; /* no more pending sounds */ } if (ps->begin > paintedtime) { break; } S_IssuePlaysound(ps); } } /* * Queues raw samples for playback. Used * by the background music an cinematics. */ void AL_RawSamples(int samples, int rate, int width, int channels, byte *data, float volume) { ALuint buffer; ALuint format = 0; /* Work out format */ if (width == 1) { if (channels == 1) { format = AL_FORMAT_MONO8; } else if (channels == 2) { format = AL_FORMAT_STEREO8; } } else if (width == 2) { if (channels == 1) { format = AL_FORMAT_MONO16; } else if (channels == 2) { format = AL_FORMAT_STEREO16; } } /* Create a buffer, and stuff the data into it */ qalGenBuffers(1, &buffer); qalBufferData(buffer, format, (ALvoid *)data, (samples * width * channels), rate); active_buffers++; /* set volume */ if (volume > 1.0f) { volume = 1.0f; } qalSourcef(streamSource, AL_GAIN, volume); /* Shove the data onto the streamSource */ qalSourceQueueBuffers(streamSource, 1, &buffer); /* emulate behavior of S_RawSamples for s_rawend */ s_rawend += samples; } /* * Kills all raw samples still in flight. * This is used to stop music playback * when silence is triggered. */ void AL_UnqueueRawSamples() { AL_StreamDie(); } void oal_update_underwater() { int i; float gain_hf; qboolean update = false; ALuint filter; if (underwaterFilter == 0) return; if (s_underwater->modified) { update = true; s_underwater->modified = false; snd_is_underwater_enabled = ((int)s_underwater->value != 0); } if (s_underwater_gain_hf->modified) { update = true; s_underwater_gain_hf->modified = false; } if (!update) return; gain_hf = s_underwater_gain_hf->value; if (gain_hf < AL_LOWPASS_MIN_GAINHF) gain_hf = AL_LOWPASS_MIN_GAINHF; if (gain_hf > AL_LOWPASS_MAX_GAINHF) gain_hf = AL_LOWPASS_MAX_GAINHF; qalFilterf(underwaterFilter, AL_LOWPASS_GAINHF, gain_hf); if (snd_is_underwater_enabled && snd_is_underwater) filter = underwaterFilter; else filter = 0; for (i = 0; i < s_numchannels; ++i) qalSourcei(s_srcnums[i], AL_DIRECT_FILTER, filter); } /* * Main update function. Called every frame, * performes all necessary calculations. */ void AL_Update(void) { int i; channel_t *ch; vec_t orientation[6]; paintedtime = cls.realtime; /* set listener (player) parameters */ AL_CopyVector(listener_forward, orientation); AL_CopyVector(listener_up, orientation + 3); qalListenerf(AL_GAIN, s_volume->value); qalListenerf(AL_MAX_GAIN, s_openal_maxgain->value); qalDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); qalListener3f(AL_POSITION, AL_UnpackVector(listener_origin)); qalListenerfv(AL_ORIENTATION, orientation); /* update spatialization for dynamic sounds */ ch = channels; for (i = 0; i < s_numchannels; i++, ch++) { if (!ch->sfx) { continue; } if (ch->autosound) { /* autosounds are regenerated fresh each frame */ if (ch->autoframe != s_framecount) { AL_StopChannel(ch); continue; } } else { ALenum state; qalGetError(); qalGetSourcei(ch->srcnum, AL_SOURCE_STATE, &state); if ((qalGetError() != AL_NO_ERROR) || (state == AL_STOPPED)) { AL_StopChannel(ch); continue; } } if (s_show->value) { Com_Printf("%3i %s\n", ch->master_vol, ch->sfx->name); } /* respatialize channel */ AL_Spatialize(ch); } s_framecount++; /* add loopsounds */ AL_AddLoopSounds(); /* add music */ #ifdef OGG OGG_Stream(); #endif AL_StreamUpdate(); AL_IssuePlaysounds(); oal_update_underwater(); } /* ----------------------------------------------------------------- */ /* * Enables underwater effect */ void AL_Underwater() { int i; if (sound_started != SS_OAL) { return; } if (underwaterFilter == 0) return; /* Apply to all sources */ for (i = 0; i < s_numchannels; i++) { qalSourcei(s_srcnums[i], AL_DIRECT_FILTER, underwaterFilter); } } /* * Disables the underwater effect */ void AL_Overwater() { int i; if (sound_started != SS_OAL) { return; } if (underwaterFilter == 0) return; /* Apply to all sources */ for (i = 0; i < s_numchannels; i++) { qalSourcei(s_srcnums[i], AL_DIRECT_FILTER, 0); } } /* ----------------------------------------------------------------- */ /* * Set up the stream sources */ static void AL_InitStreamSource() { qalSource3f(streamSource, AL_POSITION, 0.0, 0.0, 0.0); qalSource3f(streamSource, AL_VELOCITY, 0.0, 0.0, 0.0); qalSource3f(streamSource, AL_DIRECTION, 0.0, 0.0, 0.0); qalSourcef(streamSource, AL_ROLLOFF_FACTOR, 0.0); qalSourcei(streamSource, AL_BUFFER, 0); qalSourcei(streamSource, AL_LOOPING, AL_FALSE); qalSourcei(streamSource, AL_SOURCE_RELATIVE, AL_TRUE); } /* * Set up the underwater filter */ static void AL_InitUnderwaterFilter() { underwaterFilter = 0; if (!(qalGenFilters && qalFilteri && qalFilterf && qalDeleteFilters)) return; /* Generate a filter */ qalGenFilters(1, &underwaterFilter); if (qalGetError() != AL_NO_ERROR) { Com_Printf("Couldn't generate an OpenAL filter!\n"); return; } /* Low pass filter for underwater effect */ qalFilteri(underwaterFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS); if (qalGetError() != AL_NO_ERROR) { Com_Printf("Low pass filter is not supported!\n"); return; } qalFilterf(underwaterFilter, AL_LOWPASS_GAIN, AL_LOWPASS_DEFAULT_GAIN); s_underwater->modified = true; s_underwater_gain_hf->modified = true; } /* * Initializes the OpenAL backend */ qboolean AL_Init(void) { int i; if (!QAL_Init()) { Com_Printf("ERROR: OpenAL failed to initialize.\n"); return false; } s_openal_maxgain = Cvar_Get("s_openal_maxgain", "1.0", CVAR_ARCHIVE); /* check for linear distance extension */ if (!qalIsExtensionPresent("AL_EXT_LINEAR_DISTANCE")) { Com_Printf("ERROR: Required AL_EXT_LINEAR_DISTANCE extension is missing.\n"); QAL_Shutdown(); return false; } /* generate source names */ qalGetError(); qalGenSources(1, &streamSource); if (qalGetError() != AL_NO_ERROR) { Com_Printf("ERROR: Couldn't get a single Source.\n"); QAL_Shutdown(); return false; } else { /* -1 because we already got one channel for streaming */ for (i = 0; i < MAX_CHANNELS - 1; i++) { qalGenSources(1, &s_srcnums[i]); if (qalGetError() != AL_NO_ERROR) { break; } } if (i < MIN_CHANNELS - 1) { Com_Printf("ERROR: Required at least %d sources, but got %d.\n", MIN_CHANNELS, i + 1); QAL_Shutdown(); return false; } } s_numchannels = i; AL_InitStreamSource(); AL_InitUnderwaterFilter(); Com_Printf("Number of OpenAL sources: %d\n\n", s_numchannels); return true; } /* * Shuts the OpenAL backend down */ void AL_Shutdown(void) { Com_Printf("Shutting down OpenAL.\n"); AL_StreamDie(); qalDeleteSources(1, &streamSource); qalDeleteFilters(1, &underwaterFilter); if (s_numchannels) { /* delete source names */ qalDeleteSources(s_numchannels, s_srcnums); memset(s_srcnums, 0, sizeof(s_srcnums)); s_numchannels = 0; } QAL_Shutdown(); } #endif /* USE_OPENAL */ yquake2-QUAKE2_5_32/src/client/cl_network.c0000644000175000017500000003163512615162034021207 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements generic network functions * * ======================================================================= */ #include "header/client.h" #include "../client/sound/header/local.h" void CL_ParseStatusMessage(void); extern cvar_t *msg; extern cvar_t *rcon_client_password; extern cvar_t *rcon_address; extern cvar_t *cl_timeout; /* * 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_ForwardToServer(void) { char *cmd; cmd = Cmd_Argv(0); if ((cls.state <= ca_connected) || (*cmd == '-') || (*cmd == '+')) { Com_Printf("Unknown command \"%s\"\n", cmd); return; } MSG_WriteByte(&cls.netchan.message, clc_stringcmd); SZ_Print(&cls.netchan.message, cmd); if (Cmd_Argc() > 1) { SZ_Print(&cls.netchan.message, " "); SZ_Print(&cls.netchan.message, Cmd_Args()); } } void CL_ForwardToServer_f(void) { if ((cls.state != ca_connected) && (cls.state != ca_active)) { Com_Printf("Can't \"%s\", not connected\n", Cmd_Argv(0)); return; } /* don't forward the first argument */ if (Cmd_Argc() > 1) { MSG_WriteByte(&cls.netchan.message, clc_stringcmd); SZ_Print(&cls.netchan.message, Cmd_Args()); } } /* * Called after an ERR_DROP was thrown */ void CL_Drop(void) { if (cls.state == ca_uninitialized) { return; } if (cls.state == ca_disconnected) { return; } CL_Disconnect(); /* drop loading plaque unless this is the initial game start */ if (cls.disable_servercount != -1) { SCR_EndLoadingPlaque(); /* get rid of loading plaque */ } } /* * We have gotten a challenge from the server, so try and * connect. */ void CL_SendConnectPacket(void) { netadr_t adr; int port; memset(&adr, 0, sizeof(adr)); if (!NET_StringToAdr(cls.servername, &adr)) { Com_Printf("Bad server address\n"); cls.connect_time = 0; return; } if (adr.port == 0) { adr.port = BigShort(PORT_SERVER); } port = Cvar_VariableValue("qport"); userinfo_modified = false; Netchan_OutOfBandPrint(NS_CLIENT, adr, "connect %i %i %i \"%s\"\n", PROTOCOL_VERSION, port, cls.challenge, Cvar_Userinfo()); } /* * Resend a connect message if the last one has timed out */ void CL_CheckForResend(void) { netadr_t adr; /* if the local server is running and we aren't just connect */ if ((cls.state == ca_disconnected) && Com_ServerState()) { cls.state = ca_connecting; Q_strlcpy(cls.servername, "localhost", sizeof(cls.servername)); /* we don't need a challenge on the localhost */ CL_SendConnectPacket(); return; } /* resend if we haven't gotten a reply yet */ if (cls.state != ca_connecting) { return; } if (cls.realtime - cls.connect_time < 3000) { return; } if (!NET_StringToAdr(cls.servername, &adr)) { Com_Printf("Bad server address\n"); cls.state = ca_disconnected; return; } if (adr.port == 0) { adr.port = BigShort(PORT_SERVER); } cls.connect_time = cls.realtime; Com_Printf("Connecting to %s...\n", cls.servername); Netchan_OutOfBandPrint(NS_CLIENT, adr, "getchallenge\n"); } void CL_Connect_f(void) { char *server; if (Cmd_Argc() != 2) { Com_Printf("usage: connect \n"); return; } if (Com_ServerState()) { /* if running a local server, kill it and reissue note: this is connect with the save game system */ SV_Shutdown("Server quit\n", false); } else { CL_Disconnect(); } server = Cmd_Argv(1); NET_Config(true); /* allow remote */ CL_Disconnect(); cls.state = ca_connecting; Q_strlcpy(cls.servername, server, sizeof(cls.servername)); cls.connect_time = -99999; /* HACK: CL_CheckForResend() will fire immediately */ } /* * Send the rest of the command line over as * an unconnected command. */ void CL_Rcon_f(void) { char message[1024]; int i; netadr_t to; if (!rcon_client_password->string) { Com_Printf("You must set 'rcon_password' before\n" "issuing an rcon command.\n"); return; } memset(&to, 0, sizeof(to)); message[0] = (char)255; message[1] = (char)255; message[2] = (char)255; message[3] = (char)255; message[4] = 0; NET_Config(true); /* allow remote */ strcat(message, "rcon "); strcat(message, rcon_client_password->string); strcat(message, " "); for (i = 1; i < Cmd_Argc(); i++) { strcat(message, Cmd_Argv(i)); strcat(message, " "); } if (cls.state >= ca_connected) { to = cls.netchan.remote_address; } else { if (!strlen(rcon_address->string)) { Com_Printf("You must either be connected,\n" "or set the 'rcon_address' cvar\n" "to issue rcon commands\n"); return; } NET_StringToAdr(rcon_address->string, &to); if (to.port == 0) { to.port = BigShort(PORT_SERVER); } } NET_SendPacket(NS_CLIENT, strlen(message) + 1, message, to); } /* * Goes from a connected state to full screen * console state Sends a disconnect message to * the server This is also called on Com_Error, so * it shouldn't cause any errors */ void CL_Disconnect(void) { byte final[32]; if (cls.state == ca_disconnected) { return; } if (cl_timedemo && cl_timedemo->value) { int time; time = Sys_Milliseconds() - cl.timedemo_start; if (time > 0) { Com_Printf("%i frames, %3.1f seconds: %3.1f fps\n", cl.timedemo_frames, time / 1000.0, cl.timedemo_frames * 1000.0 / time); } } VectorClear(cl.refdef.blend); R_SetPalette(NULL); M_ForceMenuOff(); cls.connect_time = 0; SCR_StopCinematic(); #ifdef OGG OGG_Stop(); #endif #ifdef CDA CDAudio_Stop(); #endif if (cls.demorecording) { CL_Stop_f(); } /* send a disconnect message to the server */ final[0] = clc_stringcmd; strcpy((char *)final + 1, "disconnect"); Netchan_Transmit(&cls.netchan, strlen((const char *)final), final); Netchan_Transmit(&cls.netchan, strlen((const char *)final), final); Netchan_Transmit(&cls.netchan, strlen((const char *)final), final); CL_ClearState(); /* stop file download */ if (cls.download) { fclose(cls.download); cls.download = NULL; } cls.state = ca_disconnected; snd_is_underwater = false; } void CL_Disconnect_f(void) { Com_Error(ERR_DROP, "Disconnected from server"); } /* * packet * * Contents allows \n escape character */ void CL_Packet_f(void) { char send[2048]; int i, l; char *in, *out; netadr_t adr; if (Cmd_Argc() != 3) { Com_Printf("packet \n"); return; } NET_Config(true); /* allow remote */ if (!NET_StringToAdr(Cmd_Argv(1), &adr)) { Com_Printf("Bad address\n"); return; } if (!adr.port) { adr.port = BigShort(PORT_SERVER); } in = Cmd_Argv(2); out = send + 4; send[0] = send[1] = send[2] = send[3] = (char)0xff; l = strlen(in); for (i = 0; i < l; i++) { if ((in[i] == '\\') && (in[i + 1] == 'n')) { *out++ = '\n'; i++; } else { *out++ = in[i]; } } *out = 0; NET_SendPacket(NS_CLIENT, out - send, send, adr); } /* * Just sent as a hint to the client that they should * drop to full console */ void CL_Changing_f(void) { /* if we are downloading, we don't change! This so we don't suddenly stop downloading a map */ if (cls.download) { return; } SCR_BeginLoadingPlaque(); cls.state = ca_connected; /* not active anymore, but not disconnected */ Com_Printf("\nChanging map...\n"); } /* * The server is changing levels */ void CL_Reconnect_f(void) { /* if we are downloading, we don't change! This so we don't suddenly stop downloading a map */ if (cls.download) { return; } S_StopAllSounds(); if (cls.state == ca_connected) { Com_Printf("reconnecting...\n"); cls.state = ca_connected; MSG_WriteChar(&cls.netchan.message, clc_stringcmd); MSG_WriteString(&cls.netchan.message, "new"); return; } if (*cls.servername) { if (cls.state >= ca_connected) { CL_Disconnect(); cls.connect_time = cls.realtime - 1500; } else { cls.connect_time = -99999; /* Hack: fire immediately */ } cls.state = ca_connecting; Com_Printf("reconnecting...\n"); } } void CL_PingServers_f(void) { int i; netadr_t adr; char name[32]; char *adrstring; cvar_t *noudp; cvar_t *noipx; NET_Config(true); /* allow remote but do we even need lokal pings? */ /* send a broadcast packet */ Com_Printf("pinging broadcast...\n"); noudp = Cvar_Get("noudp", "0", CVAR_NOSET); if (!noudp->value) { adr.type = NA_BROADCAST; adr.port = BigShort(PORT_SERVER); Netchan_OutOfBandPrint(NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION)); Com_Printf("pinging multicast...\n"); adr.type = NA_MULTICAST6; adr.port = BigShort(PORT_SERVER); Netchan_OutOfBandPrint(NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION)); } noipx = Cvar_Get("noipx", "0", CVAR_NOSET); if (!noipx->value) { adr.type = NA_BROADCAST_IPX; adr.port = BigShort(PORT_SERVER); Netchan_OutOfBandPrint(NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION)); } /* send a packet to each address book entry */ for (i = 0; i < 16; i++) { Com_sprintf(name, sizeof(name), "adr%i", i); adrstring = (char *)Cvar_VariableString(name); if (!adrstring || !adrstring[0]) { continue; } Com_Printf("pinging %s...\n", adrstring); if (!NET_StringToAdr(adrstring, &adr)) { Com_Printf("Bad address: %s\n", adrstring); continue; } if (!adr.port) { adr.port = BigShort(PORT_SERVER); } Netchan_OutOfBandPrint(NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION)); } } /* * Responses to broadcasts, etc */ void CL_ConnectionlessPacket(void) { char *s; char *c; MSG_BeginReading(&net_message); MSG_ReadLong(&net_message); /* skip the -1 */ s = MSG_ReadStringLine(&net_message); Cmd_TokenizeString(s, false); c = Cmd_Argv(0); Com_Printf("%s: %s\n", NET_AdrToString(net_from), c); /* server connection */ if (!strcmp(c, "client_connect")) { if (cls.state == ca_connected) { Com_Printf("Dup connect received. Ignored.\n"); return; } Netchan_Setup(NS_CLIENT, &cls.netchan, net_from, cls.quakePort); MSG_WriteChar(&cls.netchan.message, clc_stringcmd); MSG_WriteString(&cls.netchan.message, "new"); cls.state = ca_connected; return; } /* server responding to a status broadcast */ if (!strcmp(c, "info")) { CL_ParseStatusMessage(); return; } /* remote command from gui front end */ if (!strcmp(c, "cmd")) { if (!NET_IsLocalAddress(net_from)) { Com_Printf("Command packet from remote host. Ignored.\n"); return; } s = MSG_ReadString(&net_message); Cbuf_AddText(s); Cbuf_AddText("\n"); return; } /* print command from somewhere */ if (!strcmp(c, "print")) { s = MSG_ReadString(&net_message); Com_Printf("%s", s); return; } /* ping from somewhere */ if (!strcmp(c, "ping")) { Netchan_OutOfBandPrint(NS_CLIENT, net_from, "ack"); return; } /* challenge from the server we are connecting to */ if (!strcmp(c, "challenge")) { cls.challenge = (int)strtol(Cmd_Argv(1), (char **)NULL, 10); CL_SendConnectPacket(); return; } /* echo request from server */ if (!strcmp(c, "echo")) { Netchan_OutOfBandPrint(NS_CLIENT, net_from, "%s", Cmd_Argv(1)); return; } Com_Printf("Unknown command.\n"); } void CL_ReadPackets(void) { while (NET_GetPacket(NS_CLIENT, &net_from, &net_message)) { /* remote command packet */ if (*(int *)net_message.data == -1) { CL_ConnectionlessPacket(); continue; } if ((cls.state == ca_disconnected) || (cls.state == ca_connecting)) { continue; /* dump it if not connected */ } if (net_message.cursize < 8) { Com_Printf("%s: Runt packet\n", NET_AdrToString(net_from)); continue; } /* packet from server */ if (!NET_CompareAdr(net_from, cls.netchan.remote_address)) { Com_DPrintf("%s:sequenced packet without connection\n", NET_AdrToString(net_from)); continue; } if (!Netchan_Process(&cls.netchan, &net_message)) { continue; /* wasn't accepted for some reason */ } CL_ParseServerMessage(); } /* check timeout */ if ((cls.state >= ca_connected) && (cls.realtime - cls.netchan.last_received > cl_timeout->value * 1000)) { if (++cl.timeoutcount > 5) { Com_Printf("\nServer connection timed out.\n"); CL_Disconnect(); return; } } else { cl.timeoutcount = 0; } } yquake2-QUAKE2_5_32/src/client/cl_main.c0000644000175000017500000004426412615162034020444 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 is the clients main loop as well as some miscelangelous utility * and support functions * * ======================================================================= */ #include "header/client.h" #include "../backends/generic/header/input.h" void CL_ForwardToServer_f(void); void CL_Changing_f(void); void CL_Reconnect_f(void); void CL_Connect_f(void); void CL_Rcon_f(void); void CL_CheckForResend(void); cvar_t *freelook; cvar_t *adr0; cvar_t *adr1; cvar_t *adr2; cvar_t *adr3; cvar_t *adr4; cvar_t *adr5; cvar_t *adr6; cvar_t *adr7; cvar_t *adr8; cvar_t *rcon_client_password; cvar_t *rcon_address; cvar_t *cl_noskins; cvar_t *cl_autoskins; cvar_t *cl_footsteps; cvar_t *cl_timeout; cvar_t *cl_predict; cvar_t *cl_maxfps; cvar_t *cl_drawfps; cvar_t *cl_gun; cvar_t *cl_add_particles; cvar_t *cl_add_lights; cvar_t *cl_add_entities; cvar_t *cl_add_blend; cvar_t *cl_shownet; cvar_t *cl_showmiss; cvar_t *cl_showclamp; cvar_t *cl_paused; cvar_t *cl_timedemo; cvar_t *lookspring; cvar_t *lookstrafe; cvar_t *sensitivity; cvar_t *m_pitch; cvar_t *m_yaw; cvar_t *m_forward; cvar_t *m_side; cvar_t *cl_lightlevel; /* userinfo */ cvar_t *info_password; cvar_t *info_spectator; cvar_t *name; cvar_t *skin; cvar_t *rate; cvar_t *fov; cvar_t *horplus; cvar_t *windowed_mouse; cvar_t *msg; cvar_t *hand; cvar_t *gender; cvar_t *gender_auto; cvar_t *cl_vwep; client_static_t cls; client_state_t cl; centity_t cl_entities[MAX_EDICTS]; entity_state_t cl_parse_entities[MAX_PARSE_ENTITIES]; extern cvar_t *allow_download; extern cvar_t *allow_download_players; extern cvar_t *allow_download_models; extern cvar_t *allow_download_sounds; extern cvar_t *allow_download_maps; /* * Dumps the current net message, prefixed by the length */ void CL_WriteDemoMessage(void) { int len, swlen; /* the first eight bytes are just packet sequencing stuff */ len = net_message.cursize - 8; swlen = LittleLong(len); fwrite(&swlen, 4, 1, cls.demofile); fwrite(net_message.data + 8, len, 1, cls.demofile); } /* * stop recording a demo */ void CL_Stop_f(void) { int len; if (!cls.demorecording) { Com_Printf("Not recording a demo.\n"); return; } len = -1; fwrite(&len, 4, 1, cls.demofile); fclose(cls.demofile); cls.demofile = NULL; cls.demorecording = false; Com_Printf("Stopped demo.\n"); } /* * record * Begins recording a demo from the current position */ void CL_Record_f(void) { char name[MAX_OSPATH]; byte buf_data[MAX_MSGLEN]; sizebuf_t buf; int i; int len; entity_state_t *ent; entity_state_t nullstate; if (Cmd_Argc() != 2) { Com_Printf("record \n"); return; } if (cls.demorecording) { Com_Printf("Already recording.\n"); return; } if (cls.state != ca_active) { Com_Printf("You must be in a level to record.\n"); return; } Com_sprintf(name, sizeof(name), "%s/demos/%s.dm2", FS_Gamedir(), Cmd_Argv(1)); Com_Printf("recording to %s.\n", name); FS_CreatePath(name); cls.demofile = fopen(name, "wb"); if (!cls.demofile) { Com_Printf("ERROR: couldn't open.\n"); return; } cls.demorecording = true; /* don't start saving messages until a non-delta compressed message is received */ cls.demowaiting = true; /* write out messages to hold the startup information */ SZ_Init(&buf, buf_data, sizeof(buf_data)); /* send the serverdata */ MSG_WriteByte(&buf, svc_serverdata); MSG_WriteLong(&buf, PROTOCOL_VERSION); MSG_WriteLong(&buf, 0x10000 + cl.servercount); MSG_WriteByte(&buf, 1); /* demos are always attract loops */ MSG_WriteString(&buf, cl.gamedir); MSG_WriteShort(&buf, cl.playernum); MSG_WriteString(&buf, cl.configstrings[CS_NAME]); /* configstrings */ for (i = 0; i < MAX_CONFIGSTRINGS; i++) { if (cl.configstrings[i][0]) { if (buf.cursize + strlen(cl.configstrings[i]) + 32 > buf.maxsize) { len = LittleLong(buf.cursize); fwrite(&len, 4, 1, cls.demofile); fwrite(buf.data, buf.cursize, 1, cls.demofile); buf.cursize = 0; } MSG_WriteByte(&buf, svc_configstring); MSG_WriteShort(&buf, i); MSG_WriteString(&buf, cl.configstrings[i]); } } /* baselines */ memset(&nullstate, 0, sizeof(nullstate)); for (i = 0; i < MAX_EDICTS; i++) { ent = &cl_entities[i].baseline; if (!ent->modelindex) { continue; } if (buf.cursize + 64 > buf.maxsize) { len = LittleLong(buf.cursize); fwrite(&len, 4, 1, cls.demofile); fwrite(buf.data, buf.cursize, 1, cls.demofile); buf.cursize = 0; } MSG_WriteByte(&buf, svc_spawnbaseline); MSG_WriteDeltaEntity(&nullstate, &cl_entities[i].baseline, &buf, true, true); } MSG_WriteByte(&buf, svc_stufftext); MSG_WriteString(&buf, "precache\n"); /* write it to the demo file */ len = LittleLong(buf.cursize); fwrite(&len, 4, 1, cls.demofile); fwrite(buf.data, buf.cursize, 1, cls.demofile); } void CL_Setenv_f(void) { int argc = Cmd_Argc(); if (argc > 2) { char buffer[1000]; int i; strcpy(buffer, Cmd_Argv(1)); strcat(buffer, "="); for (i = 2; i < argc; i++) { strcat(buffer, Cmd_Argv(i)); strcat(buffer, " "); } putenv(buffer); } else if (argc == 2) { char *env = getenv(Cmd_Argv(1)); if (env) { Com_Printf("%s=%s\n", Cmd_Argv(1), env); } else { Com_Printf("%s undefined\n", Cmd_Argv(1)); } } } void CL_Pause_f(void) { /* never pause in multiplayer */ if ((Cvar_VariableValue("maxclients") > 1) || !Com_ServerState()) { Cvar_SetValue("paused", 0); return; } Cvar_SetValue("paused", !cl_paused->value); } void CL_Quit_f(void) { CL_Disconnect(); Com_Quit(); } void CL_ClearState(void) { S_StopAllSounds(); CL_ClearEffects(); CL_ClearTEnts(); /* wipe the entire cl structure */ memset(&cl, 0, sizeof(cl)); memset(&cl_entities, 0, sizeof(cl_entities)); SZ_Clear(&cls.netchan.message); } /* * Handle a reply from a ping */ void CL_ParseStatusMessage(void) { char *s; s = MSG_ReadString(&net_message); Com_Printf("%s\n", s); M_AddToServerList(net_from, s); } /* * Load or download any custom player skins and models */ void CL_Skins_f(void) { int i; for (i = 0; i < MAX_CLIENTS; i++) { if (!cl.configstrings[CS_PLAYERSKINS + i][0]) { continue; } Com_Printf("client %i: %s\n", i, cl.configstrings[CS_PLAYERSKINS + i]); SCR_UpdateScreen(); Sys_SendKeyEvents(); /* pump message loop */ CL_ParseClientinfo(i); } } /* This fixes some problems with wrong tagged models and skins */ void CL_FixUpGender(void) { char *p; char sk[80]; if (gender_auto->value) { if (gender->modified) { /* was set directly, don't override the user */ gender->modified = false; return; } Q_strlcpy(sk, skin->string, sizeof(sk)); if ((p = strchr(sk, '/')) != NULL) { *p = 0; } if ((Q_stricmp(sk, "male") == 0) || (Q_stricmp(sk, "cyborg") == 0)) { Cvar_Set("gender", "male"); } else if ((Q_stricmp(sk, "female") == 0) || (Q_stricmp(sk, "crackhor") == 0)) { Cvar_Set("gender", "female"); } else { Cvar_Set("gender", "none"); } gender->modified = false; } } void CL_Userinfo_f(void) { Com_Printf("User info settings:\n"); Info_Print(Cvar_Userinfo()); } /* * Restart the sound subsystem so it can pick up * new parameters and flush all sounds */ void CL_Snd_Restart_f(void) { S_Shutdown(); S_Init(); CL_RegisterSounds(); } int precache_check; int precache_spawncount; int precache_tex; int precache_model_skin; byte *precache_model; /* * The server will send this command right * before allowing the client into the server */ void CL_Precache_f(void) { /* Yet another hack to let old demos work */ if (Cmd_Argc() < 2) { unsigned map_checksum; /* for detecting cheater maps */ CM_LoadMap(cl.configstrings[CS_MODELS + 1], true, &map_checksum); CL_RegisterSounds(); CL_PrepRefresh(); return; } precache_check = CS_MODELS; precache_spawncount = (int)strtol(Cmd_Argv(1), (char **)NULL, 10); precache_model = 0; precache_model_skin = 0; CL_RequestNextDownload(); } void CL_InitLocal(void) { cls.state = ca_disconnected; cls.realtime = Sys_Milliseconds(); CL_InitInput(); adr0 = Cvar_Get("adr0", "", CVAR_ARCHIVE); adr1 = Cvar_Get("adr1", "", CVAR_ARCHIVE); adr2 = Cvar_Get("adr2", "", CVAR_ARCHIVE); adr3 = Cvar_Get("adr3", "", CVAR_ARCHIVE); adr4 = Cvar_Get("adr4", "", CVAR_ARCHIVE); adr5 = Cvar_Get("adr5", "", CVAR_ARCHIVE); adr6 = Cvar_Get("adr6", "", CVAR_ARCHIVE); adr7 = Cvar_Get("adr7", "", CVAR_ARCHIVE); adr8 = Cvar_Get("adr8", "", CVAR_ARCHIVE); cin_force43 = Cvar_Get("cin_force43", "1", 0); /* register our variables */ cl_add_blend = Cvar_Get("cl_blend", "1", 0); cl_add_lights = Cvar_Get("cl_lights", "1", 0); cl_add_particles = Cvar_Get("cl_particles", "1", 0); cl_add_entities = Cvar_Get("cl_entities", "1", 0); cl_gun = Cvar_Get("cl_gun", "2", CVAR_ARCHIVE); cl_footsteps = Cvar_Get("cl_footsteps", "1", 0); cl_noskins = Cvar_Get("cl_noskins", "0", 0); cl_autoskins = Cvar_Get("cl_autoskins", "0", 0); cl_predict = Cvar_Get("cl_predict", "1", 0); cl_maxfps = Cvar_Get("cl_maxfps", "95", CVAR_ARCHIVE); cl_drawfps = Cvar_Get("cl_drawfps", "0", CVAR_ARCHIVE); cl_upspeed = Cvar_Get("cl_upspeed", "200", 0); cl_forwardspeed = Cvar_Get("cl_forwardspeed", "200", 0); cl_sidespeed = Cvar_Get("cl_sidespeed", "200", 0); cl_yawspeed = Cvar_Get("cl_yawspeed", "140", 0); cl_pitchspeed = Cvar_Get("cl_pitchspeed", "150", 0); cl_anglespeedkey = Cvar_Get("cl_anglespeedkey", "1.5", 0); cl_run = Cvar_Get("cl_run", "0", CVAR_ARCHIVE); freelook = Cvar_Get("freelook", "1", CVAR_ARCHIVE); lookspring = Cvar_Get("lookspring", "0", CVAR_ARCHIVE); lookstrafe = Cvar_Get("lookstrafe", "0", CVAR_ARCHIVE); sensitivity = Cvar_Get("sensitivity", "3", CVAR_ARCHIVE); m_pitch = Cvar_Get("m_pitch", "0.022", CVAR_ARCHIVE); m_yaw = Cvar_Get("m_yaw", "0.022", 0); m_forward = Cvar_Get("m_forward", "1", 0); m_side = Cvar_Get("m_side", "1", 0); cl_shownet = Cvar_Get("cl_shownet", "0", 0); cl_showmiss = Cvar_Get("cl_showmiss", "0", 0); cl_showclamp = Cvar_Get("showclamp", "0", 0); cl_timeout = Cvar_Get("cl_timeout", "120", 0); cl_paused = Cvar_Get("paused", "0", 0); cl_timedemo = Cvar_Get("timedemo", "0", 0); rcon_client_password = Cvar_Get("rcon_password", "", 0); rcon_address = Cvar_Get("rcon_address", "", 0); cl_lightlevel = Cvar_Get("gl_lightlevel", "0", 0); /* userinfo */ info_password = Cvar_Get("password", "", CVAR_USERINFO); info_spectator = Cvar_Get("spectator", "0", CVAR_USERINFO); name = Cvar_Get("name", "unnamed", CVAR_USERINFO | CVAR_ARCHIVE); skin = Cvar_Get("skin", "male/grunt", CVAR_USERINFO | CVAR_ARCHIVE); rate = Cvar_Get("rate", "8000", CVAR_USERINFO | CVAR_ARCHIVE); msg = Cvar_Get("msg", "1", CVAR_USERINFO | CVAR_ARCHIVE); hand = Cvar_Get("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE); fov = Cvar_Get("fov", "90", CVAR_USERINFO | CVAR_ARCHIVE); horplus = Cvar_Get("horplus", "1", CVAR_ARCHIVE); windowed_mouse = Cvar_Get("windowed_mouse", "1", CVAR_USERINFO | CVAR_ARCHIVE); gender = Cvar_Get("gender", "male", CVAR_USERINFO | CVAR_ARCHIVE); gender_auto = Cvar_Get("gender_auto", "1", CVAR_ARCHIVE); gender->modified = false; cl_vwep = Cvar_Get("cl_vwep", "1", CVAR_ARCHIVE); /* register our commands */ Cmd_AddCommand("cmd", CL_ForwardToServer_f); Cmd_AddCommand("pause", CL_Pause_f); Cmd_AddCommand("pingservers", CL_PingServers_f); Cmd_AddCommand("skins", CL_Skins_f); Cmd_AddCommand("userinfo", CL_Userinfo_f); Cmd_AddCommand("snd_restart", CL_Snd_Restart_f); Cmd_AddCommand("changing", CL_Changing_f); Cmd_AddCommand("disconnect", CL_Disconnect_f); Cmd_AddCommand("record", CL_Record_f); Cmd_AddCommand("stop", CL_Stop_f); Cmd_AddCommand("quit", CL_Quit_f); Cmd_AddCommand("connect", CL_Connect_f); Cmd_AddCommand("reconnect", CL_Reconnect_f); Cmd_AddCommand("rcon", CL_Rcon_f); Cmd_AddCommand("setenv", CL_Setenv_f); Cmd_AddCommand("precache", CL_Precache_f); Cmd_AddCommand("download", CL_Download_f); /* forward to server commands * the only thing this does is allow command completion * to work -- all unknown commands are automatically * forwarded to the server */ Cmd_AddCommand("wave", NULL); Cmd_AddCommand("inven", NULL); Cmd_AddCommand("kill", NULL); Cmd_AddCommand("use", NULL); Cmd_AddCommand("drop", NULL); Cmd_AddCommand("say", NULL); Cmd_AddCommand("say_team", NULL); Cmd_AddCommand("info", NULL); Cmd_AddCommand("prog", NULL); Cmd_AddCommand("give", NULL); Cmd_AddCommand("god", NULL); Cmd_AddCommand("notarget", NULL); Cmd_AddCommand("noclip", NULL); Cmd_AddCommand("invuse", NULL); Cmd_AddCommand("invprev", NULL); Cmd_AddCommand("invnext", NULL); Cmd_AddCommand("invdrop", NULL); Cmd_AddCommand("weapnext", NULL); Cmd_AddCommand("weapprev", NULL); } /* * Writes key bindings and archived cvars to config.cfg */ void CL_WriteConfiguration(void) { FILE *f; char path[MAX_OSPATH]; if (cls.state == ca_uninitialized) { return; } Com_sprintf(path, sizeof(path), "%s/config.cfg", FS_Gamedir()); f = fopen(path, "w"); if (!f) { Com_Printf("Couldn't write config.cfg.\n"); return; } fprintf(f, "// generated by quake, do not modify\n"); Key_WriteBindings(f); fclose(f); Cvar_WriteVariables(path); } typedef struct { char *name; char *value; cvar_t *var; } cheatvar_t; cheatvar_t cheatvars[] = { {"timescale", "1"}, {"timedemo", "0"}, {"gl_drawworld", "1"}, {"cl_testlights", "0"}, {"gl_fullbright", "0"}, {"gl_drawflat", "0"}, {"paused", "0"}, {"fixedtime", "0"}, {"sw_draworder", "0"}, {"gl_lightmap", "0"}, {"gl_saturatelighting", "0"}, {NULL, NULL} }; int numcheatvars; void CL_FixCvarCheats(void) { int i; cheatvar_t *var; if (!strcmp(cl.configstrings[CS_MAXCLIENTS], "1") || !cl.configstrings[CS_MAXCLIENTS][0]) { return; /* single player can cheat */ } /* find all the cvars if we haven't done it yet */ if (!numcheatvars) { while (cheatvars[numcheatvars].name) { cheatvars[numcheatvars].var = Cvar_Get(cheatvars[numcheatvars].name, cheatvars[numcheatvars].value, 0); numcheatvars++; } } /* make sure they are all set to the proper values */ for (i = 0, var = cheatvars; i < numcheatvars; i++, var++) { if (strcmp(var->var->string, var->value)) { Cvar_Set(var->name, var->value); } } } void CL_UpdateWindowedMouse(void) { if (cls.disable_screen) { return; } if (cls.key_dest == key_menu || cls.key_dest == key_console || (cls.key_dest == key_game && (cls.state != ca_active || !cl.refresh_prepped))) { if (windowed_mouse->value) { Cvar_SetValue("windowed_mouse", 0); } } else { if (!windowed_mouse->value) { Cvar_SetValue("windowed_mouse", 1); } } } void CL_SendCommand(void) { /* update windowed_mouse cvar */ CL_UpdateWindowedMouse(); /* get new key events */ Sys_SendKeyEvents(); /* process console commands */ Cbuf_Execute(); /* fix any cheating cvars */ CL_FixCvarCheats(); /* send intentions now */ CL_SendCmd(); /* resend a connection request if necessary */ CL_CheckForResend(); } void CL_Frame(int msec) { static int extratime; static int lasttimecalled; if (dedicated->value) { return; } extratime += msec; if (!cl_timedemo->value) { if ((cls.state == ca_connected) && (extratime < 100)) { return; /* don't flood packets out while connecting */ } if (extratime < 1000 / cl_maxfps->value) { return; /* framerate is too high */ } } /* decide the simulation time */ cls.frametime = extratime / 1000.0; cl.time += extratime; cls.realtime = curtime; extratime = 0; if (cls.frametime > (1.0 / 5)) { cls.frametime = (1.0 / 5); } /* if in the debugger last frame, don't timeout */ if (msec > 5000) { cls.netchan.last_received = Sys_Milliseconds(); } /* fetch results from server */ CL_ReadPackets(); /* send a new command message to the server */ CL_SendCommand(); /* predict all unacknowledged movements */ CL_PredictMovement(); /* allow renderer DLL change */ VID_CheckChanges(); if (!cl.refresh_prepped && (cls.state == ca_active)) { CL_PrepRefresh(); } /* update the screen */ if (host_speeds->value) { time_before_ref = Sys_Milliseconds(); } SCR_UpdateScreen(); if (host_speeds->value) { time_after_ref = Sys_Milliseconds(); } /* update audio */ S_Update(cl.refdef.vieworg, cl.v_forward, cl.v_right, cl.v_up); #ifdef CDA CDAudio_Update(); #endif /* advance local effects for next frame */ CL_RunDLights(); CL_RunLightStyles(); SCR_RunCinematic(); SCR_RunConsole(); cls.framecount++; if (log_stats->value) { if (cls.state == ca_active) { if (!lasttimecalled) { lasttimecalled = Sys_Milliseconds(); if (log_stats_file) { fprintf(log_stats_file, "0\n"); } } else { int now = Sys_Milliseconds(); if (log_stats_file) { fprintf(log_stats_file, "%d\n", now - lasttimecalled); } lasttimecalled = now; } } } } void CL_Init(void) { if (dedicated->value) { return; /* nothing running on the client */ } /* all archived variables will now be loaded */ Con_Init(); S_Init(); SCR_Init(); VID_Init(); IN_Init(); V_Init(); net_message.data = net_message_buffer; net_message.maxsize = sizeof(net_message_buffer); M_Init(); cls.disable_screen = true; /* don't draw yet */ #ifdef CDA CDAudio_Init(); #endif CL_InitLocal(); FS_ExecAutoexec(); Cbuf_Execute(); Key_ReadConsoleHistory(); } void CL_Shutdown(void) { static qboolean isdown = false; if (isdown) { printf("recursive shutdown\n"); return; } isdown = true; CL_WriteConfiguration(); Key_WriteConsoleHistory(); #ifdef CDA CDAudio_Shutdown(); #endif #ifdef OGG OGG_Stop(); #endif S_Shutdown(); IN_Shutdown(); VID_Shutdown(); } yquake2-QUAKE2_5_32/src/client/cl_effects.c0000644000175000017500000016224112615162034021133 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements all specialized client side effects. E.g. * weapon effects, enemy effects, flash, etc. * * ======================================================================= */ #include "header/client.h" void CL_LogoutEffect(vec3_t org, int type); void CL_ItemRespawnParticles(vec3_t org); void CL_ClearLightStyles(void); void CL_ClearDlights(void); void CL_ClearParticles(void); static vec3_t avelocities[NUMVERTEXNORMALS]; extern struct model_s *cl_mod_smoke; extern struct model_s *cl_mod_flash; extern cparticle_t *active_particles, *free_particles; void CL_AddMuzzleFlash(void) { vec3_t fv, rv; cdlight_t *dl; int i, weapon; centity_t *pl; int silenced; float volume; char soundname[64]; i = MSG_ReadShort(&net_message); if ((i < 1) || (i >= MAX_EDICTS)) { Com_Error(ERR_DROP, "CL_AddMuzzleFlash: bad entity"); } weapon = MSG_ReadByte(&net_message); silenced = weapon & MZ_SILENCED; weapon &= ~MZ_SILENCED; pl = &cl_entities[i]; dl = CL_AllocDlight(i); VectorCopy(pl->current.origin, dl->origin); AngleVectors(pl->current.angles, fv, rv, NULL); VectorMA(dl->origin, 18, fv, dl->origin); VectorMA(dl->origin, 16, rv, dl->origin); if (silenced) { dl->radius = 100.0f + (randk() & 31); } else { dl->radius = 200.0f + (randk() & 31); } dl->minlight = 32; dl->die = cl.time; if (silenced) { volume = 0.2f; } else { volume = 1; } switch (weapon) { case MZ_BLASTER: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/blastf1a.wav"), volume, ATTN_NORM, 0); break; case MZ_BLUEHYPERBLASTER: dl->color[0] = 0; dl->color[1] = 0; dl->color[2] = 1; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/hyprbf1a.wav"), volume, ATTN_NORM, 0); break; case MZ_HYPERBLASTER: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/hyprbf1a.wav"), volume, ATTN_NORM, 0); break; case MZ_MACHINEGUN: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%lub.wav", (randk() % 5) + 1); S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound( soundname), volume, ATTN_NORM, 0); break; case MZ_SHOTGUN: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/shotgf1b.wav"), volume, ATTN_NORM, 0); S_StartSound(NULL, i, CHAN_AUTO, S_RegisterSound("weapons/shotgr1b.wav"), volume, ATTN_NORM, 0.1f); break; case MZ_SSHOTGUN: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/sshotf1b.wav"), volume, ATTN_NORM, 0); break; case MZ_CHAINGUN1: dl->radius = 200.0f + (randk() & 31); dl->color[0] = 1; dl->color[1] = 0.25; dl->color[2] = 0; Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%lub.wav", (randk() % 5) + 1); S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound( soundname), volume, ATTN_NORM, 0); break; case MZ_CHAINGUN2: dl->radius = 225.0f + (randk() & 31); dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0; dl->die = cl.time + 0.1; /* long delay */ Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%lub.wav", (randk() % 5) + 1); S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound( soundname), volume, ATTN_NORM, 0); Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%lub.wav", (randk() % 5) + 1); S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound( soundname), volume, ATTN_NORM, 0.05); break; case MZ_CHAINGUN3: dl->radius = 250.0f + (randk() & 31); dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; dl->die = cl.time + 0.1; /* long delay */ Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%lub.wav", (randk() % 5) + 1); S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound( soundname), volume, ATTN_NORM, 0); Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%lub.wav", (randk() % 5) + 1); S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound( soundname), volume, ATTN_NORM, 0.033f); Com_sprintf(soundname, sizeof(soundname), "weapons/machgf%lub.wav", (randk() % 5) + 1); S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound( soundname), volume, ATTN_NORM, 0.066f); break; case MZ_RAILGUN: dl->color[0] = 0.5; dl->color[1] = 0.5; dl->color[2] = 1.0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/railgf1a.wav"), volume, ATTN_NORM, 0); break; case MZ_ROCKET: dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0.2; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/rocklf1a.wav"), volume, ATTN_NORM, 0); S_StartSound(NULL, i, CHAN_AUTO, S_RegisterSound("weapons/rocklr1b.wav"), volume, ATTN_NORM, 0.1f); break; case MZ_GRENADE: dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), volume, ATTN_NORM, 0); S_StartSound(NULL, i, CHAN_AUTO, S_RegisterSound("weapons/grenlr1b.wav"), volume, ATTN_NORM, 0.1f); break; case MZ_BFG: dl->color[0] = 0; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/bfg__f1y.wav"), volume, ATTN_NORM, 0); break; case MZ_LOGIN: dl->color[0] = 0; dl->color[1] = 1; dl->color[2] = 0; dl->die = cl.time + 1; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0); CL_LogoutEffect(pl->current.origin, weapon); break; case MZ_LOGOUT: dl->color[0] = 1; dl->color[1] = 0; dl->color[2] = 0; dl->die = cl.time + 1; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0); CL_LogoutEffect(pl->current.origin, weapon); break; case MZ_RESPAWN: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; dl->die = cl.time + 1.0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/grenlf1a.wav"), 1, ATTN_NORM, 0); CL_LogoutEffect(pl->current.origin, weapon); break; case MZ_PHALANX: dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0.5; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/plasshot.wav"), volume, ATTN_NORM, 0); break; case MZ_IONRIPPER: dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0.5; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/rippfire.wav"), volume, ATTN_NORM, 0); break; case MZ_ETF_RIFLE: dl->color[0] = 0.9f; dl->color[1] = 0.7f; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/nail1.wav"), volume, ATTN_NORM, 0); break; case MZ_SHOTGUN2: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/shotg2.wav"), volume, ATTN_NORM, 0); break; case MZ_HEATBEAM: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; dl->die = cl.time + 100; break; case MZ_BLASTER2: dl->color[0] = 0; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/blastf1a.wav"), volume, ATTN_NORM, 0); break; case MZ_TRACKER: /* negative flashes handled the same in gl/soft until CL_AddDLights */ dl->color[0] = -1; dl->color[1] = -1; dl->color[2] = -1; S_StartSound(NULL, i, CHAN_WEAPON, S_RegisterSound("weapons/disint2.wav"), volume, ATTN_NORM, 0); break; case MZ_NUKE1: dl->color[0] = 1; dl->color[1] = 0; dl->color[2] = 0; dl->die = cl.time + 100; break; case MZ_NUKE2: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; dl->die = cl.time + 100; break; case MZ_NUKE4: dl->color[0] = 0; dl->color[1] = 0; dl->color[2] = 1; dl->die = cl.time + 100; break; case MZ_NUKE8: dl->color[0] = 0; dl->color[1] = 1; dl->color[2] = 1; dl->die = cl.time + 100; break; } } void CL_AddMuzzleFlash2(void) { int ent; vec3_t origin; unsigned flash_number; cdlight_t *dl; vec3_t forward, right; char soundname[64]; ent = MSG_ReadShort(&net_message); if ((ent < 1) || (ent >= MAX_EDICTS)) { Com_Error(ERR_DROP, "CL_AddMuzzleFlash2: bad entity"); } flash_number = MSG_ReadByte(&net_message); if (flash_number > 210) { Com_Error(ERR_DROP, "CL_AddMuzzleFlash2: bad offset"); } /* locate the origin */ AngleVectors(cl_entities[ent].current.angles, forward, right, NULL); origin[0] = cl_entities[ent].current.origin[0] + forward[0] * monster_flash_offset[flash_number][0] + right[0] * monster_flash_offset[flash_number][1]; origin[1] = cl_entities[ent].current.origin[1] + forward[1] * monster_flash_offset[flash_number][0] + right[1] * monster_flash_offset[flash_number][1]; origin[2] = cl_entities[ent].current.origin[2] + forward[2] * monster_flash_offset[flash_number][0] + right[2] * monster_flash_offset[flash_number][1] + monster_flash_offset[flash_number][2]; dl = CL_AllocDlight(ent); VectorCopy(origin, dl->origin); dl->radius = 200.0f + (randk() & 31); dl->minlight = 32; dl->die = cl.time; switch (flash_number) { case MZ2_INFANTRY_MACHINEGUN_1: case MZ2_INFANTRY_MACHINEGUN_2: case MZ2_INFANTRY_MACHINEGUN_3: case MZ2_INFANTRY_MACHINEGUN_4: case MZ2_INFANTRY_MACHINEGUN_5: case MZ2_INFANTRY_MACHINEGUN_6: case MZ2_INFANTRY_MACHINEGUN_7: case MZ2_INFANTRY_MACHINEGUN_8: case MZ2_INFANTRY_MACHINEGUN_9: case MZ2_INFANTRY_MACHINEGUN_10: case MZ2_INFANTRY_MACHINEGUN_11: case MZ2_INFANTRY_MACHINEGUN_12: case MZ2_INFANTRY_MACHINEGUN_13: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NORM, 0); break; case MZ2_SOLDIER_MACHINEGUN_1: case MZ2_SOLDIER_MACHINEGUN_2: case MZ2_SOLDIER_MACHINEGUN_3: case MZ2_SOLDIER_MACHINEGUN_4: case MZ2_SOLDIER_MACHINEGUN_5: case MZ2_SOLDIER_MACHINEGUN_6: case MZ2_SOLDIER_MACHINEGUN_7: case MZ2_SOLDIER_MACHINEGUN_8: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck3.wav"), 1, ATTN_NORM, 0); break; case MZ2_GUNNER_MACHINEGUN_1: case MZ2_GUNNER_MACHINEGUN_2: case MZ2_GUNNER_MACHINEGUN_3: case MZ2_GUNNER_MACHINEGUN_4: case MZ2_GUNNER_MACHINEGUN_5: case MZ2_GUNNER_MACHINEGUN_6: case MZ2_GUNNER_MACHINEGUN_7: case MZ2_GUNNER_MACHINEGUN_8: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("gunner/gunatck2.wav"), 1, ATTN_NORM, 0); break; case MZ2_ACTOR_MACHINEGUN_1: case MZ2_SUPERTANK_MACHINEGUN_1: case MZ2_SUPERTANK_MACHINEGUN_2: case MZ2_SUPERTANK_MACHINEGUN_3: case MZ2_SUPERTANK_MACHINEGUN_4: case MZ2_SUPERTANK_MACHINEGUN_5: case MZ2_SUPERTANK_MACHINEGUN_6: case MZ2_TURRET_MACHINEGUN: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NORM, 0); break; case MZ2_BOSS2_MACHINEGUN_L1: case MZ2_BOSS2_MACHINEGUN_L2: case MZ2_BOSS2_MACHINEGUN_L3: case MZ2_BOSS2_MACHINEGUN_L4: case MZ2_BOSS2_MACHINEGUN_L5: case MZ2_CARRIER_MACHINEGUN_L1: case MZ2_CARRIER_MACHINEGUN_L2: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("infantry/infatck1.wav"), 1, ATTN_NONE, 0); break; case MZ2_SOLDIER_BLASTER_1: case MZ2_SOLDIER_BLASTER_2: case MZ2_SOLDIER_BLASTER_3: case MZ2_SOLDIER_BLASTER_4: case MZ2_SOLDIER_BLASTER_5: case MZ2_SOLDIER_BLASTER_6: case MZ2_SOLDIER_BLASTER_7: case MZ2_SOLDIER_BLASTER_8: case MZ2_TURRET_BLASTER: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck2.wav"), 1, ATTN_NORM, 0); break; case MZ2_FLYER_BLASTER_1: case MZ2_FLYER_BLASTER_2: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("flyer/flyatck3.wav"), 1, ATTN_NORM, 0); break; case MZ2_MEDIC_BLASTER_1: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("medic/medatck1.wav"), 1, ATTN_NORM, 0); break; case MZ2_HOVER_BLASTER_1: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("hover/hovatck1.wav"), 1, ATTN_NORM, 0); break; case MZ2_FLOAT_BLASTER_1: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("floater/fltatck1.wav"), 1, ATTN_NORM, 0); break; case MZ2_SOLDIER_SHOTGUN_1: case MZ2_SOLDIER_SHOTGUN_2: case MZ2_SOLDIER_SHOTGUN_3: case MZ2_SOLDIER_SHOTGUN_4: case MZ2_SOLDIER_SHOTGUN_5: case MZ2_SOLDIER_SHOTGUN_6: case MZ2_SOLDIER_SHOTGUN_7: case MZ2_SOLDIER_SHOTGUN_8: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_SmokeAndFlash(origin); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("soldier/solatck1.wav"), 1, ATTN_NORM, 0); break; case MZ2_TANK_BLASTER_1: case MZ2_TANK_BLASTER_2: case MZ2_TANK_BLASTER_3: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck3.wav"), 1, ATTN_NORM, 0); break; case MZ2_TANK_MACHINEGUN_1: case MZ2_TANK_MACHINEGUN_2: case MZ2_TANK_MACHINEGUN_3: case MZ2_TANK_MACHINEGUN_4: case MZ2_TANK_MACHINEGUN_5: case MZ2_TANK_MACHINEGUN_6: case MZ2_TANK_MACHINEGUN_7: case MZ2_TANK_MACHINEGUN_8: case MZ2_TANK_MACHINEGUN_9: case MZ2_TANK_MACHINEGUN_10: case MZ2_TANK_MACHINEGUN_11: case MZ2_TANK_MACHINEGUN_12: case MZ2_TANK_MACHINEGUN_13: case MZ2_TANK_MACHINEGUN_14: case MZ2_TANK_MACHINEGUN_15: case MZ2_TANK_MACHINEGUN_16: case MZ2_TANK_MACHINEGUN_17: case MZ2_TANK_MACHINEGUN_18: case MZ2_TANK_MACHINEGUN_19: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); Com_sprintf(soundname, sizeof(soundname), "tank/tnkatk2%c.wav", 'a' + (char)(randk() % 5)); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound(soundname), 1, ATTN_NORM, 0); break; case MZ2_CHICK_ROCKET_1: case MZ2_TURRET_ROCKET: dl->color[0] = 1; dl->color[1] = 0.5f; dl->color[2] = 0.2f; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("chick/chkatck2.wav"), 1, ATTN_NORM, 0); break; case MZ2_TANK_ROCKET_1: case MZ2_TANK_ROCKET_2: case MZ2_TANK_ROCKET_3: dl->color[0] = 1; dl->color[1] = 0.5f; dl->color[2] = 0.2f; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck1.wav"), 1, ATTN_NORM, 0); break; case MZ2_SUPERTANK_ROCKET_1: case MZ2_SUPERTANK_ROCKET_2: case MZ2_SUPERTANK_ROCKET_3: case MZ2_BOSS2_ROCKET_1: case MZ2_BOSS2_ROCKET_2: case MZ2_BOSS2_ROCKET_3: case MZ2_BOSS2_ROCKET_4: case MZ2_CARRIER_ROCKET_1: dl->color[0] = 1; dl->color[1] = 0.5f; dl->color[2] = 0.2f; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/rocket.wav"), 1, ATTN_NORM, 0); break; case MZ2_GUNNER_GRENADE_1: case MZ2_GUNNER_GRENADE_2: case MZ2_GUNNER_GRENADE_3: case MZ2_GUNNER_GRENADE_4: dl->color[0] = 1; dl->color[1] = 0.5; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("gunner/gunatck3.wav"), 1, ATTN_NORM, 0); break; case MZ2_GLADIATOR_RAILGUN_1: case MZ2_CARRIER_RAILGUN: case MZ2_WIDOW_RAIL: dl->color[0] = 0.5; dl->color[1] = 0.5; dl->color[2] = 1.0; break; case MZ2_MAKRON_BFG: dl->color[0] = 0.5; dl->color[1] = 1; dl->color[2] = 0.5; break; case MZ2_MAKRON_BLASTER_1: case MZ2_MAKRON_BLASTER_2: case MZ2_MAKRON_BLASTER_3: case MZ2_MAKRON_BLASTER_4: case MZ2_MAKRON_BLASTER_5: case MZ2_MAKRON_BLASTER_6: case MZ2_MAKRON_BLASTER_7: case MZ2_MAKRON_BLASTER_8: case MZ2_MAKRON_BLASTER_9: case MZ2_MAKRON_BLASTER_10: case MZ2_MAKRON_BLASTER_11: case MZ2_MAKRON_BLASTER_12: case MZ2_MAKRON_BLASTER_13: case MZ2_MAKRON_BLASTER_14: case MZ2_MAKRON_BLASTER_15: case MZ2_MAKRON_BLASTER_16: case MZ2_MAKRON_BLASTER_17: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("makron/blaster.wav"), 1, ATTN_NORM, 0); break; case MZ2_JORG_MACHINEGUN_L1: case MZ2_JORG_MACHINEGUN_L2: case MZ2_JORG_MACHINEGUN_L3: case MZ2_JORG_MACHINEGUN_L4: case MZ2_JORG_MACHINEGUN_L5: case MZ2_JORG_MACHINEGUN_L6: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("boss3/xfire.wav"), 1, ATTN_NORM, 0); break; case MZ2_JORG_MACHINEGUN_R1: case MZ2_JORG_MACHINEGUN_R2: case MZ2_JORG_MACHINEGUN_R3: case MZ2_JORG_MACHINEGUN_R4: case MZ2_JORG_MACHINEGUN_R5: case MZ2_JORG_MACHINEGUN_R6: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); break; case MZ2_JORG_BFG_1: dl->color[0] = 0.5; dl->color[1] = 1; dl->color[2] = 0.5; break; case MZ2_BOSS2_MACHINEGUN_R1: case MZ2_BOSS2_MACHINEGUN_R2: case MZ2_BOSS2_MACHINEGUN_R3: case MZ2_BOSS2_MACHINEGUN_R4: case MZ2_BOSS2_MACHINEGUN_R5: case MZ2_CARRIER_MACHINEGUN_R1: case MZ2_CARRIER_MACHINEGUN_R2: dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; CL_ParticleEffect(origin, vec3_origin, 0, 40); CL_SmokeAndFlash(origin); break; case MZ2_STALKER_BLASTER: case MZ2_DAEDALUS_BLASTER: case MZ2_MEDIC_BLASTER_2: case MZ2_WIDOW_BLASTER: case MZ2_WIDOW_BLASTER_SWEEP1: case MZ2_WIDOW_BLASTER_SWEEP2: case MZ2_WIDOW_BLASTER_SWEEP3: case MZ2_WIDOW_BLASTER_SWEEP4: case MZ2_WIDOW_BLASTER_SWEEP5: case MZ2_WIDOW_BLASTER_SWEEP6: case MZ2_WIDOW_BLASTER_SWEEP7: case MZ2_WIDOW_BLASTER_SWEEP8: case MZ2_WIDOW_BLASTER_SWEEP9: case MZ2_WIDOW_BLASTER_100: case MZ2_WIDOW_BLASTER_90: case MZ2_WIDOW_BLASTER_80: case MZ2_WIDOW_BLASTER_70: case MZ2_WIDOW_BLASTER_60: case MZ2_WIDOW_BLASTER_50: case MZ2_WIDOW_BLASTER_40: case MZ2_WIDOW_BLASTER_30: case MZ2_WIDOW_BLASTER_20: case MZ2_WIDOW_BLASTER_10: case MZ2_WIDOW_BLASTER_0: case MZ2_WIDOW_BLASTER_10L: case MZ2_WIDOW_BLASTER_20L: case MZ2_WIDOW_BLASTER_30L: case MZ2_WIDOW_BLASTER_40L: case MZ2_WIDOW_BLASTER_50L: case MZ2_WIDOW_BLASTER_60L: case MZ2_WIDOW_BLASTER_70L: case MZ2_WIDOW_RUN_1: case MZ2_WIDOW_RUN_2: case MZ2_WIDOW_RUN_3: case MZ2_WIDOW_RUN_4: case MZ2_WIDOW_RUN_5: case MZ2_WIDOW_RUN_6: case MZ2_WIDOW_RUN_7: case MZ2_WIDOW_RUN_8: dl->color[0] = 0; dl->color[1] = 1; dl->color[2] = 0; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("tank/tnkatck3.wav"), 1, ATTN_NORM, 0); break; case MZ2_WIDOW_DISRUPTOR: dl->color[0] = -1; dl->color[1] = -1; dl->color[2] = -1; S_StartSound(NULL, ent, CHAN_WEAPON, S_RegisterSound("weapons/disint2.wav"), 1, ATTN_NORM, 0); break; case MZ2_WIDOW_PLASMABEAM: case MZ2_WIDOW2_BEAMER_1: case MZ2_WIDOW2_BEAMER_2: case MZ2_WIDOW2_BEAMER_3: case MZ2_WIDOW2_BEAMER_4: case MZ2_WIDOW2_BEAMER_5: case MZ2_WIDOW2_BEAM_SWEEP_1: case MZ2_WIDOW2_BEAM_SWEEP_2: case MZ2_WIDOW2_BEAM_SWEEP_3: case MZ2_WIDOW2_BEAM_SWEEP_4: case MZ2_WIDOW2_BEAM_SWEEP_5: case MZ2_WIDOW2_BEAM_SWEEP_6: case MZ2_WIDOW2_BEAM_SWEEP_7: case MZ2_WIDOW2_BEAM_SWEEP_8: case MZ2_WIDOW2_BEAM_SWEEP_9: case MZ2_WIDOW2_BEAM_SWEEP_10: case MZ2_WIDOW2_BEAM_SWEEP_11: dl->radius = 300.0f + (randk() & 100); dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 0; dl->die = cl.time + 200; break; } } void CL_TeleporterParticles(entity_state_t *ent) { int i, j; cparticle_t *p; float time; time = (float)cl.time; for (i = 0; i < 8; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = 0xdb; for (j = 0; j < 2; j++) { p->org[j] = ent->origin[j] - 16 + (randk() & 31); p->vel[j] = crandk() * 14; } p->org[2] = ent->origin[2] - 8 + (randk() & 7); p->vel[2] = 80 + (randk() & 7); p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alpha = 1.0; p->alphavel = -0.5; } } void CL_LogoutEffect(vec3_t org, int type) { int i, j; cparticle_t *p; float time; time = (float)cl.time; for (i = 0; i < 500; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; if (type == MZ_LOGIN) { p->color = 0xd0 + (randk() & 7); } else if (type == MZ_LOGOUT) { p->color = 0x40 + (randk() & 7); } else { p->color = 0xe0 + (randk() & 7); } p->org[0] = org[0] - 16 + frandk() * 32; p->org[1] = org[1] - 16 + frandk() * 32; p->org[2] = org[2] - 24 + frandk() * 56; for (j = 0; j < 3; j++) { p->vel[j] = crandk() * 20; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alpha = 1.0; p->alphavel = -1.0 / (1.0 + frandk() * 0.3); } } void CL_ItemRespawnParticles(vec3_t org) { int i, j; cparticle_t *p; float time; time = (float)cl.time; for (i = 0; i < 64; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = 0xd4 + (randk() & 3); p->org[0] = org[0] + crandk() * 8; p->org[1] = org[1] + crandk() * 8; p->org[2] = org[2] + crandk() * 8; for (j = 0; j < 3; j++) { p->vel[j] = crandk() * 8; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY * 0.2; p->alpha = 1.0; p->alphavel = -1.0f / (1.0f + frandk() * 0.3f); } } void CL_ExplosionParticles(vec3_t org) { int i, j; cparticle_t *p; float time; time = (float)cl.time; for (i = 0; i < 256; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = 0xe0 + (randk() & 7); for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((randk() % 32) - 16); p->vel[j] = (randk() % 384) - 192; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alpha = 1.0; p->alphavel = -0.8f / (0.5f + frandk() * 0.3f); } } void CL_BigTeleportParticles(vec3_t org) { int i; cparticle_t *p; float time; time = (float)cl.time; float angle, dist; static int colortable[4] = {2 * 8, 13 * 8, 21 * 8, 18 * 8}; for (i = 0; i < 4096; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = colortable[randk() & 3]; angle = M_PI * 2 * (randk() & 1023) / 1023.0f; dist = (float)(randk() & 31); p->org[0] = org[0] + (float)cos(angle) * dist; p->vel[0] = (float)cos(angle) * (70 + (randk() & 63)); p->accel[0] = -(float)cos(angle) * 100; p->org[1] = org[1] + (float)sin(angle) * dist; p->vel[1] = (float)sin(angle) * (70 + (randk() & 63)); p->accel[1] = -(float)sin(angle) * 100; p->org[2] = org[2] + 8 + (randk() % 90); p->vel[2] = -100 + (randk() & 31); p->accel[2] = PARTICLE_GRAVITY * 4; p->alpha = 1.0; p->alphavel = -0.3f / (0.5f + frandk() * 0.3f); } } /* * Wall impact puffs */ void CL_BlasterParticles(vec3_t org, vec3_t dir) { int i, j; cparticle_t *p; float d; int count; float time; time = (float)cl.time; count = 40; for (i = 0; i < count; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = 0xe0 + (randk() & 7); d = randk() & 15; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((randk() & 7) - 4) + d * dir[j]; p->vel[j] = dir[j] * 30 + crandk() * 40; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alpha = 1.0; p->alphavel = -1.0f / (0.5f + frandk() * 0.3f); } } void CL_BlasterTrail(vec3_t start, vec3_t end) { vec3_t move; vec3_t vec; int len; int j; cparticle_t *p; int dec; float time; time = (float)cl.time; VectorCopy(start, move); VectorSubtract(end, start, vec); len = (int)VectorNormalize(vec); dec = 5; VectorScale(vec, 5, vec); while (len > 0) { len -= dec; if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = -1.0f / (0.3f + frandk() * 0.2f); p->color = 0xe0; for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk(); p->vel[j] = crandk() * 5; p->accel[j] = 0; } VectorAdd(move, vec, move); } } void CL_QuadTrail(vec3_t start, vec3_t end) { vec3_t move; vec3_t vec; int len; int j; cparticle_t *p; int dec; float time; time = (float)cl.time; VectorCopy(start, move); VectorSubtract(end, start, vec); len = (int)VectorNormalize(vec); dec = 5; VectorScale(vec, 5, vec); while (len > 0) { len -= dec; if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = -1.0f / (0.8f + frandk() * 0.2f); p->color = 115; for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk() * 16; p->vel[j] = crandk() * 5; p->accel[j] = 0; } VectorAdd(move, vec, move); } } void CL_FlagTrail(vec3_t start, vec3_t end, int color) { vec3_t move; vec3_t vec; int len; int j; cparticle_t *p; int dec; float time; time = (float)cl.time; VectorCopy(start, move); VectorSubtract(end, start, vec); len = (int)VectorNormalize(vec); dec = 5; VectorScale(vec, 5, vec); while (len > 0) { len -= dec; if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = -1.0f / (0.8f + frandk() * 0.2f); p->color = color; for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk() * 16; p->vel[j] = crandk() * 5; p->accel[j] = 0; } VectorAdd(move, vec, move); } } void CL_DiminishingTrail(vec3_t start, vec3_t end, centity_t *old, int flags) { vec3_t move; vec3_t vec; int len; int j; cparticle_t *p; float dec; float orgscale; float velscale; float time; time = (float)cl.time; VectorCopy(start, move); VectorSubtract(end, start, vec); len = VectorNormalize(vec); dec = 0.5; VectorScale(vec, dec, vec); if (old->trailcount > 900) { orgscale = 4; velscale = 15; } else if (old->trailcount > 800) { orgscale = 2; velscale = 10; } else { orgscale = 1; velscale = 5; } while (len > 0) { len -= dec; if (!free_particles) { return; } /* drop less particles as it flies */ if ((randk() & 1023) < old->trailcount) { p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; if (flags & EF_GIB) { p->alpha = 1.0; p->alphavel = -1.0f / (1 + frandk() * 0.4f); p->color = 0xe8 + (randk() & 7); for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk() * orgscale; p->vel[j] = crandk() * velscale; p->accel[j] = 0; } p->vel[2] -= PARTICLE_GRAVITY; } else if (flags & EF_GREENGIB) { p->alpha = 1.0; p->alphavel = -1.0f / (1 + frandk() * 0.4f); p->color = 0xdb + (randk() & 7); for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk() * orgscale; p->vel[j] = crandk() * velscale; p->accel[j] = 0; } p->vel[2] -= PARTICLE_GRAVITY; } else { p->alpha = 1.0; p->alphavel = -1.0f / (1 + frandk() * 0.2f); p->color = 4 + (randk() & 7); for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk() * orgscale; p->vel[j] = crandk() * velscale; } p->accel[2] = 20; } } old->trailcount -= 5; if (old->trailcount < 100) { old->trailcount = 100; } VectorAdd(move, vec, move); } } void MakeNormalVectors(vec3_t forward, vec3_t right, vec3_t up) { float d; /* this rotate and negate guarantees a vector not colinear with the original */ right[1] = -forward[0]; right[2] = forward[1]; right[0] = forward[2]; d = DotProduct(right, forward); VectorMA(right, -d, forward, right); VectorNormalize(right); CrossProduct(right, forward, up); } void CL_RocketTrail(vec3_t start, vec3_t end, centity_t *old) { vec3_t move; vec3_t vec; int len; int j; cparticle_t *p; int dec; float time; time = (float)cl.time; /* smoke */ CL_DiminishingTrail(start, end, old, EF_ROCKET); /* fire */ VectorCopy(start, move); VectorSubtract(end, start, vec); len = (int)VectorNormalize(vec); dec = 1; VectorScale(vec, dec, vec); while (len > 0) { len -= dec; if (!free_particles) { return; } if ((randk() & 7) == 0) { p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = -1.0f / (1 + frandk() * 0.2f); p->color = 0xdc + (randk() & 3); for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk() * 5; p->vel[j] = crandk() * 20; } p->accel[2] = -PARTICLE_GRAVITY; } VectorAdd(move, vec, move); } } void CL_RailTrail(vec3_t start, vec3_t end) { vec3_t move; vec3_t vec; int len; int j; cparticle_t *p; float dec; vec3_t right, up; int i; float d, c, s; vec3_t dir; byte clr = 0x74; float time; time = (float)cl.time; VectorCopy(start, move); VectorSubtract(end, start, vec); len = VectorNormalize(vec); MakeNormalVectors(vec, right, up); for (i = 0; i < len; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; VectorClear(p->accel); d = i * 0.1f; c = (float)cos(d); s = (float)sin(d); VectorScale(right, c, dir); VectorMA(dir, s, up, dir); p->alpha = 1.0; p->alphavel = -1.0f / (1 + frandk() * 0.2f); p->color = clr + (randk() & 7); for (j = 0; j < 3; j++) { p->org[j] = move[j] + dir[j] * 3; p->vel[j] = dir[j] * 6; } VectorAdd(move, vec, move); } dec = 0.75; VectorScale(vec, dec, vec); VectorCopy(start, move); while (len > 0) { len -= dec; if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; VectorClear(p->accel); p->alpha = 1.0; p->alphavel = -1.0f / (0.6f + frandk() * 0.2f); p->color = 0x0 + (randk() & 15); for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk() * 3; p->vel[j] = crandk() * 3; p->accel[j] = 0; } VectorAdd(move, vec, move); } } void CL_IonripperTrail(vec3_t start, vec3_t ent) { vec3_t move; vec3_t vec; int len; int j; cparticle_t *p; int dec; int left = 0; float time; time = (float)cl.time; VectorCopy(start, move); VectorSubtract(ent, start, vec); len = (int)VectorNormalize(vec); dec = 5; VectorScale(vec, 5, vec); while (len > 0) { len -= dec; if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 0.5; p->alphavel = -1.0f / (0.3f + frandk() * 0.2f); p->color = 0xe4 + (randk() & 3); for (j = 0; j < 3; j++) { p->org[j] = move[j]; p->accel[j] = 0; } if (left) { left = 0; p->vel[0] = 10; } else { left = 1; p->vel[0] = -10; } p->vel[1] = 0; p->vel[2] = 0; VectorAdd(move, vec, move); } } void CL_BubbleTrail(vec3_t start, vec3_t end) { vec3_t move; vec3_t vec; int len; int i, j; cparticle_t *p; float dec; float time; time = (float)cl.time; VectorCopy(start, move); VectorSubtract(end, start, vec); len = VectorNormalize(vec); dec = 32; VectorScale(vec, dec, vec); for (i = 0; i < len; i += 32) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = -1.0f / (1 + frandk() * 0.2f); p->color = 4 + (randk() & 7); for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk() * 2; p->vel[j] = crandk() * 5; } p->vel[2] += 6; VectorAdd(move, vec, move); } } void CL_FlyParticles(vec3_t origin, int count) { int i; cparticle_t *p; float angle; float sp, sy, cp, cy; vec3_t forward; float dist = 64; float ltime; float time; time = (float)cl.time; if (count > NUMVERTEXNORMALS) { count = NUMVERTEXNORMALS; } if (!avelocities[0][0]) { for (i = 0; i < NUMVERTEXNORMALS; i++) { avelocities[i][0] = (randk() & 255) * 0.01f; avelocities[i][1] = (randk() & 255) * 0.01f; avelocities[i][2] = (randk() & 255) * 0.01f; } } ltime = time / 1000.0f; for (i = 0; i < count; i += 2) { angle = ltime * avelocities[i][0]; sy = (float)sin(angle); cy = (float)cos(angle); angle = ltime * avelocities[i][1]; sp = (float)sin(angle); cp = (float)cos(angle); forward[0] = cp * cy; forward[1] = cp * sy; forward[2] = -sp; if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; dist = (float)sin(ltime + i) * 64; p->org[0] = origin[0] + bytedirs[i][0] * dist + forward[0] * 16; p->org[1] = origin[1] + bytedirs[i][1] * dist + forward[1] * 16; p->org[2] = origin[2] + bytedirs[i][2] * dist + forward[2] * 16; VectorClear(p->vel); VectorClear(p->accel); p->color = 0; p->colorvel = 0; p->alpha = 1; p->alphavel = -100; } } void CL_FlyEffect(centity_t *ent, vec3_t origin) { int n; int count; int starttime; if (ent->fly_stoptime < cl.time) { starttime = cl.time; ent->fly_stoptime = cl.time + 60000; } else { starttime = ent->fly_stoptime - 60000; } n = cl.time - starttime; if (n < 20000) { count = (int)n * 162 / 20000.0; } else { n = ent->fly_stoptime - cl.time; if (n < 20000) { count = (int)n * 162 / 20000.0; } else { count = 162; } } CL_FlyParticles(origin, count); } void CL_BfgParticles(entity_t *ent) { int i; cparticle_t *p; float angle; float sp, sy, cp, cy; vec3_t forward; float dist = 64; vec3_t v; float ltime; float time; time = (float)cl.time; if (!avelocities[0][0]) { for (i = 0; i < NUMVERTEXNORMALS; i++) { avelocities[i][0] = (randk() & 255) * 0.01f; avelocities[i][1] = (randk() & 255) * 0.01f; avelocities[i][2] = (randk() & 255) * 0.01f; } } ltime = time / 1000.0; for (i = 0; i < NUMVERTEXNORMALS; i++) { angle = ltime * avelocities[i][0]; sy = (float)sin(angle); cy = (float)cos(angle); angle = ltime * avelocities[i][1]; sp = (float)sin(angle); cp = (float)cos(angle); forward[0] = cp * cy; forward[1] = cp * sy; forward[2] = -sp; if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; dist = (float)sin(ltime + i) * 64; p->org[0] = ent->origin[0] + bytedirs[i][0] * dist + forward[0] * 16; p->org[1] = ent->origin[1] + bytedirs[i][1] * dist + forward[1] * 16; p->org[2] = ent->origin[2] + bytedirs[i][2] * dist + forward[2] * 16; VectorClear(p->vel); VectorClear(p->accel); VectorSubtract(p->org, ent->origin, v); dist = VectorLength(v) / 90.0f; p->color = (int)floor(0xd0 + dist * 7); p->colorvel = 0; p->alpha = 1.0f - dist; p->alphavel = -100; } } void CL_TrapParticles(entity_t *ent) { vec3_t move; vec3_t vec; vec3_t start, end; int len; int j; cparticle_t *p; int dec; float time; time = (float)cl.time; ent->origin[2] -= 14; VectorCopy(ent->origin, start); VectorCopy(ent->origin, end); end[2] += 64; VectorCopy(start, move); VectorSubtract(end, start, vec); len = (int)VectorNormalize(vec); dec = 5; VectorScale(vec, 5, vec); while (len > 0) { len -= dec; if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = -1.0f / (0.3f + frandk() * 0.2f); p->color = 0xe0; for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk(); p->vel[j] = crandk() * 15; p->accel[j] = 0; } p->accel[2] = PARTICLE_GRAVITY; VectorAdd(move, vec, move); } { int i, j, k; cparticle_t *p; float vel; vec3_t dir; vec3_t org; ent->origin[2] += 14; VectorCopy(ent->origin, org); for (i = -2; i <= 2; i += 4) { for (j = -2; j <= 2; j += 4) { for (k = -2; k <= 4; k += 4) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = 0xe0 + (randk() & 3); p->alpha = 1.0; p->alphavel = -1.0f / (0.3f + (randk() & 7) * 0.02f); p->org[0] = org[0] + i + ((randk() & 23) * crandk()); p->org[1] = org[1] + j + ((randk() & 23) * crandk()); p->org[2] = org[2] + k + ((randk() & 23) * crandk()); dir[0] = j * 8.0f; dir[1] = i * 8.0f; dir[2] = k * 8.0f; VectorNormalize(dir); vel = (float)(50 + (randk() & 63)); VectorScale(dir, vel, p->vel); p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; } } } } } void CL_BFGExplosionParticles(vec3_t org) { int i, j; cparticle_t *p; float time; time = (float)cl.time; for (i = 0; i < 256; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = 0xd0 + (randk() & 7); for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((randk() % 32) - 16); p->vel[j] = (randk() % 384) - 192; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alpha = 1.0; p->alphavel = -0.8f / (0.5f + frandk() * 0.3f); } } void CL_TeleportParticles(vec3_t org) { int i, j, k; cparticle_t *p; float vel; vec3_t dir; float time; time = (float)cl.time; for (i = -16; i <= 16; i += 4) { for (j = -16; j <= 16; j += 4) { for (k = -16; k <= 32; k += 4) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = 7 + (randk() & 7); p->alpha = 1.0; p->alphavel = -1.0f / (0.3f + (randk() & 7) * 0.02f); p->org[0] = org[0] + i + (randk() & 3); p->org[1] = org[1] + j + (randk() & 3); p->org[2] = org[2] + k + (randk() & 3); dir[0] = j * 8.0f; dir[1] = i * 8.0f; dir[2] = k * 8.0f; VectorNormalize(dir); vel = (float)(50 + (randk() & 63)); VectorScale(dir, vel, p->vel); p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; } } } } /* * An entity has just been parsed that has an * event value. the female events are there for * backwards compatability */ extern struct sfx_s *cl_sfx_footsteps[4]; void CL_EntityEvent(entity_state_t *ent) { switch (ent->event) { case EV_ITEM_RESPAWN: S_StartSound(NULL, ent->number, CHAN_WEAPON, S_RegisterSound("items/respawn1.wav"), 1, ATTN_IDLE, 0); CL_ItemRespawnParticles(ent->origin); break; case EV_PLAYER_TELEPORT: S_StartSound(NULL, ent->number, CHAN_WEAPON, S_RegisterSound("misc/tele1.wav"), 1, ATTN_IDLE, 0); CL_TeleportParticles(ent->origin); break; case EV_FOOTSTEP: if (cl_footsteps->value) { S_StartSound(NULL, ent->number, CHAN_BODY, cl_sfx_footsteps[randk() & 3], 1, ATTN_NORM, 0); } break; case EV_FALLSHORT: S_StartSound(NULL, ent->number, CHAN_AUTO, S_RegisterSound("player/land1.wav"), 1, ATTN_NORM, 0); break; case EV_FALL: S_StartSound(NULL, ent->number, CHAN_AUTO, S_RegisterSound("*fall2.wav"), 1, ATTN_NORM, 0); break; case EV_FALLFAR: S_StartSound(NULL, ent->number, CHAN_AUTO, S_RegisterSound("*fall1.wav"), 1, ATTN_NORM, 0); break; } } void CL_ClearEffects(void) { CL_ClearParticles(); CL_ClearDlights(); CL_ClearLightStyles(); } void CL_Flashlight(int ent, vec3_t pos) { cdlight_t *dl; dl = CL_AllocDlight(ent); VectorCopy(pos, dl->origin); dl->radius = 400; dl->minlight = 250; dl->die = cl.time + 100; dl->color[0] = 1; dl->color[1] = 1; dl->color[2] = 1; } void CL_ColorFlash(vec3_t pos, int ent, float intensity, float r, float g, float b) { cdlight_t *dl; dl = CL_AllocDlight(ent); VectorCopy(pos, dl->origin); dl->radius = intensity; dl->minlight = 250; dl->die = cl.time + 100; dl->color[0] = r; dl->color[1] = g; dl->color[2] = b; } void CL_DebugTrail(vec3_t start, vec3_t end) { vec3_t move; vec3_t vec; float len; cparticle_t *p; float dec; vec3_t right, up; VectorCopy(start, move); VectorSubtract(end, start, vec); len = VectorNormalize(vec); MakeNormalVectors(vec, right, up); dec = 3; VectorScale(vec, dec, vec); VectorCopy(start, move); while (len > 0) { len -= dec; if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = (float)cl.time; VectorClear(p->accel); VectorClear(p->vel); p->alpha = 1.0; p->alphavel = -0.1f; p->color = 0x74 + (randk() & 7); VectorCopy(move, p->org); VectorAdd(move, vec, move); } } void CL_SmokeTrail(vec3_t start, vec3_t end, int colorStart, int colorRun, int spacing) { vec3_t move; vec3_t vec; float len, time; int j; cparticle_t *p; VectorCopy(start, move); VectorSubtract(end, start, vec); len = VectorNormalize(vec); VectorScale(vec, spacing, vec); time = (float)cl.time; while (len > 0) { len -= spacing; if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = -1.0f / (1 + frandk() * 0.5f); p->color = colorStart + (float)(randk() % colorRun); for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk() * 3; p->accel[j] = 0; } p->vel[2] = 20 + crandk() * 5; VectorAdd(move, vec, move); } } void CL_ForceWall(vec3_t start, vec3_t end, int color8) { vec3_t move; vec3_t vec; int j; cparticle_t *p; float len, time; VectorCopy(start, move); VectorSubtract(end, start, vec); len = VectorNormalize(vec); VectorScale(vec, 4, vec); time = (float)cl.time; while (len > 0) { len -= 4; if (!free_particles) { return; } if (frandk() > 0.3) { p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = -1.0f / (3.0 + frandk() * 0.5f); p->color = color8; for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk() * 3; p->accel[j] = 0; } p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = -40 - (crandk() * 10); } VectorAdd(move, vec, move); } } /* * CL_BubbleTrail2 (lets you control the # of bubbles * by setting the distance between the spawns) */ void CL_BubbleTrail2(vec3_t start, vec3_t end, int dist) { vec3_t move; vec3_t vec; float len, time; int i; int j; cparticle_t *p; time = (float)cl.time; VectorCopy(start, move); VectorSubtract(end, start, vec); len = VectorNormalize(vec); VectorScale(vec, dist, vec); for (i = 0; i < len; i += dist) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = -1.0f / (1 + frandk() * 0.1f); p->color = 4 + (randk() & 7); for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk() * 2; p->vel[j] = crandk() * 10; } p->org[2] -= 4; p->vel[2] += 20; VectorAdd(move, vec, move); } } void CL_Heatbeam(vec3_t start, vec3_t forward) { vec3_t move; vec3_t vec; float len; int j; cparticle_t *p; vec3_t right, up; float i; float c, s; vec3_t dir; float ltime; float step = 32.0, rstep; float start_pt; float rot; float variance; float time; vec3_t end; VectorMA(start, 4096, forward, end); VectorCopy(start, move); VectorSubtract(end, start, vec); len = VectorNormalize(vec); VectorCopy(cl.v_right, right); VectorCopy(cl.v_up, up); VectorMA(move, -0.5, right, move); VectorMA(move, -0.5, up, move); time = (float)cl.time; ltime = (float)cl.time / 1000.0f; start_pt = (float)fmod(ltime * 96.0f, step); VectorMA(move, start_pt, vec, move); VectorScale(vec, step, vec); rstep = M_PI / 10.0f; for (i = start_pt; i < len; i += step) { if (i > step * 5) /* don't bother after the 5th ring */ { break; } for (rot = 0; rot < M_PI * 2; rot += rstep) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; VectorClear(p->accel); variance = 0.5; c = (float)cos(rot) * variance; s = (float)sin(rot) * variance; /* trim it so it looks like it's starting at the origin */ if (i < 10) { VectorScale(right, c * (i / 10.0f), dir); VectorMA(dir, s * (i / 10.0f), up, dir); } else { VectorScale(right, c, dir); VectorMA(dir, s, up, dir); } p->alpha = 0.5; p->alphavel = -1000.0; p->color = 223 - (randk() & 7); for (j = 0; j < 3; j++) { p->org[j] = move[j] + dir[j] * 3; p->vel[j] = 0; } } VectorAdd(move, vec, move); } } /* *Puffs with velocity along direction, with some randomness thrown in */ void CL_ParticleSteamEffect(vec3_t org, vec3_t dir, int color, int count, int magnitude) { int i, j; cparticle_t *p; float d, time; vec3_t r, u; time = (float)cl.time; MakeNormalVectors(dir, r, u); for (i = 0; i < count; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = color + (randk() & 7); for (j = 0; j < 3; j++) { p->org[j] = org[j] + magnitude * 0.1f * crandk(); } VectorScale(dir, magnitude, p->vel); d = crandk() * magnitude / 3; VectorMA(p->vel, d, r, p->vel); d = crandk() * magnitude / 3; VectorMA(p->vel, d, u, p->vel); p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY / 2; p->alpha = 1.0; p->alphavel = -1.0f / (0.5f + frandk() * 0.3f); } } void CL_ParticleSteamEffect2(cl_sustain_t *self) { int i, j; cparticle_t *p; float d; vec3_t r, u; vec3_t dir; VectorCopy(self->dir, dir); MakeNormalVectors(dir, r, u); for (i = 0; i < self->count; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cl.time; p->color = self->color + (randk() & 7); for (j = 0; j < 3; j++) { p->org[j] = self->org[j] + self->magnitude * 0.1 * crandk(); } VectorScale(dir, self->magnitude, p->vel); d = crandk() * self->magnitude / 3; VectorMA(p->vel, d, r, p->vel); d = crandk() * self->magnitude / 3; VectorMA(p->vel, d, u, p->vel); p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY / 2; p->alpha = 1.0; p->alphavel = -1.0 / (0.5 + frandk() * 0.3); } self->nextthink += self->thinkinterval; } void CL_TrackerTrail(vec3_t start, vec3_t end, int particleColor) { vec3_t move; vec3_t vec; vec3_t forward, right, up, angle_dir; float len; int j; cparticle_t *p; int dec; float dist; float time; time = (float)cl.time; VectorCopy(start, move); VectorSubtract(end, start, vec); len = VectorNormalize(vec); VectorCopy(vec, forward); AngleVectors2(forward, angle_dir); AngleVectors(angle_dir, forward, right, up); dec = 3; VectorScale(vec, 3, vec); while (len > 0) { len -= dec; if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = -2.0; p->color = particleColor; dist = DotProduct(move, forward); VectorMA(move, 8 * cos(dist), up, p->org); for (j = 0; j < 3; j++) { p->vel[j] = 0; p->accel[j] = 0; } p->vel[2] = 5; VectorAdd(move, vec, move); } } void CL_Tracker_Shell(vec3_t origin) { vec3_t dir; int i; cparticle_t *p; float time; time = (float)cl.time; for (i = 0; i < 300; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = INSTANT_PARTICLE; p->color = 0; dir[0] = crandk(); dir[1] = crandk(); dir[2] = crandk(); VectorNormalize(dir); VectorMA(origin, 40, dir, p->org); } } void CL_MonsterPlasma_Shell(vec3_t origin) { vec3_t dir; int i; cparticle_t *p; float time; time = (float)cl.time; for (i = 0; i < 40; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = INSTANT_PARTICLE; p->color = 0xe0; dir[0] = crandk(); dir[1] = crandk(); dir[2] = crandk(); VectorNormalize(dir); VectorMA(origin, 10, dir, p->org); } } void CL_Widowbeamout(cl_sustain_t *self) { vec3_t dir; int i; cparticle_t *p; static int colortable[4] = {2 * 8, 13 * 8, 21 * 8, 18 * 8}; float ratio; float time; ratio = 1.0f - (((float)self->endtime - (float)cl.time) / 2100.0f); time = (float)cl.time; for (i = 0; i < 300; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = INSTANT_PARTICLE; p->color = colortable[randk() & 3]; dir[0] = crandk(); dir[1] = crandk(); dir[2] = crandk(); VectorNormalize(dir); VectorMA(self->org, (45.0 * ratio), dir, p->org); } } void CL_Nukeblast(cl_sustain_t *self) { vec3_t dir; int i; cparticle_t *p; static int colortable[4] = {110, 112, 114, 116}; float ratio; float time; ratio = 1.0f - (((float)self->endtime - (float)cl.time) / 1000.0f); time = (float)cl.time; for (i = 0; i < 700; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = INSTANT_PARTICLE; p->color = colortable[randk() & 3]; dir[0] = crandk(); dir[1] = crandk(); dir[2] = crandk(); VectorNormalize(dir); VectorMA(self->org, (200.0 * ratio), dir, p->org); } } void CL_WidowSplash(vec3_t org) { static int colortable[4] = {2 * 8, 13 * 8, 21 * 8, 18 * 8}; int i; cparticle_t *p; vec3_t dir; float time; time = (float)cl.time; for (i = 0; i < 256; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = colortable[randk() & 3]; dir[0] = crandk(); dir[1] = crandk(); dir[2] = crandk(); VectorNormalize(dir); VectorMA(org, 45.0, dir, p->org); VectorMA(vec3_origin, 40.0, dir, p->vel); p->accel[0] = p->accel[1] = 0; p->alpha = 1.0; p->alphavel = -0.8f / (0.5f + frandk() * 0.3f); } } void CL_Tracker_Explode(vec3_t origin) { vec3_t dir, backdir; int i; cparticle_t *p; float time; time = (float)cl.time; for (i = 0; i < 300; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = -1.0; p->color = 0; dir[0] = crandk(); dir[1] = crandk(); dir[2] = crandk(); VectorNormalize(dir); VectorScale(dir, -1, backdir); VectorMA(origin, 64, dir, p->org); VectorScale(backdir, 64, p->vel); } } void CL_TagTrail(vec3_t start, vec3_t end, int color) { vec3_t move; vec3_t vec; float len; int j; cparticle_t *p; int dec; float time; time = (float)cl.time; VectorCopy(start, move); VectorSubtract(end, start, vec); len = VectorNormalize(vec); dec = 5; VectorScale(vec, 5, vec); while (len >= 0) { len -= dec; if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = -1.0f / (0.8f + frandk() * 0.2f); p->color = color; for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk() * 16; p->vel[j] = crandk() * 5; p->accel[j] = 0; } VectorAdd(move, vec, move); } } void CL_ColorExplosionParticles(vec3_t org, int color, int run) { int i; int j; cparticle_t *p; float time; time = (float)cl.time; for (i = 0; i < 128; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = color + (randk() % run); for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((randk() % 32) - 16); p->vel[j] = (randk() % 256) - 128; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alpha = 1.0; p->alphavel = -0.4f / (0.6f + frandk() * 0.2f); } } /* * Like the steam effect, but unaffected by gravity */ void CL_ParticleSmokeEffect(vec3_t org, vec3_t dir, int color, int count, int magnitude) { int i, j; cparticle_t *p; float d; vec3_t r, u; float time; time = (float)cl.time; MakeNormalVectors(dir, r, u); for (i = 0; i < count; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = color + (randk() & 7); for (j = 0; j < 3; j++) { p->org[j] = org[j] + magnitude * 0.1f * crandk(); } VectorScale(dir, magnitude, p->vel); d = crandk() * magnitude / 3; VectorMA(p->vel, d, r, p->vel); d = crandk() * magnitude / 3; VectorMA(p->vel, d, u, p->vel); p->accel[0] = p->accel[1] = p->accel[2] = 0; p->alpha = 1.0; p->alphavel = -1.0f / (0.5f + frandk() * 0.3f); } } /* * Wall impact puffs (Green) */ void CL_BlasterParticles2(vec3_t org, vec3_t dir, unsigned int color) { int i, j; cparticle_t *p; float d; int count; float time; time = (float)cl.time; count = 40; for (i = 0; i < count; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = color + (randk() & 7); d = (float)(randk() & 15); for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((randk() & 7) - 4) + d * dir[j]; p->vel[j] = dir[j] * 30 + crandk() * 40; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alpha = 1.0; p->alphavel = -1.0f / (0.5f + frandk() * 0.3f); } } /* * Green! */ void CL_BlasterTrail2(vec3_t start, vec3_t end) { vec3_t move; vec3_t vec; float len; int j; cparticle_t *p; int dec; float time; time = (float)cl.time; VectorCopy(start, move); VectorSubtract(end, start, vec); len = VectorNormalize(vec); dec = 5; VectorScale(vec, 5, vec); while (len > 0) { len -= dec; if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; VectorClear(p->accel); p->time = time; p->alpha = 1.0; p->alphavel = -1.0f / (float)(0.3f + frandk() * 0.2f); for (j = 0; j < 3; j++) { p->org[j] = move[j] + crandk(); p->vel[j] = crandk() * 5; p->accel[j] = 0; } VectorAdd(move, vec, move); } } yquake2-QUAKE2_5_32/src/client/cl_lights.c0000644000175000017500000000701112615162034020777 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements all client side lighting * * ======================================================================= */ #include "header/client.h" typedef struct { int length; float value[3]; float map[MAX_QPATH]; } clightstyle_t; clightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; int lastofs; void CL_ClearLightStyles(void) { memset(cl_lightstyle, 0, sizeof(cl_lightstyle)); lastofs = -1; } void CL_RunLightStyles(void) { int ofs; int i; clightstyle_t *ls; ofs = cl.time / 100; if (ofs == lastofs) { return; } lastofs = ofs; for (i = 0, ls = cl_lightstyle; i < MAX_LIGHTSTYLES; i++, ls++) { if (!ls->length) { ls->value[0] = ls->value[1] = ls->value[2] = 1.0; continue; } if (ls->length == 1) { ls->value[0] = ls->value[1] = ls->value[2] = ls->map[0]; } else { ls->value[0] = ls->value[1] = ls->value[2] = ls->map[ofs % ls->length]; } } } void CL_SetLightstyle(int i) { char *s; int j, k; s = cl.configstrings[i + CS_LIGHTS]; j = (int)strlen(s); cl_lightstyle[i].length = j; for (k = 0; k < j; k++) { cl_lightstyle[i].map[k] = (float)(s[k] - 'a') / (float)('m' - 'a'); } } void CL_AddLightStyles(void) { int i; clightstyle_t *ls; for (i = 0, ls = cl_lightstyle; i < MAX_LIGHTSTYLES; i++, ls++) { V_AddLightStyle(i, ls->value[0], ls->value[1], ls->value[2]); } } cdlight_t cl_dlights[MAX_DLIGHTS]; void CL_ClearDlights(void) { memset(cl_dlights, 0, sizeof(cl_dlights)); } cdlight_t * CL_AllocDlight(int key) { int i; cdlight_t *dl; /* first look for an exact key match */ if (key) { dl = cl_dlights; for (i = 0; i < MAX_DLIGHTS; i++, dl++) { if (dl->key == key) { dl->key = key; return dl; } } } /* then look for anything else */ dl = cl_dlights; for (i = 0; i < MAX_DLIGHTS; i++, dl++) { if (dl->die < cl.time) { dl->key = key; return dl; } } dl = &cl_dlights[0]; dl->key = key; return dl; } void CL_NewDlight(int key, float x, float y, float z, float radius, float time) { cdlight_t *dl; dl = CL_AllocDlight(key); dl->origin[0] = x; dl->origin[1] = y; dl->origin[2] = z; dl->radius = radius; dl->die = cl.time + time; } void CL_RunDLights(void) { int i; cdlight_t *dl; dl = cl_dlights; for (i = 0; i < MAX_DLIGHTS; i++, dl++) { if (!dl->radius) { continue; } if (dl->die < cl.time) { dl->radius = 0; return; } dl->radius -= cls.frametime * dl->decay; if (dl->radius < 0) { dl->radius = 0; } } } void CL_AddDLights(void) { int i; cdlight_t *dl; dl = cl_dlights; for (i = 0; i < MAX_DLIGHTS; i++, dl++) { if (!dl->radius) { continue; } V_AddLight(dl->origin, dl->radius, dl->color[0], dl->color[1], dl->color[2]); } } yquake2-QUAKE2_5_32/src/client/menu/0000755000175000017500000000000012615162034017630 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/client/menu/qmenu.c0000644000175000017500000003701212615162034021124 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements the generic part of the menu * * ======================================================================= */ #include #include #include "../header/client.h" #include "header/qmenu.h" static void Action_DoEnter(menuaction_s *a); static void Action_Draw(menuaction_s *a); static void Menu_DrawStatusBar(const char *string); static void MenuList_Draw(menulist_s *l); static void Separator_Draw(menuseparator_s *s); static void Slider_DoSlide(menuslider_s *s, int dir); static void Slider_Draw(menuslider_s *s); static void SpinControl_Draw(menulist_s *s); static void SpinControl_DoSlide(menulist_s *s, int dir); #define RCOLUMN_OFFSET 16 #define LCOLUMN_OFFSET -16 extern viddef_t viddef; #define VID_WIDTH viddef.width #define VID_HEIGHT viddef.height void Action_DoEnter(menuaction_s *a) { if (a->generic.callback) { a->generic.callback(a); } } void Action_Draw(menuaction_s *a) { float scale = SCR_GetMenuScale(); if (a->generic.flags & QMF_LEFT_JUSTIFY) { if (a->generic.flags & QMF_GRAYED) { Menu_DrawStringDark(a->generic.x + a->generic.parent->x + (LCOLUMN_OFFSET * scale), a->generic.y + a->generic.parent->y, a->generic.name); } else { Menu_DrawString(a->generic.x + a->generic.parent->x + (LCOLUMN_OFFSET * scale), a->generic.y + a->generic.parent->y, a->generic.name); } } else { if (a->generic.flags & QMF_GRAYED) { Menu_DrawStringR2LDark(a->generic.x + a->generic.parent->x + (LCOLUMN_OFFSET * scale), a->generic.y + a->generic.parent->y, a->generic.name); } else { Menu_DrawStringR2L(a->generic.x + a->generic.parent->x + (LCOLUMN_OFFSET * scale), a->generic.y + a->generic.parent->y, a->generic.name); } } if (a->generic.ownerdraw) { a->generic.ownerdraw(a); } } qboolean Field_DoEnter(menufield_s *f) { if (f->generic.callback) { f->generic.callback(f); return true; } return false; } void Field_Draw(menufield_s *f) { int i, n; char tempbuffer[128] = ""; float scale = SCR_GetMenuScale(); if (f->generic.name) { Menu_DrawStringR2LDark(f->generic.x + f->generic.parent->x + LCOLUMN_OFFSET * scale, f->generic.y + f->generic.parent->y, f->generic.name); } n = f->visible_length + 1; if (n > sizeof(tempbuffer)) { n = sizeof(tempbuffer); } Q_strlcpy(tempbuffer, f->buffer + f->visible_offset, n); Draw_CharScaled(f->generic.x + f->generic.parent->x + 16 * scale, (f->generic.y + f->generic.parent->y - 4) * scale, 18, scale); Draw_CharScaled(f->generic.x + f->generic.parent->x + 16 * scale, (f->generic.y + f->generic.parent->y + 4) * scale, 24, scale); Draw_CharScaled((f->generic.x + f->generic.parent->x + 24 * scale) + (f->visible_length * 8 * scale), (f->generic.y + f->generic.parent->y - 4) * scale, 20, scale); Draw_CharScaled((f->generic.x + f->generic.parent->x + 24 * scale) + (f->visible_length * 8 * scale), (f->generic.y + f->generic.parent->y + 4) * scale, 26, scale); for (i = 0; i < f->visible_length; i++) { Draw_CharScaled((f->generic.x + f->generic.parent->x + 24 * scale) + (i * 8 * scale), (f->generic.y + f->generic.parent->y - 4) * scale, 19, scale); Draw_CharScaled((f->generic.x + f->generic.parent->x + 24 * scale) + (i * 8 * scale), (f->generic.y + f->generic.parent->y + 4) * scale, 25, scale); } Menu_DrawString(f->generic.x + f->generic.parent->x + 24 * scale, f->generic.y + f->generic.parent->y, tempbuffer); if (Menu_ItemAtCursor(f->generic.parent) == f) { int offset; if (f->visible_offset) { offset = f->visible_length; } else { offset = f->cursor; } if (((int)(Sys_Milliseconds() / 250)) & 1) { Draw_CharScaled( f->generic.x + f->generic.parent->x + 24 * scale + (offset * 8 * scale), (f->generic.y + f->generic.parent->y) * scale, 11, scale); } else { Draw_CharScaled( f->generic.x + f->generic.parent->x + 24 * scale + (offset * 8 * scale), (f->generic.y + f->generic.parent->y) * scale, ' ', scale); } } } extern int keydown[]; qboolean Field_Key(menufield_s *f, int key) { switch (key) { case K_KP_SLASH: key = '/'; break; case K_KP_MINUS: key = '-'; break; case K_KP_PLUS: key = '+'; break; case K_KP_HOME: key = '7'; break; case K_KP_UPARROW: key = '8'; break; case K_KP_PGUP: key = '9'; break; case K_KP_LEFTARROW: key = '4'; break; case K_KP_5: key = '5'; break; case K_KP_RIGHTARROW: key = '6'; break; case K_KP_END: key = '1'; break; case K_KP_DOWNARROW: key = '2'; break; case K_KP_PGDN: key = '3'; break; case K_KP_INS: key = '0'; break; case K_KP_DEL: key = '.'; break; } if (key > 127) { return false; } switch (key) { case K_KP_LEFTARROW: case K_LEFTARROW: case K_BACKSPACE: if (f->cursor > 0) { memmove(&f->buffer[f->cursor - 1], &f->buffer[f->cursor], strlen(&f->buffer[f->cursor]) + 1); f->cursor--; if (f->visible_offset) { f->visible_offset--; } } break; case K_KP_DEL: case K_DEL: memmove(&f->buffer[f->cursor], &f->buffer[f->cursor + 1], strlen(&f->buffer[f->cursor + 1]) + 1); break; case K_KP_ENTER: case K_ENTER: case K_ESCAPE: case K_TAB: return false; case K_SPACE: default: if (!isdigit(key) && (f->generic.flags & QMF_NUMBERSONLY)) { return false; } if (f->cursor < f->length) { f->buffer[f->cursor++] = key; f->buffer[f->cursor] = 0; if (f->cursor > f->visible_length) { f->visible_offset++; } } } return true; } void Menu_AddItem(menuframework_s *menu, void *item) { if (menu->nitems == 0) { menu->nslots = 0; } if (menu->nitems < MAXMENUITEMS) { menu->items[menu->nitems] = item; ((menucommon_s *)menu->items[menu->nitems])->parent = menu; menu->nitems++; } menu->nslots = Menu_TallySlots(menu); } /* * This function takes the given menu, the direction, and attempts * to adjust the menu's cursor so that it's at the next available * slot. */ void Menu_AdjustCursor(menuframework_s *m, int dir) { menucommon_s *citem; /* see if it's in a valid spot */ if ((m->cursor >= 0) && (m->cursor < m->nitems)) { if ((citem = Menu_ItemAtCursor(m)) != 0) { if (citem->type != MTYPE_SEPARATOR) { return; } } } /* it's not in a valid spot, so crawl in the direction indicated until we find a valid spot */ if (dir == 1) { while (1) { citem = Menu_ItemAtCursor(m); if (citem) { if (citem->type != MTYPE_SEPARATOR) { break; } } m->cursor += dir; if (m->cursor >= m->nitems) { m->cursor = 0; } } } else { while (1) { citem = Menu_ItemAtCursor(m); if (citem) { if (citem->type != MTYPE_SEPARATOR) { break; } } m->cursor += dir; if (m->cursor < 0) { m->cursor = m->nitems - 1; } } } } void Menu_Center(menuframework_s *menu) { int height; float scale = SCR_GetMenuScale(); height = ((menucommon_s *)menu->items[menu->nitems - 1])->y; height += 10; menu->y = (VID_HEIGHT / scale - height) / 2; } void Menu_Draw(menuframework_s *menu) { int i; menucommon_s *item; float scale = SCR_GetMenuScale(); /* draw contents */ for (i = 0; i < menu->nitems; i++) { switch (((menucommon_s *)menu->items[i])->type) { case MTYPE_FIELD: Field_Draw((menufield_s *)menu->items[i]); break; case MTYPE_SLIDER: Slider_Draw((menuslider_s *)menu->items[i]); break; case MTYPE_LIST: MenuList_Draw((menulist_s *)menu->items[i]); break; case MTYPE_SPINCONTROL: SpinControl_Draw((menulist_s *)menu->items[i]); break; case MTYPE_ACTION: Action_Draw((menuaction_s *)menu->items[i]); break; case MTYPE_SEPARATOR: Separator_Draw((menuseparator_s *)menu->items[i]); break; } } item = Menu_ItemAtCursor(menu); if (item && item->cursordraw) { item->cursordraw(item); } else if (menu->cursordraw) { menu->cursordraw(menu); } else if (item && (item->type != MTYPE_FIELD)) { if (item->flags & QMF_LEFT_JUSTIFY) { Draw_CharScaled(menu->x + (item->x / scale - 24 + item->cursor_offset) * scale, (menu->y + item->y) * scale, 12 + ((int)(Sys_Milliseconds() / 250) & 1), scale); } else { Draw_CharScaled(menu->x + (item->cursor_offset) * scale, (menu->y + item->y) * scale, 12 + ((int)(Sys_Milliseconds() / 250) & 1), scale); } } if (item) { if (item->statusbarfunc) { item->statusbarfunc((void *)item); } else if (item->statusbar) { Menu_DrawStatusBar(item->statusbar); } else { Menu_DrawStatusBar(menu->statusbar); } } else { Menu_DrawStatusBar(menu->statusbar); } } void Menu_DrawStatusBar(const char *string) { float scale = SCR_GetMenuScale(); if (string) { int l = (int)strlen(string); float col = (VID_WIDTH / 2) - (l*8 / 2) * scale; Draw_Fill(0, VID_HEIGHT - 8 * scale, VID_WIDTH, 8 * scale, 4); Menu_DrawString(col, VID_HEIGHT / scale - 8, string); } else { Draw_Fill(0, VID_HEIGHT - 8 * scale, VID_WIDTH, 8 * scale, 0); } } void Menu_DrawString(int x, int y, const char *string) { unsigned i; float scale = SCR_GetMenuScale(); for (i = 0; i < strlen(string); i++) { Draw_CharScaled(x + i * 8 * scale, y * scale, string[i], scale); } } void Menu_DrawStringDark(int x, int y, const char *string) { unsigned i; float scale = SCR_GetMenuScale(); for (i = 0; i < strlen(string); i++) { Draw_CharScaled(x + i * 8 * scale, y * scale, string[i] + 128, scale); } } void Menu_DrawStringR2L(int x, int y, const char *string) { unsigned i; float scale = SCR_GetMenuScale(); for (i = 0; i < strlen(string); i++) { Draw_CharScaled(x - i * 8 * scale, y * scale, string[strlen(string) - i - 1], scale); } } void Menu_DrawStringR2LDark(int x, int y, const char *string) { unsigned i; float scale = SCR_GetMenuScale(); for (i = 0; i < strlen(string); i++) { Draw_CharScaled(x - i * 8 * scale, y * scale, string[strlen(string) - i - 1] + 128, scale); } } void * Menu_ItemAtCursor(menuframework_s *m) { if ((m->cursor < 0) || (m->cursor >= m->nitems)) { return 0; } return m->items[m->cursor]; } qboolean Menu_SelectItem(menuframework_s *s) { menucommon_s *item = (menucommon_s *)Menu_ItemAtCursor(s); if (item) { switch (item->type) { case MTYPE_FIELD: return Field_DoEnter((menufield_s *)item); case MTYPE_ACTION: Action_DoEnter((menuaction_s *)item); return true; case MTYPE_LIST: return false; case MTYPE_SPINCONTROL: return false; } } return false; } void Menu_SetStatusBar(menuframework_s *m, const char *string) { m->statusbar = string; } void Menu_SlideItem(menuframework_s *s, int dir) { menucommon_s *item = (menucommon_s *)Menu_ItemAtCursor(s); if (item) { switch (item->type) { case MTYPE_SLIDER: Slider_DoSlide((menuslider_s *)item, dir); break; case MTYPE_SPINCONTROL: SpinControl_DoSlide((menulist_s *)item, dir); break; } } } int Menu_TallySlots(menuframework_s *menu) { int i; int total = 0; for (i = 0; i < menu->nitems; i++) { if (((menucommon_s *)menu->items[i])->type == MTYPE_LIST) { int nitems = 0; const char **n = ((menulist_s *)menu->items[i])->itemnames; while (*n) { nitems++, n++; } total += nitems; } else { total++; } } return total; } void MenuList_Draw(menulist_s *l) { const char **n; int y = 0; float scale = SCR_GetMenuScale(); Menu_DrawStringR2LDark(l->generic.x + l->generic.parent->x + LCOLUMN_OFFSET * scale, l->generic.y + l->generic.parent->y, l->generic.name); n = l->itemnames; Draw_Fill(l->generic.x - 112 + l->generic.parent->x, l->generic.parent->y + l->generic.y + l->curvalue * 10 + 10, 128, 10, 16); while (*n) { Menu_DrawStringR2LDark(l->generic.x + l->generic.parent->x + LCOLUMN_OFFSET * scale, l->generic.y + l->generic.parent->y + y + 10, *n); n++; y += 10; } } void Separator_Draw(menuseparator_s *s) { if (s->generic.name) { Menu_DrawStringR2LDark(s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, s->generic.name); } } void Slider_DoSlide(menuslider_s *s, int dir) { s->curvalue += dir; if (s->curvalue > s->maxvalue) { s->curvalue = s->maxvalue; } else if (s->curvalue < s->minvalue) { s->curvalue = s->minvalue; } if (s->generic.callback) { s->generic.callback(s); } } #define SLIDER_RANGE 10 void Slider_Draw(menuslider_s *s) { int i; float scale = SCR_GetMenuScale(); Menu_DrawStringR2LDark(s->generic.x + s->generic.parent->x + LCOLUMN_OFFSET * scale, s->generic.y + s->generic.parent->y, s->generic.name); s->range = (s->curvalue - s->minvalue) / (float)(s->maxvalue - s->minvalue); if (s->range < 0) { s->range = 0; } if (s->range > 1) { s->range = 1; } Draw_CharScaled(s->generic.x + (s->generic.parent->x + RCOLUMN_OFFSET * scale), (s->generic.y + s->generic.parent->y) * scale, 128, scale); for (i = 0; i < SLIDER_RANGE * scale; i++) { Draw_CharScaled((RCOLUMN_OFFSET * scale + s->generic.x + i * 8 + s->generic.parent->x + 8), (s->generic.y + s->generic.parent->y) * scale, 129, scale); } Draw_CharScaled((RCOLUMN_OFFSET * scale + s->generic.x + i * 8 + s->generic.parent->x + 8), (s->generic.y + s->generic.parent->y) * scale, 130, scale); Draw_CharScaled(((int)(8 + RCOLUMN_OFFSET * scale + s->generic.parent->x + s->generic.x + (SLIDER_RANGE * scale - 1) * 8 * s->range)), (s->generic.y + s->generic.parent->y) * scale, 131, scale); } void SpinControl_DoSlide(menulist_s *s, int dir) { s->curvalue += dir; if (s->curvalue < 0) { s->curvalue = 0; } else if (s->itemnames[s->curvalue] == 0) { s->curvalue--; } if (s->generic.callback) { s->generic.callback(s); } } void SpinControl_Draw(menulist_s *s) { char buffer[100]; float scale = SCR_GetMenuScale(); if (s->generic.name) { Menu_DrawStringR2LDark(s->generic.x + s->generic.parent->x + LCOLUMN_OFFSET * scale, s->generic.y + s->generic.parent->y, s->generic.name); } if (!strchr(s->itemnames[s->curvalue], '\n')) { Menu_DrawString(RCOLUMN_OFFSET * scale + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, s->itemnames[s->curvalue]); } else { strcpy(buffer, s->itemnames[s->curvalue]); *strchr(buffer, '\n') = 0; Menu_DrawString(RCOLUMN_OFFSET * scale + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y, buffer); strcpy(buffer, strchr(s->itemnames[s->curvalue], '\n') + 1); Menu_DrawString(RCOLUMN_OFFSET * scale + s->generic.x + s->generic.parent->x, s->generic.y + s->generic.parent->y + 10, buffer); } } yquake2-QUAKE2_5_32/src/client/menu/header/0000755000175000017500000000000012615162034021060 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/client/menu/header/qmenu.h0000644000175000017500000000567712615162034022375 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 header file for the generic part of the menu system * * ======================================================================= */ #ifndef CL_MENU_QMENU_H #define CL_MENU_QMENU_H #define MAXMENUITEMS 64 #define MTYPE_SLIDER 0 #define MTYPE_LIST 1 #define MTYPE_ACTION 2 #define MTYPE_SPINCONTROL 3 #define MTYPE_SEPARATOR 4 #define MTYPE_FIELD 5 #define QMF_LEFT_JUSTIFY 0x00000001 #define QMF_GRAYED 0x00000002 #define QMF_NUMBERSONLY 0x00000004 typedef struct _tag_menuframework { int x, y; int cursor; int nitems; int nslots; void *items[64]; const char *statusbar; void (*cursordraw)(struct _tag_menuframework *m); } menuframework_s; typedef struct { int type; const char *name; int x, y; menuframework_s *parent; int cursor_offset; int localdata[4]; unsigned flags; const char *statusbar; void (*callback)(void *self); void (*statusbarfunc)(void *self); void (*ownerdraw)(void *self); void (*cursordraw)(void *self); } menucommon_s; typedef struct { menucommon_s generic; char buffer[80]; int cursor; int length; int visible_length; int visible_offset; } menufield_s; typedef struct { menucommon_s generic; float minvalue; float maxvalue; float curvalue; float range; } menuslider_s; typedef struct { menucommon_s generic; int curvalue; const char **itemnames; } menulist_s; typedef struct { menucommon_s generic; } menuaction_s; typedef struct { menucommon_s generic; } menuseparator_s; qboolean Field_Key(menufield_s *field, int key); void Menu_AddItem(menuframework_s *menu, void *item); void Menu_AdjustCursor(menuframework_s *menu, int dir); void Menu_Center(menuframework_s *menu); void Menu_Draw(menuframework_s *menu); void *Menu_ItemAtCursor(menuframework_s *m); qboolean Menu_SelectItem(menuframework_s *s); void Menu_SetStatusBar(menuframework_s *s, const char *string); void Menu_SlideItem(menuframework_s *s, int dir); int Menu_TallySlots(menuframework_s *menu); void Menu_DrawString(int, int, const char *); void Menu_DrawStringDark(int, int, const char *); void Menu_DrawStringR2L(int, int, const char *); void Menu_DrawStringR2LDark(int, int, const char *); #endif yquake2-QUAKE2_5_32/src/client/menu/videomenu.c0000644000175000017500000002752012615162034021775 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 is the refresher dependend video menu. If you add a new * refresher this menu must be altered. * * ======================================================================= */ #include "../../client/header/client.h" #include "../../client/menu/header/qmenu.h" #define CUSTOM_MODE 24 extern void M_ForceMenuOff(void); static cvar_t *gl_mode; static cvar_t *gl_hudscale; static cvar_t *fov; extern cvar_t *scr_viewsize; extern cvar_t *vid_gamma; extern cvar_t *vid_fullscreen; static cvar_t *gl_swapinterval; static cvar_t *gl_anisotropic; static cvar_t *gl_msaa_samples; static menuframework_s s_opengl_menu; static menulist_s s_mode_list; static menulist_s s_aspect_list; static menulist_s s_hudscale_list; static menuslider_s s_screensize_slider; static menuslider_s s_brightness_slider; static menulist_s s_fs_box; static menulist_s s_vsync_list; static menulist_s s_af_list; static menulist_s s_msaa_list; static menuaction_s s_defaults_action; static menuaction_s s_apply_action; static void ScreenSizeCallback(void *s) { menuslider_s *slider = (menuslider_s *)s; Cvar_SetValue("viewsize", slider->curvalue * 10); } static void BrightnessCallback(void *s) { menuslider_s *slider = (menuslider_s *)s; float gamma = slider->curvalue / 10.0; Cvar_SetValue("vid_gamma", gamma); } static void AnisotropicCallback(void *s) { menulist_s *list = (menulist_s *)s; if (list->curvalue == 0) { Cvar_SetValue("gl_anisotropic", 0); } else { Cvar_SetValue("gl_anisotropic", pow(2, list->curvalue)); } } static void ResetDefaults(void *unused) { VID_MenuInit(); } static void ApplyChanges(void *unused) { qboolean restart = false; /* custom mode */ if (s_mode_list.curvalue != CUSTOM_MODE) { /* Restarts automatically */ Cvar_SetValue("gl_mode", s_mode_list.curvalue); } else { /* Restarts automatically */ Cvar_SetValue("gl_mode", -1); } /* horplus */ if (s_aspect_list.curvalue == 0) { if (horplus->value != 1) { Cvar_SetValue("horplus", 1); } } else { if (horplus->value != 0) { Cvar_SetValue("horplus", 0); } } /* fov */ if (s_aspect_list.curvalue == 0 || s_aspect_list.curvalue == 1) { if (fov->value != 90) { /* Restarts automatically */ Cvar_SetValue("fov", 90); } } else if (s_aspect_list.curvalue == 2) { if (fov->value != 86) { /* Restarts automatically */ Cvar_SetValue("fov", 86); } } else if (s_aspect_list.curvalue == 3) { if (fov->value != 100) { /* Restarts automatically */ Cvar_SetValue("fov", 100); } } else if (s_aspect_list.curvalue == 4) { if (fov->value != 106) { /* Restarts automatically */ Cvar_SetValue("fov", 106); } } /* HUD scaling */ if (s_hudscale_list.curvalue == 0) { Cvar_SetValue("gl_hudscale", 1); } else if (s_hudscale_list.curvalue == 1) { Cvar_SetValue("gl_hudscale", -1); } else if (s_hudscale_list.curvalue == 2) { Cvar_SetValue("gl_hudscale", 1.5); } else if (s_hudscale_list.curvalue == 3) { Cvar_SetValue("gl_hudscale", 2); } else if (s_hudscale_list.curvalue == 4) { Cvar_SetValue("gl_hudscale", 2.5); } else if (s_hudscale_list.curvalue == 5) { Cvar_SetValue("gl_hudscale", 3); } /* Restarts automatically */ Cvar_SetValue("vid_fullscreen", s_fs_box.curvalue); /* vertical sync */ if (gl_swapinterval->value != s_vsync_list.curvalue) { Cvar_SetValue("gl_swapinterval", s_vsync_list.curvalue); restart = true; } /* multisample anti-aliasing */ if (s_msaa_list.curvalue == 0) { if (gl_msaa_samples->value != 0) { Cvar_SetValue("gl_msaa_samples", 0); restart = true; } } else { if (gl_msaa_samples->value != pow(2, s_msaa_list.curvalue)) { Cvar_SetValue("gl_msaa_samples", pow(2, s_msaa_list.curvalue)); restart = true; } } if (restart) { Cbuf_AddText("vid_restart\n"); } M_ForceMenuOff(); } void VID_MenuInit(void) { int y = 0; static const char *resolutions[] = { "[320 240 ]", "[400 300 ]", "[512 384 ]", "[640 400 ]", "[640 480 ]", "[800 500 ]", "[800 600 ]", "[960 720 ]", "[1024 480 ]", "[1024 640 ]", "[1024 768 ]", "[1152 768 ]", "[1152 864 ]", "[1280 800 ]", "[1280 854 ]", "[1280 960 ]", "[1280 1024]", "[1366 768 ]", "[1440 900 ]", "[1600 1200]", "[1680 1050]", "[1920 1080]", "[1920 1200]", "[2048 1536]", "[custom ]", 0 }; static const char *aspect_names[] = { "auto", "4:3", "5:4", "16:10", "16:9", "custom", 0 }; static const char *hudscale_names[] = { "no (1x)", "auto", "1.5x", "2x", "2.5x", "3.0x", 0 }; static const char *yesno_names[] = { "no", "yes", 0 }; static const char *pow2_names[] = { "off", "2x", "4x", "8x", "16x", 0 }; if (!gl_mode) { gl_mode = Cvar_Get("gl_mode", "4", 0); } if (!gl_hudscale) { gl_hudscale = Cvar_Get("gl_hudscale", "-1", CVAR_ARCHIVE); } if (!horplus) { horplus = Cvar_Get("horplus", "1", CVAR_ARCHIVE); } if (!fov) { fov = Cvar_Get("fov", "90", CVAR_USERINFO | CVAR_ARCHIVE); } if (!scr_viewsize) { scr_viewsize = Cvar_Get("viewsize", "100", CVAR_ARCHIVE); } if (!vid_gamma) { vid_gamma = Cvar_Get("vid_gamma", "1.0", CVAR_ARCHIVE); } if (!gl_swapinterval) { gl_swapinterval = Cvar_Get("gl_swapinterval", "1", CVAR_ARCHIVE); } if (!gl_anisotropic) { gl_anisotropic = Cvar_Get("gl_anisotropic", "0", CVAR_ARCHIVE); } if (!gl_msaa_samples) { gl_msaa_samples = Cvar_Get("gl_msaa_samples", "0", CVAR_ARCHIVE); } s_opengl_menu.x = viddef.width * 0.50; s_opengl_menu.nitems = 0; s_mode_list.generic.type = MTYPE_SPINCONTROL; s_mode_list.generic.name = "video mode"; s_mode_list.generic.x = 0; s_mode_list.generic.y = (y = 0); s_mode_list.itemnames = resolutions; if (gl_mode->value >= 0) { s_mode_list.curvalue = gl_mode->value; } else { s_mode_list.curvalue = CUSTOM_MODE; } s_aspect_list.generic.type = MTYPE_SPINCONTROL; s_aspect_list.generic.name = "aspect ratio"; s_aspect_list.generic.x = 0; s_aspect_list.generic.y = (y += 10); s_aspect_list.itemnames = aspect_names; if (horplus->value == 1) { s_aspect_list.curvalue = 0; } else if (fov->value == 90) { s_aspect_list.curvalue = 1; } else if (fov->value == 86) { s_aspect_list.curvalue = 2; } else if (fov->value == 100) { s_aspect_list.curvalue = 3; } else if (fov->value == 106) { s_aspect_list.curvalue = 4; } else { s_aspect_list.curvalue = 5; } s_hudscale_list.generic.type = MTYPE_SPINCONTROL; s_hudscale_list.generic.name = "hud scale"; s_hudscale_list.generic.x = 0; s_hudscale_list.generic.y = (y += 10); s_hudscale_list.itemnames = hudscale_names; if (gl_hudscale->value == 1) { s_hudscale_list.curvalue = 0; } else if (gl_hudscale->value < 0) { s_hudscale_list.curvalue = 1; } else if (gl_hudscale->value == 1.5f) { s_hudscale_list.curvalue = 2; } else if (gl_hudscale->value == 2) { s_hudscale_list.curvalue = 3; } else if (gl_hudscale->value == 2.5f) { s_hudscale_list.curvalue = 4; } else if (gl_hudscale->value == 3) { s_hudscale_list.curvalue = 5; } s_screensize_slider.generic.type = MTYPE_SLIDER; s_screensize_slider.generic.name = "screen size"; s_screensize_slider.generic.x = 0; s_screensize_slider.generic.y = (y += 10); s_screensize_slider.minvalue = 4; s_screensize_slider.maxvalue = 10; s_screensize_slider.generic.callback = ScreenSizeCallback; s_screensize_slider.curvalue = scr_viewsize->value / 10; s_brightness_slider.generic.type = MTYPE_SLIDER; s_brightness_slider.generic.name = "brightness"; s_brightness_slider.generic.x = 0; s_brightness_slider.generic.y = (y += 10); s_brightness_slider.generic.callback = BrightnessCallback; s_brightness_slider.minvalue = 1; s_brightness_slider.maxvalue = 20; s_brightness_slider.curvalue = vid_gamma->value * 10; s_fs_box.generic.type = MTYPE_SPINCONTROL; s_fs_box.generic.name = "fullscreen"; s_fs_box.generic.x = 0; s_fs_box.generic.y = (y += 10); s_fs_box.itemnames = yesno_names; s_fs_box.curvalue = (vid_fullscreen->value != 0); s_vsync_list.generic.type = MTYPE_SPINCONTROL; s_vsync_list.generic.name = "vertical sync"; s_vsync_list.generic.x = 0; s_vsync_list.generic.y = (y += 10); s_vsync_list.itemnames = yesno_names; s_vsync_list.curvalue = (gl_swapinterval->value != 0); s_af_list.generic.type = MTYPE_SPINCONTROL; s_af_list.generic.name = "aniso filtering"; s_af_list.generic.x = 0; s_af_list.generic.y = (y += 20); s_af_list.generic.callback = AnisotropicCallback; s_af_list.itemnames = pow2_names; s_af_list.curvalue = 0; if (gl_anisotropic->value) { do { s_af_list.curvalue++; } while (pow2_names[s_af_list.curvalue] && pow(2, s_af_list.curvalue) <= gl_anisotropic->value); s_af_list.curvalue--; } s_msaa_list.generic.type = MTYPE_SPINCONTROL; s_msaa_list.generic.name = "multisampling"; s_msaa_list.generic.x = 0; s_msaa_list.generic.y = (y += 10); s_msaa_list.itemnames = pow2_names; s_msaa_list.curvalue = 0; if (gl_msaa_samples->value) { do { s_msaa_list.curvalue++; } while (pow2_names[s_msaa_list.curvalue] && pow(2, s_msaa_list.curvalue) <= gl_msaa_samples->value); s_msaa_list.curvalue--; } s_defaults_action.generic.type = MTYPE_ACTION; s_defaults_action.generic.name = "reset to default"; s_defaults_action.generic.x = 0; s_defaults_action.generic.y = (y += 20); s_defaults_action.generic.callback = ResetDefaults; s_apply_action.generic.type = MTYPE_ACTION; s_apply_action.generic.name = "apply"; s_apply_action.generic.x = 0; s_apply_action.generic.y = (y += 10); s_apply_action.generic.callback = ApplyChanges; Menu_AddItem(&s_opengl_menu, (void *)&s_mode_list); Menu_AddItem(&s_opengl_menu, (void *)&s_aspect_list); Menu_AddItem(&s_opengl_menu, (void *)&s_hudscale_list); Menu_AddItem(&s_opengl_menu, (void *)&s_screensize_slider); Menu_AddItem(&s_opengl_menu, (void *)&s_brightness_slider); Menu_AddItem(&s_opengl_menu, (void *)&s_fs_box); Menu_AddItem(&s_opengl_menu, (void *)&s_vsync_list); Menu_AddItem(&s_opengl_menu, (void *)&s_af_list); Menu_AddItem(&s_opengl_menu, (void *)&s_msaa_list); Menu_AddItem(&s_opengl_menu, (void *)&s_defaults_action); Menu_AddItem(&s_opengl_menu, (void *)&s_apply_action); Menu_Center(&s_opengl_menu); s_opengl_menu.x -= 8; } void VID_MenuDraw(void) { int w, h; float scale = SCR_GetMenuScale(); /* draw the banner */ Draw_GetPicSize(&w, &h, "m_banner_video"); Draw_PicScaled(viddef.width / 2 - (w * scale) / 2, viddef.height / 2 - (110 * scale), "m_banner_video", scale); /* move cursor to a reasonable starting position */ Menu_AdjustCursor(&s_opengl_menu, 1); /* draw the menu */ Menu_Draw(&s_opengl_menu); } const char * VID_MenuKey(int key) { extern void M_PopMenu(void); menuframework_s *m = &s_opengl_menu; static const char *sound = "misc/menu1.wav"; switch (key) { case K_ESCAPE: M_PopMenu(); return NULL; case K_UPARROW: m->cursor--; Menu_AdjustCursor(m, -1); break; case K_DOWNARROW: m->cursor++; Menu_AdjustCursor(m, 1); break; case K_LEFTARROW: Menu_SlideItem(m, -1); break; case K_RIGHTARROW: Menu_SlideItem(m, 1); break; case K_ENTER: Menu_SelectItem(m); break; } return sound; } yquake2-QUAKE2_5_32/src/client/menu/menu.c0000644000175000017500000033003212615162034020741 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements the non generic part of the menu system, e.g. * the menu shown to the player. Beware! This code is very fragile and * should only be touched with great care and exessive testing. * Otherwise strange things and hard to track down bugs can occure. In a * better world someone would rewrite this file to something more like * Quake III Team Arena. * * ======================================================================= */ #include #include "../header/client.h" #include "../header/keyboard.h" #include "header/qmenu.h" static int m_main_cursor; /* Number of the frames of the spinning quake logo */ #define NUM_CURSOR_FRAMES 15 static char *menu_in_sound = "misc/menu1.wav"; static char *menu_move_sound = "misc/menu2.wav"; static char *menu_out_sound = "misc/menu3.wav"; void M_Menu_Main_f(void); static void M_Menu_Game_f(void); static void M_Menu_LoadGame_f(void); static void M_Menu_SaveGame_f(void); static void M_Menu_PlayerConfig_f(void); static void M_Menu_DownloadOptions_f(void); static void M_Menu_Credits_f(void); static void M_Menu_Multiplayer_f(void); static void M_Menu_JoinServer_f(void); static void M_Menu_AddressBook_f(void); static void M_Menu_StartServer_f(void); static void M_Menu_DMOptions_f(void); static void M_Menu_Video_f(void); static void M_Menu_Options_f(void); static void M_Menu_Keys_f(void); static void M_Menu_Quit_f(void); void M_Menu_Credits(void); qboolean m_entersound; /* play after drawing a frame, so caching won't disrupt the sound */ void (*m_drawfunc)(void); const char *(*m_keyfunc)(int key); /* Maximal number of submenus */ #define MAX_MENU_DEPTH 8 typedef struct { void (*draw)(void); const char *(*key)(int k); } menulayer_t; menulayer_t m_layers[MAX_MENU_DEPTH]; int m_menudepth; static void M_Banner(char *name) { int w, h; float scale = SCR_GetMenuScale(); Draw_GetPicSize(&w, &h, name); Draw_PicScaled(viddef.width / 2 - (w * scale) / 2, viddef.height / 2 - (110 * scale), name, scale); } void M_ForceMenuOff(void) { m_drawfunc = NULL; m_keyfunc = NULL; cls.key_dest = key_game; m_menudepth = 0; Key_MarkAllUp(); Cvar_Set("paused", "0"); } void M_PopMenu(void) { S_StartLocalSound(menu_out_sound); if (m_menudepth < 1) { Com_Error(ERR_FATAL, "M_PopMenu: depth < 1"); } m_menudepth--; m_drawfunc = m_layers[m_menudepth].draw; m_keyfunc = m_layers[m_menudepth].key; if (!m_menudepth) { M_ForceMenuOff(); } } /* * This crappy function maintaines a stack of opened menus. * The steps in this horrible mess are: * * 1. But the game into pause if a menu is opened * * 2. If the requested menu is already open, close it. * * 3. If the requested menu is already open but not * on top, close all menus above it and the menu * itself. This is necessary since an instance of * the reqeuested menu is in flight and will be * displayed. * * 4. Save the previous menu on top (which was in flight) * to the stack and make the requested menu the menu in * flight. */ static void M_PushMenu(void (*draw)(void), const char *(*key)(int)) { int i; int alreadyPresent = 0; if ((Cvar_VariableValue("maxclients") == 1) && Com_ServerState()) { Cvar_Set("paused", "1"); } /* if this menu is already open (and on top), close it => toggling behaviour */ if ((m_drawfunc == draw) && (m_keyfunc == key)) { M_PopMenu(); return; } /* if this menu is already present, drop back to that level to avoid stacking menus by hotkeys */ for (i = 0; i < m_menudepth; i++) { if ((m_layers[i].draw == draw) && (m_layers[i].key == key)) { alreadyPresent = 1; break; } } /* menu was already opened further down the stack */ while (alreadyPresent && i <= m_menudepth) { M_PopMenu(); /* decrements m_menudepth */ } if (m_menudepth >= MAX_MENU_DEPTH) { Com_Printf("Too many open menus!\n"); return; } m_layers[m_menudepth].draw = m_drawfunc; m_layers[m_menudepth].key = m_keyfunc; m_menudepth++; m_drawfunc = draw; m_keyfunc = key; m_entersound = true; cls.key_dest = key_menu; } const char * Default_MenuKey(menuframework_s *m, int key) { const char *sound = NULL; menucommon_s *item; if (m) { if ((item = Menu_ItemAtCursor(m)) != 0) { if (item->type == MTYPE_FIELD) { if (Field_Key((menufield_s *)item, key)) { return NULL; } } } } switch (key) { case K_ESCAPE: M_PopMenu(); return menu_out_sound; case K_KP_UPARROW: case K_UPARROW: if (m) { m->cursor--; Menu_AdjustCursor(m, -1); sound = menu_move_sound; } break; case K_TAB: if (m) { m->cursor++; Menu_AdjustCursor(m, 1); sound = menu_move_sound; } break; case K_KP_DOWNARROW: case K_DOWNARROW: if (m) { m->cursor++; Menu_AdjustCursor(m, 1); sound = menu_move_sound; } break; case K_KP_LEFTARROW: case K_LEFTARROW: if (m) { Menu_SlideItem(m, -1); sound = menu_move_sound; } break; case K_KP_RIGHTARROW: case K_RIGHTARROW: if (m) { Menu_SlideItem(m, 1); sound = menu_move_sound; } break; case K_MOUSE1: case K_MOUSE2: case K_MOUSE3: case K_MOUSE4: case K_MOUSE5: case K_AUX1: case K_AUX2: case K_AUX3: case K_AUX4: case K_AUX5: case K_AUX6: case K_AUX7: case K_AUX8: case K_AUX9: case K_AUX10: case K_AUX11: case K_AUX12: case K_AUX13: case K_AUX14: case K_AUX15: case K_AUX16: case K_AUX17: case K_AUX18: case K_AUX19: case K_AUX20: case K_AUX21: case K_AUX22: case K_AUX23: case K_AUX24: case K_AUX25: case K_AUX26: case K_AUX27: case K_AUX28: case K_AUX29: case K_AUX30: case K_AUX31: case K_AUX32: case K_KP_ENTER: case K_ENTER: if (m) { Menu_SelectItem(m); } sound = menu_move_sound; break; } return sound; } /* * Draws one solid graphics character cx and cy are in 320*240 * coordinates, and will be centered on higher res screens. */ static void M_DrawCharacter(int cx, int cy, int num) { float scale = SCR_GetMenuScale(); Draw_CharScaled(cx + ((viddef.width - 320 * (int)scale) >> 1), cy + ((viddef.height - 240 * (int)scale) >> 1), num, scale); } static void M_Print(int x, int y, char *str) { int cx, cy; float scale = SCR_GetMenuScale(); cx = x; cy = y; while (*str) { if (*str == '\n') { cx = x; cy += 8; } else { M_DrawCharacter(cx * scale, cy * scale, (*str) + 128); cx += 8; } str++; } } void M_DrawPic(int x, int y, char *pic) { float scale = SCR_GetMenuScale(); Draw_PicScaled((x + ((viddef.width - 320) >> 1)) * scale, (y + ((viddef.height - 240) >> 1)) * scale, pic, scale); } /* * Draws an animating cursor with the point at * x,y. The pic will extend to the left of x, * and both above and below y. */ static void M_DrawCursor(int x, int y, int f) { char cursorname[80]; static qboolean cached; float scale = SCR_GetMenuScale(); if (!cached) { int i; for (i = 0; i < NUM_CURSOR_FRAMES; i++) { Com_sprintf(cursorname, sizeof(cursorname), "m_cursor%d", i); Draw_FindPic(cursorname); } cached = true; } Com_sprintf(cursorname, sizeof(cursorname), "m_cursor%d", f); Draw_PicScaled(x * scale, y * scale, cursorname, scale); } static void M_DrawTextBox(int x, int y, int width, int lines) { int cx, cy; int n; float scale = SCR_GetMenuScale(); /* draw left side */ cx = x; cy = y; M_DrawCharacter(cx * scale, cy * scale, 1); for (n = 0; n < lines; n++) { cy += 8; M_DrawCharacter(cx * scale, cy * scale, 4); } M_DrawCharacter(cx * scale, cy * scale + 8 * scale, 7); /* draw middle */ cx += 8; while (width > 0) { cy = y; M_DrawCharacter(cx * scale, cy * scale, 2); for (n = 0; n < lines; n++) { cy += 8; M_DrawCharacter(cx * scale, cy * scale, 5); } M_DrawCharacter(cx * scale, cy *scale + 8 * scale, 8); width -= 1; cx += 8; } /* draw right side */ cy = y; M_DrawCharacter(cx * scale, cy * scale, 3); for (n = 0; n < lines; n++) { cy += 8; M_DrawCharacter(cx * scale, cy * scale, 6); } M_DrawCharacter(cx * scale, cy * scale + 8 * scale, 9); } static char *m_popup_string; static int m_popup_endtime; static void M_Popup(void) { int x, y, width, lines; int n; char *str; if (!m_popup_string) { return; } if (m_popup_endtime && m_popup_endtime < cls.realtime) { m_popup_string = NULL; return; } width = lines = n = 0; for (str = m_popup_string; *str; str++) { if (*str == '\n') { lines++; n = 0; } else { n++; if (n > width) { width = n; } } } if (n) { lines++; } if (width) { width += 2; x = (320 - (width + 2) * 8) / 2; y = (240 - (lines + 2) * 8) / 3; M_DrawTextBox(x, y, width, lines); M_Print(x + 16, y + 8, m_popup_string); } } /* * MAIN MENU */ #define MAIN_ITEMS 5 static void M_Main_Draw(void) { int i; int w, h; int ystart; int xoffset; int widest = -1; int totalheight = 0; char litname[80]; float scale = SCR_GetMenuScale(); char *names[] = { "m_main_game", "m_main_multiplayer", "m_main_options", "m_main_video", "m_main_quit", 0 }; for (i = 0; names[i] != 0; i++) { Draw_GetPicSize(&w, &h, names[i]); if (w > widest) { widest = w; } totalheight += (h + 12); } ystart = (viddef.height / (2 * scale) - 110); xoffset = (viddef.width / scale - widest + 70) / 2; for (i = 0; names[i] != 0; i++) { if (i != m_main_cursor) { Draw_PicScaled(xoffset * scale, (ystart + i * 40 + 13) * scale, names[i], scale); } } strcpy(litname, names[m_main_cursor]); strcat(litname, "_sel"); Draw_PicScaled(xoffset * scale, (ystart + m_main_cursor * 40 + 13) * scale, litname, scale); M_DrawCursor(xoffset - 25, ystart + m_main_cursor * 40 + 11, (int)(cls.realtime / 100) % NUM_CURSOR_FRAMES); Draw_GetPicSize(&w, &h, "m_main_plaque"); Draw_PicScaled((xoffset - 30 - w) * scale, ystart * scale, "m_main_plaque", scale); Draw_PicScaled((xoffset - 30 - w) * scale, (ystart + h + 5) * scale, "m_main_logo", scale); } const char * M_Main_Key(int key) { const char *sound = menu_move_sound; switch (key) { case K_ESCAPE: M_PopMenu(); break; case K_KP_DOWNARROW: case K_DOWNARROW: if (++m_main_cursor >= MAIN_ITEMS) { m_main_cursor = 0; } return sound; case K_KP_UPARROW: case K_UPARROW: if (--m_main_cursor < 0) { m_main_cursor = MAIN_ITEMS - 1; } return sound; case K_KP_ENTER: case K_ENTER: m_entersound = true; switch (m_main_cursor) { case 0: M_Menu_Game_f(); break; case 1: M_Menu_Multiplayer_f(); break; case 2: M_Menu_Options_f(); break; case 3: M_Menu_Video_f(); break; case 4: M_Menu_Quit_f(); break; } } return NULL; } void M_Menu_Main_f(void) { M_PushMenu(M_Main_Draw, M_Main_Key); } /* * MULTIPLAYER MENU */ static menuframework_s s_multiplayer_menu; static menuaction_s s_join_network_server_action; static menuaction_s s_start_network_server_action; static menuaction_s s_player_setup_action; static void Multiplayer_MenuDraw(void) { M_Banner("m_banner_multiplayer"); Menu_AdjustCursor(&s_multiplayer_menu, 1); Menu_Draw(&s_multiplayer_menu); } static void PlayerSetupFunc(void *unused) { M_Menu_PlayerConfig_f(); } static void JoinNetworkServerFunc(void *unused) { M_Menu_JoinServer_f(); } static void StartNetworkServerFunc(void *unused) { M_Menu_StartServer_f(); } static void Multiplayer_MenuInit(void) { float scale = SCR_GetMenuScale(); s_multiplayer_menu.x = (int)(viddef.width * 0.50f) - 64 * scale; s_multiplayer_menu.nitems = 0; s_join_network_server_action.generic.type = MTYPE_ACTION; s_join_network_server_action.generic.flags = QMF_LEFT_JUSTIFY; s_join_network_server_action.generic.x = 0; s_join_network_server_action.generic.y = 0; s_join_network_server_action.generic.name = " join network server"; s_join_network_server_action.generic.callback = JoinNetworkServerFunc; s_start_network_server_action.generic.type = MTYPE_ACTION; s_start_network_server_action.generic.flags = QMF_LEFT_JUSTIFY; s_start_network_server_action.generic.x = 0; s_start_network_server_action.generic.y = 10; s_start_network_server_action.generic.name = " start network server"; s_start_network_server_action.generic.callback = StartNetworkServerFunc; s_player_setup_action.generic.type = MTYPE_ACTION; s_player_setup_action.generic.flags = QMF_LEFT_JUSTIFY; s_player_setup_action.generic.x = 0; s_player_setup_action.generic.y = 20; s_player_setup_action.generic.name = " player setup"; s_player_setup_action.generic.callback = PlayerSetupFunc; Menu_AddItem(&s_multiplayer_menu, (void *)&s_join_network_server_action); Menu_AddItem(&s_multiplayer_menu, (void *)&s_start_network_server_action); Menu_AddItem(&s_multiplayer_menu, (void *)&s_player_setup_action); Menu_SetStatusBar(&s_multiplayer_menu, NULL); Menu_Center(&s_multiplayer_menu); } static const char * Multiplayer_MenuKey(int key) { return Default_MenuKey(&s_multiplayer_menu, key); } static void M_Menu_Multiplayer_f(void) { Multiplayer_MenuInit(); M_PushMenu(Multiplayer_MenuDraw, Multiplayer_MenuKey); } /* * KEYS MENU */ char *bindnames[][2] = { {"+attack", "attack"}, {"weapnext", "next weapon"}, {"weapprev", "previous weapon"}, {"+forward", "walk forward"}, {"+back", "backpedal"}, {"+left", "turn left"}, {"+right", "turn right"}, {"+speed", "run"}, {"+moveleft", "step left"}, {"+moveright", "step right"}, {"+strafe", "sidestep"}, {"+lookup", "look up"}, {"+lookdown", "look down"}, {"centerview", "center view"}, {"+mlook", "mouse look"}, {"+klook", "keyboard look"}, {"+moveup", "up / jump"}, {"+movedown", "down / crouch"}, {"inven", "inventory"}, {"invuse", "use item"}, {"invdrop", "drop item"}, {"invprev", "prev item"}, {"invnext", "next item"}, {"cmd help", "help computer"} }; #define NUM_BINDNAMES (sizeof bindnames / sizeof bindnames[0]) int keys_cursor; static int bind_grab; static menuframework_s s_keys_menu; static menuaction_s s_keys_actions[NUM_BINDNAMES]; static 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, ""); } } } static 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; } } } } static void KeyCursorDrawFunc(menuframework_s *menu) { float scale = SCR_GetMenuScale(); if (bind_grab) { Draw_CharScaled(menu->x, (menu->y + menu->cursor * 9) * scale, '=', scale); } else { Draw_CharScaled(menu->x, (menu->y + menu->cursor * 9) * scale, 12 + ((int)(Sys_Milliseconds() / 250) & 1), scale); } } static void DrawKeyBindingFunc(void *self) { int keys[2]; menuaction_s *a = (menuaction_s *)self; float scale = SCR_GetMenuScale(); M_FindKeysForCommand(bindnames[a->generic.localdata[0]][0], keys); if (keys[0] == -1) { Menu_DrawString(a->generic.x + a->generic.parent->x + 16 * scale, a->generic.y + a->generic.parent->y, "???"); } else { int x; const char *name; name = Key_KeynumToString(keys[0]); Menu_DrawString(a->generic.x + a->generic.parent->x + 16 * scale, a->generic.y + a->generic.parent->y, name); x = strlen(name) * 8; if (keys[1] != -1) { Menu_DrawString(a->generic.x + a->generic.parent->x + 24 * scale + (x * scale), a->generic.y + a->generic.parent->y, "or"); Menu_DrawString(a->generic.x + a->generic.parent->x + 48 * scale + (x * scale), a->generic.y + a->generic.parent->y, Key_KeynumToString(keys[1])); } } } static void KeyBindingFunc(void *self) { menuaction_s *a = (menuaction_s *)self; int keys[2]; M_FindKeysForCommand(bindnames[a->generic.localdata[0]][0], keys); if (keys[1] != -1) { M_UnbindCommand(bindnames[a->generic.localdata[0]][0]); } bind_grab = true; Menu_SetStatusBar(&s_keys_menu, "press a key or button for this action"); } static void Keys_MenuInit(void) { int i; s_keys_menu.x = (int)(viddef.width * 0.50f); s_keys_menu.nitems = 0; s_keys_menu.cursordraw = KeyCursorDrawFunc; for (i = 0; i < NUM_BINDNAMES; i++) { s_keys_actions[i].generic.type = MTYPE_ACTION; s_keys_actions[i].generic.flags = QMF_GRAYED; s_keys_actions[i].generic.x = 0; s_keys_actions[i].generic.y = (i * 9); s_keys_actions[i].generic.ownerdraw = DrawKeyBindingFunc; s_keys_actions[i].generic.localdata[0] = i; s_keys_actions[i].generic.name = bindnames[s_keys_actions[i].generic.localdata[0]][1]; Menu_AddItem(&s_keys_menu, (void *)&s_keys_actions[i]); } Menu_SetStatusBar(&s_keys_menu, "ENTER to change, BACKSPACE to clear"); Menu_Center(&s_keys_menu); } static void Keys_MenuDraw(void) { Menu_AdjustCursor(&s_keys_menu, 1); Menu_Draw(&s_keys_menu); } static const char * Keys_MenuKey(int key) { menuaction_s *item = (menuaction_s *)Menu_ItemAtCursor(&s_keys_menu); if (bind_grab) { if ((key != K_ESCAPE) && (key != '`')) { char cmd[1024]; Com_sprintf(cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n", Key_KeynumToString(key), bindnames[item->generic.localdata[0]][0]); Cbuf_InsertText(cmd); } Menu_SetStatusBar(&s_keys_menu, "ENTER to change, BACKSPACE to clear"); bind_grab = false; return menu_out_sound; } switch (key) { case K_KP_ENTER: case K_ENTER: KeyBindingFunc(item); return menu_in_sound; case K_BACKSPACE: /* delete bindings */ case K_DEL: /* delete bindings */ case K_KP_DEL: M_UnbindCommand(bindnames[item->generic.localdata[0]][0]); return menu_out_sound; default: return Default_MenuKey(&s_keys_menu, key); } } static void M_Menu_Keys_f(void) { Keys_MenuInit(); M_PushMenu(Keys_MenuDraw, Keys_MenuKey); } /* * CONTROLS MENU */ static menuframework_s s_options_menu; static menuaction_s s_options_defaults_action; static menuaction_s s_options_customize_options_action; static menuslider_s s_options_sensitivity_slider; static menulist_s s_options_freelook_box; static menulist_s s_options_alwaysrun_box; static menulist_s s_options_invertmouse_box; static menulist_s s_options_lookspring_box; static menulist_s s_options_lookstrafe_box; static menulist_s s_options_crosshair_box; static menuslider_s s_options_sfxvolume_slider; #ifdef CDA static menulist_s s_options_enablecd_box; #endif #if defined(OGG) || defined(CDA) static menulist_s s_options_cdshuffle_box; #endif #ifdef OGG static menulist_s s_options_enableogg_box; #endif static menulist_s s_options_quality_list; static menulist_s s_options_console_action; static void CrosshairFunc(void *unused) { Cvar_SetValue("crosshair", (float)s_options_crosshair_box.curvalue); } static void CustomizeControlsFunc(void *unused) { M_Menu_Keys_f(); } static void AlwaysRunFunc(void *unused) { Cvar_SetValue("cl_run", (float)s_options_alwaysrun_box.curvalue); } static void FreeLookFunc(void *unused) { Cvar_SetValue("freelook", (float)s_options_freelook_box.curvalue); } static void MouseSpeedFunc(void *unused) { Cvar_SetValue("sensitivity", s_options_sensitivity_slider.curvalue / 2.0F); } static float ClampCvar(float min, float max, float value) { if (value < min) { return min; } if (value > max) { return max; } return value; } static void ControlsSetMenuItemValues(void) { s_options_sfxvolume_slider.curvalue = Cvar_VariableValue("s_volume") * 10; #ifdef CDA s_options_enablecd_box.curvalue = (Cvar_VariableValue("cd_nocd") == 0); #endif #if defined(OGG) || defined(CDA) s_options_cdshuffle_box.curvalue = (Cvar_VariableValue("cd_shuffle") != 0); #endif #ifdef OGG s_options_enableogg_box.curvalue = (Cvar_VariableValue("ogg_enable") != 0); cvar_t *ogg; ogg = Cvar_Get("ogg_sequence", "loop", CVAR_ARCHIVE); if (!strcmp(ogg->string, "random")) { s_options_cdshuffle_box.curvalue = 1; } else { s_options_cdshuffle_box.curvalue = 0; } #endif s_options_quality_list.curvalue = (Cvar_VariableValue("s_loadas8bit") == 0); s_options_sensitivity_slider.curvalue = sensitivity->value * 2; s_options_alwaysrun_box.curvalue = (cl_run->value != 0); s_options_invertmouse_box.curvalue = (m_pitch->value < 0); s_options_lookspring_box.curvalue = (lookspring->value != 0); s_options_lookstrafe_box.curvalue = (lookstrafe->value != 0); s_options_freelook_box.curvalue = (freelook->value != 0); s_options_crosshair_box.curvalue = ClampCvar(0, 3, crosshair->value); } static void ControlsResetDefaultsFunc(void *unused) { Cbuf_AddText("exec default.cfg\n"); Cbuf_AddText("exec yq2.cfg\n"); Cbuf_Execute(); ControlsSetMenuItemValues(); } static void InvertMouseFunc(void *unused) { Cvar_SetValue("m_pitch", -m_pitch->value); } static void LookspringFunc(void *unused) { Cvar_SetValue("lookspring", (float)!lookspring->value); } static void LookstrafeFunc(void *unused) { Cvar_SetValue("lookstrafe", (float)!lookstrafe->value); } static void UpdateVolumeFunc(void *unused) { Cvar_SetValue("s_volume", s_options_sfxvolume_slider.curvalue / 10); } #if defined(OGG) || defined(CDA) static void CDShuffleFunc(void *unused) { Cvar_SetValue("cd_shuffle", s_options_cdshuffle_box.curvalue); #ifdef OGG cvar_t *ogg; ogg = Cvar_Get("ogg_enable", "1", CVAR_ARCHIVE); if (s_options_cdshuffle_box.curvalue) { Cvar_Set("ogg_sequence", "random"); if (ogg->value) { OGG_ParseCmd("?"); OGG_Stop(); } } else { Cvar_Set("ogg_sequence", "loop"); if (ogg->value) { if ((int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10) < 10) { char tmp[2] = "0"; OGG_ParseCmd(strcat(tmp, cl.configstrings[CS_CDTRACK])); } else { OGG_ParseCmd(cl.configstrings[CS_CDTRACK]); } } } #endif } #endif #ifdef CDA static void EnableCDMusic(void *unused) { Cvar_SetValue("cd_nocd", (float)!s_options_enablecd_box.curvalue); #ifdef OGG Cvar_SetValue("ogg_enable", 0); #endif if (s_options_enablecd_box.curvalue) { #ifdef OGG OGG_Shutdown(); #endif CDAudio_Init(); if (s_options_cdshuffle_box.curvalue) { CDAudio_RandomPlay(); } else { CDAudio_Play((int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10), true); } } else { CDAudio_Stop(); } } #endif #ifdef OGG static void EnableOGGMusic(void *unused) { Cvar_SetValue("ogg_enable", (float)s_options_enableogg_box.curvalue); #ifdef CDA Cvar_SetValue("cd_nocd", 1); #endif if (s_options_enableogg_box.curvalue) { #ifdef CDA CDAudio_Stop(); #endif OGG_Init(); OGG_Stop(); if ((int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10) < 10) { char tmp[3] = "0"; OGG_ParseCmd(strcat(tmp, cl.configstrings[CS_CDTRACK])); } else { OGG_ParseCmd(cl.configstrings[CS_CDTRACK]); } } else { OGG_Shutdown(); } } #endif extern void Key_ClearTyping(void); static void ConsoleFunc(void *unused) { SCR_EndLoadingPlaque(); /* get rid of loading plaque */ if (cl.attractloop) { Cbuf_AddText("killserver\n"); return; } Key_ClearTyping(); Con_ClearNotify(); M_ForceMenuOff(); cls.key_dest = key_console; if ((Cvar_VariableValue("maxclients") == 1) && Com_ServerState()) { Cvar_Set("paused", "1"); } } static void UpdateSoundQualityFunc(void *unused) { if (s_options_quality_list.curvalue == 0) { Cvar_SetValue("s_khz", 22); Cvar_SetValue("s_loadas8bit", false); } else { Cvar_SetValue("s_khz", 44); Cvar_SetValue("s_loadas8bit", false); } m_popup_string = "Restarting the sound system. This\n" "could take up to a minute, so\n" "please be patient."; m_popup_endtime = cls.realtime + 2000; M_Popup(); /* the text box won't show up unless we do a buffer swap */ GLimp_EndFrame(); CL_Snd_Restart_f(); } static void Options_MenuInit(void) { #ifdef CDA static const char *cd_music_items[] = { "disabled", "enabled", 0 }; #endif #ifdef OGG static const char *ogg_music_items[] = { "disabled", "enabled", 0 }; #endif #if defined(OGG) || defined(CDA) static const char *cd_shuffle[] = { "disabled", "enabled", 0 }; #endif static const char *quality_items[] = { "normal", "high", 0 }; static const char *yesno_names[] = { "no", "yes", 0 }; static const char *crosshair_names[] = { "none", "cross", "dot", "angle", 0 }; float scale = SCR_GetMenuScale(); /* configure controls menu and menu items */ s_options_menu.x = viddef.width / 2; s_options_menu.y = viddef.height / (2 * scale) - 58; s_options_menu.nitems = 0; s_options_sfxvolume_slider.generic.type = MTYPE_SLIDER; s_options_sfxvolume_slider.generic.x = 0; s_options_sfxvolume_slider.generic.y = 0; s_options_sfxvolume_slider.generic.name = "effects volume"; s_options_sfxvolume_slider.generic.callback = UpdateVolumeFunc; s_options_sfxvolume_slider.minvalue = 0; s_options_sfxvolume_slider.maxvalue = 10; #ifdef CDA s_options_enablecd_box.generic.type = MTYPE_SPINCONTROL; s_options_enablecd_box.generic.x = 0; s_options_enablecd_box.generic.y = 10; s_options_enablecd_box.generic.name = "CD music"; s_options_enablecd_box.generic.callback = EnableCDMusic; s_options_enablecd_box.itemnames = cd_music_items; #endif #ifdef OGG s_options_enableogg_box.generic.type = MTYPE_SPINCONTROL; s_options_enableogg_box.generic.x = 0; s_options_enableogg_box.generic.y = 20; s_options_enableogg_box.generic.name = "OGG music"; s_options_enableogg_box.generic.callback = EnableOGGMusic; s_options_enableogg_box.itemnames = ogg_music_items; #endif #if defined(OGG) || defined(CDA) s_options_cdshuffle_box.generic.type = MTYPE_SPINCONTROL; s_options_cdshuffle_box.generic.x = 0; s_options_cdshuffle_box.generic.y = 30; s_options_cdshuffle_box.generic.name = "Shuffle"; s_options_cdshuffle_box.generic.callback = CDShuffleFunc; s_options_cdshuffle_box.itemnames = cd_shuffle; #endif s_options_quality_list.generic.type = MTYPE_SPINCONTROL; s_options_quality_list.generic.x = 0; s_options_quality_list.generic.y = 40; s_options_quality_list.generic.name = "sound quality"; s_options_quality_list.generic.callback = UpdateSoundQualityFunc; s_options_quality_list.itemnames = quality_items; s_options_sensitivity_slider.generic.type = MTYPE_SLIDER; s_options_sensitivity_slider.generic.x = 0; s_options_sensitivity_slider.generic.y = 60; s_options_sensitivity_slider.generic.name = "mouse speed"; s_options_sensitivity_slider.generic.callback = MouseSpeedFunc; s_options_sensitivity_slider.minvalue = 2; s_options_sensitivity_slider.maxvalue = 22; s_options_alwaysrun_box.generic.type = MTYPE_SPINCONTROL; s_options_alwaysrun_box.generic.x = 0; s_options_alwaysrun_box.generic.y = 70; s_options_alwaysrun_box.generic.name = "always run"; s_options_alwaysrun_box.generic.callback = AlwaysRunFunc; s_options_alwaysrun_box.itemnames = yesno_names; s_options_invertmouse_box.generic.type = MTYPE_SPINCONTROL; s_options_invertmouse_box.generic.x = 0; s_options_invertmouse_box.generic.y = 80; s_options_invertmouse_box.generic.name = "invert mouse"; s_options_invertmouse_box.generic.callback = InvertMouseFunc; s_options_invertmouse_box.itemnames = yesno_names; s_options_lookspring_box.generic.type = MTYPE_SPINCONTROL; s_options_lookspring_box.generic.x = 0; s_options_lookspring_box.generic.y = 90; s_options_lookspring_box.generic.name = "lookspring"; s_options_lookspring_box.generic.callback = LookspringFunc; s_options_lookspring_box.itemnames = yesno_names; s_options_lookstrafe_box.generic.type = MTYPE_SPINCONTROL; s_options_lookstrafe_box.generic.x = 0; s_options_lookstrafe_box.generic.y = 100; s_options_lookstrafe_box.generic.name = "lookstrafe"; s_options_lookstrafe_box.generic.callback = LookstrafeFunc; s_options_lookstrafe_box.itemnames = yesno_names; s_options_freelook_box.generic.type = MTYPE_SPINCONTROL; s_options_freelook_box.generic.x = 0; s_options_freelook_box.generic.y = 110; s_options_freelook_box.generic.name = "free look"; s_options_freelook_box.generic.callback = FreeLookFunc; s_options_freelook_box.itemnames = yesno_names; s_options_crosshair_box.generic.type = MTYPE_SPINCONTROL; s_options_crosshair_box.generic.x = 0; s_options_crosshair_box.generic.y = 120; s_options_crosshair_box.generic.name = "crosshair"; s_options_crosshair_box.generic.callback = CrosshairFunc; s_options_crosshair_box.itemnames = crosshair_names; s_options_customize_options_action.generic.type = MTYPE_ACTION; s_options_customize_options_action.generic.x = 0; s_options_customize_options_action.generic.y = 140; s_options_customize_options_action.generic.name = "customize controls"; s_options_customize_options_action.generic.callback = CustomizeControlsFunc; s_options_defaults_action.generic.type = MTYPE_ACTION; s_options_defaults_action.generic.x = 0; s_options_defaults_action.generic.y = 150; s_options_defaults_action.generic.name = "reset defaults"; s_options_defaults_action.generic.callback = ControlsResetDefaultsFunc; s_options_console_action.generic.type = MTYPE_ACTION; s_options_console_action.generic.x = 0; s_options_console_action.generic.y = 160; s_options_console_action.generic.name = "go to console"; s_options_console_action.generic.callback = ConsoleFunc; ControlsSetMenuItemValues(); Menu_AddItem(&s_options_menu, (void *)&s_options_sfxvolume_slider); #ifdef CDA Menu_AddItem(&s_options_menu, (void *)&s_options_enablecd_box); #endif #ifdef OGG Menu_AddItem(&s_options_menu, (void *)&s_options_enableogg_box); #endif #if defined(OGG) || defined(CDA) Menu_AddItem(&s_options_menu, (void *)&s_options_cdshuffle_box); #endif Menu_AddItem(&s_options_menu, (void *)&s_options_quality_list); Menu_AddItem(&s_options_menu, (void *)&s_options_sensitivity_slider); Menu_AddItem(&s_options_menu, (void *)&s_options_alwaysrun_box); Menu_AddItem(&s_options_menu, (void *)&s_options_invertmouse_box); Menu_AddItem(&s_options_menu, (void *)&s_options_lookspring_box); Menu_AddItem(&s_options_menu, (void *)&s_options_lookstrafe_box); Menu_AddItem(&s_options_menu, (void *)&s_options_freelook_box); Menu_AddItem(&s_options_menu, (void *)&s_options_crosshair_box); Menu_AddItem(&s_options_menu, (void *)&s_options_customize_options_action); Menu_AddItem(&s_options_menu, (void *)&s_options_defaults_action); Menu_AddItem(&s_options_menu, (void *)&s_options_console_action); } static void Options_MenuDraw(void) { M_Banner("m_banner_options"); Menu_AdjustCursor(&s_options_menu, 1); Menu_Draw(&s_options_menu); M_Popup(); } static const char * Options_MenuKey(int key) { if (m_popup_string) { m_popup_string = NULL; return NULL; } return Default_MenuKey(&s_options_menu, key); } static void M_Menu_Options_f(void) { Options_MenuInit(); M_PushMenu(Options_MenuDraw, Options_MenuKey); } /* * VIDEO MENU */ static void M_Menu_Video_f(void) { VID_MenuInit(); M_PushMenu(VID_MenuDraw, VID_MenuKey); } /* * END GAME MENU */ static int credits_start_time; static const char **credits; static char *creditsIndex[256]; static char *creditsBuffer; static const char *idcredits[] = { "+QUAKE II BY ID SOFTWARE", "", "+PROGRAMMING", "John Carmack", "John Cash", "Brian Hook", "", "+ART", "Adrian Carmack", "Kevin Cloud", "Paul Steed", "", "+LEVEL DESIGN", "Tim Willits", "American McGee", "Christian Antkow", "Paul Jaquays", "Brandon James", "", "+BIZ", "Todd Hollenshead", "Barrett (Bear) Alexander", "Donna Jackson", "", "", "+SPECIAL THANKS", "Ben Donges for beta testing", "", "", "", "", "", "", "+ADDITIONAL SUPPORT", "", "+LINUX PORT AND CTF", "Dave \"Zoid\" Kirsch", "", "+CINEMATIC SEQUENCES", "Ending Cinematic by Blur Studio - ", "Venice, CA", "", "Environment models for Introduction", "Cinematic by Karl Dolgener", "", "Assistance with environment design", "by Cliff Iwai", "", "+SOUND EFFECTS AND MUSIC", "Sound Design by Soundelux Media Labs.", "Music Composed and Produced by", "Soundelux Media Labs. Special thanks", "to Bill Brown, Tom Ozanich, Brian", "Celano, Jeff Eisner, and The Soundelux", "Players.", "", "\"Level Music\" by Sonic Mayhem", "www.sonicmayhem.com", "", "\"Quake II Theme Song\"", "(C) 1997 Rob Zombie. All Rights", "Reserved.", "", "Track 10 (\"Climb\") by Jer Sypult", "", "Voice of computers by", "Carly Staehlin-Taylor", "", "+THANKS TO ACTIVISION", "+IN PARTICULAR:", "", "John Tam", "Steve Rosenthal", "Marty Stratton", "Henk Hartong", "", "+YAMAGI QUAKE II BY", "Yamagi Burmeister", "Daniel Gibson", "Sander van Dijk", "", "Quake II(tm) (C)1997 Id Software, Inc.", "All Rights Reserved. Distributed by", "Activision, Inc. under license.", "Quake II(tm), the Id Software name,", "the \"Q II\"(tm) logo and id(tm)", "logo are trademarks of Id Software,", "Inc. Activision(R) is a registered", "trademark of Activision, Inc. All", "other trademarks and trade names are", "properties of their respective owners.", 0 }; static const char *xatcredits[] = { "+QUAKE II MISSION PACK: THE RECKONING", "+BY", "+XATRIX ENTERTAINMENT, INC.", "", "+DESIGN AND DIRECTION", "Drew Markham", "", "+PRODUCED BY", "Greg Goodrich", "", "+PROGRAMMING", "Rafael Paiz", "", "+LEVEL DESIGN / ADDITIONAL GAME DESIGN", "Alex Mayberry", "", "+LEVEL DESIGN", "Mal Blackwell", "Dan Koppel", "", "+ART DIRECTION", "Michael \"Maxx\" Kaufman", "", "+COMPUTER GRAPHICS SUPERVISOR AND", "+CHARACTER ANIMATION DIRECTION", "Barry Dempsey", "", "+SENIOR ANIMATOR AND MODELER", "Jason Hoover", "", "+CHARACTER ANIMATION AND", "+MOTION CAPTURE SPECIALIST", "Amit Doron", "", "+ART", "Claire Praderie-Markham", "Viktor Antonov", "Corky Lehmkuhl", "", "+INTRODUCTION ANIMATION", "Dominique Drozdz", "", "+ADDITIONAL LEVEL DESIGN", "Aaron Barber", "Rhett Baldwin", "", "+3D CHARACTER ANIMATION TOOLS", "Gerry Tyra, SA Technology", "", "+ADDITIONAL EDITOR TOOL PROGRAMMING", "Robert Duffy", "", "+ADDITIONAL PROGRAMMING", "Ryan Feltrin", "", "+PRODUCTION COORDINATOR", "Victoria Sylvester", "", "+SOUND DESIGN", "Gary Bradfield", "", "+MUSIC BY", "Sonic Mayhem", "", "", "", "+SPECIAL THANKS", "+TO", "+OUR FRIENDS AT ID SOFTWARE", "", "John Carmack", "John Cash", "Brian Hook", "Adrian Carmack", "Kevin Cloud", "Paul Steed", "Tim Willits", "Christian Antkow", "Paul Jaquays", "Brandon James", "Todd Hollenshead", "Barrett (Bear) Alexander", "Dave \"Zoid\" Kirsch", "Donna Jackson", "", "", "", "+THANKS TO ACTIVISION", "+IN PARTICULAR:", "", "Marty Stratton", "Henk \"The Original Ripper\" Hartong", "Kevin Kraff", "Jamey Gottlieb", "Chris Hepburn", "", "+AND THE GAME TESTERS", "", "Tim Vanlaw", "Doug Jacobs", "Steven Rosenthal", "David Baker", "Chris Campbell", "Aaron Casillas", "Steve Elwell", "Derek Johnstone", "Igor Krinitskiy", "Samantha Lee", "Michael Spann", "Chris Toft", "Juan Valdes", "", "+THANKS TO INTERGRAPH COMPUTER SYTEMS", "+IN PARTICULAR:", "", "Michael T. Nicolaou", "", "", "Quake II Mission Pack: The Reckoning", "(tm) (C)1998 Id Software, Inc. All", "Rights Reserved. Developed by Xatrix", "Entertainment, Inc. for Id Software,", "Inc. Distributed by Activision Inc.", "under license. Quake(R) is a", "registered trademark of Id Software,", "Inc. Quake II Mission Pack: The", "Reckoning(tm), Quake II(tm), the Id", "Software name, the \"Q II\"(tm) logo", "and id(tm) logo are trademarks of Id", "Software, Inc. Activision(R) is a", "registered trademark of Activision,", "Inc. Xatrix(R) is a registered", "trademark of Xatrix Entertainment,", "Inc. All other trademarks and trade", "names are properties of their", "respective owners.", 0 }; static const char *roguecredits[] = { "+QUAKE II MISSION PACK 2: GROUND ZERO", "+BY", "+ROGUE ENTERTAINMENT, INC.", "", "+PRODUCED BY", "Jim Molinets", "", "+PROGRAMMING", "Peter Mack", "Patrick Magruder", "", "+LEVEL DESIGN", "Jim Molinets", "Cameron Lamprecht", "Berenger Fish", "Robert Selitto", "Steve Tietze", "Steve Thoms", "", "+ART DIRECTION", "Rich Fleider", "", "+ART", "Rich Fleider", "Steve Maines", "Won Choi", "", "+ANIMATION SEQUENCES", "Creat Studios", "Steve Maines", "", "+ADDITIONAL LEVEL DESIGN", "Rich Fleider", "Steve Maines", "Peter Mack", "", "+SOUND", "James Grunke", "", "+GROUND ZERO THEME", "+AND", "+MUSIC BY", "Sonic Mayhem", "", "+VWEP MODELS", "Brent \"Hentai\" Dill", "", "", "", "+SPECIAL THANKS", "+TO", "+OUR FRIENDS AT ID SOFTWARE", "", "John Carmack", "John Cash", "Brian Hook", "Adrian Carmack", "Kevin Cloud", "Paul Steed", "Tim Willits", "Christian Antkow", "Paul Jaquays", "Brandon James", "Todd Hollenshead", "Barrett (Bear) Alexander", "Katherine Anna Kang", "Donna Jackson", "Dave \"Zoid\" Kirsch", "", "", "", "+THANKS TO ACTIVISION", "+IN PARTICULAR:", "", "Marty Stratton", "Henk Hartong", "Mitch Lasky", "Steve Rosenthal", "Steve Elwell", "", "+AND THE GAME TESTERS", "", "The Ranger Clan", "Dave \"Zoid\" Kirsch", "Nihilistic Software", "Robert Duffy", "", "And Countless Others", "", "", "", "Quake II Mission Pack 2: Ground Zero", "(tm) (C)1998 Id Software, Inc. All", "Rights Reserved. Developed by Rogue", "Entertainment, Inc. for Id Software,", "Inc. Distributed by Activision Inc.", "under license. Quake(R) is a", "registered trademark of Id Software,", "Inc. Quake II Mission Pack 2: Ground", "Zero(tm), Quake II(tm), the Id", "Software name, the \"Q II\"(tm) logo", "and id(tm) logo are trademarks of Id", "Software, Inc. Activision(R) is a", "registered trademark of Activision,", "Inc. Rogue(R) is a registered", "trademark of Rogue Entertainment,", "Inc. All other trademarks and trade", "names are properties of their", "respective owners.", 0 }; static void M_Credits_MenuDraw(void) { int i, y; float scale = SCR_GetMenuScale(); /* draw the credits */ for (i = 0, y = (int)(viddef.height / scale - ((cls.realtime - credits_start_time) / 40.0F)); credits[i] && y < viddef.height / scale; y += 10, i++) { int j, stringoffset = 0; int bold = false; if (y <= -8) { continue; } if (credits[i][0] == '+') { bold = true; stringoffset = 1; } else { bold = false; stringoffset = 0; } for (j = 0; credits[i][j + stringoffset]; j++) { int x; x = (viddef.width / scale- (int)strlen(credits[i]) * 8 - stringoffset * 8) / 2 + (j + stringoffset) * 8; if (bold) { Draw_CharScaled(x * scale, y * scale, credits[i][j + stringoffset] + 128, scale); } else { Draw_CharScaled(x * scale, y * scale, credits[i][j + stringoffset], scale); } } } if (y < 0) { credits_start_time = cls.realtime; } } const char * M_Credits_Key(int key) { switch (key) { case K_ESCAPE: if (creditsBuffer) { FS_FreeFile(creditsBuffer); } M_PopMenu(); break; } return menu_out_sound; } extern int Developer_searchpath(void); static void M_Menu_Credits_f(void) { int n; int count; char *p; int isdeveloper = 0; creditsBuffer = NULL; count = FS_LoadFile("credits", (void **)&creditsBuffer); if (count != -1) { p = creditsBuffer; for (n = 0; n < 255; n++) { creditsIndex[n] = p; while (*p != '\r' && *p != '\n') { p++; if (--count == 0) { break; } } if (*p == '\r') { *p++ = 0; if (--count == 0) { break; } } *p++ = 0; if (--count == 0) { break; } } creditsIndex[++n] = 0; credits = (const char **)creditsIndex; } else { isdeveloper = Developer_searchpath(); if (isdeveloper == 1) /* Xatrix - The Reckoning */ { credits = xatcredits; } else if (isdeveloper == 2) /* Rogue - Ground Zero */ { credits = roguecredits; } else { credits = idcredits; } } credits_start_time = cls.realtime; M_PushMenu(M_Credits_MenuDraw, M_Credits_Key); } /* * GAME MENU */ static int m_game_cursor; static menuframework_s s_game_menu; static menuaction_s s_easy_game_action; static menuaction_s s_medium_game_action; static menuaction_s s_hard_game_action; static menuaction_s s_hardp_game_action; static menuaction_s s_load_game_action; static menuaction_s s_save_game_action; static menuaction_s s_credits_action; static menuseparator_s s_blankline; static void StartGame(void) { /* disable updates and start the cinematic going */ cl.servercount = -1; M_ForceMenuOff(); Cvar_SetValue("deathmatch", 0); Cvar_SetValue("coop", 0); Cbuf_AddText("loading ; killserver ; wait ; newgame\n"); cls.key_dest = key_game; } static void EasyGameFunc(void *data) { Cvar_ForceSet("skill", "0"); StartGame(); } static void MediumGameFunc(void *data) { Cvar_ForceSet("skill", "1"); StartGame(); } static void HardGameFunc(void *data) { Cvar_ForceSet("skill", "2"); StartGame(); } static void HardpGameFunc(void *data) { Cvar_ForceSet("skill", "3"); StartGame(); } static void LoadGameFunc(void *unused) { M_Menu_LoadGame_f(); } static void SaveGameFunc(void *unused) { M_Menu_SaveGame_f(); } static void CreditsFunc(void *unused) { M_Menu_Credits_f(); } void Game_MenuInit(void) { s_game_menu.x = (int)(viddef.width * 0.50f); s_game_menu.nitems = 0; s_easy_game_action.generic.type = MTYPE_ACTION; s_easy_game_action.generic.flags = QMF_LEFT_JUSTIFY; s_easy_game_action.generic.x = 0; s_easy_game_action.generic.y = 0; s_easy_game_action.generic.name = "easy"; s_easy_game_action.generic.callback = EasyGameFunc; s_medium_game_action.generic.type = MTYPE_ACTION; s_medium_game_action.generic.flags = QMF_LEFT_JUSTIFY; s_medium_game_action.generic.x = 0; s_medium_game_action.generic.y = 10; s_medium_game_action.generic.name = "medium"; s_medium_game_action.generic.callback = MediumGameFunc; s_hard_game_action.generic.type = MTYPE_ACTION; s_hard_game_action.generic.flags = QMF_LEFT_JUSTIFY; s_hard_game_action.generic.x = 0; s_hard_game_action.generic.y = 20; s_hard_game_action.generic.name = "hard"; s_hard_game_action.generic.callback = HardGameFunc; s_hardp_game_action.generic.type = MTYPE_ACTION; s_hardp_game_action.generic.flags = QMF_LEFT_JUSTIFY; s_hardp_game_action.generic.x = 0; s_hardp_game_action.generic.y = 30; s_hardp_game_action.generic.name = "nightmare"; s_hardp_game_action.generic.callback = HardpGameFunc; s_blankline.generic.type = MTYPE_SEPARATOR; s_load_game_action.generic.type = MTYPE_ACTION; s_load_game_action.generic.flags = QMF_LEFT_JUSTIFY; s_load_game_action.generic.x = 0; s_load_game_action.generic.y = 50; s_load_game_action.generic.name = "load game"; s_load_game_action.generic.callback = LoadGameFunc; s_save_game_action.generic.type = MTYPE_ACTION; s_save_game_action.generic.flags = QMF_LEFT_JUSTIFY; s_save_game_action.generic.x = 0; s_save_game_action.generic.y = 60; s_save_game_action.generic.name = "save game"; s_save_game_action.generic.callback = SaveGameFunc; s_credits_action.generic.type = MTYPE_ACTION; s_credits_action.generic.flags = QMF_LEFT_JUSTIFY; s_credits_action.generic.x = 0; s_credits_action.generic.y = 70; s_credits_action.generic.name = "credits"; s_credits_action.generic.callback = CreditsFunc; Menu_AddItem(&s_game_menu, (void *)&s_easy_game_action); Menu_AddItem(&s_game_menu, (void *)&s_medium_game_action); Menu_AddItem(&s_game_menu, (void *)&s_hard_game_action); Menu_AddItem(&s_game_menu, (void *)&s_hardp_game_action); Menu_AddItem(&s_game_menu, (void *)&s_blankline); Menu_AddItem(&s_game_menu, (void *)&s_load_game_action); Menu_AddItem(&s_game_menu, (void *)&s_save_game_action); Menu_AddItem(&s_game_menu, (void *)&s_blankline); Menu_AddItem(&s_game_menu, (void *)&s_credits_action); Menu_Center(&s_game_menu); } static void Game_MenuDraw(void) { M_Banner("m_banner_game"); Menu_AdjustCursor(&s_game_menu, 1); Menu_Draw(&s_game_menu); } static const char * Game_MenuKey(int key) { return Default_MenuKey(&s_game_menu, key); } static void M_Menu_Game_f(void) { Game_MenuInit(); M_PushMenu(Game_MenuDraw, Game_MenuKey); m_game_cursor = 1; } /* * LOADGAME MENU */ #define MAX_SAVESLOTS 16 #define MAX_SAVEPAGES 2 static char m_savestrings[MAX_SAVESLOTS][32]; static qboolean m_savevalid[MAX_SAVESLOTS]; static int m_loadsave_page; static char m_loadsave_statusbar[32]; static menuframework_s s_loadgame_menu; static menuaction_s s_loadgame_actions[MAX_SAVESLOTS]; static menuframework_s s_savegame_menu; static menuaction_s s_savegame_actions[MAX_SAVESLOTS]; static void Create_Savestrings(void) { int i; fileHandle_t f; char name[MAX_OSPATH]; for (i = 0; i < MAX_SAVESLOTS; i++) { Com_sprintf(name, sizeof(name), "save/save%i/server.ssv", m_loadsave_page * MAX_SAVESLOTS + i); FS_FOpenFile(name, &f, true); if (!f) { strcpy(m_savestrings[i], ""); m_savevalid[i] = false; } else { FS_Read(m_savestrings[i], sizeof(m_savestrings[i]), f); FS_FCloseFile(f); m_savevalid[i] = true; } } } static void LoadSave_AdjustPage(int dir) { int i; char *str; m_loadsave_page += dir; if (m_loadsave_page >= MAX_SAVEPAGES) { m_loadsave_page = 0; } else if (m_loadsave_page < 0) { m_loadsave_page = MAX_SAVEPAGES - 1; } strcpy(m_loadsave_statusbar, "pages: "); for (i = 0; i < MAX_SAVEPAGES; i++) { str = va("%c%d%c", i == m_loadsave_page ? '[' : ' ', i + 1, i == m_loadsave_page ? ']' : ' '); if (strlen(m_loadsave_statusbar) + strlen(str) >= sizeof(m_loadsave_statusbar)) { break; } strcat(m_loadsave_statusbar, str); } } static void LoadGameCallback(void *self) { menuaction_s *a = (menuaction_s *)self; Cbuf_AddText(va("load save%i\n", a->generic.localdata[0])); M_ForceMenuOff(); } static void LoadGame_MenuInit(void) { int i; float scale = SCR_GetMenuScale(); s_loadgame_menu.x = viddef.width / 2 - (120 * scale); s_loadgame_menu.y = viddef.height / (2 * scale) - 58; s_loadgame_menu.nitems = 0; Create_Savestrings(); for (i = 0; i < MAX_SAVESLOTS; i++) { s_loadgame_actions[i].generic.type = MTYPE_ACTION; s_loadgame_actions[i].generic.name = m_savestrings[i]; s_loadgame_actions[i].generic.x = 0; s_loadgame_actions[i].generic.y = i * 10; s_loadgame_actions[i].generic.localdata[0] = i + m_loadsave_page * MAX_SAVESLOTS; s_loadgame_actions[i].generic.flags = QMF_LEFT_JUSTIFY; if (!m_savevalid[i]) { s_loadgame_actions[i].generic.callback = NULL; } else { s_loadgame_actions[i].generic.callback = LoadGameCallback; } Menu_AddItem(&s_loadgame_menu, &s_loadgame_actions[i]); } Menu_SetStatusBar(&s_loadgame_menu, m_loadsave_statusbar); } static void LoadGame_MenuDraw(void) { M_Banner("m_banner_load_game"); Menu_AdjustCursor(&s_loadgame_menu, 1); Menu_Draw(&s_loadgame_menu); } static const char * LoadGame_MenuKey(int key) { static menuframework_s *m = &s_loadgame_menu; switch (key) { case K_KP_UPARROW: case K_UPARROW: if (m->cursor == 0) { LoadSave_AdjustPage(-1); LoadGame_MenuInit(); } break; case K_TAB: case K_KP_DOWNARROW: case K_DOWNARROW: if (m->cursor == m->nitems - 1) { LoadSave_AdjustPage(1); LoadGame_MenuInit(); } break; case K_KP_LEFTARROW: case K_LEFTARROW: LoadSave_AdjustPage(-1); LoadGame_MenuInit(); return menu_move_sound; case K_KP_RIGHTARROW: case K_RIGHTARROW: LoadSave_AdjustPage(1); LoadGame_MenuInit(); return menu_move_sound; default: s_savegame_menu.cursor = s_loadgame_menu.cursor; break; } return Default_MenuKey(m, key); } static void M_Menu_LoadGame_f(void) { LoadSave_AdjustPage(0); LoadGame_MenuInit(); M_PushMenu(LoadGame_MenuDraw, LoadGame_MenuKey); } /* * SAVEGAME MENU */ static void SaveGameCallback(void *self) { menuaction_s *a = (menuaction_s *)self; if (a->generic.localdata[0] == 0) { m_popup_string = "This slot is reserved for\n" "autosaving, so please select\n" "another one."; m_popup_endtime = cls.realtime + 2000; M_Popup(); return; } Cbuf_AddText(va("save save%i\n", a->generic.localdata[0])); M_ForceMenuOff(); } static void SaveGame_MenuDraw(void) { M_Banner("m_banner_save_game"); Menu_AdjustCursor(&s_savegame_menu, 1); Menu_Draw(&s_savegame_menu); M_Popup(); } static void SaveGame_MenuInit(void) { int i; float scale = SCR_GetMenuScale(); s_savegame_menu.x = viddef.width / 2 - (120 * scale); s_savegame_menu.y = viddef.height / (2 * scale) - 58; s_savegame_menu.nitems = 0; Create_Savestrings(); /* don't include the autosave slot */ for (i = 0; i < MAX_SAVESLOTS; i++) { s_savegame_actions[i].generic.type = MTYPE_ACTION; s_savegame_actions[i].generic.name = m_savestrings[i]; s_savegame_actions[i].generic.x = 0; s_savegame_actions[i].generic.y = i * 10; s_savegame_actions[i].generic.localdata[0] = i + m_loadsave_page * MAX_SAVESLOTS; s_savegame_actions[i].generic.flags = QMF_LEFT_JUSTIFY; s_savegame_actions[i].generic.callback = SaveGameCallback; Menu_AddItem(&s_savegame_menu, &s_savegame_actions[i]); } Menu_SetStatusBar(&s_savegame_menu, m_loadsave_statusbar); } static const char * SaveGame_MenuKey(int key) { static menuframework_s *m = &s_savegame_menu; if (m_popup_string) { m_popup_string = NULL; return NULL; } switch (key) { case K_KP_UPARROW: case K_UPARROW: if (m->cursor == 0) { LoadSave_AdjustPage(-1); SaveGame_MenuInit(); } break; case K_TAB: case K_KP_DOWNARROW: case K_DOWNARROW: if (m->cursor == m->nitems - 1) { LoadSave_AdjustPage(1); SaveGame_MenuInit(); } break; case K_KP_LEFTARROW: case K_LEFTARROW: LoadSave_AdjustPage(-1); SaveGame_MenuInit(); return menu_move_sound; case K_KP_RIGHTARROW: case K_RIGHTARROW: LoadSave_AdjustPage(1); SaveGame_MenuInit(); return menu_move_sound; default: s_loadgame_menu.cursor = s_savegame_menu.cursor; break; } return Default_MenuKey(m, key); } static void M_Menu_SaveGame_f(void) { if (!Com_ServerState()) { return; /* not playing a game */ } LoadSave_AdjustPage(0); SaveGame_MenuInit(); M_PushMenu(SaveGame_MenuDraw, SaveGame_MenuKey); } /* * JOIN SERVER MENU */ #define MAX_LOCAL_SERVERS 8 static menuframework_s s_joinserver_menu; static menuseparator_s s_joinserver_server_title; static menuaction_s s_joinserver_search_action; static menuaction_s s_joinserver_address_book_action; static menuaction_s s_joinserver_server_actions[MAX_LOCAL_SERVERS]; int m_num_servers; #define NO_SERVER_STRING "" /* network address */ static netadr_t local_server_netadr[MAX_LOCAL_SERVERS]; /* user readable information */ static char local_server_names[MAX_LOCAL_SERVERS][80]; static char local_server_netadr_strings[MAX_LOCAL_SERVERS][80]; void M_AddToServerList(netadr_t adr, char *info) { char *s; int i; if (m_num_servers == MAX_LOCAL_SERVERS) { return; } while (*info == ' ') { info++; } s = NET_AdrToString(adr); /* ignore if duplicated */ for (i = 0; i < m_num_servers; i++) { if (!strcmp(local_server_names[i], info) && !strcmp(local_server_netadr_strings[i], s)) { return; } } local_server_netadr[m_num_servers] = adr; Q_strlcpy(local_server_names[m_num_servers], info, sizeof(local_server_names[m_num_servers])); Q_strlcpy(local_server_netadr_strings[m_num_servers], s, sizeof(local_server_netadr_strings[m_num_servers])); m_num_servers++; } static void JoinServerFunc(void *self) { char buffer[128]; int index; index = (int)((menuaction_s *)self - s_joinserver_server_actions); if (Q_stricmp(local_server_names[index], NO_SERVER_STRING) == 0) { return; } if (index >= m_num_servers) { return; } Com_sprintf(buffer, sizeof(buffer), "connect %s\n", NET_AdrToString(local_server_netadr[index])); Cbuf_AddText(buffer); M_ForceMenuOff(); } static void AddressBookFunc(void *self) { M_Menu_AddressBook_f(); } static void SearchLocalGames(void) { int i; m_num_servers = 0; for (i = 0; i < MAX_LOCAL_SERVERS; i++) { strcpy(local_server_names[i], NO_SERVER_STRING); local_server_netadr_strings[i][0] = '\0'; } m_popup_string = "Searching for local servers. This\n" "could take up to a minute, so\n" "please be patient."; m_popup_endtime = cls.realtime + 2000; M_Popup(); /* the text box won't show up unless we do a buffer swap */ GLimp_EndFrame(); /* send out info packets */ CL_PingServers_f(); } static void SearchLocalGamesFunc(void *self) { SearchLocalGames(); } static void JoinServer_MenuInit(void) { int i; float scale = SCR_GetMenuScale(); s_joinserver_menu.x = (int)(viddef.width * 0.50f) - 120 * scale; s_joinserver_menu.nitems = 0; s_joinserver_address_book_action.generic.type = MTYPE_ACTION; s_joinserver_address_book_action.generic.name = "address book"; s_joinserver_address_book_action.generic.flags = QMF_LEFT_JUSTIFY; s_joinserver_address_book_action.generic.x = 0; s_joinserver_address_book_action.generic.y = 0; s_joinserver_address_book_action.generic.callback = AddressBookFunc; s_joinserver_search_action.generic.type = MTYPE_ACTION; s_joinserver_search_action.generic.name = "refresh server list"; s_joinserver_search_action.generic.flags = QMF_LEFT_JUSTIFY; s_joinserver_search_action.generic.x = 0; s_joinserver_search_action.generic.y = 10; s_joinserver_search_action.generic.callback = SearchLocalGamesFunc; s_joinserver_search_action.generic.statusbar = "search for servers"; s_joinserver_server_title.generic.type = MTYPE_SEPARATOR; s_joinserver_server_title.generic.name = "connect to..."; if (scale > 1) s_joinserver_server_title.generic.x = 80 * scale + (scale * 8); else s_joinserver_server_title.generic.x = 80; s_joinserver_server_title.generic.y = 30; for (i = 0; i < MAX_LOCAL_SERVERS; i++) { s_joinserver_server_actions[i].generic.type = MTYPE_ACTION; s_joinserver_server_actions[i].generic.name = local_server_names[i]; s_joinserver_server_actions[i].generic.flags = QMF_LEFT_JUSTIFY; s_joinserver_server_actions[i].generic.x = 0; s_joinserver_server_actions[i].generic.y = 40 + i * 10; s_joinserver_server_actions[i].generic.callback = JoinServerFunc; s_joinserver_server_actions[i].generic.statusbar = local_server_netadr_strings[i]; } Menu_AddItem(&s_joinserver_menu, &s_joinserver_address_book_action); Menu_AddItem(&s_joinserver_menu, &s_joinserver_server_title); Menu_AddItem(&s_joinserver_menu, &s_joinserver_search_action); for (i = 0; i < 8; i++) { Menu_AddItem(&s_joinserver_menu, &s_joinserver_server_actions[i]); } Menu_Center(&s_joinserver_menu); SearchLocalGames(); } static void JoinServer_MenuDraw(void) { M_Banner("m_banner_join_server"); Menu_Draw(&s_joinserver_menu); M_Popup(); } static const char * JoinServer_MenuKey(int key) { if (m_popup_string) { m_popup_string = NULL; return NULL; } return Default_MenuKey(&s_joinserver_menu, key); } static void M_Menu_JoinServer_f(void) { JoinServer_MenuInit(); M_PushMenu(JoinServer_MenuDraw, JoinServer_MenuKey); } /* * START SERVER MENU */ static menuframework_s s_startserver_menu; static char **mapnames = NULL; static int nummaps = 0; static menuaction_s s_startserver_start_action; static menuaction_s s_startserver_dmoptions_action; static menufield_s s_timelimit_field; static menufield_s s_fraglimit_field; static menufield_s s_maxclients_field; static menufield_s s_hostname_field; static menulist_s s_startmap_list; static menulist_s s_rules_box; static void DMOptionsFunc(void *self) { M_Menu_DMOptions_f(); } static void RulesChangeFunc(void *self) { /* Deathmatch */ if (s_rules_box.curvalue == 0) { s_maxclients_field.generic.statusbar = NULL; s_startserver_dmoptions_action.generic.statusbar = NULL; } /* Ground Zero game modes */ else if (Developer_searchpath() == 2) { if (s_rules_box.curvalue == 2) { s_maxclients_field.generic.statusbar = NULL; s_startserver_dmoptions_action.generic.statusbar = NULL; } } } static void StartServerActionFunc(void *self) { char startmap[1024]; float timelimit; float fraglimit; float maxclients; char *spot; strcpy(startmap, strchr(mapnames[s_startmap_list.curvalue], '\n') + 1); maxclients = (float)strtod(s_maxclients_field.buffer, (char **)NULL); timelimit = (float)strtod(s_timelimit_field.buffer, (char **)NULL); fraglimit = (float)strtod(s_fraglimit_field.buffer, (char **)NULL); Cvar_SetValue("maxclients", ClampCvar(0, maxclients, maxclients)); Cvar_SetValue("timelimit", ClampCvar(0, timelimit, timelimit)); Cvar_SetValue("fraglimit", ClampCvar(0, fraglimit, fraglimit)); Cvar_Set("hostname", s_hostname_field.buffer); if ((s_rules_box.curvalue < 2) || (Developer_searchpath() != 2)) { Cvar_SetValue("deathmatch", (float)!s_rules_box.curvalue); Cvar_SetValue("coop", (float)s_rules_box.curvalue); } else { Cvar_SetValue("deathmatch", 1); /* deathmatch is always true for rogue games */ Cvar_SetValue("coop", 0); /* This works for at least the main game and both addons */ } spot = NULL; if (s_rules_box.curvalue == 1) { if (Q_stricmp(startmap, "bunk1") == 0) { spot = "start"; } else if (Q_stricmp(startmap, "mintro") == 0) { spot = "start"; } else if (Q_stricmp(startmap, "fact1") == 0) { spot = "start"; } else if (Q_stricmp(startmap, "power1") == 0) { spot = "pstart"; } else if (Q_stricmp(startmap, "biggun") == 0) { spot = "bstart"; } else if (Q_stricmp(startmap, "hangar1") == 0) { spot = "unitstart"; } else if (Q_stricmp(startmap, "city1") == 0) { spot = "unitstart"; } else if (Q_stricmp(startmap, "boss1") == 0) { spot = "bosstart"; } } if (spot) { if (Com_ServerState()) { Cbuf_AddText("disconnect\n"); } Cbuf_AddText(va("gamemap \"*%s$%s\"\n", startmap, spot)); } else { Cbuf_AddText(va("map %s\n", startmap)); } M_ForceMenuOff(); } static void StartServer_MenuInit(void) { static const char *dm_coop_names[] = { "deathmatch", "cooperative", 0 }; static const char *dm_coop_names_rogue[] = { "deathmatch", "cooperative", "tag", 0 }; char *buffer; char mapsname[1024]; char *s; int length; int i; FILE *fp; float scale = SCR_GetMenuScale(); /* initialize list of maps once, reuse it afterwards (=> it isn't freed) */ if (mapnames == NULL) { /* load the list of map names */ Com_sprintf(mapsname, sizeof(mapsname), "%s/maps.lst", FS_Gamedir()); if ((fp = fopen(mapsname, "rb")) == 0) { if ((length = FS_LoadFile("maps.lst", (void **)&buffer)) == -1) { Com_Error(ERR_DROP, "couldn't find maps.lst\n"); } } else { fseek(fp, 0, SEEK_END); length = ftell(fp); fseek(fp, 0, SEEK_SET); buffer = malloc(length); fread(buffer, length, 1, fp); } s = buffer; i = 0; while (i < length) { if (s[i] == '\r') { nummaps++; } i++; } if (nummaps == 0) { Com_Error(ERR_DROP, "no maps in maps.lst\n"); } mapnames = malloc(sizeof(char *) * (nummaps + 1)); memset(mapnames, 0, sizeof(char *) * (nummaps + 1)); s = buffer; for (i = 0; i < nummaps; i++) { char shortname[MAX_TOKEN_CHARS]; char longname[MAX_TOKEN_CHARS]; char scratch[200]; int j, l; strcpy(shortname, COM_Parse(&s)); l = strlen(shortname); for (j = 0; j < l; j++) { shortname[j] = toupper(shortname[j]); } strcpy(longname, COM_Parse(&s)); Com_sprintf(scratch, sizeof(scratch), "%s\n%s", longname, shortname); mapnames[i] = malloc(strlen(scratch) + 1); strcpy(mapnames[i], scratch); } mapnames[nummaps] = 0; if (fp != 0) { fclose(fp); fp = 0; free(buffer); } else { FS_FreeFile(buffer); } } /* initialize the menu stuff */ s_startserver_menu.x = (int)(viddef.width * 0.50f); s_startserver_menu.nitems = 0; s_startmap_list.generic.type = MTYPE_SPINCONTROL; s_startmap_list.generic.x = 0; s_startmap_list.generic.y = 0; s_startmap_list.generic.name = "initial map"; s_startmap_list.itemnames = (const char **)mapnames; s_rules_box.generic.type = MTYPE_SPINCONTROL; s_rules_box.generic.x = 0; s_rules_box.generic.y = 20; s_rules_box.generic.name = "rules"; /* Ground Zero games only available with rogue game */ if (Developer_searchpath() == 2) { s_rules_box.itemnames = dm_coop_names_rogue; } else { s_rules_box.itemnames = dm_coop_names; } if (Cvar_VariableValue("coop")) { s_rules_box.curvalue = 1; } else { s_rules_box.curvalue = 0; } s_rules_box.generic.callback = RulesChangeFunc; s_timelimit_field.generic.type = MTYPE_FIELD; s_timelimit_field.generic.name = "time limit"; s_timelimit_field.generic.flags = QMF_NUMBERSONLY; s_timelimit_field.generic.x = 0; s_timelimit_field.generic.y = 36; s_timelimit_field.generic.statusbar = "0 = no limit"; s_timelimit_field.length = 3; s_timelimit_field.visible_length = 3; strcpy(s_timelimit_field.buffer, Cvar_VariableString("timelimit")); s_fraglimit_field.generic.type = MTYPE_FIELD; s_fraglimit_field.generic.name = "frag limit"; s_fraglimit_field.generic.flags = QMF_NUMBERSONLY; s_fraglimit_field.generic.x = 0; s_fraglimit_field.generic.y = 54; s_fraglimit_field.generic.statusbar = "0 = no limit"; s_fraglimit_field.length = 3; s_fraglimit_field.visible_length = 3; strcpy(s_fraglimit_field.buffer, Cvar_VariableString("fraglimit")); /* maxclients determines the maximum number of players that can join the game. If maxclients is only "1" then we should default the menu option to 8 players, otherwise use whatever its current value is. Clamping will be done when the server is actually started. */ s_maxclients_field.generic.type = MTYPE_FIELD; s_maxclients_field.generic.name = "max players"; s_maxclients_field.generic.flags = QMF_NUMBERSONLY; s_maxclients_field.generic.x = 0; s_maxclients_field.generic.y = 72; s_maxclients_field.generic.statusbar = NULL; s_maxclients_field.length = 3; s_maxclients_field.visible_length = 3; if (Cvar_VariableValue("maxclients") == 1) { strcpy(s_maxclients_field.buffer, "8"); } else { strcpy(s_maxclients_field.buffer, Cvar_VariableString("maxclients")); } s_hostname_field.generic.type = MTYPE_FIELD; s_hostname_field.generic.name = "hostname"; s_hostname_field.generic.flags = 0; s_hostname_field.generic.x = 0; s_hostname_field.generic.y = 90; s_hostname_field.generic.statusbar = NULL; s_hostname_field.length = 12; s_hostname_field.visible_length = 12; strcpy(s_hostname_field.buffer, Cvar_VariableString("hostname")); s_startserver_dmoptions_action.generic.type = MTYPE_ACTION; s_startserver_dmoptions_action.generic.name = " deathmatch flags"; s_startserver_dmoptions_action.generic.flags = QMF_LEFT_JUSTIFY; s_startserver_dmoptions_action.generic.x = 24 * scale; s_startserver_dmoptions_action.generic.y = 108; s_startserver_dmoptions_action.generic.statusbar = NULL; s_startserver_dmoptions_action.generic.callback = DMOptionsFunc; s_startserver_start_action.generic.type = MTYPE_ACTION; s_startserver_start_action.generic.name = " begin"; s_startserver_start_action.generic.flags = QMF_LEFT_JUSTIFY; s_startserver_start_action.generic.x = 24 * scale; s_startserver_start_action.generic.y = 128; s_startserver_start_action.generic.callback = StartServerActionFunc; Menu_AddItem(&s_startserver_menu, &s_startmap_list); Menu_AddItem(&s_startserver_menu, &s_rules_box); Menu_AddItem(&s_startserver_menu, &s_timelimit_field); Menu_AddItem(&s_startserver_menu, &s_fraglimit_field); Menu_AddItem(&s_startserver_menu, &s_maxclients_field); Menu_AddItem(&s_startserver_menu, &s_hostname_field); Menu_AddItem(&s_startserver_menu, &s_startserver_dmoptions_action); Menu_AddItem(&s_startserver_menu, &s_startserver_start_action); Menu_Center(&s_startserver_menu); /* call this now to set proper inital state */ RulesChangeFunc(NULL); } static void StartServer_MenuDraw(void) { Menu_Draw(&s_startserver_menu); } static const char * StartServer_MenuKey(int key) { return Default_MenuKey(&s_startserver_menu, key); } static void M_Menu_StartServer_f(void) { StartServer_MenuInit(); M_PushMenu(StartServer_MenuDraw, StartServer_MenuKey); } /* * DMOPTIONS BOOK MENU */ static char dmoptions_statusbar[128]; static menuframework_s s_dmoptions_menu; static menulist_s s_friendlyfire_box; static menulist_s s_falls_box; static menulist_s s_weapons_stay_box; static menulist_s s_instant_powerups_box; static menulist_s s_powerups_box; static menulist_s s_health_box; static menulist_s s_spawn_farthest_box; static menulist_s s_teamplay_box; static menulist_s s_samelevel_box; static menulist_s s_force_respawn_box; static menulist_s s_armor_box; static menulist_s s_allow_exit_box; static menulist_s s_infinite_ammo_box; static menulist_s s_fixed_fov_box; static menulist_s s_quad_drop_box; static menulist_s s_no_mines_box; static menulist_s s_no_nukes_box; static menulist_s s_stack_double_box; static menulist_s s_no_spheres_box; static void DMFlagCallback(void *self) { menulist_s *f = (menulist_s *)self; int flags; int bit = 0; flags = Cvar_VariableValue("dmflags"); if (f == &s_friendlyfire_box) { if (f->curvalue) { flags &= ~DF_NO_FRIENDLY_FIRE; } else { flags |= DF_NO_FRIENDLY_FIRE; } goto setvalue; } else if (f == &s_falls_box) { if (f->curvalue) { flags &= ~DF_NO_FALLING; } else { flags |= DF_NO_FALLING; } goto setvalue; } else if (f == &s_weapons_stay_box) { bit = DF_WEAPONS_STAY; } else if (f == &s_instant_powerups_box) { bit = DF_INSTANT_ITEMS; } else if (f == &s_allow_exit_box) { bit = DF_ALLOW_EXIT; } else if (f == &s_powerups_box) { if (f->curvalue) { flags &= ~DF_NO_ITEMS; } else { flags |= DF_NO_ITEMS; } goto setvalue; } else if (f == &s_health_box) { if (f->curvalue) { flags &= ~DF_NO_HEALTH; } else { flags |= DF_NO_HEALTH; } goto setvalue; } else if (f == &s_spawn_farthest_box) { bit = DF_SPAWN_FARTHEST; } else if (f == &s_teamplay_box) { if (f->curvalue == 1) { flags |= DF_SKINTEAMS; flags &= ~DF_MODELTEAMS; } else if (f->curvalue == 2) { flags |= DF_MODELTEAMS; flags &= ~DF_SKINTEAMS; } else { flags &= ~(DF_MODELTEAMS | DF_SKINTEAMS); } goto setvalue; } else if (f == &s_samelevel_box) { bit = DF_SAME_LEVEL; } else if (f == &s_force_respawn_box) { bit = DF_FORCE_RESPAWN; } else if (f == &s_armor_box) { if (f->curvalue) { flags &= ~DF_NO_ARMOR; } else { flags |= DF_NO_ARMOR; } goto setvalue; } else if (f == &s_infinite_ammo_box) { bit = DF_INFINITE_AMMO; } else if (f == &s_fixed_fov_box) { bit = DF_FIXED_FOV; } else if (f == &s_quad_drop_box) { bit = DF_QUAD_DROP; } else if (Developer_searchpath() == 2) { if (f == &s_no_mines_box) { bit = DF_NO_MINES; } else if (f == &s_no_nukes_box) { bit = DF_NO_NUKES; } else if (f == &s_stack_double_box) { bit = DF_NO_STACK_DOUBLE; } else if (f == &s_no_spheres_box) { bit = DF_NO_SPHERES; } } if (f) { if (f->curvalue == 0) { flags &= ~bit; } else { flags |= bit; } } setvalue: Cvar_SetValue("dmflags", (float)flags); Com_sprintf(dmoptions_statusbar, sizeof(dmoptions_statusbar), "dmflags = %d", flags); } static void DMOptions_MenuInit(void) { static const char *yes_no_names[] = { "no", "yes", 0 }; static const char *teamplay_names[] = { "disabled", "by skin", "by model", 0 }; int dmflags = Cvar_VariableValue("dmflags"); int y = 0; s_dmoptions_menu.x = (int)(viddef.width * 0.50f); s_dmoptions_menu.nitems = 0; s_falls_box.generic.type = MTYPE_SPINCONTROL; s_falls_box.generic.x = 0; s_falls_box.generic.y = y; s_falls_box.generic.name = "falling damage"; s_falls_box.generic.callback = DMFlagCallback; s_falls_box.itemnames = yes_no_names; s_falls_box.curvalue = (dmflags & DF_NO_FALLING) == 0; s_weapons_stay_box.generic.type = MTYPE_SPINCONTROL; s_weapons_stay_box.generic.x = 0; s_weapons_stay_box.generic.y = y += 10; s_weapons_stay_box.generic.name = "weapons stay"; s_weapons_stay_box.generic.callback = DMFlagCallback; s_weapons_stay_box.itemnames = yes_no_names; s_weapons_stay_box.curvalue = (dmflags & DF_WEAPONS_STAY) != 0; s_instant_powerups_box.generic.type = MTYPE_SPINCONTROL; s_instant_powerups_box.generic.x = 0; s_instant_powerups_box.generic.y = y += 10; s_instant_powerups_box.generic.name = "instant powerups"; s_instant_powerups_box.generic.callback = DMFlagCallback; s_instant_powerups_box.itemnames = yes_no_names; s_instant_powerups_box.curvalue = (dmflags & DF_INSTANT_ITEMS) != 0; s_powerups_box.generic.type = MTYPE_SPINCONTROL; s_powerups_box.generic.x = 0; s_powerups_box.generic.y = y += 10; s_powerups_box.generic.name = "allow powerups"; s_powerups_box.generic.callback = DMFlagCallback; s_powerups_box.itemnames = yes_no_names; s_powerups_box.curvalue = (dmflags & DF_NO_ITEMS) == 0; s_health_box.generic.type = MTYPE_SPINCONTROL; s_health_box.generic.x = 0; s_health_box.generic.y = y += 10; s_health_box.generic.callback = DMFlagCallback; s_health_box.generic.name = "allow health"; s_health_box.itemnames = yes_no_names; s_health_box.curvalue = (dmflags & DF_NO_HEALTH) == 0; s_armor_box.generic.type = MTYPE_SPINCONTROL; s_armor_box.generic.x = 0; s_armor_box.generic.y = y += 10; s_armor_box.generic.name = "allow armor"; s_armor_box.generic.callback = DMFlagCallback; s_armor_box.itemnames = yes_no_names; s_armor_box.curvalue = (dmflags & DF_NO_ARMOR) == 0; s_spawn_farthest_box.generic.type = MTYPE_SPINCONTROL; s_spawn_farthest_box.generic.x = 0; s_spawn_farthest_box.generic.y = y += 10; s_spawn_farthest_box.generic.name = "spawn farthest"; s_spawn_farthest_box.generic.callback = DMFlagCallback; s_spawn_farthest_box.itemnames = yes_no_names; s_spawn_farthest_box.curvalue = (dmflags & DF_SPAWN_FARTHEST) != 0; s_samelevel_box.generic.type = MTYPE_SPINCONTROL; s_samelevel_box.generic.x = 0; s_samelevel_box.generic.y = y += 10; s_samelevel_box.generic.name = "same map"; s_samelevel_box.generic.callback = DMFlagCallback; s_samelevel_box.itemnames = yes_no_names; s_samelevel_box.curvalue = (dmflags & DF_SAME_LEVEL) != 0; s_force_respawn_box.generic.type = MTYPE_SPINCONTROL; s_force_respawn_box.generic.x = 0; s_force_respawn_box.generic.y = y += 10; s_force_respawn_box.generic.name = "force respawn"; s_force_respawn_box.generic.callback = DMFlagCallback; s_force_respawn_box.itemnames = yes_no_names; s_force_respawn_box.curvalue = (dmflags & DF_FORCE_RESPAWN) != 0; s_teamplay_box.generic.type = MTYPE_SPINCONTROL; s_teamplay_box.generic.x = 0; s_teamplay_box.generic.y = y += 10; s_teamplay_box.generic.name = "teamplay"; s_teamplay_box.generic.callback = DMFlagCallback; s_teamplay_box.itemnames = teamplay_names; s_allow_exit_box.generic.type = MTYPE_SPINCONTROL; s_allow_exit_box.generic.x = 0; s_allow_exit_box.generic.y = y += 10; s_allow_exit_box.generic.name = "allow exit"; s_allow_exit_box.generic.callback = DMFlagCallback; s_allow_exit_box.itemnames = yes_no_names; s_allow_exit_box.curvalue = (dmflags & DF_ALLOW_EXIT) != 0; s_infinite_ammo_box.generic.type = MTYPE_SPINCONTROL; s_infinite_ammo_box.generic.x = 0; s_infinite_ammo_box.generic.y = y += 10; s_infinite_ammo_box.generic.name = "infinite ammo"; s_infinite_ammo_box.generic.callback = DMFlagCallback; s_infinite_ammo_box.itemnames = yes_no_names; s_infinite_ammo_box.curvalue = (dmflags & DF_INFINITE_AMMO) != 0; s_fixed_fov_box.generic.type = MTYPE_SPINCONTROL; s_fixed_fov_box.generic.x = 0; s_fixed_fov_box.generic.y = y += 10; s_fixed_fov_box.generic.name = "fixed FOV"; s_fixed_fov_box.generic.callback = DMFlagCallback; s_fixed_fov_box.itemnames = yes_no_names; s_fixed_fov_box.curvalue = (dmflags & DF_FIXED_FOV) != 0; s_quad_drop_box.generic.type = MTYPE_SPINCONTROL; s_quad_drop_box.generic.x = 0; s_quad_drop_box.generic.y = y += 10; s_quad_drop_box.generic.name = "quad drop"; s_quad_drop_box.generic.callback = DMFlagCallback; s_quad_drop_box.itemnames = yes_no_names; s_quad_drop_box.curvalue = (dmflags & DF_QUAD_DROP) != 0; s_friendlyfire_box.generic.type = MTYPE_SPINCONTROL; s_friendlyfire_box.generic.x = 0; s_friendlyfire_box.generic.y = y += 10; s_friendlyfire_box.generic.name = "friendly fire"; s_friendlyfire_box.generic.callback = DMFlagCallback; s_friendlyfire_box.itemnames = yes_no_names; s_friendlyfire_box.curvalue = (dmflags & DF_NO_FRIENDLY_FIRE) == 0; if (Developer_searchpath() == 2) { s_no_mines_box.generic.type = MTYPE_SPINCONTROL; s_no_mines_box.generic.x = 0; s_no_mines_box.generic.y = y += 10; s_no_mines_box.generic.name = "remove mines"; s_no_mines_box.generic.callback = DMFlagCallback; s_no_mines_box.itemnames = yes_no_names; s_no_mines_box.curvalue = (dmflags & DF_NO_MINES) != 0; s_no_nukes_box.generic.type = MTYPE_SPINCONTROL; s_no_nukes_box.generic.x = 0; s_no_nukes_box.generic.y = y += 10; s_no_nukes_box.generic.name = "remove nukes"; s_no_nukes_box.generic.callback = DMFlagCallback; s_no_nukes_box.itemnames = yes_no_names; s_no_nukes_box.curvalue = (dmflags & DF_NO_NUKES) != 0; s_stack_double_box.generic.type = MTYPE_SPINCONTROL; s_stack_double_box.generic.x = 0; s_stack_double_box.generic.y = y += 10; s_stack_double_box.generic.name = "2x/4x stacking off"; s_stack_double_box.generic.callback = DMFlagCallback; s_stack_double_box.itemnames = yes_no_names; s_stack_double_box.curvalue = (dmflags & DF_NO_STACK_DOUBLE) != 0; s_no_spheres_box.generic.type = MTYPE_SPINCONTROL; s_no_spheres_box.generic.x = 0; s_no_spheres_box.generic.y = y += 10; s_no_spheres_box.generic.name = "remove spheres"; s_no_spheres_box.generic.callback = DMFlagCallback; s_no_spheres_box.itemnames = yes_no_names; s_no_spheres_box.curvalue = (dmflags & DF_NO_SPHERES) != 0; } Menu_AddItem(&s_dmoptions_menu, &s_falls_box); Menu_AddItem(&s_dmoptions_menu, &s_weapons_stay_box); Menu_AddItem(&s_dmoptions_menu, &s_instant_powerups_box); Menu_AddItem(&s_dmoptions_menu, &s_powerups_box); Menu_AddItem(&s_dmoptions_menu, &s_health_box); Menu_AddItem(&s_dmoptions_menu, &s_armor_box); Menu_AddItem(&s_dmoptions_menu, &s_spawn_farthest_box); Menu_AddItem(&s_dmoptions_menu, &s_samelevel_box); Menu_AddItem(&s_dmoptions_menu, &s_force_respawn_box); Menu_AddItem(&s_dmoptions_menu, &s_teamplay_box); Menu_AddItem(&s_dmoptions_menu, &s_allow_exit_box); Menu_AddItem(&s_dmoptions_menu, &s_infinite_ammo_box); Menu_AddItem(&s_dmoptions_menu, &s_fixed_fov_box); Menu_AddItem(&s_dmoptions_menu, &s_quad_drop_box); Menu_AddItem(&s_dmoptions_menu, &s_friendlyfire_box); if (Developer_searchpath() == 2) { Menu_AddItem(&s_dmoptions_menu, &s_no_mines_box); Menu_AddItem(&s_dmoptions_menu, &s_no_nukes_box); Menu_AddItem(&s_dmoptions_menu, &s_stack_double_box); Menu_AddItem(&s_dmoptions_menu, &s_no_spheres_box); } Menu_Center(&s_dmoptions_menu); /* set the original dmflags statusbar */ DMFlagCallback(0); Menu_SetStatusBar(&s_dmoptions_menu, dmoptions_statusbar); } static void DMOptions_MenuDraw(void) { Menu_Draw(&s_dmoptions_menu); } const char * DMOptions_MenuKey(int key) { return Default_MenuKey(&s_dmoptions_menu, key); } static void M_Menu_DMOptions_f(void) { DMOptions_MenuInit(); M_PushMenu(DMOptions_MenuDraw, DMOptions_MenuKey); } /* * DOWNLOADOPTIONS BOOK MENU */ static menuframework_s s_downloadoptions_menu; static menuseparator_s s_download_title; static menulist_s s_allow_download_box; static menulist_s s_allow_download_maps_box; static menulist_s s_allow_download_models_box; static menulist_s s_allow_download_players_box; static menulist_s s_allow_download_sounds_box; static void DownloadCallback(void *self) { menulist_s *f = (menulist_s *)self; if (f == &s_allow_download_box) { Cvar_SetValue("allow_download", (float)f->curvalue); } else if (f == &s_allow_download_maps_box) { Cvar_SetValue("allow_download_maps", (float)f->curvalue); } else if (f == &s_allow_download_models_box) { Cvar_SetValue("allow_download_models", (float)f->curvalue); } else if (f == &s_allow_download_players_box) { Cvar_SetValue("allow_download_players", (float)f->curvalue); } else if (f == &s_allow_download_sounds_box) { Cvar_SetValue("allow_download_sounds", (float)f->curvalue); } } static void DownloadOptions_MenuInit(void) { static const char *yes_no_names[] = { "no", "yes", 0 }; int y = 0; float scale = SCR_GetMenuScale(); s_downloadoptions_menu.x = (int)(viddef.width * 0.50f); s_downloadoptions_menu.nitems = 0; s_download_title.generic.type = MTYPE_SEPARATOR; s_download_title.generic.name = "Download Options"; s_download_title.generic.x = 48 * scale; s_download_title.generic.y = y; s_allow_download_box.generic.type = MTYPE_SPINCONTROL; s_allow_download_box.generic.x = 0; s_allow_download_box.generic.y = y += 20; s_allow_download_box.generic.name = "allow downloading"; s_allow_download_box.generic.callback = DownloadCallback; s_allow_download_box.itemnames = yes_no_names; s_allow_download_box.curvalue = (Cvar_VariableValue("allow_download") != 0); s_allow_download_maps_box.generic.type = MTYPE_SPINCONTROL; s_allow_download_maps_box.generic.x = 0; s_allow_download_maps_box.generic.y = y += 20; s_allow_download_maps_box.generic.name = "maps"; s_allow_download_maps_box.generic.callback = DownloadCallback; s_allow_download_maps_box.itemnames = yes_no_names; s_allow_download_maps_box.curvalue = (Cvar_VariableValue("allow_download_maps") != 0); s_allow_download_players_box.generic.type = MTYPE_SPINCONTROL; s_allow_download_players_box.generic.x = 0; s_allow_download_players_box.generic.y = y += 10; s_allow_download_players_box.generic.name = "player models/skins"; s_allow_download_players_box.generic.callback = DownloadCallback; s_allow_download_players_box.itemnames = yes_no_names; s_allow_download_players_box.curvalue = (Cvar_VariableValue("allow_download_players") != 0); s_allow_download_models_box.generic.type = MTYPE_SPINCONTROL; s_allow_download_models_box.generic.x = 0; s_allow_download_models_box.generic.y = y += 10; s_allow_download_models_box.generic.name = "models"; s_allow_download_models_box.generic.callback = DownloadCallback; s_allow_download_models_box.itemnames = yes_no_names; s_allow_download_models_box.curvalue = (Cvar_VariableValue("allow_download_models") != 0); s_allow_download_sounds_box.generic.type = MTYPE_SPINCONTROL; s_allow_download_sounds_box.generic.x = 0; s_allow_download_sounds_box.generic.y = y += 10; s_allow_download_sounds_box.generic.name = "sounds"; s_allow_download_sounds_box.generic.callback = DownloadCallback; s_allow_download_sounds_box.itemnames = yes_no_names; s_allow_download_sounds_box.curvalue = (Cvar_VariableValue("allow_download_sounds") != 0); Menu_AddItem(&s_downloadoptions_menu, &s_download_title); Menu_AddItem(&s_downloadoptions_menu, &s_allow_download_box); Menu_AddItem(&s_downloadoptions_menu, &s_allow_download_maps_box); Menu_AddItem(&s_downloadoptions_menu, &s_allow_download_players_box); Menu_AddItem(&s_downloadoptions_menu, &s_allow_download_models_box); Menu_AddItem(&s_downloadoptions_menu, &s_allow_download_sounds_box); Menu_Center(&s_downloadoptions_menu); /* skip over title */ if (s_downloadoptions_menu.cursor == 0) { s_downloadoptions_menu.cursor = 1; } } static void DownloadOptions_MenuDraw(void) { Menu_Draw(&s_downloadoptions_menu); } static const char * DownloadOptions_MenuKey(int key) { return Default_MenuKey(&s_downloadoptions_menu, key); } static void M_Menu_DownloadOptions_f(void) { DownloadOptions_MenuInit(); M_PushMenu(DownloadOptions_MenuDraw, DownloadOptions_MenuKey); } /* * ADDRESS BOOK MENU */ #define NUM_ADDRESSBOOK_ENTRIES 9 static menuframework_s s_addressbook_menu; static menufield_s s_addressbook_fields[NUM_ADDRESSBOOK_ENTRIES]; static void AddressBook_MenuInit(void) { int i; float scale = SCR_GetMenuScale(); s_addressbook_menu.x = viddef.width / scale / 2 - 142 / scale; s_addressbook_menu.y = viddef.height / (2 * scale) - 58; s_addressbook_menu.nitems = 0; for (i = 0; i < NUM_ADDRESSBOOK_ENTRIES; i++) { cvar_t *adr; char buffer[20]; Com_sprintf(buffer, sizeof(buffer), "adr%d", i); adr = Cvar_Get(buffer, "", CVAR_ARCHIVE); s_addressbook_fields[i].generic.type = MTYPE_FIELD; s_addressbook_fields[i].generic.name = 0; s_addressbook_fields[i].generic.callback = 0; s_addressbook_fields[i].generic.x = 0; s_addressbook_fields[i].generic.y = i * 18 + 0; s_addressbook_fields[i].generic.localdata[0] = i; s_addressbook_fields[i].cursor = 0; s_addressbook_fields[i].length = 60; s_addressbook_fields[i].visible_length = 30; strcpy(s_addressbook_fields[i].buffer, adr->string); Menu_AddItem(&s_addressbook_menu, &s_addressbook_fields[i]); } } const char * AddressBook_MenuKey(int key) { if (key == K_ESCAPE) { int index; char buffer[20]; for (index = 0; index < NUM_ADDRESSBOOK_ENTRIES; index++) { Com_sprintf(buffer, sizeof(buffer), "adr%d", index); Cvar_Set(buffer, s_addressbook_fields[index].buffer); } } return Default_MenuKey(&s_addressbook_menu, key); } static void AddressBook_MenuDraw(void) { M_Banner("m_banner_addressbook"); Menu_Draw(&s_addressbook_menu); } static void M_Menu_AddressBook_f(void) { AddressBook_MenuInit(); M_PushMenu(AddressBook_MenuDraw, AddressBook_MenuKey); } /* * PLAYER CONFIG MENU */ static menuframework_s s_player_config_menu; static menufield_s s_player_name_field; static menulist_s s_player_model_box; static menulist_s s_player_skin_box; static menulist_s s_player_handedness_box; static menulist_s s_player_rate_box; static menuseparator_s s_player_skin_title; static menuseparator_s s_player_model_title; static menuseparator_s s_player_hand_title; static menuseparator_s s_player_rate_title; static menuaction_s s_player_download_action; #define MAX_DISPLAYNAME 16 #define MAX_PLAYERMODELS 1024 typedef struct { int nskins; char **skindisplaynames; char displayname[MAX_DISPLAYNAME]; char directory[MAX_QPATH]; } playermodelinfo_s; static playermodelinfo_s s_pmi[MAX_PLAYERMODELS]; static char *s_pmnames[MAX_PLAYERMODELS]; static int s_numplayermodels; static int rate_tbl[] = {2500, 3200, 5000, 10000, 25000, 0}; static const char *rate_names[] = {"28.8 Modem", "33.6 Modem", "Single ISDN", "Dual ISDN/Cable", "T1/LAN", "User defined", 0 }; static void DownloadOptionsFunc(void *self) { M_Menu_DownloadOptions_f(); } static void HandednessCallback(void *unused) { Cvar_SetValue("hand", (float)s_player_handedness_box.curvalue); } static void RateCallback(void *unused) { if (s_player_rate_box.curvalue != sizeof(rate_tbl) / sizeof(*rate_tbl) - 1) { Cvar_SetValue("rate", (float)rate_tbl[s_player_rate_box.curvalue]); } } static void ModelCallback(void *unused) { s_player_skin_box.itemnames = (const char **)s_pmi[s_player_model_box.curvalue].skindisplaynames; s_player_skin_box.curvalue = 0; } static void FreeFileList(char **list, int n) { int i; for (i = 0; i < n; i++) { if (list[i]) { free(list[i]); list[i] = 0; } } free(list); } static qboolean IconOfSkinExists(char *skin, char **pcxfiles, int npcxfiles) { int i; char scratch[1024]; strcpy(scratch, skin); *strrchr(scratch, '.') = 0; strcat(scratch, "_i.pcx"); for (i = 0; i < npcxfiles; i++) { if (strcmp(pcxfiles[i], scratch) == 0) { return true; } } return false; } extern char **FS_ListFiles(char *, int *, unsigned, unsigned); static qboolean PlayerConfig_ScanDirectories(void) { char findname[1024]; char scratch[1024]; int ndirs = 0, npms = 0; char **dirnames = NULL; char *path = NULL; int i; s_numplayermodels = 0; /* get a list of directories */ do { path = FS_NextPath(path); /* If FS_NextPath returns NULL we get a SEGV on the next line. On other platforms this propably works (path becomes the null string or something like that). */ if (path == NULL) { break; } Com_sprintf(findname, sizeof(findname), "%s/players/*.*", path); if ((dirnames = FS_ListFiles(findname, &ndirs, SFF_SUBDIR, 0)) != 0) { break; } } while (path); if (!dirnames) { return false; } /* go through the subdirectories */ npms = ndirs; if (npms > MAX_PLAYERMODELS) { npms = MAX_PLAYERMODELS; } for (i = 0; i < npms; i++) { int k, s; char *a, *b, *c; char **pcxnames; char **skinnames; int npcxfiles; int nskins = 0; if (dirnames[i] == 0) { continue; } /* verify the existence of tris.md2 */ strcpy(scratch, dirnames[i]); strcat(scratch, "/tris.md2"); if (!Sys_FindFirst(scratch, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM)) { free(dirnames[i]); dirnames[i] = 0; Sys_FindClose(); continue; } Sys_FindClose(); /* verify the existence of at least one pcx skin */ strcpy(scratch, dirnames[i]); strcat(scratch, "/*.pcx"); pcxnames = FS_ListFiles(scratch, &npcxfiles, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM); if (!pcxnames) { free(dirnames[i]); dirnames[i] = 0; continue; } /* count valid skins, which consist of a skin with a matching "_i" icon */ for (k = 0; k < npcxfiles - 1; k++) { if (!strstr(pcxnames[k], "_i.pcx")) { if (IconOfSkinExists(pcxnames[k], pcxnames, npcxfiles - 1)) { nskins++; } } } if (!nskins) { continue; } skinnames = malloc(sizeof(char *) * (nskins + 1)); memset(skinnames, 0, sizeof(char *) * (nskins + 1)); /* copy the valid skins */ for (s = 0, k = 0; k < npcxfiles - 1; k++) { char *a, *b, *c; if (!strstr(pcxnames[k], "_i.pcx")) { if (IconOfSkinExists(pcxnames[k], pcxnames, npcxfiles - 1)) { a = strrchr(pcxnames[k], '/'); b = strrchr(pcxnames[k], '\\'); if (a > b) { c = a; } else { c = b; } strcpy(scratch, c + 1); if (strrchr(scratch, '.')) { *strrchr(scratch, '.') = 0; } skinnames[s] = strdup(scratch); s++; } } } /* at this point we have a valid player model */ s_pmi[s_numplayermodels].nskins = nskins; s_pmi[s_numplayermodels].skindisplaynames = skinnames; /* make short name for the model */ a = strrchr(dirnames[i], '/'); b = strrchr(dirnames[i], '\\'); if (a > b) { c = a; } else { c = b; } Q_strlcpy(s_pmi[s_numplayermodels].displayname, c + 1, sizeof(s_pmi[s_numplayermodels].displayname)); Q_strlcpy(s_pmi[s_numplayermodels].directory, c + 1, sizeof(s_pmi[s_numplayermodels].directory)); FreeFileList(pcxnames, npcxfiles); s_numplayermodels++; } if (dirnames) { FreeFileList(dirnames, ndirs); } return true; } static int pmicmpfnc(const void *_a, const void *_b) { const playermodelinfo_s *a = (const playermodelinfo_s *)_a; const playermodelinfo_s *b = (const playermodelinfo_s *)_b; /* sort by male, female, then alphabetical */ if (strcmp(a->directory, "male") == 0) { return -1; } else if (strcmp(b->directory, "male") == 0) { return 1; } if (strcmp(a->directory, "female") == 0) { return -1; } else if (strcmp(b->directory, "female") == 0) { return 1; } return strcmp(a->directory, b->directory); } static qboolean PlayerConfig_MenuInit(void) { extern cvar_t *name; extern cvar_t *skin; char currentdirectory[1024]; char currentskin[1024]; int i = 0; float scale = SCR_GetMenuScale(); int currentdirectoryindex = 0; int currentskinindex = 0; cvar_t *hand = Cvar_Get("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE); static const char *handedness[] = {"right", "left", "center", 0}; PlayerConfig_ScanDirectories(); if (s_numplayermodels == 0) { return false; } strcpy(currentdirectory, skin->string); if (strchr(currentdirectory, '/')) { strcpy(currentskin, strchr(currentdirectory, '/') + 1); *strchr(currentdirectory, '/') = 0; } else if (strchr(currentdirectory, '\\')) { strcpy(currentskin, strchr(currentdirectory, '\\') + 1); *strchr(currentdirectory, '\\') = 0; } else { strcpy(currentdirectory, "male"); strcpy(currentskin, "grunt"); } qsort(s_pmi, s_numplayermodels, sizeof(s_pmi[0]), pmicmpfnc); memset(s_pmnames, 0, sizeof(s_pmnames)); for (i = 0; i < s_numplayermodels; i++) { s_pmnames[i] = s_pmi[i].displayname; if (Q_stricmp(s_pmi[i].directory, currentdirectory) == 0) { int j; currentdirectoryindex = i; for (j = 0; j < s_pmi[i].nskins; j++) { if (Q_stricmp(s_pmi[i].skindisplaynames[j], currentskin) == 0) { currentskinindex = j; break; } } } } s_player_config_menu.x = viddef.width / 2 - 95 * scale; s_player_config_menu.y = viddef.height / (2 * scale) - 97; s_player_config_menu.nitems = 0; s_player_name_field.generic.type = MTYPE_FIELD; s_player_name_field.generic.name = "name"; s_player_name_field.generic.callback = 0; s_player_name_field.generic.x = 0; s_player_name_field.generic.y = 0; s_player_name_field.length = 20; s_player_name_field.visible_length = 20; strcpy(s_player_name_field.buffer, name->string); s_player_name_field.cursor = strlen(name->string); s_player_model_title.generic.type = MTYPE_SEPARATOR; s_player_model_title.generic.name = "model"; s_player_model_title.generic.x = -8 * scale; s_player_model_title.generic.y = 60; s_player_model_box.generic.type = MTYPE_SPINCONTROL; s_player_model_box.generic.x = -56 * scale; s_player_model_box.generic.y = 70; s_player_model_box.generic.callback = ModelCallback; s_player_model_box.generic.cursor_offset = -48; s_player_model_box.curvalue = currentdirectoryindex; s_player_model_box.itemnames = (const char **)s_pmnames; s_player_skin_title.generic.type = MTYPE_SEPARATOR; s_player_skin_title.generic.name = "skin"; s_player_skin_title.generic.x = -16 * scale; s_player_skin_title.generic.y = 84; s_player_skin_box.generic.type = MTYPE_SPINCONTROL; s_player_skin_box.generic.x = -56 * scale; s_player_skin_box.generic.y = 94; s_player_skin_box.generic.name = 0; s_player_skin_box.generic.callback = 0; s_player_skin_box.generic.cursor_offset = -48; s_player_skin_box.curvalue = currentskinindex; s_player_skin_box.itemnames = (const char **)s_pmi[currentdirectoryindex].skindisplaynames; s_player_hand_title.generic.type = MTYPE_SEPARATOR; s_player_hand_title.generic.name = "handedness"; s_player_hand_title.generic.x = 32 * scale; s_player_hand_title.generic.y = 108; s_player_handedness_box.generic.type = MTYPE_SPINCONTROL; s_player_handedness_box.generic.x = -56 * scale; s_player_handedness_box.generic.y = 118; s_player_handedness_box.generic.name = 0; s_player_handedness_box.generic.cursor_offset = -48; s_player_handedness_box.generic.callback = HandednessCallback; s_player_handedness_box.curvalue = ClampCvar(0, 2, hand->value); s_player_handedness_box.itemnames = handedness; for (i = 0; i < sizeof(rate_tbl) / sizeof(*rate_tbl) - 1; i++) { if (Cvar_VariableValue("rate") == rate_tbl[i]) { break; } } s_player_rate_title.generic.type = MTYPE_SEPARATOR; s_player_rate_title.generic.name = "connect speed"; s_player_rate_title.generic.x = 56 * scale; s_player_rate_title.generic.y = 156; s_player_rate_box.generic.type = MTYPE_SPINCONTROL; s_player_rate_box.generic.x = -56 * scale; s_player_rate_box.generic.y = 166; s_player_rate_box.generic.name = 0; s_player_rate_box.generic.cursor_offset = -48; s_player_rate_box.generic.callback = RateCallback; s_player_rate_box.curvalue = i; s_player_rate_box.itemnames = rate_names; s_player_download_action.generic.type = MTYPE_ACTION; s_player_download_action.generic.name = "download options"; s_player_download_action.generic.flags = QMF_LEFT_JUSTIFY; s_player_download_action.generic.x = -24 * scale; s_player_download_action.generic.y = 186; s_player_download_action.generic.statusbar = NULL; s_player_download_action.generic.callback = DownloadOptionsFunc; Menu_AddItem(&s_player_config_menu, &s_player_name_field); Menu_AddItem(&s_player_config_menu, &s_player_model_title); Menu_AddItem(&s_player_config_menu, &s_player_model_box); if (s_player_skin_box.itemnames) { Menu_AddItem(&s_player_config_menu, &s_player_skin_title); Menu_AddItem(&s_player_config_menu, &s_player_skin_box); } Menu_AddItem(&s_player_config_menu, &s_player_hand_title); Menu_AddItem(&s_player_config_menu, &s_player_handedness_box); Menu_AddItem(&s_player_config_menu, &s_player_rate_title); Menu_AddItem(&s_player_config_menu, &s_player_rate_box); Menu_AddItem(&s_player_config_menu, &s_player_download_action); return true; } extern float CalcFov(float fov_x, float w, float h); static void PlayerConfig_MenuDraw(void) { refdef_t refdef; char scratch[MAX_QPATH]; float scale = SCR_GetMenuScale(); memset(&refdef, 0, sizeof(refdef)); refdef.x = viddef.width / 2; refdef.y = viddef.height / 2 - 72 * scale; refdef.width = 144 * scale; refdef.height = 168 * scale; refdef.fov_x = 40; refdef.fov_y = CalcFov(refdef.fov_x, (float)refdef.width, (float)refdef.height); refdef.time = cls.realtime * 0.001f; if (s_pmi[s_player_model_box.curvalue].skindisplaynames) { static int yaw; entity_t entity; memset(&entity, 0, sizeof(entity)); Com_sprintf(scratch, sizeof(scratch), "players/%s/tris.md2", s_pmi[s_player_model_box.curvalue].directory); entity.model = R_RegisterModel(scratch); Com_sprintf(scratch, sizeof(scratch), "players/%s/%s.pcx", s_pmi[s_player_model_box.curvalue].directory, s_pmi[s_player_model_box.curvalue].skindisplaynames[ s_player_skin_box.curvalue]); entity.skin = R_RegisterSkin(scratch); entity.flags = RF_FULLBRIGHT; entity.origin[0] = 80; entity.origin[1] = 0; entity.origin[2] = 0; VectorCopy(entity.origin, entity.oldorigin); entity.frame = 0; entity.oldframe = 0; entity.backlerp = 0.0; entity.angles[1] = (float)yaw; if (++yaw > 360) { yaw -= 360; } refdef.areabits = 0; refdef.num_entities = 1; refdef.entities = &entity; refdef.lightstyles = 0; refdef.rdflags = RDF_NOWORLDMODEL; Menu_Draw(&s_player_config_menu); M_DrawTextBox(((int)(refdef.x) * (320.0F / viddef.width) - 8), (int)((viddef.height / 2) * (240.0F / viddef.height) - 77), refdef.width / (8 * scale), refdef.height / (8 * scale)); refdef.height += 4 * scale; R_RenderFrame(&refdef); Com_sprintf(scratch, sizeof(scratch), "/players/%s/%s_i.pcx", s_pmi[s_player_model_box.curvalue].directory, s_pmi[s_player_model_box.curvalue].skindisplaynames[ s_player_skin_box.curvalue]); Draw_PicScaled(s_player_config_menu.x - 40*scale, refdef.y, scratch, scale); } } static const char * PlayerConfig_MenuKey(int key) { int i; if (key == K_ESCAPE) { char scratch[1024]; Cvar_Set("name", s_player_name_field.buffer); Com_sprintf(scratch, sizeof(scratch), "%s/%s", s_pmi[s_player_model_box.curvalue].directory, s_pmi[s_player_model_box.curvalue].skindisplaynames[ s_player_skin_box.curvalue]); Cvar_Set("skin", scratch); for (i = 0; i < s_numplayermodels; i++) { int j; for (j = 0; j < s_pmi[i].nskins; j++) { if (s_pmi[i].skindisplaynames[j]) { free(s_pmi[i].skindisplaynames[j]); } s_pmi[i].skindisplaynames[j] = 0; } free(s_pmi[i].skindisplaynames); s_pmi[i].skindisplaynames = 0; s_pmi[i].nskins = 0; } } return Default_MenuKey(&s_player_config_menu, key); } static void M_Menu_PlayerConfig_f(void) { if (!PlayerConfig_MenuInit()) { Menu_SetStatusBar(&s_multiplayer_menu, "no valid player models found"); return; } Menu_SetStatusBar(&s_multiplayer_menu, NULL); M_PushMenu(PlayerConfig_MenuDraw, PlayerConfig_MenuKey); } /* * QUIT MENU */ static const char * M_Quit_Key(int key) { switch (key) { case K_ESCAPE: case 'n': case 'N': M_PopMenu(); break; case 'Y': case 'y': cls.key_dest = key_console; CL_Quit_f(); break; default: break; } return NULL; } static void M_Quit_Draw(void) { int w, h; float scale = SCR_GetMenuScale(); Draw_GetPicSize(&w, &h, "quit"); Draw_PicScaled((viddef.width - w * scale) / 2, (viddef.height - h * scale) / 2, "quit", scale); } static void M_Menu_Quit_f(void) { M_PushMenu(M_Quit_Draw, M_Quit_Key); } void M_Init(void) { Cmd_AddCommand("menu_main", M_Menu_Main_f); Cmd_AddCommand("menu_game", M_Menu_Game_f); Cmd_AddCommand("menu_loadgame", M_Menu_LoadGame_f); Cmd_AddCommand("menu_savegame", M_Menu_SaveGame_f); Cmd_AddCommand("menu_joinserver", M_Menu_JoinServer_f); Cmd_AddCommand("menu_addressbook", M_Menu_AddressBook_f); Cmd_AddCommand("menu_startserver", M_Menu_StartServer_f); Cmd_AddCommand("menu_dmoptions", M_Menu_DMOptions_f); Cmd_AddCommand("menu_playerconfig", M_Menu_PlayerConfig_f); Cmd_AddCommand("menu_downloadoptions", M_Menu_DownloadOptions_f); Cmd_AddCommand("menu_credits", M_Menu_Credits_f); Cmd_AddCommand("menu_multiplayer", M_Menu_Multiplayer_f); Cmd_AddCommand("menu_video", M_Menu_Video_f); Cmd_AddCommand("menu_options", M_Menu_Options_f); Cmd_AddCommand("menu_keys", M_Menu_Keys_f); Cmd_AddCommand("menu_quit", M_Menu_Quit_f); } void M_Draw(void) { if (cls.key_dest != key_menu) { return; } /* repaint everything next frame */ SCR_DirtyScreen(); /* dim everything behind it down */ if (cl.cinematictime > 0) { Draw_Fill(0, 0, viddef.width, viddef.height, 0); } else { Draw_FadeScreen(); } m_drawfunc(); /* delay playing the enter sound until after the menu has been drawn, to avoid delay while caching images */ if (m_entersound) { S_StartLocalSound(menu_in_sound); m_entersound = false; } } void M_Keydown(int key) { const char *s; if (m_keyfunc) { if ((s = m_keyfunc(key)) != 0) { S_StartLocalSound((char *)s); } } } yquake2-QUAKE2_5_32/src/client/cl_view.c0000644000175000017500000003100112615162034020453 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements the camera, e.g the player's view * * ======================================================================= */ #include "header/client.h" /* development tools for weapons */ int gun_frame; struct model_s *gun_model; cvar_t *crosshair; cvar_t *crosshair_scale; cvar_t *cl_testparticles; cvar_t *cl_testentities; cvar_t *cl_testlights; cvar_t *cl_testblend; cvar_t *cl_stats; int r_numdlights; dlight_t r_dlights[MAX_DLIGHTS]; int r_numentities; entity_t r_entities[MAX_ENTITIES]; int r_numparticles; particle_t r_particles[MAX_PARTICLES]; lightstyle_t r_lightstyles[MAX_LIGHTSTYLES]; char cl_weaponmodels[MAX_CLIENTWEAPONMODELS][MAX_QPATH]; int num_cl_weaponmodels; /* * Specifies the model that will be used as the world */ void V_ClearScene(void) { r_numdlights = 0; r_numentities = 0; r_numparticles = 0; } void V_AddEntity(entity_t *ent) { if (r_numentities >= MAX_ENTITIES) { return; } r_entities[r_numentities++] = *ent; } void V_AddParticle(vec3_t org, unsigned int color, float alpha) { particle_t *p; if (r_numparticles >= MAX_PARTICLES) { return; } p = &r_particles[r_numparticles++]; VectorCopy(org, p->origin); p->color = color; p->alpha = alpha; } void V_AddLight(vec3_t org, float intensity, float r, float g, float b) { dlight_t *dl; if (r_numdlights >= MAX_DLIGHTS) { return; } dl = &r_dlights[r_numdlights++]; VectorCopy(org, dl->origin); dl->intensity = intensity; dl->color[0] = r; dl->color[1] = g; dl->color[2] = b; } void V_AddLightStyle(int style, float r, float g, float b) { lightstyle_t *ls; if ((style < 0) || (style > MAX_LIGHTSTYLES)) { Com_Error(ERR_DROP, "Bad light style %i", style); } ls = &r_lightstyles[style]; ls->white = r + g + b; ls->rgb[0] = r; ls->rgb[1] = g; ls->rgb[2] = b; } /* *If cl_testparticles is set, create 4096 particles in the view */ void V_TestParticles(void) { particle_t *p; int i, j; float d, r, u; r_numparticles = MAX_PARTICLES; for (i = 0; i < r_numparticles; i++) { d = i * 0.25f; r = 4 * ((i & 7) - 3.5f); u = 4 * (((i >> 3) & 7) - 3.5f); p = &r_particles[i]; for (j = 0; j < 3; j++) { p->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j] * d + cl.v_right[j] * r + cl.v_up[j] * u; } p->color = 8; p->alpha = cl_testparticles->value; } } /* * If cl_testentities is set, create 32 player models */ void V_TestEntities(void) { int i, j; float f, r; entity_t *ent; r_numentities = 32; memset(r_entities, 0, sizeof(r_entities)); for (i = 0; i < r_numentities; i++) { ent = &r_entities[i]; r = 64 * ((i % 4) - 1.5); f = 64 * (i / 4) + 128; for (j = 0; j < 3; j++) { ent->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j] * f + cl.v_right[j] * r; } ent->model = cl.baseclientinfo.model; ent->skin = cl.baseclientinfo.skin; } } /* * If cl_testlights is set, create 32 lights models */ void V_TestLights(void) { int i, j; float f, r; dlight_t *dl; r_numdlights = 32; memset(r_dlights, 0, sizeof(r_dlights)); for (i = 0; i < r_numdlights; i++) { dl = &r_dlights[i]; r = 64 * ((i % 4) - 1.5f); f = 64 * (i / 4.0f) + 128; for (j = 0; j < 3; j++) { dl->origin[j] = cl.refdef.vieworg[j] + cl.v_forward[j] * f + cl.v_right[j] * r; } dl->color[0] = (float)(((i % 6) + 1) & 1); dl->color[1] = (float)((((i % 6) + 1) & 2) >> 1); dl->color[2] = (float)((((i % 6) + 1) & 4) >> 2); dl->intensity = 200; } } /* * Call before entering a new level, or after changing dlls */ void CL_PrepRefresh(void) { char mapname[32]; int i; char name[MAX_QPATH]; float rotate; vec3_t axis; if (!cl.configstrings[CS_MODELS + 1][0]) { return; } SCR_AddDirtyPoint(0, 0); SCR_AddDirtyPoint(viddef.width - 1, viddef.height - 1); /* let the refresher load the map */ strcpy(mapname, cl.configstrings[CS_MODELS + 1] + 5); /* skip "maps/" */ mapname[strlen(mapname) - 4] = 0; /* cut off ".bsp" */ /* register models, pics, and skins */ Com_Printf("Map: %s\r", mapname); SCR_UpdateScreen(); R_BeginRegistration (mapname); Com_Printf(" \r"); /* precache status bar pics */ Com_Printf("pics\r"); SCR_UpdateScreen(); SCR_TouchPics(); Com_Printf(" \r"); CL_RegisterTEntModels(); num_cl_weaponmodels = 1; strcpy(cl_weaponmodels[0], "weapon.md2"); for (i = 1; i < MAX_MODELS && cl.configstrings[CS_MODELS + i][0]; i++) { strcpy(name, cl.configstrings[CS_MODELS + i]); name[37] = 0; /* never go beyond one line */ if (name[0] != '*') { Com_Printf("%s\r", name); } SCR_UpdateScreen(); Sys_SendKeyEvents(); if (name[0] == '#') { /* special player weapon model */ if (num_cl_weaponmodels < MAX_CLIENTWEAPONMODELS) { Q_strlcpy(cl_weaponmodels[num_cl_weaponmodels], cl.configstrings[CS_MODELS + i] + 1, sizeof(cl_weaponmodels[num_cl_weaponmodels])); num_cl_weaponmodels++; } } else { cl.model_draw[i] = R_RegisterModel(cl.configstrings[CS_MODELS + i]); if (name[0] == '*') { cl.model_clip[i] = CM_InlineModel(cl.configstrings[CS_MODELS + i]); } else { cl.model_clip[i] = NULL; } } if (name[0] != '*') { Com_Printf(" \r"); } } Com_Printf("images\r"); SCR_UpdateScreen(); for (i = 1; i < MAX_IMAGES && cl.configstrings[CS_IMAGES + i][0]; i++) { cl.image_precache[i] = Draw_FindPic(cl.configstrings[CS_IMAGES + i]); Sys_SendKeyEvents(); } Com_Printf(" \r"); for (i = 0; i < MAX_CLIENTS; i++) { if (!cl.configstrings[CS_PLAYERSKINS + i][0]) { continue; } Com_Printf("client %i\r", i); SCR_UpdateScreen(); Sys_SendKeyEvents(); CL_ParseClientinfo(i); Com_Printf(" \r"); } CL_LoadClientinfo(&cl.baseclientinfo, "unnamed\\male/grunt"); /* set sky textures and speed */ Com_Printf("sky\r"); SCR_UpdateScreen(); rotate = (float)strtod(cl.configstrings[CS_SKYROTATE], (char **)NULL); sscanf(cl.configstrings[CS_SKYAXIS], "%f %f %f", &axis[0], &axis[1], &axis[2]); R_SetSky(cl.configstrings[CS_SKY], rotate, axis); Com_Printf(" \r"); /* the renderer can now free unneeded stuff */ R_EndRegistration(); /* clear any lines of console text */ Con_ClearNotify(); SCR_UpdateScreen(); cl.refresh_prepped = true; cl.force_refdef = true; /* make sure we have a valid refdef */ #if defined(OGG) || defined(CDA) /* start the cd track */ if (Cvar_VariableValue("cd_shuffle")) { #ifdef CDA CDAudio_RandomPlay(); #endif } else { #ifdef CDA CDAudio_Play((int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10), true); #endif #ifdef OGG /* OGG/Vorbis */ if ((int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10) < 10) { char tmp[3] = "0"; OGG_ParseCmd(strcat(tmp, cl.configstrings[CS_CDTRACK])); } else { OGG_ParseCmd(cl.configstrings[CS_CDTRACK]); } #endif } #endif } float CalcFov(float fov_x, float width, float height) { float a; float x; if ((fov_x < 1) || (fov_x > 179)) { Com_Error(ERR_DROP, "Bad fov: %f", fov_x); } x = width / (float)tan(fov_x / 360 * M_PI); a = (float)atan(height / x); a = a * 360 / M_PI; return a; } /* gun frame debugging functions */ void V_Gun_Next_f(void) { gun_frame++; Com_Printf("frame %i\n", gun_frame); } void V_Gun_Prev_f(void) { gun_frame--; if (gun_frame < 0) { gun_frame = 0; } Com_Printf("frame %i\n", gun_frame); } void V_Gun_Model_f(void) { char name[MAX_QPATH]; if (Cmd_Argc() != 2) { gun_model = NULL; return; } Com_sprintf(name, sizeof(name), "models/%s/tris.md2", Cmd_Argv(1)); gun_model = R_RegisterModel(name); } int entitycmpfnc(const entity_t *a, const entity_t *b) { /* all other models are sorted by model then skin */ if (a->model == b->model) { return (a->skin == b->skin) ? 0 : (a->skin > b->skin) ? 1 : -1; } else { return (a->model == b->model) ? 0 : (a->model > b->model) ? 1 : -1; } } void V_RenderView(float stereo_separation) { if (cls.state != ca_active) { return; } if (!cl.refresh_prepped) { return; } if (cl_timedemo->value) { if (!cl.timedemo_start) { cl.timedemo_start = Sys_Milliseconds(); } cl.timedemo_frames++; } /* an invalid frame will just use the exact previous refdef we can't use the old frame if the video mode has changed, though... */ if (cl.frame.valid && (cl.force_refdef || !cl_paused->value)) { cl.force_refdef = false; V_ClearScene(); /* build a refresh entity list and calc cl.sim* this also calls CL_CalcViewValues which loads v_forward, etc. */ CL_AddEntities(); if (cl_testparticles->value) { V_TestParticles(); } if (cl_testentities->value) { V_TestEntities(); } if (cl_testlights->value) { V_TestLights(); } if (cl_testblend->value) { cl.refdef.blend[0] = 1; cl.refdef.blend[1] = 0.5; cl.refdef.blend[2] = 0.25; cl.refdef.blend[3] = 0.5; } /* offset vieworg appropriately if we're doing stereo separation */ if (stereo_separation != 0) { vec3_t tmp; VectorScale(cl.v_right, stereo_separation, tmp); VectorAdd(cl.refdef.vieworg, tmp, cl.refdef.vieworg); } /* 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/8 pixel, so add 1/16 in each axis */ cl.refdef.vieworg[0] += 1.0 / 16; cl.refdef.vieworg[1] += 1.0 / 16; cl.refdef.vieworg[2] += 1.0 / 16; cl.refdef.time = cl.time * 0.001f; cl.refdef.areabits = cl.frame.areabits; if (!cl_add_entities->value) { r_numentities = 0; } if (!cl_add_particles->value) { r_numparticles = 0; } if (!cl_add_lights->value) { r_numdlights = 0; } if (!cl_add_blend->value) { VectorClear(cl.refdef.blend); } cl.refdef.num_entities = r_numentities; cl.refdef.entities = r_entities; cl.refdef.num_particles = r_numparticles; cl.refdef.particles = r_particles; cl.refdef.num_dlights = r_numdlights; cl.refdef.dlights = r_dlights; cl.refdef.lightstyles = r_lightstyles; cl.refdef.rdflags = cl.frame.playerstate.rdflags; /* sort entities for better cache locality */ qsort(cl.refdef.entities, cl.refdef.num_entities, sizeof(cl.refdef.entities[0]), (int (*)(const void *, const void *)) entitycmpfnc); } cl.refdef.x = scr_vrect.x; cl.refdef.y = scr_vrect.y; cl.refdef.width = scr_vrect.width; cl.refdef.height = scr_vrect.height; cl.refdef.fov_y = CalcFov(cl.refdef.fov_x, (float)cl.refdef.width, (float)cl.refdef.height); R_RenderFrame(&cl.refdef); if (cl_stats->value) { Com_Printf("ent:%i lt:%i part:%i\n", r_numentities, r_numdlights, r_numparticles); } if (log_stats->value && (log_stats_file != 0)) { fprintf(log_stats_file, "%i,%i,%i,", r_numentities, r_numdlights, r_numparticles); } SCR_AddDirtyPoint(scr_vrect.x, scr_vrect.y); SCR_AddDirtyPoint(scr_vrect.x + scr_vrect.width - 1, scr_vrect.y + scr_vrect.height - 1); SCR_DrawCrosshair(); } void V_Viewpos_f(void) { Com_Printf("(%i %i %i) : %i\n", (int)cl.refdef.vieworg[0], (int)cl.refdef.vieworg[1], (int)cl.refdef.vieworg[2], (int)cl.refdef.viewangles[YAW]); } void V_Init(void) { Cmd_AddCommand("gun_next", V_Gun_Next_f); Cmd_AddCommand("gun_prev", V_Gun_Prev_f); Cmd_AddCommand("gun_model", V_Gun_Model_f); Cmd_AddCommand("viewpos", V_Viewpos_f); crosshair = Cvar_Get("crosshair", "0", CVAR_ARCHIVE); crosshair_scale = Cvar_Get("crosshair_scale", "1", CVAR_ARCHIVE); cl_testblend = Cvar_Get("cl_testblend", "0", 0); cl_testparticles = Cvar_Get("cl_testparticles", "0", 0); cl_testentities = Cvar_Get("cl_testentities", "0", 0); cl_testlights = Cvar_Get("cl_testlights", "0", 0); cl_stats = Cvar_Get("cl_stats", "0", 0); } yquake2-QUAKE2_5_32/src/client/cl_console.c0000644000175000017500000003124612615162034021156 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements the console * * ======================================================================= */ #include "header/client.h" #include console_t con; cvar_t *con_notifytime; extern char key_lines[NUM_KEY_LINES][MAXCMDLINE]; extern int edit_line; extern int key_linepos; void DrawString(int x, int y, char *s) { DrawStringScaled(x, y, s, 1.0f); } void DrawStringScaled(int x, int y, char *s, float factor) { while (*s) { Draw_CharScaled(x, y, *s, factor); x += 8*factor; s++; } } void DrawAltString(int x, int y, char *s) { DrawAltStringScaled(x, y, s, 1.0f); } void DrawAltStringScaled(int x, int y, char *s, float factor) { while (*s) { Draw_CharScaled(x, y, *s ^ 0x80, factor); x += 8*factor; s++; } } void Key_ClearTyping(void) { key_lines[edit_line][1] = 0; /* clear any typing */ key_linepos = 1; } void Con_ToggleConsole_f(void) { SCR_EndLoadingPlaque(); /* get rid of loading plaque */ if (cl.attractloop) { Cbuf_AddText("killserver\n"); return; } if (cls.state == ca_disconnected) { /* start the demo loop again */ Cbuf_AddText("d1\n"); return; } Key_ClearTyping(); Con_ClearNotify(); if (cls.key_dest == key_console) { M_ForceMenuOff(); Cvar_Set("paused", "0"); } else { M_ForceMenuOff(); cls.key_dest = key_console; if ((Cvar_VariableValue("maxclients") == 1) && Com_ServerState()) { Cvar_Set("paused", "1"); } } } void Con_ToggleChat_f(void) { Key_ClearTyping(); if (cls.key_dest == key_console) { if (cls.state == ca_active) { M_ForceMenuOff(); cls.key_dest = key_game; } } else { cls.key_dest = key_console; } Con_ClearNotify(); } void Con_Clear_f(void) { memset(con.text, ' ', CON_TEXTSIZE); } /* * Save the console contents out to a file */ void Con_Dump_f(void) { int l, x; char *line; FILE *f; char buffer[1024]; char name[MAX_OSPATH]; if (Cmd_Argc() != 2) { Com_Printf("usage: condump \n"); return; } if (con.linewidth >= 1024) { Com_Printf("con.linewidth too large!\n"); return; } Com_sprintf(name, sizeof(name), "%s/%s.txt", FS_Gamedir(), Cmd_Argv(1)); Com_Printf("Dumped console text to %s.\n", name); FS_CreatePath(name); f = fopen(name, "w"); if (!f) { Com_Printf("ERROR: couldn't open.\n"); return; } /* skip empty lines */ for (l = con.current - con.totallines + 1; l <= con.current; l++) { line = con.text + (l % con.totallines) * con.linewidth; for (x = 0; x < con.linewidth; x++) { if (line[x] != ' ') { break; } } if (x != con.linewidth) { break; } } /* write the remaining lines */ buffer[con.linewidth] = 0; for ( ; l <= con.current; l++) { line = con.text + (l % con.totallines) * con.linewidth; memcpy(buffer, line, con.linewidth); for (x = con.linewidth - 1; x >= 0; x--) { if (buffer[x] == ' ') { buffer[x] = 0; } else { break; } } for (x = 0; buffer[x]; x++) { buffer[x] &= 0x7f; } fprintf(f, "%s\n", buffer); } fclose(f); } void Con_ClearNotify(void) { int i; for (i = 0; i < NUM_CON_TIMES; i++) { con.times[i] = 0; } } void Con_MessageMode_f(void) { chat_team = false; cls.key_dest = key_message; } void Con_MessageMode2_f(void) { chat_team = true; cls.key_dest = key_message; } /* * If the line width has changed, reformat the buffer. */ void Con_CheckResize(void) { int i, j, width, oldwidth, oldtotallines, numlines, numchars; char tbuf[CON_TEXTSIZE]; width = (viddef.width >> 3) - 2; if (width == con.linewidth) { return; } /* video hasn't been initialized yet */ if (width < 1) { width = 38; con.linewidth = width; con.totallines = CON_TEXTSIZE / con.linewidth; memset(con.text, ' ', CON_TEXTSIZE); } else { oldwidth = con.linewidth; con.linewidth = width; oldtotallines = con.totallines; con.totallines = CON_TEXTSIZE / con.linewidth; numlines = oldtotallines; if (con.totallines < numlines) { numlines = con.totallines; } numchars = oldwidth; if (con.linewidth < numchars) { numchars = con.linewidth; } memcpy(tbuf, con.text, CON_TEXTSIZE); memset(con.text, ' ', CON_TEXTSIZE); for (i = 0; i < numlines; i++) { for (j = 0; j < numchars; j++) { con.text[(con.totallines - 1 - i) * con.linewidth + j] = tbuf[((con.current - i + oldtotallines) % oldtotallines) * oldwidth + j]; } } Con_ClearNotify(); } con.current = con.totallines - 1; con.display = con.current; } void Con_Init(void) { con.linewidth = -1; Con_CheckResize(); Com_Printf("Console initialized.\n"); /* register our commands */ con_notifytime = Cvar_Get("con_notifytime", "3", 0); Cmd_AddCommand("toggleconsole", Con_ToggleConsole_f); Cmd_AddCommand("togglechat", Con_ToggleChat_f); Cmd_AddCommand("messagemode", Con_MessageMode_f); Cmd_AddCommand("messagemode2", Con_MessageMode2_f); Cmd_AddCommand("clear", Con_Clear_f); Cmd_AddCommand("condump", Con_Dump_f); con.initialized = true; } void Con_Linefeed(void) { con.x = 0; if (con.display == con.current) { con.display++; } con.current++; memset(&con.text[(con.current % con.totallines) * con.linewidth], ' ', con.linewidth); } /* * Handles cursor positioning, line wrapping, etc All console printing * must go through this in order to be logged to disk If no console is * visible, the text will appear at the top of the game window */ void Con_Print(char *txt) { int y; int c, l; static int cr; int mask; if (!con.initialized) { return; } if ((txt[0] == 1) || (txt[0] == 2)) { mask = 128; /* go to colored text */ txt++; } else { mask = 0; } while ((c = *txt)) { /* count word length */ for (l = 0; l < con.linewidth; l++) { if (txt[l] <= ' ') { break; } } /* word wrap */ if ((l != con.linewidth) && (con.x + l > 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] = cls.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.ormask; con.x++; if (con.x >= con.linewidth) { con.x = 0; } break; } } } void Con_CenteredPrint(char *text) { int l; char buffer[1024]; l = strlen(text); l = (con.linewidth - l) / 2; if (l <= 0) { l = 0; } else { memset(buffer, ' ', l); } strcpy(buffer + l, text); strcat(buffer, "\n"); Con_Print(buffer); } /* * The input line scrolls horizontally if * typing goes beyond the right edge */ void Con_DrawInput(void) { int i; float scale; char *text; if (cls.key_dest == key_menu) { return; } /* don't draw anything (always draw if not active) */ if ((cls.key_dest != key_console) && (cls.state == ca_active)) { return; } scale = SCR_GetConsoleScale(); text = key_lines[edit_line]; /* add the cursor frame */ text[key_linepos] = 10 + ((int)(cls.realtime >> 8) & 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; } for (i = 0; i < con.linewidth; i++) { Draw_CharScaled(((i + 1) << 3) * scale, con.vislines - 22 * scale, text[i], scale); } /* remove cursor */ key_lines[edit_line][key_linepos] = 0; } /* * Draws the last few lines of output transparently over the game top */ void Con_DrawNotify(void) { int x, v; char *text; int i; int time; char *s; int skip; float scale; v = 0; scale = SCR_GetConsoleScale(); for (i = con.current - NUM_CON_TIMES + 1; i <= con.current; i++) { if (i < 0) { continue; } time = con.times[i % NUM_CON_TIMES]; if (time == 0) { continue; } time = cls.realtime - time; if (time > con_notifytime->value * 1000) { continue; } text = con.text + (i % con.totallines) * con.linewidth; for (x = 0; x < con.linewidth; x++) { Draw_CharScaled(((x + 1) << 3) * scale, v * scale, text[x], scale); } v += 8; } if (cls.key_dest == key_message) { if (chat_team) { DrawStringScaled(8 * scale, v * scale, "say_team:", scale); skip = 11; } else { DrawStringScaled(8 * scale, v * scale, "say:", scale); skip = 5; } s = chat_buffer; if (chat_bufferlen > (viddef.width >> 3) - (skip + 1)) { s += chat_bufferlen - ((viddef.width >> 3) - (skip + 1)); } x = 0; while (s[x]) { Draw_CharScaled(((x + skip) << 3) * scale, v * scale, s[x], scale); x++; } Draw_CharScaled(((x + skip) << 3) * scale, v + scale, 10 + ((cls.realtime >> 8) & 1), scale); v += 8; } if (v) { SCR_AddDirtyPoint(0, 0); SCR_AddDirtyPoint(viddef.width - 1, v); } } /* * Draws the console with the solid background */ void Con_DrawConsole(float frac) { int i, j, x, y, n; int rows; char *text; int row; int lines; float scale; char version[48]; char dlbar[1024]; char timebuf[48]; char tmpbuf[48]; time_t t; struct tm *today; scale = SCR_GetConsoleScale(); lines = viddef.height * frac; if (lines <= 0) { return; } if (lines > viddef.height) { lines = viddef.height; } /* draw the background */ Draw_StretchPic(0, -viddef.height + lines, viddef.width, viddef.height, "conback"); SCR_AddDirtyPoint(0, 0); SCR_AddDirtyPoint(viddef.width - 1, lines - 1); Com_sprintf(version, sizeof(version), "Yamagi Quake II v%s", YQ2VERSION); for (x = 0; x < 21; x++) { Draw_CharScaled(viddef.width - (173 * scale) + x * 8 * scale, lines - 35 * scale, 128 + version[x], scale); } t = time(NULL); today = localtime(&t); strftime(timebuf, sizeof(timebuf), "%H:%M:%S - %m/%d/%Y", today); Com_sprintf(tmpbuf, sizeof(tmpbuf), "%s", timebuf); for (x = 0; x < 21; x++) { Draw_CharScaled(viddef.width - (173 * scale) + x * 8 * scale, lines - 25 * scale, 128 + tmpbuf[x], scale); } /* draw the text */ con.vislines = lines; rows = (lines - 22) >> 3; /* rows of text to draw */ y = (lines - 30 * scale) / scale; /* draw from the bottom up */ if (con.display != con.current) { /* draw arrows to show the buffer is backscrolled */ for (x = 0; x < con.linewidth; x += 4) { Draw_CharScaled(((x + 1) << 3) * scale, y * scale, '^', scale); } y -= 8; rows--; } row = con.display; for (i = 0; i < rows; i++, y -= 8, row--) { if (row < 0) { break; } if (con.current - row >= con.totallines) { break; /* past scrollback wrap point */ } text = con.text + (row % con.totallines) * con.linewidth; for (x = 0; x < con.linewidth; x++) { Draw_CharScaled(((x + 1) << 3) * scale, y * scale, text[x], scale); } } /* draw the download bar, figure out width */ if (cls.download) { if ((text = strrchr(cls.downloadname, '/')) != NULL) { text++; } else { text = cls.downloadname; } x = con.linewidth - ((con.linewidth * 7) / 40); y = x - strlen(text) - 8; i = con.linewidth / 3; if (strlen(text) > i) { y = x - i - 11; memcpy(dlbar, text, i); dlbar[i] = 0; strcat(dlbar, "..."); } else { strcpy(dlbar, text); } strcat(dlbar, ": "); i = strlen(dlbar); dlbar[i++] = '\x80'; /* where's the dot gone? */ if (cls.downloadpercent == 0) { n = 0; } else { n = y * cls.downloadpercent / 100; } for (j = 0; j < y; j++) { if (j == n) { dlbar[i++] = '\x83'; } else { dlbar[i++] = '\x81'; } } dlbar[i++] = '\x82'; dlbar[i] = 0; sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent); /* draw it */ y = con.vislines - 12; for (i = 0; i < strlen(dlbar); i++) { Draw_CharScaled(((i + 1) << 3) * scale, y * scale, dlbar[i], scale); } } /* draw the input prompt, user text, and cursor if desired */ Con_DrawInput(); } yquake2-QUAKE2_5_32/src/client/cl_particles.c0000644000175000017500000001216512615162034021501 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements all generic particle stuff * * ======================================================================= */ #include "header/client.h" cparticle_t *active_particles, *free_particles; cparticle_t particles[MAX_PARTICLES]; int cl_numparticles = MAX_PARTICLES; void CL_ClearParticles(void) { int i; free_particles = &particles[0]; active_particles = NULL; for (i = 0; i < cl_numparticles; i++) { particles[i].next = &particles[i + 1]; } particles[cl_numparticles - 1].next = NULL; } void CL_ParticleEffect(vec3_t org, vec3_t dir, int color, int count) { int i, j; cparticle_t *p; float d; for (i = 0; i < count; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cl.time; p->color = color + (randk() & 7); d = randk() & 31; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((randk() & 7) - 4) + d * dir[j]; p->vel[j] = crandk() * 20; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY + 0.2f; p->alpha = 1.0; p->alphavel = -1.0 / (0.5 + frandk() * 0.3); } } void CL_ParticleEffect2(vec3_t org, vec3_t dir, int color, int count) { int i, j; cparticle_t *p; float d; float time; time = (float)cl.time; for (i = 0; i < count; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = color + (randk() & 7); d = randk() & 7; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((randk() & 7) - 4) + d * dir[j]; p->vel[j] = crandk() * 20; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alpha = 1.0; p->alphavel = -1.0f / (0.5f + frandk() * 0.3f); } } void CL_ParticleEffect3(vec3_t org, vec3_t dir, int color, int count) { int i, j; cparticle_t *p; float d; float time; time = (float)cl.time; for (i = 0; i < count; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; p->color = color; d = randk() & 7; for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((randk() & 7) - 4) + d * dir[j]; p->vel[j] = crandk() * 20; } p->accel[0] = p->accel[1] = 0; p->accel[2] = PARTICLE_GRAVITY; p->alpha = 1.0; p->alphavel = -1.0f / (0.5f + frandk() * 0.3f); } } void CL_AddParticles(void) { cparticle_t *p, *next; float alpha; float time, time2; vec3_t org; int color; cparticle_t *active, *tail; active = NULL; tail = NULL; for (p = active_particles; p; p = next) { next = p->next; if (p->alphavel != INSTANT_PARTICLE) { time = (cl.time - p->time) * 0.001; alpha = p->alpha + time * p->alphavel; if (alpha <= 0) { /* faded out */ p->next = free_particles; free_particles = p; continue; } } else { time = 0.0f; alpha = p->alpha; } p->next = NULL; if (!tail) { active = tail = p; } else { tail->next = p; tail = p; } if (alpha > 1.0f) { alpha = 1; } color = p->color; time2 = time * time; org[0] = p->org[0] + p->vel[0] * time + p->accel[0] * time2; org[1] = p->org[1] + p->vel[1] * time + p->accel[1] * time2; org[2] = p->org[2] + p->vel[2] * time + p->accel[2] * time2; V_AddParticle(org, color, alpha); if (p->alphavel == INSTANT_PARTICLE) { p->alphavel = 0.0; p->alpha = 0.0; } } active_particles = active; } void CL_GenericParticleEffect(vec3_t org, vec3_t dir, int color, int count, int numcolors, int dirspread, float alphavel) { int i, j; cparticle_t *p; float d; float time; time = (float)cl.time; for (i = 0; i < count; i++) { if (!free_particles) { return; } p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = time; if (numcolors > 1) { p->color = color + (randk() & numcolors); } else { p->color = color; } d = (float)(randk() & dirspread); for (j = 0; j < 3; j++) { p->org[j] = org[j] + ((randk() & 7) - 4) + d * dir[j]; p->vel[j] = crandk() * 20; } p->accel[0] = p->accel[1] = 0; p->accel[2] = -PARTICLE_GRAVITY; p->alpha = 1.0; p->alphavel = -1.0f / (0.5f + frandk() * alphavel); } } yquake2-QUAKE2_5_32/src/client/cl_entities.c0000644000175000017500000004556612615162034021352 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements all static entities at client site. * * ======================================================================= */ #include #include "header/client.h" extern struct model_s *cl_mod_powerscreen; struct model_s * S_RegisterSexedModel(entity_state_t *ent, char *base) { int n; char *p; struct model_s *md2; char model[MAX_QPATH]; char buffer[MAX_QPATH]; /* determine what model the client is using */ model[0] = 0; n = CS_PLAYERSKINS + ent->number - 1; if (cl.configstrings[n][0]) { p = strchr(cl.configstrings[n], '\\'); if (p) { p += 1; strcpy(model, p); p = strchr(model, '/'); if (p) { *p = 0; } } } /* if we can't figure it out, they're male */ if (!model[0]) { strcpy(model, "male"); } Com_sprintf(buffer, sizeof(buffer), "players/%s/%s", model, base + 1); md2 = R_RegisterModel(buffer); if (!md2) { /* not found, try default weapon model */ Com_sprintf(buffer, sizeof(buffer), "players/%s/weapon.md2", model); md2 = R_RegisterModel(buffer); if (!md2) { /* no, revert to the male model */ Com_sprintf(buffer, sizeof(buffer), "players/%s/%s", "male", base + 1); md2 = R_RegisterModel(buffer); if (!md2) { /* last try, default male weapon.md2 */ Com_sprintf(buffer, sizeof(buffer), "players/male/weapon.md2"); md2 = R_RegisterModel(buffer); } } } return md2; } extern int Developer_searchpath(int who); void CL_AddPacketEntities(frame_t *frame) { entity_t ent = {0}; entity_state_t *s1; float autorotate; int i; int pnum; centity_t *cent; int autoanim; clientinfo_t *ci; unsigned int effects, renderfx; /* bonus items rotate at a fixed rate */ autorotate = anglemod(cl.time * 0.1f); /* brush models can auto animate their frames */ autoanim = 2 * cl.time / 1000; for (pnum = 0; pnum < frame->num_entities; pnum++) { s1 = &cl_parse_entities[(frame->parse_entities + pnum) & (MAX_PARSE_ENTITIES - 1)]; cent = &cl_entities[s1->number]; effects = s1->effects; renderfx = s1->renderfx; /* set frame */ if (effects & EF_ANIM01) { ent.frame = autoanim & 1; } else if (effects & EF_ANIM23) { ent.frame = 2 + (autoanim & 1); } else if (effects & EF_ANIM_ALL) { ent.frame = autoanim; } else if (effects & EF_ANIM_ALLFAST) { ent.frame = cl.time / 100; } else { ent.frame = s1->frame; } /* quad and pent can do different things on client */ if (effects & EF_PENT) { effects &= ~EF_PENT; effects |= EF_COLOR_SHELL; renderfx |= RF_SHELL_RED; } if (effects & EF_QUAD) { effects &= ~EF_QUAD; effects |= EF_COLOR_SHELL; renderfx |= RF_SHELL_BLUE; } if (effects & EF_DOUBLE) { effects &= ~EF_DOUBLE; effects |= EF_COLOR_SHELL; renderfx |= RF_SHELL_DOUBLE; } if (effects & EF_HALF_DAMAGE) { effects &= ~EF_HALF_DAMAGE; effects |= EF_COLOR_SHELL; renderfx |= RF_SHELL_HALF_DAM; } ent.oldframe = cent->prev.frame; ent.backlerp = 1.0f - cl.lerpfrac; if (renderfx & (RF_FRAMELERP | RF_BEAM)) { /* step origin discretely, because the frames do the animation properly */ VectorCopy(cent->current.origin, ent.origin); VectorCopy(cent->current.old_origin, ent.oldorigin); } else { /* interpolate origin */ for (i = 0; i < 3; i++) { ent.origin[i] = ent.oldorigin[i] = cent->prev.origin[i] + cl.lerpfrac * (cent->current.origin[i] - cent->prev.origin[i]); } } /* tweak the color of beams */ if (renderfx & RF_BEAM) { /* the four beam colors are encoded in 32 bits of skinnum (hack) */ ent.alpha = 0.30f; ent.skinnum = (s1->skinnum >> ((randk() % 4) * 8)) & 0xff; ent.model = NULL; } else { /* set skin */ if (s1->modelindex == 255) { /* use custom player skin */ ent.skinnum = 0; ci = &cl.clientinfo[s1->skinnum & 0xff]; ent.skin = ci->skin; ent.model = ci->model; if (!ent.skin || !ent.model) { ent.skin = cl.baseclientinfo.skin; ent.model = cl.baseclientinfo.model; } if (renderfx & RF_USE_DISGUISE) { if (ent.skin != NULL) { if (!strncmp((char *)ent.skin, "players/male", 12)) { ent.skin = R_RegisterSkin("players/male/disguise.pcx"); ent.model = R_RegisterModel("players/male/tris.md2"); } else if (!strncmp((char *)ent.skin, "players/female", 14)) { ent.skin = R_RegisterSkin("players/female/disguise.pcx"); ent.model = R_RegisterModel("players/female/tris.md2"); } else if (!strncmp((char *)ent.skin, "players/cyborg", 14)) { ent.skin = R_RegisterSkin("players/cyborg/disguise.pcx"); ent.model = R_RegisterModel("players/cyborg/tris.md2"); } } } } else { ent.skinnum = s1->skinnum; ent.skin = NULL; ent.model = cl.model_draw[s1->modelindex]; } } /* only used for black hole model right now */ if (renderfx & RF_TRANSLUCENT && !(renderfx & RF_BEAM)) { ent.alpha = 0.70f; } /* render effects (fullbright, translucent, etc) */ if ((effects & EF_COLOR_SHELL)) { ent.flags = 0; /* renderfx go on color shell entity */ } else { ent.flags = renderfx; } /* calculate angles */ if (effects & EF_ROTATE) { /* some bonus items auto-rotate */ ent.angles[0] = 0; ent.angles[1] = autorotate; ent.angles[2] = 0; } else if (effects & EF_SPINNINGLIGHTS) { ent.angles[0] = 0; ent.angles[1] = anglemod(cl.time / 2) + s1->angles[1]; ent.angles[2] = 180; { vec3_t forward; vec3_t start; AngleVectors(ent.angles, forward, NULL, NULL); VectorMA(ent.origin, 64, forward, start); V_AddLight(start, 100, 1, 0, 0); } } else { /* interpolate angles */ float a1, a2; for (i = 0; i < 3; i++) { a1 = cent->current.angles[i]; a2 = cent->prev.angles[i]; ent.angles[i] = LerpAngle(a2, a1, cl.lerpfrac); } } if (s1->number == cl.playernum + 1) { ent.flags |= RF_VIEWERMODEL; if (effects & EF_FLAG1) { V_AddLight(ent.origin, 225, 1.0f, 0.1f, 0.1f); } else if (effects & EF_FLAG2) { V_AddLight(ent.origin, 225, 0.1f, 0.1f, 1.0f); } else if (effects & EF_TAGTRAIL) { V_AddLight(ent.origin, 225, 1.0f, 1.0f, 0.0f); } else if (effects & EF_TRACKERTRAIL) { V_AddLight(ent.origin, 225, -1.0f, -1.0f, -1.0f); } continue; } /* if set to invisible, skip */ if (!s1->modelindex) { continue; } if (effects & EF_BFG) { ent.flags |= RF_TRANSLUCENT; ent.alpha = 0.30f; } if (effects & EF_PLASMA) { ent.flags |= RF_TRANSLUCENT; ent.alpha = 0.6f; } if (effects & EF_SPHERETRANS) { ent.flags |= RF_TRANSLUCENT; if (effects & EF_TRACKERTRAIL) { ent.alpha = 0.6f; } else { ent.alpha = 0.3f; } } /* add to refresh list */ V_AddEntity(&ent); /* color shells generate a seperate entity for the main model */ if (effects & EF_COLOR_SHELL) { /* all of the solo colors are fine. we need to catch any of the combinations that look bad (double & half) and turn them into the appropriate color, and make double/quad something special */ if (renderfx & RF_SHELL_HALF_DAM) { if (Developer_searchpath(2) == 2) { /* ditch the half damage shell if any of red, blue, or double are on */ if (renderfx & (RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_DOUBLE)) { renderfx &= ~RF_SHELL_HALF_DAM; } } } if (renderfx & RF_SHELL_DOUBLE) { if (Developer_searchpath(2) == 2) { /* lose the yellow shell if we have a red, blue, or green shell */ if (renderfx & (RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_GREEN)) { renderfx &= ~RF_SHELL_DOUBLE; } /* if we have a red shell, turn it to purple by adding blue */ if (renderfx & RF_SHELL_RED) { renderfx |= RF_SHELL_BLUE; } /* if we have a blue shell (and not a red shell), turn it to cyan by adding green */ else if (renderfx & RF_SHELL_BLUE) { /* go to green if it's on already, otherwise do cyan (flash green) */ if (renderfx & RF_SHELL_GREEN) { renderfx &= ~RF_SHELL_BLUE; } else { renderfx |= RF_SHELL_GREEN; } } } } ent.flags = renderfx | RF_TRANSLUCENT; ent.alpha = 0.30f; V_AddEntity(&ent); } ent.skin = NULL; /* never use a custom skin on others */ ent.skinnum = 0; ent.flags = 0; ent.alpha = 0; /* duplicate for linked models */ if (s1->modelindex2) { if (s1->modelindex2 == 255) { /* custom weapon */ ci = &cl.clientinfo[s1->skinnum & 0xff]; i = (s1->skinnum >> 8); /* 0 is default weapon model */ if (!cl_vwep->value || (i > MAX_CLIENTWEAPONMODELS - 1)) { i = 0; } ent.model = ci->weaponmodel[i]; if (!ent.model) { if (i != 0) { ent.model = ci->weaponmodel[0]; } if (!ent.model) { ent.model = cl.baseclientinfo.weaponmodel[0]; } } } else { ent.model = cl.model_draw[s1->modelindex2]; } /* check for the defender sphere shell and make it translucent */ if (!Q_strcasecmp(cl.configstrings[CS_MODELS + (s1->modelindex2)], "models/items/shell/tris.md2")) { ent.alpha = 0.32f; ent.flags = RF_TRANSLUCENT; } V_AddEntity(&ent); ent.flags = 0; ent.alpha = 0; } if (s1->modelindex3) { ent.model = cl.model_draw[s1->modelindex3]; V_AddEntity(&ent); } if (s1->modelindex4) { ent.model = cl.model_draw[s1->modelindex4]; V_AddEntity(&ent); } if (effects & EF_POWERSCREEN) { ent.model = cl_mod_powerscreen; ent.oldframe = 0; ent.frame = 0; ent.flags |= (RF_TRANSLUCENT | RF_SHELL_GREEN); ent.alpha = 0.30f; V_AddEntity(&ent); } /* add automatic particle trails */ if ((effects & ~EF_ROTATE)) { if (effects & EF_ROCKET) { CL_RocketTrail(cent->lerp_origin, ent.origin, cent); V_AddLight(ent.origin, 200, 1, 0.25f, 0); } /* Do not reorder EF_BLASTER and EF_HYPERBLASTER. EF_BLASTER | EF_TRACKER is a special case for EF_BLASTER2 */ else if (effects & EF_BLASTER) { if (effects & EF_TRACKER) { CL_BlasterTrail2(cent->lerp_origin, ent.origin); V_AddLight(ent.origin, 200, 0, 1, 0); } else { CL_BlasterTrail(cent->lerp_origin, ent.origin); V_AddLight(ent.origin, 200, 1, 1, 0); } } else if (effects & EF_HYPERBLASTER) { if (effects & EF_TRACKER) { V_AddLight(ent.origin, 200, 0, 1, 0); } else { V_AddLight(ent.origin, 200, 1, 1, 0); } } else if (effects & EF_GIB) { CL_DiminishingTrail(cent->lerp_origin, ent.origin, cent, effects); } else if (effects & EF_GRENADE) { CL_DiminishingTrail(cent->lerp_origin, ent.origin, cent, effects); } else if (effects & EF_FLIES) { CL_FlyEffect(cent, ent.origin); } else if (effects & EF_BFG) { static int bfg_lightramp[6] = {300, 400, 600, 300, 150, 75}; if (effects & EF_ANIM_ALLFAST) { CL_BfgParticles(&ent); i = 200; } else { i = bfg_lightramp[s1->frame]; } V_AddLight(ent.origin, i, 0, 1, 0); } else if (effects & EF_TRAP) { ent.origin[2] += 32; CL_TrapParticles(&ent); i = (randk() % 100) + 100; V_AddLight(ent.origin, i, 1, 0.8f, 0.1f); } else if (effects & EF_FLAG1) { CL_FlagTrail(cent->lerp_origin, ent.origin, 242); V_AddLight(ent.origin, 225, 1, 0.1f, 0.1f); } else if (effects & EF_FLAG2) { CL_FlagTrail(cent->lerp_origin, ent.origin, 115); V_AddLight(ent.origin, 225, 0.1f, 0.1f, 1); } else if (effects & EF_TAGTRAIL) { CL_TagTrail(cent->lerp_origin, ent.origin, 220); V_AddLight(ent.origin, 225, 1.0, 1.0, 0.0); } else if (effects & EF_TRACKERTRAIL) { if (effects & EF_TRACKER) { float intensity; intensity = 50 + (500 * ((float)sin(cl.time / 500.0f) + 1.0f)); V_AddLight(ent.origin, intensity, -1.0, -1.0, -1.0); } else { CL_Tracker_Shell(cent->lerp_origin); V_AddLight(ent.origin, 155, -1.0, -1.0, -1.0); } } else if (effects & EF_TRACKER) { CL_TrackerTrail(cent->lerp_origin, ent.origin, 0); V_AddLight(ent.origin, 200, -1, -1, -1); } else if (effects & EF_IONRIPPER) { CL_IonripperTrail(cent->lerp_origin, ent.origin); V_AddLight(ent.origin, 100, 1, 0.5, 0.5); } else if (effects & EF_BLUEHYPERBLASTER) { V_AddLight(ent.origin, 200, 0, 0, 1); } else if (effects & EF_PLASMA) { if (effects & EF_ANIM_ALLFAST) { CL_BlasterTrail(cent->lerp_origin, ent.origin); } V_AddLight(ent.origin, 130, 1, 0.5, 0.5); } } VectorCopy(ent.origin, cent->lerp_origin); } } void CL_AddViewWeapon(player_state_t *ps, player_state_t *ops) { entity_t gun = {0}; /* view model */ int i; /* allow the gun to be completely removed */ if (!cl_gun->value) { return; } /* don't draw gun if in wide angle view and drawing not forced */ if (ps->fov > 90) { if (cl_gun->value < 2) { return; } } if (gun_model) { gun.model = gun_model; } else { gun.model = cl.model_draw[ps->gunindex]; } if (!gun.model) { return; } /* set up gun position */ for (i = 0; i < 3; i++) { gun.origin[i] = cl.refdef.vieworg[i] + ops->gunoffset[i] + cl.lerpfrac * (ps->gunoffset[i] - ops->gunoffset[i]); gun.angles[i] = cl.refdef.viewangles[i] + LerpAngle(ops->gunangles[i], ps->gunangles[i], cl.lerpfrac); } if (gun_frame) { gun.frame = gun_frame; gun.oldframe = gun_frame; } else { gun.frame = ps->gunframe; if (gun.frame == 0) { gun.oldframe = 0; /* just changed weapons, don't lerp from old */ } else { gun.oldframe = ops->gunframe; } } gun.flags = RF_MINLIGHT | RF_DEPTHHACK | RF_WEAPONMODEL; gun.backlerp = 1.0f - cl.lerpfrac; VectorCopy(gun.origin, gun.oldorigin); /* don't lerp at all */ V_AddEntity(&gun); } /* * Adapts a 4:3 aspect FOV to the current aspect (Hor+) */ static inline float AdaptFov(float fov, float w, float h) { static const float pi = M_PI; /* float instead of double */ if (w <= 0 || h <= 0) return fov; /* * Formula: * * fov = 2.0 * atan(width / height * 3.0 / 4.0 * tan(fov43 / 2.0)) * * The code below is equivalent but precalculates a few values and * converts between degrees and radians when needed. */ return (atanf(tanf(fov / 360.0f * pi) * (w / h * 0.75f)) / pi * 360.0f); } /* * Sets cl.refdef view values */ void CL_CalcViewValues(void) { int i; float lerp, backlerp, ifov; frame_t *oldframe; player_state_t *ps, *ops; /* find the previous frame to interpolate from */ ps = &cl.frame.playerstate; i = (cl.frame.serverframe - 1) & UPDATE_MASK; oldframe = &cl.frames[i]; if ((oldframe->serverframe != cl.frame.serverframe - 1) || !oldframe->valid) { oldframe = &cl.frame; /* previous frame was dropped or invalid */ } ops = &oldframe->playerstate; /* see if the player entity was teleported this frame */ if ((abs(ops->pmove.origin[0] - ps->pmove.origin[0]) > 256 * 8) || (abs(ops->pmove.origin[1] - ps->pmove.origin[1]) > 256 * 8) || (abs(ops->pmove.origin[2] - ps->pmove.origin[2]) > 256 * 8)) { ops = ps; /* don't interpolate */ } lerp = cl.lerpfrac; /* calculate the origin */ if ((cl_predict->value) && !(cl.frame.playerstate.pmove.pm_flags & PMF_NO_PREDICTION)) { /* use predicted values */ unsigned delta; backlerp = 1.0f - lerp; for (i = 0; i < 3; i++) { cl.refdef.vieworg[i] = cl.predicted_origin[i] + ops->viewoffset[i] + cl.lerpfrac * (ps->viewoffset[i] - ops->viewoffset[i]) - backlerp * cl.prediction_error[i]; } /* smooth out stair climbing */ delta = cls.realtime - cl.predicted_step_time; if (delta < 100) { cl.refdef.vieworg[2] -= cl.predicted_step * (100 - delta) * 0.01; } } else { /* just use interpolated values */ for (i = 0; i < 3; i++) { cl.refdef.vieworg[i] = ops->pmove.origin[i] * 0.125 + ops->viewoffset[i] + lerp * (ps->pmove.origin[i] * 0.125 + ps->viewoffset[i] - (ops->pmove.origin[i] * 0.125 + ops->viewoffset[i])); } } /* if not running a demo or on a locked frame, add the local angle movement */ if (cl.frame.playerstate.pmove.pm_type < PM_DEAD) { /* use predicted values */ for (i = 0; i < 3; i++) { cl.refdef.viewangles[i] = cl.predicted_angles[i]; } } else { /* just use interpolated values */ for (i = 0; i < 3; i++) { cl.refdef.viewangles[i] = LerpAngle(ops->viewangles[i], ps->viewangles[i], lerp); } } for (i = 0; i < 3; i++) { cl.refdef.viewangles[i] += LerpAngle(ops->kick_angles[i], ps->kick_angles[i], lerp); } AngleVectors(cl.refdef.viewangles, cl.v_forward, cl.v_right, cl.v_up); /* interpolate field of view */ ifov = ops->fov + lerp * (ps->fov - ops->fov); if (horplus->value) { cl.refdef.fov_x = AdaptFov(ifov, cl.refdef.width, cl.refdef.height); } else { cl.refdef.fov_x = ifov; } /* don't interpolate blend color */ for (i = 0; i < 4; i++) { cl.refdef.blend[i] = ps->blend[i]; } /* add the weapon */ CL_AddViewWeapon(ps, ops); } /* * Emits all entities, particles, and lights to the refresh */ void CL_AddEntities(void) { if (cls.state != ca_active) { return; } if (cl.time > cl.frame.servertime) { if (cl_showclamp->value) { Com_Printf("high clamp %i\n", cl.time - cl.frame.servertime); } cl.time = cl.frame.servertime; cl.lerpfrac = 1.0; } else if (cl.time < cl.frame.servertime - 100) { if (cl_showclamp->value) { Com_Printf("low clamp %i\n", cl.frame.servertime - 100 - cl.time); } cl.time = cl.frame.servertime - 100; cl.lerpfrac = 0; } else { cl.lerpfrac = 1.0 - (cl.frame.servertime - cl.time) * 0.01f; } if (cl_timedemo->value) { cl.lerpfrac = 1.0; } CL_CalcViewValues(); CL_AddPacketEntities(&cl.frame); CL_AddTEnts(); CL_AddParticles(); CL_AddDLights(); CL_AddLightStyles(); } /* * Called to get the sound spatialization origin */ void CL_GetEntitySoundOrigin(int ent, vec3_t org) { centity_t *old; if ((ent < 0) || (ent >= MAX_EDICTS)) { Com_Error(ERR_DROP, "CL_GetEntitySoundOrigin: bad ent"); } old = &cl_entities[ent]; VectorCopy(old->lerp_origin, org); } yquake2-QUAKE2_5_32/src/common/0000755000175000017500000000000012615162034016676 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/common/netchan.c0000644000175000017500000002363712615162034020475 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 low level, platform independant network code * * ======================================================================= */ #include "header/common.h" /* * packet header * ------------- * 31 sequence * 1 does this message contain a reliable payload * 31 acknowledge sequence * 1 acknowledge receipt of even/odd message * 16 qport * * The remote connection never knows if it missed a reliable message, * the local side detects that it has been dropped by seeing a sequence * acknowledge higher thatn the last reliable sequence, but without the * correct even/odd bit for the reliable set. * * If the sender notices that a reliable message has been dropped, it * will be retransmitted. It will not be retransmitted again until a * message after the retransmit has been acknowledged and the reliable * still failed to get there. * * if the sequence number is -1, the packet should be handled without a * netcon * * The reliable message can be added to at any time by doing MSG_Write* * (&netchan->message, ). * * If the message buffer is overflowed, either by a single message, or * by multiple frames worth piling up while the last reliable transmit * goes unacknowledged, the netchan signals a fatal error. * * Reliable messages are always placed first in a packet, then the * unreliable message is included if there is sufficient room. * * To the receiver, there is no distinction between the reliable and * unreliable parts of the message, they are just processed out as a * single larger message. * * Illogical packet sequence numbers cause the packet to be dropped, but * do not kill the connection. This, combined with the tight window of * valid reliable acknowledgement numbers provides protection against * malicious address spoofing. * * The qport field is a workaround for bad address translating routers * that sometimes remap the client's source port on a packet during * gameplay. * * If the base part of the net address matches and the qport matches, * then the channel matches even if the IP port differs. The IP port * should be updated to the new value before sending out any replies. * * If there is no information that needs to be transfered on a given * frame, such as during the connection stage while waiting for the * client to load, then a packet only needs to be delivered if there is * something in the unacknowledged reliable */ cvar_t *showpackets; cvar_t *showdrop; cvar_t *qport; netadr_t net_from; sizebuf_t net_message; byte net_message_buffer[MAX_MSGLEN]; void Netchan_Init(void) { int port; /* pick a port value that should be nice and random */ port = Sys_Milliseconds() & 0xffff; showpackets = Cvar_Get("showpackets", "0", 0); showdrop = Cvar_Get("showdrop", "0", 0); qport = Cvar_Get("qport", va("%i", port), CVAR_NOSET); } /* * Sends an out-of-band datagram */ void Netchan_OutOfBand(int net_socket, netadr_t adr, int length, byte *data) { sizebuf_t send; byte send_buf[MAX_MSGLEN]; /* write the packet header */ SZ_Init(&send, send_buf, sizeof(send_buf)); MSG_WriteLong(&send, -1); /* -1 sequence means out of band */ SZ_Write(&send, data, length); /* send the datagram */ NET_SendPacket(net_socket, send.cursize, send.data, adr); } /* * Sends a text message in an out-of-band datagram */ void Netchan_OutOfBandPrint(int net_socket, netadr_t adr, char *format, ...) { va_list argptr; static char string[MAX_MSGLEN - 4]; va_start(argptr, format); vsnprintf(string, MAX_MSGLEN - 4, format, argptr); va_end(argptr); Netchan_OutOfBand(net_socket, adr, strlen(string), (byte *)string); } /* * called to open a channel to a remote system */ void Netchan_Setup(netsrc_t sock, netchan_t *chan, netadr_t adr, int qport) { memset(chan, 0, sizeof(*chan)); chan->sock = sock; chan->remote_address = adr; chan->qport = qport; chan->last_received = curtime; chan->incoming_sequence = 0; chan->outgoing_sequence = 1; SZ_Init(&chan->message, chan->message_buf, sizeof(chan->message_buf)); chan->message.allowoverflow = true; } /* * Returns true if the last reliable message has acked */ qboolean Netchan_CanReliable(netchan_t *chan) { if (chan->reliable_length) { return false; /* waiting for ack */ } return true; } qboolean Netchan_NeedReliable(netchan_t *chan) { qboolean send_reliable; /* if the remote side dropped the last reliable message, resend it */ send_reliable = false; if ((chan->incoming_acknowledged > chan->last_reliable_sequence) && (chan->incoming_reliable_acknowledged != chan->reliable_sequence)) { send_reliable = true; } /* if the reliable transmit buffer is empty, copy the current message out */ if (!chan->reliable_length && chan->message.cursize) { send_reliable = true; } return send_reliable; } /* * tries to send an unreliable message to a connection, and handles the * transmition / retransmition of the reliable messages. * * A 0 length will still generate a packet and deal with the reliable messages. */ void Netchan_Transmit(netchan_t *chan, int length, byte *data) { sizebuf_t send; byte send_buf[MAX_MSGLEN]; qboolean send_reliable; unsigned w1, w2; /* check for message overflow */ if (chan->message.overflowed) { chan->fatal_error = true; Com_Printf("%s:Outgoing message overflow\n", NET_AdrToString(chan->remote_address)); return; } send_reliable = Netchan_NeedReliable(chan); if (!chan->reliable_length && chan->message.cursize) { memcpy(chan->reliable_buf, chan->message_buf, chan->message.cursize); chan->reliable_length = chan->message.cursize; chan->message.cursize = 0; chan->reliable_sequence ^= 1; } /* write the packet header */ SZ_Init(&send, send_buf, sizeof(send_buf)); w1 = (chan->outgoing_sequence & ~(1 << 31)) | (send_reliable << 31); w2 = (chan->incoming_sequence & ~(1 << 31)) | (chan->incoming_reliable_sequence << 31); chan->outgoing_sequence++; chan->last_sent = curtime; MSG_WriteLong(&send, w1); MSG_WriteLong(&send, w2); /* send the qport if we are a client */ if (chan->sock == NS_CLIENT) { MSG_WriteShort(&send, qport->value); } /* copy the reliable message to the packet first */ if (send_reliable) { SZ_Write(&send, chan->reliable_buf, chan->reliable_length); chan->last_reliable_sequence = chan->outgoing_sequence; } /* add the unreliable part if space is available */ if (send.maxsize - send.cursize >= length) { SZ_Write(&send, data, length); } else { Com_Printf("Netchan_Transmit: dumped unreliable\n"); } /* send the datagram */ NET_SendPacket(chan->sock, send.cursize, send.data, chan->remote_address); if (showpackets->value) { if (send_reliable) { Com_Printf("send %4i : s=%i reliable=%i ack=%i rack=%i\n", send.cursize, chan->outgoing_sequence - 1, chan->reliable_sequence, chan->incoming_sequence, chan->incoming_reliable_sequence); } else { Com_Printf("send %4i : s=%i ack=%i rack=%i\n", send.cursize, chan->outgoing_sequence - 1, chan->incoming_sequence, chan->incoming_reliable_sequence); } } } /* * called when the current net_message is from remote_address * modifies net_message so that it points to the packet payload */ qboolean Netchan_Process(netchan_t *chan, sizebuf_t *msg) { unsigned sequence, sequence_ack; unsigned reliable_ack, reliable_message; /* get sequence numbers */ MSG_BeginReading(msg); sequence = MSG_ReadLong(msg); sequence_ack = MSG_ReadLong(msg); /* read the qport if we are a server */ if (chan->sock == NS_SERVER) { (void)MSG_ReadShort(msg); } reliable_message = sequence >> 31; reliable_ack = sequence_ack >> 31; sequence &= ~(1 << 31); sequence_ack &= ~(1 << 31); if (showpackets->value) { if (reliable_message) { Com_Printf("recv %4i : s=%i reliable=%i ack=%i rack=%i\n", msg->cursize, sequence, chan->incoming_reliable_sequence ^ 1, sequence_ack, reliable_ack); } else { Com_Printf("recv %4i : s=%i ack=%i rack=%i\n", msg->cursize, sequence, sequence_ack, reliable_ack); } } /* discard stale or duplicated packets */ if (sequence <= chan->incoming_sequence) { if (showdrop->value) { Com_Printf("%s:Out of order packet %i at %i\n", NET_AdrToString(chan->remote_address), sequence, chan->incoming_sequence); } return false; } /* dropped packets don't keep the message from being used */ chan->dropped = sequence - (chan->incoming_sequence + 1); if (chan->dropped > 0) { if (showdrop->value) { Com_Printf("%s:Dropped %i packets at %i\n", NET_AdrToString(chan->remote_address), chan->dropped, sequence); } } /* if the current outgoing reliable message has been acknowledged * clear the buffer to make way for the next */ if (reliable_ack == chan->reliable_sequence) { chan->reliable_length = 0; /* it has been received */ } /* if this message contains a reliable message, bump incoming_reliable_sequence */ chan->incoming_sequence = sequence; chan->incoming_acknowledged = sequence_ack; chan->incoming_reliable_acknowledged = reliable_ack; if (reliable_message) { chan->incoming_reliable_sequence ^= 1; } /* the message can now be read from the current message pointer */ chan->last_received = curtime; return true; } yquake2-QUAKE2_5_32/src/common/movemsg.c0000644000175000017500000004411112615162034020520 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Movement message (forward, backward, left, right, etc) handling. * * ======================================================================= */ #include "header/common.h" vec3_t bytedirs[NUMVERTEXNORMALS] = { {-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} }; 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, (int)strlen(s) + 1); } } void MSG_WriteCoord(sizebuf_t *sb, float f) { MSG_WriteShort(sb, (int)(f * 8)); } void MSG_WritePos(sizebuf_t *sb, vec3_t pos) { MSG_WriteShort(sb, (int)(pos[0] * 8)); MSG_WriteShort(sb, (int)(pos[1] * 8)); MSG_WriteShort(sb, (int)(pos[2] * 8)); } void MSG_WriteAngle(sizebuf_t *sb, float f) { MSG_WriteByte(sb, (int)(f * 256 / 360) & 255); } void MSG_WriteAngle16(sizebuf_t *sb, float f) { MSG_WriteShort(sb, ANGLE2SHORT(f)); } void MSG_WriteDeltaUsercmd(sizebuf_t *buf, usercmd_t *from, usercmd_t *cmd) { int bits; /* Movement messages */ bits = 0; if (cmd->angles[0] != from->angles[0]) { bits |= CM_ANGLE1; } if (cmd->angles[1] != from->angles[1]) { bits |= CM_ANGLE2; } if (cmd->angles[2] != from->angles[2]) { bits |= CM_ANGLE3; } if (cmd->forwardmove != from->forwardmove) { bits |= CM_FORWARD; } if (cmd->sidemove != from->sidemove) { bits |= CM_SIDE; } if (cmd->upmove != from->upmove) { bits |= CM_UP; } if (cmd->buttons != from->buttons) { bits |= CM_BUTTONS; } if (cmd->impulse != from->impulse) { bits |= CM_IMPULSE; } MSG_WriteByte(buf, bits); if (bits & CM_ANGLE1) { MSG_WriteShort(buf, cmd->angles[0]); } if (bits & CM_ANGLE2) { MSG_WriteShort(buf, cmd->angles[1]); } if (bits & CM_ANGLE3) { MSG_WriteShort(buf, cmd->angles[2]); } if (bits & CM_FORWARD) { MSG_WriteShort(buf, cmd->forwardmove); } if (bits & CM_SIDE) { MSG_WriteShort(buf, cmd->sidemove); } if (bits & CM_UP) { MSG_WriteShort(buf, cmd->upmove); } if (bits & CM_BUTTONS) { MSG_WriteByte(buf, cmd->buttons); } if (bits & CM_IMPULSE) { MSG_WriteByte(buf, cmd->impulse); } MSG_WriteByte(buf, cmd->msec); MSG_WriteByte(buf, cmd->lightlevel); } void MSG_WriteDir(sizebuf_t *sb, vec3_t dir) { int i, best; float d, bestd; if (!dir) { MSG_WriteByte(sb, 0); return; } bestd = 0; best = 0; for (i = 0; i < NUMVERTEXNORMALS; i++) { d = DotProduct(dir, bytedirs[i]); if (d > bestd) { bestd = d; best = i; } } MSG_WriteByte(sb, best); } void MSG_ReadDir(sizebuf_t *sb, vec3_t dir) { int b; b = MSG_ReadByte(sb); if (b >= NUMVERTEXNORMALS) { Com_Error(ERR_DROP, "MSF_ReadDir: out of range"); } VectorCopy(bytedirs[b], dir); } /* * Writes part of a packetentities message. * Can delta from either a baseline or a previous packet_entity */ void MSG_WriteDeltaEntity(entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force, qboolean newentity) { int bits; if (!to->number) { Com_Error(ERR_FATAL, "Unset entity number"); } if (to->number >= MAX_EDICTS) { Com_Error(ERR_FATAL, "Entity number >= MAX_EDICTS"); } /* send an update */ bits = 0; if (to->number >= 256) { bits |= U_NUMBER16; /* number8 is implicit otherwise */ } if (to->origin[0] != from->origin[0]) { bits |= U_ORIGIN1; } if (to->origin[1] != from->origin[1]) { bits |= U_ORIGIN2; } if (to->origin[2] != from->origin[2]) { bits |= U_ORIGIN3; } if (to->angles[0] != from->angles[0]) { bits |= U_ANGLE1; } if (to->angles[1] != from->angles[1]) { bits |= U_ANGLE2; } if (to->angles[2] != from->angles[2]) { bits |= U_ANGLE3; } if (to->skinnum != from->skinnum) { if ((unsigned)to->skinnum < 256) { bits |= U_SKIN8; } else if ((unsigned)to->skinnum < 0x10000) { bits |= U_SKIN16; } else { bits |= (U_SKIN8 | U_SKIN16); } } if (to->frame != from->frame) { if (to->frame < 256) { bits |= U_FRAME8; } else { bits |= U_FRAME16; } } if (to->effects != from->effects) { if (to->effects < 256) { bits |= U_EFFECTS8; } else if (to->effects < 0x8000) { bits |= U_EFFECTS16; } else { bits |= U_EFFECTS8 | U_EFFECTS16; } } if (to->renderfx != from->renderfx) { if (to->renderfx < 256) { bits |= U_RENDERFX8; } else if (to->renderfx < 0x8000) { bits |= U_RENDERFX16; } else { bits |= U_RENDERFX8 | U_RENDERFX16; } } if (to->solid != from->solid) { bits |= U_SOLID; } /* event is not delta compressed, just 0 compressed */ if (to->event) { bits |= U_EVENT; } if (to->modelindex != from->modelindex) { bits |= U_MODEL; } if (to->modelindex2 != from->modelindex2) { bits |= U_MODEL2; } if (to->modelindex3 != from->modelindex3) { bits |= U_MODEL3; } if (to->modelindex4 != from->modelindex4) { bits |= U_MODEL4; } if (to->sound != from->sound) { bits |= U_SOUND; } if (newentity || (to->renderfx & RF_BEAM)) { bits |= U_OLDORIGIN; } /* write the message */ if (!bits && !force) { return; /* nothing to send! */ } if (bits & 0xff000000) { bits |= U_MOREBITS3 | U_MOREBITS2 | U_MOREBITS1; } else if (bits & 0x00ff0000) { bits |= U_MOREBITS2 | U_MOREBITS1; } else if (bits & 0x0000ff00) { bits |= U_MOREBITS1; } MSG_WriteByte(msg, bits & 255); if (bits & 0xff000000) { MSG_WriteByte(msg, (bits >> 8) & 255); MSG_WriteByte(msg, (bits >> 16) & 255); MSG_WriteByte(msg, (bits >> 24) & 255); } else if (bits & 0x00ff0000) { MSG_WriteByte(msg, (bits >> 8) & 255); MSG_WriteByte(msg, (bits >> 16) & 255); } else if (bits & 0x0000ff00) { MSG_WriteByte(msg, (bits >> 8) & 255); } if (bits & U_NUMBER16) { MSG_WriteShort(msg, to->number); } else { MSG_WriteByte(msg, to->number); } if (bits & U_MODEL) { MSG_WriteByte(msg, to->modelindex); } if (bits & U_MODEL2) { MSG_WriteByte(msg, to->modelindex2); } if (bits & U_MODEL3) { MSG_WriteByte(msg, to->modelindex3); } if (bits & U_MODEL4) { MSG_WriteByte(msg, to->modelindex4); } if (bits & U_FRAME8) { MSG_WriteByte(msg, to->frame); } if (bits & U_FRAME16) { MSG_WriteShort(msg, to->frame); } if ((bits & U_SKIN8) && (bits & U_SKIN16)) /*used for laser colors */ { MSG_WriteLong(msg, to->skinnum); } else if (bits & U_SKIN8) { MSG_WriteByte(msg, to->skinnum); } else if (bits & U_SKIN16) { MSG_WriteShort(msg, to->skinnum); } if ((bits & (U_EFFECTS8 | U_EFFECTS16)) == (U_EFFECTS8 | U_EFFECTS16)) { MSG_WriteLong(msg, to->effects); } else if (bits & U_EFFECTS8) { MSG_WriteByte(msg, to->effects); } else if (bits & U_EFFECTS16) { MSG_WriteShort(msg, to->effects); } if ((bits & (U_RENDERFX8 | U_RENDERFX16)) == (U_RENDERFX8 | U_RENDERFX16)) { MSG_WriteLong(msg, to->renderfx); } else if (bits & U_RENDERFX8) { MSG_WriteByte(msg, to->renderfx); } else if (bits & U_RENDERFX16) { MSG_WriteShort(msg, to->renderfx); } if (bits & U_ORIGIN1) { MSG_WriteCoord(msg, to->origin[0]); } if (bits & U_ORIGIN2) { MSG_WriteCoord(msg, to->origin[1]); } if (bits & U_ORIGIN3) { MSG_WriteCoord(msg, to->origin[2]); } if (bits & U_ANGLE1) { MSG_WriteAngle(msg, to->angles[0]); } if (bits & U_ANGLE2) { MSG_WriteAngle(msg, to->angles[1]); } if (bits & U_ANGLE3) { MSG_WriteAngle(msg, to->angles[2]); } if (bits & U_OLDORIGIN) { MSG_WriteCoord(msg, to->old_origin[0]); MSG_WriteCoord(msg, to->old_origin[1]); MSG_WriteCoord(msg, to->old_origin[2]); } if (bits & U_SOUND) { MSG_WriteByte(msg, to->sound); } if (bits & U_EVENT) { MSG_WriteByte(msg, to->event); } if (bits & U_SOLID) { MSG_WriteShort(msg, to->solid); } } void MSG_BeginReading(sizebuf_t *msg) { msg->readcount = 0; } int MSG_ReadChar(sizebuf_t *msg_read) { int c; if (msg_read->readcount + 1 > msg_read->cursize) { c = -1; } else { c = (signed char)msg_read->data[msg_read->readcount]; } msg_read->readcount++; return c; } int MSG_ReadByte(sizebuf_t *msg_read) { int c; if (msg_read->readcount + 1 > msg_read->cursize) { c = -1; } else { c = (unsigned char)msg_read->data[msg_read->readcount]; } msg_read->readcount++; return c; } int MSG_ReadShort(sizebuf_t *msg_read) { int c; if (msg_read->readcount + 2 > msg_read->cursize) { c = -1; } else { c = (short)(msg_read->data[msg_read->readcount] + (msg_read->data[msg_read->readcount + 1] << 8)); } msg_read->readcount += 2; return c; } int MSG_ReadLong(sizebuf_t *msg_read) { int c; if (msg_read->readcount + 4 > msg_read->cursize) { c = -1; } else { c = msg_read->data[msg_read->readcount] + (msg_read->data[msg_read->readcount + 1] << 8) + (msg_read->data[msg_read->readcount + 2] << 16) + (msg_read->data[msg_read->readcount + 3] << 24); } msg_read->readcount += 4; return c; } float MSG_ReadFloat(sizebuf_t *msg_read) { union { byte b[4]; float f; int l; } dat; if (msg_read->readcount + 4 > msg_read->cursize) { dat.f = -1; } else { dat.b[0] = msg_read->data[msg_read->readcount]; dat.b[1] = msg_read->data[msg_read->readcount + 1]; dat.b[2] = msg_read->data[msg_read->readcount + 2]; dat.b[3] = msg_read->data[msg_read->readcount + 3]; } msg_read->readcount += 4; dat.l = LittleLong(dat.l); return dat.f; } char * MSG_ReadString(sizebuf_t *msg_read) { static char string[2048]; int l, c; l = 0; do { c = MSG_ReadByte(msg_read); if ((c == -1) || (c == 0)) { break; } string[l] = c; l++; } while (l < sizeof(string) - 1); string[l] = 0; return string; } char * MSG_ReadStringLine(sizebuf_t *msg_read) { static char string[2048]; int l, c; l = 0; do { c = MSG_ReadByte(msg_read); if ((c == -1) || (c == 0) || (c == '\n')) { break; } string[l] = c; l++; } while (l < sizeof(string) - 1); string[l] = 0; return string; } float MSG_ReadCoord(sizebuf_t *msg_read) { return MSG_ReadShort(msg_read) * (0.125f); } void MSG_ReadPos(sizebuf_t *msg_read, vec3_t pos) { pos[0] = MSG_ReadShort(msg_read) * (0.125f); pos[1] = MSG_ReadShort(msg_read) * (0.125f); pos[2] = MSG_ReadShort(msg_read) * (0.125f); } float MSG_ReadAngle(sizebuf_t *msg_read) { return MSG_ReadChar(msg_read) * 1.40625f; } float MSG_ReadAngle16(sizebuf_t *msg_read) { return SHORT2ANGLE(MSG_ReadShort(msg_read)); } void MSG_ReadDeltaUsercmd(sizebuf_t *msg_read, usercmd_t *from, usercmd_t *move) { int bits; memcpy(move, from, sizeof(*move)); bits = MSG_ReadByte(msg_read); /* read current angles */ if (bits & CM_ANGLE1) { move->angles[0] = MSG_ReadShort(msg_read); } if (bits & CM_ANGLE2) { move->angles[1] = MSG_ReadShort(msg_read); } if (bits & CM_ANGLE3) { move->angles[2] = MSG_ReadShort(msg_read); } /* read movement */ if (bits & CM_FORWARD) { move->forwardmove = MSG_ReadShort(msg_read); } if (bits & CM_SIDE) { move->sidemove = MSG_ReadShort(msg_read); } if (bits & CM_UP) { move->upmove = MSG_ReadShort(msg_read); } /* read buttons */ if (bits & CM_BUTTONS) { move->buttons = MSG_ReadByte(msg_read); } if (bits & CM_IMPULSE) { move->impulse = MSG_ReadByte(msg_read); } /* read time to run command */ move->msec = MSG_ReadByte(msg_read); /* read the light level */ move->lightlevel = MSG_ReadByte(msg_read); } void MSG_ReadData(sizebuf_t *msg_read, void *data, int len) { int i; for (i = 0; i < len; i++) { ((byte *)data)[i] = MSG_ReadByte(msg_read); } } yquake2-QUAKE2_5_32/src/common/shared/0000755000175000017500000000000012615162034020144 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/common/shared/rand.c0000644000175000017500000000275112615162034021241 0ustar greffrathgreffrath/* * KISS PRNG (c) 2011 Shinobu * * This file was optained from zuttobenkyou.wordpress.com * and modified by the Yamagi Quake II developers. * * LICENSE: Public domain * * ======================================================================= * * KISS PRNG, as devised by Dr. George Marsaglia * * ======================================================================= */ #include #define QSIZE 0x200000 #define CNG (cng = 6906969069ULL * cng + 13579) #define XS (xs ^= (xs << 13), xs ^= (xs >> 17), xs ^= (xs << 43)) #define KISS (B64MWC() + CNG + XS) static uint64_t QARY[QSIZE]; static int j; static uint64_t carry; static uint64_t xs; static uint64_t cng; uint64_t B64MWC(void) { uint64_t t, x; j = (j + 1) & (QSIZE - 1); x = QARY[j]; t = (x << 28) + carry; carry = (x >> 36) - (t < x); return QARY[j] = t - x; } /* * Generate a pseudorandom * integer >0. */ int randk(void) { int r; r = (int)KISS; r = (r < 0) ? (r * -1) : r; return r; } /* * Generate a pseudorandom * signed float between * 0 and 1. */ float frandk(void) { return (randk()&32767)* (1.0/32767); } /* Generate a pseudorandom * float between -1 and 1. */ float crandk(void) { return (randk()&32767)* (2.0/32767) - 1; } /* * Seeds the PRNG */ void randk_seed(void) { uint64_t i; /* Seed QARY[] with CNG+XS: */ for (i = 0; i < QSIZE; i++) { QARY[i] = CNG + XS; } /* Run through several rounds to warm up the state */ for (i = 0; i < 256; i++) { randk(); } } yquake2-QUAKE2_5_32/src/common/shared/flash.c0000644000175000017500000003216712615162034021416 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Muzzle flash posititions. * * ======================================================================= */ #include "../header/shared.h" vec3_t monster_flash_offset[] = { /* flash 0 is not used */ {0.0, 0.0, 0.0}, /* MZ2_TANK_BLASTER_1 1 */ {20.7, -18.5, 28.7}, /* MZ2_TANK_BLASTER_2 2 */ {16.6, -21.5, 30.1}, /* MZ2_TANK_BLASTER_3 3 */ {11.8, -23.9, 32.1}, /* MZ2_TANK_MACHINEGUN_1 4 */ {22.9, -0.7, 25.3}, /* MZ2_TANK_MACHINEGUN_2 5 */ {22.2, 6.2, 22.3}, /* MZ2_TANK_MACHINEGUN_3 6 */ {19.4, 13.1, 18.6}, /* MZ2_TANK_MACHINEGUN_4 7 */ {19.4, 18.8, 18.6}, /* MZ2_TANK_MACHINEGUN_5 8 */ {17.9, 25.0, 18.6}, /* MZ2_TANK_MACHINEGUN_6 9 */ {14.1, 30.5, 20.6}, /* MZ2_TANK_MACHINEGUN_7 10 */ {9.3, 35.3, 22.1}, /* MZ2_TANK_MACHINEGUN_8 11 */ {4.7, 38.4, 22.1}, /* MZ2_TANK_MACHINEGUN_9 12 */ {-1.1, 40.4, 24.1}, /* MZ2_TANK_MACHINEGUN_10 13 */ {-6.5, 41.2, 24.1}, /* MZ2_TANK_MACHINEGUN_11 14 */ {3.2, 40.1, 24.7}, /* MZ2_TANK_MACHINEGUN_12 15 */ {11.7, 36.7, 26.0}, /* MZ2_TANK_MACHINEGUN_13 16 */ {18.9, 31.3, 26.0}, /* MZ2_TANK_MACHINEGUN_14 17 */ {24.4, 24.4, 26.4}, /* MZ2_TANK_MACHINEGUN_15 18 */ {27.1, 17.1, 27.2}, /* MZ2_TANK_MACHINEGUN_16 19 */ {28.5, 9.1, 28.0}, /* MZ2_TANK_MACHINEGUN_17 20 */ {27.1, 2.2, 28.0}, /* MZ2_TANK_MACHINEGUN_18 21 */ {24.9, -2.8, 28.0}, /* MZ2_TANK_MACHINEGUN_19 22 */ {21.6, -7.0, 26.4}, /* MZ2_TANK_ROCKET_1 23 */ {6.2, 29.1, 49.1}, /* MZ2_TANK_ROCKET_2 24 */ {6.9, 23.8, 49.1}, /* MZ2_TANK_ROCKET_3 25 */ {8.3, 17.8, 49.5}, /* MZ2_INFANTRY_MACHINEGUN_1 26 */ {26.6, 7.1, 13.1}, /* MZ2_INFANTRY_MACHINEGUN_2 27 */ {18.2, 7.5, 15.4}, /* MZ2_INFANTRY_MACHINEGUN_3 28 */ {17.2, 10.3, 17.9}, /* MZ2_INFANTRY_MACHINEGUN_4 29 */ {17.0, 12.8, 20.1}, /* MZ2_INFANTRY_MACHINEGUN_5 30 */ {15.1, 14.1, 21.8}, /* MZ2_INFANTRY_MACHINEGUN_6 31 */ {11.8, 17.2, 23.1}, /* MZ2_INFANTRY_MACHINEGUN_7 32 */ {11.4, 20.2, 21.0}, /* MZ2_INFANTRY_MACHINEGUN_8 33 */ {9.0, 23.0, 18.9}, /* MZ2_INFANTRY_MACHINEGUN_9 34 */ {13.9, 18.6, 17.7}, /* MZ2_INFANTRY_MACHINEGUN_10 35 */ {15.4, 15.6, 15.8}, /* MZ2_INFANTRY_MACHINEGUN_11 36 */ {10.2, 15.2, 25.1}, /* MZ2_INFANTRY_MACHINEGUN_12 37 */ {-1.9, 15.1, 28.2}, /* MZ2_INFANTRY_MACHINEGUN_13 38 */ {-12.4, 13.0, 20.2}, /* MZ2_SOLDIER_BLASTER_1 39 */ {10.6 * 1.2, 7.7 * 1.2, 7.8 * 1.2}, /* MZ2_SOLDIER_BLASTER_2 40 */ {21.1 * 1.2, 3.6 * 1.2, 19.0 * 1.2}, /* MZ2_SOLDIER_SHOTGUN_1 41 */ {10.6 * 1.2, 7.7 * 1.2, 7.8 * 1.2}, /* MZ2_SOLDIER_SHOTGUN_2 42 */ {21.1 * 1.2, 3.6 * 1.2, 19.0 * 1.2}, /* MZ2_SOLDIER_MACHINEGUN_1 43 */ {10.6 * 1.2, 7.7 * 1.2, 7.8 * 1.2}, /* MZ2_SOLDIER_MACHINEGUN_2 44 */ {21.1 * 1.2, 3.6 * 1.2, 19.0 * 1.2}, /* MZ2_GUNNER_MACHINEGUN_1 45 */ {30.1 * 1.15, 3.9 * 1.15, 19.6 * 1.15}, /* MZ2_GUNNER_MACHINEGUN_2 46 */ {29.1 * 1.15, 2.5 * 1.15, 20.7 * 1.15}, /* MZ2_GUNNER_MACHINEGUN_3 47 */ {28.2 * 1.15, 2.5 * 1.15, 22.2 * 1.15}, /* MZ2_GUNNER_MACHINEGUN_4 48 */ {28.2 * 1.15, 3.6 * 1.15, 22.0 * 1.15}, /* MZ2_GUNNER_MACHINEGUN_5 49 */ {26.9 * 1.15, 2.0 * 1.15, 23.4 * 1.15}, /* MZ2_GUNNER_MACHINEGUN_6 50 */ {26.5 * 1.15, 0.6 * 1.15, 20.8 * 1.15}, /* MZ2_GUNNER_MACHINEGUN_7 51 */ {26.9 * 1.15, 0.5 * 1.15, 21.5 * 1.15}, /* MZ2_GUNNER_MACHINEGUN_8 52 */ {29.0 * 1.15, 2.4 * 1.15, 19.5 * 1.15}, /* MZ2_GUNNER_GRENADE_1 53 */ {4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15}, /* MZ2_GUNNER_GRENADE_2 54 */ {4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15}, /* MZ2_GUNNER_GRENADE_3 55 */ {4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15}, /* MZ2_GUNNER_GRENADE_4 56 */ {4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15}, /* MZ2_CHICK_ROCKET_1 57 */ {24.8, -9.0, 39.0}, /* MZ2_FLYER_BLASTER_1 58 */ {12.1, 13.4, -14.5}, /* MZ2_FLYER_BLASTER_2 59 */ {12.1, -7.4, -14.5}, /* MZ2_MEDIC_BLASTER_1 60 */ {12.1, 5.4, 16.5}, /* MZ2_GLADIATOR_RAILGUN_1 61 */ {30.0, 18.0, 28.0}, /* MZ2_HOVER_BLASTER_1 62 */ {32.5, -0.8, 10.0}, /* MZ2_ACTOR_MACHINEGUN_1 63 */ {18.4, 7.4, 9.6}, /* MZ2_SUPERTANK_MACHINEGUN_1 64 */ {30.0, 30.0, 88.5}, /* MZ2_SUPERTANK_MACHINEGUN_2 65 */ {30.0, 30.0, 88.5}, /* MZ2_SUPERTANK_MACHINEGUN_3 66 */ {30.0, 30.0, 88.5}, /* MZ2_SUPERTANK_MACHINEGUN_4 67 */ {30.0, 30.0, 88.5}, /* MZ2_SUPERTANK_MACHINEGUN_5 68 */ {30.0, 30.0, 88.5}, /* MZ2_SUPERTANK_MACHINEGUN_6 69 */ {30.0, 30.0, 88.5}, /* MZ2_SUPERTANK_ROCKET_1 70 */ {16.0, -22.5, 91.2}, /* MZ2_SUPERTANK_ROCKET_2 71 */ {16.0, -33.4, 86.7}, /* MZ2_SUPERTANK_ROCKET_3 72 */ {16.0, -42.8, 83.3}, /* MZ2_BOSS2_MACHINEGUN_L1 73 */ {32, -40, 70}, /* MZ2_BOSS2_MACHINEGUN_L2 74 */ {32, -40, 70}, /* MZ2_BOSS2_MACHINEGUN_L3 75 */ {32, -40, 70}, /* MZ2_BOSS2_MACHINEGUN_L4 76 */ {32, -40, 70}, /* MZ2_BOSS2_MACHINEGUN_L5 77 */ {32, -40, 70}, /* MZ2_BOSS2_ROCKET_1 78 */ {22.0, 16.0, 10.0}, /* MZ2_BOSS2_ROCKET_2 79 */ {22.0, 8.0, 10.0}, /* MZ2_BOSS2_ROCKET_3 80 */ {22.0, -8.0, 10.0}, /* MZ2_BOSS2_ROCKET_4 81 */ {22.0, -16.0, 10.0}, /* MZ2_FLOAT_BLASTER_1 82 */ {32.5, -0.8, 10}, /* MZ2_SOLDIER_BLASTER_3 83 */ {20.8 * 1.2, 10.1 * 1.2, -2.7 * 1.2}, /* MZ2_SOLDIER_SHOTGUN_3 84 */ {20.8 * 1.2, 10.1 * 1.2, -2.7 * 1.2}, /* MZ2_SOLDIER_MACHINEGUN_3 85 */ {20.8 * 1.2, 10.1 * 1.2, -2.7 * 1.2}, /* MZ2_SOLDIER_BLASTER_4 86 */ {7.6 * 1.2, 9.3 * 1.2, 0.8 * 1.2}, /* MZ2_SOLDIER_SHOTGUN_4 87 */ {7.6 * 1.2, 9.3 * 1.2, 0.8 * 1.2}, /* MZ2_SOLDIER_MACHINEGUN_4 88 */ {7.6 * 1.2, 9.3 * 1.2, 0.8 * 1.2}, /* MZ2_SOLDIER_BLASTER_5 89 */ {30.5 * 1.2, 9.9 * 1.2, -18.7 * 1.2}, /* MZ2_SOLDIER_SHOTGUN_5 90 */ {30.5 * 1.2, 9.9 * 1.2, -18.7 * 1.2}, /* MZ2_SOLDIER_MACHINEGUN_5 91 */ {30.5 * 1.2, 9.9 * 1.2, -18.7 * 1.2}, /* MZ2_SOLDIER_BLASTER_6 92 */ {27.6 * 1.2, 3.4 * 1.2, -10.4 * 1.2}, /* MZ2_SOLDIER_SHOTGUN_6 93 */ {27.6 * 1.2, 3.4 * 1.2, -10.4 * 1.2}, /* MZ2_SOLDIER_MACHINEGUN_6 94 */ {27.6 * 1.2, 3.4 * 1.2, -10.4 * 1.2}, /* MZ2_SOLDIER_BLASTER_7 95 */ {28.9 * 1.2, 4.6 * 1.2, -8.1 * 1.2}, /* MZ2_SOLDIER_SHOTGUN_7 96 */ {28.9 * 1.2, 4.6 * 1.2, -8.1 * 1.2}, /* MZ2_SOLDIER_MACHINEGUN_7 97 */ {28.9 * 1.2, 4.6 * 1.2, -8.1 * 1.2}, /* MZ2_SOLDIER_BLASTER_8 98 */ {31.5 * 1.2, 9.6 * 1.2, 10.1 * 1.2}, /* MZ2_SOLDIER_SHOTGUN_8 99 */ {34.5 * 1.2, 9.6 * 1.2, 6.1 * 1.2}, /* MZ2_SOLDIER_MACHINEGUN_8 100 */ {34.5 * 1.2, 9.6 * 1.2, 6.1 * 1.2}, /* MZ2_MAKRON_BFG 101 */ {17, -19.5, 62.9}, /* MZ2_MAKRON_BLASTER_1 102 */ {-3.6, -24.1, 59.5}, /* MZ2_MAKRON_BLASTER_2 103 */ {-1.6, -19.3, 59.5}, /* MZ2_MAKRON_BLASTER_3 104 */ {-0.1, -14.4, 59.5}, /* MZ2_MAKRON_BLASTER_4 105 */ {2.0, -7.6, 59.5}, /* MZ2_MAKRON_BLASTER_5 106 */ {3.4, 1.3, 59.5}, /* MZ2_MAKRON_BLASTER_6 107 */ {3.7, 11.1, 59.5}, /* MZ2_MAKRON_BLASTER_7 108 */ {-0.3, 22.3, 59.5}, /* MZ2_MAKRON_BLASTER_8 109 */ {-6, 33, 59.5}, /* MZ2_MAKRON_BLASTER_9 110 */ {-9.3, 36.4, 59.5}, /* MZ2_MAKRON_BLASTER_10 111 */ {-7, 35, 59.5}, /* MZ2_MAKRON_BLASTER_11 112 */ {-2.1, 29, 59.5}, /* MZ2_MAKRON_BLASTER_12 113 */ {3.9, 17.3, 59.5}, /* MZ2_MAKRON_BLASTER_13 114 */ {6.1, 5.8, 59.5}, /* MZ2_MAKRON_BLASTER_14 115 */ {5.9, -4.4, 59.5}, /* MZ2_MAKRON_BLASTER_15 116 */ {4.2, -14.1, 59.5}, /* MZ2_MAKRON_BLASTER_16 117 */ {2.4, -18.8, 59.5}, /* MZ2_MAKRON_BLASTER_17 118 */ {-1.8, -25.5, 59.5}, /* MZ2_MAKRON_RAILGUN_1 119 */ {-17.3, 7.8, 72.4}, /* MZ2_JORG_MACHINEGUN_L1 120 */ {78.5, -47.1, 96}, /* MZ2_JORG_MACHINEGUN_L2 121 */ {78.5, -47.1, 96}, /* MZ2_JORG_MACHINEGUN_L3 122 */ {78.5, -47.1, 96}, /* MZ2_JORG_MACHINEGUN_L4 123 */ {78.5, -47.1, 96}, /* MZ2_JORG_MACHINEGUN_L5 124 */ {78.5, -47.1, 96}, /* MZ2_JORG_MACHINEGUN_L6 125 */ {78.5, -47.1, 96}, /* MZ2_JORG_MACHINEGUN_R1 126 */ {78.5, 46.7, 96}, /* MZ2_JORG_MACHINEGUN_R2 127 */ {78.5, 46.7, 96}, /* MZ2_JORG_MACHINEGUN_R3 128 */ {78.5, 46.7, 96}, /* MZ2_JORG_MACHINEGUN_R4 129 */ {78.5, 46.7, 96}, /* MZ2_JORG_MACHINEGUN_R5 130 */ {78.5, 46.7, 96}, /* MZ2_JORG_MACHINEGUN_R6 131 */ {78.5, 46.7, 96}, /* MZ2_JORG_BFG_1 132 */ {6.3, -9, 111.2}, /* MZ2_BOSS2_MACHINEGUN_R1 73 */ {32, 40, 70}, /* MZ2_BOSS2_MACHINEGUN_R2 74 */ {32, 40, 70}, /* MZ2_BOSS2_MACHINEGUN_R3 75 */ {32, 40, 70}, /* MZ2_BOSS2_MACHINEGUN_R4 76 */ {32, 40, 70}, /* MZ2_BOSS2_MACHINEGUN_R5 77 */ {32, 40, 70}, /* MZ2_CARRIER_MACHINEGUN_L1 */ {56, -32, 32}, /* MZ2_CARRIER_MACHINEGUN_R1 */ {56, 32, 32}, /* MZ2_CARRIER_GRENADE */ {42, 24, 50}, /* MZ2_TURRET_MACHINEGUN 141 */ {16, 0, 0}, /* MZ2_TURRET_ROCKET 142 */ {16, 0, 0}, /* MZ2_TURRET_BLASTER 143 */ {16, 0, 0}, /* MZ2_STALKER_BLASTER 144 */ {24, 0, 6}, /* MZ2_DAEDALUS_BLASTER 145 */ {32.5, -0.8, 10.0}, /* MZ2_MEDIC_BLASTER_2 146 */ {12.1, 5.4, 16.5}, /* MZ2_CARRIER_RAILGUN 147 */ {32, 0, 6}, /* MZ2_WIDOW_DISRUPTOR 148 */ {57.72, 14.50, 88.81}, /* MZ2_WIDOW_BLASTER 149 */ {56, 32, 32}, /* MZ2_WIDOW_RAIL 150 */ {62, -20, 84}, /* MZ2_WIDOW_PLASMABEAM 151 */ {32, 0, 6}, /* MZ2_CARRIER_MACHINEGUN_L2 152 */ {61, -32, 12}, /* MZ2_CARRIER_MACHINEGUN_R2 153 */ {61, 32, 12}, /* MZ2_WIDOW_RAIL_LEFT 154 */ {17, -62, 91}, /* MZ2_WIDOW_RAIL_RIGHT 155 */ {68, 12, 86}, /* MZ2_WIDOW_BLASTER_SWEEP1 156 */ {47.5, 56, 89}, /* MZ2_WIDOW_BLASTER_SWEEP2 157 */ {54, 52, 91}, /* MZ2_WIDOW_BLASTER_SWEEP3 158 */ {58, 40, 91}, /* MZ2_WIDOW_BLASTER_SWEEP4 159 */ {68, 30, 88}, /* MZ2_WIDOW_BLASTER_SWEEP5 160 */ {74, 20, 88}, /* MZ2_WIDOW_BLASTER_SWEEP6 161 */ {73, 11, 87}, /* MZ2_WIDOW_BLASTER_SWEEP7 162 */ {73, 3, 87}, /* MZ2_WIDOW_BLASTER_SWEEP8 163 */ {70, -12, 87}, /* MZ2_WIDOW_BLASTER_SWEEP9 164 */ {67, -20, 90}, /* MZ2_WIDOW_BLASTER_100 165 */ {-20, 76, 90}, /* MZ2_WIDOW_BLASTER_90 166 */ {-8, 74, 90}, /* MZ2_WIDOW_BLASTER_80 167 */ {0, 72, 90}, /* MZ2_WIDOW_BLASTER_70 168 d06 */ {10, 71, 89}, /* MZ2_WIDOW_BLASTER_60 169 d07 */ {23, 70, 87}, /* MZ2_WIDOW_BLASTER_50 170 d08 */ {32, 64, 85}, /* MZ2_WIDOW_BLASTER_40 171 */ {40, 58, 84}, /* MZ2_WIDOW_BLASTER_30 172 d10 */ {48, 50, 83}, /* MZ2_WIDOW_BLASTER_20 173 */ {54, 42, 82}, /* MZ2_WIDOW_BLASTER_10 174 d12 */ {56, 34, 82}, /* MZ2_WIDOW_BLASTER_0 175 */ {58, 26, 82}, /* MZ2_WIDOW_BLASTER_10L 176 d14 */ {60, 16, 82}, /* MZ2_WIDOW_BLASTER_20L 177 */ {59, 6, 81}, /* MZ2_WIDOW_BLASTER_30L 178 d16 */ {58, -2, 80}, /* MZ2_WIDOW_BLASTER_40L 179 */ {57, -10, 79}, /* MZ2_WIDOW_BLASTER_50L 180 d18 */ {54, -18, 78}, /* MZ2_WIDOW_BLASTER_60L 181 */ {42, -32, 80}, /* MZ2_WIDOW_BLASTER_70L 182 d20 */ {36, -40, 78}, /* MZ2_WIDOW_RUN_1 183 */ {68.4, 10.88, 82.08}, /* MZ2_WIDOW_RUN_2 184 */ {68.51, 8.64, 85.14}, /* MZ2_WIDOW_RUN_3 185 */ {68.66, 6.38, 88.78}, /* MZ2_WIDOW_RUN_4 186 */ {68.73, 5.1, 84.47}, /* MZ2_WIDOW_RUN_5 187 */ {68.82, 4.79, 80.52}, /* MZ2_WIDOW_RUN_6 188 */ {68.77, 6.11, 85.37}, /* MZ2_WIDOW_RUN_7 189 */ {68.67, 7.99, 90.24}, /* MZ2_WIDOW_RUN_8 190 */ {68.55, 9.54, 87.36}, /* MZ2_CARRIER_ROCKET_1 191 */ {0, 0, -5}, /* MZ2_CARRIER_ROCKET_2 192 */ {0, 0, -5}, /* MZ2_CARRIER_ROCKET_3 193 */ {0, 0, -5}, /* MZ2_CARRIER_ROCKET_4 194 */ {0, 0, -5}, /* MZ2_WIDOW2_BEAMER_1 195 */ /* { 72.13, -17.63, 93.77 }, */ {69.00, -17.63, 93.77}, /* MZ2_WIDOW2_BEAMER_2 196 */ /* { 71.46, -17.08, 89.82 }, */ {69.00, -17.08, 89.82}, /* MZ2_WIDOW2_BEAMER_3 197 */ /* { 71.47, -18.40, 90.70 }, */ {69.00, -18.40, 90.70}, /* MZ2_WIDOW2_BEAMER_4 198 */ /* { 71.96, -18.34, 94.32 }, */ {69.00, -18.34, 94.32}, /* MZ2_WIDOW2_BEAMER_5 199 */ /* { 72.25, -18.30, 97.98 }, */ {69.00, -18.30, 97.98}, /* MZ2_WIDOW2_BEAM_SWEEP_1 200 */ {45.04, -59.02, 92.24}, /* MZ2_WIDOW2_BEAM_SWEEP_2 201 */ {50.68, -54.70, 91.96}, /* MZ2_WIDOW2_BEAM_SWEEP_3 202 */ {56.57, -47.72, 91.65}, /* MZ2_WIDOW2_BEAM_SWEEP_4 203 */ {61.75, -38.75, 91.38}, /* MZ2_WIDOW2_BEAM_SWEEP_5 204 */ {65.55, -28.76, 91.24}, /* MZ2_WIDOW2_BEAM_SWEEP_6 205 */ {67.79, -18.90, 91.22}, /* MZ2_WIDOW2_BEAM_SWEEP_7 206 */ {68.60, -9.52, 91.23}, /* MZ2_WIDOW2_BEAM_SWEEP_8 207 */ {68.08, 0.18, 91.32}, /* MZ2_WIDOW2_BEAM_SWEEP_9 208 */ {66.14, 9.79, 91.44}, /* MZ2_WIDOW2_BEAM_SWEEP_10 209 */ {62.77, 18.91, 91.65}, /* MZ2_WIDOW2_BEAM_SWEEP_11 210 */ {58.29, 27.11, 92.00}, /* end of table */ {0.0, 0.0, 0.0} }; yquake2-QUAKE2_5_32/src/common/shared/shared.c0000644000175000017500000005240412615162034021563 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Support functions, linked into client, server, renderer and game. * * ======================================================================= */ #include #include "../header/shared.h" #define DEG2RAD(a) (a * M_PI) / 180.0F vec3_t vec3_origin = {0, 0, 0}; /* ============================================================================ */ 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; zrot[0][0] = (float)cos(DEG2RAD(degrees)); zrot[0][1] = (float)sin(DEG2RAD(degrees)); zrot[1][0] = (float)-sin(DEG2RAD(degrees)); zrot[1][1] = (float)cos(DEG2RAD(degrees)); 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]; } } void AngleVectors(vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) { float angle; static float sr, sp, sy, cr, cp, cy; angle = angles[YAW] * (M_PI * 2 / 360); sy = (float)sin(angle); cy = (float)cos(angle); angle = angles[PITCH] * (M_PI * 2 / 360); sp = (float)sin(angle); cp = (float)cos(angle); angle = angles[ROLL] * (M_PI * 2 / 360); sr = (float)sin(angle); cr = (float)cos(angle); if (forward) { forward[0] = cp * cy; forward[1] = cp * sy; forward[2] = -sp; } if (right) { right[0] = (-1 * sr * sp * cy + - 1 * cr * -sy); right[1] = (-1 * sr * sp * sy + - 1 * cr * cy); right[2] = -1 * sr * cp; } if (up) { up[0] = (cr * sp * cy + - sr * -sy); up[1] = (cr * sp * sy + - sr * cy); up[2] = cr * cp; } } void AngleVectors2(vec3_t value1, vec3_t angles) { float forward; float yaw, pitch; if ((value1[1] == 0) && (value1[0] == 0)) { yaw = 0; if (value1[2] > 0) { pitch = 90; } else { pitch = 270; } } else { if (value1[0]) { yaw = ((float)atan2(value1[1], value1[0]) * 180 / M_PI); } else if (value1[1] > 0) { yaw = 90; } else { yaw = 270; } if (yaw < 0) { yaw += 360; } forward = (float)sqrt(value1[0] * value1[0] + value1[1] * value1[1]); pitch = ((float)atan2(value1[2], forward) * 180 / M_PI); if (pitch < 0) { pitch += 360; } } angles[PITCH] = -pitch; angles[YAW] = yaw; angles[ROLL] = 0; } 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++) { if (fabs(src[i]) < minelem) { pos = i; minelem = (float)fabs(src[i]); } } 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 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]; } 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]; } /* ============================================================================ */ float Q_fabs(float f) { int tmp = *(int *)&f; tmp &= 0x7FFFFFFF; return *(float *)&tmp; } float LerpAngle(float a2, float a1, float frac) { if (a1 - a2 > 180) { a1 -= 360; } if (a1 - a2 < -180) { a1 += 360; } return a2 + frac * (a1 - a2); } float anglemod(float a) { a = (360.0 / 65536) * ((int)(a * (65536 / 360.0)) & 65535); return a; } /* * This is the slow, general version */ int BoxOnPlaneSide2(vec3_t emins, vec3_t emaxs, struct cplane_s *p) { int i; float dist1, dist2; int sides; vec3_t corners[2]; for (i = 0; i < 3; i++) { if (p->normal[i] < 0) { corners[0][i] = emins[i]; corners[1][i] = emaxs[i]; } else { corners[1][i] = emins[i]; corners[0][i] = emaxs[i]; } } dist1 = DotProduct(p->normal, corners[0]) - p->dist; dist2 = DotProduct(p->normal, corners[1]) - p->dist; sides = 0; if (dist1 >= 0) { sides = 1; } if (dist2 < 0) { sides |= 2; } return sides; } /* * Returns 1, 2, or 1 + 2 */ int BoxOnPlaneSide(vec3_t emins, vec3_t emaxs, struct cplane_s *p) { float dist1, dist2; int sides; /* fast axial cases */ if (p->type < 3) { if (p->dist <= emins[p->type]) { return 1; } if (p->dist >= emaxs[p->type]) { return 2; } return 3; } /* general case */ switch (p->signbits) { case 0: dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; dist2 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; break; case 1: dist1 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; break; case 2: dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; dist2 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; break; case 3: dist1 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; break; case 4: dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; dist2 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; break; case 5: dist1 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; break; case 6: dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; dist2 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; break; case 7: dist1 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; break; default: dist1 = dist2 = 0; break; } sides = 0; if (dist1 >= p->dist) { sides = 1; } if (dist2 < p->dist) { sides |= 2; } return sides; } void ClearBounds(vec3_t mins, vec3_t maxs) { mins[0] = mins[1] = mins[2] = 99999; maxs[0] = maxs[1] = maxs[2] = -99999; } void AddPointToBounds(vec3_t v, vec3_t mins, vec3_t maxs) { int i; vec_t val; for (i = 0; i < 3; i++) { val = v[i]; if (val < mins[i]) { mins[i] = val; } if (val > maxs[i]) { maxs[i] = val; } } } int VectorCompare(vec3_t v1, vec3_t v2) { if ((v1[0] != v2[0]) || (v1[1] != v2[1]) || (v1[2] != v2[2])) { return 0; } return 1; } vec_t VectorNormalize(vec3_t v) { float length, ilength; length = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; length = (float)sqrt(length); if (length) { ilength = 1 / length; v[0] *= ilength; v[1] *= ilength; v[2] *= ilength; } return length; } vec_t VectorNormalize2(vec3_t v, vec3_t out) { float length, ilength; length = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; length = (float)sqrt(length); if (length) { ilength = 1 / length; out[0] = v[0] * ilength; out[1] = v[1] * ilength; out[2] = v[2] * ilength; } return length; } void VectorMA(vec3_t veca, float scale, vec3_t vecb, vec3_t vecc) { vecc[0] = veca[0] + scale * vecb[0]; vecc[1] = veca[1] + scale * vecb[1]; vecc[2] = veca[2] + scale * vecb[2]; } vec_t _DotProduct(vec3_t v1, vec3_t v2) { return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; } void _VectorSubtract(vec3_t veca, vec3_t vecb, vec3_t out) { out[0] = veca[0] - vecb[0]; out[1] = veca[1] - vecb[1]; out[2] = veca[2] - vecb[2]; } void _VectorAdd(vec3_t veca, vec3_t vecb, vec3_t out) { out[0] = veca[0] + vecb[0]; out[1] = veca[1] + vecb[1]; out[2] = veca[2] + vecb[2]; } void _VectorCopy(vec3_t in, vec3_t out) { out[0] = in[0]; out[1] = in[1]; out[2] = in[2]; } void CrossProduct(vec3_t v1, vec3_t v2, vec3_t cross) { cross[0] = v1[1] * v2[2] - v1[2] * v2[1]; cross[1] = v1[2] * v2[0] - v1[0] * v2[2]; cross[2] = v1[0] * v2[1] - v1[1] * v2[0]; } double sqrt(double x); vec_t VectorLength(vec3_t v) { int i; float length; length = 0; for (i = 0; i < 3; i++) { length += v[i] * v[i]; } length = (float)sqrt(length); 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; } /* ==================================================================================== */ char * COM_SkipPath(char *pathname) { char *last; last = pathname; while (*pathname) { if (*pathname == '/') { last = pathname + 1; } pathname++; } return last; } void COM_StripExtension(char *in, char *out) { while (*in && *in != '.') { *out++ = *in++; } *out = 0; } const char * COM_FileExtension(const char *in) { const char *ext = strrchr(in, '.'); if (!ext || ext == in) { return ""; } return ext + 1; } void COM_FileBase(char *in, char *out) { char *s, *s2; s = in + strlen(in) - 1; while (s != in && *s != '.') { s--; } for (s2 = s; s2 != in && *s2 != '/'; s2--) { } if (s - s2 < 2) { out[0] = 0; } else { s--; memcpy(out, s2 + 1, s - s2); out[s - s2] = 0; } } /* * Returns the path up to, but not including the last / */ void COM_FilePath(const char *in, char *out) { const char *s; s = in + strlen(in) - 1; while (s != in && *s != '/') { s--; } memcpy(out, in, s - in); out[s - in] = 0; } void COM_DefaultExtension(char *path, const 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); } /* * ============================================================================ * * BYTE ORDER FUNCTIONS * * ============================================================================ */ qboolean bigendien; /* can't just use function pointers, or dll linkage can mess up when qcommon is included in multiple places */ 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 BigShort(short l) { return _BigShort(l); } short LittleShort(short l) { return _LittleShort(l); } int BigLong(int l) { return _BigLong(l); } int LittleLong(int l) { return _LittleLong(l); } float BigFloat(float l) { return _BigFloat(l); } float LittleFloat(float l) { return _LittleFloat(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; } void Swap_Init(void) { byte swaptest[2] = {1, 0}; /* set the byte swapping variables in a portable manner */ if (*(short *)swaptest == 1) { bigendien = false; _BigShort = ShortSwap; _LittleShort = ShortNoSwap; _BigLong = LongSwap; _LittleLong = LongNoSwap; _BigFloat = FloatSwap; _LittleFloat = FloatNoSwap; Com_Printf("Byte ordering: little endian\n\n"); } else { bigendien = true; _BigShort = ShortNoSwap; _LittleShort = ShortSwap; _BigLong = LongNoSwap; _LittleLong = LongSwap; _BigFloat = FloatNoSwap; _LittleFloat = FloatSwap; Com_Printf("Byte ordering: big endian\n\n"); } if (LittleShort(*(short *)swaptest) != 1) assert("Error in the endian conversion!"); } /* * does a varargs printf into a temp buffer, so I don't * need to have varargs versions of all text functions. */ char * va(char *format, ...) { va_list argptr; static char string[1024]; va_start(argptr, format); vsnprintf(string, 1024, format, argptr); va_end(argptr); return string; } char com_token[MAX_TOKEN_CHARS]; /* * Parse a token out of a string */ char * COM_Parse(char **data_p) { int c; int len; char *data; data = *data_p; len = 0; com_token[0] = 0; if (!data) { *data_p = NULL; return ""; } 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) { goto done; } 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); done: if (len == MAX_TOKEN_CHARS) { len = 0; } com_token[len] = 0; *data_p = data; return com_token; } int paged_total; void Com_PageInMemory(byte *buffer, int size) { int i; for (i = size - 1; i > 0; i -= 4096) { paged_total += buffer[i]; } } /* * ============================================================================ * * LIBRARY REPLACEMENT FUNCTIONS * * ============================================================================ */ int Q_stricmp(const char *s1, const char *s2) { return strcasecmp(s1, s2); } int Q_strncasecmp(char *s1, char *s2, int n) { int c1, c2; do { c1 = *s1++; c2 = *s2++; if (!n--) { return 0; /* strings are equal until end point */ } if (c1 != c2) { if ((c1 >= 'a') && (c1 <= 'z')) { c1 -= ('a' - 'A'); } if ((c2 >= 'a') && (c2 <= 'z')) { c2 -= ('a' - 'A'); } if (c1 != c2) { return -1; /* strings not equal */ } } } while (c1); return 0; /* strings are equal */ } int Q_strcasecmp(char *s1, char *s2) { return Q_strncasecmp(s1, s2, 99999); } void Com_sprintf(char *dest, int size, char *fmt, ...) { int len; va_list argptr; va_start(argptr, fmt); len = vsnprintf(dest, size, fmt, argptr); va_end(argptr); if (len >= size) { Com_Printf("Com_sprintf: overflow\n"); } } char * Q_strlwr ( char *s ) { char *p = s; while ( *s ) { *s = tolower( *s ); s++; } return ( p ); } int Q_strlcpy(char *dst, const char *src, int size) { const char *s = src; while (*s) { if (size > 1) { *dst++ = *s; size--; } s++; } if (size > 0) { *dst = '\0'; } return s - src; } int Q_strlcat(char *dst, const char *src, int size) { char *d = dst; while (size > 0 && *d) { size--; d++; } return (d - dst) + Q_strlcpy(d, src, size); } /* * ===================================================================== * * INFO STRINGS * * ===================================================================== */ /* * Searches the string for the given * key and returns the associated value, * or an empty string. */ char * Info_ValueForKey(char *s, char *key) { char pkey[512]; static char value[2][512]; /* use two buffers so compares work without stomping on each other */ static int valueindex; char *o; valueindex ^= 1; if (*s == '\\') { s++; } while (1) { o = pkey; while (*s != '\\') { if (!*s) { return ""; } *o++ = *s++; } *o = 0; s++; o = value[valueindex]; while (*s != '\\' && *s) { if (!*s) { return ""; } *o++ = *s++; } *o = 0; if (!strcmp(key, pkey)) { return value[valueindex]; } if (!*s) { return ""; } s++; } } void Info_RemoveKey(char *s, char *key) { char *start; char pkey[512]; char value[512]; char *o; if (strstr(key, "\\")) { return; } while (1) { start = s; if (*s == '\\') { s++; } o = pkey; while (*s != '\\') { if (!*s) { return; } *o++ = *s++; } *o = 0; s++; o = value; while (*s != '\\' && *s) { if (!*s) { return; } *o++ = *s++; } *o = 0; if (!strcmp(key, pkey)) { memmove(start, s, strlen(s) + 1); /* remove this part */ return; } if (!*s) { return; } } } /* * Some characters are illegal in info strings * because they can mess up the server's parsing */ qboolean Info_Validate(char *s) { if (strstr(s, "\"")) { return false; } if (strstr(s, ";")) { return false; } return true; } void Info_SetValueForKey(char *s, char *key, char *value) { char newi[MAX_INFO_STRING], *v; int c; int maxsize = MAX_INFO_STRING; if (strstr(key, "\\") || strstr(value, "\\")) { Com_Printf("Can't use keys or values with a \\\n"); return; } if (strstr(key, ";")) { Com_Printf("Can't use keys or values with a semicolon\n"); return; } if (strstr(key, "\"") || strstr(value, "\"")) { Com_Printf("Can't use keys or values with a \"\n"); return; } if ((strlen(key) > MAX_INFO_KEY - 1) || (strlen(value) > MAX_INFO_KEY - 1)) { Com_Printf("Keys and values must be < 64 characters.\n"); return; } Info_RemoveKey(s, key); if (!value || !strlen(value)) { return; } Com_sprintf(newi, sizeof(newi), "\\%s\\%s", key, value); if (strlen(newi) + strlen(s) > maxsize) { Com_Printf("Info string length exceeded\n"); return; } /* only copy ascii values */ s += strlen(s); v = newi; while (*v) { c = *v++; c &= 127; /* strip high bits */ if ((c >= 32) && (c < 127)) { *s++ = c; } } *s = 0; } yquake2-QUAKE2_5_32/src/common/pmove.c0000644000175000017500000006337712615162034020210 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Player movement code. This is the core of Quake IIs legendary physics * engine * * ======================================================================= */ #include "header/common.h" #include "../client/sound/header/local.h" #include "../client/header/client.h" #if !defined(DEDICATED_ONLY) && defined(USE_OPENAL) void AL_Underwater(); void AL_Overwater(); #endif #define STEPSIZE 18 /* all of the locals will be zeroed before each * pmove, just to make damn sure we don't have * any differences when running on client or server */ typedef struct { vec3_t origin; /* full float precision */ vec3_t velocity; /* full float precision */ vec3_t forward, right, up; float frametime; csurface_t *groundsurface; cplane_t groundplane; int groundcontents; vec3_t previous_origin; qboolean ladder; } pml_t; pmove_t *pm; pml_t pml; /* movement parameters */ float pm_stopspeed = 100; float pm_maxspeed = 300; float pm_duckspeed = 100; float pm_accelerate = 10; float pm_airaccelerate = 0; float pm_wateraccelerate = 10; float pm_friction = 6; float pm_waterfriction = 1; float pm_waterspeed = 400; #define STOP_EPSILON 0.1 /* Slide off of the impacting object returns the blocked flags (1 = floor, 2 = step / wall) */ #define MIN_STEP_NORMAL 0.7 /* can't step up onto very steep slopes */ #define MAX_CLIP_PLANES 5 void PM_ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce) { float backoff; float change; int i; 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; } } } /* * Each intersection will try to step over the obstruction instead of * sliding along it. * * Returns a new origin, velocity, and contact entity * Does not modify any world state? */ void PM_StepSlideMove_(void) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity; int i, j; trace_t trace; vec3_t end; float time_left; numbumps = 4; VectorCopy(pml.velocity, primal_velocity); numplanes = 0; time_left = pml.frametime; for (bumpcount = 0; bumpcount < numbumps; bumpcount++) { for (i = 0; i < 3; i++) { end[i] = pml.origin[i] + time_left * pml.velocity[i]; } trace = pm->trace(pml.origin, pm->mins, pm->maxs, end); if (trace.allsolid) { /* entity is trapped in another solid */ pml.velocity[2] = 0; /* don't build up falling damage */ return; } if (trace.fraction > 0) { /* actually covered some distance */ VectorCopy(trace.endpos, pml.origin); numplanes = 0; } if (trace.fraction == 1) { break; /* moved the entire distance */ } /* save entity for contact */ if ((pm->numtouch < MAXTOUCH) && trace.ent) { pm->touchents[pm->numtouch] = trace.ent; pm->numtouch++; } time_left -= time_left * trace.fraction; /* slide along this plane */ if (numplanes >= MAX_CLIP_PLANES) { /* this shouldn't really happen */ VectorCopy(vec3_origin, pml.velocity); break; } VectorCopy(trace.plane.normal, planes[numplanes]); numplanes++; /* modify original_velocity so it parallels all of the clip planes */ for (i = 0; i < numplanes; i++) { PM_ClipVelocity(pml.velocity, planes[i], pml.velocity, 1.01f); for (j = 0; j < numplanes; j++) { if (j != i) { if (DotProduct(pml.velocity, planes[j]) < 0) { break; /* not ok */ } } } if (j == numplanes) { break; } } if (i != numplanes) { /* go along this plane */ } else { /* go along the crease */ if (numplanes != 2) { VectorCopy(vec3_origin, pml.velocity); break; } CrossProduct(planes[0], planes[1], dir); d = DotProduct(dir, pml.velocity); VectorScale(dir, d, pml.velocity); } /* if velocity is against the original velocity, stop dead to avoid tiny occilations in sloping corners */ if (DotProduct(pml.velocity, primal_velocity) <= 0) { VectorCopy(vec3_origin, pml.velocity); break; } } if (pm->s.pm_time) { VectorCopy(primal_velocity, pml.velocity); } } void PM_StepSlideMove(void) { vec3_t start_o, start_v; vec3_t down_o, down_v; trace_t trace; float down_dist, up_dist; vec3_t up, down; VectorCopy(pml.origin, start_o); VectorCopy(pml.velocity, start_v); PM_StepSlideMove_(); VectorCopy(pml.origin, down_o); VectorCopy(pml.velocity, down_v); VectorCopy(start_o, up); up[2] += STEPSIZE; trace = pm->trace(up, pm->mins, pm->maxs, up); if (trace.allsolid) { return; /* can't step up */ } /* try sliding above */ VectorCopy(up, pml.origin); VectorCopy(start_v, pml.velocity); PM_StepSlideMove_(); /* push down the final amount */ VectorCopy(pml.origin, down); down[2] -= STEPSIZE; trace = pm->trace(pml.origin, pm->mins, pm->maxs, down); if (!trace.allsolid) { VectorCopy(trace.endpos, pml.origin); } VectorCopy(pml.origin, up); /* decide which one went farther */ down_dist = (down_o[0] - start_o[0]) * (down_o[0] - start_o[0]) + (down_o[1] - start_o[1]) * (down_o[1] - start_o[1]); up_dist = (up[0] - start_o[0]) * (up[0] - start_o[0]) + (up[1] - start_o[1]) * (up[1] - start_o[1]); if ((down_dist > up_dist) || (trace.plane.normal[2] < MIN_STEP_NORMAL)) { VectorCopy(down_o, pml.origin); VectorCopy(down_v, pml.velocity); return; } pml.velocity[2] = down_v[2]; } /* * Handles both ground friction and water friction */ void PM_Friction(void) { float *vel; float speed, newspeed, control; float friction; float drop; vel = pml.velocity; speed = (float)sqrt(vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2]); if (speed < 1) { vel[0] = 0; vel[1] = 0; return; } drop = 0; /* apply ground friction */ if ((pm->groundentity && pml.groundsurface && !(pml.groundsurface->flags & SURF_SLICK)) || (pml.ladder)) { friction = pm_friction; control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control * friction * pml.frametime; } /* apply water friction */ if (pm->waterlevel && !pml.ladder) { drop += speed * pm_waterfriction * pm->waterlevel * pml.frametime; } /* scale the velocity */ newspeed = speed - drop; if (newspeed < 0) { newspeed = 0; } newspeed /= speed; vel[0] = vel[0] * newspeed; vel[1] = vel[1] * newspeed; vel[2] = vel[2] * newspeed; } /* * Handles user intended acceleration */ void PM_Accelerate(vec3_t wishdir, float wishspeed, float accel) { int i; float addspeed, accelspeed, currentspeed; currentspeed = DotProduct(pml.velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) { return; } accelspeed = accel * pml.frametime * wishspeed; if (accelspeed > addspeed) { accelspeed = addspeed; } for (i = 0; i < 3; i++) { pml.velocity[i] += accelspeed * wishdir[i]; } } void PM_AirAccelerate(vec3_t wishdir, float wishspeed, float accel) { int i; float addspeed, accelspeed, currentspeed, wishspd = wishspeed; if (wishspd > 30) { wishspd = 30; } currentspeed = DotProduct(pml.velocity, wishdir); addspeed = wishspd - currentspeed; if (addspeed <= 0) { return; } accelspeed = accel * wishspeed * pml.frametime; if (accelspeed > addspeed) { accelspeed = addspeed; } for (i = 0; i < 3; i++) { pml.velocity[i] += accelspeed * wishdir[i]; } } void PM_AddCurrents(vec3_t wishvel) { vec3_t v; float s; /* account for ladders */ if (pml.ladder && (fabs(pml.velocity[2]) <= 200)) { if ((pm->viewangles[PITCH] <= -15) && (pm->cmd.forwardmove > 0)) { wishvel[2] = 200; } else if ((pm->viewangles[PITCH] >= 15) && (pm->cmd.forwardmove > 0)) { wishvel[2] = -200; } else if (pm->cmd.upmove > 0) { wishvel[2] = 200; } else if (pm->cmd.upmove < 0) { wishvel[2] = -200; } else { wishvel[2] = 0; } /* limit horizontal speed when on a ladder */ if (wishvel[0] < -25) { wishvel[0] = -25; } else if (wishvel[0] > 25) { wishvel[0] = 25; } if (wishvel[1] < -25) { wishvel[1] = -25; } else if (wishvel[1] > 25) { wishvel[1] = 25; } } /* add water currents */ if (pm->watertype & MASK_CURRENT) { VectorClear(v); if (pm->watertype & CONTENTS_CURRENT_0) { v[0] += 1; } if (pm->watertype & CONTENTS_CURRENT_90) { v[1] += 1; } if (pm->watertype & CONTENTS_CURRENT_180) { v[0] -= 1; } if (pm->watertype & CONTENTS_CURRENT_270) { v[1] -= 1; } if (pm->watertype & CONTENTS_CURRENT_UP) { v[2] += 1; } if (pm->watertype & CONTENTS_CURRENT_DOWN) { v[2] -= 1; } s = pm_waterspeed; if ((pm->waterlevel == 1) && (pm->groundentity)) { s /= 2; } VectorMA(wishvel, s, v, wishvel); } /* add conveyor belt velocities */ if (pm->groundentity) { VectorClear(v); if (pml.groundcontents & CONTENTS_CURRENT_0) { v[0] += 1; } if (pml.groundcontents & CONTENTS_CURRENT_90) { v[1] += 1; } if (pml.groundcontents & CONTENTS_CURRENT_180) { v[0] -= 1; } if (pml.groundcontents & CONTENTS_CURRENT_270) { v[1] -= 1; } if (pml.groundcontents & CONTENTS_CURRENT_UP) { v[2] += 1; } if (pml.groundcontents & CONTENTS_CURRENT_DOWN) { v[2] -= 1; } VectorMA(wishvel, 100, v, wishvel); } } void PM_WaterMove(void) { int i; vec3_t wishvel; float wishspeed; vec3_t wishdir; /* user intentions */ for (i = 0; i < 3; i++) { wishvel[i] = pml.forward[i] * pm->cmd.forwardmove + pml.right[i] * pm->cmd.sidemove; } if (!pm->cmd.forwardmove && !pm->cmd.sidemove && !pm->cmd.upmove) { wishvel[2] -= 60; /* drift towards bottom */ } else { wishvel[2] += pm->cmd.upmove; } PM_AddCurrents(wishvel); VectorCopy(wishvel, wishdir); wishspeed = VectorNormalize(wishdir); if (wishspeed > pm_maxspeed) { VectorScale(wishvel, pm_maxspeed / wishspeed, wishvel); wishspeed = pm_maxspeed; } wishspeed *= 0.5; PM_Accelerate(wishdir, wishspeed, pm_wateraccelerate); PM_StepSlideMove(); } void PM_AirMove(void) { int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; float maxspeed; fmove = pm->cmd.forwardmove; smove = pm->cmd.sidemove; for (i = 0; i < 2; i++) { wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; } wishvel[2] = 0; PM_AddCurrents(wishvel); VectorCopy(wishvel, wishdir); wishspeed = VectorNormalize(wishdir); /* clamp to server defined max speed */ maxspeed = (pm->s.pm_flags & PMF_DUCKED) ? pm_duckspeed : pm_maxspeed; if (wishspeed > maxspeed) { VectorScale(wishvel, maxspeed / wishspeed, wishvel); wishspeed = maxspeed; } if (pml.ladder) { PM_Accelerate(wishdir, wishspeed, pm_accelerate); if (!wishvel[2]) { if (pml.velocity[2] > 0) { pml.velocity[2] -= pm->s.gravity * pml.frametime; if (pml.velocity[2] < 0) { pml.velocity[2] = 0; } } else { pml.velocity[2] += pm->s.gravity * pml.frametime; if (pml.velocity[2] > 0) { pml.velocity[2] = 0; } } } PM_StepSlideMove(); } else if (pm->groundentity) { /* walking on ground */ pml.velocity[2] = 0; PM_Accelerate(wishdir, wishspeed, pm_accelerate); if (pm->s.gravity > 0) { pml.velocity[2] = 0; } else { pml.velocity[2] -= pm->s.gravity * pml.frametime; } if (!pml.velocity[0] && !pml.velocity[1]) { return; } PM_StepSlideMove(); } else { /* not on ground, so little effect on velocity */ if (pm_airaccelerate) { PM_AirAccelerate(wishdir, wishspeed, pm_accelerate); } else { PM_Accelerate(wishdir, wishspeed, 1); } /* add gravity */ pml.velocity[2] -= pm->s.gravity * pml.frametime; PM_StepSlideMove(); } } void PM_CatagorizePosition(void) { vec3_t point; int cont; trace_t trace; float sample1; float sample2; /* if the player hull point one unit down is solid, the player is on ground */ /* see if standing on something solid */ point[0] = pml.origin[0]; point[1] = pml.origin[1]; point[2] = pml.origin[2] - 0.25f; if (pml.velocity[2] > 180) { pm->s.pm_flags &= ~PMF_ON_GROUND; pm->groundentity = NULL; } else { trace = pm->trace(pml.origin, pm->mins, pm->maxs, point); pml.groundplane = trace.plane; pml.groundsurface = trace.surface; pml.groundcontents = trace.contents; if (!trace.ent || ((trace.plane.normal[2] < 0.7) && !trace.startsolid)) { pm->groundentity = NULL; pm->s.pm_flags &= ~PMF_ON_GROUND; } else { pm->groundentity = trace.ent; /* hitting solid ground will end a waterjump */ if (pm->s.pm_flags & PMF_TIME_WATERJUMP) { pm->s.pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT); pm->s.pm_time = 0; } if (!(pm->s.pm_flags & PMF_ON_GROUND)) { /* just hit the ground */ pm->s.pm_flags |= PMF_ON_GROUND; /* don't do landing time if we were just going down a slope */ if (pml.velocity[2] < -200) { pm->s.pm_flags |= PMF_TIME_LAND; /* don't allow another jump for a little while */ if (pml.velocity[2] < -400) { pm->s.pm_time = 25; } else { pm->s.pm_time = 18; } } } } if ((pm->numtouch < MAXTOUCH) && trace.ent) { pm->touchents[pm->numtouch] = trace.ent; pm->numtouch++; } } /* get waterlevel, accounting for ducking */ pm->waterlevel = 0; pm->watertype = 0; sample2 = pm->viewheight - pm->mins[2]; sample1 = sample2 / 2; point[2] = pml.origin[2] + pm->mins[2] + 1; cont = pm->pointcontents(point); if (cont & MASK_WATER) { pm->watertype = cont; pm->waterlevel = 1; point[2] = pml.origin[2] + pm->mins[2] + sample1; cont = pm->pointcontents(point); if (cont & MASK_WATER) { pm->waterlevel = 2; point[2] = pml.origin[2] + pm->mins[2] + sample2; cont = pm->pointcontents(point); if (cont & MASK_WATER) { pm->waterlevel = 3; } } } } void PM_CheckJump(void) { if (pm->s.pm_flags & PMF_TIME_LAND) { /* hasn't been long enough since landing to jump again */ return; } if (pm->cmd.upmove < 10) { /* not holding jump */ pm->s.pm_flags &= ~PMF_JUMP_HELD; return; } /* must wait for jump to be released */ if (pm->s.pm_flags & PMF_JUMP_HELD) { return; } if (pm->s.pm_type == PM_DEAD) { return; } if (pm->waterlevel >= 2) { /* swimming, not jumping */ pm->groundentity = NULL; if (pml.velocity[2] <= -300) { return; } if (pm->watertype == CONTENTS_WATER) { pml.velocity[2] = 100; } else if (pm->watertype == CONTENTS_SLIME) { pml.velocity[2] = 80; } else { pml.velocity[2] = 50; } return; } if (pm->groundentity == NULL) { return; /* in air, so no effect */ } pm->s.pm_flags |= PMF_JUMP_HELD; pm->groundentity = NULL; pml.velocity[2] += 270; if (pml.velocity[2] < 270) { pml.velocity[2] = 270; } } void PM_CheckSpecialMovement(void) { vec3_t spot; int cont; vec3_t flatforward; trace_t trace; if (pm->s.pm_time) { return; } pml.ladder = false; /* check for ladder */ flatforward[0] = pml.forward[0]; flatforward[1] = pml.forward[1]; flatforward[2] = 0; VectorNormalize(flatforward); VectorMA(pml.origin, 1, flatforward, spot); trace = pm->trace(pml.origin, pm->mins, pm->maxs, spot); if ((trace.fraction < 1) && (trace.contents & CONTENTS_LADDER)) { pml.ladder = true; } /* check for water jump */ if (pm->waterlevel != 2) { return; } VectorMA(pml.origin, 30, flatforward, spot); spot[2] += 4; cont = pm->pointcontents(spot); if (!(cont & CONTENTS_SOLID)) { return; } spot[2] += 16; cont = pm->pointcontents(spot); if (cont) { return; } /* jump out of water */ VectorScale(flatforward, 50, pml.velocity); pml.velocity[2] = 350; pm->s.pm_flags |= PMF_TIME_WATERJUMP; pm->s.pm_time = 255; } void PM_FlyMove(qboolean doclip) { float speed, drop, friction, control, newspeed; float currentspeed, addspeed, accelspeed; int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; vec3_t end; trace_t trace; pm->viewheight = 22; /* friction */ speed = VectorLength(pml.velocity); if (speed < 1) { VectorCopy(vec3_origin, pml.velocity); } else { drop = 0; friction = pm_friction * 1.5f; /* extra friction */ control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control * friction * pml.frametime; /* scale the velocity */ newspeed = speed - drop; if (newspeed < 0) { newspeed = 0; } newspeed /= speed; VectorScale(pml.velocity, newspeed, pml.velocity); } /* accelerate */ fmove = pm->cmd.forwardmove; smove = pm->cmd.sidemove; VectorNormalize(pml.forward); VectorNormalize(pml.right); for (i = 0; i < 3; i++) { wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; } wishvel[2] += pm->cmd.upmove; VectorCopy(wishvel, wishdir); wishspeed = VectorNormalize(wishdir); /* clamp to server defined max speed */ if (wishspeed > pm_maxspeed) { VectorScale(wishvel, pm_maxspeed / wishspeed, wishvel); wishspeed = pm_maxspeed; } currentspeed = DotProduct(pml.velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) { return; } accelspeed = pm_accelerate * pml.frametime * wishspeed; if (accelspeed > addspeed) { accelspeed = addspeed; } for (i = 0; i < 3; i++) { pml.velocity[i] += accelspeed * wishdir[i]; } if (doclip) { for (i = 0; i < 3; i++) { end[i] = pml.origin[i] + pml.frametime * pml.velocity[i]; } trace = pm->trace(pml.origin, pm->mins, pm->maxs, end); VectorCopy(trace.endpos, pml.origin); } else { /* move */ VectorMA(pml.origin, pml.frametime, pml.velocity, pml.origin); } } /* * Sets mins, maxs, and pm->viewheight */ void PM_CheckDuck(void) { trace_t trace; pm->mins[0] = -16; pm->mins[1] = -16; pm->maxs[0] = 16; pm->maxs[1] = 16; if (pm->s.pm_type == PM_GIB) { pm->mins[2] = 0; pm->maxs[2] = 16; pm->viewheight = 8; return; } pm->mins[2] = -24; if (pm->s.pm_type == PM_DEAD) { pm->s.pm_flags |= PMF_DUCKED; } else if ((pm->cmd.upmove < 0) && (pm->s.pm_flags & PMF_ON_GROUND)) { /* duck */ pm->s.pm_flags |= PMF_DUCKED; } else { /* stand up if possible */ if (pm->s.pm_flags & PMF_DUCKED) { /* try to stand up */ pm->maxs[2] = 32; trace = pm->trace(pml.origin, pm->mins, pm->maxs, pml.origin); if (!trace.allsolid) { pm->s.pm_flags &= ~PMF_DUCKED; } } } if (pm->s.pm_flags & PMF_DUCKED) { pm->maxs[2] = 4; pm->viewheight = -2; } else { pm->maxs[2] = 32; pm->viewheight = 22; } } void PM_DeadMove(void) { float forward; if (!pm->groundentity) { return; } /* extra friction */ forward = VectorLength(pml.velocity); forward -= 20; if (forward <= 0) { VectorClear(pml.velocity); } else { VectorNormalize(pml.velocity); VectorScale(pml.velocity, forward, pml.velocity); } } qboolean PM_GoodPosition(void) { trace_t trace; vec3_t origin, end; int i; if (pm->s.pm_type == PM_SPECTATOR) { return true; } for (i = 0; i < 3; i++) { origin[i] = end[i] = pm->s.origin[i] * 0.125f; } trace = pm->trace(origin, pm->mins, pm->maxs, end); return !trace.allsolid; } /* * On exit, the origin will have a value that is pre-quantized to the 0.125 * precision of the network channel and in a valid position. */ void PM_SnapPosition(void) { int sign[3]; int i, j, bits; short base[3]; /* try all single bits first */ static int jitterbits[8] = {0, 4, 1, 2, 3, 5, 6, 7}; /* snap velocity to eigths */ for (i = 0; i < 3; i++) { pm->s.velocity[i] = (int)(pml.velocity[i] * 8); } for (i = 0; i < 3; i++) { if (pml.origin[i] >= 0) { sign[i] = 1; } else { sign[i] = -1; } pm->s.origin[i] = (int)(pml.origin[i] * 8); if (pm->s.origin[i] * 0.125f == pml.origin[i]) { sign[i] = 0; } } VectorCopy(pm->s.origin, base); /* try all combinations */ for (j = 0; j < 8; j++) { bits = jitterbits[j]; VectorCopy(base, pm->s.origin); for (i = 0; i < 3; i++) { if (bits & (1 << i)) { pm->s.origin[i] += sign[i]; } } if (PM_GoodPosition()) { return; } } /* go back to the last position */ VectorCopy(pml.previous_origin, pm->s.origin); } void PM_InitialSnapPosition(void) { int x, y, z; short base[3]; static int offset[3] = {0, -1, 1}; VectorCopy(pm->s.origin, base); for (z = 0; z < 3; z++) { pm->s.origin[2] = base[2] + offset[z]; for (y = 0; y < 3; y++) { pm->s.origin[1] = base[1] + offset[y]; for (x = 0; x < 3; x++) { pm->s.origin[0] = base[0] + offset[x]; if (PM_GoodPosition()) { pml.origin[0] = pm->s.origin[0] * 0.125f; pml.origin[1] = pm->s.origin[1] * 0.125f; pml.origin[2] = pm->s.origin[2] * 0.125f; VectorCopy(pm->s.origin, pml.previous_origin); return; } } } } Com_DPrintf("Bad InitialSnapPosition\n"); } void PM_ClampAngles(void) { short temp; int i; if (pm->s.pm_flags & PMF_TIME_TELEPORT) { pm->viewangles[YAW] = SHORT2ANGLE( pm->cmd.angles[YAW] + pm->s.delta_angles[YAW]); pm->viewangles[PITCH] = 0; pm->viewangles[ROLL] = 0; } else { /* circularly clamp the angles with deltas */ for (i = 0; i < 3; i++) { temp = pm->cmd.angles[i] + pm->s.delta_angles[i]; pm->viewangles[i] = SHORT2ANGLE(temp); } /* don't let the player look up or down more than 90 degrees */ if ((pm->viewangles[PITCH] > 89) && (pm->viewangles[PITCH] < 180)) { pm->viewangles[PITCH] = 89; } else if ((pm->viewangles[PITCH] < 271) && (pm->viewangles[PITCH] >= 180)) { pm->viewangles[PITCH] = 271; } } AngleVectors(pm->viewangles, pml.forward, pml.right, pml.up); } #if !defined(DEDICATED_ONLY) void PM_CalculateViewHeightForDemo() { if (pm->s.pm_type == PM_GIB) pm->viewheight = 8; else { if ((pm->s.pm_flags & PMF_DUCKED) != 0) pm->viewheight = -2; else pm->viewheight = 22; } } void PM_CalculateWaterLevelForDemo() { vec3_t point; int cont; point[0] = pml.origin[0]; point[1] = pml.origin[1]; point[2] = pml.origin[2] + pm->viewheight; pm->waterlevel = 0; pm->watertype = 0; cont = pm->pointcontents(point); if ((cont & MASK_WATER) != 0) { pm->waterlevel = 3; pm->watertype = cont; } } void PM_UpdateUnderwaterSfx() { static int underwater; if ((pm->waterlevel == 3) && !underwater) { underwater = 1; snd_is_underwater = 1; #ifdef USE_OPENAL if (snd_is_underwater_enabled) AL_Underwater(); #endif } if ((pm->waterlevel < 3) && underwater) { underwater = 0; snd_is_underwater = 0; #ifdef USE_OPENAL if (snd_is_underwater_enabled) AL_Overwater(); #endif } } #endif /* * Can be called by either the server or the client */ void Pmove(pmove_t *pmove) { pm = pmove; /* clear results */ pm->numtouch = 0; VectorClear(pm->viewangles); pm->viewheight = 0; pm->groundentity = 0; pm->watertype = 0; pm->waterlevel = 0; /* clear all pmove local vars */ memset(&pml, 0, sizeof(pml)); /* convert origin and velocity to float values */ pml.origin[0] = pm->s.origin[0] * 0.125f; pml.origin[1] = pm->s.origin[1] * 0.125f; pml.origin[2] = pm->s.origin[2] * 0.125f; pml.velocity[0] = pm->s.velocity[0] * 0.125f; pml.velocity[1] = pm->s.velocity[1] * 0.125f; pml.velocity[2] = pm->s.velocity[2] * 0.125f; /* save old org in case we get stuck */ VectorCopy(pm->s.origin, pml.previous_origin); pml.frametime = pm->cmd.msec * 0.001f; PM_ClampAngles(); if (pm->s.pm_type == PM_SPECTATOR) { PM_FlyMove(false); PM_SnapPosition(); return; } if (pm->s.pm_type >= PM_DEAD) { pm->cmd.forwardmove = 0; pm->cmd.sidemove = 0; pm->cmd.upmove = 0; } if (pm->s.pm_type == PM_FREEZE) { #if !defined(DEDICATED_ONLY) if (cl.attractloop) { PM_CalculateViewHeightForDemo(); PM_CalculateWaterLevelForDemo(); PM_UpdateUnderwaterSfx(); } #endif return; /* no movement at all */ } /* set mins, maxs, and viewheight */ PM_CheckDuck(); if (pm->snapinitial) { PM_InitialSnapPosition(); } /* set groundentity, watertype, and waterlevel */ PM_CatagorizePosition(); if (pm->s.pm_type == PM_DEAD) { PM_DeadMove(); } PM_CheckSpecialMovement(); /* drop timing counter */ if (pm->s.pm_time) { int msec; msec = pm->cmd.msec >> 3; if (!msec) { msec = 1; } if (msec >= pm->s.pm_time) { pm->s.pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT); pm->s.pm_time = 0; } else { pm->s.pm_time -= msec; } } if (pm->s.pm_flags & PMF_TIME_TELEPORT) { /* teleport pause stays exactly in place */ } else if (pm->s.pm_flags & PMF_TIME_WATERJUMP) { /* waterjump has no control, but falls */ pml.velocity[2] -= pm->s.gravity * pml.frametime; if (pml.velocity[2] < 0) { /* cancel as soon as we are falling down again */ pm->s.pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT); pm->s.pm_time = 0; } PM_StepSlideMove(); } else { PM_CheckJump(); PM_Friction(); if (pm->waterlevel >= 2) { PM_WaterMove(); } else { vec3_t angles; VectorCopy(pm->viewangles, angles); if (angles[PITCH] > 180) { angles[PITCH] = angles[PITCH] - 360; } angles[PITCH] /= 3; AngleVectors(angles, pml.forward, pml.right, pml.up); PM_AirMove(); } } /* set groundentity, watertype, and waterlevel for final spot */ PM_CatagorizePosition(); #if !defined(DEDICATED_ONLY) PM_UpdateUnderwaterSfx(); #endif PM_SnapPosition(); } yquake2-QUAKE2_5_32/src/common/filesystem.c0000644000175000017500000007637212615162034021245 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 Quake II file system, implements generic file system operations * as well as the .pak file format and support for .pk3 files. * * ======================================================================= */ #include "header/common.h" #include "../common/header/glob.h" #ifdef ZIP #include "unzip/unzip.h" #endif #define MAX_HANDLES 512 #define MAX_PAKS 100 #ifdef SYSTEMWIDE #ifndef SYSTEMDIR #define SYSTEMDIR "/usr/share/games/quake2" #endif #endif typedef struct { char name[MAX_QPATH]; fsMode_t mode; FILE *file; /* Only one will be used. */ #ifdef ZIP unzFile *zip; /* (file or zip) */ #endif } fsHandle_t; typedef struct fsLink_s { char *from; char *to; int length; struct fsLink_s *next; } fsLink_t; typedef struct { char name[MAX_QPATH]; int size; int offset; /* Ignored in PK3 files. */ } fsPackFile_t; typedef struct { char name[MAX_OSPATH]; int numFiles; FILE *pak; #ifdef ZIP unzFile *pk3; #endif fsPackFile_t *files; } fsPack_t; typedef struct fsSearchPath_s { char path[MAX_OSPATH]; /* Only one used. */ fsPack_t *pack; /* (path or pack) */ struct fsSearchPath_s *next; } fsSearchPath_t; typedef enum { PAK, #ifdef ZIP PK3 #endif } fsPackFormat_t; typedef struct { char *suffix; fsPackFormat_t format; } fsPackTypes_t; fsHandle_t fs_handles[MAX_HANDLES]; fsLink_t *fs_links; fsSearchPath_t *fs_searchPaths; fsSearchPath_t *fs_baseSearchPaths; /* Pack formats / suffixes. */ fsPackTypes_t fs_packtypes[] = { {"pak", PAK}, #ifdef ZIP {"pk2", PK3}, {"pk3", PK3}, {"zip", PK3} #endif }; char fs_gamedir[MAX_OSPATH]; static char fs_currentGame[MAX_QPATH]; static char fs_fileInPath[MAX_OSPATH]; static qboolean fs_fileInPack; /* Set by FS_FOpenFile. */ int file_from_pak = 0; #ifdef ZIP int file_from_pk3 = 0; char file_from_pk3_name[MAX_QPATH]; #endif cvar_t *fs_homepath; cvar_t *fs_basedir; cvar_t *fs_cddir; cvar_t *fs_gamedirvar; cvar_t *fs_debug; fsHandle_t *FS_GetFileByHandle(fileHandle_t f); char *Sys_GetCurrentDirectory(void); /* * 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. * */ /* * Returns the path up to, but not including the last '/'. */ void Com_FilePath(const char *path, char *dst, int dstSize) { char *pos; /* Position of the last '/'. */ if ((pos = strrchr(path, '/')) != NULL) { pos--; if ((pos - path) < dstSize) { memcpy(dst, path, pos - path); dst[pos - path] = '\0'; } else { Com_Printf("Com_FilePath: not enough space.\n"); return; } } else { Q_strlcpy(dst, path, dstSize); } } int FS_FileLength(FILE *f) { int pos; /* Current position. */ int end; /* End of file. */ pos = ftell(f); fseek(f, 0, SEEK_END); end = ftell(f); fseek(f, pos, SEEK_SET); return end; } /* * Creates any directories needed to store the given filename. */ void FS_CreatePath(char *path) { char *cur; /* Current '/'. */ char *old; /* Old '/'. */ FS_DPrintf("FS_CreatePath(%s)\n", path); if (strstr(path, "..") != NULL) { Com_Printf("WARNING: refusing to create relative path '%s'.\n", path); return; } cur = old = path; while (cur != NULL) { if ((cur - old) > 1) { *cur = '\0'; Sys_Mkdir(path); *cur = '/'; } old = cur; cur = strchr(old + 1, '/'); } } void FS_DPrintf(const char *format, ...) { char msg[1024]; va_list argPtr; if (fs_debug->value != 1) { return; } va_start(argPtr, format); vsnprintf(msg, sizeof(msg), format, argPtr); va_end(argPtr); Com_Printf("%s", msg); } char * FS_Gamedir(void) { return fs_gamedir; } /* * Finds a free fileHandle_t. */ fsHandle_t * FS_HandleForFile(const char *path, fileHandle_t *f) { int i; fsHandle_t *handle; handle = fs_handles; for (i = 0; i < MAX_HANDLES; i++, handle++) { if ((handle->file == NULL) #ifdef ZIP && (handle->zip == NULL) #endif ) { Q_strlcpy(handle->name, path, sizeof(handle->name)); *f = i + 1; return handle; } } /* Failed. */ Com_Error(ERR_DROP, "FS_HandleForFile: none free"); return NULL; } /* * Returns a fsHandle_t * for the given fileHandle_t. */ fsHandle_t * FS_GetFileByHandle(fileHandle_t f) { if ((f < 0) || (f > MAX_HANDLES)) { Com_Error(ERR_DROP, "FS_GetFileByHandle: out of range"); } if (f == 0) { f++; } return &fs_handles[f - 1]; } /* * Returns file size or -1 on error. */ int FS_FOpenFileAppend(fsHandle_t *handle) { char path[MAX_OSPATH]; FS_CreatePath(handle->name); Com_sprintf(path, sizeof(path), "%s/%s", fs_gamedir, handle->name); handle->file = fopen(path, "ab"); if (handle->file) { if (fs_debug->value) { Com_Printf("FS_FOpenFileAppend: '%s'.\n", path); } return FS_FileLength(handle->file); } if (fs_debug->value) { Com_Printf("FS_FOpenFileAppend: couldn't open '%s'.\n", path); } return -1; } /* * Always returns 0 or -1 on error. */ int FS_FOpenFileWrite(fsHandle_t *handle) { char path[MAX_OSPATH]; FS_CreatePath(handle->name); Com_sprintf(path, sizeof(path), "%s/%s", fs_gamedir, handle->name); if ((handle->file = fopen(path, "wb")) != NULL) { if (fs_debug->value) { Com_Printf("FS_FOpenFileWrite: '%s'.\n", path); } return 0; } if (fs_debug->value) { Com_Printf("FS_FOpenFileWrite: couldn't open '%s'.\n", path); } return -1; } /* * Other dll's can't just call fclose() on files returned by FS_FOpenFile. */ void FS_FCloseFile(fileHandle_t f) { fsHandle_t *handle; handle = FS_GetFileByHandle(f); if (handle->file) { fclose(handle->file); } #ifdef ZIP else if (handle->zip) { unzCloseCurrentFile(handle->zip); unzClose(handle->zip); } #endif memset(handle, 0, sizeof(*handle)); } int Developer_searchpath(int who) { fsSearchPath_t *search; for (search = fs_searchPaths; search; search = search->next) { if (strstr(search->path, "xatrix")) { return 1; } if (strstr(search->path, "rogue")) { return 2; } } return 0; } /* * Finds the file in the search path. Returns filesize and an open FILE *. Used * for streaming data out of either a pak file or a seperate file. */ int FS_FOpenFile(const char *name, fileHandle_t *f, qboolean gamedir_only) { char path[MAX_OSPATH]; fsHandle_t *handle; fsPack_t *pack; fsSearchPath_t *search; int i; file_from_pak = 0; #ifdef ZIP file_from_pk3 = 0; #endif handle = FS_HandleForFile(name, f); Q_strlcpy(handle->name, name, sizeof(handle->name)); handle->mode = FS_READ; /* Search through the path, one element at a time. */ for (search = fs_searchPaths; search; search = search->next) { if (gamedir_only) { if (strstr(search->path, FS_Gamedir()) == NULL) { continue; } } /* Search inside a pack file. */ if (search->pack) { pack = search->pack; for (i = 0; i < pack->numFiles; i++) { if (Q_stricmp(pack->files[i].name, handle->name) == 0) { /* Found it! */ Com_FilePath(pack->name, fs_fileInPath, sizeof(fs_fileInPath)); fs_fileInPack = true; if (fs_debug->value) { Com_Printf("FS_FOpenFile: '%s' (found in '%s').\n", handle->name, pack->name); } if (pack->pak) { /* PAK */ file_from_pak = 1; handle->file = fopen(pack->name, "rb"); if (handle->file) { fseek(handle->file, pack->files[i].offset, SEEK_SET); return pack->files[i].size; } } #ifdef ZIP else if (pack->pk3) { /* PK3 */ file_from_pk3 = 1; Q_strlcpy(file_from_pk3_name, strrchr(pack->name, '/') + 1, sizeof(file_from_pk3_name)); handle->zip = unzOpen(pack->name); if (handle->zip) { if (unzLocateFile(handle->zip, handle->name, 2) == UNZ_OK) { if (unzOpenCurrentFile(handle->zip) == UNZ_OK) { return pack->files[i].size; } } unzClose(handle->zip); } } #endif Com_Error(ERR_FATAL, "Couldn't reopen '%s'", pack->name); } } } else { /* Search in a directory tree. */ Com_sprintf(path, sizeof(path), "%s/%s", search->path, handle->name); handle->file = fopen(path, "rb"); if (!handle->file) { Q_strlwr(path); handle->file = fopen(path, "rb"); } if (!handle->file) { continue; } if (handle->file) { /* Found it! */ Q_strlcpy(fs_fileInPath, search->path, sizeof(fs_fileInPath)); fs_fileInPack = false; if (fs_debug->value) { Com_Printf("FS_FOpenFile: '%s' (found in '%s').\n", handle->name, search->path); } return FS_FileLength(handle->file); } } } /* Not found! */ fs_fileInPath[0] = 0; fs_fileInPack = false; if (fs_debug->value) { Com_Printf("FS_FOpenFile: couldn't find '%s'.\n", handle->name); } /* Couldn't open, so free the handle. */ memset(handle, 0, sizeof(*handle)); *f = 0; return -1; } /* * Properly handles partial reads. */ int FS_Read(void *buffer, int size, fileHandle_t f) { qboolean tried = false; /* Tried to read from a CD. */ byte *buf; /* Buffer. */ int r; /* Number of bytes read. */ int remaining; /* Remaining bytes. */ fsHandle_t *handle; /* File handle. */ handle = FS_GetFileByHandle(f); /* Read. */ remaining = size; buf = (byte *)buffer; while (remaining) { if (handle->file) { r = fread(buf, 1, remaining, handle->file); } #ifdef ZIP else if (handle->zip) { r = unzReadCurrentFile(handle->zip, buf, remaining); } #endif else { return 0; } if (r == 0) { if (!tried) { /* Might tried to read from a CD. */ tried = true; } else { /* Already tried once. */ Com_Error(ERR_FATAL, va("FS_Read: 0 bytes read from '%s'", handle->name)); return size - remaining; } } else if (r == -1) { Com_Error(ERR_FATAL, "FS_Read: -1 bytes read from '%s'", handle->name); } remaining -= r; buf += r; } return size; } /* * Properly handles partial reads of size up to count times. No error if it * can't read. */ int FS_FRead(void *buffer, int size, int count, fileHandle_t f) { qboolean tried = false; /* Tried to read from a CD. */ byte *buf; /* Buffer. */ int loops; /* Loop indicator. */ int r; /* Number of bytes read. */ int remaining; /* Remaining bytes. */ fsHandle_t *handle; /* File handle. */ handle = FS_GetFileByHandle(f); /* Read. */ loops = count; buf = (byte *)buffer; while (loops) { /* Read in chunks. */ remaining = size; while (remaining) { if (handle->file) { r = fread(buf, 1, remaining, handle->file); } #ifdef ZIP else if (handle->zip) { r = unzReadCurrentFile(handle->zip, buf, remaining); } #endif else { return 0; } if (r == 0) { if (!tried) { /* Might tried to read from a CD. */ tried = true; } else { /* Already tried once. */ return size - remaining; } } else if (r == -1) { Com_Error(ERR_FATAL, "FS_FRead: -1 bytes read from '%s'", handle->name); } remaining -= r; buf += r; } loops--; } return size; } /* * Filename are reletive to the quake search path. A null buffer will just * return the file length without loading. */ int FS_LoadFile(char *path, void **buffer) { byte *buf; /* Buffer. */ int size; /* File size. */ fileHandle_t f; /* File handle. */ buf = NULL; size = FS_FOpenFile(path, &f, false); if (size <= 0) { if (buffer) { *buffer = NULL; } return size; } if (buffer == NULL) { FS_FCloseFile(f); return size; } buf = Z_Malloc(size); *buffer = buf; FS_Read(buf, size, f); FS_FCloseFile(f); return size; } void FS_FreeFile(void *buffer) { if (buffer == NULL) { FS_DPrintf("FS_FreeFile: NULL buffer.\n"); return; } Z_Free(buffer); } /* * 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. */ fsPack_t * FS_LoadPAK(const char *packPath) { int i; /* Loop counter. */ int numFiles; /* Number of files in PAK. */ FILE *handle; /* File handle. */ fsPackFile_t *files; /* List of files in PAK. */ fsPack_t *pack; /* PAK file. */ dpackheader_t header; /* PAK file header. */ dpackfile_t info[MAX_FILES_IN_PACK]; /* PAK info. */ handle = fopen(packPath, "rb"); if (handle == NULL) { return NULL; } fread(&header, 1, sizeof(dpackheader_t), handle); if (LittleLong(header.ident) != IDPAKHEADER) { fclose(handle); Com_Error(ERR_FATAL, "FS_LoadPAK: '%s' is not a pack file", packPath); } header.dirofs = LittleLong(header.dirofs); header.dirlen = LittleLong(header.dirlen); numFiles = header.dirlen / sizeof(dpackfile_t); if ((numFiles > MAX_FILES_IN_PACK) || (numFiles == 0)) { fclose(handle); Com_Error(ERR_FATAL, "FS_LoadPAK: '%s' has %i files", packPath, numFiles); } files = Z_Malloc(numFiles * sizeof(fsPackFile_t)); fseek(handle, header.dirofs, SEEK_SET); fread(info, 1, header.dirlen, handle); /* Parse the directory. */ for (i = 0; i < numFiles; i++) { Q_strlcpy(files[i].name, info[i].name, sizeof(files[i].name)); files[i].offset = LittleLong(info[i].filepos); files[i].size = LittleLong(info[i].filelen); } pack = Z_Malloc(sizeof(fsPack_t)); Q_strlcpy(pack->name, packPath, sizeof(pack->name)); pack->pak = handle; #ifdef ZIP pack->pk3 = NULL; #endif pack->numFiles = numFiles; pack->files = files; Com_Printf("Added packfile '%s' (%i files).\n", pack, numFiles); return pack; } #ifdef ZIP /* * Takes an explicit (not game tree related) path to a pack file. * * Loads the header and directory, adding the files at the beginning of the list * so they override previous pack files. */ fsPack_t * FS_LoadPK3(const char *packPath) { char fileName[MAX_QPATH]; /* File name. */ int i = 0; /* Loop counter. */ int numFiles; /* Number of files in PK3. */ int status; /* Error indicator. */ fsPackFile_t *files; /* List of files in PK3. */ fsPack_t *pack; /* PK3 file. */ unzFile *handle; /* Zip file handle. */ unz_file_info info; /* Zip file info. */ unz_global_info global; /* Zip file global info. */ handle = unzOpen(packPath); if (handle == NULL) { return NULL; } if (unzGetGlobalInfo(handle, &global) != UNZ_OK) { unzClose(handle); Com_Error(ERR_FATAL, "FS_LoadPK3: '%s' is not a pack file", packPath); } numFiles = global.number_entry; if ((numFiles > MAX_FILES_IN_PACK) || (numFiles == 0)) { unzClose(handle); Com_Error(ERR_FATAL, "FS_LoadPK3: '%s' has %i files", packPath, numFiles); } files = Z_Malloc(numFiles * sizeof(fsPackFile_t)); /* Parse the directory. */ status = unzGoToFirstFile(handle); while (status == UNZ_OK) { fileName[0] = '\0'; unzGetCurrentFileInfo(handle, &info, fileName, MAX_QPATH, NULL, 0, NULL, 0); Q_strlcpy(files[i].name, fileName, sizeof(files[i].name)); files[i].offset = -1; /* Not used in ZIP files */ files[i].size = info.uncompressed_size; i++; status = unzGoToNextFile(handle); } pack = Z_Malloc(sizeof(fsPack_t)); Q_strlcpy(pack->name, packPath, sizeof(pack->name)); pack->pak = NULL; pack->pk3 = handle; pack->numFiles = numFiles; pack->files = files; Com_Printf("Added packfile '%s' (%i files).\n", pack, numFiles); return pack; } #endif /* * Adds the directory to the head of the path, then loads and adds pak1.pak * pak2.pak ... * * Extended all functionality to include Quake III .pk3 */ void FS_AddGameDirectory(const char *dir) { char **list; /* File list. */ char path[MAX_OSPATH]; /* Path to PAK / PK3. */ int i, j; /* Loop counters. */ int nfiles; /* Number of files in list. */ fsSearchPath_t *search; /* Search path. */ fsPack_t *pack; /* PAK / PK3 file. */ pack = NULL; /* Set game directory. */ Q_strlcpy(fs_gamedir, dir, sizeof(fs_gamedir)); /* Create directory if it does not exist. */ FS_CreatePath(fs_gamedir); /* Add the directory to the search path. */ search = Z_Malloc(sizeof(fsSearchPath_t)); Q_strlcpy(search->path, dir, sizeof(search->path)); search->next = fs_searchPaths; fs_searchPaths = search; /* Add numbered pack files in sequence. */ for (i = 0; i < sizeof(fs_packtypes) / sizeof(fs_packtypes[0]); i++) { for (j = 0; j < MAX_PAKS; j++) { Com_sprintf(path, sizeof(path), "%s/pak%d.%s", dir, j, fs_packtypes[i].suffix); switch (fs_packtypes[i].format) { case PAK: pack = FS_LoadPAK(path); break; #ifdef ZIP case PK3: pack = FS_LoadPK3(path); break; #endif } if (pack == NULL) { continue; } search = Z_Malloc(sizeof(fsSearchPath_t)); search->pack = pack; search->next = fs_searchPaths; fs_searchPaths = search; } } /* Add not numbered pack files. */ for (i = 0; i < sizeof(fs_packtypes) / sizeof(fs_packtypes[0]); i++) { Com_sprintf(path, sizeof(path), "%s/*.%s", dir, fs_packtypes[i].suffix); if ((list = FS_ListFiles(path, &nfiles, 0, SFF_SUBDIR)) == NULL) { continue; } Com_sprintf(path, sizeof(path), "%s/pak*.%s", dir, fs_packtypes[i].suffix); for (j = 0; j < nfiles - 1; j++) { /* Skip all packs starting with "pak" */ if (glob_match(path, list[j])) { continue; } switch (fs_packtypes[i].format) { case PAK: pack = FS_LoadPAK(list[j]); break; #ifdef ZIP case PK3: pack = FS_LoadPK3(list[j]); break; #endif } if (pack == NULL) { continue; } search = Z_Malloc(sizeof(fsSearchPath_t)); search->pack = pack; search->next = fs_searchPaths; fs_searchPaths = search; } FS_FreeList(list, nfiles); } } /* * Use ~/.quake2/dir as fs_gamedir. */ void FS_AddHomeAsGameDirectory(char *dir) { char *home; char gdir[MAX_OSPATH]; size_t len; home = Sys_GetHomeDir(); if (home == NULL) { return; } len = snprintf(gdir, sizeof(gdir), "%s%s/", home, dir); FS_CreatePath(gdir); if ((len > 0) && (len < sizeof(gdir)) && (gdir[len - 1] == '/')) { gdir[len - 1] = 0; } Q_strlcpy(fs_gamedir, gdir, sizeof(fs_gamedir)); FS_AddGameDirectory(gdir); } void FS_AddBinaryDirAsGameDirectory(const char* dir) { char gdir[MAX_OSPATH]; const char *datadir = Sys_GetBinaryDir(); if(datadir[0] == '\0') { return; } int len = snprintf(gdir, sizeof(gdir), "%s%s/", datadir, dir); printf("Using binary dir %s to fetch paks\n", gdir); if ((len > 0) && (len < sizeof(gdir)) && (gdir[len - 1] == '/')) { gdir[len - 1] = 0; } FS_AddGameDirectory(gdir); } /* * Allows enumerating all of the directories in the search path. */ char * FS_NextPath(char *prevPath) { char *prev; fsSearchPath_t *search; if (prevPath == NULL) { return fs_gamedir; } prev = fs_gamedir; for (search = fs_searchPaths; search; search = search->next) { if (search->pack != NULL) { continue; } if (prevPath == prev) { return search->path; } prev = search->path; } return NULL; } void FS_Path_f(void) { int i; int totalFiles = 0; fsSearchPath_t *search; fsHandle_t *handle; fsLink_t *link; Com_Printf("Current search path:\n"); for (search = fs_searchPaths; search; search = search->next) { if (search->pack != NULL) { Com_Printf("%s (%i files)\n", search->pack->name, search->pack->numFiles); totalFiles += search->pack->numFiles; } else { Com_Printf("%s\n", search->path); } } Com_Printf("\n"); for (i = 0, handle = fs_handles; i < MAX_HANDLES; i++, handle++) { if ((handle->file != NULL) #ifdef ZIP || (handle->zip != NULL) #endif ) { Com_Printf("Handle %i: '%s'.\n", i + 1, handle->name); } } for (i = 0, link = fs_links; link; i++, link = link->next) { Com_Printf("Link %i: '%s' -> '%s'.\n", i, link->from, link->to); } Com_Printf("----------------------\n"); #ifdef ZIP Com_Printf("%i files in PAK/PK2/PK3/ZIP files.\n", totalFiles); #else Com_Printf("%i files in PAK/PK2 files.\n", totalFiles); #endif } void FS_ExecAutoexec(void) { char *dir; char name[MAX_QPATH]; dir = (char *)Cvar_VariableString("gamedir"); if (dir[0] != '\0') { Com_sprintf(name, sizeof(name), "%s/%s/autoexec.cfg", fs_basedir->string, dir); } else { Com_sprintf(name, sizeof(name), "%s/%s/autoexec.cfg", fs_basedir->string, BASEDIRNAME); } if (Sys_FindFirst(name, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM) != NULL) { Cbuf_AddText("exec autoexec.cfg\n"); } Sys_FindClose(); } /* * Sets the gamedir and path to a different directory. */ void FS_SetGamedir(char *dir) { int i; fsSearchPath_t *next; if (!*dir || !strcmp(dir, ".") || strstr(dir, "..") || strstr(dir, "/")) { Com_Printf("Gamedir should be a single filename, not a path.\n"); return; } /* Free up any current game dir info. */ while (fs_searchPaths != fs_baseSearchPaths) { if (fs_searchPaths->pack) { if (fs_searchPaths->pack->pak) { fclose(fs_searchPaths->pack->pak); } #ifdef ZIP if (fs_searchPaths->pack->pk3) { unzClose(fs_searchPaths->pack->pk3); } #endif Z_Free(fs_searchPaths->pack->files); Z_Free(fs_searchPaths->pack); } next = fs_searchPaths->next; Z_Free(fs_searchPaths); fs_searchPaths = next; } /* Close open files for game dir. */ for (i = 0; i < MAX_HANDLES; i++) { if (strstr(fs_handles[i].name, dir) && ((fs_handles[i].file != NULL) #ifdef ZIP || (fs_handles[i].zip != NULL) #endif )) { FS_FCloseFile(i); } } /* Flush all data, so it will be forced to reload. */ if ((dedicated != NULL) && (dedicated->value != 1)) { Cbuf_AddText("vid_restart\nsnd_restart\n"); } Com_sprintf(fs_gamedir, sizeof(fs_gamedir), "%s/%s", fs_basedir->string, dir); if ((strcmp(dir, BASEDIRNAME) == 0) || (*dir == 0)) { Cvar_FullSet("gamedir", "", CVAR_SERVERINFO | CVAR_NOSET); Cvar_FullSet("game", "", CVAR_LATCH | CVAR_SERVERINFO); } else { Cvar_FullSet("gamedir", dir, CVAR_SERVERINFO | CVAR_NOSET); if (fs_cddir->string[0] == '\0') { FS_AddGameDirectory(va("%s/%s", fs_cddir->string, dir)); } FS_AddGameDirectory(va("%s/%s", fs_basedir->string, dir)); FS_AddBinaryDirAsGameDirectory(dir); FS_AddHomeAsGameDirectory(dir); } } /* * Creates a filelink_t. */ void FS_Link_f(void) { fsLink_t *l, **prev; if (Cmd_Argc() != 3) { Com_Printf("USAGE: link \n"); return; } /* See if the link already exists. */ prev = &fs_links; for (l = fs_links; l != NULL; l = l->next) { if (strcmp(l->from, Cmd_Argv(1)) == 0) { Z_Free(l->to); if (strlen(Cmd_Argv(2)) == 0) { /* Delete it. */ *prev = l->next; Z_Free(l->from); Z_Free(l); return; } l->to = CopyString(Cmd_Argv(2)); return; } prev = &l->next; } /* Create a new link. */ l = Z_Malloc(sizeof(*l)); l->next = fs_links; fs_links = l; l->from = CopyString(Cmd_Argv(1)); l->length = strlen(l->from); l->to = CopyString(Cmd_Argv(2)); } /* * Create a list of files that match a criteria. */ char ** FS_ListFiles(char *findname, int *numfiles, unsigned musthave, unsigned canthave) { char **list; /* List of files. */ char *s; /* Next file in list. */ int nfiles; /* Number of files in list. */ /* Initialize variables. */ list = NULL; nfiles = 0; /* Count the number of matches. */ s = Sys_FindFirst(findname, musthave, canthave); while (s != NULL) { if (s[strlen(s) - 1] != '.') { nfiles++; } s = Sys_FindNext(musthave, canthave); } Sys_FindClose(); /* Check if there are matches. */ if (nfiles == 0) { return NULL; } nfiles++; /* Add space for a guard. */ *numfiles = nfiles; /* Allocate the list. */ list = calloc(nfiles, sizeof(char *)); /* Fill the list. */ s = Sys_FindFirst(findname, musthave, canthave); nfiles = 0; while (s) { if (s[strlen(s) - 1] != '.') { list[nfiles] = strdup(s); nfiles++; } s = Sys_FindNext(musthave, canthave); } Sys_FindClose(); return list; } /* * Compare file attributes (musthave and canthave) in packed files. If * "output" is not NULL, "size" is greater than zero and the file matches the * attributes then a copy of the matching string will be placed there (with * SFF_SUBDIR it changes). */ qboolean ComparePackFiles(const char *findname, const char *name, unsigned musthave, unsigned canthave, char *output, int size) { qboolean retval; char *ptr; char buffer[MAX_OSPATH]; Q_strlcpy(buffer, name, sizeof(buffer)); if ((canthave & SFF_SUBDIR) && (name[strlen(name) - 1] == '/')) { return false; } if (musthave & SFF_SUBDIR) { if ((ptr = strrchr(buffer, '/')) != NULL) { *ptr = '\0'; } else { return false; } } if ((musthave & SFF_HIDDEN) || (canthave & SFF_HIDDEN)) { if ((ptr = strrchr(buffer, '/')) == NULL) { ptr = buffer; } if (((musthave & SFF_HIDDEN) && (ptr[1] != '.')) || ((canthave & SFF_HIDDEN) && (ptr[1] == '.'))) { return false; } } if (canthave & SFF_RDONLY) { return false; } retval = glob_match((char *)findname, buffer); if (retval && (output != NULL)) { Q_strlcpy(output, buffer, size); } return retval; } /* * Create a list of files that match a criteria. * Searchs are relative to the game directory and use all the search paths * including .pak and .pk3 files. */ char ** FS_ListFiles2(char *findname, int *numfiles, unsigned musthave, unsigned canthave) { fsSearchPath_t *search; /* Search path. */ int i, j; /* Loop counters. */ int nfiles; /* Number of files found. */ int tmpnfiles; /* Temp number of files. */ char **tmplist; /* Temporary list of files. */ char **list; /* List of files found. */ char path[MAX_OSPATH]; /* Temporary path. */ nfiles = 0; list = malloc(sizeof(char *)); for (search = fs_searchPaths; search != NULL; search = search->next) { if (search->pack != NULL) { if (canthave & SFF_INPACK) { continue; } for (i = 0, j = 0; i < search->pack->numFiles; i++) { if (ComparePackFiles(findname, search->pack->files[i].name, musthave, canthave, NULL, 0)) { j++; } } if (j == 0) { continue; } nfiles += j; list = realloc(list, nfiles * sizeof(char *)); for (i = 0, j = nfiles - j; i < search->pack->numFiles; i++) { if (ComparePackFiles(findname, search->pack->files[i].name, musthave, canthave, path, sizeof(path))) { list[j++] = strdup(path); } } } if (musthave & SFF_INPACK) { continue; } Com_sprintf(path, sizeof(path), "%s/%s", search->path, findname); tmplist = FS_ListFiles(path, &tmpnfiles, musthave, canthave); if (tmplist != NULL) { tmpnfiles--; nfiles += tmpnfiles; list = realloc(list, nfiles * sizeof(char *)); for (i = 0, j = nfiles - tmpnfiles; i < tmpnfiles; i++, j++) { list[j] = strdup(tmplist[i] + strlen(search->path) + 1); } FS_FreeList(tmplist, tmpnfiles + 1); } } /* Delete duplicates. */ tmpnfiles = 0; for (i = 0; i < nfiles; i++) { if (list[i] == NULL) { continue; } for (j = i + 1; j < nfiles; j++) { if ((list[j] != NULL) && (strcmp(list[i], list[j]) == 0)) { free(list[j]); list[j] = NULL; tmpnfiles++; } } } if (tmpnfiles > 0) { nfiles -= tmpnfiles; tmplist = malloc(nfiles * sizeof(char *)); for (i = 0, j = 0; i < nfiles + tmpnfiles; i++) { if (list[i] != NULL) { tmplist[j++] = list[i]; } } free(list); list = tmplist; } /* Add a guard. */ if (nfiles > 0) { nfiles++; list = realloc(list, nfiles * sizeof(char *)); list[nfiles - 1] = NULL; } else { free(list); list = NULL; } *numfiles = nfiles; return list; } /* * Free list of files created by FS_ListFiles(). */ void FS_FreeList(char **list, int nfiles) { int i; for (i = 0; i < nfiles - 1; i++) { free(list[i]); } free(list); } /* * Directory listing. */ void FS_Dir_f(void) { char **dirnames; /* File list. */ char findname[1024]; /* File search path and pattern. */ char *path = NULL; /* Search path. */ char wildcard[1024] = "*.*"; /* File pattern. */ int i; /* Loop counter. */ int ndirs; /* Number of files in list. */ /* Check for pattern in arguments. */ if (Cmd_Argc() != 1) { Q_strlcpy(wildcard, Cmd_Argv(1), sizeof(wildcard)); } /* Scan search paths and list files. */ while ((path = FS_NextPath(path)) != NULL) { Com_sprintf(findname, sizeof(findname), "%s/%s", path, wildcard); Com_Printf("Directory of '%s'.\n", findname); Com_Printf("----\n"); if ((dirnames = FS_ListFiles(findname, &ndirs, 0, 0)) != 0) { for (i = 0; i < ndirs - 1; i++) { if (strrchr(dirnames[i], '/')) { Com_Printf("%s\n", strrchr(dirnames[i], '/') + 1); } else { Com_Printf("%s\n", dirnames[i]); } } FS_FreeList(dirnames, ndirs); } Com_Printf("\n"); } } void FS_InitFilesystem(void) { /* Register FS commands. */ Cmd_AddCommand("path", FS_Path_f); Cmd_AddCommand("link", FS_Link_f); Cmd_AddCommand("dir", FS_Dir_f); /* basedir Allows the game to run from outside the data tree. */ fs_basedir = Cvar_Get("basedir", #ifdef SYSTEMWIDE SYSTEMDIR, #else ".", #endif CVAR_NOSET); /* cddir Logically concatenates the cddir after the basedir to allow the game to run from outside the data tree. */ fs_cddir = Cvar_Get("cddir", "", CVAR_NOSET); if (fs_cddir->string[0] != '\0') { FS_AddGameDirectory(va("%s/" BASEDIRNAME, fs_cddir->string)); } /* Debug flag. */ fs_debug = Cvar_Get("fs_debug", "0", 0); /* Game directory. */ fs_gamedirvar = Cvar_Get("game", "", CVAR_LATCH | CVAR_SERVERINFO); /* Current directory. */ fs_homepath = Cvar_Get("homepath", Sys_GetCurrentDirectory(), CVAR_NOSET); /* Add baseq2 to search path. */ FS_AddGameDirectory(va("%s/" BASEDIRNAME, fs_basedir->string)); FS_AddBinaryDirAsGameDirectory(BASEDIRNAME); FS_AddHomeAsGameDirectory(BASEDIRNAME); /* Any set gamedirs will be freed up to here. */ fs_baseSearchPaths = fs_searchPaths; Q_strlcpy(fs_currentGame, BASEDIRNAME, sizeof(fs_currentGame)); /* Check for game override. */ if (fs_gamedirvar->string[0] != '\0') { FS_SetGamedir(fs_gamedirvar->string); } /* Create directory if it does not exist. */ FS_CreatePath(fs_gamedir); Com_Printf("Using '%s' for writing.\n", fs_gamedir); } yquake2-QUAKE2_5_32/src/common/md4.c0000644000175000017500000001015112615162034017524 0ustar greffrathgreffrath/* * Public Domain C source implementation of RFC 1320 * - The MD4 Message-Digest Algorithm - * * http://www.faqs.org/rfcs/rfc1320.html * by Steven Fuller */ #include #define ROTATELEFT32(x, s) (((x) << (s)) | ((x) >> (32 - (s)))) #define F(X, Y, Z) (((X)&(Y)) | ((~X) & (Z))) #define G(X, Y, Z) (((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z))) #define H(X, Y, Z) ((X) ^ (Y) ^ (Z)) #define S(a, b, c, d, k, s) \ { \ a += (F((b), (c), (d)) + X[(k)]); \ a = ROTATELEFT32(a, s); \ } #define T(a, b, c, d, k, s) \ { \ a += (G((b), (c), (d)) + X[(k)] + 0x5A827999); \ a = ROTATELEFT32(a, s); \ } #define U(a, b, c, d, k, s) \ { \ a += (H((b), (c), (d)) + X[(k)] + 0x6ED9EBA1); \ a = ROTATELEFT32(a, s); \ } static uint32_t X[16]; static uint32_t A, AA; static uint32_t B, BB; static uint32_t C, CC; static uint32_t D, DD; static void DoMD4() { AA = A; BB = B; CC = C; DD = D; S(A, B, C, D, 0, 3); S(D, A, B, C, 1, 7); S(C, D, A, B, 2, 11); S(B, C, D, A, 3, 19); S(A, B, C, D, 4, 3); S(D, A, B, C, 5, 7); S(C, D, A, B, 6, 11); S(B, C, D, A, 7, 19); S(A, B, C, D, 8, 3); S(D, A, B, C, 9, 7); S(C, D, A, B, 10, 11); S(B, C, D, A, 11, 19); S(A, B, C, D, 12, 3); S(D, A, B, C, 13, 7); S(C, D, A, B, 14, 11); S(B, C, D, A, 15, 19); T(A, B, C, D, 0, 3); T(D, A, B, C, 4, 5); T(C, D, A, B, 8, 9); T(B, C, D, A, 12, 13); T(A, B, C, D, 1, 3); T(D, A, B, C, 5, 5); T(C, D, A, B, 9, 9); T(B, C, D, A, 13, 13); T(A, B, C, D, 2, 3); T(D, A, B, C, 6, 5); T(C, D, A, B, 10, 9); T(B, C, D, A, 14, 13); T(A, B, C, D, 3, 3); T(D, A, B, C, 7, 5); T(C, D, A, B, 11, 9); T(B, C, D, A, 15, 13); U(A, B, C, D, 0, 3); U(D, A, B, C, 8, 9); U(C, D, A, B, 4, 11); U(B, C, D, A, 12, 15); U(A, B, C, D, 2, 3); U(D, A, B, C, 10, 9); U(C, D, A, B, 6, 11); U(B, C, D, A, 14, 15); U(A, B, C, D, 1, 3); U(D, A, B, C, 9, 9); U(C, D, A, B, 5, 11); U(B, C, D, A, 13, 15); U(A, B, C, D, 3, 3); U(D, A, B, C, 11, 9); U(C, D, A, B, 7, 11); U(B, C, D, A, 15, 15); A += AA; B += BB; C += CC; D += DD; } static void PerformMD4(const unsigned char *buf, int length, unsigned char *digest) { int len = length / 64; /* number of full blocks */ int rem = length % 64; /* number of left over bytes */ int i, j; const unsigned char *ptr = buf; /* initialize the MD buffer */ A = 0x67452301; B = 0xEFCDAB89; C = 0x98BADCFE; D = 0x10325476; for (i = 0; i < len; i++) { for (j = 0; j < 16; j++) { X[j] = ((ptr[0] << 0) | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24)); ptr += 4; } DoMD4(); } i = rem / 4; for (j = 0; j < i; j++) { X[j] = ((ptr[0] << 0) | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24)); ptr += 4; } switch (rem % 4) { case 0: X[j] = 0x80U; break; case 1: X[j] = ((ptr[0] << 0) | ((0x80U) << 8)); break; case 2: X[j] = ((ptr[0] << 0) | (ptr[1] << 8) | ((0x80U) << 16)); break; case 3: X[j] = ((ptr[0] << 0) | (ptr[1] << 8) | (ptr[2] << 16) | ((0x80U) << 24)); break; } j++; if (j > 14) { for ( ; j < 16; j++) { X[j] = 0; } DoMD4(); j = 0; } for ( ; j < 14; j++) { X[j] = 0; } X[14] = (length & 0x1FFFFFFF) << 3; X[15] = (length & ~0x1FFFFFFF) >> 29; DoMD4(); digest[0] = (A & 0x000000FF) >> 0; digest[1] = (A & 0x0000FF00) >> 8; digest[2] = (A & 0x00FF0000) >> 16; digest[3] = (A & 0xFF000000) >> 24; digest[4] = (B & 0x000000FF) >> 0; digest[5] = (B & 0x0000FF00) >> 8; digest[6] = (B & 0x00FF0000) >> 16; digest[7] = (B & 0xFF000000) >> 24; digest[8] = (C & 0x000000FF) >> 0; digest[9] = (C & 0x0000FF00) >> 8; digest[10] = (C & 0x00FF0000) >> 16; digest[11] = (C & 0xFF000000) >> 24; digest[12] = (D & 0x000000FF) >> 0; digest[13] = (D & 0x0000FF00) >> 8; digest[14] = (D & 0x00FF0000) >> 16; digest[15] = (D & 0xFF000000) >> 24; A = AA = 0; B = BB = 0; C = CC = 0; D = DD = 0; for (j = 0; j < 16; j++) { X[j] = 0; } } unsigned Com_BlockChecksum(void *buffer, int length) { uint32_t digest[4]; unsigned val; PerformMD4((unsigned char *)buffer, length, (unsigned char *)digest); val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; return val; } yquake2-QUAKE2_5_32/src/common/header/0000755000175000017500000000000012615162034020126 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/common/header/files.h0000644000175000017500000002435012615162034021405 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 prototypes for most file formats used by Quake II * * ======================================================================= */ #ifndef CO_FILES_H #define CO_FILES_H /* The .pak files are just a linear collapse of a directory tree */ #define IDPAKHEADER (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P') typedef struct { char name[56]; int filepos, filelen; } dpackfile_t; typedef struct { int ident; /* == IDPAKHEADER */ int dirofs; int dirlen; } dpackheader_t; #define MAX_FILES_IN_PACK 4096 /* PCX files are used for as many images as possible */ typedef struct { char manufacturer; char version; char encoding; char bits_per_pixel; unsigned short xmin, ymin, xmax, ymax; unsigned short hres, vres; unsigned char palette[48]; char reserved; char color_planes; unsigned short bytes_per_line; unsigned short palette_type; char filler[58]; unsigned char data; /* unbounded */ } pcx_t; /* .MD2 triangle model file format */ #define IDALIASHEADER (('2' << 24) + ('P' << 16) + ('D' << 8) + 'I') #define ALIAS_VERSION 8 #define MAX_TRIANGLES 4096 #define MAX_VERTS 2048 #define MAX_FRAMES 512 #define MAX_MD2SKINS 32 #define MAX_SKINNAME 64 typedef struct { short s; short t; } dstvert_t; typedef struct { short index_xyz[3]; short index_st[3]; } dtriangle_t; typedef struct { byte v[3]; /* scaled byte to fit in frame mins/maxs */ byte lightnormalindex; } dtrivertx_t; #define DTRIVERTX_V0 0 #define DTRIVERTX_V1 1 #define DTRIVERTX_V2 2 #define DTRIVERTX_LNI 3 #define DTRIVERTX_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 */ dtrivertx_t verts[1]; /* variable sized */ } daliasframe_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 */ } dmdl_t; /* .SP2 sprite file format */ #define IDSPRITEHEADER (('2' << 24) + ('S' << 16) + ('D' << 8) + 'I') /* little-endian "IDS2" */ #define SPRITE_VERSION 2 typedef struct { int width, height; int origin_x, origin_y; /* raster coordinates inside pic */ char name[MAX_SKINNAME]; /* name of pcx file */ } dsprframe_t; typedef struct { int ident; int version; int numframes; dsprframe_t frames[1]; /* variable sized */ } dsprite_t; /* .WAL texture file format */ #define MIPLEVELS 4 typedef struct miptex_s { char name[32]; unsigned width, height; unsigned offsets[MIPLEVELS]; /* four mip maps stored */ char animname[32]; /* next frame in animation chain */ int flags; int contents; int value; } miptex_t; /* .BSP file format */ #define IDBSPHEADER (('P' << 24) + ('S' << 16) + ('B' << 8) + 'I') /* little-endian "IBSP" */ #define BSPVERSION 38 /* upper design bounds: leaffaces, leafbrushes, planes, and * verts are still bounded by 16 bit short limits */ #define MAX_MAP_MODELS 1024 #define MAX_MAP_BRUSHES 8192 #define MAX_MAP_ENTITIES 2048 #define MAX_MAP_ENTSTRING 0x40000 #define MAX_MAP_TEXINFO 8192 #define MAX_MAP_AREAS 256 #define MAX_MAP_AREAPORTALS 1024 #define MAX_MAP_PLANES 65536 #define MAX_MAP_NODES 65536 #define MAX_MAP_BRUSHSIDES 65536 #define MAX_MAP_LEAFS 65536 #define MAX_MAP_VERTS 65536 #define MAX_MAP_FACES 65536 #define MAX_MAP_LEAFFACES 65536 #define MAX_MAP_LEAFBRUSHES 65536 #define MAX_MAP_PORTALS 65536 #define MAX_MAP_EDGES 128000 #define MAX_MAP_SURFEDGES 256000 #define MAX_MAP_LIGHTING 0x200000 #define MAX_MAP_VISIBILITY 0x100000 /* key / value pair sizes */ #define MAX_KEY 32 #define MAX_VALUE 1024 /* ================================================================== */ typedef struct { int fileofs, filelen; } lump_t; #define LUMP_ENTITIES 0 #define LUMP_PLANES 1 #define LUMP_VERTEXES 2 #define LUMP_VISIBILITY 3 #define LUMP_NODES 4 #define LUMP_TEXINFO 5 #define LUMP_FACES 6 #define LUMP_LIGHTING 7 #define LUMP_LEAFS 8 #define LUMP_LEAFFACES 9 #define LUMP_LEAFBRUSHES 10 #define LUMP_EDGES 11 #define LUMP_SURFEDGES 12 #define LUMP_MODELS 13 #define LUMP_BRUSHES 14 #define LUMP_BRUSHSIDES 15 #define LUMP_POP 16 #define LUMP_AREAS 17 #define LUMP_AREAPORTALS 18 #define HEADER_LUMPS 19 typedef struct { int ident; int version; lump_t lumps[HEADER_LUMPS]; } dheader_t; typedef struct { float mins[3], maxs[3]; float origin[3]; /* for sounds or lights */ int headnode; int firstface, numfaces; /* submodels just draw faces without walking the bsp tree */ } dmodel_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 /* planes (x&~1) and (x&~1)+1 are always opposites */ typedef struct { float normal[3]; float dist; int type; /* PLANE_X - PLANE_ANYZ */ } dplane_t; /* contents flags are seperate bits * - given brush can contribute multiple content bits * - multiple brushes can be in a single leaf */ /* lower bits are stronger, and will eat weaker brushes completely */ #define CONTENTS_SOLID 1 /* an eye is never valid in a solid */ #define CONTENTS_WINDOW 2 /* translucent, but not watery */ #define CONTENTS_AUX 4 #define CONTENTS_LAVA 8 #define CONTENTS_SLIME 16 #define CONTENTS_WATER 32 #define CONTENTS_MIST 64 #define LAST_VISIBLE_CONTENTS 64 /* remaining contents are non-visible, and don't eat brushes */ #define CONTENTS_AREAPORTAL 0x8000 #define CONTENTS_PLAYERCLIP 0x10000 #define CONTENTS_MONSTERCLIP 0x20000 /* currents can be added to any other contents, and may be mixed */ #define CONTENTS_CURRENT_0 0x40000 #define CONTENTS_CURRENT_90 0x80000 #define CONTENTS_CURRENT_180 0x100000 #define CONTENTS_CURRENT_270 0x200000 #define CONTENTS_CURRENT_UP 0x400000 #define CONTENTS_CURRENT_DOWN 0x800000 #define CONTENTS_ORIGIN 0x1000000 /* removed before bsping an entity */ #define CONTENTS_MONSTER 0x2000000 /* should never be on a brush, only in game */ #define CONTENTS_DEADMONSTER 0x4000000 #define CONTENTS_DETAIL 0x8000000 /* brushes to be added after vis leafs */ #define CONTENTS_TRANSLUCENT 0x10000000 /* auto set if any surface has trans */ #define CONTENTS_LADDER 0x20000000 #define SURF_LIGHT 0x1 /* value will hold the light strength */ #define SURF_SLICK 0x2 /* effects game physics */ #define SURF_SKY 0x4 /* don't draw, but add to skybox */ #define SURF_WARP 0x8 /* turbulent water warp */ #define SURF_TRANS33 0x10 #define SURF_TRANS66 0x20 #define SURF_FLOWING 0x40 /* scroll towards angle */ #define SURF_NODRAW 0x80 /* don't bother referencing the texture */ typedef struct { int planenum; int children[2]; /* negative numbers are -(leafs+1), not nodes */ short mins[3]; /* for frustom culling */ short maxs[3]; unsigned short firstface; unsigned short numfaces; /* counting both sides */ } dnode_t; typedef struct texinfo_s { float vecs[2][4]; /* [s/t][xyz offset] */ int flags; /* miptex flags + overrides light emission, etc */ int value; char texture[32]; /* texture name (textures*.wal) */ int nexttexinfo; /* for animations, -1 = end of chain */ } texinfo_t; /* 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 { unsigned 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; typedef struct { int contents; /* OR of all brushes (not needed?) */ short cluster; short area; short mins[3]; /* for frustum culling */ short maxs[3]; unsigned short firstleafface; unsigned short numleaffaces; unsigned short firstleafbrush; unsigned short numleafbrushes; } dleaf_t; typedef struct { unsigned short planenum; /* facing out of the leaf */ short texinfo; } dbrushside_t; typedef struct { int firstside; int numsides; int contents; } dbrush_t; #define ANGLE_UP -1 #define ANGLE_DOWN -2 /* the visibility lump consists of a header with a count, then * byte offsets for the PVS and PHS of each cluster, then the raw * compressed bit vectors */ #define DVIS_PVS 0 #define DVIS_PHS 1 typedef struct { int numclusters; int bitofs[8][2]; /* bitofs[numclusters][2] */ } dvis_t; /* each area has a list of portals that lead into other areas * when portals are closed, other areas may not be visible or * hearable even if the vis info says that it should be */ typedef struct { int portalnum; int otherarea; } dareaportal_t; typedef struct { int numareaportals; int firstareaportal; } darea_t; #endif yquake2-QUAKE2_5_32/src/common/header/shared.h0000644000175000017500000010453612615162034021556 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 is the main header file shared between client, renderer, server * and the game. Do NOT edit this file unless you know what you're * doing. Changes here may break the client <-> renderer <-> server * <-> game API, leading to problems with mods! * * ======================================================================= */ #ifndef COMMON_SHARED_H #define COMMON_SHARED_H #include #include #include #include #include #include #include #ifdef true #undef true #endif #ifdef false #undef false #endif typedef enum {false, true} qboolean; typedef unsigned char byte; #ifndef NULL #define NULL ((void *)0) #endif /* angle indexes */ #define PITCH 0 /* up / down */ #define YAW 1 /* left / right */ #define ROLL 2 /* fall over */ #define MAX_STRING_CHARS 1024 /* max length of a string passed to Cmd_TokenizeString */ #define MAX_STRING_TOKENS 80 /* max tokens resulting from Cmd_TokenizeString */ #define MAX_TOKEN_CHARS 128 /* max length of an individual token */ #define MAX_QPATH 64 /* max length of a quake game pathname */ /* * DG: For some stupid reason, SV_WriteServerFile() and SV_ReadeServerFile() used * MAX_OSPATH as buffer length for CVAR_LATCH CVARS and saved the whole buffer * into $game/save/current/server.ssv, so changing MAX_OSPATH breaks savegames... * Unfortunately, for some other fucking reason MAX_OSPATH was 128 for non-Windows * which is just horrible.. so I introduced LATCH_CVAR_SAVELENGTH with the stupid * values so I could bump MAX_OSPATH. * TODO: whenever you break savegame compatibility next, make * LATCH_CVAR_SAVELENGTH system-independent (or remove it and hardcode a * sensible value in the two functions) */ #ifdef _WIN32 #define MAX_OSPATH 256 /* max length of a filesystem pathname (same as MAX_PATH) */ #define LATCH_CVAR_SAVELENGTH 256 #else #define MAX_OSPATH 4096 /* max length of a filesystem pathname */ #define LATCH_CVAR_SAVELENGTH 128 #endif /* per-level limits */ #define MAX_CLIENTS 256 /* absolute limit */ #define MAX_EDICTS 1024 /* must change protocol to increase more */ #define MAX_LIGHTSTYLES 256 #define MAX_MODELS 256 /* these are sent over the net as bytes */ #define MAX_SOUNDS 256 /* so they cannot be blindly increased */ #define MAX_IMAGES 256 #define MAX_ITEMS 256 #define MAX_GENERAL (MAX_CLIENTS * 2) /* general config strings */ /* game print flags */ #define PRINT_LOW 0 /* pickup messages */ #define PRINT_MEDIUM 1 /* death messages */ #define PRINT_HIGH 2 /* critical messages */ #define PRINT_CHAT 3 /* chat messages */ #define ERR_FATAL 0 /* exit the entire game with a popup window */ #define ERR_DROP 1 /* print to console and disconnect from game */ #define ERR_DISCONNECT 2 /* don't kill server */ #define PRINT_ALL 0 #define PRINT_DEVELOPER 1 /* only print when "developer 1" */ #define PRINT_ALERT 2 /* destination class for gi.multicast() */ typedef enum { MULTICAST_ALL, MULTICAST_PHS, MULTICAST_PVS, MULTICAST_ALL_R, MULTICAST_PHS_R, MULTICAST_PVS_R } multicast_t; /* * ============================================================== * * MATHLIB * * ============================================================== */ typedef float vec_t; typedef vec_t vec3_t[3]; typedef vec_t vec5_t[5]; typedef int fixed4_t; typedef int fixed8_t; typedef int fixed16_t; #ifndef M_PI #define M_PI 3.14159265358979323846 /* matches value in gcc v2 math.h */ #endif struct cplane_s; extern vec3_t vec3_origin; #define nanmask (255 << 23) #define IS_NAN(x) (((*(int *)&x) & nanmask) == nanmask) #define Q_ftol(f) (long)(f) #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)) void VectorMA(vec3_t veca, float scale, vec3_t vecb, vec3_t vecc); /* just in case you do't want to use the macros */ 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 ClearBounds(vec3_t mins, vec3_t maxs); void AddPointToBounds(vec3_t v, vec3_t mins, vec3_t maxs); int VectorCompare(vec3_t v1, vec3_t v2); vec_t VectorLength(vec3_t v); void CrossProduct(vec3_t v1, vec3_t v2, vec3_t cross); vec_t VectorNormalize(vec3_t v); /* returns vector length */ vec_t VectorNormalize2(vec3_t v, vec3_t out); 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 AngleVectors(vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); void AngleVectors2(vec3_t value1, vec3_t angles); int BoxOnPlaneSide(vec3_t emins, vec3_t emaxs, struct cplane_s *plane); float anglemod(float a); float LerpAngle(float a1, float a2, float frac); #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))) void ProjectPointOnPlane(vec3_t dst, const vec3_t p, const vec3_t normal); void PerpendicularVector(vec3_t dst, const vec3_t src); void RotatePointAroundVector(vec3_t dst, const vec3_t dir, const vec3_t point, float degrees); /* ============================================= */ char *COM_SkipPath(char *pathname); void COM_StripExtension(char *in, char *out); const char *COM_FileExtension(const char *in); void COM_FileBase(char *in, char *out); void COM_FilePath(const char *in, char *out); void COM_DefaultExtension(char *path, const char *extension); char *COM_Parse(char **data_p); /* data is an in/out parm, returns a parsed out token */ void Com_sprintf(char *dest, int size, char *fmt, ...); void Com_PageInMemory(byte *buffer, int size); /* ============================================= */ /* portable case insensitive compare */ int Q_stricmp(const char *s1, const char *s2); int Q_strcasecmp(char *s1, char *s2); int Q_strncasecmp(char *s1, char *s2, int n); /* portable string lowercase */ char *Q_strlwr(char *s); /* portable safe string copy/concatenate */ int Q_strlcpy(char *dst, const char *src, int size); int Q_strlcat(char *dst, const char *src, int size); /* ============================================= */ short BigShort(short l); short LittleShort(short l); int BigLong(int l); int LittleLong(int l); float BigFloat(float l); float LittleFloat(float l); void Swap_Init(void); char *va(char *format, ...); /* ============================================= */ /* key / value info strings */ #define MAX_INFO_KEY 64 #define MAX_INFO_VALUE 64 #define MAX_INFO_STRING 512 char *Info_ValueForKey(char *s, char *key); void Info_RemoveKey(char *s, char *key); void Info_SetValueForKey(char *s, char *key, char *value); qboolean Info_Validate(char *s); /* ============================================= */ /* Random number generator */ int randk(void); float frandk(void); float crandk(void); void randk_seed(void); /* * ============================================================== * * SYSTEM SPECIFIC * * ============================================================== */ extern int curtime; /* time returned by last Sys_Milliseconds */ int Sys_Milliseconds(void); void Sys_Mkdir(char *path); /* large block stack allocation routines */ void *Hunk_Begin(int maxsize); void *Hunk_Alloc(int size); void Hunk_Free(void *buf); int Hunk_End(void); /* directory searching */ #define SFF_ARCH 0x01 #define SFF_HIDDEN 0x02 #define SFF_RDONLY 0x04 #define SFF_SUBDIR 0x08 #define SFF_SYSTEM 0x10 /* pass in an attribute mask of things you wish to REJECT */ char *Sys_FindFirst(char *path, unsigned musthave, unsigned canthave); char *Sys_FindNext(unsigned musthave, unsigned canthave); void Sys_FindClose(void); /* this is only here so the functions in shared source files can link */ void Sys_Error(char *error, ...); void Com_Printf(char *msg, ...); /* * ========================================================== * * CVARS (console variables) * * ========================================================== */ #ifndef CVAR #define CVAR #define CVAR_ARCHIVE 1 /* set to cause it to be saved to vars.rc */ #define CVAR_USERINFO 2 /* added to userinfo when changed */ #define CVAR_SERVERINFO 4 /* added to serverinfo when changed */ #define CVAR_NOSET 8 /* don't allow change from console at all, */ /* but can be set from the command line */ #define CVAR_LATCH 16 /* save changes until server restart */ /* nothing outside the Cvar_*() functions should modify these fields! */ typedef struct cvar_s { char *name; char *string; char *latched_string; /* for CVAR_LATCH vars */ int flags; qboolean modified; /* set each time the cvar is changed */ float value; struct cvar_s *next; } cvar_t; #endif /* CVAR */ /* * ============================================================== * * COLLISION DETECTION * * ============================================================== */ /* lower bits are stronger, and will eat weaker brushes completely */ #define CONTENTS_SOLID 1 /* an eye is never valid in a solid */ #define CONTENTS_WINDOW 2 /* translucent, but not watery */ #define CONTENTS_AUX 4 #define CONTENTS_LAVA 8 #define CONTENTS_SLIME 16 #define CONTENTS_WATER 32 #define CONTENTS_MIST 64 #define LAST_VISIBLE_CONTENTS 64 /* remaining contents are non-visible, and don't eat brushes */ #define CONTENTS_AREAPORTAL 0x8000 #define CONTENTS_PLAYERCLIP 0x10000 #define CONTENTS_MONSTERCLIP 0x20000 /* currents can be added to any other contents, and may be mixed */ #define CONTENTS_CURRENT_0 0x40000 #define CONTENTS_CURRENT_90 0x80000 #define CONTENTS_CURRENT_180 0x100000 #define CONTENTS_CURRENT_270 0x200000 #define CONTENTS_CURRENT_UP 0x400000 #define CONTENTS_CURRENT_DOWN 0x800000 #define CONTENTS_ORIGIN 0x1000000 /* removed before bsping an entity */ #define CONTENTS_MONSTER 0x2000000 /* should never be on a brush, only in game */ #define CONTENTS_DEADMONSTER 0x4000000 #define CONTENTS_DETAIL 0x8000000 /* brushes to be added after vis leafs */ #define CONTENTS_TRANSLUCENT 0x10000000 /* auto set if any surface has trans */ #define CONTENTS_LADDER 0x20000000 #define SURF_LIGHT 0x1 /* value will hold the light strength */ #define SURF_SLICK 0x2 /* effects game physics */ #define SURF_SKY 0x4 /* don't draw, but add to skybox */ #define SURF_WARP 0x8 /* turbulent water warp */ #define SURF_TRANS33 0x10 #define SURF_TRANS66 0x20 #define SURF_FLOWING 0x40 /* scroll towards angle */ #define SURF_NODRAW 0x80 /* don't bother referencing the texture */ /* content masks */ #define MASK_ALL (-1) #define MASK_SOLID (CONTENTS_SOLID | CONTENTS_WINDOW) #define MASK_PLAYERSOLID \ (CONTENTS_SOLID | CONTENTS_PLAYERCLIP | \ CONTENTS_WINDOW | CONTENTS_MONSTER) #define MASK_DEADSOLID (CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WINDOW) #define MASK_MONSTERSOLID \ (CONTENTS_SOLID | CONTENTS_MONSTERCLIP | \ CONTENTS_WINDOW | CONTENTS_MONSTER) #define MASK_WATER (CONTENTS_WATER | CONTENTS_LAVA | CONTENTS_SLIME) #define MASK_OPAQUE (CONTENTS_SOLID | CONTENTS_SLIME | CONTENTS_LAVA) #define MASK_SHOT \ (CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_WINDOW | \ CONTENTS_DEADMONSTER) #define MASK_CURRENT \ (CONTENTS_CURRENT_0 | CONTENTS_CURRENT_90 | \ CONTENTS_CURRENT_180 | CONTENTS_CURRENT_270 | \ CONTENTS_CURRENT_UP | \ CONTENTS_CURRENT_DOWN) /* gi.BoxEdicts() can return a list of either solid or trigger entities */ #define AREA_SOLID 1 #define AREA_TRIGGERS 2 /* plane_t structure */ typedef struct cplane_s { vec3_t normal; float dist; byte type; /* for fast side tests */ byte signbits; /* signx + (signy<<1) + (signz<<1) */ byte pad[2]; } cplane_t; /* structure offset for asm code */ #define CPLANE_NORMAL_X 0 #define CPLANE_NORMAL_Y 4 #define CPLANE_NORMAL_Z 8 #define CPLANE_DIST 12 #define CPLANE_TYPE 16 #define CPLANE_SIGNBITS 17 #define CPLANE_PAD0 18 #define CPLANE_PAD1 19 typedef struct cmodel_s { vec3_t mins, maxs; vec3_t origin; /* for sounds or lights */ int headnode; } cmodel_t; typedef struct csurface_s { char name[16]; int flags; int value; } csurface_t; typedef struct mapsurface_s /* used internally due to name len probs */ { csurface_t c; char rname[32]; } mapsurface_t; /* a trace is returned when a box is swept through the world */ typedef struct { qboolean allsolid; /* if true, plane is not valid */ qboolean startsolid; /* if true, the initial point was in a solid area */ float fraction; /* time completed, 1.0 = didn't hit anything */ vec3_t endpos; /* final position */ cplane_t plane; /* surface normal at impact */ csurface_t *surface; /* surface hit */ int contents; /* contents on other side of surface hit */ struct edict_s *ent; /* not set by CM_*() functions */ } trace_t; /* pmove_state_t is the information necessary for client side movement */ /* prediction */ typedef enum { /* can accelerate and turn */ PM_NORMAL, PM_SPECTATOR, /* no acceleration or turning */ PM_DEAD, PM_GIB, /* different bounding box */ PM_FREEZE } pmtype_t; /* pmove->pm_flags */ #define PMF_DUCKED 1 #define PMF_JUMP_HELD 2 #define PMF_ON_GROUND 4 #define PMF_TIME_WATERJUMP 8 /* pm_time is waterjump */ #define PMF_TIME_LAND 16 /* pm_time is time before rejump */ #define PMF_TIME_TELEPORT 32 /* pm_time is non-moving time */ #define PMF_NO_PREDICTION 64 /* temporarily disables prediction (used for grappling hook) */ /* this structure needs to be communicated bit-accurate/ * from the server to the client to guarantee that * prediction stays in sync, so no floats are used. * if any part of the game code modifies this struct, it * will result in a prediction error of some degree. */ typedef struct { pmtype_t pm_type; short origin[3]; /* 12.3 */ short velocity[3]; /* 12.3 */ byte pm_flags; /* ducked, jump_held, etc */ byte pm_time; /* each unit = 8 ms */ short gravity; short delta_angles[3]; /* add to command angles to get view direction * changed by spawns, rotating objects, and teleporters */ } pmove_state_t; /* button bits */ #define BUTTON_ATTACK 1 #define BUTTON_USE 2 #define BUTTON_ANY 128 /* any key whatsoever */ /* usercmd_t is sent to the server each client frame */ typedef struct usercmd_s { byte msec; byte buttons; short angles[3]; short forwardmove, sidemove, upmove; byte impulse; /* remove? */ byte lightlevel; /* light level the player is standing on */ } usercmd_t; #define MAXTOUCH 32 typedef struct { /* state (in / out) */ pmove_state_t s; /* command (in) */ usercmd_t cmd; qboolean snapinitial; /* if s has been changed outside pmove */ /* results (out) */ int numtouch; struct edict_s *touchents[MAXTOUCH]; vec3_t viewangles; /* clamped */ float viewheight; vec3_t mins, maxs; /* bounding box size */ struct edict_s *groundentity; int watertype; int waterlevel; /* callbacks to test the world */ trace_t (*trace)(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end); int (*pointcontents)(vec3_t point); } pmove_t; /* entity_state_t->effects * Effects are things handled on the client side (lights, particles, * frame animations) that happen constantly on the given entity. * An entity that has effects will be sent to the client even if * it has a zero index model. */ #define EF_ROTATE 0x00000001 /* rotate (bonus items) */ #define EF_GIB 0x00000002 /* leave a trail */ #define EF_BLASTER 0x00000008 /* redlight + trail */ #define EF_ROCKET 0x00000010 /* redlight + trail */ #define EF_GRENADE 0x00000020 #define EF_HYPERBLASTER 0x00000040 #define EF_BFG 0x00000080 #define EF_COLOR_SHELL 0x00000100 #define EF_POWERSCREEN 0x00000200 #define EF_ANIM01 0x00000400 /* automatically cycle between frames 0 and 1 at 2 hz */ #define EF_ANIM23 0x00000800 /* automatically cycle between frames 2 and 3 at 2 hz */ #define EF_ANIM_ALL 0x00001000 /* automatically cycle through all frames at 2hz */ #define EF_ANIM_ALLFAST 0x00002000 /* automatically cycle through all frames at 10hz */ #define EF_FLIES 0x00004000 #define EF_QUAD 0x00008000 #define EF_PENT 0x00010000 #define EF_TELEPORTER 0x00020000 /* particle fountain */ #define EF_FLAG1 0x00040000 #define EF_FLAG2 0x00080000 #define EF_IONRIPPER 0x00100000 #define EF_GREENGIB 0x00200000 #define EF_BLUEHYPERBLASTER 0x00400000 #define EF_SPINNINGLIGHTS 0x00800000 #define EF_PLASMA 0x01000000 #define EF_TRAP 0x02000000 #define EF_TRACKER 0x04000000 #define EF_DOUBLE 0x08000000 #define EF_SPHERETRANS 0x10000000 #define EF_TAGTRAIL 0x20000000 #define EF_HALF_DAMAGE 0x40000000 #define EF_TRACKERTRAIL 0x80000000 /* entity_state_t->renderfx flags */ #define RF_MINLIGHT 1 /* allways have some light (viewmodel) */ #define RF_VIEWERMODEL 2 /* don't draw through eyes, only mirrors */ #define RF_WEAPONMODEL 4 /* only draw through eyes */ #define RF_FULLBRIGHT 8 /* allways draw full intensity */ #define RF_DEPTHHACK 16 /* for view weapon Z crunching */ #define RF_TRANSLUCENT 32 #define RF_FRAMELERP 64 #define RF_BEAM 128 #define RF_CUSTOMSKIN 256 /* skin is an index in image_precache */ #define RF_GLOW 512 /* pulse lighting for bonus items */ #define RF_SHELL_RED 1024 #define RF_SHELL_GREEN 2048 #define RF_SHELL_BLUE 4096 #define RF_NOSHADOW 8192 /* don't draw a shadow */ #define RF_IR_VISIBLE 0x00008000 /* 32768 */ #define RF_SHELL_DOUBLE 0x00010000 /* 65536 */ #define RF_SHELL_HALF_DAM 0x00020000 #define RF_USE_DISGUISE 0x00040000 /* player_state_t->refdef flags */ #define RDF_UNDERWATER 1 /* warp the screen as apropriate */ #define RDF_NOWORLDMODEL 2 /* used for player configuration screen */ #define RDF_IRGOGGLES 4 #define RDF_UVGOGGLES 8 /* muzzle flashes / player effects */ #define MZ_BLASTER 0 #define MZ_MACHINEGUN 1 #define MZ_SHOTGUN 2 #define MZ_CHAINGUN1 3 #define MZ_CHAINGUN2 4 #define MZ_CHAINGUN3 5 #define MZ_RAILGUN 6 #define MZ_ROCKET 7 #define MZ_GRENADE 8 #define MZ_LOGIN 9 #define MZ_LOGOUT 10 #define MZ_RESPAWN 11 #define MZ_BFG 12 #define MZ_SSHOTGUN 13 #define MZ_HYPERBLASTER 14 #define MZ_ITEMRESPAWN 15 #define MZ_IONRIPPER 16 #define MZ_BLUEHYPERBLASTER 17 #define MZ_PHALANX 18 #define MZ_SILENCED 128 /* bit flag ORed with one of the above numbers */ #define MZ_ETF_RIFLE 30 #define MZ_UNUSED 31 #define MZ_SHOTGUN2 32 #define MZ_HEATBEAM 33 #define MZ_BLASTER2 34 #define MZ_TRACKER 35 #define MZ_NUKE1 36 #define MZ_NUKE2 37 #define MZ_NUKE4 38 #define MZ_NUKE8 39 /* monster muzzle flashes */ #define MZ2_TANK_BLASTER_1 1 #define MZ2_TANK_BLASTER_2 2 #define MZ2_TANK_BLASTER_3 3 #define MZ2_TANK_MACHINEGUN_1 4 #define MZ2_TANK_MACHINEGUN_2 5 #define MZ2_TANK_MACHINEGUN_3 6 #define MZ2_TANK_MACHINEGUN_4 7 #define MZ2_TANK_MACHINEGUN_5 8 #define MZ2_TANK_MACHINEGUN_6 9 #define MZ2_TANK_MACHINEGUN_7 10 #define MZ2_TANK_MACHINEGUN_8 11 #define MZ2_TANK_MACHINEGUN_9 12 #define MZ2_TANK_MACHINEGUN_10 13 #define MZ2_TANK_MACHINEGUN_11 14 #define MZ2_TANK_MACHINEGUN_12 15 #define MZ2_TANK_MACHINEGUN_13 16 #define MZ2_TANK_MACHINEGUN_14 17 #define MZ2_TANK_MACHINEGUN_15 18 #define MZ2_TANK_MACHINEGUN_16 19 #define MZ2_TANK_MACHINEGUN_17 20 #define MZ2_TANK_MACHINEGUN_18 21 #define MZ2_TANK_MACHINEGUN_19 22 #define MZ2_TANK_ROCKET_1 23 #define MZ2_TANK_ROCKET_2 24 #define MZ2_TANK_ROCKET_3 25 #define MZ2_INFANTRY_MACHINEGUN_1 26 #define MZ2_INFANTRY_MACHINEGUN_2 27 #define MZ2_INFANTRY_MACHINEGUN_3 28 #define MZ2_INFANTRY_MACHINEGUN_4 29 #define MZ2_INFANTRY_MACHINEGUN_5 30 #define MZ2_INFANTRY_MACHINEGUN_6 31 #define MZ2_INFANTRY_MACHINEGUN_7 32 #define MZ2_INFANTRY_MACHINEGUN_8 33 #define MZ2_INFANTRY_MACHINEGUN_9 34 #define MZ2_INFANTRY_MACHINEGUN_10 35 #define MZ2_INFANTRY_MACHINEGUN_11 36 #define MZ2_INFANTRY_MACHINEGUN_12 37 #define MZ2_INFANTRY_MACHINEGUN_13 38 #define MZ2_SOLDIER_BLASTER_1 39 #define MZ2_SOLDIER_BLASTER_2 40 #define MZ2_SOLDIER_SHOTGUN_1 41 #define MZ2_SOLDIER_SHOTGUN_2 42 #define MZ2_SOLDIER_MACHINEGUN_1 43 #define MZ2_SOLDIER_MACHINEGUN_2 44 #define MZ2_GUNNER_MACHINEGUN_1 45 #define MZ2_GUNNER_MACHINEGUN_2 46 #define MZ2_GUNNER_MACHINEGUN_3 47 #define MZ2_GUNNER_MACHINEGUN_4 48 #define MZ2_GUNNER_MACHINEGUN_5 49 #define MZ2_GUNNER_MACHINEGUN_6 50 #define MZ2_GUNNER_MACHINEGUN_7 51 #define MZ2_GUNNER_MACHINEGUN_8 52 #define MZ2_GUNNER_GRENADE_1 53 #define MZ2_GUNNER_GRENADE_2 54 #define MZ2_GUNNER_GRENADE_3 55 #define MZ2_GUNNER_GRENADE_4 56 #define MZ2_CHICK_ROCKET_1 57 #define MZ2_FLYER_BLASTER_1 58 #define MZ2_FLYER_BLASTER_2 59 #define MZ2_MEDIC_BLASTER_1 60 #define MZ2_GLADIATOR_RAILGUN_1 61 #define MZ2_HOVER_BLASTER_1 62 #define MZ2_ACTOR_MACHINEGUN_1 63 #define MZ2_SUPERTANK_MACHINEGUN_1 64 #define MZ2_SUPERTANK_MACHINEGUN_2 65 #define MZ2_SUPERTANK_MACHINEGUN_3 66 #define MZ2_SUPERTANK_MACHINEGUN_4 67 #define MZ2_SUPERTANK_MACHINEGUN_5 68 #define MZ2_SUPERTANK_MACHINEGUN_6 69 #define MZ2_SUPERTANK_ROCKET_1 70 #define MZ2_SUPERTANK_ROCKET_2 71 #define MZ2_SUPERTANK_ROCKET_3 72 #define MZ2_BOSS2_MACHINEGUN_L1 73 #define MZ2_BOSS2_MACHINEGUN_L2 74 #define MZ2_BOSS2_MACHINEGUN_L3 75 #define MZ2_BOSS2_MACHINEGUN_L4 76 #define MZ2_BOSS2_MACHINEGUN_L5 77 #define MZ2_BOSS2_ROCKET_1 78 #define MZ2_BOSS2_ROCKET_2 79 #define MZ2_BOSS2_ROCKET_3 80 #define MZ2_BOSS2_ROCKET_4 81 #define MZ2_FLOAT_BLASTER_1 82 #define MZ2_SOLDIER_BLASTER_3 83 #define MZ2_SOLDIER_SHOTGUN_3 84 #define MZ2_SOLDIER_MACHINEGUN_3 85 #define MZ2_SOLDIER_BLASTER_4 86 #define MZ2_SOLDIER_SHOTGUN_4 87 #define MZ2_SOLDIER_MACHINEGUN_4 88 #define MZ2_SOLDIER_BLASTER_5 89 #define MZ2_SOLDIER_SHOTGUN_5 90 #define MZ2_SOLDIER_MACHINEGUN_5 91 #define MZ2_SOLDIER_BLASTER_6 92 #define MZ2_SOLDIER_SHOTGUN_6 93 #define MZ2_SOLDIER_MACHINEGUN_6 94 #define MZ2_SOLDIER_BLASTER_7 95 #define MZ2_SOLDIER_SHOTGUN_7 96 #define MZ2_SOLDIER_MACHINEGUN_7 97 #define MZ2_SOLDIER_BLASTER_8 98 #define MZ2_SOLDIER_SHOTGUN_8 99 #define MZ2_SOLDIER_MACHINEGUN_8 100 #define MZ2_MAKRON_BFG 101 #define MZ2_MAKRON_BLASTER_1 102 #define MZ2_MAKRON_BLASTER_2 103 #define MZ2_MAKRON_BLASTER_3 104 #define MZ2_MAKRON_BLASTER_4 105 #define MZ2_MAKRON_BLASTER_5 106 #define MZ2_MAKRON_BLASTER_6 107 #define MZ2_MAKRON_BLASTER_7 108 #define MZ2_MAKRON_BLASTER_8 109 #define MZ2_MAKRON_BLASTER_9 110 #define MZ2_MAKRON_BLASTER_10 111 #define MZ2_MAKRON_BLASTER_11 112 #define MZ2_MAKRON_BLASTER_12 113 #define MZ2_MAKRON_BLASTER_13 114 #define MZ2_MAKRON_BLASTER_14 115 #define MZ2_MAKRON_BLASTER_15 116 #define MZ2_MAKRON_BLASTER_16 117 #define MZ2_MAKRON_BLASTER_17 118 #define MZ2_MAKRON_RAILGUN_1 119 #define MZ2_JORG_MACHINEGUN_L1 120 #define MZ2_JORG_MACHINEGUN_L2 121 #define MZ2_JORG_MACHINEGUN_L3 122 #define MZ2_JORG_MACHINEGUN_L4 123 #define MZ2_JORG_MACHINEGUN_L5 124 #define MZ2_JORG_MACHINEGUN_L6 125 #define MZ2_JORG_MACHINEGUN_R1 126 #define MZ2_JORG_MACHINEGUN_R2 127 #define MZ2_JORG_MACHINEGUN_R3 128 #define MZ2_JORG_MACHINEGUN_R4 129 #define MZ2_JORG_MACHINEGUN_R5 130 #define MZ2_JORG_MACHINEGUN_R6 131 #define MZ2_JORG_BFG_1 132 #define MZ2_BOSS2_MACHINEGUN_R1 133 #define MZ2_BOSS2_MACHINEGUN_R2 134 #define MZ2_BOSS2_MACHINEGUN_R3 135 #define MZ2_BOSS2_MACHINEGUN_R4 136 #define MZ2_BOSS2_MACHINEGUN_R5 137 #define MZ2_CARRIER_MACHINEGUN_L1 138 #define MZ2_CARRIER_MACHINEGUN_R1 139 #define MZ2_CARRIER_GRENADE 140 #define MZ2_TURRET_MACHINEGUN 141 #define MZ2_TURRET_ROCKET 142 #define MZ2_TURRET_BLASTER 143 #define MZ2_STALKER_BLASTER 144 #define MZ2_DAEDALUS_BLASTER 145 #define MZ2_MEDIC_BLASTER_2 146 #define MZ2_CARRIER_RAILGUN 147 #define MZ2_WIDOW_DISRUPTOR 148 #define MZ2_WIDOW_BLASTER 149 #define MZ2_WIDOW_RAIL 150 #define MZ2_WIDOW_PLASMABEAM 151 #define MZ2_CARRIER_MACHINEGUN_L2 152 #define MZ2_CARRIER_MACHINEGUN_R2 153 #define MZ2_WIDOW_RAIL_LEFT 154 #define MZ2_WIDOW_RAIL_RIGHT 155 #define MZ2_WIDOW_BLASTER_SWEEP1 156 #define MZ2_WIDOW_BLASTER_SWEEP2 157 #define MZ2_WIDOW_BLASTER_SWEEP3 158 #define MZ2_WIDOW_BLASTER_SWEEP4 159 #define MZ2_WIDOW_BLASTER_SWEEP5 160 #define MZ2_WIDOW_BLASTER_SWEEP6 161 #define MZ2_WIDOW_BLASTER_SWEEP7 162 #define MZ2_WIDOW_BLASTER_SWEEP8 163 #define MZ2_WIDOW_BLASTER_SWEEP9 164 #define MZ2_WIDOW_BLASTER_100 165 #define MZ2_WIDOW_BLASTER_90 166 #define MZ2_WIDOW_BLASTER_80 167 #define MZ2_WIDOW_BLASTER_70 168 #define MZ2_WIDOW_BLASTER_60 169 #define MZ2_WIDOW_BLASTER_50 170 #define MZ2_WIDOW_BLASTER_40 171 #define MZ2_WIDOW_BLASTER_30 172 #define MZ2_WIDOW_BLASTER_20 173 #define MZ2_WIDOW_BLASTER_10 174 #define MZ2_WIDOW_BLASTER_0 175 #define MZ2_WIDOW_BLASTER_10L 176 #define MZ2_WIDOW_BLASTER_20L 177 #define MZ2_WIDOW_BLASTER_30L 178 #define MZ2_WIDOW_BLASTER_40L 179 #define MZ2_WIDOW_BLASTER_50L 180 #define MZ2_WIDOW_BLASTER_60L 181 #define MZ2_WIDOW_BLASTER_70L 182 #define MZ2_WIDOW_RUN_1 183 #define MZ2_WIDOW_RUN_2 184 #define MZ2_WIDOW_RUN_3 185 #define MZ2_WIDOW_RUN_4 186 #define MZ2_WIDOW_RUN_5 187 #define MZ2_WIDOW_RUN_6 188 #define MZ2_WIDOW_RUN_7 189 #define MZ2_WIDOW_RUN_8 190 #define MZ2_CARRIER_ROCKET_1 191 #define MZ2_CARRIER_ROCKET_2 192 #define MZ2_CARRIER_ROCKET_3 193 #define MZ2_CARRIER_ROCKET_4 194 #define MZ2_WIDOW2_BEAMER_1 195 #define MZ2_WIDOW2_BEAMER_2 196 #define MZ2_WIDOW2_BEAMER_3 197 #define MZ2_WIDOW2_BEAMER_4 198 #define MZ2_WIDOW2_BEAMER_5 199 #define MZ2_WIDOW2_BEAM_SWEEP_1 200 #define MZ2_WIDOW2_BEAM_SWEEP_2 201 #define MZ2_WIDOW2_BEAM_SWEEP_3 202 #define MZ2_WIDOW2_BEAM_SWEEP_4 203 #define MZ2_WIDOW2_BEAM_SWEEP_5 204 #define MZ2_WIDOW2_BEAM_SWEEP_6 205 #define MZ2_WIDOW2_BEAM_SWEEP_7 206 #define MZ2_WIDOW2_BEAM_SWEEP_8 207 #define MZ2_WIDOW2_BEAM_SWEEP_9 208 #define MZ2_WIDOW2_BEAM_SWEEP_10 209 #define MZ2_WIDOW2_BEAM_SWEEP_11 210 extern vec3_t monster_flash_offset[]; /* Temp entity events are for things that happen * at a location seperate from any existing entity. * Temporary entity messages are explicitly constructed * and broadcast. */ typedef enum { TE_GUNSHOT, TE_BLOOD, TE_BLASTER, TE_RAILTRAIL, TE_SHOTGUN, TE_EXPLOSION1, TE_EXPLOSION2, TE_ROCKET_EXPLOSION, TE_GRENADE_EXPLOSION, TE_SPARKS, TE_SPLASH, TE_BUBBLETRAIL, TE_SCREEN_SPARKS, TE_SHIELD_SPARKS, TE_BULLET_SPARKS, TE_LASER_SPARKS, TE_PARASITE_ATTACK, TE_ROCKET_EXPLOSION_WATER, TE_GRENADE_EXPLOSION_WATER, TE_MEDIC_CABLE_ATTACK, TE_BFG_EXPLOSION, TE_BFG_BIGEXPLOSION, TE_BOSSTPORT, /* used as '22' in a map, so DON'T RENUMBER!!! */ TE_BFG_LASER, TE_GRAPPLE_CABLE, TE_WELDING_SPARKS, TE_GREENBLOOD, TE_BLUEHYPERBLASTER, TE_PLASMA_EXPLOSION, TE_TUNNEL_SPARKS, TE_BLASTER2, TE_RAILTRAIL2, TE_FLAME, TE_LIGHTNING, TE_DEBUGTRAIL, TE_PLAIN_EXPLOSION, TE_FLASHLIGHT, TE_FORCEWALL, TE_HEATBEAM, TE_MONSTER_HEATBEAM, TE_STEAM, TE_BUBBLETRAIL2, TE_MOREBLOOD, TE_HEATBEAM_SPARKS, TE_HEATBEAM_STEAM, TE_CHAINFIST_SMOKE, TE_ELECTRIC_SPARKS, TE_TRACKER_EXPLOSION, TE_TELEPORT_EFFECT, TE_DBALL_GOAL, TE_WIDOWBEAMOUT, TE_NUKEBLAST, TE_WIDOWSPLASH, TE_EXPLOSION1_BIG, TE_EXPLOSION1_NP, TE_FLECHETTE } temp_event_t; #define SPLASH_UNKNOWN 0 #define SPLASH_SPARKS 1 #define SPLASH_BLUE_WATER 2 #define SPLASH_BROWN_WATER 3 #define SPLASH_SLIME 4 #define SPLASH_LAVA 5 #define SPLASH_BLOOD 6 /* sound channels: * channel 0 never willingly overrides * other channels (1-7) allways override * a playing sound on that channel */ #define CHAN_AUTO 0 #define CHAN_WEAPON 1 #define CHAN_VOICE 2 #define CHAN_ITEM 3 #define CHAN_BODY 4 /* modifier flags */ #define CHAN_NO_PHS_ADD 8 /* send to all clients, not just ones in PHS (ATTN 0 will also do this) */ #define CHAN_RELIABLE 16 /* send by reliable message, not datagram */ /* sound attenuation values */ #define ATTN_NONE 0 /* full volume the entire level */ #define ATTN_NORM 1 #define ATTN_IDLE 2 #define ATTN_STATIC 3 /* diminish very rapidly with distance */ /* player_state->stats[] indexes */ #define STAT_HEALTH_ICON 0 #define STAT_HEALTH 1 #define STAT_AMMO_ICON 2 #define STAT_AMMO 3 #define STAT_ARMOR_ICON 4 #define STAT_ARMOR 5 #define STAT_SELECTED_ICON 6 #define STAT_PICKUP_ICON 7 #define STAT_PICKUP_STRING 8 #define STAT_TIMER_ICON 9 #define STAT_TIMER 10 #define STAT_HELPICON 11 #define STAT_SELECTED_ITEM 12 #define STAT_LAYOUTS 13 #define STAT_FRAGS 14 #define STAT_FLASHES 15 /* cleared each frame, 1 = health, 2 = armor */ #define STAT_CHASE 16 #define STAT_SPECTATOR 17 #define MAX_STATS 32 /* dmflags->value flags */ #define DF_NO_HEALTH 0x00000001 /* 1 */ #define DF_NO_ITEMS 0x00000002 /* 2 */ #define DF_WEAPONS_STAY 0x00000004 /* 4 */ #define DF_NO_FALLING 0x00000008 /* 8 */ #define DF_INSTANT_ITEMS 0x00000010 /* 16 */ #define DF_SAME_LEVEL 0x00000020 /* 32 */ #define DF_SKINTEAMS 0x00000040 /* 64 */ #define DF_MODELTEAMS 0x00000080 /* 128 */ #define DF_NO_FRIENDLY_FIRE 0x00000100 /* 256 */ #define DF_SPAWN_FARTHEST 0x00000200 /* 512 */ #define DF_FORCE_RESPAWN 0x00000400 /* 1024 */ #define DF_NO_ARMOR 0x00000800 /* 2048 */ #define DF_ALLOW_EXIT 0x00001000 /* 4096 */ #define DF_INFINITE_AMMO 0x00002000 /* 8192 */ #define DF_QUAD_DROP 0x00004000 /* 16384 */ #define DF_FIXED_FOV 0x00008000 /* 32768 */ #define DF_QUADFIRE_DROP 0x00010000 /* 65536 */ #define DF_NO_MINES 0x00020000 #define DF_NO_STACK_DOUBLE 0x00040000 #define DF_NO_NUKES 0x00080000 #define DF_NO_SPHERES 0x00100000 #define ROGUE_VERSION_STRING "08/21/1998 Beta 2 for Ensemble" /* * ========================================================== * * ELEMENTS COMMUNICATED ACROSS THE NET * * ========================================================== */ #define ANGLE2SHORT(x) ((int)((x) * 65536 / 360) & 65535) #define SHORT2ANGLE(x) ((x) * (360.0 / 65536)) /* config strings are a general means of communication from * the server to all connected clients. Each config string * can be at most MAX_QPATH characters. */ #define CS_NAME 0 #define CS_CDTRACK 1 #define CS_SKY 2 #define CS_SKYAXIS 3 /* %f %f %f format */ #define CS_SKYROTATE 4 #define CS_STATUSBAR 5 /* display program string */ #define CS_AIRACCEL 29 /* air acceleration control */ #define CS_MAXCLIENTS 30 #define CS_MAPCHECKSUM 31 /* for catching cheater maps */ #define CS_MODELS 32 #define CS_SOUNDS (CS_MODELS + MAX_MODELS) #define CS_IMAGES (CS_SOUNDS + MAX_SOUNDS) #define CS_LIGHTS (CS_IMAGES + MAX_IMAGES) #define CS_ITEMS (CS_LIGHTS + MAX_LIGHTSTYLES) #define CS_PLAYERSKINS (CS_ITEMS + MAX_ITEMS) #define CS_GENERAL (CS_PLAYERSKINS + MAX_CLIENTS) #define MAX_CONFIGSTRINGS (CS_GENERAL + MAX_GENERAL) /* ============================================== */ /* entity_state_t->event values * entity events are for effects that take place reletive * to an existing entities origin. Very network efficient. * All muzzle flashes really should be converted to events... */ typedef enum { EV_NONE, EV_ITEM_RESPAWN, EV_FOOTSTEP, EV_FALLSHORT, EV_FALL, EV_FALLFAR, EV_PLAYER_TELEPORT, EV_OTHER_TELEPORT } entity_event_t; /* entity_state_t is the information conveyed from the server * in an update message about entities that the client will * need to render in some way */ typedef struct entity_state_s { int number; /* edict index */ vec3_t origin; vec3_t angles; vec3_t old_origin; /* for lerping */ int modelindex; int modelindex2, modelindex3, modelindex4; /* weapons, CTF flags, etc */ int frame; int skinnum; unsigned int effects; int renderfx; int solid; /* for client side prediction, 8*(bits 0-4) is x/y radius */ /* 8*(bits 5-9) is z down distance, 8(bits10-15) is z up */ /* gi.linkentity sets this properly */ int sound; /* for looping sounds, to guarantee shutoff */ int event; /* impulse events -- muzzle flashes, footsteps, etc */ /* events only go out for a single frame, they */ /* are automatically cleared each frame */ } entity_state_t; /* ============================================== */ /* player_state_t is the information needed in addition to pmove_state_t * to rendered a view. There will only be 10 player_state_t sent each second, * but the number of pmove_state_t changes will be reletive to client * frame rates */ typedef struct { pmove_state_t pmove; /* for prediction */ vec3_t viewangles; /* for fixed views */ vec3_t viewoffset; /* add to pmovestate->origin */ vec3_t kick_angles; /* add to view direction to get render angles */ /* set by weapon kicks, pain effects, etc */ vec3_t gunangles; vec3_t gunoffset; int gunindex; int gunframe; float blend[4]; /* rgba full screen effect */ float fov; /* horizontal field of view */ int rdflags; /* refdef flags */ short stats[MAX_STATS]; /* fast status bar updates */ } player_state_t; size_t verify_fread(void *, size_t, size_t, FILE *); size_t verify_fwrite(void *, size_t, size_t, FILE *); #endif /* COMMON_SHARED_H */ yquake2-QUAKE2_5_32/src/common/header/glob.h0000644000175000017500000000205412615162034021223 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Header file for global string matching * * ======================================================================= */ #ifndef UNIX_GLOB_H #define UNIX_GLOB_H int glob_match(char *pattern, char *text); #endif yquake2-QUAKE2_5_32/src/common/header/common.h0000644000175000017500000005576312615162034021607 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Prototypes witch are shared between the client, the server and the * game. This is the main game API, changes here will most likely * requiere changes to the game ddl. * * ======================================================================= */ #ifndef CO_COMMON_H #define CO_COMMON_H #include "shared.h" #include "crc.h" /* Should have 4 characters. */ #define YQ2VERSION "5.32" #define BASEDIRNAME "baseq2" #if defined __linux__ #define BUILDSTRING "Linux" #elif defined __FreeBSD__ #define BUILDSTRING "FreeBSD" #elif defined __OpenBSD__ #define BUILDSTRING "OpenBSD" #elif defined _WIN32 #define BUILDSTRING "Windows" #elif defined __APPLE__ #define BUILDSTRING "MacOS X" #else #define BUILDSTRING "Unknown" #endif #ifdef __i386__ #define CPUSTRING "i386" #elif defined __x86_64__ #define CPUSTRING "amd64" #elif defined __sparc__ #define CPUSTRING "sparc64" #elif defined __ia64__ #define CPUSTRING "ia64" #else #define CPUSTRING "Unknown" #endif #ifdef _WIN32 #define CFGDIR "YamagiQ2" #else #define CFGDIR ".yq2" #endif #ifdef _WIN32 #define LIBGL "opengl32.dll" #elif defined __OpenBSD__ #define LIBGL "libGL.so" #elif defined __APPLE__ #define LIBGL "/System/Library/Frameworks/OpenGL.framework/OpenGL" #else #define LIBGL "libGL.so.1" #endif /* ================================================================== */ typedef struct sizebuf_s { qboolean allowoverflow; /* if false, do a Com_Error */ qboolean overflowed; /* set to true if the buffer size failed */ byte *data; int maxsize; int cursize; int readcount; } sizebuf_t; void SZ_Init(sizebuf_t *buf, byte *data, int length); 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 */ /* ================================================================== */ struct usercmd_s; struct entity_state_s; 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_WritePos(sizebuf_t *sb, vec3_t pos); void MSG_WriteAngle(sizebuf_t *sb, float f); void MSG_WriteAngle16(sizebuf_t *sb, float f); void MSG_WriteDeltaUsercmd(sizebuf_t *sb, struct usercmd_s *from, struct usercmd_s *cmd); void MSG_WriteDeltaEntity(struct entity_state_s *from, struct entity_state_s *to, sizebuf_t *msg, qboolean force, qboolean newentity); void MSG_WriteDir(sizebuf_t *sb, vec3_t vector); void MSG_BeginReading(sizebuf_t *sb); int MSG_ReadChar(sizebuf_t *sb); int MSG_ReadByte(sizebuf_t *sb); int MSG_ReadShort(sizebuf_t *sb); int MSG_ReadLong(sizebuf_t *sb); float MSG_ReadFloat(sizebuf_t *sb); char *MSG_ReadString(sizebuf_t *sb); char *MSG_ReadStringLine(sizebuf_t *sb); float MSG_ReadCoord(sizebuf_t *sb); void MSG_ReadPos(sizebuf_t *sb, vec3_t pos); float MSG_ReadAngle(sizebuf_t *sb); float MSG_ReadAngle16(sizebuf_t *sb); void MSG_ReadDeltaUsercmd(sizebuf_t *sb, struct usercmd_s *from, struct usercmd_s *cmd); void MSG_ReadDir(sizebuf_t *sb, vec3_t vector); void MSG_ReadData(sizebuf_t *sb, void *buffer, int size); /* ================================================================== */ 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); /* ================================================================== */ int COM_Argc(void); char *COM_Argv(int arg); /* range and null checked */ void COM_ClearArgv(int arg); int COM_CheckParm(char *parm); void COM_AddParm(char *parm); void COM_Init(void); void COM_InitArgv(int argc, char **argv); char *CopyString(char *in); /* ================================================================== */ void Info_Print(char *s); /* PROTOCOL */ #define PROTOCOL_VERSION 34 /* ========================================= */ #define PORT_MASTER 27900 #define PORT_CLIENT 27901 #define PORT_SERVER 27910 /* ========================================= */ #define UPDATE_BACKUP 16 /* copies of entity_state_t to keep buffered */ #define UPDATE_MASK (UPDATE_BACKUP - 1) /* server to client */ enum svc_ops_e { svc_bad, /* these ops are known to the game dll */ svc_muzzleflash, svc_muzzleflash2, svc_temp_entity, svc_layout, svc_inventory, /* the rest are private to the client and server */ svc_nop, svc_disconnect, svc_reconnect, svc_sound, /* */ svc_print, /* [byte] id [string] null terminated string */ svc_stufftext, /* [string] stuffed into client's console buffer, should be \n terminated */ svc_serverdata, /* [long] protocol ... */ svc_configstring, /* [short] [string] */ svc_spawnbaseline, svc_centerprint, /* [string] to put in center of the screen */ svc_download, /* [short] size [size bytes] */ svc_playerinfo, /* variable */ svc_packetentities, /* [...] */ svc_deltapacketentities, /* [...] */ svc_frame }; /* ============================================== */ /* client to server */ enum clc_ops_e { clc_bad, clc_nop, clc_move, /* [[usercmd_t] */ clc_userinfo, /* [[userinfo string] */ clc_stringcmd /* [string] message */ }; /* ============================================== */ /* plyer_state_t communication */ #define PS_M_TYPE (1 << 0) #define PS_M_ORIGIN (1 << 1) #define PS_M_VELOCITY (1 << 2) #define PS_M_TIME (1 << 3) #define PS_M_FLAGS (1 << 4) #define PS_M_GRAVITY (1 << 5) #define PS_M_DELTA_ANGLES (1 << 6) #define PS_VIEWOFFSET (1 << 7) #define PS_VIEWANGLES (1 << 8) #define PS_KICKANGLES (1 << 9) #define PS_BLEND (1 << 10) #define PS_FOV (1 << 11) #define PS_WEAPONINDEX (1 << 12) #define PS_WEAPONFRAME (1 << 13) #define PS_RDFLAGS (1 << 14) /*============================================== */ /* user_cmd_t communication */ /* ms and light always sent, the others are optional */ #define CM_ANGLE1 (1 << 0) #define CM_ANGLE2 (1 << 1) #define CM_ANGLE3 (1 << 2) #define CM_FORWARD (1 << 3) #define CM_SIDE (1 << 4) #define CM_UP (1 << 5) #define CM_BUTTONS (1 << 6) #define CM_IMPULSE (1 << 7) /*============================================== */ /* a sound without an ent or pos will be a local only sound */ #define SND_VOLUME (1 << 0) /* a byte */ #define SND_ATTENUATION (1 << 1) /* a byte */ #define SND_POS (1 << 2) /* three coordinates */ #define SND_ENT (1 << 3) /* a short 0-2: channel, 3-12: entity */ #define SND_OFFSET (1 << 4) /* a byte, msec offset from frame start */ #define DEFAULT_SOUND_PACKET_VOLUME 1.0 #define DEFAULT_SOUND_PACKET_ATTENUATION 1.0 /*============================================== */ /* entity_state_t communication */ /* try to pack the common update flags into the first byte */ #define U_ORIGIN1 (1 << 0) #define U_ORIGIN2 (1 << 1) #define U_ANGLE2 (1 << 2) #define U_ANGLE3 (1 << 3) #define U_FRAME8 (1 << 4) /* frame is a byte */ #define U_EVENT (1 << 5) #define U_REMOVE (1 << 6) /* REMOVE this entity, don't add it */ #define U_MOREBITS1 (1 << 7) /* read one additional byte */ /* second byte */ #define U_NUMBER16 (1 << 8) /* NUMBER8 is implicit if not set */ #define U_ORIGIN3 (1 << 9) #define U_ANGLE1 (1 << 10) #define U_MODEL (1 << 11) #define U_RENDERFX8 (1 << 12) /* fullbright, etc */ #define U_EFFECTS8 (1 << 14) /* autorotate, trails, etc */ #define U_MOREBITS2 (1 << 15) /* read one additional byte */ /* third byte */ #define U_SKIN8 (1 << 16) #define U_FRAME16 (1 << 17) /* frame is a short */ #define U_RENDERFX16 (1 << 18) /* 8 + 16 = 32 */ #define U_EFFECTS16 (1 << 19) /* 8 + 16 = 32 */ #define U_MODEL2 (1 << 20) /* weapons, flags, etc */ #define U_MODEL3 (1 << 21) #define U_MODEL4 (1 << 22) #define U_MOREBITS3 (1 << 23) /* read one additional byte */ /* fourth byte */ #define U_OLDORIGIN (1 << 24) #define U_SKIN16 (1 << 25) #define U_SOUND (1 << 26) #define U_SOLID (1 << 27) /* CMD - Command text buffering and command execution */ /* * Any number of commands can be added in a frame, from several different * sources. Most commands come from either keybindings or console line * input, but remote servers can also send across commands and entire text * files can be execed. * * The + command line options are also added to the command buffer. * * The game starts with a Cbuf_AddText ("exec quake.rc\n"); Cbuf_Execute * (); */ #define EXEC_NOW 0 /* don't return until completed */ #define EXEC_INSERT 1 /* insert at current position, but don't run yet */ #define EXEC_APPEND 2 /* add to end of the command buffer */ 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_ExecuteText(int exec_when, char *text); /* this can be used in place of either Cbuf_AddText or Cbuf_InsertText */ void Cbuf_AddEarlyCommands(qboolean clear); /* adds all the +set commands from the command line */ qboolean Cbuf_AddLateCommands(void); /* adds all the remaining + commands from the command line */ /* Returns true if any late commands were added, which */ /* will keep the demoloop from immediately starting */ 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! */ void Cbuf_CopyToDefer(void); void Cbuf_InsertFromDefer(void); /* These two functions are used to defer any pending commands while a map */ /* is being loaded */ /*=================================================================== */ /* * Command execution takes a null terminated string, breaks it into tokens, * then searches for a command or variable that matches the first token. */ typedef void (*xcommand_t)(void); 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 */ /* if function is NULL, the command will be forwarded to the server */ /* as a clc_stringcmd instead of executed locally */ void Cmd_RemoveCommand(char *cmd_name); 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 always safe. */ void Cmd_TokenizeString(char *text, qboolean macroExpand); /* Takes a null terminated string. Does not need to be /n terminated. */ /* breaks the string up into arg tokens. */ void Cmd_ExecuteString(char *text); /* Parses a single line of text into arguments and tries to execute it */ /* as if it was typed at the console */ 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. */ /* CVAR */ /* * 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. * * The user can access cvars from the console in three ways: * r_draworder prints the current value * r_draworder 0 sets the current value to 0 * set r_draworder 0 as above, but creates the cvar if not present * Cvars are restricted from having the same names as commands to keep this * interface from being ambiguous. */ extern cvar_t *cvar_vars; cvar_t *Cvar_Get(char *var_name, char *value, int flags); /* creates the variable if it doesn't exist, or returns the existing one */ /* if it exists, the value will not be changed, but flags will be ORed in */ /* that allows variables to be unarchived without needing bitflags */ cvar_t *Cvar_Set(char *var_name, char *value); /* will create the variable if it doesn't exist */ cvar_t *Cvar_ForceSet(char *var_name, char *value); /* will set the variable even if NOSET or LATCH */ cvar_t *Cvar_FullSet(char *var_name, char *value, int flags); 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 */ const char *Cvar_VariableString(const 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 */ void Cvar_GetLatchedVars(void); /* any CVAR_LATCHED variables that have been set will now take effect */ 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(char *path); /* appends lines containing "set variable value" for all variables */ /* with the archive flag set to true. */ void Cvar_Init(void); char *Cvar_Userinfo(void); /* returns an info string containing all the CVAR_USERINFO cvars */ char *Cvar_Serverinfo(void); /* returns an info string containing all the CVAR_SERVERINFO cvars */ extern qboolean userinfo_modified; /* this is set each time a CVAR_USERINFO variable is changed */ /* so that the client knows to send it to the server */ /* NET */ #define PORT_ANY -1 #define MAX_MSGLEN 1400 /* max length of a message */ #define PACKET_HEADER 10 /* two ints and a short */ typedef enum { NA_LOOPBACK, NA_BROADCAST, NA_IP, NA_IPX, NA_BROADCAST_IPX, NA_IP6, NA_MULTICAST6 } netadrtype_t; typedef enum {NS_CLIENT, NS_SERVER} netsrc_t; typedef struct { netadrtype_t type; byte ip[16]; unsigned int scope_id; byte ipx[10]; unsigned short port; } netadr_t; void NET_Init(void); void NET_Shutdown(void); void NET_Config(qboolean multiplayer); qboolean NET_GetPacket(netsrc_t sock, netadr_t *net_from, sizebuf_t *net_message); void NET_SendPacket(netsrc_t sock, int length, void *data, netadr_t to); qboolean NET_CompareAdr(netadr_t a, netadr_t b); qboolean NET_CompareBaseAdr(netadr_t a, netadr_t b); qboolean NET_IsLocalAddress(netadr_t adr); char *NET_AdrToString(netadr_t a); qboolean NET_StringToAdr(char *s, netadr_t *a); void NET_Sleep(int msec); /*=================================================================== */ #define OLD_AVG 0.99 #define MAX_LATENT 32 typedef struct { qboolean fatal_error; netsrc_t sock; int dropped; /* between last packet and previous */ int last_received; /* for timeouts */ int last_sent; /* for retransmits */ netadr_t remote_address; int qport; /* qport value to write when transmitting */ /* sequencing variables */ int incoming_sequence; int incoming_acknowledged; int incoming_reliable_acknowledged; /* single bit */ int incoming_reliable_sequence; /* single bit, maintained local */ int outgoing_sequence; int reliable_sequence; /* single bit */ int last_reliable_sequence; /* sequence number of last send */ /* reliable staging and holding areas */ sizebuf_t message; /* writing buffer to send to server */ byte message_buf[MAX_MSGLEN - 16]; /* leave space for header */ /* message is copied to this buffer when it is first transfered */ int reliable_length; byte reliable_buf[MAX_MSGLEN - 16]; /* unacked reliable message */ } netchan_t; extern netadr_t net_from; extern sizebuf_t net_message; extern byte net_message_buffer[MAX_MSGLEN]; void Netchan_Init(void); void Netchan_Setup(netsrc_t sock, netchan_t *chan, netadr_t adr, int qport); qboolean Netchan_NeedReliable(netchan_t *chan); void Netchan_Transmit(netchan_t *chan, int length, byte *data); void Netchan_OutOfBand(int net_socket, netadr_t adr, int length, byte *data); void Netchan_OutOfBandPrint(int net_socket, netadr_t adr, char *format, ...); qboolean Netchan_Process(netchan_t *chan, sizebuf_t *msg); qboolean Netchan_CanReliable(netchan_t *chan); /* CMODEL */ #include "files.h" cmodel_t *CM_LoadMap(char *name, qboolean clientload, unsigned *checksum); cmodel_t *CM_InlineModel(char *name); /* *1, *2, etc */ int CM_NumClusters(void); int CM_NumInlineModels(void); char *CM_EntityString(void); /* creates a clipping hull for an arbitrary box */ int CM_HeadnodeForBox(vec3_t mins, vec3_t maxs); /* returns an ORed contents mask */ int CM_PointContents(vec3_t p, int headnode); int CM_TransformedPointContents(vec3_t p, int headnode, vec3_t origin, vec3_t angles); trace_t CM_BoxTrace(vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int headnode, int brushmask); trace_t CM_TransformedBoxTrace(vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int headnode, int brushmask, vec3_t origin, vec3_t angles); byte *CM_ClusterPVS(int cluster); byte *CM_ClusterPHS(int cluster); int CM_PointLeafnum(vec3_t p); /* call with topnode set to the headnode, returns with topnode */ /* set to the first node that splits the box */ int CM_BoxLeafnums(vec3_t mins, vec3_t maxs, int *list, int listsize, int *topnode); int CM_LeafContents(int leafnum); int CM_LeafCluster(int leafnum); int CM_LeafArea(int leafnum); void CM_SetAreaPortalState(int portalnum, qboolean open); qboolean CM_AreasConnected(int area1, int area2); int CM_WriteAreaBits(byte *buffer, int area); qboolean CM_HeadnodeVisible(int headnode, byte *visbits); void CM_WritePortalState(FILE *f); /* PLAYER MOVEMENT CODE */ extern float pm_airaccelerate; void Pmove(pmove_t *pmove); /* FILESYSTEM */ #define SFF_INPACK 0x20 extern int file_from_pak; typedef int fileHandle_t; typedef enum { FS_READ, FS_WRITE, FS_APPEND } fsMode_t; typedef enum { FS_SEEK_CUR, FS_SEEK_SET, FS_SEEK_END } fsOrigin_t; typedef enum { FS_SEARCH_PATH_EXTENSION, FS_SEARCH_BY_FILTER, FS_SEARCH_FULL_PATH } fsSearchType_t; void FS_DPrintf(const char *format, ...); int FS_FOpenFile(const char *name, fileHandle_t *f, qboolean gamedir_only); void FS_FCloseFile(fileHandle_t f); int FS_Read(void *buffer, int size, fileHandle_t f); int FS_FRead(void *buffer, int size, int count, fileHandle_t f); char **FS_ListFiles(char *findname, int *numfiles, unsigned musthave, unsigned canthave); char **FS_ListFiles2(char *findname, int *numfiles, unsigned musthave, unsigned canthave); void FS_FreeList(char **list, int nfiles); void FS_InitFilesystem(void); void FS_SetGamedir(char *dir); char *FS_Gamedir(void); char *FS_NextPath(char *prevpath); void FS_ExecAutoexec(void); int FS_LoadFile(char *path, void **buffer); /* a null buffer will just return the file length without loading */ /* a -1 length is not present */ /* properly handles partial reads */ void FS_FreeFile(void *buffer); void FS_CreatePath(char *path); /* MISC */ #define ERR_FATAL 0 /* exit the entire game with a popup window */ #define ERR_DROP 1 /* print to console and disconnect from game */ #define ERR_QUIT 2 /* not an error, just a normal exit */ #define EXEC_NOW 0 /* don't return until completed */ #define EXEC_INSERT 1 /* insert at current position, but don't run yet */ #define EXEC_APPEND 2 /* add to end of the command buffer */ #define PRINT_ALL 0 #define PRINT_DEVELOPER 1 /* only print when "developer 1" */ void Com_BeginRedirect(int target, char *buffer, int buffersize, void (*flush)); void Com_EndRedirect(void); void Com_Printf(char *fmt, ...); void Com_DPrintf(char *fmt, ...); void Com_MDPrintf(char *fmt, ...); void Com_Error(int code, char *fmt, ...); void Com_Quit(void); int Com_ServerState(void); /* this should have just been a cvar... */ void Com_SetServerState(int state); unsigned Com_BlockChecksum(void *buffer, int length); byte COM_BlockSequenceCRCByte(byte *base, int length, int sequence); extern cvar_t *developer; extern cvar_t *modder; extern cvar_t *dedicated; extern cvar_t *host_speeds; extern cvar_t *log_stats; extern FILE *log_stats_file; /* host_speeds times */ extern int time_before_game; extern int time_after_game; extern int time_before_ref; extern int time_after_ref; void Z_Free(void *ptr); void *Z_Malloc(int size); /* returns 0 filled memory */ void *Z_TagMalloc(int size, int tag); void Z_FreeTags(int tag); void Qcommon_Init(int argc, char **argv); void Qcommon_Frame(int msec); void Qcommon_Shutdown(void); #define NUMVERTEXNORMALS 162 extern vec3_t bytedirs[NUMVERTEXNORMALS]; /* this is in the client code, but can be used for debugging from server */ void SCR_DebugGraph(float value, int color); /* NON-PORTABLE SYSTEM SERVICES */ void Sys_Init(void); void Sys_UnloadGame(void); void *Sys_GetGameAPI(void *parms); char *Sys_ConsoleInput(void); void Sys_ConsoleOutput(char *string); void Sys_SendKeyEvents(void); void Sys_Error(char *error, ...); void Sys_Quit(void); char *Sys_GetHomeDir(void); const char *Sys_GetBinaryDir(void); void Sys_FreeLibrary(void *handle); void *Sys_LoadLibrary(const char *path, const char *sym, void **handle); void *Sys_GetProcAddress(void *handle, const char *sym); /* CLIENT / SERVER SYSTEMS */ void CL_Init(void); void CL_Drop(void); void CL_Shutdown(void); void CL_Frame(int msec); void Con_Print(char *text); void SCR_BeginLoadingPlaque(void); void SV_Init(void); void SV_Shutdown(char *finalmsg, qboolean reconnect); void SV_Frame(int msec); #endif yquake2-QUAKE2_5_32/src/common/header/zone.h0000644000175000017500000000221212615162034021247 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Header file to the zone malloc * * ======================================================================= */ #ifndef CO_ZONE_H #define CO_ZONE_H typedef struct zhead_s { struct zhead_s *prev, *next; short magic; short tag; /* for group free */ int size; } zhead_t; void Z_Stats_f (void); #endif yquake2-QUAKE2_5_32/src/common/header/crc.h0000644000175000017500000000227412615162034021053 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Corresponding header for crc.c * * ======================================================================= */ #ifndef CO_CRC_H #define CO_CRC_H void CRC_Init(unsigned short *crcvalue); void CRC_ProcessByte(unsigned short *crcvalue, byte data); unsigned short CRC_Value(unsigned short crcvalue); unsigned short CRC_Block(byte *start, int count); #endif yquake2-QUAKE2_5_32/src/common/crc.c0000644000175000017500000000733312615162034017617 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 is a 16 bit, non-reflected CRC using the polynomial 0x1021 * and the initial and final xor values shown below... In other words, * the CCITT standard CRC used by XMODEM. * * ======================================================================= */ #include "header/common.h" #define CRC_INIT_VALUE 0xffff #define CRC_XOR_VALUE 0x0000 static unsigned short crctable[256] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 }; void CRC_Init(unsigned short *crcvalue) { *crcvalue = CRC_INIT_VALUE; } void CRC_ProcessByte(unsigned short *crcvalue, byte data) { *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data]; } unsigned short CRC_Value(unsigned short crcvalue) { return crcvalue ^ CRC_XOR_VALUE; } unsigned short CRC_Block(byte *start, int count) { unsigned short crc; CRC_Init(&crc); while (count--) { crc = (crc << 8) ^ crctable[(crc >> 8) ^ *start++]; } return crc; } yquake2-QUAKE2_5_32/src/common/unzip/0000755000175000017500000000000012615162034020043 5ustar greffrathgreffrathyquake2-QUAKE2_5_32/src/common/unzip/ioapi.c0000644000175000017500000000721112615162034021311 0ustar greffrathgreffrath/* ioapi.c -- IO base function header for compress/uncompress .zip files using zlib + zip or unzip API Version 1.01h, December 28th, 2009 Copyright (C) 1998-2009 Gilles Vollant */ #include #include #include #include "zlib.h" #include "ioapi.h" /* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ #ifndef SEEK_CUR #define SEEK_CUR 1 #endif #ifndef SEEK_END #define SEEK_END 2 #endif #ifndef SEEK_SET #define SEEK_SET 0 #endif voidpf ZCALLBACK fopen_file_func OF(( voidpf opaque, const char* filename, int mode)); uLong ZCALLBACK fread_file_func OF(( voidpf opaque, voidpf stream, void* buf, uLong size)); uLong ZCALLBACK fwrite_file_func OF(( voidpf opaque, voidpf stream, const void* buf, uLong size)); long ZCALLBACK ftell_file_func OF(( voidpf opaque, voidpf stream)); long ZCALLBACK fseek_file_func OF(( voidpf opaque, voidpf stream, uLong offset, int origin)); int ZCALLBACK fclose_file_func OF(( voidpf opaque, voidpf stream)); int ZCALLBACK ferror_file_func OF(( voidpf opaque, voidpf stream)); voidpf ZCALLBACK fopen_file_func (opaque, filename, mode) voidpf opaque; const char* filename; int mode; { FILE* file = NULL; const char* mode_fopen = NULL; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) mode_fopen = "rb"; else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) mode_fopen = "r+b"; else if (mode & ZLIB_FILEFUNC_MODE_CREATE) mode_fopen = "wb"; if ((filename!=NULL) && (mode_fopen != NULL)) file = fopen(filename, mode_fopen); return file; } uLong ZCALLBACK fread_file_func (opaque, stream, buf, size) voidpf opaque; voidpf stream; void* buf; uLong size; { uLong ret; ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); return ret; } uLong ZCALLBACK fwrite_file_func (opaque, stream, buf, size) voidpf opaque; voidpf stream; const void* buf; uLong size; { uLong ret; ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); return ret; } long ZCALLBACK ftell_file_func (opaque, stream) voidpf opaque; voidpf stream; { long ret; ret = ftell((FILE *)stream); return ret; } long ZCALLBACK fseek_file_func (opaque, stream, offset, origin) voidpf opaque; voidpf stream; uLong offset; int origin; { int fseek_origin=0; long ret; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR : fseek_origin = SEEK_CUR; break; case ZLIB_FILEFUNC_SEEK_END : fseek_origin = SEEK_END; break; case ZLIB_FILEFUNC_SEEK_SET : fseek_origin = SEEK_SET; break; default: return -1; } ret = 0; if (fseek((FILE *)stream, offset, fseek_origin) != 0) ret = -1; return ret; } int ZCALLBACK fclose_file_func (opaque, stream) voidpf opaque; voidpf stream; { int ret; ret = fclose((FILE *)stream); return ret; } int ZCALLBACK ferror_file_func (opaque, stream) voidpf opaque; voidpf stream; { int ret; ret = ferror((FILE *)stream); return ret; } void fill_fopen_filefunc (pzlib_filefunc_def) zlib_filefunc_def* pzlib_filefunc_def; { pzlib_filefunc_def->zopen_file = fopen_file_func; pzlib_filefunc_def->zread_file = fread_file_func; pzlib_filefunc_def->zwrite_file = fwrite_file_func; pzlib_filefunc_def->ztell_file = ftell_file_func; pzlib_filefunc_def->zseek_file = fseek_file_func; pzlib_filefunc_def->zclose_file = fclose_file_func; pzlib_filefunc_def->zerror_file = ferror_file_func; pzlib_filefunc_def->opaque = NULL; } yquake2-QUAKE2_5_32/src/common/unzip/unzip.h0000644000175000017500000003135312615162034021366 0ustar greffrathgreffrath/* unzip.h -- IO for uncompress .zip files using zlib Version 1.01h, December 28th, 2009 Copyright (C) 1998-2009 Gilles Vollant This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g WinZip, InfoZip tools and compatible. Multi volume ZipFile (span) are not supported. Encryption compatible with pkzip 2.04g only supported Old compressions used by old PKZip 1.x are not supported I WAIT FEEDBACK at mail info@winimage.com Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution Condition of use and distribution are the same than zlib : This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* for more info about .ZIP format, see http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip http://www.info-zip.org/pub/infozip/doc/ PkWare has also a specification at : ftp://ftp.pkware.com/probdesc.zip */ #ifndef _unz_H #define _unz_H #ifdef __cplusplus extern "C" { #endif #ifndef _ZLIB_H #include "zlib.h" #endif #ifndef _ZLIBIOAPI_H #include "ioapi.h" #endif #ifdef HAVE_BZIP2 #include "bzlib.h" #endif #define Z_BZIP2ED 12 #if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) /* like the STRICT of WIN32, we define a pointer that cannot be converted from (void*) without cast */ typedef struct TagunzFile__ { int unused; } unzFile__; typedef unzFile__ *unzFile; #else typedef voidp unzFile; #endif #define UNZ_OK (0) #define UNZ_END_OF_LIST_OF_FILE (-100) #define UNZ_ERRNO (Z_ERRNO) #define UNZ_EOF (0) #define UNZ_PARAMERROR (-102) #define UNZ_BADZIPFILE (-103) #define UNZ_INTERNALERROR (-104) #define UNZ_CRCERROR (-105) /* tm_unz contain date/time info */ typedef struct tm_unz_s { uInt tm_sec; /* seconds after the minute - [0,59] */ uInt tm_min; /* minutes after the hour - [0,59] */ uInt tm_hour; /* hours since midnight - [0,23] */ uInt tm_mday; /* day of the month - [1,31] */ uInt tm_mon; /* months since January - [0,11] */ uInt tm_year; /* years - [1980..2044] */ } tm_unz; /* unz_global_info structure contain global data about the ZIPfile These data comes from the end of central dir */ typedef struct unz_global_info_s { uLong number_entry; /* total number of entries in the central dir on this disk */ uLong size_comment; /* size of the global comment of the zipfile */ } unz_global_info; /* unz_file_info contain information about a file in the zipfile */ typedef struct unz_file_info_s { uLong version; /* version made by 2 bytes */ uLong version_needed; /* version needed to extract 2 bytes */ uLong flag; /* general purpose bit flag 2 bytes */ uLong compression_method; /* compression method 2 bytes */ uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ uLong crc; /* crc-32 4 bytes */ uLong compressed_size; /* compressed size 4 bytes */ uLong uncompressed_size; /* uncompressed size 4 bytes */ uLong size_filename; /* filename length 2 bytes */ uLong size_file_extra; /* extra field length 2 bytes */ uLong size_file_comment; /* file comment length 2 bytes */ uLong disk_num_start; /* disk number start 2 bytes */ uLong internal_fa; /* internal file attributes 2 bytes */ uLong external_fa; /* external file attributes 4 bytes */ tm_unz tmu_date; } unz_file_info; extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, const char* fileName2, int iCaseSensitivity)); /* Compare two filename (fileName1,fileName2). If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is defaut of your operating system (like 1 on Unix, 2 on Windows) */ extern unzFile ZEXPORT unzOpen OF((const char *path)); /* Open a Zip file. path contain the full pathname (by example, on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer "zlib/zlib113.zip". If the zipfile cannot be opened (file don't exist or in not valid), the return value is NULL. Else, the return value is a unzFile Handle, usable with other function of this unzip package. */ extern unzFile ZEXPORT unzOpen2 OF((const char *path, zlib_filefunc_def* pzlib_filefunc_def)); /* Open a Zip file, like unzOpen, but provide a set of file low level API for read/write the zip file (see ioapi.h) */ extern int ZEXPORT unzClose OF((unzFile file)); /* Close a ZipFile opened with unzipOpen. If there is files inside the .Zip opened with unzOpenCurrentFile (see later), these files MUST be closed with unzipCloseCurrentFile before call unzipClose. return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, unz_global_info *pglobal_info)); /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalComment OF((unzFile file, char *szComment, uLong uSizeBuf)); /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. return the number of byte copied or an error code <0 */ /***************************************************************************/ /* Unzip package allow you browse the directory of the zipfile */ extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int ZEXPORT unzGoToNextFile OF((unzFile file)); /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ extern int ZEXPORT unzLocateFile OF((unzFile file, const char *szFileName, int iCaseSensitivity)); /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ /* ****************************************** */ /* Ryan supplied functions */ /* unz_file_info contain information about a file in the zipfile */ typedef struct unz_file_pos_s { uLong pos_in_zip_directory; /* offset in zip file directory */ uLong num_of_file; /* # of file */ } unz_file_pos; extern int ZEXPORT unzGetFilePos( unzFile file, unz_file_pos* file_pos); extern int ZEXPORT unzGoToFilePos( unzFile file, unz_file_pos* file_pos); /* ****************************************** */ extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, unz_file_info *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)); /* Get Info about the current file if pfile_info!=NULL, the *pfile_info structure will contain somes info about the current file if szFileName!=NULL, the filemane string will be copied in szFileName (fileNameBufferSize is the size of the buffer) if extraField!=NULL, the extra field information will be copied in extraField (extraFieldBufferSize is the size of the buffer). This is the Central-header version of the extra field if szComment!=NULL, the comment string of the file will be copied in szComment (commentBufferSize is the size of the buffer) */ /***************************************************************************/ /* for reading the content of the current zipfile, you can open it, read data from it, and close it (you can close it before reading all the file) */ extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); /* Open for reading data the current file in the zipfile. If there is no error, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, const char* password)); /* Open for reading data the current file in the zipfile. password is a crypting password If there is no error, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, int* method, int* level, int raw)); /* Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) if raw==1 *method will receive method of compression, *level will receive level of compression note : you can set level parameter as NULL (if you did not want known level, but you CANNOT set method parameter as NULL */ extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, int* method, int* level, int raw, const char* password)); /* Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) if raw==1 *method will receive method of compression, *level will receive level of compression note : you can set level parameter as NULL (if you did not want known level, but you CANNOT set method parameter as NULL */ extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); /* Close the file in zip opened with unzOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int ZEXPORT unzReadCurrentFile OF((unzFile file, voidp buf, unsigned len)); /* Read bytes from the current file (opened by unzOpenCurrentFile) buf contain buffer where data must be copied len the size of buf. return the number of byte copied if somes bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ extern z_off_t ZEXPORT unztell OF((unzFile file)); /* Give the current position in uncompressed data */ extern int ZEXPORT unzeof OF((unzFile file)); /* return 1 if the end of file was reached, 0 elsewhere */ extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, voidp buf, unsigned len)); /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the local-header version of the extra field (sometimes, there is more info in the local-header version than in the central-header) if buf==NULL, it return the size of the local extra field if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. the return value is the number of bytes copied in buf, or (if <0) the error code */ /***************************************************************************/ /* Get the current file offset */ extern uLong ZEXPORT unzGetOffset (unzFile file); /* Set the current file offset */ extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); #ifdef __cplusplus } #endif #endif /* _unz_H */ yquake2-QUAKE2_5_32/src/common/unzip/unzip.c0000644000175000017500000014772212615162034021371 0ustar greffrathgreffrath/* unzip.c -- IO for uncompress .zip files using zlib Version 1.01h, December 28th, 2009 Copyright (C) 1998-2009 Gilles Vollant Read unzip.h for more info */ /* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of compatibility with older software. The following is from the original crypt.c. Code woven in by Terry Thorsen 1/2003. */ /* Copyright (c) 1990-2000 Info-ZIP. All rights reserved. See the accompanying file LICENSE, version 2000-Apr-09 or later (the contents of which are also included in zip.h) for terms of use. If, for some reason, all these files are missing, the Info-ZIP license also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html */ /* crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] The encryption/decryption parts of this source code (as opposed to the non-echoing password parts) were originally written in Europe. The whole source package can be freely distributed, including from the USA. (Prior to January 2000, re-export from the US was a violation of US law.) */ /* This encryption code is a direct transcription of the algorithm from Roger Schlafly, described by Phil Katz in the file appnote.txt. This file (appnote.txt) is distributed with the PKZIP program (even in the version without encryption capabilities). */ #include #include #include #include "zlib.h" #include "unzip.h" #ifdef STDC # include # include # include #endif #ifdef NO_ERRNO_H extern int errno; #else # include #endif #ifndef local # define local static #endif /* compile with -Dlocal if your debugger can't find static symbols */ #ifndef CASESENSITIVITYDEFAULT_NO # if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) # define CASESENSITIVITYDEFAULT_NO # endif #endif #ifndef UNZ_BUFSIZE #define UNZ_BUFSIZE (16384) #endif #ifndef UNZ_MAXFILENAMEINZIP #define UNZ_MAXFILENAMEINZIP (256) #endif #ifndef ALLOC # define ALLOC(size) (malloc(size)) #endif #ifndef TRYFREE # define TRYFREE(p) {if (p) free(p);} #endif #define SIZECENTRALDIRITEM (0x2e) #define SIZEZIPLOCALHEADER (0x1e) const char unz_copyright[] = " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; /* unz_file_info_interntal contain internal info about a file in zipfile*/ typedef struct unz_file_info_internal_s { uLong offset_curfile;/* relative offset of local header 4 bytes */ } unz_file_info_internal; /* file_in_zip_read_info_s contain internal information about a file in zipfile, when reading and decompress it */ typedef struct { char *read_buffer; /* internal buffer for compressed data */ z_stream stream; /* zLib stream structure for inflate */ #ifdef HAVE_BZIP2 bz_stream bstream; /* bzLib stream structure for bziped */ #endif uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ uLong stream_initialised; /* flag set if stream structure is initialised*/ uLong offset_local_extrafield;/* offset of the local extra field */ uInt size_local_extrafield;/* size of the local extra field */ uLong pos_local_extrafield; /* position in the local extra field in read*/ uLong crc32; /* crc32 of all data uncompressed */ uLong crc32_wait; /* crc32 we must obtain after decompress all */ uLong rest_read_compressed; /* number of byte to be decompressed */ uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ zlib_filefunc_def z_filefunc; voidpf filestream; /* io structore of the zipfile */ uLong compression_method; /* compression method (0==store) */ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ int raw; } file_in_zip_read_info_s; /* unz_s contain internal information about the zipfile */ typedef struct { zlib_filefunc_def z_filefunc; voidpf filestream; /* io structore of the zipfile */ unz_global_info gi; /* public global information */ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ uLong num_file; /* number of the current file in the zipfile*/ uLong pos_in_central_dir; /* pos of the current file in the central dir*/ uLong current_file_ok; /* flag about the usability of the current file*/ uLong central_pos; /* position of the beginning of the central dir*/ uLong size_central_dir; /* size of the central directory */ uLong offset_central_dir; /* offset of start of central directory with respect to the starting disk number */ unz_file_info cur_file_info; /* public info about the current file in zip*/ unz_file_info_internal cur_file_info_internal; /* private info about it*/ file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current file if we are decompressing it */ int encrypted; # ifndef NOUNCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ const unsigned long* pcrc_32_tab; # endif } unz_s; #ifndef NOUNCRYPT #include "crypt.h" #endif /* =========================================================================== Read a byte from a gz_stream; update next_in and avail_in. Return EOF for end of file. IN assertion: the stream s has been sucessfully opened for reading. */ local int unzlocal_getByte OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream, int *pi)); local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; int *pi; { unsigned char c; int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); if (err==1) { *pi = (int)c; return UNZ_OK; } else { if (ZERROR(*pzlib_filefunc_def,filestream)) return UNZ_ERRNO; else return UNZ_EOF; } } /* =========================================================================== Reads a long in LSB order from the given gz_stream. Sets */ local int unzlocal_getShort OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; uLong *pX; { uLong x ; int i = 0; int err; err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==UNZ_OK) err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<8; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; } local int unzlocal_getLong OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; uLong *pX; { uLong x ; int i = 0; int err; err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x = (uLong)i; if (err==UNZ_OK) err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<8; if (err==UNZ_OK) err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<16; if (err==UNZ_OK) err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); x += ((uLong)i)<<24; if (err==UNZ_OK) *pX = x; else *pX = 0; return err; } /* My own strcmpi / strcasecmp */ local int strcmpcasenosensitive_internal (fileName1,fileName2) const char* fileName1; const char* fileName2; { for (;;) { char c1=*(fileName1++); char c2=*(fileName2++); if ((c1>='a') && (c1<='z')) c1 -= 0x20; if ((c2>='a') && (c2<='z')) c2 -= 0x20; if (c1=='\0') return ((c2=='\0') ? 0 : -1); if (c2=='\0') return 1; if (c1c2) return 1; } } #ifdef CASESENSITIVITYDEFAULT_NO #define CASESENSITIVITYDEFAULTVALUE 2 #else #define CASESENSITIVITYDEFAULTVALUE 1 #endif #ifndef STRCMPCASENOSENTIVEFUNCTION #define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal #endif /* Compare two filename (fileName1,fileName2). If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is defaut of your operating system (like 1 on Unix, 2 on Windows) */ extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) const char* fileName1; const char* fileName2; int iCaseSensitivity; { if (iCaseSensitivity==0) iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); } #ifndef BUFREADCOMMENT #define BUFREADCOMMENT (0x400) #endif /* Locate the Central directory of a zipfile (at the end, just before the global comment) */ local uLong unzlocal_SearchCentralDir OF(( const zlib_filefunc_def* pzlib_filefunc_def, voidpf filestream)); local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream) const zlib_filefunc_def* pzlib_filefunc_def; voidpf filestream; { unsigned char* buf; uLong uSizeFile; uLong uBackRead; uLong uMaxBack=0xffff; /* maximum size of global comment */ uLong uPosFound=0; if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return 0; uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return 0; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) break; for (i=(int)uReadSize-3; (i--)>0;) if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) { uPosFound = uReadPos+i; break; } if (uPosFound!=0) break; } TRYFREE(buf); return uPosFound; } /* Open a Zip file. path contain the full pathname (by example, on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer "zlib/zlib114.zip". If the zipfile cannot be opened (file doesn't exist or in not valid), the return value is NULL. Else, the return value is a unzFile Handle, usable with other function of this unzip package. */ extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def) const char *path; zlib_filefunc_def* pzlib_filefunc_def; { unz_s us; unz_s *s; uLong central_pos,uL; uLong number_disk; /* number of the current dist, used for spaning ZIP, unsupported, always 0*/ uLong number_disk_with_CD; /* number the the disk with central dir, used for spaning ZIP, unsupported, always 0*/ uLong number_entry_CD; /* total number of entries in the central dir (same than number_entry on nospan) */ int err=UNZ_OK; if (unz_copyright[0]!=' ') return NULL; if (pzlib_filefunc_def==NULL) fill_fopen_filefunc(&us.z_filefunc); else us.z_filefunc = *pzlib_filefunc_def; us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque, path, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING); if (us.filestream==NULL) return NULL; central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream); if (central_pos==0) err=UNZ_ERRNO; if (ZSEEK(us.z_filefunc, us.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* the signature, already checked */ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; /* number of this disk */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; /* number of the disk with the start of the central directory */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central dir on this disk */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central dir */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; /* size of the central directory */ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; /* offset of start of central directory with respect to the starting disk number */ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; /* zipfile comment length */ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; if ((central_pospfile_in_zip_read!=NULL) unzCloseCurrentFile(file); ZCLOSE(s->z_filefunc, s->filestream); TRYFREE(s); return UNZ_OK; } /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) unzFile file; unz_global_info *pglobal_info; { unz_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; *pglobal_info=s->gi; return UNZ_OK; } /* Translate date/time from Dos format to tm_unz (readable more easilty) */ local void unzlocal_DosDateToTmuDate (ulDosDate, ptm) uLong ulDosDate; tm_unz* ptm; { uLong uDate; uDate = (uLong)(ulDosDate>>16); ptm->tm_mday = (uInt)(uDate&0x1f) ; ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; } /* Get Info about the current file in the zipfile, with internal only info */ local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, unz_file_info *pfile_info, unz_file_info_internal *pfile_info_internal, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)); local int unzlocal_GetCurrentFileInfoInternal (file, pfile_info, pfile_info_internal, szFileName, fileNameBufferSize, extraField, extraFieldBufferSize, szComment, commentBufferSize) unzFile file; unz_file_info *pfile_info; unz_file_info_internal *pfile_info_internal; char *szFileName; uLong fileNameBufferSize; void *extraField; uLong extraFieldBufferSize; char *szComment; uLong commentBufferSize; { unz_s* s; unz_file_info file_info; unz_file_info_internal file_info_internal; int err=UNZ_OK; uLong uMagic; long lSeek=0; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (ZSEEK(s->z_filefunc, s->filestream, s->pos_in_central_dir+s->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* we check the magic */ if (err==UNZ_OK) { if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x02014b50) err=UNZ_BADZIPFILE; } if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) err=UNZ_ERRNO; unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) err=UNZ_ERRNO; lSeek+=file_info.size_filename; if ((err==UNZ_OK) && (szFileName!=NULL)) { uLong uSizeRead ; if (file_info.size_filename0) && (fileNameBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek -= uSizeRead; } if ((err==UNZ_OK) && (extraField!=NULL)) { uLong uSizeRead ; if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek += file_info.size_file_extra - uSizeRead; } else lSeek+=file_info.size_file_extra; if ((err==UNZ_OK) && (szComment!=NULL)) { uLong uSizeRead ; if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_comment>0) && (commentBufferSize>0)) if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek+=file_info.size_file_comment - uSizeRead; } else lSeek+=file_info.size_file_comment; if ((err==UNZ_OK) && (pfile_info!=NULL)) *pfile_info=file_info; if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) *pfile_info_internal=file_info_internal; return err; } /* Write info about the ZipFile in the *pglobal_info structure. No preparation of the structure is needed return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetCurrentFileInfo (file, pfile_info, szFileName, fileNameBufferSize, extraField, extraFieldBufferSize, szComment, commentBufferSize) unzFile file; unz_file_info *pfile_info; char *szFileName; uLong fileNameBufferSize; void *extraField; uLong extraFieldBufferSize; char *szComment; uLong commentBufferSize; { return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, szFileName,fileNameBufferSize, extraField,extraFieldBufferSize, szComment,commentBufferSize); } /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int ZEXPORT unzGoToFirstFile (file) unzFile file; { int err=UNZ_OK; unz_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; s->pos_in_central_dir=s->offset_central_dir; s->num_file=0; err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } /* Set the current file of the zipfile to the next file. return UNZ_OK if there is no problem return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. */ extern int ZEXPORT unzGoToNextFile (file) unzFile file; { unz_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ if (s->num_file+1==s->gi.number_entry) return UNZ_END_OF_LIST_OF_FILE; s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; s->num_file++; err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } /* Try locate the file szFileName in the zipfile. For the iCaseSensitivity signification, see unzipStringFileNameCompare return value : UNZ_OK if the file is found. It becomes the current file. UNZ_END_OF_LIST_OF_FILE if the file is not found */ extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) unzFile file; const char *szFileName; int iCaseSensitivity; { unz_s* s; int err; /* We remember the 'current' position in the file so that we can jump * back there if we fail. */ unz_file_info cur_file_infoSaved; unz_file_info_internal cur_file_info_internalSaved; uLong num_fileSaved; uLong pos_in_central_dirSaved; if (file==NULL) return UNZ_PARAMERROR; if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; /* Save the current state */ num_fileSaved = s->num_file; pos_in_central_dirSaved = s->pos_in_central_dir; cur_file_infoSaved = s->cur_file_info; cur_file_info_internalSaved = s->cur_file_info_internal; err = unzGoToFirstFile(file); while (err == UNZ_OK) { char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; err = unzGetCurrentFileInfo(file,NULL, szCurrentFileName,sizeof(szCurrentFileName)-1, NULL,0,NULL,0); if (err == UNZ_OK) { if (unzStringFileNameCompare(szCurrentFileName, szFileName,iCaseSensitivity)==0) return UNZ_OK; err = unzGoToNextFile(file); } } /* We failed, so restore the state of the 'current file' to where we * were. */ s->num_file = num_fileSaved ; s->pos_in_central_dir = pos_in_central_dirSaved ; s->cur_file_info = cur_file_infoSaved; s->cur_file_info_internal = cur_file_info_internalSaved; return err; } /* /////////////////////////////////////////// // Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) // I need random access // // Further optimization could be realized by adding an ability // to cache the directory in memory. The goal being a single // comprehensive file read to put the file I need in a memory. */ /* typedef struct unz_file_pos_s { uLong pos_in_zip_directory; // offset in file uLong num_of_file; // # of file } unz_file_pos; */ extern int ZEXPORT unzGetFilePos(file, file_pos) unzFile file; unz_file_pos* file_pos; { unz_s* s; if (file==NULL || file_pos==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_END_OF_LIST_OF_FILE; file_pos->pos_in_zip_directory = s->pos_in_central_dir; file_pos->num_of_file = s->num_file; return UNZ_OK; } extern int ZEXPORT unzGoToFilePos(file, file_pos) unzFile file; unz_file_pos* file_pos; { unz_s* s; int err; if (file==NULL || file_pos==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; /* jump to the right spot */ s->pos_in_central_dir = file_pos->pos_in_zip_directory; s->num_file = file_pos->num_of_file; /* set the current file */ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); /* return results */ s->current_file_ok = (err == UNZ_OK); return err; } /* // Unzip Helper Functions - should be here? /////////////////////////////////////////// */ /* Read the local header of the current zipfile Check the coherency of the local header and info in the end of central directory about this file store in *piSizeVar the size of extra info in local header (filename and size of extra field data) */ local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, poffset_local_extrafield, psize_local_extrafield) unz_s* s; uInt* piSizeVar; uLong *poffset_local_extrafield; uInt *psize_local_extrafield; { uLong uMagic,uData,uFlags; uLong size_filename; uLong size_extra_field; int err=UNZ_OK; *piSizeVar = 0; *poffset_local_extrafield = 0; *psize_local_extrafield = 0; if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (err==UNZ_OK) { if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x04034b50) err=UNZ_BADZIPFILE; } if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; /* else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) err=UNZ_BADZIPFILE; */ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) err=UNZ_ERRNO; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) err=UNZ_BADZIPFILE; if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && /* #ifdef HAVE_BZIP2 */ (s->cur_file_info.compression_method!=Z_BZIP2ED) && /* #endif */ (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ err=UNZ_ERRNO; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ err=UNZ_ERRNO; else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) err=UNZ_ERRNO; else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) err=UNZ_BADZIPFILE; *piSizeVar += (uInt)size_filename; if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) err=UNZ_ERRNO; *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + size_filename; *psize_local_extrafield = (uInt)size_extra_field; *piSizeVar += (uInt)size_extra_field; return err; } /* Open for reading data the current file in the zipfile. If there is no error and the file is opened, the return value is UNZ_OK. */ extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) unzFile file; int* method; int* level; int raw; const char* password; { int err=UNZ_OK; uInt iSizeVar; unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; uLong offset_local_extrafield; /* offset of the local extra field */ uInt size_local_extrafield; /* size of the local extra field */ # ifndef NOUNCRYPT char source[12]; # else if (password != NULL) return UNZ_PARAMERROR; # endif if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; if (!s->current_file_ok) return UNZ_PARAMERROR; if (s->pfile_in_zip_read != NULL) unzCloseCurrentFile(file); if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) return UNZ_BADZIPFILE; pfile_in_zip_read_info = (file_in_zip_read_info_s*) ALLOC(sizeof(file_in_zip_read_info_s)); if (pfile_in_zip_read_info==NULL) return UNZ_INTERNALERROR; pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; pfile_in_zip_read_info->pos_local_extrafield=0; pfile_in_zip_read_info->raw=raw; if (pfile_in_zip_read_info->read_buffer==NULL) { TRYFREE(pfile_in_zip_read_info); return UNZ_INTERNALERROR; } pfile_in_zip_read_info->stream_initialised=0; if (method!=NULL) *method = (int)s->cur_file_info.compression_method; if (level!=NULL) { *level = 6; switch (s->cur_file_info.flag & 0x06) { case 6 : *level = 1; break; case 4 : *level = 2; break; case 2 : *level = 9; break; } } if ((s->cur_file_info.compression_method!=0) && /* #ifdef HAVE_BZIP2 */ (s->cur_file_info.compression_method!=Z_BZIP2ED) && /* #endif */ (s->cur_file_info.compression_method!=Z_DEFLATED)) err=UNZ_BADZIPFILE; pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; pfile_in_zip_read_info->crc32=0; pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; pfile_in_zip_read_info->filestream=s->filestream; pfile_in_zip_read_info->z_filefunc=s->z_filefunc; pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; pfile_in_zip_read_info->stream.total_out = 0; if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) { #ifdef HAVE_BZIP2 pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; pfile_in_zip_read_info->bstream.bzfree = (free_func)0; pfile_in_zip_read_info->bstream.opaque = (voidpf)0; pfile_in_zip_read_info->bstream.state = (voidpf)0; pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; pfile_in_zip_read_info->stream.zfree = (free_func)0; pfile_in_zip_read_info->stream.opaque = (voidpf)0; pfile_in_zip_read_info->stream.next_in = (voidpf)0; pfile_in_zip_read_info->stream.avail_in = 0; err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); if (err == Z_OK) pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; else { TRYFREE(pfile_in_zip_read_info); return err; } #else pfile_in_zip_read_info->raw=1; #endif } else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) { pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; pfile_in_zip_read_info->stream.zfree = (free_func)0; pfile_in_zip_read_info->stream.opaque = (voidpf)0; pfile_in_zip_read_info->stream.next_in = (voidpf)0; pfile_in_zip_read_info->stream.avail_in = 0; err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); if (err == Z_OK) pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; else { TRYFREE(pfile_in_zip_read_info); return err; } /* windowBits is passed < 0 to tell that there is no zlib header. * Note that in this case inflate *requires* an extra "dummy" byte * after the compressed stream in order to complete decompression and * return Z_STREAM_END. * In unzip, i don't wait absolutely Z_STREAM_END because I known the * size of both compressed and uncompressed data */ } pfile_in_zip_read_info->rest_read_compressed = s->cur_file_info.compressed_size ; pfile_in_zip_read_info->rest_read_uncompressed = s->cur_file_info.uncompressed_size ; pfile_in_zip_read_info->pos_in_zipfile = s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + iSizeVar; pfile_in_zip_read_info->stream.avail_in = (uInt)0; s->pfile_in_zip_read = pfile_in_zip_read_info; s->encrypted = 0; # ifndef NOUNCRYPT if (password != NULL) { int i; s->pcrc_32_tab = get_crc_table(); init_keys(password,s->keys,s->pcrc_32_tab); if (ZSEEK(s->z_filefunc, s->filestream, s->pfile_in_zip_read->pos_in_zipfile + s->pfile_in_zip_read->byte_before_the_zipfile, SEEK_SET)!=0) return UNZ_INTERNALERROR; if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12) return UNZ_INTERNALERROR; for (i = 0; i<12; i++) zdecode(s->keys,s->pcrc_32_tab,source[i]); s->pfile_in_zip_read->pos_in_zipfile+=12; s->encrypted=1; } # endif return UNZ_OK; } extern int ZEXPORT unzOpenCurrentFile (file) unzFile file; { return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); } extern int ZEXPORT unzOpenCurrentFilePassword (file, password) unzFile file; const char* password; { return unzOpenCurrentFile3(file, NULL, NULL, 0, password); } extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw) unzFile file; int* method; int* level; int raw; { return unzOpenCurrentFile3(file, method, level, raw, NULL); } /* Read bytes from the current file. buf contain buffer where data must be copied len the size of buf. return the number of byte copied if somes bytes are copied return 0 if the end of file was reached return <0 with error code if there is an error (UNZ_ERRNO for IO error, or zLib error for uncompress error) */ extern int ZEXPORT unzReadCurrentFile (file, buf, len) unzFile file; voidp buf; unsigned len; { int err=UNZ_OK; uInt iRead = 0; unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if (pfile_in_zip_read_info->read_buffer == NULL) return UNZ_END_OF_LIST_OF_FILE; if (len==0) return 0; pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; pfile_in_zip_read_info->stream.avail_out = (uInt)len; if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && (!(pfile_in_zip_read_info->raw))) pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; if ((len>pfile_in_zip_read_info->rest_read_compressed+ pfile_in_zip_read_info->stream.avail_in) && (pfile_in_zip_read_info->raw)) pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_compressed+ pfile_in_zip_read_info->stream.avail_in; while (pfile_in_zip_read_info->stream.avail_out>0) { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) { uInt uReadThis = UNZ_BUFSIZE; if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; if (uReadThis == 0) return UNZ_EOF; if (ZSEEK(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (ZREAD(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->read_buffer, uReadThis)!=uReadThis) return UNZ_ERRNO; # ifndef NOUNCRYPT if(s->encrypted) { uInt i; for(i=0;iread_buffer[i] = zdecode(s->keys,s->pcrc_32_tab, pfile_in_zip_read_info->read_buffer[i]); } # endif pfile_in_zip_read_info->pos_in_zipfile += uReadThis; pfile_in_zip_read_info->rest_read_compressed-=uReadThis; pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->read_buffer; pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; } if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) { uInt uDoCopy,i ; if ((pfile_in_zip_read_info->stream.avail_in == 0) && (pfile_in_zip_read_info->rest_read_compressed == 0)) return (iRead==0) ? UNZ_EOF : iRead; if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) uDoCopy = pfile_in_zip_read_info->stream.avail_out ; else uDoCopy = pfile_in_zip_read_info->stream.avail_in ; for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, pfile_in_zip_read_info->stream.next_out, uDoCopy); pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; pfile_in_zip_read_info->stream.avail_in -= uDoCopy; pfile_in_zip_read_info->stream.avail_out -= uDoCopy; pfile_in_zip_read_info->stream.next_out += uDoCopy; pfile_in_zip_read_info->stream.next_in += uDoCopy; pfile_in_zip_read_info->stream.total_out += uDoCopy; iRead += uDoCopy; } else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) { #ifdef HAVE_BZIP2 uLong uTotalOutBefore,uTotalOutAfter; const Bytef *bufBefore; uLong uOutThis; pfile_in_zip_read_info->bstream.next_in = pfile_in_zip_read_info->stream.next_in; pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; pfile_in_zip_read_info->bstream.total_in_hi32 = 0; pfile_in_zip_read_info->bstream.next_out = pfile_in_zip_read_info->stream.next_out; pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; pfile_in_zip_read_info->bstream.total_out_hi32 = 0; uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; bufBefore = pfile_in_zip_read_info->bstream.next_out; err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; uOutThis = uTotalOutAfter-uTotalOutBefore; pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); pfile_in_zip_read_info->stream.next_in = pfile_in_zip_read_info->bstream.next_in; pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; pfile_in_zip_read_info->stream.next_out = pfile_in_zip_read_info->bstream.next_out; pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; if (err==BZ_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; if (err!=BZ_OK) break; #endif } else { uLong uTotalOutBefore,uTotalOutAfter; const Bytef *bufBefore; uLong uOutThis; int flush=Z_SYNC_FLUSH; uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; bufBefore = pfile_in_zip_read_info->stream.next_out; /* if ((pfile_in_zip_read_info->rest_read_uncompressed == pfile_in_zip_read_info->stream.avail_out) && (pfile_in_zip_read_info->rest_read_compressed == 0)) flush = Z_FINISH; */ err=inflate(&pfile_in_zip_read_info->stream,flush); if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) err = Z_DATA_ERROR; uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; uOutThis = uTotalOutAfter-uTotalOutBefore; pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; if (err!=Z_OK) break; } } if (err==Z_OK) return iRead; return err; } /* Give the current position in uncompressed data */ extern z_off_t ZEXPORT unztell (file) unzFile file; { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; return (z_off_t)pfile_in_zip_read_info->stream.total_out; } /* return 1 if the end of file was reached, 0 elsewhere */ extern int ZEXPORT unzeof (file) unzFile file; { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if (pfile_in_zip_read_info->rest_read_uncompressed == 0) return 1; else return 0; } /* Read extra field from the current file (opened by unzOpenCurrentFile) This is the local-header version of the extra field (sometimes, there is more info in the local-header version than in the central-header) if buf==NULL, it return the size of the local extra field that can be read if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. the return value is the number of bytes copied in buf, or (if <0) the error code */ extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) unzFile file; voidp buf; unsigned len; { unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; uInt read_now; uLong size_to_read; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; size_to_read = (pfile_in_zip_read_info->size_local_extrafield - pfile_in_zip_read_info->pos_local_extrafield); if (buf==NULL) return (int)size_to_read; if (len>size_to_read) read_now = (uInt)size_to_read; else read_now = (uInt)len ; if (read_now==0) return 0; if (ZSEEK(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield, ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (ZREAD(pfile_in_zip_read_info->z_filefunc, pfile_in_zip_read_info->filestream, buf,read_now)!=read_now) return UNZ_ERRNO; return (int)read_now; } /* Close the file in zip opened with unzipOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int ZEXPORT unzCloseCurrentFile (file) unzFile file; { int err=UNZ_OK; unz_s* s; file_in_zip_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && (!pfile_in_zip_read_info->raw)) { if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) err=UNZ_CRCERROR; } TRYFREE(pfile_in_zip_read_info->read_buffer); pfile_in_zip_read_info->read_buffer = NULL; if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) inflateEnd(&pfile_in_zip_read_info->stream); #ifdef HAVE_BZIP2 else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); #endif pfile_in_zip_read_info->stream_initialised = 0; TRYFREE(pfile_in_zip_read_info); s->pfile_in_zip_read=NULL; return err; } /* Get the global comment string of the ZipFile, in the szComment buffer. uSizeBuf is the size of the szComment buffer. return the number of byte copied or an error code <0 */ extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) unzFile file; char *szComment; uLong uSizeBuf; { unz_s* s; uLong uReadThis ; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; uReadThis = uSizeBuf; if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (uReadThis>0) { *szComment='\0'; if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) return UNZ_ERRNO; } if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; return (int)uReadThis; } /* Additions by RX '2004 */ extern uLong ZEXPORT unzGetOffset (file) unzFile file; { unz_s* s; if (file==NULL) return 0; s=(unz_s*)file; if (!s->current_file_ok) return 0; if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) if (s->num_file==s->gi.number_entry) return 0; return s->pos_in_central_dir; } extern int ZEXPORT unzSetOffset (file, pos) unzFile file; uLong pos; { unz_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz_s*)file; s->pos_in_central_dir = pos; s->num_file = s->gi.number_entry; /* hack */ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, &s->cur_file_info_internal, NULL,0,NULL,0,NULL,0); s->current_file_ok = (err == UNZ_OK); return err; } yquake2-QUAKE2_5_32/src/common/unzip/ioapi.h0000644000175000017500000000505712615162034021324 0ustar greffrathgreffrath/* ioapi.h -- IO base function header for compress/uncompress .zip files using zlib + zip or unzip API Version 1.01h, December 28th, 2009 Copyright (C) 1998-2009 Gilles Vollant */ #ifndef _ZLIBIOAPI_H #define _ZLIBIOAPI_H #define ZLIB_FILEFUNC_SEEK_CUR (1) #define ZLIB_FILEFUNC_SEEK_END (2) #define ZLIB_FILEFUNC_SEEK_SET (0) #define ZLIB_FILEFUNC_MODE_READ (1) #define ZLIB_FILEFUNC_MODE_WRITE (2) #define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) #define ZLIB_FILEFUNC_MODE_EXISTING (4) #define ZLIB_FILEFUNC_MODE_CREATE (8) #if PRE_ANSI_C89 #define OF(args) () #else #define OF(args) args #endif #ifndef ZCALLBACK #if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) #define ZCALLBACK CALLBACK #else #define ZCALLBACK #endif #endif #ifdef __cplusplus extern "C" { #endif typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); typedef struct zlib_filefunc_def_s { open_file_func zopen_file; read_file_func zread_file; write_file_func zwrite_file; tell_file_func ztell_file; seek_file_func zseek_file; close_file_func zclose_file; testerror_file_func zerror_file; voidpf opaque; } zlib_filefunc_def; void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); #define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) #define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) #define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) #define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) #define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) #define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) #ifdef __cplusplus } #endif #endif yquake2-QUAKE2_5_32/src/common/clientserver.c0000644000175000017500000001140112615162034021544 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 / Server interactions * * ======================================================================= */ #include "header/common.h" #include #include #define MAXPRINTMSG 4096 FILE *logfile; cvar_t *logfile_active; /* 1 = buffer log, 2 = flush after each print */ jmp_buf abortframe; /* an ERR_DROP occured, exit the entire frame */ int server_state; static int rd_target; static char *rd_buffer; static int rd_buffersize; static void (*rd_flush)(int target, char *buffer); void Com_BeginRedirect(int target, char *buffer, int buffersize, void (*flush)) { if (!target || !buffer || !buffersize || !flush) { return; } rd_target = target; rd_buffer = buffer; rd_buffersize = buffersize; rd_flush = flush; *rd_buffer = 0; } void Com_EndRedirect(void) { rd_flush(rd_target, rd_buffer); rd_target = 0; rd_buffer = NULL; rd_buffersize = 0; rd_flush = NULL; } /* * Both client and server can use this, and it will output * to the apropriate place. */ void Com_Printf(char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; va_start(argptr, fmt); vsnprintf(msg, MAXPRINTMSG, fmt, argptr); va_end(argptr); if (rd_target) { if ((strlen(msg) + strlen(rd_buffer)) > (rd_buffersize - 1)) { rd_flush(rd_target, rd_buffer); *rd_buffer = 0; } strcat(rd_buffer, msg); return; } #ifndef DEDICATED_ONLY Con_Print(msg); #endif /* also echo to debugging console */ Sys_ConsoleOutput(msg); /* logfile */ if (logfile_active && logfile_active->value) { char name[MAX_QPATH]; if (!logfile) { Com_sprintf(name, sizeof(name), "%s/qconsole.log", FS_Gamedir()); if (logfile_active->value > 2) { logfile = fopen(name, "a"); } else { logfile = fopen(name, "w"); } } if (logfile) { fprintf(logfile, "%s", msg); } if (logfile_active->value > 1) { fflush(logfile); /* force it to save every time */ } } } /* * A Com_Printf that only shows up if the "developer" cvar is set */ void Com_DPrintf(char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; if (!developer || !developer->value) { return; /* don't confuse non-developers with techie stuff... */ } va_start(argptr, fmt); vsnprintf(msg, MAXPRINTMSG, fmt, argptr); va_end(argptr); Com_Printf("%s", msg); } /* * A Com_Printf that only shows up when either the "modder" or "developer" * cvars is set */ void Com_MDPrintf(char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; if ((!modder || !modder->value) && (!developer || !developer->value)) { return; } va_start(argptr, fmt); vsnprintf(msg, MAXPRINTMSG, fmt, argptr); va_end(argptr); Com_Printf("%s", msg); } /* * Both client and server can use this, and it will * do the apropriate things. */ void Com_Error(int code, char *fmt, ...) { va_list argptr; static char msg[MAXPRINTMSG]; static qboolean recursive; if (recursive) { Sys_Error("recursive error after: %s", msg); } recursive = true; va_start(argptr, fmt); vsnprintf(msg, MAXPRINTMSG, fmt, argptr); va_end(argptr); if (code == ERR_DISCONNECT) { #ifndef DEDICATED_ONLY CL_Drop(); #endif recursive = false; longjmp(abortframe, -1); } else if (code == ERR_DROP) { Com_Printf("********************\nERROR: %s\n********************\n", msg); SV_Shutdown(va("Server crashed: %s\n", msg), false); #ifndef DEDICATED_ONLY CL_Drop(); #endif recursive = false; longjmp(abortframe, -1); } else { SV_Shutdown(va("Server fatal crashed: %s\n", msg), false); #ifndef DEDICATED_ONLY CL_Shutdown(); #endif } if (logfile) { fclose(logfile); logfile = NULL; } Sys_Error("%s", msg); recursive = false; } /* * Both client and server can use this, and it will * do the apropriate things. */ void Com_Quit(void) { Com_Printf("\n----------- shutting down ----------\n"); SV_Shutdown("Server quit\n", false); Sys_Quit(); } int Com_ServerState(void) { return server_state; } void Com_SetServerState(int state) { server_state = state; } yquake2-QUAKE2_5_32/src/common/zone.c0000644000175000017500000000414012615162034020014 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Zone malloc. It's just a normal malloc with tags. * * ======================================================================= */ #include "header/common.h" #include "header/zone.h" #define Z_MAGIC 0x1d1d zhead_t z_chain; int z_count, z_bytes; void Z_Free(void *ptr) { zhead_t *z; z = ((zhead_t *)ptr) - 1; if (z->magic != Z_MAGIC) { printf("free: %p failed\n", ptr); abort(); Com_Error(ERR_FATAL, "Z_Free: bad magic"); } z->prev->next = z->next; z->next->prev = z->prev; z_count--; z_bytes -= z->size; free(z); } void Z_Stats_f(void) { Com_Printf("%i bytes in %i blocks\n", z_bytes, z_count); } void Z_FreeTags(int tag) { zhead_t *z, *next; for (z = z_chain.next; z != &z_chain; z = next) { next = z->next; if (z->tag == tag) { Z_Free((void *)(z + 1)); } } } void * Z_TagMalloc(int size, int tag) { zhead_t *z; size = size + sizeof(zhead_t); z = malloc(size); if (!z) { Com_Error(ERR_FATAL, "Z_Malloc: failed on allocation of %i bytes", size); } memset(z, 0, size); z_count++; z_bytes += size; z->magic = Z_MAGIC; z->tag = tag; z->size = size; z->next = z_chain.next; z->prev = &z_chain; z_chain.next->prev = z; z_chain.next = z; return (void *)(z + 1); } void * Z_Malloc(int size) { return Z_TagMalloc(size, 0); } yquake2-QUAKE2_5_32/src/common/cmdparser.c0000644000175000017500000004000412615162034021020 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 implements the Quake II command processor. Every command * which is send via the command line at startup, via the console and * via rcon is processed here and send to the apropriate subsystem. * * ======================================================================= */ #include "header/common.h" #define MAX_ALIAS_NAME 32 #define ALIAS_LOOP_COUNT 16 typedef struct cmd_function_s { struct cmd_function_s *next; char *name; xcommand_t function; } cmd_function_t; static cmd_function_t *cmd_functions; /* possible commands to execute */ typedef struct cmdalias_s { struct cmdalias_s *next; char name[MAX_ALIAS_NAME]; char *value; } cmdalias_t; char retval[256]; int alias_count; /* for detecting runaway loops */ cmdalias_t *cmd_alias; qboolean cmd_wait; static int cmd_argc; static int cmd_argc; static char *cmd_argv[MAX_STRING_TOKENS]; static char *cmd_null_string = ""; static char cmd_args[MAX_STRING_CHARS]; sizebuf_t cmd_text; byte cmd_text_buf[8192]; char defer_text_buf[8192]; /* * 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; } void Cbuf_Init(void) { SZ_Init(&cmd_text, cmd_text_buf, sizeof(cmd_text_buf)); } /* * Adds command text at the end of the buffer */ void Cbuf_AddText(char *text) { int l; l = strlen(text); if (cmd_text.cursize + l >= cmd_text.maxsize) { Com_Printf("Cbuf_AddText: overflow\n"); return; } SZ_Write(&cmd_text, text, strlen(text)); } /* * Adds command text immediately after the current command * Adds a \n to the text */ 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); memcpy(temp, cmd_text.data, templen); SZ_Clear(&cmd_text); } else { temp = NULL; } /* 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); } } void Cbuf_CopyToDefer(void) { memcpy(defer_text_buf, cmd_text_buf, cmd_text.cursize); defer_text_buf[cmd_text.cursize] = 0; cmd_text.cursize = 0; } void Cbuf_InsertFromDefer(void) { Cbuf_InsertText(defer_text_buf); defer_text_buf[0] = 0; } void Cbuf_ExecuteText(int exec_when, char *text) { switch (exec_when) { case EXEC_NOW: Cmd_ExecuteString(text); break; case EXEC_INSERT: Cbuf_InsertText(text); break; case EXEC_APPEND: Cbuf_AddText(text); break; default: Com_Error(ERR_FATAL, "Cbuf_ExecuteText: bad exec_when"); } } void Cbuf_Execute(void) { int i; char *text; char line[1024]; int quotes; alias_count = 0; /* don't allow infinite alias loops */ 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; } } if (i > sizeof(line) - 1) { memcpy(line, text, sizeof(line) - 1); line[sizeof(line) - 1] = 0; } else { 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; memmove(text, text + i, cmd_text.cursize); } /* execute the command line */ Cmd_ExecuteString(line); if (cmd_wait) { /* skip out while text still remains in buffer, leaving it for next frame */ cmd_wait = false; break; } } } /* * Adds command line parameters as script statements Commands lead with * a +, and continue until another + * * Set commands are added early, so they are guaranteed to be set before * the client and server initialize for the first time. * * Other commands are added late, after all initialization is complete. */ void Cbuf_AddEarlyCommands(qboolean clear) { int i; char *s; for (i = 0; i < COM_Argc(); i++) { s = COM_Argv(i); if (strcmp(s, "+set")) { continue; } Cbuf_AddText(va("set %s %s\n", COM_Argv(i + 1), COM_Argv(i + 2))); if (clear) { COM_ClearArgv(i); COM_ClearArgv(i + 1); COM_ClearArgv(i + 2); } i += 2; } } /* * Adds command line parameters as script statements * Commands lead with a + and continue until another + or - * quake +developer 1 +map amlev1 * * Returns true if any late commands were added, which * will keep the demoloop from immediately starting */ qboolean Cbuf_AddLateCommands(void) { int i, j; int s; char *text, *build, c; int argc; qboolean ret; /* build the combined string to parse from */ s = 0; argc = COM_Argc(); for (i = 1; i < argc; i++) { s += strlen(COM_Argv(i)) + 1; } if (!s) { return false; } text = Z_Malloc(s + 1); text[0] = 0; for (i = 1; i < argc; i++) { strcat(text, COM_Argv(i)); if (i != argc - 1) { strcat(text, " "); } } /* pull out the commands */ build = Z_Malloc(s + 1); build[0] = 0; for (i = 0; i < s - 1; i++) { if (text[i] == '+') { i++; for (j = i; (text[j] != '+') && (text[j] != '-') && (text[j] != 0); j++) { } c = text[j]; text[j] = 0; strcat(build, text + i); strcat(build, "\n"); text[j] = c; i = j - 1; } } ret = (build[0] != 0); if (ret) { Cbuf_AddText(build); } Z_Free(text); Z_Free(build); return ret; } void Cmd_Exec_f(void) { char *f, *f2; int len; if (Cmd_Argc() != 2) { Com_Printf("exec : execute a script file\n"); return; } len = FS_LoadFile(Cmd_Argv(1), (void **)&f); if (!f) { Com_Printf("couldn't exec %s\n", Cmd_Argv(1)); return; } Com_Printf("execing %s\n", Cmd_Argv(1)); /* the file doesn't have a trailing 0, so we need to copy it off */ f2 = Z_Malloc(len + 1); memcpy(f2, f, len); f2[len] = 0; Cbuf_InsertText(f2); Z_Free(f2); FS_FreeFile(f); } /* * Just prints the rest of the line to the console */ void Cmd_Echo_f(void) { int i; for (i = 1; i < Cmd_Argc(); i++) { Com_Printf("%s ", Cmd_Argv(i)); } Com_Printf("\n"); } /* * Creates a new command that executes * a command string (possibly ; seperated) */ void Cmd_Alias_f(void) { cmdalias_t *a; char cmd[1024]; int i, c; char *s; if (Cmd_Argc() == 1) { Com_Printf("Current alias commands:\n"); for (a = cmd_alias; a; a = a->next) { Com_Printf("%s : %s\n", a->name, a->value); } return; } s = Cmd_Argv(1); if (strlen(s) >= MAX_ALIAS_NAME) { Com_Printf("Alias name is too long\n"); return; } /* if the alias already exists, reuse it */ for (a = cmd_alias; a; a = a->next) { if (!strcmp(s, a->name)) { Z_Free(a->value); break; } } if (!a) { a = Z_Malloc(sizeof(cmdalias_t)); a->next = cmd_alias; cmd_alias = a; } strcpy(a->name, s); /* 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 - 1)) { strcat(cmd, " "); } } strcat(cmd, "\n"); a->value = CopyString(cmd); } int Cmd_Argc(void) { return cmd_argc; } char * Cmd_Argv(int arg) { if ((unsigned)arg >= cmd_argc) { return cmd_null_string; } return cmd_argv[arg]; } /* * Returns a single string containing argv(1) to argv(argc()-1) */ char * Cmd_Args(void) { return cmd_args; } char * Cmd_MacroExpandString(char *text) { int i, j, count, len; qboolean inquote; char *scan; static char expanded[MAX_STRING_CHARS]; char temporary[MAX_STRING_CHARS]; char *token, *start; inquote = false; scan = text; len = strlen(scan); if (len >= MAX_STRING_CHARS) { Com_Printf("Line exceeded %i chars, discarded.\n", MAX_STRING_CHARS); return NULL; } count = 0; for (i = 0; i < len; i++) { if (scan[i] == '"') { inquote ^= 1; } if (inquote) { continue; /* don't expand inside quotes */ } if (scan[i] != '$') { continue; } /* scan out the complete macro */ start = scan + i + 1; token = COM_Parse(&start); if (!start) { continue; } token = (char *)Cvar_VariableString(token); j = strlen(token); len += j; if (len >= MAX_STRING_CHARS) { Com_Printf("Expanded line exceeded %i chars, discarded.\n", MAX_STRING_CHARS); return NULL; } memcpy(temporary, scan, i); memcpy(temporary + i, token, j); strcpy(temporary + i + j, start); strcpy(expanded, temporary); scan = expanded; i--; if (++count == 100) { Com_Printf("Macro expansion loop, discarded.\n"); return NULL; } } if (inquote) { Com_Printf("Line has unmatched quote, discarded.\n"); return NULL; } return scan; } /* * Parses the given string into command line tokens. * $Cvars will be expanded unless they are in a quoted token */ void Cmd_TokenizeString(char *text, qboolean macroExpand) { int i; const char *com_token; /* clear the args from the last string */ for (i = 0; i < cmd_argc; i++) { Z_Free(cmd_argv[i]); } cmd_argc = 0; cmd_args[0] = 0; /* macro expand the text */ if (macroExpand) { text = Cmd_MacroExpandString(text); } if (!text) { return; } while (1) { /* skip whitespace up to a /n */ while (*text && *text <= ' ' && *text != '\n') { text++; } if (*text == '\n') { /* a newline seperates commands in the buffer */ text++; break; } if (!*text) { return; } /* set cmd_args to everything after the first arg */ if (cmd_argc == 1) { int l; strcpy(cmd_args, text); /* strip off any trailing whitespace */ l = strlen(cmd_args) - 1; for ( ; l >= 0; l--) { if (cmd_args[l] <= ' ') { cmd_args[l] = 0; } else { break; } } } com_token = COM_Parse(&text); if (!text) { return; } if (cmd_argc < MAX_STRING_TOKENS) { cmd_argv[cmd_argc] = Z_Malloc(strlen(com_token) + 1); strcpy(cmd_argv[cmd_argc], com_token); cmd_argc++; } } } void Cmd_AddCommand(char *cmd_name, xcommand_t function) { cmd_function_t *cmd; cmd_function_t **pos; /* fail if the command is a variable name */ if (Cvar_VariableString(cmd_name)[0]) { Cmd_RemoveCommand(cmd_name); } /* fail if the command already exists */ for (cmd = cmd_functions; cmd; cmd = cmd->next) { if (!strcmp(cmd_name, cmd->name)) { Com_Printf("Cmd_AddCommand: %s already defined\n", cmd_name); return; } } cmd = Z_Malloc(sizeof(cmd_function_t)); cmd->name = cmd_name; cmd->function = function; /* link the command in */ pos = &cmd_functions; while (*pos && strcmp((*pos)->name, cmd->name) < 0) { pos = &(*pos)->next; } cmd->next = *pos; *pos = cmd; } void Cmd_RemoveCommand(char *cmd_name) { cmd_function_t *cmd, **back; back = &cmd_functions; while (1) { cmd = *back; if (!cmd) { Com_Printf("Cmd_RemoveCommand: %s not added\n", cmd_name); return; } if (!strcmp(cmd_name, cmd->name)) { *back = cmd->next; Z_Free(cmd); return; } back = &cmd->next; } } qboolean Cmd_Exists(char *cmd_name) { cmd_function_t *cmd; for (cmd = cmd_functions; cmd; cmd = cmd->next) { if (!strcmp(cmd_name, cmd->name)) { return true; } } return false; } int qsort_strcomp(const void *s1, const void *s2) { return strcmp(*(char **)s1, *(char **)s2); } char * Cmd_CompleteCommand(char *partial) { cmd_function_t *cmd; int len, i, o, p; cmdalias_t *a; cvar_t *cvar; char *pmatch[1024]; qboolean diff = false; len = strlen(partial); if (!len) { return NULL; } /* check for exact match */ for (cmd = cmd_functions; cmd; cmd = cmd->next) { if (!strcmp(partial, cmd->name)) { return cmd->name; } } for (a = cmd_alias; a; a = a->next) { if (!strcmp(partial, a->name)) { return a->name; } } for (cvar = cvar_vars; cvar; cvar = cvar->next) { if (!strcmp(partial, cvar->name)) { return cvar->name; } } for (i = 0; i < 1024; i++) { pmatch[i] = NULL; } i = 0; /* check for partial match */ for (cmd = cmd_functions; cmd; cmd = cmd->next) { if (!strncmp(partial, cmd->name, len)) { pmatch[i] = cmd->name; i++; } } for (a = cmd_alias; a; a = a->next) { if (!strncmp(partial, a->name, len)) { pmatch[i] = a->name; i++; } } for (cvar = cvar_vars; cvar; cvar = cvar->next) { if (!strncmp(partial, cvar->name, len)) { pmatch[i] = cvar->name; i++; } } if (i) { if (i == 1) { return pmatch[0]; } /* Sort it */ qsort(pmatch, i, sizeof(pmatch[0]), qsort_strcomp); Com_Printf("\n\n", partial); for (o = 0; o < i; o++) { Com_Printf(" %s\n", pmatch[o]); } strcpy(retval, ""); p = 0; while (!diff && p < 256) { retval[p] = pmatch[0][p]; for (o = 0; o < i; o++) { if (p > strlen(pmatch[o])) { continue; } if (retval[p] != pmatch[o][p]) { retval[p] = 0; diff = true; } } p++; } return retval; } return NULL; } qboolean Cmd_IsComplete(char *command) { cmd_function_t *cmd; cmdalias_t *a; cvar_t *cvar; /* check for exact match */ for (cmd = cmd_functions; cmd; cmd = cmd->next) { if (!strcmp(command, cmd->name)) { return true; } } for (a = cmd_alias; a; a = a->next) { if (!strcmp(command, a->name)) { return true; } } for (cvar = cvar_vars; cvar; cvar = cvar->next) { if (!strcmp(command, cvar->name)) { return true; } } return false; } /* ugly hack to suppress warnings from default.cfg in Key_Bind_f() */ qboolean doneWithDefaultCfg; /* * A complete command line has been parsed, so try to execute it */ void Cmd_ExecuteString(char *text) { cmd_function_t *cmd; cmdalias_t *a; Cmd_TokenizeString(text, true); /* execute the command line */ if (!Cmd_Argc()) { return; /* no tokens */ } if(Cmd_Argc() > 1 && Q_strcasecmp(cmd_argv[0], "exec") == 0 && Q_strcasecmp(cmd_argv[1], "yq2.cfg") == 0) { /* exec yq2.cfg is done directly after exec default.cfg, see Qcommon_Init() */ doneWithDefaultCfg = true; } /* check functions */ for (cmd = cmd_functions; cmd; cmd = cmd->next) { if (!Q_strcasecmp(cmd_argv[0], cmd->name)) { if (!cmd->function) { /* forward to server command */ Cmd_ExecuteString(va("cmd %s", text)); } else { cmd->function(); } return; } } /* check alias */ for (a = cmd_alias; a; a = a->next) { if (!Q_strcasecmp(cmd_argv[0], a->name)) { if (++alias_count == ALIAS_LOOP_COUNT) { Com_Printf("ALIAS_LOOP_COUNT\n"); return; } Cbuf_InsertText(a->value); return; } } /* check cvars */ if (Cvar_Command()) { return; } #ifndef DEDICATED_ONLY /* send it as a server command if we are connected */ Cmd_ForwardToServer(); #endif } void Cmd_List_f(void) { cmd_function_t *cmd; int i; i = 0; for (cmd = cmd_functions; cmd; cmd = cmd->next, i++) { Com_Printf("%s\n", cmd->name); } Com_Printf("%i commands\n", i); } void Cmd_Init(void) { /* register our commands */ Cmd_AddCommand("cmdlist", Cmd_List_f); Cmd_AddCommand("exec", Cmd_Exec_f); Cmd_AddCommand("echo", Cmd_Echo_f); Cmd_AddCommand("alias", Cmd_Alias_f); Cmd_AddCommand("wait", Cmd_Wait_f); } yquake2-QUAKE2_5_32/src/common/cvar.c0000644000175000017500000002122312615162034017775 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 Quake II CVAR subsystem. Implements dynamic variable handling. * * ======================================================================= */ #include "header/common.h" cvar_t *cvar_vars; static qboolean Cvar_InfoValidate(char *s) { if (strstr(s, "\\")) { return false; } if (strstr(s, "\"")) { return false; } if (strstr(s, ";")) { return false; } return true; } static cvar_t * Cvar_FindVar(const char *var_name) { cvar_t *var; for (var = cvar_vars; var; var = var->next) { if (!strcmp(var_name, var->name)) { return var; } } return NULL; } float Cvar_VariableValue(char *var_name) { cvar_t *var; var = Cvar_FindVar(var_name); if (!var) { return 0; } return strtod(var->string, (char **)NULL); } const char * Cvar_VariableString(const char *var_name) { cvar_t *var; var = Cvar_FindVar(var_name); if (!var) { return ""; } return var->string; } char * Cvar_CompleteVariable(char *partial) { cvar_t *cvar; int len; len = (int)strlen(partial); if (!len) { return NULL; } /* check exact match */ for (cvar = cvar_vars; cvar; cvar = cvar->next) { if (!strcmp(partial, cvar->name)) { return cvar->name; } } /* check partial match */ for (cvar = cvar_vars; cvar; cvar = cvar->next) { if (!strncmp(partial, cvar->name, len)) { return cvar->name; } } return NULL; } /* * If the variable already exists, the value will not be set * The flags will be or'ed in if the variable exists. */ cvar_t * Cvar_Get(char *var_name, char *var_value, int flags) { cvar_t *var; cvar_t **pos; if (flags & (CVAR_USERINFO | CVAR_SERVERINFO)) { if (!Cvar_InfoValidate(var_name)) { Com_Printf("invalid info cvar name\n"); return NULL; } } var = Cvar_FindVar(var_name); if (var) { var->flags |= flags; return var; } if (!var_value) { return NULL; } if (flags & (CVAR_USERINFO | CVAR_SERVERINFO)) { if (!Cvar_InfoValidate(var_value)) { Com_Printf("invalid info cvar value\n"); return NULL; } } var = Z_Malloc(sizeof(*var)); var->name = CopyString(var_name); var->string = CopyString(var_value); var->modified = true; var->value = strtod(var->string, (char **)NULL); /* link the variable in */ pos = &cvar_vars; while (*pos && strcmp((*pos)->name, var->name) < 0) { pos = &(*pos)->next; } var->next = *pos; *pos = var; var->flags = flags; return var; } cvar_t * Cvar_Set2(char *var_name, char *value, qboolean force) { cvar_t *var; var = Cvar_FindVar(var_name); if (!var) { return Cvar_Get(var_name, value, 0); } if (var->flags & (CVAR_USERINFO | CVAR_SERVERINFO)) { if (!Cvar_InfoValidate(value)) { Com_Printf("invalid info cvar value\n"); return var; } } if (!force) { if (var->flags & CVAR_NOSET) { Com_Printf("%s is write protected.\n", var_name); return var; } if (var->flags & CVAR_LATCH) { if (var->latched_string) { if (strcmp(value, var->latched_string) == 0) { return var; } Z_Free(var->latched_string); } else { if (strcmp(value, var->string) == 0) { return var; } } if (Com_ServerState()) { Com_Printf("%s will be changed for next game.\n", var_name); var->latched_string = CopyString(value); } else { var->string = CopyString(value); var->value = (float)strtod(var->string, (char **)NULL); if (!strcmp(var->name, "game")) { FS_SetGamedir(var->string); FS_ExecAutoexec(); } } return var; } } else { if (var->latched_string) { Z_Free(var->latched_string); var->latched_string = NULL; } } if (!strcmp(value, var->string)) { return var; } var->modified = true; if (var->flags & CVAR_USERINFO) { userinfo_modified = true; } Z_Free(var->string); var->string = CopyString(value); var->value = strtod(var->string, (char **)NULL); return var; } cvar_t * Cvar_ForceSet(char *var_name, char *value) { return Cvar_Set2(var_name, value, true); } cvar_t * Cvar_Set(char *var_name, char *value) { return Cvar_Set2(var_name, value, false); } cvar_t * Cvar_FullSet(char *var_name, char *value, int flags) { cvar_t *var; var = Cvar_FindVar(var_name); if (!var) { return Cvar_Get(var_name, value, flags); } var->modified = true; if (var->flags & CVAR_USERINFO) { userinfo_modified = true; } Z_Free(var->string); var->string = CopyString(value); var->value = (float)strtod(var->string, (char **)NULL); var->flags = flags; return var; } void Cvar_SetValue(char *var_name, float value) { char val[32]; if (value == (int)value) { Com_sprintf(val, sizeof(val), "%i", (int)value); } else { Com_sprintf(val, sizeof(val), "%f", value); } Cvar_Set(var_name, val); } /* * Any variables with latched values will now be updated */ void Cvar_GetLatchedVars(void) { cvar_t *var; for (var = cvar_vars; var; var = var->next) { if (!var->latched_string) { continue; } Z_Free(var->string); var->string = var->latched_string; var->latched_string = NULL; var->value = strtod(var->string, (char **)NULL); if (!strcmp(var->name, "game")) { FS_SetGamedir(var->string); FS_ExecAutoexec(); } } } /* * 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) { Com_Printf("\"%s\" is \"%s\"\n", v->name, v->string); return true; } Cvar_Set(v->name, Cmd_Argv(1)); return true; } /* * Allows setting and defining of arbitrary cvars from console */ void Cvar_Set_f(void) { int c; int flags; c = Cmd_Argc(); if ((c != 3) && (c != 4)) { Com_Printf("usage: set [u / s]\n"); return; } if (c == 4) { if (!strcmp(Cmd_Argv(3), "u")) { flags = CVAR_USERINFO; } else if (!strcmp(Cmd_Argv(3), "s")) { flags = CVAR_SERVERINFO; } else { Com_Printf("flags can only be 'u' or 's'\n"); return; } Cvar_FullSet(Cmd_Argv(1), Cmd_Argv(2), flags); } else { Cvar_Set(Cmd_Argv(1), Cmd_Argv(2)); } } /* * Appends lines containing "set variable value" for all variables * with the archive flag set to true. */ void Cvar_WriteVariables(char *path) { cvar_t *var; char buffer[1024]; FILE *f; f = fopen(path, "a"); for (var = cvar_vars; var; var = var->next) { if (var->flags & CVAR_ARCHIVE) { Com_sprintf(buffer, sizeof(buffer), "set %s \"%s\"\n", var->name, var->string); fprintf(f, "%s", buffer); } } fclose(f); } void Cvar_List_f(void) { cvar_t *var; int i; i = 0; for (var = cvar_vars; var; var = var->next, i++) { if (var->flags & CVAR_ARCHIVE) { Com_Printf("*"); } else { Com_Printf(" "); } if (var->flags & CVAR_USERINFO) { Com_Printf("U"); } else { Com_Printf(" "); } if (var->flags & CVAR_SERVERINFO) { Com_Printf("S"); } else { Com_Printf(" "); } if (var->flags & CVAR_NOSET) { Com_Printf("-"); } else if (var->flags & CVAR_LATCH) { Com_Printf("L"); } else { Com_Printf(" "); } Com_Printf(" %s \"%s\"\n", var->name, var->string); } Com_Printf("%i cvars\n", i); } qboolean userinfo_modified; char * Cvar_BitInfo(int bit) { static char info[MAX_INFO_STRING]; cvar_t *var; info[0] = 0; for (var = cvar_vars; var; var = var->next) { if (var->flags & bit) { Info_SetValueForKey(info, var->name, var->string); } } return info; } /* * returns an info string containing * all the CVAR_USERINFO cvars */ char * Cvar_Userinfo(void) { return Cvar_BitInfo(CVAR_USERINFO); } /* * returns an info string containing * all the CVAR_SERVERINFO cvars */ char * Cvar_Serverinfo(void) { return Cvar_BitInfo(CVAR_SERVERINFO); } /* * Reads in all archived cvars */ void Cvar_Init(void) { Cmd_AddCommand("set", Cvar_Set_f); Cmd_AddCommand("cvarlist", Cvar_List_f); } yquake2-QUAKE2_5_32/src/common/szone.c0000644000175000017500000000427012615162034020203 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 zone, server side of memory management * * ======================================================================= */ #include "header/common.h" void SZ_Init(sizebuf_t *buf, byte *data, int length) { memset(buf, 0, sizeof(*buf)); buf->data = data; buf->maxsize = length; } 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) { Com_Error(ERR_FATAL, "SZ_GetSpace: overflow without allowoverflow set"); } if (length > buf->maxsize) { Com_Error(ERR_FATAL, "SZ_GetSpace: %i is > full buffer size", length); } SZ_Clear(buf); buf->overflowed = true; Com_Printf("SZ_GetSpace: overflow\n"); } data = buf->data + buf->cursize; buf->cursize += length; return data; } void SZ_Write(sizebuf_t *buf, void *data, int length) { memcpy(SZ_GetSpace(buf, length), data, length); } void SZ_Print(sizebuf_t *buf, char *data) { int len; len = (int)strlen(data) + 1; if (buf->cursize) { if (buf->data[buf->cursize - 1]) { memcpy((byte *)SZ_GetSpace(buf, len), data, len); /* no trailing 0 */ } else { memcpy((byte *)SZ_GetSpace(buf, len - 1) - 1, data, len); /* write over trailing 0 */ } } else { memcpy((byte *)SZ_GetSpace(buf, len), data, len); } } yquake2-QUAKE2_5_32/src/common/collision.c0000644000175000017500000010164212615162034021041 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 collision model. Slaps "boxes" through the world and checks if * they collide with the world model, entities or other boxes. * * ======================================================================= */ #include "header/common.h" typedef struct { cplane_t *plane; int children[2]; /* negative numbers are leafs */ } cnode_t; typedef struct { cplane_t *plane; mapsurface_t *surface; } cbrushside_t; typedef struct { int contents; int cluster; int area; unsigned short firstleafbrush; unsigned short numleafbrushes; } cleaf_t; typedef struct { int contents; int numsides; int firstbrushside; int checkcount; /* to avoid repeated testings */ } cbrush_t; typedef struct { int numareaportals; int firstareaportal; int floodnum; /* if two areas have equal floodnums, they are connected */ int floodvalid; } carea_t; byte *cmod_base; byte map_visibility[MAX_MAP_VISIBILITY]; byte pvsrow[MAX_MAP_LEAFS / 8]; byte phsrow[MAX_MAP_LEAFS / 8]; carea_t map_areas[MAX_MAP_AREAS]; cbrush_t map_brushes[MAX_MAP_BRUSHES]; cbrushside_t map_brushsides[MAX_MAP_BRUSHSIDES]; char map_name[MAX_QPATH]; char map_entitystring[MAX_MAP_ENTSTRING]; cbrush_t *box_brush; cleaf_t *box_leaf; cleaf_t map_leafs[MAX_MAP_LEAFS]; cmodel_t map_cmodels[MAX_MAP_MODELS]; cnode_t map_nodes[MAX_MAP_NODES+6]; /* extra for box hull */ cplane_t *box_planes; cplane_t map_planes[MAX_MAP_PLANES+6]; /* extra for box hull */ cvar_t *map_noareas; dareaportal_t map_areaportals[MAX_MAP_AREAPORTALS]; dvis_t *map_vis = (dvis_t *)map_visibility; int box_headnode; int checkcount; int emptyleaf, solidleaf; int floodvalid; float *leaf_mins, *leaf_maxs; int leaf_count, leaf_maxcount; int *leaf_list; int leaf_topnode; int numareaportals; int numareas = 1; int numbrushes; int numbrushsides; int numclusters = 1; int numcmodels; int numentitychars; int numleafbrushes; int numleafs = 1; /* allow leaf funcs to be called without a map */ int numnodes; int numplanes; int numtexinfo; int numvisibility; int trace_contents; mapsurface_t map_surfaces[MAX_MAP_TEXINFO]; mapsurface_t nullsurface; qboolean portalopen[MAX_MAP_AREAPORTALS]; qboolean trace_ispoint; /* optimized case */ trace_t trace_trace; unsigned short map_leafbrushes[MAX_MAP_LEAFBRUSHES]; vec3_t trace_start, trace_end; vec3_t trace_mins, trace_maxs; vec3_t trace_extents; #ifndef DEDICATED_ONLY int c_pointcontents; int c_traces, c_brush_traces; #endif /* 1/32 epsilon to keep floating point happy */ #define DIST_EPSILON (0.03125f) void FloodArea_r(carea_t *area, int floodnum) { int i; dareaportal_t *p; if (area->floodvalid == floodvalid) { if (area->floodnum == floodnum) { return; } Com_Error(ERR_DROP, "FloodArea_r: reflooded"); } area->floodnum = floodnum; area->floodvalid = floodvalid; p = &map_areaportals[area->firstareaportal]; for (i = 0; i < area->numareaportals; i++, p++) { if (portalopen[LittleLong(p->portalnum)]) { FloodArea_r(&map_areas[LittleLong(p->otherarea)], floodnum); } } } void FloodAreaConnections(void) { int i; carea_t *area; int floodnum; /* all current floods are now invalid */ floodvalid++; floodnum = 0; /* area 0 is not used */ for (i = 1; i < numareas; i++) { area = &map_areas[i]; if (area->floodvalid == floodvalid) { continue; /* already flooded into */ } floodnum++; FloodArea_r(area, floodnum); } } void CM_SetAreaPortalState(int portalnum, qboolean open) { if (portalnum > numareaportals) { Com_Error(ERR_DROP, "areaportal > numareaportals"); } portalopen[portalnum] = open; FloodAreaConnections(); } qboolean CM_AreasConnected(int area1, int area2) { if (map_noareas->value) { return true; } if ((area1 > numareas) || (area2 > numareas)) { Com_Error(ERR_DROP, "area > numareas"); } if (map_areas[area1].floodnum == map_areas[area2].floodnum) { return true; } return false; } /* * Writes a length byte followed by a bit vector of all the areas * that area in the same flood as the area parameter * * This is used by the client refreshes to cull visibility */ int CM_WriteAreaBits(byte *buffer, int area) { int i; int floodnum; int bytes; bytes = (numareas + 7) >> 3; if (map_noareas->value) { /* for debugging, send everything */ memset(buffer, 255, bytes); } else { memset(buffer, 0, bytes); floodnum = map_areas[area].floodnum; for (i = 0; i < numareas; i++) { if ((map_areas[i].floodnum == floodnum) || !area) { buffer[i >> 3] |= 1 << (i & 7); } } } return bytes; } /* * Writes the portal state to a savegame file */ void CM_WritePortalState(FILE *f) { fwrite(portalopen, sizeof(portalopen), 1, f); } /* * Reads the portal state from a savegame file * and recalculates the area connections */ void CM_ReadPortalState(fileHandle_t f) { FS_Read(portalopen, sizeof(portalopen), f); FloodAreaConnections(); } /* * Returns true if any leaf under headnode has a cluster that * is potentially visible */ qboolean CM_HeadnodeVisible(int nodenum, byte *visbits) { int leafnum1; int cluster; cnode_t *node; if (nodenum < 0) { leafnum1 = -1 - nodenum; cluster = map_leafs[leafnum1].cluster; if (cluster == -1) { return false; } if (visbits[cluster >> 3] & (1 << (cluster & 7))) { return true; } return false; } node = &map_nodes[nodenum]; if (CM_HeadnodeVisible(node->children[0], visbits)) { return true; } return CM_HeadnodeVisible(node->children[1], visbits); } /* * Set up the planes and nodes so that the six floats of a bounding box * can just be stored out and get a proper clipping hull structure. */ void CM_InitBoxHull(void) { int i; int side; cnode_t *c; cplane_t *p; cbrushside_t *s; box_headnode = numnodes; box_planes = &map_planes[numplanes]; if ((numnodes + 6 > MAX_MAP_NODES) || (numbrushes + 1 > MAX_MAP_BRUSHES) || (numleafbrushes + 1 > MAX_MAP_LEAFBRUSHES) || (numbrushsides + 6 > MAX_MAP_BRUSHSIDES) || (numplanes + 12 > MAX_MAP_PLANES)) { Com_Error(ERR_DROP, "Not enough room for box tree"); } box_brush = &map_brushes[numbrushes]; box_brush->numsides = 6; box_brush->firstbrushside = numbrushsides; box_brush->contents = CONTENTS_MONSTER; box_leaf = &map_leafs[numleafs]; box_leaf->contents = CONTENTS_MONSTER; box_leaf->firstleafbrush = numleafbrushes; box_leaf->numleafbrushes = 1; map_leafbrushes[numleafbrushes] = numbrushes; for (i = 0; i < 6; i++) { side = i & 1; /* brush sides */ s = &map_brushsides[numbrushsides + i]; s->plane = map_planes + (numplanes + i * 2 + side); s->surface = &nullsurface; /* nodes */ c = &map_nodes[box_headnode + i]; c->plane = map_planes + (numplanes + i * 2); c->children[side] = -1 - emptyleaf; if (i != 5) { c->children[side ^ 1] = box_headnode + i + 1; } else { c->children[side ^ 1] = -1 - numleafs; } /* planes */ p = &box_planes[i * 2]; p->type = i >> 1; p->signbits = 0; VectorClear(p->normal); p->normal[i >> 1] = 1; p = &box_planes[i * 2 + 1]; p->type = 3 + (i >> 1); p->signbits = 0; VectorClear(p->normal); p->normal[i >> 1] = -1; } } /* * To keep everything totally uniform, bounding boxes are turned into * small BSP trees instead of being compared directly. */ int CM_HeadnodeForBox(vec3_t mins, vec3_t maxs) { box_planes[0].dist = maxs[0]; box_planes[1].dist = -maxs[0]; box_planes[2].dist = mins[0]; box_planes[3].dist = -mins[0]; box_planes[4].dist = maxs[1]; box_planes[5].dist = -maxs[1]; box_planes[6].dist = mins[1]; box_planes[7].dist = -mins[1]; box_planes[8].dist = maxs[2]; box_planes[9].dist = -maxs[2]; box_planes[10].dist = mins[2]; box_planes[11].dist = -mins[2]; return box_headnode; } int CM_PointLeafnum_r(vec3_t p, int num) { float d; cnode_t *node; cplane_t *plane; while (num >= 0) { node = map_nodes + num; plane = node->plane; 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]; } } #ifndef DEDICATED_ONLY c_pointcontents++; /* optimize counter */ #endif return -1 - num; } int CM_PointLeafnum(vec3_t p) { if (!numplanes) { return 0; /* sound may call this without map loaded */ } return CM_PointLeafnum_r(p, 0); } /* * Fills in a list of all the leafs touched */ void CM_BoxLeafnums_r(int nodenum) { cplane_t *plane; cnode_t *node; int s; while (1) { if (nodenum < 0) { if (leaf_count >= leaf_maxcount) { return; } leaf_list[leaf_count++] = -1 - nodenum; return; } node = &map_nodes[nodenum]; plane = node->plane; s = BOX_ON_PLANE_SIDE(leaf_mins, leaf_maxs, plane); if (s == 1) { nodenum = node->children[0]; } else if (s == 2) { nodenum = node->children[1]; } else { /* go down both */ if (leaf_topnode == -1) { leaf_topnode = nodenum; } CM_BoxLeafnums_r(node->children[0]); nodenum = node->children[1]; } } } int CM_BoxLeafnums_headnode(vec3_t mins, vec3_t maxs, int *list, int listsize, int headnode, int *topnode) { leaf_list = list; leaf_count = 0; leaf_maxcount = listsize; leaf_mins = mins; leaf_maxs = maxs; leaf_topnode = -1; CM_BoxLeafnums_r(headnode); if (topnode) { *topnode = leaf_topnode; } return leaf_count; } int CM_BoxLeafnums(vec3_t mins, vec3_t maxs, int *list, int listsize, int *topnode) { return CM_BoxLeafnums_headnode(mins, maxs, list, listsize, map_cmodels[0].headnode, topnode); } int CM_PointContents(vec3_t p, int headnode) { int l; if (!numnodes) /* map not loaded */ { return 0; } l = CM_PointLeafnum_r(p, headnode); return map_leafs[l].contents; } /* * Handles offseting and rotation of the end points for moving and * rotating entities */ int CM_TransformedPointContents(vec3_t p, int headnode, vec3_t origin, vec3_t angles) { vec3_t p_l; vec3_t temp; vec3_t forward, right, up; int l; /* subtract origin offset */ VectorSubtract(p, origin, p_l); /* rotate start and end into the models frame of reference */ if ((headnode != box_headnode) && (angles[0] || angles[1] || angles[2])) { AngleVectors(angles, forward, right, up); VectorCopy(p_l, temp); p_l[0] = DotProduct(temp, forward); p_l[1] = -DotProduct(temp, right); p_l[2] = DotProduct(temp, up); } l = CM_PointLeafnum_r(p_l, headnode); return map_leafs[l].contents; } void CM_ClipBoxToBrush(vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2, trace_t *trace, cbrush_t *brush) { int i, j; cplane_t *plane, *clipplane; float dist; float enterfrac, leavefrac; vec3_t ofs; float d1, d2; qboolean getout, startout; float f; cbrushside_t *side, *leadside; enterfrac = -1; leavefrac = 1; clipplane = NULL; if (!brush->numsides) { return; } #ifndef DEDICATED_ONLY c_brush_traces++; #endif getout = false; startout = false; leadside = NULL; for (i = 0; i < brush->numsides; i++) { side = &map_brushsides[brush->firstbrushside + i]; plane = side->plane; if (!trace_ispoint) { /* general box case push the plane out apropriately for mins/maxs */ for (j = 0; j < 3; j++) { if (plane->normal[j] < 0) { ofs[j] = maxs[j]; } else { ofs[j] = mins[j]; } } dist = DotProduct(ofs, plane->normal); dist = plane->dist - dist; } else { /* special point case */ dist = plane->dist; } d1 = DotProduct(p1, plane->normal) - dist; d2 = DotProduct(p2, plane->normal) - dist; if (d2 > 0) { getout = true; /* endpoint is not in solid */ } if (d1 > 0) { startout = true; } /* if completely in front of face, no intersection */ if ((d1 > 0) && (d2 >= d1)) { return; } if ((d1 <= 0) && (d2 <= 0)) { continue; } /* crosses face */ if (d1 > d2) { /* enter */ f = (d1 - DIST_EPSILON) / (d1 - d2); if (f > enterfrac) { enterfrac = f; clipplane = plane; leadside = side; } } else { /* leave */ f = (d1 + DIST_EPSILON) / (d1 - d2); if (f < leavefrac) { leavefrac = f; } } } if (!startout) { /* original point was inside brush */ trace->startsolid = true; if (!getout) { trace->allsolid = true; } return; } if (enterfrac < leavefrac) { if ((enterfrac > -1) && (enterfrac < trace->fraction)) { if (enterfrac < 0) { enterfrac = 0; } if (clipplane == NULL) { Com_Error(ERR_FATAL, "clipplane was NULL!\n"); } trace->fraction = enterfrac; trace->plane = *clipplane; trace->surface = &(leadside->surface->c); trace->contents = brush->contents; } } } void CM_TestBoxInBrush(vec3_t mins, vec3_t maxs, vec3_t p1, trace_t *trace, cbrush_t *brush) { int i, j; cplane_t *plane; float dist; vec3_t ofs; float d1; cbrushside_t *side; if (!brush->numsides) { return; } for (i = 0; i < brush->numsides; i++) { side = &map_brushsides[brush->firstbrushside + i]; plane = side->plane; /* general box case push the plane out apropriately for mins/maxs */ for (j = 0; j < 3; j++) { if (plane->normal[j] < 0) { ofs[j] = maxs[j]; } else { ofs[j] = mins[j]; } } dist = DotProduct(ofs, plane->normal); dist = plane->dist - dist; d1 = DotProduct(p1, plane->normal) - dist; /* if completely in front of face, no intersection */ if (d1 > 0) { return; } } /* inside this brush */ trace->startsolid = trace->allsolid = true; trace->fraction = 0; trace->contents = brush->contents; } void CM_TraceToLeaf(int leafnum) { int k; int brushnum; cleaf_t *leaf; cbrush_t *b; leaf = &map_leafs[leafnum]; if (!(leaf->contents & trace_contents)) { return; } /* trace line against all brushes in the leaf */ for (k = 0; k < leaf->numleafbrushes; k++) { brushnum = map_leafbrushes[leaf->firstleafbrush + k]; b = &map_brushes[brushnum]; if (b->checkcount == checkcount) { continue; /* already checked this brush in another leaf */ } b->checkcount = checkcount; if (!(b->contents & trace_contents)) { continue; } CM_ClipBoxToBrush(trace_mins, trace_maxs, trace_start, trace_end, &trace_trace, b); if (!trace_trace.fraction) { return; } } } void CM_TestInLeaf(int leafnum) { int k; int brushnum; cleaf_t *leaf; cbrush_t *b; leaf = &map_leafs[leafnum]; if (!(leaf->contents & trace_contents)) { return; } /* trace line against all brushes in the leaf */ for (k = 0; k < leaf->numleafbrushes; k++) { brushnum = map_leafbrushes[leaf->firstleafbrush + k]; b = &map_brushes[brushnum]; if (b->checkcount == checkcount) { continue; /* already checked this brush in another leaf */ } b->checkcount = checkcount; if (!(b->contents & trace_contents)) { continue; } CM_TestBoxInBrush(trace_mins, trace_maxs, trace_start, &trace_trace, b); if (!trace_trace.fraction) { return; } } } void CM_RecursiveHullCheck(int num, float p1f, float p2f, vec3_t p1, vec3_t p2) { cnode_t *node; cplane_t *plane; float t1, t2, offset; float frac, frac2; float idist; int i; vec3_t mid; int side; float midf; if (trace_trace.fraction <= p1f) { return; /* already hit something nearer */ } /* if < 0, we are in a leaf node */ if (num < 0) { CM_TraceToLeaf(-1 - num); return; } /* find the point distances to the seperating plane and the offset for the size of the box */ node = map_nodes + num; plane = node->plane; if (plane->type < 3) { t1 = p1[plane->type] - plane->dist; t2 = p2[plane->type] - plane->dist; offset = trace_extents[plane->type]; } else { t1 = DotProduct(plane->normal, p1) - plane->dist; t2 = DotProduct(plane->normal, p2) - plane->dist; if (trace_ispoint) { offset = 0; } else { offset = (float)fabs(trace_extents[0] * plane->normal[0]) + (float)fabs(trace_extents[1] * plane->normal[1]) + (float)fabs(trace_extents[2] * plane->normal[2]); } } /* see which sides we need to consider */ if ((t1 >= offset) && (t2 >= offset)) { CM_RecursiveHullCheck(node->children[0], p1f, p2f, p1, p2); return; } if ((t1 < -offset) && (t2 < -offset)) { CM_RecursiveHullCheck(node->children[1], p1f, p2f, p1, p2); return; } /* put the crosspoint DIST_EPSILON pixels on the near side */ if (t1 < t2) { idist = 1.0f / (t1 - t2); side = 1; frac2 = (t1 + offset + DIST_EPSILON) * idist; frac = (t1 - offset + DIST_EPSILON) * idist; } else if (t1 > t2) { idist = 1.0 / (t1 - t2); side = 0; frac2 = (t1 - offset - DIST_EPSILON) * idist; frac = (t1 + offset + DIST_EPSILON) * idist; } else { side = 0; frac = 1; frac2 = 0; } /* move up to the node */ 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]); } CM_RecursiveHullCheck(node->children[side], p1f, midf, p1, mid); /* go past the node */ if (frac2 < 0) { frac2 = 0; } if (frac2 > 1) { frac2 = 1; } midf = p1f + (p2f - p1f) * frac2; for (i = 0; i < 3; i++) { mid[i] = p1[i] + frac2 * (p2[i] - p1[i]); } CM_RecursiveHullCheck(node->children[side ^ 1], midf, p2f, mid, p2); } trace_t CM_BoxTrace(vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int headnode, int brushmask) { int i; checkcount++; /* for multi-check avoidance */ #ifndef DEDICATED_ONLY c_traces++; /* for statistics, may be zeroed */ #endif /* fill in a default trace */ memset(&trace_trace, 0, sizeof(trace_trace)); trace_trace.fraction = 1; trace_trace.surface = &(nullsurface.c); if (!numnodes) /* map not loaded */ { return trace_trace; } trace_contents = brushmask; VectorCopy(start, trace_start); VectorCopy(end, trace_end); VectorCopy(mins, trace_mins); VectorCopy(maxs, trace_maxs); /* check for position test special case */ if ((start[0] == end[0]) && (start[1] == end[1]) && (start[2] == end[2])) { int leafs[1024]; int i, numleafs; vec3_t c1, c2; int topnode; VectorAdd(start, mins, c1); VectorAdd(start, maxs, c2); for (i = 0; i < 3; i++) { c1[i] -= 1; c2[i] += 1; } numleafs = CM_BoxLeafnums_headnode(c1, c2, leafs, 1024, headnode, &topnode); for (i = 0; i < numleafs; i++) { CM_TestInLeaf(leafs[i]); if (trace_trace.allsolid) { break; } } VectorCopy(start, trace_trace.endpos); return trace_trace; } /* check for point special case */ if ((mins[0] == 0) && (mins[1] == 0) && (mins[2] == 0) && (maxs[0] == 0) && (maxs[1] == 0) && (maxs[2] == 0)) { trace_ispoint = true; VectorClear(trace_extents); } else { trace_ispoint = false; trace_extents[0] = -mins[0] > maxs[0] ? -mins[0] : maxs[0]; trace_extents[1] = -mins[1] > maxs[1] ? -mins[1] : maxs[1]; trace_extents[2] = -mins[2] > maxs[2] ? -mins[2] : maxs[2]; } /* general sweeping through world */ CM_RecursiveHullCheck(headnode, 0, 1, start, end); if (trace_trace.fraction == 1) { VectorCopy(end, trace_trace.endpos); } else { for (i = 0; i < 3; i++) { trace_trace.endpos[i] = start[i] + trace_trace.fraction * (end[i] - start[i]); } } return trace_trace; } /* * Handles offseting and rotation of the end points for moving and * rotating entities */ trace_t CM_TransformedBoxTrace(vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, int headnode, int brushmask, vec3_t origin, vec3_t angles) { trace_t trace; vec3_t start_l, end_l; vec3_t a; vec3_t forward, right, up; vec3_t temp; qboolean rotated; /* subtract origin offset */ VectorSubtract(start, origin, start_l); VectorSubtract(end, origin, end_l); /* rotate start and end into the models frame of reference */ if ((headnode != box_headnode) && (angles[0] || angles[1] || angles[2])) { rotated = true; } else { rotated = false; } if (rotated) { AngleVectors(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); } /* sweep the box through the model */ trace = CM_BoxTrace(start_l, end_l, mins, maxs, headnode, brushmask); if (rotated && (trace.fraction != 1.0)) { VectorNegate(angles, a); AngleVectors(a, forward, right, 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); } trace.endpos[0] = start[0] + trace.fraction * (end[0] - start[0]); trace.endpos[1] = start[1] + trace.fraction * (end[1] - start[1]); trace.endpos[2] = start[2] + trace.fraction * (end[2] - start[2]); return trace; } void CMod_LoadSubmodels(lump_t *l) { dmodel_t *in; cmodel_t *out; int i, j, count; in = (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); } count = l->filelen / sizeof(*in); if (count < 1) { Com_Error(ERR_DROP, "Map with no models"); } if (count > MAX_MAP_MODELS) { Com_Error(ERR_DROP, "Map has too many models"); } numcmodels = count; for (i = 0; i < count; i++, in++, out++) { out = &map_cmodels[i]; for (j = 0; j < 3; j++) { /* spread the mins / maxs by a pixel */ out->mins[j] = LittleFloat(in->mins[j]) - 1; out->maxs[j] = LittleFloat(in->maxs[j]) + 1; out->origin[j] = LittleFloat(in->origin[j]); } out->headnode = LittleLong(in->headnode); } } void CMod_LoadSurfaces(lump_t *l) { texinfo_t *in; mapsurface_t *out; int i, count; in = (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); } count = l->filelen / sizeof(*in); if (count < 1) { Com_Error(ERR_DROP, "Map with no surfaces"); } if (count > MAX_MAP_TEXINFO) { Com_Error(ERR_DROP, "Map has too many surfaces"); } numtexinfo = count; out = map_surfaces; for (i = 0; i < count; i++, in++, out++) { Q_strlcpy(out->c.name, in->texture, sizeof(out->c.name)); Q_strlcpy(out->rname, in->texture, sizeof(out->rname)); out->c.flags = LittleLong(in->flags); out->c.value = LittleLong(in->value); } } void CMod_LoadNodes(lump_t *l) { dnode_t *in; int child; cnode_t *out; int i, j, count; in = (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); } count = l->filelen / sizeof(*in); if (count < 1) { Com_Error(ERR_DROP, "Map has no nodes"); } if (count > MAX_MAP_NODES) { Com_Error(ERR_DROP, "Map has too many nodes"); } out = map_nodes; numnodes = count; for (i = 0; i < count; i++, out++, in++) { out->plane = map_planes + LittleLong(in->planenum); for (j = 0; j < 2; j++) { child = LittleLong(in->children[j]); out->children[j] = child; } } } void CMod_LoadBrushes(lump_t *l) { dbrush_t *in; cbrush_t *out; int i, count; in = (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); } count = l->filelen / sizeof(*in); if (count > MAX_MAP_BRUSHES) { Com_Error(ERR_DROP, "Map has too many brushes"); } out = map_brushes; numbrushes = count; for (i = 0; i < count; i++, out++, in++) { out->firstbrushside = LittleLong(in->firstside); out->numsides = LittleLong(in->numsides); out->contents = LittleLong(in->contents); } } void CMod_LoadLeafs(lump_t *l) { int i; cleaf_t *out; dleaf_t *in; int count; in = (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); } count = l->filelen / sizeof(*in); if (count < 1) { Com_Error(ERR_DROP, "Map with no leafs"); } /* need to save space for box planes */ if (count > MAX_MAP_PLANES) { Com_Error(ERR_DROP, "Map has too many planes"); } out = map_leafs; numleafs = count; numclusters = 0; for (i = 0; i < count; i++, in++, out++) { out->contents = LittleLong(in->contents); out->cluster = LittleShort(in->cluster); out->area = LittleShort(in->area); out->firstleafbrush = LittleShort(in->firstleafbrush); out->numleafbrushes = LittleShort(in->numleafbrushes); if (out->cluster >= numclusters) { numclusters = out->cluster + 1; } } if (map_leafs[0].contents != CONTENTS_SOLID) { Com_Error(ERR_DROP, "Map leaf 0 is not CONTENTS_SOLID"); } solidleaf = 0; emptyleaf = -1; for (i = 1; i < numleafs; i++) { if (!map_leafs[i].contents) { emptyleaf = i; break; } } if (emptyleaf == -1) { Com_Error(ERR_DROP, "Map does not have an empty leaf"); } } void CMod_LoadPlanes(lump_t *l) { int i, j; cplane_t *out; dplane_t *in; int count; int bits; in = (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); } count = l->filelen / sizeof(*in); if (count < 1) { Com_Error(ERR_DROP, "Map with no planes"); } /* need to save space for box planes */ if (count > MAX_MAP_PLANES) { Com_Error(ERR_DROP, "Map has too many planes"); } out = map_planes; numplanes = count; for (i = 0; i < count; i++, in++, out++) { bits = 0; for (j = 0; j < 3; j++) { out->normal[j] = LittleFloat(in->normal[j]); if (out->normal[j] < 0) { bits |= 1 << j; } } out->dist = LittleFloat(in->dist); out->type = LittleLong(in->type); out->signbits = bits; } } void CMod_LoadLeafBrushes(lump_t *l) { int i; unsigned short *out; unsigned short *in; int count; in = (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); } count = l->filelen / sizeof(*in); if (count < 1) { Com_Error(ERR_DROP, "Map with no planes"); } /* need to save space for box planes */ if (count > MAX_MAP_LEAFBRUSHES) { Com_Error(ERR_DROP, "Map has too many leafbrushes"); } out = map_leafbrushes; numleafbrushes = count; for (i = 0; i < count; i++, in++, out++) { *out = LittleShort(*in); } } void CMod_LoadBrushSides(lump_t *l) { int i, j; cbrushside_t *out; dbrushside_t *in; int count; int num; in = (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); } count = l->filelen / sizeof(*in); /* need to save space for box planes */ if (count > MAX_MAP_BRUSHSIDES) { Com_Error(ERR_DROP, "Map has too many planes"); } out = map_brushsides; numbrushsides = count; for (i = 0; i < count; i++, in++, out++) { num = LittleShort(in->planenum); out->plane = &map_planes[num]; j = LittleShort(in->texinfo); if (j >= numtexinfo) { Com_Error(ERR_DROP, "Bad brushside texinfo"); } out->surface = &map_surfaces[j]; } } void CMod_LoadAreas(lump_t *l) { int i; carea_t *out; darea_t *in; int count; in = (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); } count = l->filelen / sizeof(*in); if (count > MAX_MAP_AREAS) { Com_Error(ERR_DROP, "Map has too many areas"); } out = map_areas; numareas = count; for (i = 0; i < count; i++, in++, out++) { out->numareaportals = LittleLong(in->numareaportals); out->firstareaportal = LittleLong(in->firstareaportal); out->floodvalid = 0; out->floodnum = 0; } } void CMod_LoadAreaPortals(lump_t *l) { dareaportal_t *out; dareaportal_t *in; int count; in = (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Com_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size"); } count = l->filelen / sizeof(*in); if (count > MAX_MAP_AREAS) { Com_Error(ERR_DROP, "Map has too many areas"); } out = map_areaportals; numareaportals = count; memcpy(out, in, sizeof(dareaportal_t) * count); } void CMod_LoadVisibility(lump_t *l) { numvisibility = l->filelen; if (l->filelen > MAX_MAP_VISIBILITY) { Com_Error(ERR_DROP, "Map has too large visibility lump"); } memcpy(map_visibility, cmod_base + l->fileofs, l->filelen); map_vis->numclusters = LittleLong(map_vis->numclusters); } void CMod_LoadEntityString(lump_t *l) { numentitychars = l->filelen; if (l->filelen > MAX_MAP_ENTSTRING) { Com_Error(ERR_DROP, "Map has too large entity lump"); } memcpy(map_entitystring, cmod_base + l->fileofs, l->filelen); } /* * Loads in the map and all submodels */ cmodel_t * CM_LoadMap(char *name, qboolean clientload, unsigned *checksum) { unsigned *buf; int i; dheader_t header; int length; static unsigned last_checksum; map_noareas = Cvar_Get("map_noareas", "0", 0); if (!strcmp(map_name, name) && (clientload || !Cvar_VariableValue("flushmap"))) { *checksum = last_checksum; if (!clientload) { memset(portalopen, 0, sizeof(portalopen)); FloodAreaConnections(); } return &map_cmodels[0]; /* still have the right version */ } /* free old stuff */ numplanes = 0; numnodes = 0; numleafs = 0; numcmodels = 0; numvisibility = 0; numentitychars = 0; map_entitystring[0] = 0; map_name[0] = 0; if (!name || !name[0]) { numleafs = 1; numclusters = 1; numareas = 1; *checksum = 0; return &map_cmodels[0]; /* cinematic servers won't have anything at all */ } length = FS_LoadFile(name, (void **)&buf); if (!buf) { Com_Error(ERR_DROP, "Couldn't load %s", name); } last_checksum = LittleLong(Com_BlockChecksum(buf, length)); *checksum = last_checksum; header = *(dheader_t *)buf; for (i = 0; i < sizeof(dheader_t) / 4; i++) { ((int *)&header)[i] = LittleLong(((int *)&header)[i]); } if (header.version != BSPVERSION) { Com_Error(ERR_DROP, "CMod_LoadBrushModel: %s has wrong version number (%i should be %i)", name, header.version, BSPVERSION); } cmod_base = (byte *)buf; /* load into heap */ CMod_LoadSurfaces(&header.lumps[LUMP_TEXINFO]); CMod_LoadLeafs(&header.lumps[LUMP_LEAFS]); CMod_LoadLeafBrushes(&header.lumps[LUMP_LEAFBRUSHES]); CMod_LoadPlanes(&header.lumps[LUMP_PLANES]); CMod_LoadBrushes(&header.lumps[LUMP_BRUSHES]); CMod_LoadBrushSides(&header.lumps[LUMP_BRUSHSIDES]); CMod_LoadSubmodels(&header.lumps[LUMP_MODELS]); CMod_LoadNodes(&header.lumps[LUMP_NODES]); CMod_LoadAreas(&header.lumps[LUMP_AREAS]); CMod_LoadAreaPortals(&header.lumps[LUMP_AREAPORTALS]); CMod_LoadVisibility(&header.lumps[LUMP_VISIBILITY]); CMod_LoadEntityString(&header.lumps[LUMP_ENTITIES]); FS_FreeFile(buf); CM_InitBoxHull(); memset(portalopen, 0, sizeof(portalopen)); FloodAreaConnections(); strcpy(map_name, name); return &map_cmodels[0]; } cmodel_t * CM_InlineModel(char *name) { int num; if (!name || (name[0] != '*')) { Com_Error(ERR_DROP, "CM_InlineModel: bad name"); } num = (int)strtol(name + 1, (char **)NULL, 10); if ((num < 1) || (num >= numcmodels)) { Com_Error(ERR_DROP, "CM_InlineModel: bad number"); } return &map_cmodels[num]; } int CM_NumClusters(void) { return numclusters; } int CM_NumInlineModels(void) { return numcmodels; } char * CM_EntityString(void) { return map_entitystring; } int CM_LeafContents(int leafnum) { if ((leafnum < 0) || (leafnum >= numleafs)) { Com_Error(ERR_DROP, "CM_LeafContents: bad number"); } return map_leafs[leafnum].contents; } int CM_LeafCluster(int leafnum) { if ((leafnum < 0) || (leafnum >= numleafs)) { Com_Error(ERR_DROP, "CM_LeafCluster: bad number"); } return map_leafs[leafnum].cluster; } int CM_LeafArea(int leafnum) { if ((leafnum < 0) || (leafnum >= numleafs)) { Com_Error(ERR_DROP, "CM_LeafArea: bad number"); } return map_leafs[leafnum].area; } void CM_DecompressVis(byte *in, byte *out) { int c; byte *out_p; int row; row = (numclusters + 7) >> 3; out_p = out; if (!in || !numvisibility) { /* no vis info, so make all visible */ while (row) { *out_p++ = 0xff; row--; } return; } do { if (*in) { *out_p++ = *in++; continue; } c = in[1]; in += 2; if ((out_p - out) + c > row) { c = row - (out_p - out); Com_DPrintf("warning: Vis decompression overrun\n"); } while (c) { *out_p++ = 0; c--; } } while (out_p - out < row); } byte * CM_ClusterPVS(int cluster) { if (cluster == -1) { memset(pvsrow, 0, (numclusters + 7) >> 3); } else { CM_DecompressVis(map_visibility + LittleLong(map_vis->bitofs[cluster][DVIS_PVS]), pvsrow); } return pvsrow; } byte * CM_ClusterPHS(int cluster) { if (cluster == -1) { memset(phsrow, 0, (numclusters + 7) >> 3); } else { CM_DecompressVis(map_visibility + LittleLong(map_vis->bitofs[cluster][DVIS_PHS]), phsrow); } return phsrow; } yquake2-QUAKE2_5_32/src/common/argproc.c0000644000175000017500000000567112615162034020510 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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 argument processing * * ======================================================================= */ #include "header/common.h" #define MAX_NUM_ARGVS 50 int com_argc; char *com_argv[MAX_NUM_ARGVS + 1]; /* * 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; i < com_argc; i++) { if (!strcmp(parm, com_argv[i])) { return i; } } return 0; } int COM_Argc(void) { return com_argc; } char * COM_Argv(int arg) { if ((arg < 0) || (arg >= com_argc) || !com_argv[arg]) { return ""; } return com_argv[arg]; } void COM_ClearArgv(int arg) { if ((arg < 0) || (arg >= com_argc) || !com_argv[arg]) { return; } com_argv[arg] = ""; } void COM_InitArgv(int argc, char **argv) { int i; if (argc > MAX_NUM_ARGVS) { Com_Error(ERR_FATAL, "argc > MAX_NUM_ARGVS"); } com_argc = argc; for (i = 0; i < argc; i++) { if (!argv[i] || (strlen(argv[i]) >= MAX_TOKEN_CHARS)) { com_argv[i] = ""; } else { com_argv[i] = argv[i]; } } } /* * Adds the given string at the end of the current argument list */ void COM_AddParm(char *parm) { if (com_argc == MAX_NUM_ARGVS) { Com_Error(ERR_FATAL, "COM_AddParm: MAX_NUM)ARGS"); } com_argv[com_argc++] = parm; } int memsearch(byte *start, int count, int search) { int i; for (i = 0; i < count; i++) { if (start[i] == search) { return i; } } return -1; } char * CopyString(char *in) { char *out; out = Z_Malloc((int)strlen(in) + 1); strcpy(out, in); return out; } void Info_Print(char *s) { char key[512]; char value[512]; char *o; int l; if (*s == '\\') { s++; } while (*s) { o = key; while (*s && *s != '\\') { *o++ = *s++; } l = o - key; if (l < 20) { memset(o, ' ', 20 - l); key[20] = 0; } else { *o = 0; } Com_Printf("%s", key); if (!*s) { Com_Printf("MISSING VALUE\n"); return; } o = value; s++; while (*s && *s != '\\') { *o++ = *s++; } *o = 0; if (*s) { s++; } Com_Printf("%s\n", value); } } yquake2-QUAKE2_5_32/src/common/glob.c0000644000175000017500000000751512615162034017775 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Global string matching * * ======================================================================= */ #include #include "header/glob.h" /* * Like glob_match, but match PATTERN against any final segment of TEXT. */ static int glob_match_after_star(char *pattern, char *text) { register char *p = pattern, *t = text; register char c, c1; while ((c = *p++) == '?' || c == '*') { if ((c == '?') && (*t++ == '\0')) { return 0; } } if (c == '\0') { return 1; } if (c == '\\') { c1 = *p; } else { c1 = c; } while (1) { if (((c == '[') || (*t == c1)) && glob_match(p - 1, t)) { return 1; } if (*t++ == '\0') { return 0; } } } /* Match the pattern PATTERN against the string TEXT; * return 1 if it matches, 0 otherwise. * * A match means the entire string TEXT is used up in matching. * * In the pattern string, `*' matches any sequence of characters, * `?' matches any character, [SET] matches any character in the specified set, * [!SET] matches any character not in the specified set. * * A set is composed of characters or ranges; a range looks like * character hyphen character (as in 0-9 or A-Z). * [0-9a-zA-Z_] is the set of characters allowed in C identifiers. * Any other character in the pattern must be matched exactly. * * To suppress the special syntactic significance of any of `[]*?!-\', * and match the character exactly, precede it with a `\'. */ int glob_match(char *pattern, char *text) { register char *p = pattern, *t = text; register char c; while ((c = *p++) != '\0') { switch (c) { case '?': if (*t == '\0') { return 0; } else { ++t; } break; case '\\': if (*p++ != *t++) { return 0; } break; case '*': return glob_match_after_star(p, t); case '[': { register char c1 = *t++; int invert; if (!c1) { return 0; } invert = ((*p == '!') || (*p == '^')); if (invert) { p++; } c = *p++; while (1) { register char cstart = c, cend = c; if (c == '\\') { cstart = *p++; cend = cstart; } if (c == '\0') { return 0; } c = *p++; if ((c == '-') && (*p != ']')) { cend = *p++; if (cend == '\\') { cend = *p++; } if (cend == '\0') { return 0; } c = *p++; } if ((c1 >= cstart) && (c1 <= cend)) { goto match; } if (c == ']') { break; } } if (!invert) { return 0; } break; match: /* Skip the rest of the [...] construct that already matched. */ while (c != ']') { if (c == '\0') { return 0; } c = *p++; if (c == '\0') { return 0; } else if (c == '\\') { ++p; } } if (invert) { return 0; } break; } default: if (c != *t++) { return 0; } } } return *t == '\0'; } yquake2-QUAKE2_5_32/src/common/misc.c0000644000175000017500000003001112615162034017770 0ustar greffrathgreffrath/* * Copyright (C) 1997-2001 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. * * ======================================================================= * * Misc. common stuff * * ======================================================================= */ #include "header/common.h" #include "header/zone.h" #include FILE *log_stats_file; cvar_t *host_speeds; cvar_t *log_stats; cvar_t *developer; cvar_t *modder; cvar_t *timescale; cvar_t *fixedtime; #ifndef DEDICATED_ONLY cvar_t *showtrace; #endif cvar_t *dedicated; extern cvar_t *logfile_active; extern jmp_buf abortframe; /* an ERR_DROP occured, exit the entire frame */ extern zhead_t z_chain; static byte chktbl[1024] = { 0x84, 0x47, 0x51, 0xc1, 0x93, 0x22, 0x21, 0x24, 0x2f, 0x66, 0x60, 0x4d, 0xb0, 0x7c, 0xda, 0x88, 0x54, 0x15, 0x2b, 0xc6, 0x6c, 0x89, 0xc5, 0x9d, 0x48, 0xee, 0xe6, 0x8a, 0xb5, 0xf4, 0xcb, 0xfb, 0xf1, 0x0c, 0x2e, 0xa0, 0xd7, 0xc9, 0x1f, 0xd6, 0x06, 0x9a, 0x09, 0x41, 0x54, 0x67, 0x46, 0xc7, 0x74, 0xe3, 0xc8, 0xb6, 0x5d, 0xa6, 0x36, 0xc4, 0xab, 0x2c, 0x7e, 0x85, 0xa8, 0xa4, 0xa6, 0x4d, 0x96, 0x19, 0x19, 0x9a, 0xcc, 0xd8, 0xac, 0x39, 0x5e, 0x3c, 0xf2, 0xf5, 0x5a, 0x72, 0xe5, 0xa9, 0xd1, 0xb3, 0x23, 0x82, 0x6f, 0x29, 0xcb, 0xd1, 0xcc, 0x71, 0xfb, 0xea, 0x92, 0xeb, 0x1c, 0xca, 0x4c, 0x70, 0xfe, 0x4d, 0xc9, 0x67, 0x43, 0x47, 0x94, 0xb9, 0x47, 0xbc, 0x3f, 0x01, 0xab, 0x7b, 0xa6, 0xe2, 0x76, 0xef, 0x5a, 0x7a, 0x29, 0x0b, 0x51, 0x54, 0x67, 0xd8, 0x1c, 0x14, 0x3e, 0x29, 0xec, 0xe9, 0x2d, 0x48, 0x67, 0xff, 0xed, 0x54, 0x4f, 0x48, 0xc0, 0xaa, 0x61, 0xf7, 0x78, 0x12, 0x03, 0x7a, 0x9e, 0x8b, 0xcf, 0x83, 0x7b, 0xae, 0xca, 0x7b, 0xd9, 0xe9, 0x53, 0x2a, 0xeb, 0xd2, 0xd8, 0xcd, 0xa3, 0x10, 0x25, 0x78, 0x5a, 0xb5, 0x23, 0x06, 0x93, 0xb7, 0x84, 0xd2, 0xbd, 0x96, 0x75, 0xa5, 0x5e, 0xcf, 0x4e, 0xe9, 0x50, 0xa1, 0xe6, 0x9d, 0xb1, 0xe3, 0x85, 0x66, 0x28, 0x4e, 0x43, 0xdc, 0x6e, 0xbb, 0x33, 0x9e, 0xf3, 0x0d, 0x00, 0xc1, 0xcf, 0x67, 0x34, 0x06, 0x7c, 0x71, 0xe3, 0x63, 0xb7, 0xb7, 0xdf, 0x92, 0xc4, 0xc2, 0x25, 0x5c, 0xff, 0xc3, 0x6e, 0xfc, 0xaa, 0x1e, 0x2a, 0x48, 0x11, 0x1c, 0x36, 0x68, 0x78, 0x86, 0x79, 0x30, 0xc3, 0xd6, 0xde, 0xbc, 0x3a, 0x2a, 0x6d, 0x1e, 0x46, 0xdd, 0xe0, 0x80, 0x1e, 0x44, 0x3b, 0x6f, 0xaf, 0x31, 0xda, 0xa2, 0xbd, 0x77, 0x06, 0x56, 0xc0, 0xb7, 0x92, 0x4b, 0x37, 0xc0, 0xfc, 0xc2, 0xd5, 0xfb, 0xa8, 0xda, 0xf5, 0x57, 0xa8, 0x18, 0xc0, 0xdf, 0xe7, 0xaa, 0x2a, 0xe0, 0x7c, 0x6f, 0x77, 0xb1, 0x26, 0xba, 0xf9, 0x2e, 0x1d, 0x16, 0xcb, 0xb8, 0xa2, 0x44, 0xd5, 0x2f, 0x1a, 0x79, 0x74, 0x87, 0x4b, 0x00, 0xc9, 0x4a, 0x3a, 0x65, 0x8f, 0xe6, 0x5d, 0xe5, 0x0a, 0x77, 0xd8, 0x1a, 0x14, 0x41, 0x75, 0xb1, 0xe2, 0x50, 0x2c, 0x93, 0x38, 0x2b, 0x6d, 0xf3, 0xf6, 0xdb, 0x1f, 0xcd, 0xff, 0x14, 0x70, 0xe7, 0x16, 0xe8, 0x3d, 0xf0, 0xe3, 0xbc, 0x5e, 0xb6, 0x3f, 0xcc, 0x81, 0x24, 0x67, 0xf3, 0x97, 0x3b, 0xfe, 0x3a, 0x96, 0x85, 0xdf, 0xe4, 0x6e, 0x3c, 0x85, 0x05, 0x0e, 0xa3, 0x2b, 0x07, 0xc8, 0xbf, 0xe5, 0x13, 0x82, 0x62, 0x08, 0x61, 0x69, 0x4b, 0x47, 0x62, 0x73, 0x44, 0x64, 0x8e, 0xe2, 0x91, 0xa6, 0x9a, 0xb7, 0xe9, 0x04, 0xb6, 0x54, 0x0c, 0xc5, 0xa9, 0x47, 0xa6, 0xc9, 0x08, 0xfe, 0x4e, 0xa6, 0xcc, 0x8a, 0x5b, 0x90, 0x6f, 0x2b, 0x3f, 0xb6, 0x0a, 0x96, 0xc0, 0x78, 0x58, 0x3c, 0x76, 0x6d, 0x94, 0x1a, 0xe4, 0x4e, 0xb8, 0x38, 0xbb, 0xf5, 0xeb, 0x29, 0xd8, 0xb0, 0xf3, 0x15, 0x1e, 0x99, 0x96, 0x3c, 0x5d, 0x63, 0xd5, 0xb1, 0xad, 0x52, 0xb8, 0x55, 0x70, 0x75, 0x3e, 0x1a, 0xd5, 0xda, 0xf6, 0x7a, 0x48, 0x7d, 0x44, 0x41, 0xf9, 0x11, 0xce, 0xd7, 0xca, 0xa5, 0x3d, 0x7a, 0x79, 0x7e, 0x7d, 0x25, 0x1b, 0x77, 0xbc, 0xf7, 0xc7, 0x0f, 0x84, 0x95, 0x10, 0x92, 0x67, 0x15, 0x11, 0x5a, 0x5e, 0x41, 0x66, 0x0f, 0x38, 0x03, 0xb2, 0xf1, 0x5d, 0xf8, 0xab, 0xc0, 0x02, 0x76, 0x84, 0x28, 0xf4, 0x9d, 0x56, 0x46, 0x60, 0x20, 0xdb, 0x68, 0xa7, 0xbb, 0xee, 0xac, 0x15, 0x01, 0x2f, 0x20, 0x09, 0xdb, 0xc0, 0x16, 0xa1, 0x89, 0xf9, 0x94, 0x59, 0x00, 0xc1, 0x76, 0xbf, 0xc1, 0x4d, 0x5d, 0x2d, 0xa9, 0x85, 0x2c, 0xd6, 0xd3, 0x14, 0xcc, 0x02, 0xc3, 0xc2, 0xfa, 0x6b, 0xb7, 0xa6, 0xef, 0xdd, 0x12, 0x26, 0xa4, 0x63, 0xe3, 0x62, 0xbd, 0x56, 0x8a, 0x52, 0x2b, 0xb9, 0xdf, 0x09, 0xbc, 0x0e, 0x97, 0xa9, 0xb0, 0x82, 0x46, 0x08, 0xd5, 0x1a, 0x8e, 0x1b, 0xa7, 0x90, 0x98, 0xb9, 0xbb, 0x3c, 0x17, 0x9a, 0xf2, 0x82, 0xba, 0x64, 0x0a, 0x7f, 0xca, 0x5a, 0x8c, 0x7c, 0xd3, 0x79, 0x09, 0x5b, 0x26, 0xbb, 0xbd, 0x25, 0xdf, 0x3d, 0x6f, 0x9a, 0x8f, 0xee, 0x21, 0x66, 0xb0, 0x8d, 0x84, 0x4c, 0x91, 0x45, 0xd4, 0x77, 0x4f, 0xb3, 0x8c, 0xbc, 0xa8, 0x99, 0xaa, 0x19, 0x53, 0x7c, 0x02, 0x87, 0xbb, 0x0b, 0x7c, 0x1a, 0x2d, 0xdf, 0x48, 0x44, 0x06, 0xd6, 0x7d, 0x0c, 0x2d, 0x35, 0x76, 0xae, 0xc4, 0x5f, 0x71, 0x85, 0x97, 0xc4, 0x3d, 0xef, 0x52, 0xbe, 0x00, 0xe4, 0xcd, 0x49, 0xd1, 0xd1, 0x1c, 0x3c, 0xd0, 0x1c, 0x42, 0xaf, 0xd4, 0xbd, 0x58, 0x34, 0x07, 0x32, 0xee, 0xb9, 0xb5, 0xea, 0xff, 0xd7, 0x8c, 0x0d, 0x2e, 0x2f, 0xaf, 0x87, 0xbb, 0xe6, 0x52, 0x71, 0x22, 0xf5, 0x25, 0x17, 0xa1, 0x82, 0x04, 0xc2, 0x4a, 0xbd, 0x57, 0xc6, 0xab, 0xc8, 0x35, 0x0c, 0x3c, 0xd9, 0xc2, 0x43, 0xdb, 0x27, 0x92, 0xcf, 0xb8, 0x25, 0x60, 0xfa, 0x21, 0x3b, 0x04, 0x52, 0xc8, 0x96, 0xba, 0x74, 0xe3, 0x67, 0x3e, 0x8e, 0x8d, 0x61, 0x90, 0x92, 0x59, 0xb6, 0x1a, 0x1c, 0x5e, 0x21, 0xc1, 0x65, 0xe5, 0xa6, 0x34, 0x05, 0x6f, 0xc5, 0x60, 0xb1, 0x83, 0xc1, 0xd5, 0xd5, 0xed, 0xd9, 0xc7, 0x11, 0x7b, 0x49, 0x7a, 0xf9, 0xf9, 0x84, 0x47, 0x9b, 0xe2, 0xa5, 0x82, 0xe0, 0xc2, 0x88, 0xd0, 0xb2, 0x58, 0x88, 0x7f, 0x45, 0x09, 0x67, 0x74, 0x61, 0xbf, 0xe6, 0x40, 0xe2, 0x9d, 0xc2, 0x47, 0x05, 0x89, 0xed, 0xcb, 0xbb, 0xb7, 0x27, 0xe7, 0xdc, 0x7a, 0xfd, 0xbf, 0xa8, 0xd0, 0xaa, 0x10, 0x39, 0x3c, 0x20, 0xf0, 0xd3, 0x6e, 0xb1, 0x72, 0xf8, 0xe6, 0x0f, 0xef, 0x37, 0xe5, 0x09, 0x33, 0x5a, 0x83, 0x43, 0x80, 0x4f, 0x65, 0x2f, 0x7c, 0x8c, 0x6a, 0xa0, 0x82, 0x0c, 0xd4, 0xd4, 0xfa, 0x81, 0x60, 0x3d, 0xdf, 0x06, 0xf1, 0x5f, 0x08, 0x0d, 0x6d, 0x43, 0xf2, 0xe3, 0x11, 0x7d, 0x80, 0x32, 0xc5, 0xfb, 0xc5, 0xd9, 0x27, 0xec, 0xc6, 0x4e, 0x65, 0x27, 0x76, 0x87, 0xa6, 0xee, 0xee, 0xd7, 0x8b, 0xd1, 0xa0, 0x5c, 0xb0, 0x42, 0x13, 0x0e, 0x95, 0x4a, 0xf2, 0x06, 0xc6, 0x43, 0x33, 0xf4, 0xc7, 0xf8, 0xe7, 0x1f, 0xdd, 0xe4, 0x46, 0x4a, 0x70, 0x39, 0x6c, 0xd0, 0xed, 0xca, 0xbe, 0x60, 0x3b, 0xd1, 0x7b, 0x57, 0x48, 0xe5, 0x3a, 0x79, 0xc1, 0x69, 0x33, 0x53, 0x1b, 0x80, 0xb8, 0x91, 0x7d, 0xb4, 0xf6, 0x17, 0x1a, 0x1d, 0x5a, 0x32, 0xd6, 0xcc, 0x71, 0x29, 0x3f, 0x28, 0xbb, 0xf3, 0x5e, 0x71, 0xb8, 0x43, 0xaf, 0xf8, 0xb9, 0x64, 0xef, 0xc4, 0xa5, 0x6c, 0x08, 0x53, 0xc7, 0x00, 0x10, 0x39, 0x4f, 0xdd, 0xe4, 0xb6, 0x19, 0x27, 0xfb, 0xb8, 0xf5, 0x32, 0x73, 0xe5, 0xcb, 0x32 }; /* host_speeds times */ int time_before_game; int time_after_game; int time_before_ref; int time_after_ref; /* * For proxy protecting */ byte COM_BlockSequenceCRCByte(byte *base, int length, int sequence) { int n; int x; byte *p; byte chkb[60 + 4]; unsigned short crc; byte r; if (sequence < 0) { Sys_Error("sequence < 0, this shouldn't happen\n"); } p = chktbl + (sequence % (sizeof(chktbl) - 4)); if (length > 60) { length = 60; } memcpy(chkb, base, length); chkb[length] = p[0]; chkb[length + 1] = p[1]; chkb[length + 2] = p[2]; chkb[length + 3] = p[3]; length += 4; crc = CRC_Block(chkb, length); for (x = 0, n = 0; n < length; n++) { x += chkb[n]; } r = (crc ^ x) & 0xff; return r; } #ifndef DEDICATED_ONLY void Key_Init(void); void SCR_EndLoadingPlaque(void); #endif /* * Just throw a fatal error to * test error shutdown procedures */ void Com_Error_f(void) { Com_Error(ERR_FATAL, "%s", Cmd_Argv(1)); } void Qcommon_Init(int argc, char **argv) { char *s; if (setjmp(abortframe)) { Sys_Error("Error during initialization"); } z_chain.next = z_chain.prev = &z_chain; /* prepare enough of the subsystems to handle cvar and command buffer management */ COM_InitArgv(argc, argv); Swap_Init(); Cbuf_Init(); Cmd_Init(); Cvar_Init(); #ifndef DEDICATED_ONLY Key_Init(); #endif /* we need to add the early commands twice, because a basedir or cddir needs to be set before execing config files, but we want other parms to override the settings of the config files */ Cbuf_AddEarlyCommands(false); Cbuf_Execute(); FS_InitFilesystem(); Cbuf_AddText("exec default.cfg\n"); Cbuf_AddText("exec yq2.cfg\n"); Cbuf_AddText("exec config.cfg\n"); Cbuf_AddEarlyCommands(true); Cbuf_Execute(); /* init commands and vars */ Cmd_AddCommand("z_stats", Z_Stats_f); Cmd_AddCommand("error", Com_Error_f); host_speeds = Cvar_Get("host_speeds", "0", 0); log_stats = Cvar_Get("log_stats", "0", 0); developer = Cvar_Get("developer", "0", 0); modder = Cvar_Get("modder", "0", 0); timescale = Cvar_Get("timescale", "1", 0); fixedtime = Cvar_Get("fixedtime", "0", 0); logfile_active = Cvar_Get("logfile", "1", CVAR_ARCHIVE); #ifndef DEDICATED_ONLY showtrace = Cvar_Get("showtrace", "0", 0); #endif #ifdef DEDICATED_ONLY dedicated = Cvar_Get("dedicated", "1", CVAR_NOSET); #else dedicated = Cvar_Get("dedicated", "0", CVAR_NOSET); #endif s = va("%s %s %s %s", YQ2VERSION, CPUSTRING, __DATE__, BUILDSTRING); Cvar_Get("version", s, CVAR_SERVERINFO | CVAR_NOSET); if (dedicated->value) { Cmd_AddCommand("quit", Com_Quit); } Sys_Init(); NET_Init(); Netchan_Init(); SV_Init(); #ifndef DEDICATED_ONLY CL_Init(); #endif /* add + commands from command line */ if (!Cbuf_AddLateCommands()) { /* if the user didn't give any commands, run default action */ if (!dedicated->value) { Cbuf_AddText("d1\n"); } else { Cbuf_AddText("dedicated_start\n"); } Cbuf_Execute(); } #ifndef DEDICATED_ONLY else { /* the user asked for something explicit so drop the loading plaque */ SCR_EndLoadingPlaque(); } #endif Com_Printf("==== Yamagi Quake II Initialized ====\n\n"); Com_Printf("*************************************\n\n"); } void Qcommon_Frame(int msec) { char *s; #ifndef DEDICATED_ONLY int time_before = 0; int time_between = 0; int time_after; #endif if (setjmp(abortframe)) { return; /* an ERR_DROP was thrown */ } if (log_stats->modified) { log_stats->modified = false; if (log_stats->value) { if (log_stats_file) { fclose(log_stats_file); log_stats_file = 0; } log_stats_file = fopen("stats.log", "w"); if (log_stats_file) { fprintf(log_stats_file, "entities,dlights,parts,frame time\n"); } } else { if (log_stats_file) { fclose(log_stats_file); log_stats_file = 0; } } } if (fixedtime->value) { msec = fixedtime->value; } else if (timescale->value) { msec *= timescale->value; if (msec < 1) { msec = 1; } } #ifndef DEDICATED_ONLY if (showtrace->value) { extern int c_traces, c_brush_traces; extern int c_pointcontents; Com_Printf("%4i traces %4i points\n", c_traces, c_pointcontents); c_traces = 0; c_brush_traces = 0; c_pointcontents = 0; } #endif do { s = Sys_ConsoleInput(); if (s) { Cbuf_AddText(va("%s\n", s)); } } while (s); Cbuf_Execute(); #ifndef DEDICATED_ONLY if (host_speeds->value) { time_before = Sys_Milliseconds(); } #endif SV_Frame(msec); #ifndef DEDICATED_ONLY if (host_speeds->value) { time_between = Sys_Milliseconds(); } CL_Frame(msec); if (host_speeds->value) { int all, sv, gm, cl, rf; time_after = Sys_Milliseconds(); all = time_after - time_before; sv = time_between - time_before; cl = time_after - time_between; gm = time_after_game - time_before_game; rf = time_after_ref - time_before_ref; sv -= gm; cl -= rf; Com_Printf("all:%3i sv:%3i gm:%3i cl:%3i rf:%3i\n", all, sv, gm, cl, rf); } #endif } void Qcommon_Shutdown(void) { } yquake2-QUAKE2_5_32/Makefile0000755000175000017500000004743412615162034016276 0ustar greffrathgreffrath# ------------------------------------------------------ # # Makefile for the "Yamagi Quake 2 Client" # # # # Just type "make" to compile the # # - SDL Client (quake2) # # - Server (q2ded) # # - Quake II Game (baseq2) # # # # Base dependencies: # # - SDL 1.2 or SDL 2.0 # # - libGL # # # # Further dependencies: # # - libogg # # - libvorbis # # - OpenAL # # - zlib # # # # Platforms: # # - FreeBSD # # - Linux # # - OpenBSD # # - OS X # # - Windows (MinGW) # # ------------------------------------------------------ # # User configurable options # ------------------------- # Enables CD audio playback. CD audio playback is used # for the background music and doesn't add any further # dependencies. It should work on all platforms where # CD playback is supported by SDL. WITH_CDA:=yes # Enables OGG/Vorbis support. OGG/Vorbis files can be # used as a substitute of CD audio playback. Adds # dependencies to libogg, libvorbis and libvorbisfile. WITH_OGG:=yes # Enables the optional OpenAL sound system. # To use it your system needs libopenal.so.1 # or openal32.dll (we recommend openal-soft) # installed WITH_OPENAL:=yes # Use SDL2 instead of SDL1.2. Disables CD audio support, # because SDL2 has none. Use OGG/Vorbis music instead :-) # On Windows sdl-config isn't used, so make sure that # you've got the SDL2 headers and libs installed. WITH_SDL2:=yes # Set the gamma via X11 and not via SDL. This works # around problems in some SDL version. Adds dependencies # to pkg-config, libX11 and libXxf86vm. Unsupported on # Windows and OS X. WITH_X11GAMMA:=no # Enables opening of ZIP files (also known as .pk3 paks). # Adds a dependency to libz WITH_ZIP:=yes # Enable systemwide installation of game assets WITH_SYSTEMWIDE:=no # This will set the default SYSTEMDIR, a non-empty string # would actually be used. On Windows normals slashes (/) # instead of backslashed (\) should be used! The string # MUST NOT be surrounded by quotation marks! WITH_SYSTEMDIR:="" # This will set the architectures of the OSX-binaries. # You have to make sure your libs/frameworks supports # these architectures! To build an universal ppc-compatible # one would add -arch ppc for example. OSX_ARCH:=-arch $(shell uname -m | sed -e s/i.86/i386/) # This will set the build options to create an MacOS .app-bundle. # The app-bundle itself will not be created, but the runtime paths # will be set to expect the game-data in *.app/ # Contents/Resources OSX_APP:=yes # This is an optional configuration file, it'll be used in # case of presence. CONFIG_FILE := config.mk # ---------- # In case a of a configuration file being present, we'll just use it ifeq ($(wildcard $(CONFIG_FILE)), $(CONFIG_FILE)) include $(CONFIG_FILE) endif # Detect the OS ifdef SystemRoot OSTYPE := Windows else OSTYPE := $(shell uname -s) endif # Special case for MinGW ifneq (,$(findstring MINGW,$(OSTYPE))) OSTYPE := Windows endif # Detect the architecture ifeq ($(OSTYPE), Windows) # At this time only i386 is supported on Windows # (amd64 works, but building an 64 bit executable # is not that easy. Especially SDL and OpenAL are # somewhat problematic) ARCH ?= i386 else # Some platforms call it "amd64" and some "x86_64" ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/amd64/x86_64/) endif # Refuse all other platforms as a firewall against PEBKAC # (You'll need some #ifdef for your unsupported plattform!) ifeq ($(findstring $(ARCH), i386 x86_64 sparc64 ia64),) $(error arch $(ARCH) is currently not supported) endif # Disable CDA for SDL2 ifeq ($(WITH_SDL2),yes) ifeq ($(WITH_CDA),yes) WITH_CDA:=no # Evil hack to tell the "all" target # that CDA was disabled because SDL2 # is enabled. CDA_DISABLED:=yes endif endif # ---------- # Base CFLAGS. # # -O2 are enough optimizations. # # -fno-strict-aliasing since the source doesn't comply # with strict aliasing rules and it's next to impossible # to get it there... # # -fomit-frame-pointer since the framepointer is mostly # useless for debugging Quake II and slows things down. # # -g to build always with debug symbols. Please DO NOT # CHANGE THIS, since it's our only chance to debug this # crap when random crashes happen! # # -MMD to generate header dependencies. (They cannot be # generated if building universal binaries on OSX) ifeq ($(OSTYPE), Darwin) CFLAGS := -O2 -fno-strict-aliasing -fomit-frame-pointer \ -Wall -pipe -g -fwrapv #-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.5.sdk CFLAGS += $(OSX_ARCH) else CFLAGS := -O2 -fno-strict-aliasing -fomit-frame-pointer \ -Wall -pipe -g -ggdb -MMD -fwrapv endif # ---------- # Systemwide installation ifeq ($(WITH_SYSTEMWIDE),yes) CFLAGS += -DSYSTEMWIDE ifneq ($(WITH_SYSTEMDIR),"") CFLAGS += -DSYSTEMDIR=\"$(WITH_SYSTEMDIR)\" endif endif # ---------- # On Windows / MinGW $(CC) is # undefined by default. ifeq ($(OSTYPE),Windows) CC := gcc endif # ---------- # Extra CFLAGS for SDL ifeq ($(WITH_SDL2),yes) ifeq ($(OSTYPE),Windows) SDLCFLAGS := $(shell /custom/bin/sdl2-config --cflags) else SDLCFLAGS := $(shell sdl2-config --cflags) endif else # not SDL2 ifeq ($(OSTYPE),Windows) SDLCFLAGS := else SDLCFLAGS := $(shell sdl-config --cflags) endif endif # SDL2 # ---------- # Extra CFLAGS for X11 ifneq ($(OSTYPE), Windows) ifneq ($(OSTYPE), Darwin) ifeq ($(WITH_X11GAMMA),yes) X11CFLAGS := $(shell pkg-config x11 --cflags) X11CFLAGS += $(shell pkg-config xxf86vm --cflags) endif endif endif # ---------- # Base include path. ifeq ($(OSTYPE),Linux) INCLUDE := -I/usr/include else ifeq ($(OSTYPE),FreeBSD) INCLUDE := -I/usr/local/include else ifeq ($(OSTYPE),OpenBSD) INCLUDE := -I/usr/local/include else ifeq ($(OSTYPE),Windows) INCLUDE := -I/custom/include endif # ---------- # Base LDFLAGS. ifeq ($(OSTYPE),Linux) LDFLAGS := -L/usr/lib -lm -ldl -rdynamic else ifeq ($(OSTYPE),FreeBSD) LDFLAGS := -L/usr/local/lib -lm else ifeq ($(OSTYPE),OpenBSD) LDFLAGS := -L/usr/local/lib -lm else ifeq ($(OSTYPE),Windows) LDFLAGS := -L/custom/lib -lws2_32 -lwinmm else ifeq ($(OSTYPE), Darwin) LDFLAGS := $(OSX_ARCH) -lm endif # ---------- # Extra LDFLAGS for SDL ifeq ($(OSTYPE), Windows) ifeq ($(WITH_SDL2),yes) SDLLDFLAGS := $(shell /custom/bin/sdl2-config --libs) else # not SDL2 SDLLDFLAGS := -lSDL endif # SDL2 else ifeq ($(OSTYPE), Darwin) ifeq ($(WITH_SDL2),yes) SDLLDFLAGS := -lSDL2 -framework OpenGL -framework Cocoa else # not SDL2 SDLLDFLAGS := -lSDL -framework OpenGL -framework Cocoa endif # SDL2 else # not Darwin/Win ifeq ($(WITH_SDL2),yes) SDLLDFLAGS := $(shell sdl2-config --libs) else # not SDL2 SDLLDFLAGS := $(shell sdl-config --libs) endif # SDL2 endif # Darwin/Win # ---------- # Extra LDFLAGS for X11 ifneq ($(OSTYPE), Windows) ifneq ($(OSTYPE), Darwin) ifeq ($(WITH_X11GAMMA),yes) X11LDFLAGS := $(shell pkg-config x11 --libs) X11LDFLAGS += $(shell pkg-config xxf86vm --libs) X11LDFLAGS += $(shell pkg-config xrandr --libs) endif endif endif # ---------- # When make is invoked by "make VERBOSE=1" print # the compiler and linker commands. ifdef VERBOSE Q := else Q := @ endif # ---------- # Phony targets .PHONY : all client game icon server # ---------- # Builds everything all: config client server game # Print config values config: @echo "Build configuration" @echo "============================" @echo "WITH_CDA = $(WITH_CDA)" @echo "WITH_OPENAL = $(WITH_OPENAL)" @echo "WITH_SDL2 = $(WITH_SDL2)" @echo "WITH_X11GAMMA = $(WITH_X11GAMMA)" @echo "WITH_ZIP = $(WITH_ZIP)" @echo "WITH_SYSTEMWIDE = $(WITH_SYSTEMWIDE)" @echo "WITH_SYSTEMDIR = $(WITH_SYSTEMDIR)" @echo "============================" @echo "" ifeq ($(WITH_SDL2),yes) ifeq ($(CDA_DISABLED),yes) @echo "WARNING: CDA disabled because SDL2 doesn't support it!" @echo "" endif endif # ---------- # Special target to compile # the icon on Windows ifeq ($(OSTYPE), Windows) icon: @echo "===> WR build/icon/icon.res" ${Q}mkdir -p build/icon ${Q}windres stuff\icon\icon.rc -O COFF -o build\icon\icon.res endif # ---------- # Cleanup clean: @echo "===> CLEAN" ${Q}rm -Rf build release # ---------- # The client ifeq ($(OSTYPE), Windows) client: @echo "===> Building quake2.exe" ${Q}mkdir -p release $(MAKE) release/quake2.exe build/client/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(SDLCFLAGS) $(INCLUDE) -o $@ $< ifeq ($(WITH_CDA),yes) release/quake2.exe : CFLAGS += -DCDA endif ifeq ($(WITH_OGG),yes) release/quake2.exe : CFLAGS += -DOGG release/quake2.exe : LDFLAGS += -lvorbisfile -lvorbis -logg endif ifeq ($(WITH_OPENAL),yes) release/quake2.exe : CFLAGS += -DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER='"openal32.dll"' endif ifeq ($(WITH_ZIP),yes) release/quake2.exe : CFLAGS += -DZIP -DNOUNCRYPT release/quake2.exe : LDFLAGS += -lz endif ifeq ($(WITH_SDL2),yes) release/quake2.exe : CFLAGS += -DSDL2 endif release/quake2.exe : LDFLAGS += -mwindows -lopengl32 else # not Windows client: @echo "===> Building quake2" ${Q}mkdir -p release $(MAKE) release/quake2 build/client/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(SDLCFLAGS) $(X11CFLAGS) $(INCLUDE) -o $@ $< ifeq ($(OSTYPE), Darwin) build/client/%.o : %.m @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) $(OSX_ARCH) -x objective-c -c $< -o $@ endif ifeq ($(WITH_CDA),yes) release/quake2 : CFLAGS += -DCDA endif ifeq ($(WITH_OGG),yes) release/quake2 : CFLAGS += -DOGG release/quake2 : LDFLAGS += -lvorbis -lvorbisfile -logg endif ifeq ($(WITH_OPENAL),yes) ifeq ($(OSTYPE), OpenBSD) release/quake2 : CFLAGS += -DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER='"libopenal.so"' else ifeq ($(OSTYPE), Darwin) release/quake2 : CFLAGS += -DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER='"libopenal.dylib"' else release/quake2 : CFLAGS += -DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER='"libopenal.so.1"' endif endif ifeq ($(WITH_ZIP),yes) release/quake2 : CFLAGS += -DZIP -DNOUNCRYPT release/quake2 : LDFLAGS += -lz endif ifeq ($(WITH_X11GAMMA),yes) release/quake2 : CFLAGS += -DX11GAMMA endif ifeq ($(WITH_SDL2),yes) release/quake2 : CFLAGS += -DSDL2 endif ifneq ($(OSTYPE), Darwin) release/quake2 : LDFLAGS += -lGL endif ifeq ($(OSTYPE), FreeBSD) release/quake2 : LDFLAGS += -Wl,-z,origin,-rpath='$$ORIGIN/lib' else ifeq ($(OSTYPE), Linux) release/quake2 : LDFLAGS += -Wl,-z,origin,-rpath='$$ORIGIN/lib' endif ifeq ($(WITH_SYSTEMWIDE),yes) ifneq ($(WITH_SYSTEMDIR),"") ifeq ($(OSTYPE), FreeBSD) release/quake2 : LDFLAGS += -Wl,-z,origin,-rpath='$(WITH_SYSTEMDIR)/lib' else ifeq ($(OSTYPE), Linux) release/quake2 : LDFLAGS += -Wl,-z,origin,-rpath='$(WITH_SYSTEMDIR)/lib' endif else ifeq ($(OSTYPE), FreeBSD) release/quake2 : LDFLAGS += -Wl,-z,origin,-rpath='/usr/share/games/quake2/lib' else ifeq ($(OSTYPE), Linux) release/quake2 : LDFLAGS += -Wl,-z,origin,-rpath='/usr/share/games/quake2/lib' endif endif endif endif # ---------- # The server ifeq ($(OSTYPE), Windows) server: @echo "===> Building q2ded" ${Q}mkdir -p release $(MAKE) release/q2ded.exe build/server/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(INCLUDE) -o $@ $< release/q2ded.exe : CFLAGS += -DDEDICATED_ONLY release/q2ded.exe : LDFLAGS += -lz ifeq ($(WITH_ZIP),yes) release/q2ded.exe : CFLAGS += -DZIP -DNOUNCRYPT release/q2ded.exe : LDFLAGS += -lz endif else # not Windows server: @echo "===> Building q2ded" ${Q}mkdir -p release $(MAKE) release/q2ded build/server/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(INCLUDE) -o $@ $< release/q2ded : CFLAGS += -DDEDICATED_ONLY ifeq ($(WITH_ZIP),yes) release/q2ded : CFLAGS += -DZIP -DNOUNCRYPT release/q2ded : LDFLAGS += -lz endif endif # ---------- # The baseq2 game ifeq ($(OSTYPE), Windows) game: @echo "===> Building baseq2/game.dll" ${Q}mkdir -p release/baseq2 $(MAKE) release/baseq2/game.dll build/baseq2/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(INCLUDE) -o $@ $< release/baseq2/game.dll : LDFLAGS += -shared else ifeq ($(OSTYPE), Darwin) game: @echo "===> Building baseq2/game.dynlib" ${Q}mkdir -p release/baseq2 $(MAKE) release/baseq2/game.dynlib build/baseq2/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(INCLUDE) -o $@ $< release/baseq2/game.dynlib : CFLAGS += -fPIC release/baseq2/game.dynlib : LDFLAGS += -shared else # not Windows or Darwin game: @echo "===> Building baseq2/game.so" ${Q}mkdir -p release/baseq2 $(MAKE) release/baseq2/game.so build/baseq2/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(INCLUDE) -o $@ $< release/baseq2/game.so : CFLAGS += -fPIC release/baseq2/game.so : LDFLAGS += -shared endif # ---------- # Used by the game GAME_OBJS_ = \ src/common/shared/flash.o \ src/common/shared/rand.o \ src/common/shared/shared.o \ src/game/g_ai.o \ src/game/g_chase.o \ src/game/g_cmds.o \ src/game/g_combat.o \ src/game/g_func.o \ src/game/g_items.o \ src/game/g_main.o \ src/game/g_misc.o \ src/game/g_monster.o \ src/game/g_phys.o \ src/game/g_spawn.o \ src/game/g_svcmds.o \ src/game/g_target.o \ src/game/g_trigger.o \ src/game/g_turret.o \ src/game/g_utils.o \ src/game/g_weapon.o \ src/game/monster/berserker/berserker.o \ src/game/monster/boss2/boss2.o \ src/game/monster/boss3/boss3.o \ src/game/monster/boss3/boss31.o \ src/game/monster/boss3/boss32.o \ src/game/monster/brain/brain.o \ src/game/monster/chick/chick.o \ src/game/monster/flipper/flipper.o \ src/game/monster/float/float.o \ src/game/monster/flyer/flyer.o \ src/game/monster/gladiator/gladiator.o \ src/game/monster/gunner/gunner.o \ src/game/monster/hover/hover.o \ src/game/monster/infantry/infantry.o \ src/game/monster/insane/insane.o \ src/game/monster/medic/medic.o \ src/game/monster/misc/move.o \ src/game/monster/mutant/mutant.o \ src/game/monster/parasite/parasite.o \ src/game/monster/soldier/soldier.o \ src/game/monster/supertank/supertank.o \ src/game/monster/tank/tank.o \ src/game/player/client.o \ src/game/player/hud.o \ src/game/player/trail.o \ src/game/player/view.o \ src/game/player/weapon.o \ src/game/savegame/savegame.o # ---------- # Used by the client CLIENT_OBJS_ := \ src/backends/generic/misc.o \ src/backends/generic/qal.o \ src/backends/generic/vid.o \ src/backends/generic/qgl.o \ src/backends/sdl/cd.o \ src/backends/sdl/input.o \ src/backends/sdl/refresh.o \ src/backends/sdl/sound.o \ src/client/cl_cin.o \ src/client/cl_console.o \ src/client/cl_download.o \ src/client/cl_effects.o \ src/client/cl_entities.o \ src/client/cl_input.o \ src/client/cl_inventory.o \ src/client/cl_keyboard.o \ src/client/cl_lights.o \ src/client/cl_main.o \ src/client/cl_network.o \ src/client/cl_parse.o \ src/client/cl_particles.o \ src/client/cl_prediction.o \ src/client/cl_screen.o \ src/client/cl_tempentities.o \ src/client/cl_view.o \ src/client/refresh/r_draw.o \ src/client/refresh/r_image.o \ src/client/refresh/r_light.o \ src/client/refresh/r_lightmap.o \ src/client/refresh/r_main.o \ src/client/refresh/r_mesh.o \ src/client/refresh/r_misc.o \ src/client/refresh/r_model.o \ src/client/refresh/r_scrap.o \ src/client/refresh/r_surf.o \ src/client/refresh/r_warp.o \ src/client/refresh/files/md2.o \ src/client/refresh/files/pcx.o \ src/client/refresh/files/sp2.o \ src/client/refresh/files/stb.o \ src/client/refresh/files/wal.o \ src/client/menu/menu.o \ src/client/menu/qmenu.o \ src/client/menu/videomenu.o \ src/client/sound/ogg.o \ src/client/sound/openal.o \ src/client/sound/sound.o \ src/client/sound/wave.o \ src/common/argproc.o \ src/common/clientserver.o \ src/common/collision.o \ src/common/crc.o \ src/common/cmdparser.o \ src/common/cvar.o \ src/common/filesystem.o \ src/common/glob.o \ src/common/md4.o \ src/common/movemsg.o \ src/common/misc.o \ src/common/netchan.o \ src/common/pmove.o \ src/common/szone.o \ src/common/zone.o \ src/common/shared/flash.o \ src/common/shared/rand.o \ src/common/shared/shared.o \ src/common/unzip/ioapi.o \ src/common/unzip/unzip.o \ src/server/sv_cmd.o \ src/server/sv_conless.o \ src/server/sv_entities.o \ src/server/sv_game.o \ src/server/sv_init.o \ src/server/sv_main.o \ src/server/sv_save.o \ src/server/sv_send.o \ src/server/sv_user.o \ src/server/sv_world.o ifeq ($(OSTYPE), Windows) CLIENT_OBJS_ += \ src/backends/windows/network.o \ src/backends/windows/system.o \ src/backends/windows/shared/mem.o else CLIENT_OBJS_ += \ src/backends/unix/main.o \ src/backends/unix/network.o \ src/backends/unix/signalhandler.o \ src/backends/unix/system.o \ src/backends/unix/shared/hunk.o endif # ---------- # Used by the server SERVER_OBJS_ := \ src/common/argproc.o \ src/common/clientserver.o \ src/common/collision.o \ src/common/crc.o \ src/common/cmdparser.o \ src/common/cvar.o \ src/common/filesystem.o \ src/common/glob.o \ src/common/md4.o \ src/common/misc.o \ src/common/movemsg.o \ src/common/netchan.o \ src/common/pmove.o \ src/common/szone.o \ src/common/zone.o \ src/common/shared/rand.o \ src/common/shared/shared.o \ src/common/unzip/ioapi.o \ src/common/unzip/unzip.o \ src/server/sv_cmd.o \ src/server/sv_conless.o \ src/server/sv_entities.o \ src/server/sv_game.o \ src/server/sv_init.o \ src/server/sv_main.o \ src/server/sv_save.o \ src/server/sv_send.o \ src/server/sv_user.o \ src/server/sv_world.o \ src/backends/generic/misc.o ifeq ($(OSTYPE), Windows) SERVER_OBJS_ += \ src/backends/windows/network.o \ src/backends/windows/system.o \ src/backends/windows/shared/mem.o else # not Windows SERVER_OBJS_ += \ src/backends/unix/main.o \ src/backends/unix/network.o \ src/backends/unix/signalhandler.o \ src/backends/unix/system.o \ src/backends/unix/shared/hunk.o endif # ---------- # Rewrite pathes to our object directory CLIENT_OBJS = $(patsubst %,build/client/%,$(CLIENT_OBJS_)) SERVER_OBJS = $(patsubst %,build/server/%,$(SERVER_OBJS_)) GAME_OBJS = $(patsubst %,build/baseq2/%,$(GAME_OBJS_)) # ---------- # Generate header dependencies CLIENT_DEPS= $(CLIENT_OBJS:.o=.d) SERVER_DEPS= $(SERVER_OBJS:.o=.d) GAME_DEPS= $(GAME_OBJS:.o=.d) # ---------- # Suck header dependencies in -include $(CLIENT_DEPS) -include $(SERVER_DEPS) -include $(GAME_DEPS) # ---------- # release/quake2 ifeq ($(OSTYPE), Windows) release/quake2.exe : $(CLIENT_OBJS) icon @echo "===> LD $@" ${Q}$(CC) build/icon/icon.res $(CLIENT_OBJS) $(LDFLAGS) $(SDLLDFLAGS) -o $@ $(Q)strip $@ else release/quake2 : $(CLIENT_OBJS) @echo "===> LD $@" ${Q}$(CC) $(CLIENT_OBJS) $(LDFLAGS) $(SDLLDFLAGS) $(X11LDFLAGS) -o $@ endif # release/q2ded ifeq ($(OSTYPE), Windows) release/q2ded.exe : $(SERVER_OBJS) icon @echo "===> LD $@.exe" ${Q}$(CC) build/icon/icon.res $(SERVER_OBJS) $(LDFLAGS) -o $@ $(Q)strip $@ else release/q2ded : $(SERVER_OBJS) @echo "===> LD $@" ${Q}$(CC) $(SERVER_OBJS) $(LDFLAGS) -o $@ endif # release/baseq2/game.so ifeq ($(OSTYPE), Windows) release/baseq2/game.dll : $(GAME_OBJS) @echo "===> LD $@" ${Q}$(CC) $(GAME_OBJS) $(LDFLAGS) -o $@ $(Q)strip $@ else ifeq ($(OSTYPE), Darwin) release/baseq2/game.dynlib : $(GAME_OBJS) @echo "===> LD $@" ${Q}$(CC) $(GAME_OBJS) $(LDFLAGS) -o $@ else release/baseq2/game.so : $(GAME_OBJS) @echo "===> LD $@" ${Q}$(CC) $(GAME_OBJS) $(LDFLAGS) -o $@ endif # ---------- yquake2-QUAKE2_5_32/CMakeLists.txt0000644000175000017500000003420212615162034017360 0ustar greffrathgreffrathcmake_minimum_required(VERSION 3.0) # Enforce "Debug" as standard build type if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) endif(NOT CMAKE_BUILD_TYPE) # CMake project configuration project (yquake2) # Cmake module search path set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/stuff/cmake/modules ${CMAKE_MODULE_PATH}) if (YQUAKE2LIBS) set(ENV{CMAKE_PREFIX_PATH} ${YQUAKE2LIBS}) set(ENV{OPENALDIR} ${YQUAKE2LIBS}) set(ENV{SDLDIR} ${YQUAKE2LIBS}) set(ENV{SDL2DIR} ${YQUAKE2LIBS}) endif() # Add extended path for FreeBSD and Homebrew on OS X list(APPEND CMAKE_PREFIX_PATH /usr/local) # Enforce compiler flags (GCC / Clang compatible, yquake2 # won't build with another compiler anyways) # -Wall -> More warnings # -fno-strict-aliasing -> Quake 2 is far away from strict aliasing # -fwrapv -> Make signed integer overflows defined set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fno-strict-aliasing -fwrapv") # Use -O2 as maximum optimization level. -O3 has it's problems with yquake2. string(REPLACE "-O3" "-O2" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") # yquake2 compilation options option(ZIP_SUPPORT "ZIP support" ON) option(OGG_SUPPORT "OGG Vorbis playback support (Music)" ON) option(OPENAL_SUPPORT "OpenAL support" ON) # These variables will act as our list of include folders and linker flags set(yquake2LinkerFlags) set(yquake2IncludeDirectories) set(yquake2LinkerDirectories) # Set directory locations (allowing us to move directories easily) set(SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/src) set(BACKENDS_SRC_DIR ${SOURCE_DIR}/backends) set(COMMON_SRC_DIR ${SOURCE_DIR}/common) set(GAME_SRC_DIR ${SOURCE_DIR}/game) set(SERVER_SRC_DIR ${SOURCE_DIR}/server) set(CLIENT_SRC_DIR ${SOURCE_DIR}/client) # Required libraries to build the different components of the binaries. Find # them and add the include/linker directories and flags (in case the package # manager find it in a weird place) find_package(SDL2) if(${SDL2_FOUND}) add_definitions(-DSDL2) list(APPEND yquake2IncludeDirectories "${SDL2_INCLUDE_DIR}/..") list(APPEND yquake2LinkerFlags ${SDL2_LIBRARY}) else() find_package(SDL REQUIRED) add_definitions(-DWITH_CDA) list(APPEND yquake2IncludeDirectories "${SDL_INCLUDE_DIR}/..") list(APPEND yquake2LinkerFlags ${SDL_LIBRARY}) endif() find_package(OpenGL REQUIRED) list(APPEND yquake2IncludeDirectories ${OPENGL_INCLUDE_DIR}) list(APPEND yquake2LinkerFlags ${OPENGL_LIBRARIES}) if(${ZIP_SUPPORT}) find_package(ZLIB REQUIRED) list(APPEND yquake2IncludeDirectories ${ZLIB_INCLUDE_DIRS}) list(APPEND yquake2LinkerFlags ${ZLIB_LIBRARIES}) add_definitions(-DZIP -DNOUNCRYPT) endif() if(${OGG_SUPPORT}) find_package(OggVorbis) if(${OGGVORBIS_FOUND}) add_definitions(-DOGG) list(APPEND yquake2IncludeDirectories ${OGGVORBIS_INCLUDE_DIR}) list(APPEND yquake2LinkerFlags ${OGG_LIBRARY} ${VORBIS_LIBRARY} ${VORBISFILE_LIBRARY}) endif() endif() if(${OPENAL_SUPPORT}) find_package(OpenAL) # TODO: OS X is still missing here if(${OPENAL_FOUND} AND NOT(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")) list(APPEND yquake2IncludeDirectories "${OPENAL_INCLUDE_DIR}") list(APPEND yquake2LinkerFlags ${OPENAL_LIBRARY}) if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") add_definitions(-DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER="openal32.dll") elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") add_definitions(-DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER="libopenal.dylib") elseif((${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") OR (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")) add_definitions(-DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER="libopenal.so") else() add_definitions(-DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER="libopenal.so.1") endif() endif() endif() if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") list(APPEND yquake2LinkerFlags "-lm") else() list(APPEND yquake2LinkerFlags "-lm -rdynamic") endif() list(APPEND yquake2LinkerFlags ${CMAKE_DL_LIBS}) # With all of those libraries and user defined paths # added, lets give them to the compiler and linker. include_directories(${yquake2IncludeDirectories}) link_directories(${yquake2LinkerDirectories}) set(Backends-Generic-Source ${BACKENDS_SRC_DIR}/generic/qal.c ${BACKENDS_SRC_DIR}/generic/vid.c ${BACKENDS_SRC_DIR}/generic/qgl.c ${BACKENDS_SRC_DIR}/generic/misc.c ${BACKENDS_SRC_DIR}/sdl/cd.c ${BACKENDS_SRC_DIR}/sdl/input.c ${BACKENDS_SRC_DIR}/sdl/refresh.c ${BACKENDS_SRC_DIR}/sdl/sound.c ) set(Backends-Generic-Header ${BACKENDS_SRC_DIR}/generic/header/input.h ${BACKENDS_SRC_DIR}/generic/header/qal.h ${BACKENDS_SRC_DIR}/generic/header/qgl.h ) set(Backends-Unix-Source ${BACKENDS_SRC_DIR}/unix/network.c ${BACKENDS_SRC_DIR}/unix/system.c ${BACKENDS_SRC_DIR}/generic/misc.c ${BACKENDS_SRC_DIR}/unix/main.c ${BACKENDS_SRC_DIR}/unix/signalhandler.c ${BACKENDS_SRC_DIR}/unix/shared/hunk.c ) set(Backends-Unix-Header ${BACKENDS_SRC_DIR}/unix/header/unix.h ) set(Backends-Windows-Source ${BACKENDS_SRC_DIR}/generic/misc.c ${BACKENDS_SRC_DIR}/windows/network.c ${BACKENDS_SRC_DIR}/windows/system.c ${BACKENDS_SRC_DIR}/windows/shared/mem.c ) set(Backends-Windows-Header ${BACKENDS_SRC_DIR}/windows/header/resource.h ${BACKENDS_SRC_DIR}/windows/header/winquake.h ) # Set the nessesary platform specific source if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") set(Platform-Specific-Source ${Backends-Windows-Source} ${Backends-Windows-Header}) else() set(Platform-Specific-Source ${Backends-Unix-Source} ${Backends-Unix-Header}) endif() set(Game-Source ${COMMON_SRC_DIR}/shared/flash.c ${COMMON_SRC_DIR}/shared/rand.c ${COMMON_SRC_DIR}/shared/shared.c ${GAME_SRC_DIR}/g_ai.c ${GAME_SRC_DIR}/g_chase.c ${GAME_SRC_DIR}/g_cmds.c ${GAME_SRC_DIR}/g_combat.c ${GAME_SRC_DIR}/g_func.c ${GAME_SRC_DIR}/g_items.c ${GAME_SRC_DIR}/g_main.c ${GAME_SRC_DIR}/g_misc.c ${GAME_SRC_DIR}/g_monster.c ${GAME_SRC_DIR}/g_phys.c ${GAME_SRC_DIR}/g_spawn.c ${GAME_SRC_DIR}/g_svcmds.c ${GAME_SRC_DIR}/g_target.c ${GAME_SRC_DIR}/g_trigger.c ${GAME_SRC_DIR}/g_turret.c ${GAME_SRC_DIR}/g_utils.c ${GAME_SRC_DIR}/g_weapon.c ${GAME_SRC_DIR}/monster/berserker/berserker.c ${GAME_SRC_DIR}/monster/boss2/boss2.c ${GAME_SRC_DIR}/monster/boss3/boss3.c ${GAME_SRC_DIR}/monster/boss3/boss31.c ${GAME_SRC_DIR}/monster/boss3/boss32.c ${GAME_SRC_DIR}/monster/brain/brain.c ${GAME_SRC_DIR}/monster/chick/chick.c ${GAME_SRC_DIR}/monster/flipper/flipper.c ${GAME_SRC_DIR}/monster/float/float.c ${GAME_SRC_DIR}/monster/flyer/flyer.c ${GAME_SRC_DIR}/monster/gladiator/gladiator.c ${GAME_SRC_DIR}/monster/gunner/gunner.c ${GAME_SRC_DIR}/monster/hover/hover.c ${GAME_SRC_DIR}/monster/infantry/infantry.c ${GAME_SRC_DIR}/monster/insane/insane.c ${GAME_SRC_DIR}/monster/medic/medic.c ${GAME_SRC_DIR}/monster/misc/move.c ${GAME_SRC_DIR}/monster/mutant/mutant.c ${GAME_SRC_DIR}/monster/parasite/parasite.c ${GAME_SRC_DIR}/monster/soldier/soldier.c ${GAME_SRC_DIR}/monster/supertank/supertank.c ${GAME_SRC_DIR}/monster/tank/tank.c ${GAME_SRC_DIR}/player/client.c ${GAME_SRC_DIR}/player/hud.c ${GAME_SRC_DIR}/player/trail.c ${GAME_SRC_DIR}/player/view.c ${GAME_SRC_DIR}/player/weapon.c ${GAME_SRC_DIR}/savegame/savegame.c ) set(Game-Header ${GAME_SRC_DIR}/header/game.h ${GAME_SRC_DIR}/header/local.h ${GAME_SRC_DIR}/monster/berserker/berserker.h ${GAME_SRC_DIR}/monster/boss2/boss2.h ${GAME_SRC_DIR}/monster/boss3/boss31.h ${GAME_SRC_DIR}/monster/boss3/boss32.h ${GAME_SRC_DIR}/monster/brain/brain.h ${GAME_SRC_DIR}/monster/chick/chick.h ${GAME_SRC_DIR}/monster/flipper/flipper.h ${GAME_SRC_DIR}/monster/float/float.h ${GAME_SRC_DIR}/monster/flyer/flyer.h ${GAME_SRC_DIR}/monster/gladiator/gladiator.h ${GAME_SRC_DIR}/monster/gunner/gunner.h ${GAME_SRC_DIR}/monster/hover/hover.h ${GAME_SRC_DIR}/monster/infantry/infantry.h ${GAME_SRC_DIR}/monster/insane/insane.h ${GAME_SRC_DIR}/monster/medic/medic.h ${GAME_SRC_DIR}/monster/misc/player.h ${GAME_SRC_DIR}/monster/mutant/mutant.h ${GAME_SRC_DIR}/monster/parasite/parasite.h ${GAME_SRC_DIR}/monster/soldier/soldier.h ${GAME_SRC_DIR}/monster/supertank/supertank.h ${GAME_SRC_DIR}/monster/tank/tank.h ${GAME_SRC_DIR}/savegame/tables/clientfields.h ${GAME_SRC_DIR}/savegame/tables/fields.h ${GAME_SRC_DIR}/savegame/tables/gamefunc_decs.h ${GAME_SRC_DIR}/savegame/tables/gamefunc_list.h ${GAME_SRC_DIR}/savegame/tables/gamemmove_decs.h ${GAME_SRC_DIR}/savegame/tables/gamemmove_list.h ${GAME_SRC_DIR}/savegame/tables/levelfields.h ) set(Client-Source ${CLIENT_SRC_DIR}/cl_cin.c ${CLIENT_SRC_DIR}/cl_console.c ${CLIENT_SRC_DIR}/cl_download.c ${CLIENT_SRC_DIR}/cl_effects.c ${CLIENT_SRC_DIR}/cl_entities.c ${CLIENT_SRC_DIR}/cl_input.c ${CLIENT_SRC_DIR}/cl_inventory.c ${CLIENT_SRC_DIR}/cl_keyboard.c ${CLIENT_SRC_DIR}/cl_lights.c ${CLIENT_SRC_DIR}/cl_main.c ${CLIENT_SRC_DIR}/cl_network.c ${CLIENT_SRC_DIR}/cl_parse.c ${CLIENT_SRC_DIR}/cl_particles.c ${CLIENT_SRC_DIR}/cl_prediction.c ${CLIENT_SRC_DIR}/cl_screen.c ${CLIENT_SRC_DIR}/cl_tempentities.c ${CLIENT_SRC_DIR}/cl_view.c ${CLIENT_SRC_DIR}/refresh/r_draw.c ${CLIENT_SRC_DIR}/refresh/r_image.c ${CLIENT_SRC_DIR}/refresh/r_light.c ${CLIENT_SRC_DIR}/refresh/r_lightmap.c ${CLIENT_SRC_DIR}/refresh/r_main.c ${CLIENT_SRC_DIR}/refresh/r_mesh.c ${CLIENT_SRC_DIR}/refresh/r_misc.c ${CLIENT_SRC_DIR}/refresh/r_model.c ${CLIENT_SRC_DIR}/refresh/r_scrap.c ${CLIENT_SRC_DIR}/refresh/r_surf.c ${CLIENT_SRC_DIR}/refresh/r_warp.c ${CLIENT_SRC_DIR}/refresh/files/md2.c ${CLIENT_SRC_DIR}/refresh/files/pcx.c ${CLIENT_SRC_DIR}/refresh/files/sp2.c ${CLIENT_SRC_DIR}/refresh/files/stb.c ${CLIENT_SRC_DIR}/refresh/files/wal.c ${CLIENT_SRC_DIR}/menu/menu.c ${CLIENT_SRC_DIR}/menu/qmenu.c ${CLIENT_SRC_DIR}/menu/videomenu.c ${CLIENT_SRC_DIR}/sound/ogg.c ${CLIENT_SRC_DIR}/sound/openal.c ${CLIENT_SRC_DIR}/sound/sound.c ${CLIENT_SRC_DIR}/sound/wave.c ${COMMON_SRC_DIR}/argproc.c ${COMMON_SRC_DIR}/clientserver.c ${COMMON_SRC_DIR}/collision.c ${COMMON_SRC_DIR}/crc.c ${COMMON_SRC_DIR}/cmdparser.c ${COMMON_SRC_DIR}/cvar.c ${COMMON_SRC_DIR}/filesystem.c ${COMMON_SRC_DIR}/glob.c ${COMMON_SRC_DIR}/md4.c ${COMMON_SRC_DIR}/movemsg.c ${COMMON_SRC_DIR}/misc.c ${COMMON_SRC_DIR}/netchan.c ${COMMON_SRC_DIR}/pmove.c ${COMMON_SRC_DIR}/szone.c ${COMMON_SRC_DIR}/zone.c ${COMMON_SRC_DIR}/shared/flash.c ${COMMON_SRC_DIR}/shared/rand.c ${COMMON_SRC_DIR}/shared/shared.c ${COMMON_SRC_DIR}/unzip/ioapi.c ${COMMON_SRC_DIR}/unzip/unzip.c ${SERVER_SRC_DIR}/sv_cmd.c ${SERVER_SRC_DIR}/sv_conless.c ${SERVER_SRC_DIR}/sv_entities.c ${SERVER_SRC_DIR}/sv_game.c ${SERVER_SRC_DIR}/sv_init.c ${SERVER_SRC_DIR}/sv_main.c ${SERVER_SRC_DIR}/sv_save.c ${SERVER_SRC_DIR}/sv_send.c ${SERVER_SRC_DIR}/sv_user.c ${SERVER_SRC_DIR}/sv_world.c ) set(Client-Header ${CLIENT_SRC_DIR}/header/client.h ${CLIENT_SRC_DIR}/header/console.h ${CLIENT_SRC_DIR}/header/keyboard.h ${CLIENT_SRC_DIR}/header/ref.h ${CLIENT_SRC_DIR}/header/screen.h ${CLIENT_SRC_DIR}/header/vid.h ${CLIENT_SRC_DIR}/menu/header/qmenu.h ${CLIENT_SRC_DIR}/refresh/constants/anorms.h ${CLIENT_SRC_DIR}/refresh/constants/anormtab.h ${CLIENT_SRC_DIR}/refresh/constants/warpsin.h ${CLIENT_SRC_DIR}/refresh/files/stb_image.h ${CLIENT_SRC_DIR}/refresh/header/local.h ${CLIENT_SRC_DIR}/refresh/header/model.h ${CLIENT_SRC_DIR}/sound/header/cdaudio.h ${CLIENT_SRC_DIR}/sound/header/local.h ${CLIENT_SRC_DIR}/sound/header/sound.h ${CLIENT_SRC_DIR}/sound/header/vorbis.h ${COMMON_SRC_DIR}/header/common.h ${COMMON_SRC_DIR}/header/crc.h ${COMMON_SRC_DIR}/header/files.h ${COMMON_SRC_DIR}/header/glob.h ${COMMON_SRC_DIR}/header/shared.h ${COMMON_SRC_DIR}/header/zone.h ${COMMON_SRC_DIR}/unzip/ioapi.h ${COMMON_SRC_DIR}/unzip/unzip.h ${SERVER_SRC_DIR}/header/server.h ) set(Server-Source ${COMMON_SRC_DIR}/argproc.c ${COMMON_SRC_DIR}/clientserver.c ${COMMON_SRC_DIR}/collision.c ${COMMON_SRC_DIR}/crc.c ${COMMON_SRC_DIR}/cmdparser.c ${COMMON_SRC_DIR}/cvar.c ${COMMON_SRC_DIR}/filesystem.c ${COMMON_SRC_DIR}/glob.c ${COMMON_SRC_DIR}/md4.c ${COMMON_SRC_DIR}/misc.c ${COMMON_SRC_DIR}/movemsg.c ${COMMON_SRC_DIR}/netchan.c ${COMMON_SRC_DIR}/pmove.c ${COMMON_SRC_DIR}/szone.c ${COMMON_SRC_DIR}/zone.c ${COMMON_SRC_DIR}/shared/rand.c ${COMMON_SRC_DIR}/shared/shared.c ${COMMON_SRC_DIR}/unzip/ioapi.c ${COMMON_SRC_DIR}/unzip/unzip.c ${SERVER_SRC_DIR}/sv_cmd.c ${SERVER_SRC_DIR}/sv_conless.c ${SERVER_SRC_DIR}/sv_entities.c ${SERVER_SRC_DIR}/sv_game.c ${SERVER_SRC_DIR}/sv_init.c ${SERVER_SRC_DIR}/sv_main.c ${SERVER_SRC_DIR}/sv_save.c ${SERVER_SRC_DIR}/sv_send.c ${SERVER_SRC_DIR}/sv_user.c ${SERVER_SRC_DIR}/sv_world.c ) set(Server-Header ${COMMON_SRC_DIR}/header/common.h ${COMMON_SRC_DIR}/header/crc.h ${COMMON_SRC_DIR}/header/files.h ${COMMON_SRC_DIR}/header/glob.h ${COMMON_SRC_DIR}/header/shared.h ${COMMON_SRC_DIR}/header/zone.h ${COMMON_SRC_DIR}/unzip/ioapi.h ${COMMON_SRC_DIR}/unzip/unzip.h ${SERVER_SRC_DIR}/header/server.h ) # Build the game dynamic library add_library(game SHARED ${Game-Source} ${Game-Header}) set_target_properties(game PROPERTIES PREFIX "" LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/Debug/baseq2 LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/Release/baseq2 ) target_link_libraries(game ${yquake2LinkerFlags}) # Main Quake 2 executable add_executable(quake2 ${Client-Source} ${Client-Header} ${Platform-Specific-Source} ${Backends-Generic-Source} ${Backends-Generic-Header}) set_target_properties(quake2 PROPERTIES PREFIX "" RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/Debug RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/Release ) if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") target_link_libraries(quake2 ${yquake2LinkerFlags} ws2_32 winmm) else() target_link_libraries(quake2 ${yquake2LinkerFlags}) endif() # Quake 2 Dedicated Server add_executable(q2ded ${Server-Source} ${Server-Header} ${Platform-Specific-Source}) set_target_properties(q2ded PROPERTIES PREFIX "" COMPILE_DEFINITIONS "DEDICATED_ONLY" RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/Debug RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/Release ) if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") target_link_libraries(q2ded game ${yquake2LinkerFlags} ws2_32 winmm) else() target_link_libraries(q2ded game ${yquake2LinkerFlags}) endif() yquake2-QUAKE2_5_32/CHANGELOG0000644000175000017500000002027512615162034016037 0ustar greffrathgreffrathQuake II 5.31 to 5.32: - Fix problems with never openal-soft versions. - Fix overbright bits not applied to regular meshes. (by David Reid) - Several improvements to GUI / HUD scaling. (by David Reid) - Don't stop generatings gibs after map change. - A new high resolution application / windows icon. (by Ryan) - Don't display baseq2 savegames in mods / addons. - Some smaller bugfixes. Quake II 5.30 to 5.31: - Enabled hud scaling by default and added an option to the video menu to switch scaling off. - Fixed animated textures on transparent surfaces. - Added CMake as an optional build system. - Implemented a persistent console history. (by caedes) - Fix bug with high velocities in vents in 32bit builds. - A lot of small fixes and changes. (by Ozkan Sezer) Quake II 5.24 to 5.30: - Support for Mac OS X was overhauled. (by Jarvik7) - Overhauled retexturing support, replacing libjpeg with stb_image, adding png support because it was so easy (by caedes). - Fix gamma (was broken in last release) - Fix mouse grabbing (again!) - Add binary directory to game data path Quake II 5.23 to 5.24: - Fix keyboard layouts in the console. - Use GL_ARB_texture_non_power_of_two if it's supported by the GPU. - Provide gl_consolescale and gl_menuscale cvars to change the scale of the console and the menu. - Several bugfixes and improvements to the sound system. Some bugs were fixed and the underwater effect is now supported in the SDL backend. (by bibendovsky) Quake II 5.22 to 5.23: - Provide gl_hudscale cvar that can be changed to scale the HUD, for high resolutions etc. (by caedes) - Several menu improvements. - A better work around for the "the mouse cursor hits the window border" SDL2 problem. Quake II 5.21 to 5.22: - Provide a fallback if SDL2s relative mouse mode cannot be activated. - Add support for MSAA through the gl_msaa_samples cvar. Quake II 5.20 to 5.21: - Fix a bug regaring mouse key handling (reported by phenixia2003) - Correct MS Windows builds. Add official support for Win64. This still needs some testing. - Allow to shutdown the client by pressing ctrl-c or sending SIGTERM. Unix / Linux only. Quake II 5.11 to 5.20 - Integrate the refresher into the client and remove QGL. This means that ref_gl.so is gone and libGL is now linked like every other lib. (by Alejandro Ricoveri) - Port the client to SDL 2.0. The newer SDL version solves some long standing problems, like broken keyboard layouts and non working SDL sound on Windows. While SDL 2.0 is enabled by default, one can switch back to SDL 1.2 by editing the Makefile. - OS X support was put to hold. While OS X support is nice to have, non of the deveolpers has interest in maintaining it. Until someone steps up and takes responsibility, no OS X versions will be released. Quake II 5.10 to 5.11 - A lot of bugfixes. - Videos are scaled to 4:3 to prevent distortions. - Another sound system cleanup. Quake II 5.00 to 5.10 - Support for OS X. (by W. Beser) - Correct field of view handling (by Ricardo Garci) - Many improvements to the mouse release code (by svdijk) - Scrolling save / load menus (by svdijk) Quake II 4.21 to 5.00 - Backport to Microsoft Windows. - Support for OpenBSD. (by Jonathan Gray) - Aspect ration can be set via the video menu. - A better random number generator. - The SDL sound driver is now selectable by "s_sdldriver". Quake II 4.20 to 4.21 - Fix several segfaults with OpenAL. (reported by Joran and mxmvasilyev0) - Add a file CONTRIBUTE. - Some minor changes to the Makefile. Quake II 4.10 to 4.20 - Add an options framework to allow disabling most option features (OGG/Vorbis, ZIP file loading, OpenAL, etc) at compile time. - Integrate OpenAL support, enabling surround sound and better stereo sound calculations. - Enforce the "C" locale to prevent libraries like broken LADSPA plugins to override it and break printf, scanf, etc. Quake II 4.03 to 4.10 - Change the behavior of hotkey menus to fix some strange bugs and memory leaks in the menu system. - Add the "gl_farsee" cvar. When set to "1" Quake II renders maps up to 4096x4096 units instead of being limited to 2300x2300. The default is "0". (by Richard Allen) - Add support for the high resolution retexturing pack. - Reenable support for gamma via SDL, since the upstream bug was fixed with SDL 1.2.15. Gamma via X11 can be forced by defining X11GAMMA at compile time. - Add support for big endian architectures and enable the build on SPARC64 CPUs. (by Kieron Gillespie) Quake II 4.02 to 4.03 - Fix wrong function call in the Quake II file system. - Fix gl_ext_multitexture set to 1. (reported by Richard Allen and Lukas Sabota) - Print the version number in the window title. Quake II 4.01 to 4.02 - Fix a linker problem manifesting only on Ubuntu. Quake II 4.00 to 4.01 - Fix a linker problem. - Fix a problem with displaying the version number. Quake II 3.00 to 4.00 - A major rewrite of the savegame system. - Add a crash handler, printing a backtrace on Linux platforms. - Add support for system wide installations. - ctf was moved into a separate download. - All open bugs were fixed. - Fix spawnpoint selection if single player maps are loaded via console. - Rename ~/.quake2 to ~/.yq2 to ease parallel installation with other Quake II clients. - The client does no longer crash if the menu is opened while connecting to remote server. - The game code received a code audit and major rework. Quake II 3.00RC2 to 3.00 - Improve compatiblity with pulseaudio (by Ozkan Sezer) Quake II 3.00RC1 to 3.00RC2 - Many improvements to the OGG/Vorbis support. (by Ozkan Sezer) - The map command now works even when a server is running. Quake II 2.11 to 3.00RC1 - Complete refactoring and code audit of the client, leading to much cleaner and more maintainable code and improved stability. - Fixed a wrong cast under linux. This could result in sudden crashes. - Reworked the input system. - Much improved console tab completion. - Better TTY output of the startup and shutdown. - More reliable shutdown of the client. - Plugged a memory leak in the quake file system. - Major rework of the sound system: - A lot of bugfixes for the upper layer. - Simplified code and removed crap from the 90ies. - Rewrote the low level backend from scratch. - Major rework of the refresher: - Added multitexturing and paletted textures back (this was requested by many, many people). - Rewrote the SDL backend. - Reimplemented the gamma control using native X11 calls. This should fix the gamma for all setups. - Support for overbright bits. - Changed the window title to "Yamagi Quake II". Quake II 2.10 to 2.11 - Fix a bug in the client. This fixes the strange crashes under Ubuntu. (Reported by many, special thanks to jhansonxi for his help) - Add a null pointer check in cl_view.c. This fixes a rare case crash in Ground Zero when files are missing. (by Sascha K.) - Add a script as possible work around for Ubuntu bug 32452 (by caedes) Quake II 2.10RC3 to 2.10: - Fix blending Quake II 2.10RC2 to 2.10RC3: - The refresher uses OpenGL 1.4 not 1.0 - Saner standard configuration - Fix a bug when a video follows a video (like in Ground Zero) - Fix a crash when enabling OGG/Vorbis in the menu - Fix a crash when changing level after deactivating OGG/Vorbis playback - Do not show the gun symbol when fov is bigger than 91 and cl_gun is set to 2 Quake II 2.10RC to 2.10RC2: - Fix a problem with machine gun soldiers deadlocking - Change CFLAGS to a saner default - Quake II now creates ~/.quake2 if it doesn't exist (reported by N. Tsakiris) - Slightly better game performance (~10 FPS) Quake II 2.00 to 2.10RC: - Automagical releases the mouse when the console is opened - Increased the maximal amount of file descriptores from 64 to 256. This should fix some very rare crashes with 'The Reconing' and possible similar problems with mods. (reported by E. Müller) - Support for custom resolutions (by caedes and Yamagi) Quake II 1.05 to 2.00: - Ogg/Vorbis Support as an optional replacement for the CD playback - Support for hardware gamma via SDL - Support for grabbing the mouse - Some bugfixes Quake II 1.04 to 1.05: - Fix slowdown with CD music under Linux (by caedes) Quake II 1.03 to 1.04: - Added icon (by caedes) - Added README - Removed duplicated file