yquake2-QUAKE2_7_10/000077500000000000000000000000001321245476300141225ustar00rootroot00000000000000yquake2-QUAKE2_7_10/.gitignore000066400000000000000000000000421321245476300161060ustar00rootroot00000000000000/build/ /release/ *.mk *.user *.d yquake2-QUAKE2_7_10/CHANGELOG000066400000000000000000000311101321245476300153300ustar00rootroot00000000000000Quake II 7.02 to 7.10: - Joystick support including haptic feedback. This fantastic work was done by Denis Pauk. The dirty work is done by SDL, how good or bad a joystick or gamepad is supported depends on SDLs support for it. - Fix the old SDL sound backend, s_openal set to 0 is working again. - Fix possible Vorbis buffer underruns if too many sound samples are in flight. This occured only in large multi player games with at least 6 custom models. - Fix a possible crash on Windows if MSAA was set to a value not supported by the driver. - It's now possible to play through the whole game on a Raspberry PI and other ARM boards. Please note that the RPIs hardware is really limited. Only the OpenGL 1.4 renderer is supported and the framerate is highly dependend on the screen resolution. Quake II 7.01 to 7.02: - Fix several corner cases regarding render library loading. The game should now always fall back to the OpenGL 1.4 renderer if the new OpenGL 3.2 renderer can't be initialized. Also the game aborts if no useable OpenGL implementation exists. - Refactor the search path code. This should fix several bugs with Quake II writing to the wrong directories or being unable to find some / all assets. - Reimplement portable binaries. If called with the -portable command line option Quake II saves all data (configs, savegames, screenshorts etc.) into it's systemwide installation directory and not users home directory. In contrast to the old implementation on Windows stdout.txt contains all output, the first lines are no longer missing. - vid_fullscreen set to 1 now keeps the desktops resolution. Set it to 2 to change the resolution. - Instead of a list with precalculated FOV values the video menu now shows a slider with possible values from 60 to 120. Horplus is now always enabled, set the horplus cvar to 0 to disable it. - The game is now able to hold the requested framerate (either by the vsync or the gl_maxfps cvar) with an accuracy of about +/- 1% as long as the hardware is fast enough. The framecounter was reimplemented to be much more precise. - Fix misspredictions if an original client running on Win32 connects to a Yamagi Quake II server running on Linux/i386. Quake II 7.00 to 7.01: - Fix build of GL3 for platforms without SSE. - Fix Jennel Jaquays name in credits and quit screen. - Make Quake II high DPI aware on Window Vista and above. - Fix some problems with loading dependend librarys on Windows. Quake II 6.00 to 7.00: - Remove the broken multitexturing render path from the OpenGL 1.4 renderer. It was switched off by default in 6.00. - Reimplement the support for shared renderer libraries. Please note the this is an incompatible implementation with an custom API. The original renderer libraries will not work! - Implement an OpenGL 3.2 renderer. This renderer has the same look and feel as the old OpenGL 1.4 renderer but makes heavy use of modern OpenGL and GPU features. An OpenGL 3.2 capable GPU (Intel starting with Ivy Bridge on Windows or Sandy Bridge on Linux, Nvidia staring with G80 and AMD starting with R600 / HD2000) is required. - Fix OpenAL compatibility with modern openal-soft versions. - Several fixes and optimizations to OpenAL, implement support for doppler effects. (by xorw) Quake II 5.34 to 6.00: - Make the client asynchronous. The old behaviour can be forced by setting cl_async to 0. Please note that asynchronicity can lead to problems if the old SDL 1.2 backend is used and vsync is enabled. - Implement gl_overbrightbits in the non multitexturing case. A value of 1 just fixes lighting on water surfaces, higher values increase the brightness of everything. - General renderer overhaul for better compatibility with modern GPUs. OpenGL 1.4 is now required, older versions are no longer supported. Multitexturing was deprecated and will be removed in a future release. - Fix some longstanding AI problems. - Several general gameplay fixes. Quake II 5.33 to 5.34: - Add support for stereo 3D (by Valery Guskov) - Make gibt solid so they move with conveyor belts. - Disable gl_ext_multitexturing by default. - Switch from an arch whitelist to an "all archs are supported" approach. - Add a new README. Quake II 5.32 to 5.33: - Add OGG volume slider in settings menu - Fixed some bugs in volume settings - Replaced HUD scale option in video menu with generic UI scale - General UI upscaling improvements - Better support for keyboards with AZERTY layout Quake 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 generating 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 yquake2-QUAKE2_7_10/CMakeLists.txt000066400000000000000000000433231321245476300166670ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.0 FATAL_ERROR) # 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() # 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) if(CMAKE_CROSSCOMPILING) set(CMAKE_FIND_ROOT_PATH ${YQUAKE2LIBS}) else() set(ENV{CMAKE_PREFIX_PATH} ${YQUAKE2LIBS}) endif() 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} -std=gnu99 -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) option(SYSTEMWIDE_SUPPORT "Enable systemwide installation of game assets" OFF) # These variables will act as our list of include folders and linker flags set(yquake2IncludeDirectories) set(yquake2LinkerDirectories) set(yquake2LinkerFlags) set(yquake2ClientLinkerFlags) set(yquake2OpenGLLinkerFlags) set(yquake2SDLLinkerFlags) set(yquake2ZLibLinkerFlags) # 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) set(GL_SRC_DIR ${SOURCE_DIR}/client/refresh) # Operating system set(YQ2OSTYPE "${CMAKE_SYSTEM_NAME}" CACHE STRING "Override operation system type") add_definitions(-DYQ2OSTYPE="${YQ2OSTYPE}") # Architecture string set(YQ2ARCH "${CMAKE_SYSTEM_PROCESSOR}" CACHE STRING "Override CPU architecture") string(REGEX REPLACE "amd64" "x86_64" ARCH ${YQ2ARCH}) string(REGEX REPLACE "i.86" "i386" ARCH ${ARCH}) string(REGEX REPLACE "^arm.*" "arm" ARCH ${ARCH}) add_definitions(-DYQ2ARCH="${ARCH}") # Systemwide installation of game assets if(${SYSTEMWIDE_SUPPORT}) add_definitions(-DSYSTEMWIDE) endif() # 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 yquake2SDLLinkerFlags ${SDL2_LIBRARY}) else() find_package(SDL REQUIRED) add_definitions(-DWITH_CDA) list(APPEND yquake2IncludeDirectories "${SDL_INCLUDE_DIR}/..") list(APPEND yquake2SDLLinkerFlags ${SDL_LIBRARY}) endif() find_package(OpenGL REQUIRED) list(APPEND yquake2IncludeDirectories ${OPENGL_INCLUDE_DIR}) list(APPEND yquake2OpenGLLinkerFlags ${OPENGL_LIBRARIES}) if(${ZIP_SUPPORT}) find_package(ZLIB REQUIRED) list(APPEND yquake2IncludeDirectories ${ZLIB_INCLUDE_DIRS}) list(APPEND yquake2ZLibLinkerFlags ${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 yquake2ClientLinkerFlags ${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 yquake2ClientLinkerFlags ${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 -static-libgcc") 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} ${CMAKE_SOURCE_DIR}/src/client/refresh/gl3/glad/include) link_directories(${yquake2LinkerDirectories}) # If we're building with gcc for i386 let's define -ffloat-store. # This helps the old and crappy x87 FPU to produce correct values. # Would be nice if Clang had something comparable. if ("${ARCH}" STREQUAL "i386") if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffloat-store") endif() endif() # Force SSE math on x86_64. All sane compilers should do this # anyway, just to protect us from broken Linux distros. if ("${ARCH}" STREQUAL "x86_64") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse") endif() set(Backends-Generic-Source ${BACKENDS_SRC_DIR}/generic/misc.c ${BACKENDS_SRC_DIR}/generic/qal.c ${BACKENDS_SRC_DIR}/generic/vid.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 ) set(Backends-Unix-Source ${BACKENDS_SRC_DIR}/generic/misc.c ${BACKENDS_SRC_DIR}/unix/main.c ${BACKENDS_SRC_DIR}/unix/network.c ${BACKENDS_SRC_DIR}/unix/signalhandler.c ${BACKENDS_SRC_DIR}/unix/system.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/frame.c ${BACKENDS_SRC_DIR}/windows/icon.rc ${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(GL-Windows-Source ${BACKENDS_SRC_DIR}/windows/shared/mem.c ) set(GL-Unix-Source ${BACKENDS_SRC_DIR}/unix/shared/hunk.c ) # Set the nessesary platform specific source if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") set(Platform-Specific-Source ${Backends-Windows-Source} ${Backends-Windows-Header}) set(GL-Platform-Specific-Source ${GL-Windows-Source}) else() set(Platform-Specific-Source ${Backends-Unix-Source} ${Backends-Unix-Header}) set(GL-Platform-Specific-Source ${GL-Unix-Source}) 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}/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}/frame.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}/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}/frame.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 ) set(GL1-Source ${GL_SRC_DIR}/gl/qgl.c ${GL_SRC_DIR}/gl/r_draw.c ${GL_SRC_DIR}/gl/r_image.c ${GL_SRC_DIR}/gl/r_light.c ${GL_SRC_DIR}/gl/r_lightmap.c ${GL_SRC_DIR}/gl/r_main.c ${GL_SRC_DIR}/gl/r_mesh.c ${GL_SRC_DIR}/gl/r_misc.c ${GL_SRC_DIR}/gl/r_model.c ${GL_SRC_DIR}/gl/r_scrap.c ${GL_SRC_DIR}/gl/r_surf.c ${GL_SRC_DIR}/gl/r_warp.c ${GL_SRC_DIR}/gl/r_sdl.c ${GL_SRC_DIR}/gl/r_md2.c ${GL_SRC_DIR}/gl/r_sp2.c ${GL_SRC_DIR}/files/pcx.c ${GL_SRC_DIR}/files/stb.c ${GL_SRC_DIR}/files/wal.c ${COMMON_SRC_DIR}/shared/shared.c ${COMMON_SRC_DIR}/md4.c ) set(GL1-Header ${GL_SRC_DIR}/ref_shared.h ${GL_SRC_DIR}/constants/anorms.h ${GL_SRC_DIR}/constants/anormtab.h ${GL_SRC_DIR}/constants/warpsin.h ${GL_SRC_DIR}/files/stb_image.h ${GL_SRC_DIR}/gl/header/local.h ${GL_SRC_DIR}/gl/header/model.h ${GL_SRC_DIR}/gl/header/qgl.h ${COMMON_SRC_DIR}/header/shared.h ) set(GL3-Source ${GL_SRC_DIR}/gl3/gl3_draw.c ${GL_SRC_DIR}/gl3/gl3_image.c ${GL_SRC_DIR}/gl3/gl3_light.c ${GL_SRC_DIR}/gl3/gl3_lightmap.c ${GL_SRC_DIR}/gl3/gl3_main.c ${GL_SRC_DIR}/gl3/gl3_mesh.c ${GL_SRC_DIR}/gl3/gl3_misc.c ${GL_SRC_DIR}/gl3/gl3_model.c ${GL_SRC_DIR}/gl3/gl3_sdl.c ${GL_SRC_DIR}/gl3/gl3_surf.c ${GL_SRC_DIR}/gl3/gl3_warp.c ${GL_SRC_DIR}/gl3/gl3_shaders.c ${GL_SRC_DIR}/gl3/gl3_md2.c ${GL_SRC_DIR}/gl3/gl3_sp2.c ${GL_SRC_DIR}/gl3/glad/src/glad.c ${GL_SRC_DIR}/files/pcx.c ${GL_SRC_DIR}/files/stb.c ${GL_SRC_DIR}/files/wal.c ${COMMON_SRC_DIR}/shared/shared.c ${COMMON_SRC_DIR}/md4.c ) set(GL3-Header ${GL_SRC_DIR}/ref_shared.h ${GL_SRC_DIR}/constants/anorms.h ${GL_SRC_DIR}/constants/anormtab.h ${GL_SRC_DIR}/constants/warpsin.h ${GL_SRC_DIR}/files/stb_image.h ${GL_SRC_DIR}/gl3/glad/include/glad/glad.h ${GL_SRC_DIR}/gl3/glad/include/KHR/khrplatform.h ${GL_SRC_DIR}/gl3/header/DG_dynarr.h ${GL_SRC_DIR}/gl3/header/HandmadeMath.h ${GL_SRC_DIR}/gl3/header/local.h ${GL_SRC_DIR}/gl3/header/model.h ${COMMON_SRC_DIR}/header/shared.h ) # 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 RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release ) if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") target_link_libraries(quake2 ${yquake2LinkerFlags} ${yquake2ClientLinkerFlags} ${yquake2SDLLinkerFlags} ${yquake2ZLibLinkerFlags} ws2_32 winmm) else() target_link_libraries(quake2 ${yquake2LinkerFlags} ${yquake2ClientLinkerFlags} ${yquake2SDLLinkerFlags} ${yquake2ZLibLinkerFlags}) endif() # Quake 2 Dedicated Server add_executable(q2ded ${Server-Source} ${Server-Header} ${Platform-Specific-Source}) set_target_properties(q2ded PROPERTIES COMPILE_DEFINITIONS "DEDICATED_ONLY" RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release ) if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") target_link_libraries(q2ded ${yquake2LinkerFlags} ${yquake2ZLibLinkerFlags} ws2_32 winmm) else() target_link_libraries(q2ded ${yquake2LinkerFlags} ${yquake2ZLibLinkerFlags}) endif() # Build the game dynamic library add_library(game MODULE ${Game-Source} ${Game-Header}) set_target_properties(game PROPERTIES PREFIX "" LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release/baseq2 RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release/baseq2 SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX} ) target_link_libraries(game ${yquake2LinkerFlags}) # Build the GL1 dynamic library add_library(ref_gl1 MODULE ${GL1-Source} ${GL1-Header} ${GL-Platform-Specific-Source}) set_target_properties(ref_gl1 PROPERTIES PREFIX "" LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX} ) target_link_libraries(ref_gl1 ${yquake2LinkerFlags} ${yquake2OpenGLLinkerFlags} ${yquake2SDLLinkerFlags}) # Build the GL3 dynamic library add_library(ref_gl3 MODULE ${GL3-Source} ${GL3-Header} ${GL-Platform-Specific-Source}) set_target_properties(ref_gl3 PROPERTIES PREFIX "" LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX} ) target_link_libraries(ref_gl3 ${yquake2LinkerFlags} ${yquake2SDLLinkerFlags}) yquake2-QUAKE2_7_10/CONTRIBUTE000066400000000000000000000044611321245476300155300ustar00rootroot00000000000000 * ****************************** * * Yamagi Quake II * * http://www.yamagi.org/quake2 * * http://github.com/yquake2 * * ****************************** * TODO List =============================================================================== At this time there're no open tasks regaring Quake II. Nevertheless the hints for working with the code: - 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. yquake2-QUAKE2_7_10/LICENSE000066400000000000000000000550141321245476300151340ustar00rootroot00000000000000Yamagi 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 - stb 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 - stereo quake - zeq2 The following code is used in library form and thus not part of Yamagi Quake II: - libogg - libopenal - libvorbis - libz - libX11 - libXxf86vm - SDL =============================================================================== 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 =============================================================================== Sean's Tool Box -- public domain -- http://nothings.org/stb.h no warranty is offered or implied; use this code at your own risk =============================================================================== yquake2-QUAKE2_7_10/Makefile000077500000000000000000000635421321245476300155770ustar00rootroot00000000000000# ------------------------------------------------------ # # 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 # Enables optional runtime loading of OpenAL (dlopen or # similar). If set to "no", the library is linked in at # compile time in the normal way. On Windows this option # is ignored, OpenAL is always loaded at runtime. DLOPEN_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 YQ2_OSTYPE := Windows else YQ2_OSTYPE ?= $(shell uname -s) endif # Special case for MinGW ifneq (,$(findstring MINGW,$(YQ2_OSTYPE))) YQ2_OSTYPE := Windows endif # Detect the architecture ifeq ($(YQ2_OSTYPE), Windows) ifdef PROCESSOR_ARCHITEW6432 # 64 bit Windows YQ2_ARCH ?= $(PROCESSOR_ARCHITEW6432) else # 32 bit Windows YQ2_ARCH ?= $(PROCESSOR_ARCHITECTURE) endif else # Normalize some abiguous YQ2_ARCH strings YQ2_ARCH ?= $(shell uname -m | sed -e 's/i.86/i386/' -e 's/amd64/x86_64/' -e 's/^arm.*/arm/') endif # Detect the compiler ifeq ($(shell $(CC) -v 2>&1 | grep -c "clang version"), 1) COMPILER := clang else ifeq ($(shell $(CC) -v 2>&1 | grep -c "gcc version"), 1) COMPILER := gcc else COMPILER := unknown 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 ($(YQ2_OSTYPE), Darwin) CFLAGS := -O2 -fno-strict-aliasing -fomit-frame-pointer \ -Wall -pipe -g -fwrapv CFLAGS += $(OSX_ARCH) else CFLAGS := -std=gnu99 -O2 -fno-strict-aliasing \ -Wall -pipe -g -ggdb -MMD -fwrapv endif # ---------- # Defines the operating system and architecture CFLAGS += -DYQ2OSTYPE=\"$(YQ2_OSTYPE)\" -DYQ2ARCH=\"$(YQ2_ARCH)\" # ---------- # https://reproducible-builds.org/specs/source-date-epoch/ ifdef SOURCE_DATE_EPOCH CFLAGS += -DBUILD_DATE=\"$(shell date --utc --date="@${SOURCE_DATE_EPOCH}" +"%b %_d %Y" | sed -e 's/ /\\ /g')\" endif # ---------- # If we're building with gcc for i386 let's define -ffloat-store. # This helps the old and crappy x87 FPU to produce correct values. # Would be nice if Clang had something comparable. ifeq ($(YQ2_ARCH), i386) ifeq ($(COMPILER), gcc) CFLAGS += -ffloat-store endif endif # Force SSE math on x86_64. All sane compilers should do this # anyway, just to protect us from broken Linux distros. ifeq ($(YQ2_ARCH), x86_64) CFLAGS += -mfpmath=sse 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 ($(YQ2_OSTYPE),Windows) CC := gcc endif # ---------- # Extra CFLAGS for SDL ifeq ($(WITH_SDL2),yes) SDLCFLAGS := $(shell sdl2-config --cflags) else # not SDL2 SDLCFLAGS := $(shell sdl-config --cflags) endif # SDL2 # ---------- # Extra CFLAGS for X11 ifneq ($(YQ2_OSTYPE), Windows) ifneq ($(YQ2_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 ($(YQ2_OSTYPE),Linux) INCLUDE := -I/usr/include else ifeq ($(YQ2_OSTYPE),FreeBSD) INCLUDE := -I/usr/local/include else ifeq ($(YQ2_OSTYPE),OpenBSD) INCLUDE := -I/usr/local/include else ifeq ($(YQ2_OSTYPE),Windows) INCLUDE := -I/usr/include endif # ---------- # Extra includes for GLAD GLAD_INCLUDE = -Isrc/client/refresh/gl3/glad/include # ---------- # Base LDFLAGS. ifeq ($(YQ2_OSTYPE),Linux) LDFLAGS := -L/usr/lib -lm -ldl -rdynamic else ifeq ($(YQ2_OSTYPE),FreeBSD) LDFLAGS := -L/usr/local/lib -lm else ifeq ($(YQ2_OSTYPE),OpenBSD) LDFLAGS := -L/usr/local/lib -lm else ifeq ($(YQ2_OSTYPE),Windows) LDFLAGS := -L/usr/lib -lws2_32 -lwinmm -static-libgcc else ifeq ($(YQ2_OSTYPE), Darwin) LDFLAGS := $(OSX_ARCH) -lm endif CFLAGS += -fvisibility=hidden LDFLAGS += -fvisibility=hidden ifneq ($(YQ2_OSTYPE), $(filter $(YQ2_OSTYPE), Darwin OpenBSD)) # for some reason the OSX & OpenBSD linker doesn't support this LDFLAGS += -Wl,--no-undefined endif # ---------- # Extra LDFLAGS for SDL ifeq ($(YQ2_OSTYPE), Darwin) ifeq ($(WITH_SDL2),yes) SDLLDFLAGS := -lSDL2 else # not SDL2 SDLLDFLAGS := -lSDL -framework OpenGL -framework Cocoa endif # SDL2 else # not Darwin ifeq ($(WITH_SDL2),yes) SDLLDFLAGS := $(shell sdl2-config --libs) else # not SDL2 SDLLDFLAGS := $(shell sdl-config --libs) endif # SDL2 endif # Darwin # ---------- # Extra LDFLAGS for X11 ifneq ($(YQ2_OSTYPE), Windows) ifneq ($(YQ2_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 ref_gl1 ref_gl3 # ---------- # Builds everything all: config client server game ref_gl1 ref_gl3 # ---------- # 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 ($(YQ2_OSTYPE), Windows) icon: @echo "===> WR build/icon/icon.res" ${Q}mkdir -p build/icon ${Q}windres src/backends/windows/icon.rc -O COFF -o build/icon/icon.res endif # ---------- # Cleanup clean: @echo "===> CLEAN" ${Q}rm -Rf build release/* cleanall: @echo "===> CLEAN" ${Q}rm -Rf build release # ---------- # The client ifeq ($(YQ2_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"' -DDLOPEN_OPENAL 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 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) $(INCLUDE) -o $@ $< ifeq ($(YQ2_OSTYPE), Darwin) build/client/%.o : %.m @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) $(OSX_ARCH) -x objective-c -c $< -o $@ endif release/quake2 : CFLAGS += -Wno-unused-result 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 ($(DLOPEN_OPENAL),yes) ifeq ($(YQ2_OSTYPE), OpenBSD) release/quake2 : CFLAGS += -DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER='"libopenal.so"' -DDLOPEN_OPENAL else ifeq ($(YQ2_OSTYPE), Darwin) release/quake2 : CFLAGS += -DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER='"libopenal.dylib"' -I/usr/local/opt/openal-soft/include -DDLOPEN_OPENAL release/quake2 : LDFLAGS += -L/usr/local/opt/openal-soft/lib -rpath /usr/local/opt/openal-soft/lib else release/quake2 : CFLAGS += -DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER='"libopenal.so.1"' -DDLOPEN_OPENAL endif else # !DLOPEN_OPENAL release/quake2 : CFLAGS += -DUSE_OPENAL release/quake2 : LDFLAGS += -lopenal ifeq ($(YQ2_OSTYPE), Darwin) release/quake2 : CFLAGS += -I/usr/local/opt/openal-soft/include release/quake2 : LDFLAGS += -L/usr/local/opt/openal-soft/lib -rpath /usr/local/opt/openal-soft/lib endif # Darwin endif # !DLOPEN_OPENAL endif # WITH_OPENAL 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 ($(YQ2_OSTYPE), Darwin) release/ref_gl1.so : LDFLAGS += -lGL endif ifeq ($(YQ2_OSTYPE), FreeBSD) release/quake2 : LDFLAGS += -Wl,-z,origin,-rpath='$$ORIGIN/lib' else ifeq ($(YQ2_OSTYPE), Linux) release/quake2 : LDFLAGS += -Wl,-z,origin,-rpath='$$ORIGIN/lib' endif ifeq ($(WITH_SYSTEMWIDE),yes) ifneq ($(WITH_SYSTEMDIR),"") ifeq ($(YQ2_OSTYPE), FreeBSD) release/quake2 : LDFLAGS += -Wl,-z,origin,-rpath='$(WITH_SYSTEMDIR)/lib' else ifeq ($(YQ2_OSTYPE), Linux) release/quake2 : LDFLAGS += -Wl,-z,origin,-rpath='$(WITH_SYSTEMDIR)/lib' endif else ifeq ($(YQ2_OSTYPE), FreeBSD) release/quake2 : LDFLAGS += -Wl,-z,origin,-rpath='/usr/share/games/quake2/lib' else ifeq ($(YQ2_OSTYPE), Linux) release/quake2 : LDFLAGS += -Wl,-z,origin,-rpath='/usr/share/games/quake2/lib' endif endif endif endif # ---------- # The server ifeq ($(YQ2_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 -Wno-unused-result ifeq ($(WITH_ZIP),yes) release/q2ded : CFLAGS += -DZIP -DNOUNCRYPT release/q2ded : LDFLAGS += -lz endif endif # ---------- # The OpenGL 1.x renderer lib ifeq ($(YQ2_OSTYPE), Windows) ref_gl1: @echo "===> Building ref_gl1.dll" $(MAKE) release/ref_gl1.dll ifeq ($(WITH_SDL2),yes) release/ref_gl1.dll : CFLAGS += -DSDL2 endif release/ref_gl1.dll : LDFLAGS += -lopengl32 -shared # don't want the dll to link against libSDL2main or libmingw32, and no -mwindows either # that's for the .exe only DLL_SDLLDFLAGS = $(subst -mwindows,,$(subst -lmingw32,,$(subst -lSDL2main,,$(SDLLDFLAGS)))) else ifeq ($(YQ2_OSTYPE), Darwin) ref_gl1: @echo "===> Building ref_gl1.dylib" $(MAKE) release/ref_gl1.dylib ifeq ($(WITH_SDL2),yes) release/ref_gl1.dylib : CFLAGS += -DSDL2 endif release/ref_gl1.dylib : LDFLAGS += -shared -framework OpenGL else # not Windows or Darwin ref_gl1: @echo "===> Building ref_gl1.so" $(MAKE) release/ref_gl1.so release/ref_gl1.so : CFLAGS += -fPIC release/ref_gl1.so : LDFLAGS += -shared ifeq ($(WITH_SDL2),yes) release/ref_gl1.so : CFLAGS += -DSDL2 endif endif # OS specific ref_gl1 shit build/ref_gl1/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(SDLCFLAGS) $(X11CFLAGS) $(INCLUDE) -o $@ $< # ---------- # The OpenGL 3.x renderer lib ifeq ($(YQ2_OSTYPE), Windows) ref_gl3: @echo "===> Building ref_gl3.dll" $(MAKE) release/ref_gl3.dll ifeq ($(WITH_SDL2),yes) release/ref_gl3.dll : CFLAGS += -DSDL2 endif release/ref_gl3.dll : LDFLAGS += -shared else ifeq ($(YQ2_OSTYPE), Darwin) ref_gl3: @echo "===> Building ref_gl3.dylib" $(MAKE) release/ref_gl3.dylib ifeq ($(WITH_SDL2),yes) release/ref_gl3.dylib : CFLAGS += -DSDL2 endif release/ref_gl3.dylib : LDFLAGS += -shared else # not Windows or Darwin ref_gl3: @echo "===> Building ref_gl3.so" $(MAKE) release/ref_gl3.so release/ref_gl3.so : CFLAGS += -fPIC release/ref_gl3.so : LDFLAGS += -shared ifeq ($(WITH_SDL2),yes) release/ref_gl3.so : CFLAGS += -DSDL2 endif endif # OS specific ref_gl3 shit build/ref_gl3/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(SDLCFLAGS) $(INCLUDE) $(GLAD_INCLUDE) -o $@ $< # ---------- # The baseq2 game ifeq ($(YQ2_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 ($(YQ2_OSTYPE), Darwin) game: @echo "===> Building baseq2/game.dylib" ${Q}mkdir -p release/baseq2 $(MAKE) release/baseq2/game.dylib build/baseq2/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(INCLUDE) -o $@ $< release/baseq2/game.dylib : CFLAGS += -fPIC release/baseq2/game.dylib : 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 -Wno-unused-result 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/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/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/frame.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 ($(YQ2_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 # ---------- REFGL1_OBJS_ := \ src/client/refresh/gl/qgl.o \ src/client/refresh/gl/r_draw.o \ src/client/refresh/gl/r_image.o \ src/client/refresh/gl/r_light.o \ src/client/refresh/gl/r_lightmap.o \ src/client/refresh/gl/r_main.o \ src/client/refresh/gl/r_mesh.o \ src/client/refresh/gl/r_misc.o \ src/client/refresh/gl/r_model.o \ src/client/refresh/gl/r_scrap.o \ src/client/refresh/gl/r_surf.o \ src/client/refresh/gl/r_warp.o \ src/client/refresh/gl/r_sdl.o \ src/client/refresh/gl/r_md2.o \ src/client/refresh/gl/r_sp2.o \ src/client/refresh/files/pcx.o \ src/client/refresh/files/stb.o \ src/client/refresh/files/wal.o \ src/common/shared/shared.o \ src/common/md4.o ifeq ($(YQ2_OSTYPE), Windows) REFGL1_OBJS_ += \ src/backends/windows/shared/mem.o else # not Windows REFGL1_OBJS_ += \ src/backends/unix/shared/hunk.o endif # ---------- REFGL3_OBJS_ := \ src/client/refresh/gl3/gl3_draw.o \ src/client/refresh/gl3/gl3_image.o \ src/client/refresh/gl3/gl3_light.o \ src/client/refresh/gl3/gl3_lightmap.o \ src/client/refresh/gl3/gl3_main.o \ src/client/refresh/gl3/gl3_mesh.o \ src/client/refresh/gl3/gl3_misc.o \ src/client/refresh/gl3/gl3_model.o \ src/client/refresh/gl3/gl3_sdl.o \ src/client/refresh/gl3/gl3_surf.o \ src/client/refresh/gl3/gl3_warp.o \ src/client/refresh/gl3/gl3_shaders.o \ src/client/refresh/gl3/gl3_md2.o \ src/client/refresh/gl3/gl3_sp2.o \ src/client/refresh/gl3/glad/src/glad.o \ src/client/refresh/files/pcx.o \ src/client/refresh/files/stb.o \ src/client/refresh/files/wal.o \ src/common/shared/shared.o \ src/common/md4.o ifeq ($(YQ2_OSTYPE), Windows) REFGL3_OBJS_ += \ src/backends/windows/shared/mem.o else # not Windows REFGL3_OBJS_ += \ 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/frame.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 ($(YQ2_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_)) REFGL1_OBJS = $(patsubst %,build/ref_gl1/%,$(REFGL1_OBJS_)) REFGL3_OBJS = $(patsubst %,build/ref_gl3/%,$(REFGL3_OBJS_)) SERVER_OBJS = $(patsubst %,build/server/%,$(SERVER_OBJS_)) GAME_OBJS = $(patsubst %,build/baseq2/%,$(GAME_OBJS_)) # ---------- # Generate header dependencies CLIENT_DEPS= $(CLIENT_OBJS:.o=.d) REFGL1_DEPS= $(REFGL1_OBJS:.o=.d) REFGL3_DEPS= $(REFGL3_OBJS:.o=.d) SERVER_DEPS= $(SERVER_OBJS:.o=.d) GAME_DEPS= $(GAME_OBJS:.o=.d) # ---------- # Suck header dependencies in -include $(CLIENT_DEPS) -include $(REFGL1_DEPS) -include $(REFGL3_DEPS) -include $(SERVER_DEPS) -include $(GAME_DEPS) # ---------- # release/quake2 ifeq ($(YQ2_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 ($(YQ2_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/ref_gl1.so ifeq ($(YQ2_OSTYPE), Windows) release/ref_gl1.dll : $(REFGL1_OBJS) @echo "===> LD $@" ${Q}$(CC) $(REFGL1_OBJS) $(LDFLAGS) $(DLL_SDLLDFLAGS) -o $@ $(Q)strip $@ else ifeq ($(YQ2_OSTYPE), Darwin) release/ref_gl1.dylib : $(REFGL1_OBJS) @echo "===> LD $@" ${Q}$(CC) $(REFGL1_OBJS) $(LDFLAGS) $(SDLLDFLAGS) -o $@ else release/ref_gl1.so : $(REFGL1_OBJS) @echo "===> LD $@" ${Q}$(CC) $(REFGL1_OBJS) $(LDFLAGS) $(SDLLDFLAGS) -o $@ endif # release/ref_gl3.so ifeq ($(YQ2_OSTYPE), Windows) release/ref_gl3.dll : $(REFGL3_OBJS) @echo "===> LD $@" ${Q}$(CC) $(REFGL3_OBJS) $(LDFLAGS) $(DLL_SDLLDFLAGS) -o $@ $(Q)strip $@ else ifeq ($(YQ2_OSTYPE), Darwin) release/ref_gl3.dylib : $(REFGL3_OBJS) @echo "===> LD $@" ${Q}$(CC) $(REFGL3_OBJS) $(LDFLAGS) $(SDLLDFLAGS) -o $@ else release/ref_gl3.so : $(REFGL3_OBJS) @echo "===> LD $@" ${Q}$(CC) $(REFGL3_OBJS) $(LDFLAGS) $(SDLLDFLAGS) -o $@ endif # release/baseq2/game.so ifeq ($(YQ2_OSTYPE), Windows) release/baseq2/game.dll : $(GAME_OBJS) @echo "===> LD $@" ${Q}$(CC) $(GAME_OBJS) $(LDFLAGS) -o $@ $(Q)strip $@ else ifeq ($(YQ2_OSTYPE), Darwin) release/baseq2/game.dylib : $(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_7_10/README.md000066400000000000000000000203161321245476300154030ustar00rootroot00000000000000# Yamagi Quake II This is the Yamagi Quake II Client, an enhanced version of id Software's Quake II with focus on offline and coop gameplay. Both the gameplay and the graphics are unchanged, but many bugs if the last official release were fixed and some nice to have features like widescreen support and a modern OpenGL 3.2 renderer were added. Unlike most other Quake II source ports Yamagi Quake II is fully 64 bit clean. It works perfectly on modern processors and operating systems. Yamagi Quake II runs on nearly all common platforms; including FreeBSD, Linux, OpenBSD, Windows and OS X. This code is build upon Icculus Quake II, which itself is based on Quake II 3.21. Yamagi Quake II is released under the terms of the GPL version 2. See the LICENSE file for further information. ## Installation Yamagi Quake II is installed in 3 steps: - Game data setup. - Music extraction. - Download and extract the executables. ### Game data Setup Over the years Quake II was distributed in a myriad of ways: - As a demo version. - As a retail release on CD. - As part of Quake IV. - Through steam. - etc. Yamagi Quake II supports all of these version, even the rather limited demo. Full versions require about 1.6 GiB hard drive space, the demo version about 150 Mib. #### Full versions The easiest way to install a full version of Quake II is to start with the patch. Please note that the patch is **required** for all full versions of the game, even the newer ones like Steam. Without it Yamagi Quake II will not work! 1. Download the patch: http://deponie.yamagi.org/quake2/idstuff/q2-3.20-x86-full-ctf.exe 2. Extract the patch into an empty directory. The patch is just an ordinary self-extracting ZIP file. On Windows it can be extracted by double clicking on it, on other systems an archiver or even the *unzip* command can be used. Now it's time to remove the following files from the extracted patch. They're the original executables, documentation and so on. They aren't needed anymore: - 3.20_Changes.txt - quake2.exe - ref_gl.dll - ref_soft.dll - baseq2/gamex86.dll - ctf/ctf2.ico - ctf/gamex86.dll - ctf/readme.txt - ctf/server.cfg - xatrix/gamex86.dll - rogue/gamex86.dll Copy the pak0.pak file and the video/ subdirectory from your Quake II distribution (CD, Steam download, etc) into the baseq2/ subdirectory of the extracted patch. If you own the optional addons you'll need to copy their gamedata too: - For The Reckoning copy the pak0.pak and the video/ subdirectory from your addon distribution into the xatrix/ subdirectory. - For Ground Zero copy the pak0.pak and the video/ subdirectory from your addon distribution into the rogue/ subdirectory. #### The demo version 1. Get the demo from http://deponie.yamagi.org/quake2/idstuff/q2-314-demo-x86.exe and extract it. It's just an ordinary, self-extract ZIP file. On Windows it can be extracted by double clicking on it, on other system an archiver or even the *unzip* command can be used. 3. Create a new directory and a subdirectory baseq2/ in it. 3. Copy the pak0.pak and the players/ subdirectory from Install/Data/baseq2/ into the newly created baseq2/ subdirectory. The demo **must not** be patched! Patching the demo will break it! ### Music extraction The retail CD version of Quake II and both addons contain up to 11 Audio CD tracks, forming the soundtrack. Since modern computers lack the ability for classic Audio CD playback, it's often emulated by a transparent combination of CD ripping and playback. The tracks can be ripped into OGG/Vorbis files. Yamagi Quake II will use these files instead of the CD tracks. Later Quake II version, for example the one included with Quake IV and the one available through Steam, lack the soundtrack. Nevertheless Yamagi Quake II can play it if you copy the files into the directories mentioned above. #### Using a generic CD extractor / CD ripper 1. Install a CD extractor and set it to OGG/Vorbis files. 2. Put the Quake II CD into your CD drive and extract the files. 3. The files must be named after the CD track. Track 02 becomes 02.ogg, track 03 becomes 03.ogg and so on. On both the Quake II and the Addon CDs track 1 is the data track and can't be ripped. 4. Put these files into the corresponding subdirectory: - baseq2/music for Quake II. - xatrix/music for The Reckoning. - rogue/music for Ground Zero. #### Extracting music on BSD, Linux and OS X An easy way to extract the music on unixoid platforms is to use *stuff/cdripper.sh*, a simple shellscript we provide. It needs *cdparanoia* and oggenc from the *vorbis-tools* package installed. Use your package manager of choice (apt-get, dnf, homebrew, ...) to install them. Just execute the script and copy the resulting music/ directory to: - baseq2/ for Quake II. - xatrix/ for The Reckoning. - rogue/ for Ground Zero. ### Download and extract the executables How the Yamagi Quake II executables are installed depends on the platform: - For Windows a package with all Yamagi Quake II executables is provided. - Some Linux distributions and BSD systems provide Yamagi Quake II packages. - On OS X you need to compile from source. - Of course Yamagi Quake II can be compiled from source on all platforms. #### Microsoft Windows 1. Get the latest release from http://www.yamagi.org/quake2 2. Extract it into the gamedata directory created above. quake2.exe must be placed next to the baseq2/ subdirectory. On Windows Yamagi Quake II is fully portable, you can move the installation directory wherever and whenever you like. To update Yamagi Quake II just overwrite the old files with the new ones. #### Binary Package from your Linux distribution or BSD system Many Linux distributions and BSD systems provide Yamagi Quake II packages. Please refer to the documentation of your distribution or system. The gamedata is searched at: - A global directory specified by the package. - The same directory as the quake2 executable. - In $HOME/.yq2 - The directory given with the *+set basedir /path/to/quake2_installation/* commandline argument. If you're a package maintainer, please look at our documentation at [stuff/packaging.md](stuff/packaging.md). ### Compiling from source To compile Yamagi Quake II from source, you need the following dependencies, including development headers: - A GCC-compatible compiler like GCC, MinGW (see below) or clang. - GNU make or cmake. - A libGL implementation with OpenGL system headers. - SDL 2.0 or SDL 1.2 (2.0 recommended, edit the Makefile to use 1.2 instead). - libogg, libvorbis and libvorbisfile. - A OpenAL implementation, openal-soft is highly recommended. - zlib. Yamagi Quake II has 2 build systems: - A classic GNU Makefile. - A CMake file. Both build system provide the same options and features, it's up to the user to decide which one to use. #### On Linux distribution or BSD systems On debian based distributions (including Ubuntu and Mint) the dependencies can be installed with: `apt-get install build-essential libgl1-mesa-dev libsdl2-dev libogg-dev libvorbis-dev libopenal-dev zlib1g-dev` On OS X we recommend using [homebrew](http://brew.sh) to install the dependencies: `brew install sdl2 libvorbis libogg openal-soft` On FreeBSD you'll need something like: `pkg install gmake libGL sdl2 libogg libvorbis openal-soft` #### On Windows On Windows a MinGW environment is needed. A preinstalled environment with all dependencies can be found at http://deponie.yamagi.org/quake2/windows/build/ Just extract it into C:\MSYS2\ and start either the 32 bit or 64 bit version through *C:\MSYS2\msys32.exe* or *C:\MSYS2\msys64.exe*. With the preinstalled MinGW environment GNU Make is highly recommended, CMake requires further configuration. At this time Yamagi Quake II can't be compiled with Microsoft Visual Studio. #### Compiling Download the latest release from http://www.yamagi.org/quake2 or clone the source from https://github.com/yquake2/yquake2.git, change into the yquake2/ source directory and type *make*. After that copy everything from the release/ directory to your Yamagi Quake II installation directory. For the addons download or clone their source, change into the source directory and type *make*. After the compilation finishes the release/game.so is copied to the corresponding directory in your Quake II installation. yquake2-QUAKE2_7_10/src/000077500000000000000000000000001321245476300147115ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/backends/000077500000000000000000000000001321245476300164635ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/backends/generic/000077500000000000000000000000001321245476300200775ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/backends/generic/header/000077500000000000000000000000001321245476300213275ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/backends/generic/header/input.h000066400000000000000000000025651321245476300226470ustar00rootroot00000000000000/* * 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); /* * Removes all pending events from SDLs queue. */ void In_FlushQueue(void); #endif yquake2-QUAKE2_7_10/src/backends/generic/header/qal.h000066400000000000000000000107301321245476300222560ustar00rootroot00000000000000/* * 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_7_10/src/backends/generic/header/stb_image_write.h000066400000000000000000001663521321245476300246610ustar00rootroot00000000000000 /* !!! NOTE: this version of stb_image_write.h is modified !!! * I added the following things: * - JPEG support (maybe this gets upstream, we'll see: https://github.com/nothings/stb/pull/432 * - STBIW_ZLIB_COMPRESS to supply custom zlib compress()-like function for PNG * - stbi_png_level to allow configuring PNG compression level */ /* stb_image_write - v1.05 - public domain - http://nothings.org/stb/stb_image_write.h writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk Before #including, #define STB_IMAGE_WRITE_IMPLEMENTATION in the file that you want to have the implementation. Will probably not work correctly with strict-aliasing optimizations. ABOUT: This header file is a library for writing images to C stdio. It could be adapted to write to memory or a general streaming interface; let me know. The PNG output is not optimal; it is 20-50% larger than the file written by a decent optimizing implementation. This library is designed for source code compactness and simplicity, not optimal image file size or run-time performance. BUILDING: You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace malloc,realloc,free. You can define STBIW_MEMMOVE() to replace memmove() USAGE: There are four functions, one for each image file format: int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) There are also four equivalent functions that use an arbitrary write function. You are expected to open/close your file-equivalent before and after calling these: int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); where the callback is: void stbi_write_func(void *context, void *data, int size); You can define STBI_WRITE_NO_STDIO to disable the file variant of these functions, so the library will not use stdio.h at all. However, this will also disable HDR writing, because it requires stdio for formatted output. Each function returns 0 on failure and non-0 on success. The functions create an image file defined by the parameters. The image is a rectangle of pixels stored from left-to-right, top-to-bottom. Each pixel contains 'comp' channels of data stored interleaved with 8-bits per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. The *data pointer points to the first byte of the top-left-most pixel. For PNG, "stride_in_bytes" is the distance in bytes from the first byte of a row of pixels to the first byte of the next row of pixels. PNG creates output files with the same number of components as the input. The BMP format expands Y to RGB in the file format and does not output alpha. PNG supports writing rectangles of data even when the bytes storing rows of data are not consecutive in memory (e.g. sub-rectangles of a larger image), by supplying the stride between the beginning of adjacent rows. The other formats do not. (Thus you cannot write a native-format BMP through the BMP writer, both because it is in BGR order and because it may have padding at the end of the line.) HDR expects linear float data. Since the format is always 32-bit rgb(e) data, alpha (if provided) is discarded, and for monochrome data it is replicated across all three channels. TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed data, set the global variable 'stbi_write_tga_with_rle' to 0. JPEG does ignore alpha channels in input data; quality is between 1 and 100. Higher quality looks better but results in a bigger image. JPEG baseline (no JPEG progressive). CREDITS: PNG/BMP/TGA Sean Barrett HDR Baldur Karlsson TGA monochrome: Jean-Sebastien Guay misc enhancements: Tim Kelsey TGA RLE Alan Hickman initial file IO callback implementation Emmanuel Julien JPEG Jon Olick (original jo_jpeg.cpp code) Daniel Gibson bugfixes: github:Chribba Guillaume Chereau github:jry2 github:romigrou Sergio Gonzalez Jonas Karlsson Filip Wasil Thatcher Ulrich github:poppolopoppo Patrick Boettcher LICENSE See end of file for license information. */ #ifndef INCLUDE_STB_IMAGE_WRITE_H #define INCLUDE_STB_IMAGE_WRITE_H #ifdef __cplusplus extern "C" { #endif #ifdef STB_IMAGE_WRITE_STATIC #define STBIWDEF static #else #define STBIWDEF extern extern int stbi_write_tga_with_rle; extern int stbi_png_level; #endif #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); #endif typedef void stbi_write_func(void *context, void *data, int size); STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); #ifdef __cplusplus } #endif #endif//INCLUDE_STB_IMAGE_WRITE_H #ifdef STB_IMAGE_WRITE_IMPLEMENTATION #ifdef _WIN32 #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #ifndef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE #endif #endif #ifndef STBI_WRITE_NO_STDIO #include #endif // STBI_WRITE_NO_STDIO #include #include #include #include #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) // ok #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) // ok #else #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." #endif #ifndef STBIW_MALLOC #define STBIW_MALLOC(sz) malloc(sz) #define STBIW_REALLOC(p,newsz) realloc(p,newsz) #define STBIW_FREE(p) free(p) #endif #ifndef STBIW_REALLOC_SIZED #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) #endif #ifndef STBIW_MEMMOVE #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) #endif #ifndef STBIW_ASSERT #include #define STBIW_ASSERT(x) assert(x) #endif #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) typedef struct { stbi_write_func *func; void *context; } stbi__write_context; // initialize a callback-based context static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) { s->func = c; s->context = context; } #ifndef STBI_WRITE_NO_STDIO static void stbi__stdio_write(void *context, void *data, int size) { fwrite(data,1,size,(FILE*) context); } static int stbi__start_write_file(stbi__write_context *s, const char *filename) { FILE *f = fopen(filename, "wb"); stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); return f != NULL; } static void stbi__end_write_file(stbi__write_context *s) { fclose((FILE *)s->context); } #endif // !STBI_WRITE_NO_STDIO typedef unsigned int stbiw_uint32; typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; #ifdef STB_IMAGE_WRITE_STATIC static int stbi_write_tga_with_rle = 1; #else int stbi_write_tga_with_rle = 1; #endif static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { while (*fmt) { switch (*fmt++) { case ' ': break; case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); s->func(s->context,&x,1); break; } case '2': { int x = va_arg(v,int); unsigned char b[2]; b[0] = STBIW_UCHAR(x); b[1] = STBIW_UCHAR(x>>8); s->func(s->context,b,2); break; } case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4]; b[0]=STBIW_UCHAR(x); b[1]=STBIW_UCHAR(x>>8); b[2]=STBIW_UCHAR(x>>16); b[3]=STBIW_UCHAR(x>>24); s->func(s->context,b,4); break; } default: STBIW_ASSERT(0); return; } } } static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) { va_list v; va_start(v, fmt); stbiw__writefv(s, fmt, v); va_end(v); } static void stbiw__putc(stbi__write_context *s, unsigned char c) { s->func(s->context, &c, 1); } static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { unsigned char arr[3]; arr[0] = a, arr[1] = b, arr[2] = c; s->func(s->context, arr, 3); } static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) { unsigned char bg[3] = { 255, 0, 255}, px[3]; int k; if (write_alpha < 0) s->func(s->context, &d[comp - 1], 1); switch (comp) { case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case case 1: if (expand_mono) stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp else s->func(s->context, d, 1); // monochrome TGA break; case 4: if (!write_alpha) { // composite against pink background for (k = 0; k < 3; ++k) px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); break; } /* FALLTHROUGH */ case 3: stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); break; } if (write_alpha > 0) s->func(s->context, &d[comp - 1], 1); } static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) { stbiw_uint32 zero = 0; int i,j, j_end; if (y <= 0) return; if (vdir < 0) j_end = -1, j = y-1; else j_end = y, j = 0; for (; j != j_end; j += vdir) { for (i=0; i < x; ++i) { unsigned char *d = (unsigned char *) data + (j*x+i)*comp; stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); } s->func(s->context, &zero, scanline_pad); } } static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) { if (y < 0 || x < 0) { return 0; } else { va_list v; va_start(v, fmt); stbiw__writefv(s, fmt, v); va_end(v); stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); return 1; } } static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { int pad = (-x*3) & 3; return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, "11 4 22 4" "4 44 22 444444", 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header } STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); return stbi_write_bmp_core(&s, x, y, comp, data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_bmp_core(&s, x, y, comp, data); stbi__end_write_file(&s); return r; } else return 0; } #endif //!STBI_WRITE_NO_STDIO static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) { int has_alpha = (comp == 2 || comp == 4); int colorbytes = has_alpha ? comp-1 : comp; int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 if (y < 0 || x < 0) return 0; if (!stbi_write_tga_with_rle) { return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); } else { int i,j,k; stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); for (j = y - 1; j >= 0; --j) { unsigned char *row = (unsigned char *) data + j * x * comp; int len; for (i = 0; i < x; i += len) { unsigned char *begin = row + i * comp; int diff = 1; len = 1; if (i < x - 1) { ++len; diff = memcmp(begin, row + (i + 1) * comp, comp); if (diff) { const unsigned char *prev = begin; for (k = i + 2; k < x && len < 128; ++k) { if (memcmp(prev, row + k * comp, comp)) { prev += comp; ++len; } else { --len; break; } } } else { for (k = i + 2; k < x && len < 128; ++k) { if (!memcmp(begin, row + k * comp, comp)) { ++len; } else { break; } } } } if (diff) { unsigned char header = STBIW_UCHAR(len - 1); s->func(s->context, &header, 1); for (k = 0; k < len; ++k) { stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); } } else { unsigned char header = STBIW_UCHAR(len - 129); s->func(s->context, &header, 1); stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); } } } } return 1; } STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); return stbi_write_tga_core(&s, x, y, comp, (void *) data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); stbi__end_write_file(&s); return r; } else return 0; } #endif // ************************************************************************************************* // Radiance RGBE HDR writer // by Baldur Karlsson #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) { int exponent; float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); if (maxcomp < 1e-32f) { rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; } else { float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; rgbe[0] = (unsigned char)(linear[0] * normalize); rgbe[1] = (unsigned char)(linear[1] * normalize); rgbe[2] = (unsigned char)(linear[2] * normalize); rgbe[3] = (unsigned char)(exponent + 128); } } void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) { unsigned char lengthbyte = STBIW_UCHAR(length+128); STBIW_ASSERT(length+128 <= 255); s->func(s->context, &lengthbyte, 1); s->func(s->context, &databyte, 1); } void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) { unsigned char lengthbyte = STBIW_UCHAR(length); STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code s->func(s->context, &lengthbyte, 1); s->func(s->context, data, length); } void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) { unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; unsigned char rgbe[4]; float linear[3]; int x; scanlineheader[2] = (width&0xff00)>>8; scanlineheader[3] = (width&0x00ff); /* skip RLE for images too small or large */ if (width < 8 || width >= 32768) { for (x=0; x < width; x++) { switch (ncomp) { case 4: /* fallthrough */ case 3: linear[2] = scanline[x*ncomp + 2]; linear[1] = scanline[x*ncomp + 1]; linear[0] = scanline[x*ncomp + 0]; break; default: linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; break; } stbiw__linear_to_rgbe(rgbe, linear); s->func(s->context, rgbe, 4); } } else { int c,r; /* encode into scratch buffer */ for (x=0; x < width; x++) { switch(ncomp) { case 4: /* fallthrough */ case 3: linear[2] = scanline[x*ncomp + 2]; linear[1] = scanline[x*ncomp + 1]; linear[0] = scanline[x*ncomp + 0]; break; default: linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; break; } stbiw__linear_to_rgbe(rgbe, linear); scratch[x + width*0] = rgbe[0]; scratch[x + width*1] = rgbe[1]; scratch[x + width*2] = rgbe[2]; scratch[x + width*3] = rgbe[3]; } s->func(s->context, scanlineheader, 4); /* RLE each component separately */ for (c=0; c < 4; c++) { unsigned char *comp = &scratch[width*c]; x = 0; while (x < width) { // find first run r = x; while (r+2 < width) { if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) break; ++r; } if (r+2 >= width) r = width; // dump up to first run while (x < r) { int len = r-x; if (len > 128) len = 128; stbiw__write_dump_data(s, len, &comp[x]); x += len; } // if there's a run, output it if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd // find next byte after run while (r < width && comp[r] == comp[x]) ++r; // output run up to r while (x < r) { int len = r-x; if (len > 127) len = 127; stbiw__write_run_data(s, len, comp[x]); x += len; } } } } } } static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) { if (y <= 0 || x <= 0 || data == NULL) return 0; else { // Each component is stored separately. Allocate scratch space for full output scanline. unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); int i, len; char buffer[128]; char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; s->func(s->context, header, sizeof(header)-1); len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); s->func(s->context, buffer, len); for(i=0; i < y; i++) stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); STBIW_FREE(scratch); return 1; } } STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); return stbi_write_hdr_core(&s, x, y, comp, (float *) data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); stbi__end_write_file(&s); return r; } else return 0; } #endif // STBI_WRITE_NO_STDIO ////////////////////////////////////////////////////////////////////////////// // // PNG writer // // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() #ifndef STBIW_ZLIB_COMPRESS #define stbiw__sbraw(a) ((int *) (a) - 2) #define stbiw__sbm(a) stbiw__sbraw(a)[0] #define stbiw__sbn(a) stbiw__sbraw(a)[1] #define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) #define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) #define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) { int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); STBIW_ASSERT(p); if (p) { if (!*arr) ((int *) p)[1] = 0; *arr = (void *) ((int *) p + 2); stbiw__sbm(*arr) = m; } return *arr; } static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) { while (*bitcount >= 8) { stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); *bitbuffer >>= 8; *bitcount -= 8; } return data; } static int stbiw__zlib_bitrev(int code, int codebits) { int res=0; while (codebits--) { res = (res << 1) | (code & 1); code >>= 1; } return res; } static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) { int i; for (i=0; i < limit && i < 258; ++i) if (a[i] != b[i]) break; return i; } static unsigned int stbiw__zhash(unsigned char *data) { stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); hash ^= hash << 3; hash += hash >> 5; hash ^= hash << 4; hash += hash >> 17; hash ^= hash << 25; hash += hash >> 6; return hash; } #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) #define stbiw__zlib_add(code,codebits) \ (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) #define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) // default huffman tables #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) #define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) #define stbiw__ZHASH 16384 #endif // STBIW_ZLIB_COMPRESS #define MZ_MAX(a,b) (((a)>(b))?(a):(b)) unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) { #ifdef STBIW_ZLIB_COMPRESS // user provided a zlib compress implementation, use that return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); #else // use our own static unsigned short lengthc[] = { 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, 259 }; static unsigned char lengtheb[]= { 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 }; static unsigned short distc[] = { 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, 32768 }; static unsigned char disteb[] = { 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 }; unsigned int bitbuf=0; int i,j, bitcount=0; unsigned char *out = NULL; int guessedSize = MZ_MAX(128 + (data_len * 110) / 100, 128 + data_len + ((data_len / (31 * 1024)) + 1) * 5); //stbiw__sbgrow(out, guessedSize); unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); if (quality < 5) quality = 5; stbiw__sbpush(out, 0x78); // DEFLATE 32K window stbiw__sbpush(out, 0x5e); // FLEVEL = 1 stbiw__zlib_add(1,1); // BFINAL = 1 stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman for (i=0; i < stbiw__ZHASH; ++i) hash_table[i] = NULL; i=0; while (i < data_len-3) { // hash next 3 bytes of data to be compressed int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; unsigned char *bestloc = 0; unsigned char **hlist = hash_table[h]; int n = stbiw__sbcount(hlist); for (j=0; j < n; ++j) { if (hlist[j]-data > i-32768) { // if entry lies within window int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); if (d >= best) best=d,bestloc=hlist[j]; } } // when hash table entry is too long, delete half the entries if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); stbiw__sbn(hash_table[h]) = quality; } stbiw__sbpush(hash_table[h],data+i); if (bestloc) { // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); hlist = hash_table[h]; n = stbiw__sbcount(hlist); for (j=0; j < n; ++j) { if (hlist[j]-data > i-32767) { int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); if (e > best) { // if next match is better, bail on current match bestloc = NULL; break; } } } } if (bestloc) { int d = (int) (data+i - bestloc); // distance back STBIW_ASSERT(d <= 32767 && best <= 258); for (j=0; best > lengthc[j+1]-1; ++j); stbiw__zlib_huff(j+257); if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); for (j=0; d > distc[j+1]-1; ++j); stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); i += best; } else { stbiw__zlib_huffb(data[i]); ++i; } } // write out final bytes for (;i < data_len; ++i) stbiw__zlib_huffb(data[i]); stbiw__zlib_huff(256); // end of block // pad with 0 bits to byte boundary while (bitcount) stbiw__zlib_add(0,1); for (i=0; i < stbiw__ZHASH; ++i) (void) stbiw__sbfree(hash_table[i]); STBIW_FREE(hash_table); { // compute adler32 on input unsigned int s1=1, s2=0; int blocklen = (int) (data_len % 5552); j=0; while (j < data_len) { for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; s1 %= 65521, s2 %= 65521; j += blocklen; blocklen = 5552; } stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); stbiw__sbpush(out, STBIW_UCHAR(s2)); stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); stbiw__sbpush(out, STBIW_UCHAR(s1)); } *out_len = stbiw__sbn(out); // make returned pointer freeable STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); return (unsigned char *) stbiw__sbraw(out); #endif // !STBIW_ZLIB_COMPRESS } static unsigned int stbiw__crc32(unsigned char *buffer, int len) { static unsigned int crc_table[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; unsigned int crc = ~0u; int i; for (i=0; i < len; ++i) crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; return ~crc; } #define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) static void stbiw__wpcrc(unsigned char **data, int len) { unsigned int crc = stbiw__crc32(*data - len - 4, len+4); stbiw__wp32(*data, crc); } static unsigned char stbiw__paeth(int a, int b, int c) { int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); if (pb <= pc) return STBIW_UCHAR(b); return STBIW_UCHAR(c); } // @OPTIMIZE: provide an option that always forces left-predict or paeth predict unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) { int ctype[5] = { -1, 0, 4, 2, 6 }; unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; unsigned char *out,*o, *filt, *zlib; signed char *line_buffer; int i,j,k,p,zlen; if (stride_bytes == 0) stride_bytes = x * n; filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } for (j=0; j < y; ++j) { static int mapping[] = { 0,1,2,3,4 }; static int firstmap[] = { 0,1,0,5,6 }; int *mymap = (j != 0) ? mapping : firstmap; int best = 0, bestval = 0x7fffffff; for (p=0; p < 2; ++p) { for (k= p?best:0; k < 5; ++k) { // @TODO: clarity: rewrite this to go 0..5, and 'continue' the unwanted ones during 2nd pass int type = mymap[k],est=0; unsigned char *z = pixels + stride_bytes*j; for (i=0; i < n; ++i) switch (type) { case 0: line_buffer[i] = z[i]; break; case 1: line_buffer[i] = z[i]; break; case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; case 5: line_buffer[i] = z[i]; break; case 6: line_buffer[i] = z[i]; break; } for (i=n; i < x*n; ++i) { switch (type) { case 0: line_buffer[i] = z[i]; break; case 1: line_buffer[i] = z[i] - z[i-n]; break; case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; } } if (p) break; for (i=0; i < x*n; ++i) est += abs((signed char) line_buffer[i]); if (est < bestval) { bestval = est; best = k; } } } // when we get here, best contains the filter type, and line_buffer contains the data filt[j*(x*n+1)] = (unsigned char) best; STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); } STBIW_FREE(line_buffer); zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_png_level); STBIW_FREE(filt); if (!zlib) return 0; // each tag requires 12 bytes of overhead out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); if (!out) return 0; *out_len = 8 + 12+13 + 12+zlen + 12; o=out; STBIW_MEMMOVE(o,sig,8); o+= 8; stbiw__wp32(o, 13); // header length stbiw__wptag(o, "IHDR"); stbiw__wp32(o, x); stbiw__wp32(o, y); *o++ = 8; *o++ = STBIW_UCHAR(ctype[n]); *o++ = 0; *o++ = 0; *o++ = 0; stbiw__wpcrc(&o,13); stbiw__wp32(o, zlen); stbiw__wptag(o, "IDAT"); STBIW_MEMMOVE(o, zlib, zlen); o += zlen; STBIW_FREE(zlib); stbiw__wpcrc(&o, zlen); stbiw__wp32(o,0); stbiw__wptag(o, "IEND"); stbiw__wpcrc(&o,0); STBIW_ASSERT(o == out + *out_len); return out; } int stbi_png_level = 8; #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) { FILE *f; int len; unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; f = fopen(filename, "wb"); if (!f) { STBIW_FREE(png); return 0; } fwrite(png, 1, len, f); fclose(f); STBIW_FREE(png); return 1; } #endif STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) { int len; unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; func(context, png, len); STBIW_FREE(png); return 1; } /* *************************************************************************** * * JPEG writer * * This is based on Jon Olick's jo_jpeg.cpp: * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html */ static const unsigned char stbiw__jpg_ZigZag[] = { 0, 1, 5, 6,14,15,27,28, 2, 4, 7,13,16,26,29,42,3 ,8 ,12,17,25,30,41,43, 9,11,18,24,31,40,44,53, 10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { int bitBuf = *bitBufP, bitCnt = *bitCntP; bitCnt += bs[1]; bitBuf |= bs[0] << (24 - bitCnt); while(bitCnt >= 8) { unsigned char c = (bitBuf >> 16) & 255; stbiw__putc(s, c); if(c == 255) { stbiw__putc(s, 0); } bitBuf <<= 8; bitCnt -= 8; } *bitBufP = bitBuf; *bitCntP = bitCnt; } static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; float z1, z2, z3, z4, z5, z11, z13; float tmp0 = d0 + d7; float tmp7 = d0 - d7; float tmp1 = d1 + d6; float tmp6 = d1 - d6; float tmp2 = d2 + d5; float tmp5 = d2 - d5; float tmp3 = d3 + d4; float tmp4 = d3 - d4; // Even part float tmp10 = tmp0 + tmp3; // phase 2 float tmp13 = tmp0 - tmp3; float tmp11 = tmp1 + tmp2; float tmp12 = tmp1 - tmp2; d0 = tmp10 + tmp11; // phase 3 d4 = tmp10 - tmp11; z1 = (tmp12 + tmp13) * 0.707106781f; // c4 d2 = tmp13 + z1; // phase 5 d6 = tmp13 - z1; // Odd part tmp10 = tmp4 + tmp5; // phase 2 tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; // The rotator is modified from fig 4-8 to avoid extra negations. z5 = (tmp10 - tmp12) * 0.382683433f; // c6 z2 = tmp10 * 0.541196100f + z5; // c2-c6 z4 = tmp12 * 1.306562965f + z5; // c2+c6 z3 = tmp11 * 0.707106781f; // c4 z11 = tmp7 + z3; // phase 5 z13 = tmp7 - z3; *d5p = z13 + z2; // phase 6 *d3p = z13 - z2; *d1p = z11 + z4; *d7p = z11 - z4; *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; } static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { int tmp1 = val < 0 ? -val : val; val = val < 0 ? val-1 : val; bits[1] = 1; while(tmp1 >>= 1) { ++bits[1]; } bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { } // end0pos = first element in reverse order !=0 if(end0pos == 0) { stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); return DU[0]; } for(i = 1; i <= end0pos; ++i) { int startpos = i; int nrzeroes; unsigned short bits[2]; for (; DU[i]==0 && i<=end0pos; ++i) { } nrzeroes = i-startpos; if ( nrzeroes >= 16 ) { int lng = nrzeroes>>4; int nrmarker; for (nrmarker=1; nrmarker <= lng; ++nrmarker) stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); nrzeroes &= 15; } stbiw__jpg_calcBits(DU[i], bits); stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); } if(end0pos != 63) { stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); } return DU[0]; } static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { // Constants that don't pollute global namespace static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; static const unsigned char std_ac_luminance_values[] = { 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa }; static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; static const unsigned char std_ac_chrominance_values[] = { 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa }; // Huffman tables static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; static const unsigned short YAC_HT[256][2] = { {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} }; static const unsigned short UVAC_HT[256][2] = { {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} }; static const int YQT[] = {16,11,10,16,24, 40, 51,61,12,12,14,19,26, 58, 60,55,14,13,16,24, 40, 57, 69, 56,14,17,22,29, 51, 87, 80,62, 18,22,37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; int row, col, i, k; float fdtbl_Y[64], fdtbl_UV[64]; unsigned char YTable[64], UVTable[64]; if(!data || !width || !height || comp > 4 || comp < 1) { return 0; } quality = quality ? quality : 90; quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; quality = quality < 50 ? 5000 / quality : 200 - quality * 2; for(i = 0; i < 64; ++i) { int uvti, yti = (YQT[i]*quality+50)/100; YTable[stbiw__jpg_ZigZag[i]] = yti < 1 ? 1 : yti > 255 ? 255 : yti; uvti = (UVQT[i]*quality+50)/100; UVTable[stbiw__jpg_ZigZag[i]] = uvti < 1 ? 1 : uvti > 255 ? 255 : uvti; } for(row = 0, k = 0; row < 8; ++row) { for(col = 0; col < 8; ++col, ++k) { fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); } } // Write Headers { static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,height>>8,height&0xFF,width>>8,width&0xFF,3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; s->func(s->context, (void*)head0, sizeof(head0)); s->func(s->context, (void*)YTable, sizeof(YTable)); stbiw__putc(s, 1); s->func(s->context, UVTable, sizeof(UVTable)); s->func(s->context, (void*)head1, sizeof(head1)); s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); stbiw__putc(s, 0x10); // HTYACinfo s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); stbiw__putc(s, 1); // HTUDCinfo s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); stbiw__putc(s, 0x11); // HTUACinfo s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); s->func(s->context, (void*)head2, sizeof(head2)); } // Encode 8x8 macroblocks { static const unsigned short fillBits[] = {0x7F, 7}; const unsigned char *imageData = (const unsigned char *)data; int DCY=0, DCU=0, DCV=0; int bitBuf=0, bitCnt=0; // comp == 2 is grey+alpha (alpha is ignored) int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; int x, y, pos; for(y = 0; y < height; y += 8) { for(x = 0; x < width; x += 8) { float YDU[64], UDU[64], VDU[64]; for(row = y, pos = 0; row < y+8; ++row) { for(col = x; col < x+8; ++col, ++pos) { int p = row*width*comp + col*comp; float r, g, b; if(row >= height) { p -= width*comp*(row+1 - height); } if(col >= width) { p -= comp*(col+1 - width); } r = imageData[p+0]; g = imageData[p+ofsG]; b = imageData[p+ofsB]; YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; } } DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); } } // Do the bit alignment of the EOI marker stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); } // EOI stbiw__putc(s, 0xFF); stbiw__putc(s, 0xD9); return 1; } STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) { stbi__write_context s; stbi__start_write_callbacks(&s, func, context); return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) { stbi__write_context s; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); stbi__end_write_file(&s); return r; } else return 0; } #endif #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history 1.04 (2017-03-03) monochrome BMP expansion 1.03 ??? 1.02 (2016-04-02) avoid allocating large structures on the stack 1.01 (2016-01-16) STBIW_REALLOC_SIZED: support allocators with no realloc support avoid race-condition in crc initialization minor compile issues 1.00 (2015-09-14) installable file IO function 0.99 (2015-09-13) warning fixes; TGA rle support 0.98 (2015-04-08) added STBIW_MALLOC, STBIW_ASSERT etc 0.97 (2015-01-18) fixed HDR asserts, rewrote HDR rle logic 0.96 (2015-01-17) add HDR output fix monochrome BMP 0.95 (2014-08-17) add monochrome TGA output 0.94 (2014-05-31) rename private functions to avoid conflicts with stb_image.h 0.93 (2014-05-27) warning fixes 0.92 (2010-08-01) casts to unsigned char to fix warnings 0.91 (2010-07-17) first public release 0.90 first internal release */ /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ yquake2-QUAKE2_7_10/src/backends/generic/misc.c000066400000000000000000000115041321245476300211770ustar00rootroot00000000000000/* * 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 functions. * * ======================================================================= */ #include #include #include "../../common/header/shared.h" #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 #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__) // 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. OpenBSD once // had /proc but removed it for security reasons. 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 // Several platforms (for example OpenBSD) donn't provide a // reliable way to determine the executable path. Just return // an empty string. exePath[0] = '\0'; // feel free to add implementation for your platform and send a pull request. #warning "SetExecutablePath() is unimplemented on this platform" #endif } const char *Sys_GetBinaryDir(void) { static char exeDir[PATH_MAX] = {0}; if(exeDir[0] != '\0') { return exeDir; } SetExecutablePath(exeDir); if (exeDir[0] == '\0') { Com_Printf("Couldn't determine executable path. Using ./ instead.\n"); Q_strlcpy(exeDir, "./", sizeof(exeDir)); } else { // 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; } #if defined (__GNUC__) && (__i386 || __x86_64__) void Sys_SetupFPU(void) { // Get current x87 control word volatile unsigned short old_cw = 0; asm ("fstcw %0" : : "m" (*&old_cw)); unsigned short new_cw = old_cw; // The precision is set through bit 8 and 9. For // double precision bit 8 must unset and bit 9 set. new_cw &= ~(1 << 8); new_cw |= (1 << 9); // Setting the control word is expensive since it // resets the FPU state. Do it only if necessary. if (new_cw != old_cw) { asm ("fldcw %0" : : "m" (*&new_cw)); } } #else void Sys_SetupFPU(void) { } #endif yquake2-QUAKE2_7_10/src/backends/generic/qal.c000066400000000000000000000374231321245476300210310ustar00rootroot00000000000000/* * 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; #ifdef DLOPEN_OPENAL static cvar_t *al_driver; #endif 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; if (handle) { /* Unload the shared lib */ Sys_FreeLibrary(handle); handle = NULL; } } /* * Loads the OpenAL shared lib, creates * a context and device handle. */ qboolean QAL_Init() { al_device = Cvar_Get("al_device", "", CVAR_ARCHIVE); #ifdef DLOPEN_OPENAL /* DEFAULT_OPENAL_DRIVER is defined at compile time via the compiler */ al_driver = Cvar_Get("al_driver", DEFAULT_OPENAL_DRIVER, 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; } # define ALSYMBOL(handle, sym) Sys_GetProcAddress(handle, #sym) #else handle = NULL; # define ALSYMBOL(handle, sym) sym #endif /* Connect function pointers to management functions */ qalcCreateContext = ALSYMBOL(handle, alcCreateContext); qalcMakeContextCurrent = ALSYMBOL(handle, alcMakeContextCurrent); qalcProcessContext = ALSYMBOL(handle, alcProcessContext); qalcSuspendContext = ALSYMBOL(handle, alcSuspendContext); qalcDestroyContext = ALSYMBOL(handle, alcDestroyContext); qalcGetCurrentContext = ALSYMBOL(handle, alcGetCurrentContext); qalcGetContextsDevice = ALSYMBOL(handle, alcGetContextsDevice); qalcOpenDevice = ALSYMBOL(handle, alcOpenDevice); qalcCloseDevice = ALSYMBOL(handle, alcCloseDevice); qalcGetError = ALSYMBOL(handle, alcGetError); qalcIsExtensionPresent = ALSYMBOL(handle, alcIsExtensionPresent); qalcGetProcAddress = ALSYMBOL(handle, alcGetProcAddress); qalcGetEnumValue = ALSYMBOL(handle, alcGetEnumValue); qalcGetString = ALSYMBOL(handle, alcGetString); qalcGetIntegerv = ALSYMBOL(handle, alcGetIntegerv); qalcCaptureOpenDevice = ALSYMBOL(handle, alcCaptureOpenDevice); qalcCaptureCloseDevice = ALSYMBOL(handle, alcCaptureCloseDevice); qalcCaptureStart = ALSYMBOL(handle, alcCaptureStart); qalcCaptureStop = ALSYMBOL(handle, alcCaptureStop); qalcCaptureSamples = ALSYMBOL(handle, alcCaptureSamples); /* Connect function pointers to to OpenAL API functions */ qalEnable = ALSYMBOL(handle, alEnable); qalDisable = ALSYMBOL(handle, alDisable); qalIsEnabled = ALSYMBOL(handle, alIsEnabled); qalGetString = ALSYMBOL(handle, alGetString); qalGetBooleanv = ALSYMBOL(handle, alGetBooleanv); qalGetIntegerv = ALSYMBOL(handle, alGetIntegerv); qalGetFloatv = ALSYMBOL(handle, alGetFloatv); qalGetDoublev = ALSYMBOL(handle, alGetDoublev); qalGetBoolean = ALSYMBOL(handle, alGetBoolean); qalGetInteger = ALSYMBOL(handle, alGetInteger); qalGetFloat = ALSYMBOL(handle, alGetFloat); qalGetDouble = ALSYMBOL(handle, alGetDouble); qalGetError = ALSYMBOL(handle, alGetError); qalIsExtensionPresent = ALSYMBOL(handle, alIsExtensionPresent); qalGetProcAddress = ALSYMBOL(handle, alGetProcAddress); qalGetEnumValue = ALSYMBOL(handle, alGetEnumValue); qalListenerf = ALSYMBOL(handle, alListenerf); qalListener3f = ALSYMBOL(handle, alListener3f); qalListenerfv = ALSYMBOL(handle, alListenerfv); qalListeneri = ALSYMBOL(handle, alListeneri); qalListener3i = ALSYMBOL(handle, alListener3i); qalListeneriv = ALSYMBOL(handle, alListeneriv); qalGetListenerf = ALSYMBOL(handle, alGetListenerf); qalGetListener3f = ALSYMBOL(handle, alGetListener3f); qalGetListenerfv = ALSYMBOL(handle, alGetListenerfv); qalGetListeneri = ALSYMBOL(handle, alGetListeneri); qalGetListener3i = ALSYMBOL(handle, alGetListener3i); qalGetListeneriv = ALSYMBOL(handle, alGetListeneriv); qalGenSources = ALSYMBOL(handle, alGenSources); qalDeleteSources = ALSYMBOL(handle, alDeleteSources); qalIsSource = ALSYMBOL(handle, alIsSource); qalSourcef = ALSYMBOL(handle, alSourcef); qalSource3f = ALSYMBOL(handle, alSource3f); qalSourcefv = ALSYMBOL(handle, alSourcefv); qalSourcei = ALSYMBOL(handle, alSourcei); qalSource3i = ALSYMBOL(handle, alSource3i); qalSourceiv = ALSYMBOL(handle, alSourceiv); qalGetSourcef = ALSYMBOL(handle, alGetSourcef); qalGetSource3f = ALSYMBOL(handle, alGetSource3f); qalGetSourcefv = ALSYMBOL(handle, alGetSourcefv); qalGetSourcei = ALSYMBOL(handle, alGetSourcei); qalGetSource3i = ALSYMBOL(handle, alGetSource3i); qalGetSourceiv = ALSYMBOL(handle, alGetSourceiv); qalSourcePlayv = ALSYMBOL(handle, alSourcePlayv); qalSourceStopv = ALSYMBOL(handle, alSourceStopv); qalSourceRewindv = ALSYMBOL(handle, alSourceRewindv); qalSourcePausev = ALSYMBOL(handle, alSourcePausev); qalSourcePlay = ALSYMBOL(handle, alSourcePlay); qalSourceStop = ALSYMBOL(handle, alSourceStop); qalSourceRewind = ALSYMBOL(handle, alSourceRewind); qalSourcePause = ALSYMBOL(handle, alSourcePause); qalSourceQueueBuffers = ALSYMBOL(handle, alSourceQueueBuffers); qalSourceUnqueueBuffers = ALSYMBOL(handle, alSourceUnqueueBuffers); qalGenBuffers = ALSYMBOL(handle, alGenBuffers); qalDeleteBuffers = ALSYMBOL(handle, alDeleteBuffers); qalIsBuffer = ALSYMBOL(handle, alIsBuffer); qalBufferData = ALSYMBOL(handle, alBufferData); qalBufferf = ALSYMBOL(handle, alBufferf); qalBuffer3f = ALSYMBOL(handle, alBuffer3f); qalBufferfv = ALSYMBOL(handle, alBufferfv); qalBufferi = ALSYMBOL(handle, alBufferi); qalBuffer3i = ALSYMBOL(handle, alBuffer3i); qalBufferiv = ALSYMBOL(handle, alBufferiv); qalGetBufferf = ALSYMBOL(handle, alGetBufferf); qalGetBuffer3f = ALSYMBOL(handle, alGetBuffer3f); qalGetBufferfv = ALSYMBOL(handle, alGetBufferfv); qalGetBufferi = ALSYMBOL(handle, alGetBufferi); qalGetBuffer3i = ALSYMBOL(handle, alGetBuffer3i); qalGetBufferiv = ALSYMBOL(handle, alGetBufferiv); qalDopplerFactor = ALSYMBOL(handle, alDopplerFactor); qalDopplerVelocity = ALSYMBOL(handle, alDopplerVelocity); qalSpeedOfSound = ALSYMBOL(handle, alSpeedOfSound); qalDistanceModel = ALSYMBOL(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_7_10/src/backends/generic/vid.c000066400000000000000000000335261321245476300210360ustar00rootroot00000000000000/* * 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" #ifdef ZIP // if we build with zip support, zlib is available and we can use that for better PNG compression #include static unsigned char* compress_for_stbiw(unsigned char *data, int data_len, int *out_len, int quality) { uLongf bufSize = compressBound(data_len); unsigned char* buf = malloc(bufSize); if(buf == NULL) return NULL; if(compress2(buf, &bufSize, data, data_len, quality) != Z_OK) { free(buf); return NULL; } *out_len = bufSize; return buf; } #define STBIW_ZLIB_COMPRESS compress_for_stbiw #endif // ZIP #define STB_IMAGE_WRITE_IMPLEMENTATION #include "header/stb_image_write.h" qboolean VID_LoadRefresh(void); 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: 1280x720", 1280, 720, 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}, {"Mode 24: 2560x1080", 2560, 1080, 24}, {"Mode 25: 2560x1440", 2560, 1440, 25}, {"Mode 26: 2560x1600", 2560, 1600, 26}, {"Mode 27: 3440x1440", 3440, 1440, 27}, {"Mode 28: 3840x1600", 3840, 1600, 28}, {"Mode 29: 3840x2160", 3840, 2160, 29}, {"Mode 30: 4096x2160", 4096, 2160, 30}, {"Mode 31: 5120x2880", 5120, 2880, 31}, }; /* Console variables that we need to access from this module */ cvar_t *vid_gamma; cvar_t *vid_fullscreen; cvar_t *vid_renderer; /* Global variables used internally by this module */ viddef_t viddef; /* global video state; used by other modules */ #define VID_NUM_MODES (sizeof(vid_modes) / sizeof(vid_modes[0])) #define MAXPRINTMSG 4096 /* * 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; } void VID_ListModes_f(void) { int i; Com_Printf("Supported video modes (gl_mode):\n"); for(i=0; i= 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; } /* * 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 if(!VID_LoadRefresh()) { if (strcmp(vid_renderer->string, "gl1") != 0) { Com_Printf("\n ... trying again with standard OpenGL1.x renderer ... \n\n"); Cvar_Set("vid_renderer", "gl1"); VID_LoadRefresh(); } else { Com_Error(ERR_FATAL, "Couldn't load a rendering backend!\n"); } } cls.disable_screen = false; } } void VID_Init(void) { /* Create the video variables so we know how to start the graphics drivers */ vid_fullscreen = Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); vid_gamma = Cvar_Get("vid_gamma", "1.2", CVAR_ARCHIVE); vid_renderer = Cvar_Get("vid_renderer", "gl1", CVAR_ARCHIVE); /* Add some console commands that we want to handle */ Cmd_AddCommand("vid_restart", VID_Restart_f); Cmd_AddCommand("vid_listmodes", VID_ListModes_f); /* Start the graphics mode and load refresh DLL */ VID_CheckChanges(); } // called with image data of width*height pixel which comp bytes per pixel (must be 3 or 4 for RGB or RGBA) // expects the pixels data to be row-wise, starting at top left void VID_WriteScreenshot( int width, int height, int comp, const void* data ) { char picname[80]; char checkname[MAX_OSPATH]; int i, success=0; static const char* supportedFormats[] = { "tga", "bmp", "png", "jpg" }; static const int numFormats = sizeof(supportedFormats)/sizeof(supportedFormats[0]); int format = 0; // 0=tga 1=bmp 2=png 3=jpg int quality = 85; int argc = Cmd_Argc(); const char* gameDir = FS_Gamedir(); /* FS_InitFilesystem() made sure the screenshots dir exists */ if(argc > 1) { const char* maybeFormat = Cmd_Argv(1); for(i=0; i 2) { const char* q = Cmd_Argv(2); int qualityStrLen = strlen(q); for(i=0; i '9') { Com_Printf("the (optional!) third argument to 'screenshot' is jpg quality, a number between 1 and 100\n"); Com_Printf(" or png compression level, between 0 and 10!\n"); return; } } quality = atoi(q); if(format == 2) // png { if(quality < 0) quality = 0; else if(quality > 10) quality = 10; } else if(format == 3) // jpg { if(quality < 1) quality = 1; else if(quality > 100) quality = 100; } } } /* find a file name to save it to */ for (i = 0; i <= 9999; i++) { FILE *f; Com_sprintf(checkname, sizeof(checkname), "%s/scrnshot/q2_%04d.%s", gameDir, i, supportedFormats[format]); f = fopen(checkname, "rb"); if (!f) { Com_sprintf(picname, sizeof(picname), "q2_%04d.%s", i, supportedFormats[format]); break; /* file doesn't exist */ } fclose(f); } if (i == 10000) { Com_Printf("SCR_ScreenShot_f: Couldn't create a file\n"); return; } switch(format) // 0=tga 1=bmp 2=png 3=jpg { case 0: success = stbi_write_tga(checkname, width, height, comp, data); break; case 1: success = stbi_write_bmp(checkname, width, height, comp, data); break; case 2: stbi_png_level = (quality <= 10) ? quality : 7; success = stbi_write_png(checkname, width, height, comp, data, 0); break; case 3: success = stbi_write_jpg(checkname, width, height, comp, data, quality); break; } if(success) { Com_Printf("Wrote %s\n", picname); } else { Com_Printf("SCR_ScreenShot_f: Couldn't write %s\n", picname); } } // Structure containing functions exported from refresh DLL refexport_t re; void *reflib_handle = NULL; // Handle to refresh DLL qboolean ref_active = false; /* Is the refresher being used? */ void Key_MarkAllUp(void); extern int GLimp_Init(void); extern qboolean GLimp_InitGraphics(int fullscreen, int *pwidth, int *pheight); extern void VID_ShutdownWindow(void); qboolean VID_LoadRefresh(void) { refimport_t ri; GetRefAPI_t GetRefAPI; #ifdef __APPLE__ const char* lib_ext = "dylib"; #elif defined(_WIN32) const char* lib_ext = "dll"; #else const char* lib_ext = "so"; #endif char reflib_name[64] = {0}; char reflib_path[MAX_OSPATH] = {0}; // If the refresher is already active // we'll shut it down VID_Shutdown(); // Log it! Com_Printf("----- refresher initialization -----\n"); snprintf(reflib_name, sizeof(reflib_name), "ref_%s.%s", vid_renderer->string, lib_ext); snprintf(reflib_path, sizeof(reflib_path), "%s%s", Sys_GetBinaryDir(), reflib_name); Com_Printf("LoadLibrary(%s)\n", reflib_name); GetRefAPI = Sys_LoadLibrary(reflib_path, "GetRefAPI", &reflib_handle); if(GetRefAPI == NULL) { Com_Error( ERR_FATAL, "Loading %s as renderer lib failed!", reflib_name ); Cvar_Set("vid_renderer", "gl1"); return false; } ri.Cmd_AddCommand = Cmd_AddCommand; ri.Cmd_RemoveCommand = Cmd_RemoveCommand; ri.Cmd_Argc = Cmd_Argc; ri.Cmd_Argv = Cmd_Argv; ri.Cmd_ExecuteText = Cbuf_ExecuteText; ri.Com_VPrintf = Com_VPrintf; ri.Sys_Error = Com_Error; ri.FS_LoadFile = FS_LoadFile; ri.FS_FreeFile = FS_FreeFile; ri.FS_Gamedir = FS_Gamedir; ri.Cvar_Get = Cvar_Get; ri.Cvar_Set = Cvar_Set; ri.Cvar_SetValue = Cvar_SetValue; ri.Vid_GetModeInfo = VID_GetModeInfo; ri.Vid_MenuInit = VID_MenuInit; ri.Vid_NewWindow = VID_NewWindow; ri.Vid_WriteScreenshot = VID_WriteScreenshot; ri.Vid_ShutdownWindow = VID_ShutdownWindow; ri.GLimp_Init = GLimp_Init; ri.GLimp_InitGraphics = GLimp_InitGraphics; re = GetRefAPI( ri ); // Declare the refresher as active ref_active = true; if (re.api_version != API_VERSION) { VID_Shutdown(); Com_Error (ERR_FATAL, "%s has incompatible api_version %d", reflib_name, re.api_version); } // Initiate the refresher if (!re.Init()) { VID_Shutdown(); // Isn't that just too bad? :( Com_Printf("ERROR: Loading %s as rendering backend failed!\n", reflib_name); Com_Printf("------------------------------------\n\n"); return false; // TODO: try again with default renderer? } /* Ensure that all key states are cleared */ Key_MarkAllUp(); Com_Printf("Successfully loaded %s as rendering backend\n", reflib_name); Com_Printf("------------------------------------\n\n"); return true; } void VID_Shutdown(void) { if (ref_active) { /* Shut down the renderer */ re.Shutdown(); Sys_FreeLibrary(reflib_handle); reflib_handle = NULL; memset(&re, 0, sizeof(re)); } // Declare the refresher as inactive ref_active = false; } // ======== wrappers for functions from refresh lib ======== void R_BeginRegistration(char *map) { if(ref_active) { re.BeginRegistration(map); } } struct model_s* R_RegisterModel(char *name) { if(ref_active) { return re.RegisterModel(name); } return NULL; } struct image_s* R_RegisterSkin(char *name) { if(ref_active) { return re.RegisterSkin(name); } return NULL; } void R_SetSky(char *name, float rotate, vec3_t axis) { if(ref_active) { re.SetSky(name, rotate, axis); } } void R_EndRegistration(void) { if(ref_active) { re.EndRegistration(); } } void R_RenderFrame(refdef_t *fd) { if(ref_active) { re.RenderFrame(fd); } } struct image_s* Draw_FindPic(char *name) { if(ref_active) { return re.DrawFindPic(name); } return NULL; } void Draw_GetPicSize(int *w, int *h, char *name) { if(ref_active) { re.DrawGetPicSize(w, h, name); } } void Draw_StretchPic(int x, int y, int w, int h, char *name) { if(ref_active) { re.DrawStretchPic(x, y, w, h, name); } } void Draw_PicScaled(int x, int y, char *pic, float factor) { if(ref_active) { re.DrawPicScaled(x, y, pic, factor); } } void Draw_CharScaled(int x, int y, int num, float scale) { if(ref_active) { re.DrawCharScaled(x, y, num, scale); } } void Draw_TileClear(int x, int y, int w, int h, char *name) { if(ref_active) { re.DrawTileClear(x, y, w, h, name); } } void Draw_Fill(int x, int y, int w, int h, int c) { if(ref_active) { re.DrawFill(x, y, w, h, c); } } void Draw_FadeScreen(void) { if(ref_active) { re.DrawFadeScreen(); } } void Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, byte *data) { if(ref_active) { re.DrawStretchRaw(x, y, w, h, cols, rows, data); } } void R_SetPalette(const unsigned char *palette) { if(ref_active) { re.SetPalette(palette); } } void R_BeginFrame(float camera_separation) { if(ref_active) { re.BeginFrame(camera_separation); } } void R_EndFrame(void) { if(ref_active) { re.EndFrame(); } } qboolean R_IsVSyncActive(void) { if(ref_active) { return re.IsVSyncActive(); } return false; } yquake2-QUAKE2_7_10/src/backends/sdl/000077500000000000000000000000001321245476300172455ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/backends/sdl/cd.c000066400000000000000000000177331321245476300200120ustar00rootroot00000000000000/* * Copyright (C) 2001 Robert Buml * 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_7_10/src/backends/sdl/icon/000077500000000000000000000000001321245476300201755ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/backends/sdl/icon/q2icon.xbm000066400000000000000000000015731321245476300221060ustar00rootroot00000000000000#define q2icon_width 32 #define q2icon_height 32 static unsigned char q2icon_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x01, 0x80, 0x00, 0x80, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x03, 0x60, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x0c, 0x70, 0x00, 0x00, 0x0e, 0x70, 0x00, 0x00, 0x0e, 0xe0, 0xf0, 0x0f, 0x07, 0xe0, 0xe0, 0x07, 0x07, 0xc0, 0x61, 0x86, 0x03, 0xc0, 0x67, 0xe6, 0x03, 0x80, 0x7f, 0xfe, 0x01, 0x00, 0xff, 0xff, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; yquake2-QUAKE2_7_10/src/backends/sdl/icon/q2icon64.h000066400000000000000000001062621321245476300217220ustar00rootroot00000000000000static 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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\40\0\0\0u\0\0\0^\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\226\0\0\0\244\0\0\0" "\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\10\0\0\0\216\0\0\0\374\0\0\0\262\0\0\0\35\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\15\0\0\0\177\0\0\0\355\0\0\0s\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\311\0\0\0\336\0\0\0I\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0/\0\0\0\333\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0k\0\0\0\366\0\0\0\236\0\0\0\12\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\20\0\0\0\262\0\0\0\354" "\0\0\0D\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0u\0\0\0\377\0\0\0\202\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\2" "\0\0\0\222\0\0\0\371\0\0\0G\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0y\0\0\0\371\0\0\0\206\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\245\0\0\0\365\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\374\0\0\0\267" "\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\13\0\0\0\325\0\0\0\341\0\0\0\23\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\25\0\0\0\345" "\0\0\0\360\0\0\0\24\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0+\0\0\0\371" "\0\0\0\266\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" "\256\0\0\0\377\0\0\0x\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\204\0\0\0\377\0\0\0o\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0I\0\0\0\377\0\0\0\347\0\0\0\11\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\12\0\0\0\332\0\0\0\360\0\0\0\26\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\322\0\0\0\377\0\0\0\177\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0O\0\0\0\377\0\0\0\211" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\375\0\0\0\34\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\22\0\0" "\0\376\0\0\0\362\0\0\0\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\266\0\0\0\377\0\0\0\277\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\322\0\0\0\377\0\0\0q\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\14\0\0\0\370\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" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\221\0\0\0\377\0\0\0\323\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0S\0" "\0\0\377\0\0\0\377\0\0\0G\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0f\0\0\0\377\0\0\0\377\0\0\0" "\16\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\237\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\0I\0\0\0\377\0" "\0\0\377\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\333\0\0\0\377\0\0\0\373\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\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\13\0\0\0\376\0\0\0\377\0\0\0\374\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\17\0\0\0\307\0\0\0\377\0\0\0\277\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\23\0\0\0\377\0\0\0\377" "\0\0\0\377\0\0\0\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\35\0\0\0\372\0\0\0\377\0\0\0\331" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\21\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\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\370\0\0\0\7\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0R\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\30\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""8\0\0\0\377\0\0\0\377\0\0\0\313\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\213\0\0\0\377\0\0\0\377\0\0\0\355\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\36\0\0\0\377\0\0" "\0\377\0\0\0\377\0\0\0\222\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\301\0\0\0\377\0\0\0\377" "\0\0\0\377\0\0\0\35\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\375\0\0\0\377\0\0\0\377\0\0\0\306\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\16\0\0\0" "\366\0\0\0\377\0\0\0\377\0\0\0\230\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\342\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\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0e\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0L\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\274\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\262\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\377\0\0\0\377\0\0" "\0v\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\206\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\376\0\0\0.\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\0\0\0\376\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""6\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" "\0\0\0\256\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\301\0\0\0\303\0\0\0\257\0\0\0\232\0\0\0\231\0\0\0\243\0" "\0\0\302\0\0\0\330\0\0\0\325\0\0\0\301\0\0\0\264\0\0\0\252\0\0\0\275\0\0" "\0\322\0\0\0\335\0\0\0\217\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\11\0\0\0\235\0\0\0\377\0\0\0\377\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\1\0\0\0\300\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0" "\0\0G\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0.\0\0\0w\0\0\0\340\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\363\0" "\0\0\364\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\304\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\207" "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0f\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\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\255\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\342\0\0\0\32\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\202\0\0\0" "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0y\0\0\0\205\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\0h\0\0\0\376\0\0\0\377\0\0\0\377\0\0\0\377\0" "\0\0\353\0\0\0\14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\271\0\0\0\377\0\0\0\377\0" "\0\0\377\0\0\0\377\0\0\0\321\0\0\0\21\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0S\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" "\0\0\0""5\0\0\0L\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\364\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0N\0\0\0\374" "\0\0\0\377\0\0\0\377\0\0\0\377\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\0\0" "\0\0\0\0\0\0;\0\0\0\375\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\323" "\0\0\0\40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""7" "\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\27\0\0\0""3\0\0\0\377\0\0" "\0\377\0\0\0\377\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\0E\0\0\0\364\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" "\0\0\0\340\0\0\0\13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\203\0\0" "\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\356\0\0\0J\0\0\0\0\0" "\0\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\21\0\0\0,\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\330\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0b\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\0K\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\272\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\0n\0\0\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\16\0\0\0+\0\0\0" "\377\0\0\0\377\0\0\0\377\0\0\0\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\16\0\0\0\237\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\226\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\20\0\0\0\326\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""7\0\0\0\22\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\376\0\0\0\4\0\0\0+\0\0\0\377\0\0\0\377\0\0\0\377" "\0\0\0\333\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0U\0\0\0\342\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\276\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\32" "\0\0\0\315\0\0\0\353\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\372\0" "\0\0\375\0\0\0\273\0\0\0G\0\0\0\0\0\0\0\23\0\0\0\355\0\0\0\377\0\0\0\377" "\0\0\0\360\0\0\0\0\0\0\0*\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\347\0\0\0\0" "\0\0\0\6\0\0\0W\0\0\0\310\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" "\0\0\0\366\0\0\0\366\0\0\0\331\0\0\0\22\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\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\0D\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""2\0\0\0h\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\347\0\0\0""3\0\0" "\0Q\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\366\0\0\0\265\0\0\0\363\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\305\0" "\0\0\212\0\0\0%\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\276\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\363\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\377\0\0\0\377\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\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\21\0\0\0\231\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\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\231\0\0\0\16\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0:\0\0\0\313\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\377\0\0\0\377\0\0\0\377\0\0\0\377" "\0\0\0\320\0\0\0A\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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[\0\0\0\177\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\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\316\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\2\0\0\0\230\0\0\0\351\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\346\0\0\0\224\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\1\0\0\0j\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377" "\0\0\0\321\0\0\0\334\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\364\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0P\0\0\0\377\0\0\0" "\377\0\0\0\377\0\0\0\377\0\0\0\25\0\0\0S\0\0\0\377\0\0\0\377\0\0\0\377\0" "\0\0\375\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\0\0\0\0\0" "\0\0\0F\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\20\0\0\0Q\0\0\0\377" "\0\0\0\377\0\0\0\377\0\0\0\377\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\0\0\0\0\0\0\0\0""1\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\372\0" "\0\0\1\0\0\0,\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\376\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\12\0\0\0\353\0\0\0\377" "\0\0\0\377\0\0\0\347\0\0\0\0\0\0\0\4\0\0\0\373\0\0\0\377\0\0\0\377\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\371\0\0\0\377\0\0\0\377\0\0\0\356\0\0\0\0\0\0\0B\0\0\0\377\0\0" "\0\377\0\0\0\377\0\0\0\361\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\365\0\0\0\0" "\0\0\0K\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\346\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\15\0\0\0\376\0\0\0\377\0\0\0" "\377\0\0\0\353\0\0\0\0\0\0\0,\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\320\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\340\0\0\0\377\0\0\0\377\0\0\0\321\0\0\0\0\0\0\0\16\0\0\0\377\0\0\0\377" "\0\0\0\377\0\0\0\256\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\270\0\0\0\377\0\0\0\377\0\0\0\237\0\0\0\0\0\0" "\0\0\0\0\0\345\0\0\0\377\0\0\0\377\0\0\0\211\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\231\0\0\0\377\0\0\0\377" "\0\0\0g\0\0\0\0\0\0\0\0\0\0\0\271\0\0\0\377\0\0\0\377\0\0\0b\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\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@\0\0\0\0\0\0\0\0\0\0\0\217\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\377\0\0\0\377\0\0\0\34\0\0\0\0\0\0\0\0\0\0\0q\0\0\0\377\0\0" "\0\377\0\0\0\24\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0A\0\0\0\377\0\0\0\367\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0T" "\0\0\0\377\0\0\0\347\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\325\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0""7\0\0\0\377\0\0\0\273\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\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\373\0\0\0\264\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\31\0\0\0\377\0\0\0\227\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\331\0\0\0\227\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2\0\0\0\367\0\0\0}\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\252\0\0\0\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\316\0\0" "\0c\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\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\241\0\0\0?\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0A\0\0\0\37\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0I\0\0\0\10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\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_7_10/src/backends/sdl/input.c000066400000000000000000001074031321245476300205550ustar00rootroot00000000000000/* * Copyright (C) 2010 Yamagi Burmeister * Copyright (C) 1997-2005 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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. * * ======================================================================= * * Joystick threshold code is partially based on http://ioquake3.org code. */ #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; #if SDL_VERSION_ATLEAST(2, 0, 0) static float joystick_yaw, joystick_pitch; static float joystick_forwardmove, joystick_sidemove; static float joystick_up; static int back_button_id = -1; static char last_hat = SDL_HAT_CENTERED; static qboolean left_trigger = false; static qboolean right_trigger = false; qboolean show_haptic = false; /* Haptic feedback types */ enum QHARPICTYPES { HAPTIC_EFFECT_UNKNOWN = -1, HAPTIC_EFFECT_BLASTER = 0, HAPTIC_EFFECT_MENY, HAPTIC_EFFECT_HYPER_BLASTER, HAPTIC_EFFECT_MACHINEGUN, HAPTIC_EFFECT_SHOTGUN, HAPTIC_EFFECT_SSHOTGUN, HAPTIC_EFFECT_RAILGUN, HAPTIC_EFFECT_ROCKETGUN, HAPTIC_EFFECT_GRENADE, HAPTIC_EFFECT_BFG, HAPTIC_EFFECT_PALANX, HAPTIC_EFFECT_IONRIPPER, HAPTIC_EFFECT_ETFRIFLE, HAPTIC_EFFECT_SHOTGUN2, HAPTIC_EFFECT_TRACKER, HAPTIC_EFFECT_PAIN, HAPTIC_EFFECT_STEP, HAPTIC_EFFECT_TRAPCOCK, HAPTIC_EFFECT_LAST }; struct hapric_effects_cache { int effect_type; int effect_id; }; static int last_haptic_volume = 0; static struct hapric_effects_cache last_haptic_efffect[HAPTIC_EFFECT_LAST]; static int last_haptic_efffect_size = HAPTIC_EFFECT_LAST; static int last_haptic_efffect_pos = 0; /* Joystick */ static SDL_Haptic *joystick_haptic = NULL; static SDL_Joystick *joystick = NULL; static SDL_GameController *controller = NULL; #endif /* CVars */ cvar_t *vid_fullscreen; static cvar_t *in_grab; 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_up; cvar_t *m_yaw; cvar_t *sensitivity; static cvar_t *windowed_mouse; #if SDL_VERSION_ATLEAST(2, 0, 0) /* Joystick sensitivity */ static cvar_t *joy_yawsensitivity; static cvar_t *joy_pitchsensitivity; static cvar_t *joy_forwardsensitivity; static cvar_t *joy_sidesensitivity; static cvar_t *joy_upsensitivity; /* Joystick direction settings */ static cvar_t *joy_axis_leftx; static cvar_t *joy_axis_lefty; static cvar_t *joy_axis_rightx; static cvar_t *joy_axis_righty; static cvar_t *joy_axis_triggerleft; static cvar_t *joy_axis_triggerright; /* Joystick threshold settings */ static cvar_t *joy_axis_leftx_threshold; static cvar_t *joy_axis_lefty_threshold; static cvar_t *joy_axis_rightx_threshold; static cvar_t *joy_axis_righty_threshold; static cvar_t *joy_axis_triggerleft_threshold; static cvar_t *joy_axis_triggerright_threshold; /* Joystick haptic */ static cvar_t *joy_haptic_magnitude; #endif extern void GLimp_GrabInput(qboolean grab); /* ------------------------------------------------------------------ */ /* * 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; } /* ------------------------------------------------------------------ */ extern int glimp_refreshRate; /* * 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 /* fall-through */ 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] >= ' ') && (event.text.text[0] <= '~')) { 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 /* fall-through */ case SDL_KEYUP: { qboolean down = (event.type == SDL_KEYDOWN); #if SDL_VERSION_ATLEAST(2, 0, 0) /* workaround for AZERTY-keyboards, which don't have 1, 2, ..., 9, 0 in first row: * always map those physical keys (scancodes) to those keycodes anyway * see also https://bugzilla.libsdl.org/show_bug.cgi?id=3188 */ SDL_Scancode sc = event.key.keysym.scancode; if(sc >= SDL_SCANCODE_1 && sc <= SDL_SCANCODE_0) { /* Note that the SDL_SCANCODEs are SDL_SCANCODE_1, _2, ..., _9, SDL_SCANCODE_0 * while in ASCII it's '0', '1', ..., '9' => handle 0 and 1-9 separately * (quake2 uses the ASCII values for those keys) */ int key = '0'; /* implicitly handles SDL_SCANCODE_0 */ if(sc <= SDL_SCANCODE_9) { key = '1' + (sc - SDL_SCANCODE_1); } Key_Event(key, down, false); } else #endif /* SDL2; (SDL1.2 doesn't have scancodes so nothing we can do there) */ if((event.key.keysym.sym >= SDLK_SPACE) && (event.key.keysym.sym < SDLK_DELETE)) { Key_Event(event.key.keysym.sym, down, false); } else { Key_Event(IN_TranslateSDLtoQ2Key(event.key.keysym.sym), down, true); } } break; #if SDL_VERSION_ATLEAST(2, 0, 0) case SDL_WINDOWEVENT: if(event.window.event == SDL_WINDOWEVENT_FOCUS_LOST || event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { Key_MarkAllUp(); } else if(event.window.event == SDL_WINDOWEVENT_MOVED) { // make sure GLimp_GetRefreshRate() will query from SDL again - the window might // be on another display now! glimp_refreshRate = -1; } #else /* SDL1.2 */ case SDL_ACTIVEEVENT: if(event.active.gain == 0 && (event.active.state & SDL_APPINPUTFOCUS)) { Key_MarkAllUp(); } #endif break; #if SDL_VERSION_ATLEAST(2, 0, 0) case SDL_CONTROLLERBUTTONUP: case SDL_CONTROLLERBUTTONDOWN: /* Handle Controller Back button */ { qboolean down = (event.type == SDL_CONTROLLERBUTTONDOWN); if(event.cbutton.button == SDL_CONTROLLER_BUTTON_BACK) { Key_Event(K_JOY_BACK, down, true); } } break; case SDL_CONTROLLERAXISMOTION: /* Handle Controller Motion */ { char* direction_type; float threshold = 0; float fix_value = 0; int axis_value = event.caxis.value; switch (event.caxis.axis) { /* left/right */ case SDL_CONTROLLER_AXIS_LEFTX: direction_type = joy_axis_leftx->string; threshold = joy_axis_leftx_threshold->value; break; /* top/bottom */ case SDL_CONTROLLER_AXIS_LEFTY: direction_type = joy_axis_lefty->string; threshold = joy_axis_lefty_threshold->value; break; /* second left/right */ case SDL_CONTROLLER_AXIS_RIGHTX: direction_type = joy_axis_rightx->string; threshold = joy_axis_rightx_threshold->value; break; /* second top/bottom */ case SDL_CONTROLLER_AXIS_RIGHTY: direction_type = joy_axis_righty->string; threshold = joy_axis_righty_threshold->value; break; case SDL_CONTROLLER_AXIS_TRIGGERLEFT: direction_type = joy_axis_triggerleft->string; threshold = joy_axis_triggerleft_threshold->value; break; case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: direction_type = joy_axis_triggerright->string; threshold = joy_axis_triggerright_threshold->value; break; default: direction_type = "none"; } if (threshold > 0.9) threshold = 0.9; if (axis_value < 0 && (axis_value > (32768 * threshold))) axis_value = 0; else if (axis_value > 0 && (axis_value < (32768 * threshold))) axis_value = 0; // Smoothly ramp from dead zone to maximum value (from ioquake) // https://github.com/ioquake/ioq3/blob/master/code/sdl/sdl_input.c fix_value = ((float)abs(axis_value) / 32767.0f - threshold) / (1.0f - threshold); if (fix_value < 0.0f) fix_value = 0.0f; axis_value = (int)(32767 * ((axis_value < 0) ? -fix_value : fix_value)); if (cls.key_dest == key_game && (int)cl_paused->value == 0) { if (strcmp(direction_type, "sidemove") == 0) { joystick_sidemove = axis_value * joy_sidesensitivity->value; // We need to be twice faster because with joystic we run... joystick_sidemove *= cl_sidespeed->value * 2.0f; } else if (strcmp(direction_type, "forwardmove") == 0) { joystick_forwardmove = axis_value * joy_forwardsensitivity->value; // We need to be twice faster because with joystic we run... joystick_forwardmove *= cl_forwardspeed->value * 2.0f; } else if (strcmp(direction_type, "yaw") == 0) { joystick_yaw = axis_value * joy_yawsensitivity->value; joystick_yaw *= cl_yawspeed->value; } else if (strcmp(direction_type, "pitch") == 0) { joystick_pitch = axis_value * joy_pitchsensitivity->value; joystick_pitch *= cl_pitchspeed->value; } else if (strcmp(direction_type, "updown") == 0) { joystick_up = axis_value * joy_upsensitivity->value; joystick_up *= cl_upspeed->value; } } if (strcmp(direction_type, "triggerleft") == 0) { qboolean new_left_trigger = abs(axis_value) > (32767 / 4); if (new_left_trigger != left_trigger) { left_trigger = new_left_trigger; Key_Event(K_TRIG_LEFT, left_trigger, true); } } else if (strcmp(direction_type, "triggerright") == 0) { qboolean new_right_trigger = abs(axis_value) > (32767 / 4); if (new_right_trigger != right_trigger) { right_trigger = new_right_trigger; Key_Event(K_TRIG_RIGHT, right_trigger, true); } } } break; /* Joystick can have more buttons than on general game controller * so try to map not free buttons */ case SDL_JOYBUTTONUP: case SDL_JOYBUTTONDOWN: { qboolean down = (event.type == SDL_JOYBUTTONDOWN); /* Ignore back button, we dont need event for such button */ if (back_button_id == event.jbutton.button) return; if(event.jbutton.button <= (K_JOY32 - K_JOY1)) { Key_Event(event.jbutton.button + K_JOY1, down, true); } } break; case SDL_JOYHATMOTION: { if (last_hat != event.jhat.value) { char diff = last_hat ^ event.jhat.value; int i; for (i=0; i < 4; i++) { if (diff & (1 << i)) { /* check that we have button up for some bit */ if (last_hat & (1 << i)) Key_Event(i + K_HAT_UP, false, true); /* check that we have button down for some bit */ if (event.jhat.value & (1 << i)) Key_Event(i + K_HAT_UP, true, true); } } last_hat = event.jhat.value; } } break; #endif case SDL_QUIT: Com_Quit(); 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); } /* * Removes all pending events from SDLs queue. */ void In_FlushQueue(void) { #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); #else SDL_Event event; while(SDL_PollEvent(&event)); #endif Key_MarkAllUp(); } /* * 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; } #if SDL_VERSION_ATLEAST(2, 0, 0) // to make the the viewangles changes independent of framerate // we need to scale with frametime (assuming the configured values are for 60hz) // 1/32768 is to normalize the input values from SDL (they're between -32768 and 32768 and we want -1 to 1) // (for movement this is not needed, as those are absolute values independent of framerate) float joyViewFactor = (1.0f/32768.0f) * (cls.rframetime/0.01666f); if (joystick_yaw) { cl.viewangles[YAW] -= (m_yaw->value * joystick_yaw) * joyViewFactor; } if(joystick_pitch) { cl.viewangles[PITCH] += (m_pitch->value * joystick_pitch) * joyViewFactor; } if (joystick_forwardmove) { cmd->forwardmove -= (m_forward->value * joystick_forwardmove) / 32768; } if (joystick_sidemove) { cmd->sidemove += (m_side->value * joystick_sidemove) / 32768; } if (joystick_up) { cmd->upmove -= (m_up->value * joystick_up) / 32768; } #endif } /* ------------------------------------------------------------------ */ /* * Look down */ static void IN_MLookDown(void) { mlooking = true; } /* * Look up */ static void IN_MLookUp(void) { mlooking = false; IN_CenterView(); } #if SDL_VERSION_ATLEAST(2, 0, 0) /* * Shutdown haptic functionality */ static void IN_Haptic_Shutdown(void); /* ------------------------------------------------------------------ */ /* * Init haptic effects */ static int IN_Haptic_Effect_Init(int dir, int period, int magnitude, int length, int attack, int fade) { /* * Direction: * North - 0 * East - 9000 * South - 18000 * West - 27000 */ int effect_id; static SDL_HapticEffect haptic_effect; SDL_memset(&haptic_effect, 0, sizeof(SDL_HapticEffect)); // 0 is safe default haptic_effect.type = SDL_HAPTIC_SINE; haptic_effect.periodic.direction.type = SDL_HAPTIC_POLAR; // Polar coordinates haptic_effect.periodic.direction.dir[0] = dir; haptic_effect.periodic.period = period; haptic_effect.periodic.magnitude = magnitude; haptic_effect.periodic.length = length; haptic_effect.periodic.attack_length = attack; haptic_effect.periodic.fade_length = fade; effect_id = SDL_HapticNewEffect(joystick_haptic, &haptic_effect); if (effect_id < 0) { Com_Printf ("SDL_HapticNewEffect failed: %s\n", SDL_GetError()); Com_Printf ("Please try to rerun game. Effects will be disabled for now.\n"); IN_Haptic_Shutdown(); } return effect_id; } static int IN_Haptic_Effects_To_Id(int haptic_effect) { if ((SDL_HapticQuery(joystick_haptic) & SDL_HAPTIC_SINE)==0) return -1; int hapric_volume = joy_haptic_magnitude->value * 255; // * 128 = 32767 max strength; if (hapric_volume > 255) hapric_volume = 255; else if (hapric_volume < 0) hapric_volume = 0; switch(haptic_effect) { case HAPTIC_EFFECT_MENY: case HAPTIC_EFFECT_TRAPCOCK: case HAPTIC_EFFECT_STEP: /* North */ return IN_Haptic_Effect_Init( 0/* Force comes from N*/, 500/* 500 ms*/, hapric_volume * 48, 200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */, 100/* Takes 0.1 second to fade away */); case HAPTIC_EFFECT_PAIN: return IN_Haptic_Effect_Init( 0/* Force comes from N*/, 700/* 700 ms*/, hapric_volume * 196, 300/* 0.3 seconds long */, 200/* Takes 0.2 second to get max strength */, 200/* Takes 0.2 second to fade away */); case HAPTIC_EFFECT_BLASTER: /* 30 degrees */ return IN_Haptic_Effect_Init( 2000/* Force comes from NNE*/, 500/* 500 ms*/, hapric_volume * 64, 200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */, 100/* Takes 0.1 second to fade away */); case HAPTIC_EFFECT_HYPER_BLASTER: return IN_Haptic_Effect_Init( 4000/* Force comes from NNE*/, 500/* 500 ms*/, hapric_volume * 64, 200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */, 100/* Takes 0.1 second to fade away */); case HAPTIC_EFFECT_ETFRIFLE: /* 60 degrees */ return IN_Haptic_Effect_Init( 5000/* Force comes from NEE*/, 500/* 500 ms*/, hapric_volume * 64, 200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */, 100/* Takes 0.1 second to fade away */); case HAPTIC_EFFECT_TRACKER: return IN_Haptic_Effect_Init( 7000/* Force comes from NEE*/, 500/* 500 ms*/, hapric_volume * 64, 200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */, 100/* Takes 0.1 second to fade away */); case HAPTIC_EFFECT_MACHINEGUN: /* 90 degrees */ return IN_Haptic_Effect_Init( 9000/* Force comes from E*/, 800/* 800 ms*/, hapric_volume * 88, 600/* 0.6 seconds long */, 200/* Takes 0.2 second to get max strength */, 400/* Takes 0.4 second to fade away */); case HAPTIC_EFFECT_SHOTGUN: /* 120 degrees */ return IN_Haptic_Effect_Init( 12000/* Force comes from EES*/, 700/* 700 ms*/, hapric_volume * 100, 500/* 0.5 seconds long */, 100/* Takes 0.1 second to get max strength */, 200/* Takes 0.2 second to fade away */); case HAPTIC_EFFECT_SHOTGUN2: /* 150 degrees */ return IN_Haptic_Effect_Init( 14000/* Force comes from ESS*/, 700/* 700 ms*/, hapric_volume * 96, 500/* 0.5 seconds long */, 100/* Takes 0.1 second to get max strength */, 100/* Takes 0.1 second to fade away */); case HAPTIC_EFFECT_SSHOTGUN: return IN_Haptic_Effect_Init( 16000/* Force comes from ESS*/, 700/* 700 ms*/, hapric_volume * 96, 500/* 0.5 seconds long */, 100/* Takes 0.1 second to get max strength */, 100/* Takes 0.1 second to fade away */); case HAPTIC_EFFECT_RAILGUN: /* 180 degrees */ return IN_Haptic_Effect_Init( 18000/* Force comes from S*/, 700/* 700 ms*/, hapric_volume * 64, 400/* 0.4 seconds long */, 100/* Takes 0.1 second to get max strength */, 100/* Takes 0.1 second to fade away */); case HAPTIC_EFFECT_ROCKETGUN: /* 210 degrees */ return IN_Haptic_Effect_Init( 21000/* Force comes from SSW*/, 700/* 700 ms*/, hapric_volume * 128, 400/* 0.4 seconds long */, 300/* Takes 0.3 second to get max strength */, 100/* Takes 0.1 second to fade away */); case HAPTIC_EFFECT_GRENADE: /* 240 degrees */ return IN_Haptic_Effect_Init( 24000/* Force comes from SWW*/, 500/* 500 ms*/, hapric_volume * 64, 200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */, 100/* Takes 0.1 second to fade away */); case HAPTIC_EFFECT_BFG: /* 270 degrees */ return IN_Haptic_Effect_Init( 27000/* Force comes from W*/, 800/* 800 ms*/, hapric_volume * 100, 600/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */, 100/* Takes 0.1 second to fade away */); case HAPTIC_EFFECT_PALANX: /* 300 degrees */ return IN_Haptic_Effect_Init( 30000/* Force comes from WWN*/, 500/* 500 ms*/, hapric_volume * 64, 200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */, 100/* Takes 0.1 second to fade away */); case HAPTIC_EFFECT_IONRIPPER: /* 330 degrees */ return IN_Haptic_Effect_Init( 33000/* Force comes from WNN*/, 500/* 500 ms*/, hapric_volume * 64, 200/* 0.2 seconds long */, 100/* Takes 0.1 second to get max strength */, 100/* Takes 0.1 second to fade away */); default: return -1; } } static void IN_Haptic_Effects_Info(void) { show_haptic = true; Com_Printf ("Joystic/Mouse haptic:\n"); Com_Printf (" * %d effects\n", SDL_HapticNumEffects(joystick_haptic)); Com_Printf (" * %d effects in same time\n", SDL_HapticNumEffectsPlaying(joystick_haptic)); Com_Printf (" * %d haptic axis\n", SDL_HapticNumAxes(joystick_haptic)); } static void IN_Haptic_Effects_Init(void) { last_haptic_efffect_size = SDL_HapticNumEffectsPlaying(joystick_haptic); if (last_haptic_efffect_size > HAPTIC_EFFECT_LAST) last_haptic_efffect_size = HAPTIC_EFFECT_LAST; for (int i=0; i= 0) SDL_HapticDestroyEffect(joystick_haptic, *effect_id); *effect_id = -1; } static void IN_Haptic_Effects_Shutdown(void) { for (int i=0; ivalue <= 0) return; if (!joystick_haptic) return; if (last_haptic_volume != (int)(joy_haptic_magnitude->value * 255)) { IN_Haptic_Effects_Shutdown(); IN_Haptic_Effects_Init(); } last_haptic_volume = joy_haptic_magnitude->value * 255; if (strstr(name, "misc/menu")) { effect_type = HAPTIC_EFFECT_MENY; } else if (strstr(name, "weapons/blastf1a")) { effect_type = HAPTIC_EFFECT_BLASTER; } else if (strstr(name, "weapons/hyprbf1a")) { effect_type = HAPTIC_EFFECT_HYPER_BLASTER; } else if (strstr(name, "weapons/machgf")) { effect_type = HAPTIC_EFFECT_MACHINEGUN; } else if (strstr(name, "weapons/shotgf1b")) { effect_type = HAPTIC_EFFECT_SHOTGUN; } else if (strstr(name, "weapons/sshotf1b")) { effect_type = HAPTIC_EFFECT_SSHOTGUN; } else if (strstr(name, "weapons/railgf1a")) { effect_type = HAPTIC_EFFECT_RAILGUN; } else if (strstr(name, "weapons/rocklf1a")) { effect_type = HAPTIC_EFFECT_ROCKETGUN; } else if (strstr(name, "weapons/grenlf1a") || strstr(name, "weapons/hgrent1a")) { effect_type = HAPTIC_EFFECT_GRENADE; } else if (strstr(name, "weapons/bfg__f1y")) { effect_type = HAPTIC_EFFECT_BFG; } else if (strstr(name, "weapons/plasshot")) { effect_type = HAPTIC_EFFECT_PALANX; } else if (strstr(name, "weapons/rippfire")) { effect_type = HAPTIC_EFFECT_IONRIPPER; } else if (strstr(name, "weapons/nail1")) { effect_type = HAPTIC_EFFECT_ETFRIFLE; } else if (strstr(name, "weapons/shotg2")) { effect_type = HAPTIC_EFFECT_SHOTGUN2; } else if (strstr(name, "weapons/disint2")) { effect_type = HAPTIC_EFFECT_TRACKER; } else if (strstr(name, "player/male/pain") || strstr(name, "player/female/pain") || strstr(name, "players/male/pain") || strstr(name, "players/female/pain")) { effect_type = HAPTIC_EFFECT_PAIN; } else if (strstr(name, "player/step") || strstr(name, "player/land")) { effect_type = HAPTIC_EFFECT_STEP; } else if (strstr(name, "weapons/trapcock")) { effect_type = HAPTIC_EFFECT_TRAPCOCK; } if (effect_type != HAPTIC_EFFECT_UNKNOWN) { // check last effect for reuse if (last_haptic_efffect[last_haptic_efffect_pos].effect_type != effect_type) { // FIFO for effects last_haptic_efffect_pos = (last_haptic_efffect_pos+1) % last_haptic_efffect_size; IN_Haptic_Effect_Shutdown(&last_haptic_efffect[last_haptic_efffect_pos].effect_id); last_haptic_efffect[last_haptic_efffect_pos].effect_type = effect_type; last_haptic_efffect[last_haptic_efffect_pos].effect_id = IN_Haptic_Effects_To_Id(effect_type); } SDL_HapticRunEffect(joystick_haptic, last_haptic_efffect[last_haptic_efffect_pos].effect_id, 1); } #endif } /* * Initializes the backend */ void IN_Init(void) { Com_Printf("------- input initialization -------\n"); mouse_x = mouse_y = 0; #if SDL_VERSION_ATLEAST(2, 0, 0) joystick_yaw = joystick_pitch = joystick_forwardmove = joystick_sidemove = 0; #endif exponential_speedup = Cvar_Get("exponential_speedup", "0", CVAR_ARCHIVE); freelook = Cvar_Get("freelook", "1", 0); in_grab = Cvar_Get("in_grab", "2", CVAR_ARCHIVE); lookstrafe = Cvar_Get("lookstrafe", "0", 0); m_filter = Cvar_Get("m_filter", "0", CVAR_ARCHIVE); m_up = Cvar_Get("m_up", "1", 0); 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); #if SDL_VERSION_ATLEAST(2, 0, 0) joy_haptic_magnitude = Cvar_Get("joy_haptic_magnitude", "0.0", CVAR_ARCHIVE); joy_yawsensitivity = Cvar_Get("joy_yawsensitivity", "1.0", CVAR_ARCHIVE); joy_pitchsensitivity = Cvar_Get("joy_pitchsensitivity", "1.0", CVAR_ARCHIVE); joy_forwardsensitivity = Cvar_Get("joy_forwardsensitivity", "1.0", CVAR_ARCHIVE); joy_sidesensitivity = Cvar_Get("joy_sidesensitivity", "1.0", CVAR_ARCHIVE); joy_upsensitivity = Cvar_Get("joy_upsensitivity", "1.0", CVAR_ARCHIVE); joy_axis_leftx = Cvar_Get("joy_axis_leftx", "sidemove", CVAR_ARCHIVE); joy_axis_lefty = Cvar_Get("joy_axis_lefty", "forwardmove", CVAR_ARCHIVE); joy_axis_rightx = Cvar_Get("joy_axis_rightx", "yaw", CVAR_ARCHIVE); joy_axis_righty = Cvar_Get("joy_axis_righty", "pitch", CVAR_ARCHIVE); joy_axis_triggerleft = Cvar_Get("joy_axis_triggerleft", "triggerleft", CVAR_ARCHIVE); joy_axis_triggerright = Cvar_Get("joy_axis_triggerright", "triggerright", CVAR_ARCHIVE); joy_axis_leftx_threshold = Cvar_Get("joy_axis_leftx_threshold", "0.15", CVAR_ARCHIVE); joy_axis_lefty_threshold = Cvar_Get("joy_axis_lefty_threshold", "0.15", CVAR_ARCHIVE); joy_axis_rightx_threshold = Cvar_Get("joy_axis_rightx_threshold", "0.15", CVAR_ARCHIVE); joy_axis_righty_threshold = Cvar_Get("joy_axis_righty_threshold", "0.15", CVAR_ARCHIVE); joy_axis_triggerleft_threshold = Cvar_Get("joy_axis_triggerleft_threshold", "0.15", CVAR_ARCHIVE); joy_axis_triggerright_threshold = Cvar_Get("joy_axis_triggerright_threshold", "0.15", CVAR_ARCHIVE); #endif 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 #if SDL_VERSION_ATLEAST(2, 0, 0) /* joystik init */ if (!SDL_WasInit(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC)) { if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) == -1) { Com_Printf ("Couldn't init SDL joystick: %s.\n", SDL_GetError ()); } else { Com_Printf ("%i joysticks were found.\n", SDL_NumJoysticks()); if (SDL_NumJoysticks() > 0) { int i; for (i=0; istring); Com_Printf (" * lefty = %s\n", joy_axis_lefty->string); Com_Printf (" * rightx = %s\n", joy_axis_rightx->string); Com_Printf (" * righty = %s\n", joy_axis_righty->string); Com_Printf (" * triggerleft = %s\n", joy_axis_triggerleft->string); Com_Printf (" * triggerright = %s\n", joy_axis_triggerright->string); Com_Printf ("Controller thresholds: \n"); Com_Printf (" * leftx = %f\n", joy_axis_leftx_threshold->value); Com_Printf (" * lefty = %f\n", joy_axis_lefty_threshold->value); Com_Printf (" * rightx = %f\n", joy_axis_rightx_threshold->value); Com_Printf (" * righty = %f\n", joy_axis_righty_threshold->value); Com_Printf (" * triggerleft = %f\n", joy_axis_triggerleft_threshold->value); Com_Printf (" * triggerright = %f\n", joy_axis_triggerright_threshold->value); backBind = SDL_GameControllerGetBindForButton(controller, SDL_CONTROLLER_BUTTON_BACK); if (backBind.bindType == SDL_CONTROLLER_BINDTYPE_BUTTON) { back_button_id = backBind.value.button; Com_Printf ("\nBack button JOY%d will be unbindable.\n", back_button_id+1); } break; } else { char joystick_guid[256] = {0}; SDL_JoystickGUID guid; guid = SDL_JoystickGetDeviceGUID(i); SDL_JoystickGetGUIDString(guid, joystick_guid, 255); Com_Printf ("For use joystic as game contoller please set SDL_GAMECONTROLLERCONFIG:\n"); Com_Printf ("e.g.: SDL_GAMECONTROLLERCONFIG='%s,%s,leftx:a0,lefty:a1,rightx:a2,righty:a3,back:b1,...\n", joystick_guid, SDL_JoystickName(joystick)); } } } else { joystick_haptic = SDL_HapticOpenFromMouse(); if (joystick_haptic == NULL) Com_Printf ("Most likely mouse isn't haptic\n"); else IN_Haptic_Effects_Info(); } } } #endif Com_Printf("------------------------------------\n\n"); } #if SDL_VERSION_ATLEAST(2, 0, 0) /* * Shuts the backend down */ static void IN_Haptic_Shutdown(void) { if (joystick_haptic) { IN_Haptic_Effects_Shutdown(); SDL_HapticClose(joystick_haptic); joystick_haptic = NULL; } } #endif void IN_Shutdown(void) { Cmd_RemoveCommand("force_centerview"); Cmd_RemoveCommand("+mlook"); Cmd_RemoveCommand("-mlook"); Com_Printf("Shutting down input.\n"); #if SDL_VERSION_ATLEAST(2, 0, 0) IN_Haptic_Shutdown(); if (controller) { back_button_id = -1; SDL_GameControllerClose(controller); controller = NULL; } if (joystick) { SDL_JoystickClose(joystick); joystick = NULL; } #endif } /* ------------------------------------------------------------------ */ yquake2-QUAKE2_7_10/src/backends/sdl/refresh.c000066400000000000000000000250201321245476300210460ustar00rootroot00000000000000/* * 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 "../../common/header/common.h" /* CVar_*, qboolean (through shared.h) */ #include "../../client/header/ref.h" #ifdef SDL2 #include #include #else // SDL1.2 #include #endif //SDL2 #if SDL_VERSION_ATLEAST(2, 0, 0) static SDL_Window* window = NULL; #else static SDL_Surface* window = NULL; #endif #if SDL_VERSION_ATLEAST(2, 0, 0) // some compatibility defines #define SDL_SRCCOLORKEY SDL_TRUE #define SDL_OPENGL SDL_WINDOW_OPENGL #endif /* * Initializes the SDL video subsystem */ int GLimp_Init(void) { if (!SDL_WasInit(SDL_INIT_VIDEO)) { if (SDL_Init(SDL_INIT_VIDEO) == -1) { Com_Printf("Couldn't init SDL video: %s.\n", SDL_GetError()); return false; } SDL_version version; #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_GetVersion(&version); const char* driverName = SDL_GetCurrentVideoDriver(); #else char driverName[64]; SDL_VideoDriverName(driverName, sizeof(driverName)); version = *SDL_Linked_Version(); #endif Com_Printf("SDL version is: %i.%i.%i\n", (int)version.major, (int)version.minor, (int)version.patch); Com_Printf("SDL video driver is \"%s\".\n", driverName); } return true; } /* * 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 = 0; 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 */ static int IsFullscreen() { #if SDL_VERSION_ATLEAST(2, 0, 0) if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) { return 1; } else if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { return 2; } else { return 0; } #else return !!(window->flags & SDL_FULLSCREEN); #endif } static qboolean CreateSDLWindow(int flags, int w, int h) { #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, w, h, flags); return window != NULL; #else window = SDL_SetVideoMode(w, h, 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) { Com_Printf("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 qboolean initSuccessful = false; /* * Initializes the OpenGL window */ qboolean GLimp_InitGraphics(int fullscreen, int *pwidth, int *pheight) { int flags; int curWidth, curHeight; int width = *pwidth; int height = *pheight; unsigned int fs_flag = 0; #if SDL_VERSION_ATLEAST(2, 0, 0) if (fullscreen == 1) { fs_flag = SDL_WINDOW_FULLSCREEN_DESKTOP; } else if (fullscreen == 2) { fs_flag = SDL_WINDOW_FULLSCREEN; } #else if (fullscreen) { fs_flag = SDL_FULLSCREEN; } #endif // only do this if we already have a working window and fully initialized rendering backend // (GLimp_InitGraphics() is also called when recovering if creating GL context fails or the one we got is unusable) if (initSuccessful && GetWindowSize(&curWidth, &curHeight) && (curWidth == width) && (curHeight == height)) { /* If we want fullscreen, but aren't */ if (fullscreen != IsFullscreen()) { #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_SetWindowFullscreen(window, fs_flag); #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) re.ShutdownWindow(true); SDL_DestroyWindow(window); #else SDL_FreeSurface(window); #endif window = NULL; } /* Create the window */ VID_NewWindow(width, height); // let renderer prepare things (set OpenGL attributes) flags = re.PrepareForWindow(); if(flags == -1) { // hopefully PrepareForWindow() logged an error return false; } if (fs_flag) { flags |= fs_flag; } #if !SDL_VERSION_ATLEAST(2, 0, 0) /* Set window icon - For SDL1.2, this must be done before creating the window */ SetSDLIcon(); #endif cvar_t *gl_msaa_samples = Cvar_Get("gl_msaa_samples", "0", CVAR_ARCHIVE); while (1) { if (!CreateSDLWindow(flags, width, height)) { if((flags & SDL_OPENGL) && gl_msaa_samples->value) { Com_Printf("SDL SetVideoMode failed: %s\n", SDL_GetError()); Com_Printf("Reverting to %s gl_mode %i (%ix%i) without MSAA.\n", (flags & fs_flag) ? "fullscreen" : "windowed", (int) Cvar_VariableValue("gl_mode"), width, 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 (width != 640 || height != 480 || (flags & fs_flag)) { Com_Printf("SDL SetVideoMode failed: %s\n", SDL_GetError()); Com_Printf("Reverting to windowed gl_mode 4 (640x480).\n"); /* Try to recover */ Cvar_SetValue("gl_mode", 4); Cvar_SetValue("vid_fullscreen", 0); VID_NewWindow(width, height); *pwidth = width = 640; *pheight = height = 480; flags &= ~fs_flag; } else { Com_Error(ERR_FATAL, "Failed to revert to gl_mode 4. Exiting...\n"); return false; } } else { break; } } if(!re.InitContext(window)) { // InitContext() should have logged an error return false; } /* Note: window title is now set in re.InitContext() to include renderer name */ #if SDL_VERSION_ATLEAST(2, 0, 0) /* Set the window icon - For SDL2, this must be done after creating the window */ SetSDLIcon(); #endif /* No cursor */ SDL_ShowCursor(0); initSuccessful = true; return true; } /* * (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) { Com_Printf("WARNING: Setting Relative Mousemode failed, reason: %s\n", SDL_GetError()); Com_Printf(" You should probably update to SDL 2.0.3 or newer!\n"); } #else SDL_WM_GrabInput(grab ? SDL_GRAB_ON : SDL_GRAB_OFF); #endif } int glimp_refreshRate = -1; /* * Returns the current display refresh rate. */ int GLimp_GetRefreshRate(void) { #if SDL_VERSION_ATLEAST(2, 0, 0) // do this only once, assuming people don't change their display settings // or plug in new displays while the game is running if (glimp_refreshRate == -1) { SDL_DisplayMode mode; // TODO: probably refreshRate should be reset to -1 if window is moved int i = SDL_GetWindowDisplayIndex(window); if(i >= 0 && SDL_GetCurrentDisplayMode(i, &mode) == 0) { glimp_refreshRate = mode.refresh_rate; } if (glimp_refreshRate <= 0) { glimp_refreshRate = 60; // apparently the stuff above failed, use default } } return glimp_refreshRate; #else // Asume 60hz. return 60; #endif } /* * Shuts the SDL render backend down */ void VID_ShutdownWindow(void) { if (window) { /* cleanly ungrab input (needs window) */ GLimp_GrabInput(false); #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_DestroyWindow(window); #else SDL_FreeSurface(window); #endif } window = NULL; // make sure that after vid_restart the refreshrate will be queried from SDL2 again. glimp_refreshRate = -1; initSuccessful = false; // not initialized anymore if (SDL_WasInit(SDL_INIT_EVERYTHING) == SDL_INIT_VIDEO) { SDL_Quit(); } else { SDL_QuitSubSystem(SDL_INIT_VIDEO); } } yquake2-QUAKE2_7_10/src/backends/sdl/sound.c000066400000000000000000000672761321245476300205630ustar00rootroot00000000000000/* * 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_7_10/src/backends/unix/000077500000000000000000000000001321245476300174465ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/backends/unix/header/000077500000000000000000000000001321245476300206765ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/backends/unix/header/unix.h000066400000000000000000000020571321245476300220360ustar00rootroot00000000000000/* * 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_7_10/src/backends/unix/main.c000066400000000000000000000072121321245476300205400ustar00rootroot00000000000000/* * 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 "../../common/header/common.h" #include "header/unix.h" qboolean is_portable; int main(int argc, char **argv) { int verLen, i; long long oldtime, newtime; const char* versionString; // Time slept each frame. #ifndef DEDICATED_ONLY struct timespec t = {0, 5000}; #else struct timespec t = {0, 850000}; #endif /* register signal handler */ registerHandler(); /* Setup FPU if necessary */ Sys_SetupFPU(); /* Are we portable? */ for (i = 0; i < argc; i++) { if (strcmp(argv[i], "-portable") == 0) { is_portable = true; } } /* 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 doing 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); versionString = va("Yamagi Quake II v%s", YQ2VERSION); verLen = strlen(versionString); printf("\n%s\n", versionString); for(i=0; iai_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_7_10/src/backends/unix/shared/000077500000000000000000000000001321245476300207145ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/backends/unix/shared/hunk.c000066400000000000000000000073621321245476300220350ustar00rootroot00000000000000/* * 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_7_10/src/backends/unix/signalhandler.c000066400000000000000000000074301321245476300224310ustar00rootroot00000000000000/* * 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("Platform: %s\n", YQ2OSTYPE); printf("Architecture: %s\n", YQ2ARCH); 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("Platform: %s\n", YQ2OSTYPE); printf("Architecture: %s\n", YQ2ARCH); 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 useful\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_7_10/src/backends/unix/system.c000066400000000000000000000235341321245476300211450ustar00rootroot00000000000000/* * 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 #ifdef __APPLE__ #include #include #endif #include "../../common/header/common.h" #include "../../common/header/glob.h" #include "../generic/header/input.h" #include "header/unix.h" unsigned sys_frame_time; 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) { } long long Sys_Microseconds(void) { #ifdef __APPLE__ // OSX didn't have clock_gettime() until recently, so use Mach's clock_get_time() // instead. fortunately its mach_timespec_t seems identical to POSIX struct timespec // so lots of code can be shared clock_serv_t cclock; mach_timespec_t now; static mach_timespec_t first; host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); clock_get_time(cclock, &now); mach_port_deallocate(mach_task_self(), cclock); #else // not __APPLE__ - other Unix-likes will hopefully support clock_gettime() struct timespec now; static struct timespec first; #ifdef _POSIX_MONOTONIC_CLOCK clock_gettime(CLOCK_MONOTONIC, &now); #else clock_gettime(CLOCK_REALTIME, &now); #endif #endif // not __APPLE__ if(first.tv_sec == 0) { long long nsec = now.tv_nsec; long long sec = now.tv_sec; // set back first by 1ms so neither this function nor Sys_Milliseconds() // (which calls this) will ever return 0 nsec -= 1000000; if(nsec < 0) { nsec += 1000000000ll; // 1s in ns => definitely positive now --sec; } first.tv_sec = sec; first.tv_nsec = nsec; } long long sec = now.tv_sec - first.tv_sec; long long nsec = now.tv_nsec - first.tv_nsec; if(nsec < 0) { nsec += 1000000000ll; // 1s in ns --sec; } return sec*1000000ll + nsec/1000ll; } int Sys_Milliseconds(void) { return (int)(Sys_Microseconds()/1000ll); } 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) { if (handle == NULL) { #ifdef RTLD_DEFAULT return dlsym(RTLD_DEFAULT, sym); #else /* POSIX suggests that this is a portable equivalent */ static void *global_namespace = NULL; if (global_namespace == NULL) global_namespace = dlopen(NULL, RTLD_GLOBAL|RTLD_LAZY); return dlsym(global_namespace, sym); #endif } 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()); } } /* * Just a dummy. There's no need on unixoid systems to * redirect stdout and stderr. */ void Sys_RedirectStdout(void) { return; } yquake2-QUAKE2_7_10/src/backends/windows/000077500000000000000000000000001321245476300201555ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/backends/windows/header/000077500000000000000000000000001321245476300214055ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/backends/windows/header/resource.h000066400000000000000000000005371321245476300234120ustar00rootroot00000000000000#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_7_10/src/backends/windows/header/winquake.h000066400000000000000000000025411321245476300234040ustar00rootroot00000000000000/* * 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_7_10/src/backends/windows/icon.rc000066400000000000000000000000531321245476300214310ustar00rootroot00000000000000icon ICON "../../../stuff/icon/Quake2.ico" yquake2-QUAKE2_7_10/src/backends/windows/network.c000066400000000000000000000656701321245476300220300ustar00rootroot00000000000000/* * 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_7_10/src/backends/windows/shared/000077500000000000000000000000001321245476300214235ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/backends/windows/shared/mem.c000066400000000000000000000041401321245476300223440ustar00rootroot00000000000000/* * 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_7_10/src/backends/windows/system.c000066400000000000000000000407241321245476300216540ustar00rootroot00000000000000/* * 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 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]; qboolean is_portable; /* ================================================================ */ 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); fprintf(stderr, "Error: %s\n", text); 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++; } } } } /* ======================================================================= */ long long Sys_Microseconds(void) { long long microseconds; static long long uSecbase; FILETIME ft; unsigned long long tmpres = 0; GetSystemTimeAsFileTime(&ft); tmpres |= ft.dwHighDateTime; tmpres <<= 32; tmpres |= ft.dwLowDateTime; tmpres /= 10; // Convert to microseconds. tmpres -= 11644473600000000ULL; // ...and to unix epoch. microseconds = tmpres; if (!uSecbase) { uSecbase = microseconds - 1001ll; } return microseconds - uSecbase; } int Sys_Milliseconds(void) { return (int)(Sys_Microseconds()/1000ll); } void Sys_Sleep(int msec) { Sleep(msec); } void Sys_Nanosleep(int nanosec) { HANDLE timer; LARGE_INTEGER li; timer = CreateWaitableTimer(NULL, TRUE, NULL); // Windows has a max. resolution of 100ns. li.QuadPart = -nanosec / 100; SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE); WaitForSingleObject(timer, INFINITE); CloseHandle(timer); } /* ======================================================================= */ 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 *old; char dir[MAX_OSPATH]; char path_stdout[MAX_OSPATH]; char path_stderr[MAX_OSPATH]; const char *tmp; if (is_portable) { tmp = Sys_GetBinaryDir(); Q_strlcpy(dir, tmp, sizeof(dir)); } else { tmp = Sys_GetHomeDir(); Q_strlcpy(dir, tmp, sizeof(dir)); } if (dir == NULL) { return; } cur = old = dir; while (cur != NULL) { if ((cur - old) > 1) { *cur = '\0'; Sys_Mkdir(dir); *cur = '/'; } old = cur; cur = strchr(old + 1, '/'); } snprintf(path_stdout, sizeof(path_stdout), "%s/%s", dir, "stdout.txt"); snprintf(path_stderr, sizeof(path_stderr), "%s/%s", dir, "stderr.txt"); freopen(path_stdout, "w", stdout); freopen(path_stderr, "w", stderr); } /* ======================================================================= */ typedef enum YQ2_PROCESS_DPI_AWARENESS { YQ2_PROCESS_DPI_UNAWARE = 0, YQ2_PROCESS_SYSTEM_DPI_AWARE = 1, YQ2_PROCESS_PER_MONITOR_DPI_AWARE = 2 } YQ2_PROCESS_DPI_AWARENESS; void Sys_SetHighDPIMode(void) { /* For Vista, Win7 and Win8 */ BOOL(WINAPI *SetProcessDPIAware)(void) = NULL; /* Win8.1 and later */ HRESULT(WINAPI *SetProcessDpiAwareness)(YQ2_PROCESS_DPI_AWARENESS dpiAwareness) = NULL; HINSTANCE userDLL = LoadLibrary("USER32.DLL"); if (userDLL) { SetProcessDPIAware = (BOOL(WINAPI *)(void)) GetProcAddress(userDLL, "SetProcessDPIAware"); } HINSTANCE shcoreDLL = LoadLibrary("SHCORE.DLL"); if (shcoreDLL) { SetProcessDpiAwareness = (HRESULT(WINAPI *)(YQ2_PROCESS_DPI_AWARENESS)) GetProcAddress(shcoreDLL, "SetProcessDpiAwareness"); } if (SetProcessDpiAwareness) { SetProcessDpiAwareness(YQ2_PROCESS_PER_MONITOR_DPI_AWARE); } else if (SetProcessDPIAware) { SetProcessDPIAware(); } } /* ======================================================================= */ /* * Windows main function. Containts the * initialization code and the main loop */ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { MSG msg; long long oldtime, newtime; /* Previous instances do not exist in Win32 */ if (hPrevInstance) { return 0; } /* Make the current instance global */ global_hInstance = hInstance; /* Setup FPU if necessary */ Sys_SetupFPU(); /* Force DPI awareness */ Sys_SetHighDPIMode(); /* Parse the command line arguments */ ParseCommandLine(lpCmdLine); /* Are we portable? */ for (int i = 0; i < argc; i++) { if (strcmp(argv[i], "-portable") == 0) { is_portable = true; } } /* Need to redirect stdout before anything happens. */ #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", YQ2OSTYPE); printf("Architecture: %s\n", YQ2ARCH); /* Seed PRNG */ randk_seed(); /* Call the initialization code */ Qcommon_Init(argc, argv); /* Save our time */ oldtime = Sys_Microseconds(); /* 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); } // Throttle the game a little bit Sys_Nanosleep(5000); newtime = Sys_Microseconds(); Qcommon_Frame(newtime - oldtime); 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_7_10/src/client/000077500000000000000000000000001321245476300161675ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/cl_cin.c000066400000000000000000000264511321245476300175720ustar00rootroot00000000000000/* * 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" #include "../backends/generic/header/input.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, full_size; int dataByte, runLength; byte *out, *pix; *pic = NULL; /* load the file */ len = FS_LoadFile(filename, (void **)&raw); if (!raw || len < sizeof(pcx_t)) { 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; } full_size = (pcx->ymax + 1) * (pcx->xmax + 1); out = Z_Malloc(full_size); *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) { if ((*pic + full_size) <= (pix + x)) { x += runLength; runLength = 0; } else { 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", (int)(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; In_FlushQueue(); /* 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_7_10/src/client/cl_console.c000066400000000000000000000307411321245476300204600ustar00rootroot00000000000000/* * 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 "sound/header/local.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 DrawStringScaled(int x, int y, char *s, float factor) { while (*s) { Draw_CharScaled(x, y, *s, factor); x += 8*factor; s++; } } 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(); #ifdef USE_OPENAL if (cl.cinematic_file) { AL_UnqueueRawSamples(); } #endif 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]; float scale = SCR_GetConsoleScale(); width = ((int)(viddef.width / scale) >> 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; } } } /* * 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; int verLen; 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); verLen = strlen(version); for (x = 0; x < verLen; x++) { Draw_CharScaled(viddef.width - ((verLen*8+5) * 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_7_10/src/client/cl_download.c000066400000000000000000000332161321245476300206250ustar00rootroot00000000000000/* * 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)); cls.forcePacket = true; } 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++; cls.forcePacket = true; 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"); cls.forcePacket = true; } 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_7_10/src/client/cl_effects.c000066400000000000000000001622431321245476300204400ustar00rootroot00000000000000/* * 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_DPrintf("CL_AddMuzzleFlash2: bad offset"); return; } /* 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_7_10/src/client/cl_entities.c000066400000000000000000000472241321245476300206460ustar00rootroot00000000000000/* * 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; } 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; /* To distinguish baseq2, xatrix and rogue. */ cvar_t *game = Cvar_Get("game", "", CVAR_LATCH | CVAR_SERVERINFO); /* 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 (strcmp(game->string, "rogue") == 0) { /* 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 (strcmp(game->string, "rogue") == 0) { /* 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 */ } if(cl_paused->value){ lerp = 1.0f; } else { 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); } /* * Called to get the sound spatialization */ void CL_GetEntitySoundVelocity(int ent, vec3_t vel) { centity_t *old; if ((ent < 0) || (ent >= MAX_EDICTS)) { Com_Error(ERR_DROP, "CL_GetEntitySoundVelocity: bad ent"); } old = &cl_entities[ent]; VectorSubtract(old->current.origin, old->prev.origin, vel); } void CL_GetViewVelocity(vec3_t vel) { // restore value from 12.3 fixed point const float scale_factor = 1.0f / 8.0f; vel[0] = (float)cl.frame.playerstate.pmove.velocity[0] * scale_factor; vel[1] = (float)cl.frame.playerstate.pmove.velocity[1] * scale_factor; vel[2] = (float)cl.frame.playerstate.pmove.velocity[2] * scale_factor; } yquake2-QUAKE2_7_10/src/client/cl_input.c000066400000000000000000000352531321245476300201600ustar00rootroot00000000000000/* * 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.nframetime * cl_anglespeedkey->value; } else { speed = cls.nframetime; } 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.nframetime * 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; } 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_RefreshCmd(void) { int ms; usercmd_t *cmd; // CMD to fill cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP - 1)]; // Calculate delta frame_msec = sys_frame_time - old_sys_frame_time; // Check bounds if (frame_msec < 1) { return; } else if (frame_msec > 200) { frame_msec = 200; } // Add movement CL_BaseMove(cmd); IN_Move(cmd); // Clamp angels for prediction CL_ClampPitch(); cmd->angles[0] = ANGLE2SHORT(cl.viewangles[0]); cmd->angles[1] = ANGLE2SHORT(cl.viewangles[1]); cmd->angles[2] = ANGLE2SHORT(cl.viewangles[2]); // Update time for prediction ms = (int)(cls.nframetime * 1000.0f); if (ms > 250) { ms = 100; } cmd->msec = ms; // Update frame time for the next call old_sys_frame_time = sys_frame_time; // Important events are send immediately if (((in_attack.state & 2)) || (in_use.state & 2)) { cls.forcePacket = true; } } void CL_RefreshMove(void) { usercmd_t *cmd; // CMD to fill cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP - 1)]; // Calculate delta frame_msec = sys_frame_time - old_sys_frame_time; // Check bounds if (frame_msec < 1) { return; } else if (frame_msec > 200) { frame_msec = 200; } // Add movement CL_BaseMove(cmd); IN_Move(cmd); old_sys_frame_time = sys_frame_time; } void CL_FinalizeCmd(void) { usercmd_t *cmd; // CMD to fill cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP - 1)]; // Mouse button events 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; // Keyboard events if (anykeydown && cls.key_dest == key_game) { cmd->buttons |= BUTTON_ANY; } cmd->impulse = in_impulse; in_impulse = 0; // Set light level for muzzle flash cmd->lightlevel = (byte)cl_lightlevel->value; } void CL_SendCmd(void) { sizebuf_t buf; byte data[128]; int i; usercmd_t *cmd, *oldcmd; usercmd_t nullcmd; int checksumIndex; memset(&buf, 0, sizeof(buf)); /* 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 */ CL_FinalizeCmd(); 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 > 1000)) { Netchan_Transmit(&cls.netchan, 0, buf.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); /* Reinit the current cmd buffer */ cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP - 1)]; memset(cmd, 0, sizeof(*cmd)); } yquake2-QUAKE2_7_10/src/client/cl_inventory.c000066400000000000000000000061661321245476300210570ustar00rootroot00000000000000/* * 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_7_10/src/client/cl_keyboard.c000066400000000000000000000575141321245476300206250ustar00rootroot00000000000000/* * 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" 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}, {"JOY1", K_JOY1}, {"JOY2", K_JOY2}, {"JOY3", K_JOY3}, {"JOY4", K_JOY4}, {"JOY5", K_JOY5}, {"JOY6", K_JOY6}, {"JOY7", K_JOY7}, {"JOY8", K_JOY8}, {"JOY9", K_JOY9}, {"JOY10", K_JOY10}, {"JOY11", K_JOY11}, {"JOY12", K_JOY12}, {"JOY13", K_JOY13}, {"JOY14", K_JOY14}, {"JOY15", K_JOY15}, {"JOY16", K_JOY16}, {"JOY17", K_JOY17}, {"JOY18", K_JOY18}, {"JOY19", K_JOY19}, {"JOY20", K_JOY20}, {"JOY21", K_JOY21}, {"JOY22", K_JOY22}, {"JOY23", K_JOY23}, {"JOY24", K_JOY24}, {"JOY25", K_JOY25}, {"JOY26", K_JOY26}, {"JOY27", K_JOY27}, {"JOY28", K_JOY28}, {"JOY29", K_JOY29}, {"JOY30", K_JOY30}, {"JOY31", K_JOY31}, {"JOY32", K_JOY32}, {"HAT_UP", K_HAT_UP}, {"HAT_RIGHT", K_HAT_RIGHT}, {"HAT_DOWN", K_HAT_DOWN}, {"HAT_LEFT", K_HAT_LEFT}, {"TRIG_LEFT", K_TRIG_LEFT}, {"TRIG_RIGHT", K_TRIG_RIGHT}, {"JOY_BACK", K_JOY_BACK}, {"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 == '~' || b == K_JOY_BACK) { 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]; if (is_portable) { Com_sprintf(path, sizeof(path), "%sconsole_history.txt", Sys_GetBinaryDir()); } else { 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_console: Key_Console(key); break; /* Console is really open but key_dest is game anyway (not connected) */ case key_game: if(cls.state == ca_disconnected || cls.state == ca_connecting) Key_Console(key); break; default: 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) { Cvar_Set("vid_fullscreen", "1"); fullscreen->modified = true; } else { Cvar_Set("vid_fullscreen", "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 >= K_MOUSE1 && key != K_JOY_BACK) && !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 Fully same logic for K_JOY_BACK */ if (!cls.disable_screen) { if (key == K_ESCAPE || key == K_JOY_BACK) { 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_7_10/src/client/cl_lights.c000066400000000000000000000070121321245476300203030ustar00rootroot00000000000000/* * 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.rframetime * 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_7_10/src/client/cl_main.c000066400000000000000000000437541321245476300177520ustar00rootroot00000000000000/* * 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 *rcon_client_password; cvar_t *rcon_address; cvar_t *cl_noskins; cvar_t *cl_footsteps; cvar_t *cl_timeout; cvar_t *cl_predict; 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 *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 *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 *gl_stereo; cvar_t *gl_stereo_separation; cvar_t *gl_stereo_convergence; 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]; /*Evil hack against too many power screen and power shield impact sounds. For example if the player fires his shotgun onto a Brain. */ int num_power_sounds; 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(); /* register our variables */ cin_force43 = Cvar_Get("cin_force43", "1", 0); 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_predict = Cvar_Get("cl_predict", "1", 0); 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); 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); gl_stereo = Cvar_Get( "gl_stereo", "0", CVAR_ARCHIVE ); gl_stereo_separation = Cvar_Get( "gl_stereo_separation", "1", CVAR_ARCHIVE ); gl_stereo_convergence = Cvar_Get( "gl_stereo_convergence", "1.4", CVAR_ARCHIVE ); rcon_client_password = Cvar_Get("rcon_password", "", 0); rcon_address = Cvar_Get("rcon_address", "", 0); cl_lightlevel = Cvar_Get("gl_lightlevel", "0", 0); /* 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; // USERINFO cvars are special, they just need to be registered Cvar_Get("password", "", CVAR_USERINFO); Cvar_Get("spectator", "0", CVAR_USERINFO); 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); fflush(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_Frame(int packetdelta, int renderdelta, int timedelta, qboolean packetframe, qboolean renderframe) { static int lasttimecalled; // Dedicated? if (dedicated->value) { return; } // Calculate simulation time. cls.nframetime = packetdelta / 1000000.0f; cls.rframetime = renderdelta / 1000000.0f; cls.realtime = curtime; cl.time += timedelta / 1000; // Don't extrapolate too far ahead. if (cls.nframetime > 0.5f) { cls.nframetime = 0.5f; } if (cls.rframetime > 0.5f) { cls.rframetime = 0.5f; } // if in the debugger last frame, don't timeout. if (timedelta > 5000000) { cls.netchan.last_received = Sys_Milliseconds(); } // Reset power shield / power screen sound counter. num_power_sounds = 0; if (!cl_timedemo->value) { // Don't throttle too much when connecting / loading. if ((cls.state == ca_connected) && (packetdelta > 100000)) { packetframe = true; } } // Update input stuff if (packetframe || renderframe) { CL_ReadPackets(); CL_UpdateWindowedMouse(); Sys_SendKeyEvents(); Cbuf_Execute(); CL_FixCvarCheats(); if (cls.state > ca_connecting) { CL_RefreshCmd(); } else { CL_RefreshMove(); } } if (cls.forcePacket || userinfo_modified) { packetframe = true; cls.forcePacket = false; } if (packetframe) { CL_SendCmd(); CL_CheckForResend(); } if (renderframe) { VID_CheckChanges(); CL_PredictMovement(); 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(); /* Update framecounter */ 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(); 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_7_10/src/client/cl_network.c000066400000000000000000000316351321245476300205120ustar00rootroot00000000000000/* * 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_7_10/src/client/cl_parse.c000066400000000000000000000701011321245476300201220ustar00rootroot00000000000000/* * 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_7_10/src/client/cl_particles.c000066400000000000000000000121651321245476300210040ustar00rootroot00000000000000/* * 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_7_10/src/client/cl_prediction.c000066400000000000000000000145721321245476300211620ustar00rootroot00000000000000/* * 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; usercmd_t *cmd; pmove_t pm; int i; int step; vec3_t tmp; 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 */ memset (&pm, 0, sizeof(pm)); pm.trace = CL_PMTrace; pm.pointcontents = CL_PMpointcontents; pm_airaccelerate = atof(cl.configstrings[CS_AIRACCEL]); pm.s = cl.frame.playerstate.pmove; /* run frames */ while (++ack <= current) { frame = ack & (CMD_BACKUP - 1); cmd = &cl.cmds[frame]; // Ignore null entries if (!cmd->msec) { continue; } pm.cmd = *cmd; Pmove(&pm); /* save for debug checking */ VectorCopy(pm.s.origin, cl.predicted_origins[frame]); } step = pm.s.origin[2] - (int)(cl.predicted_origin[2] * 8); VectorCopy(tmp, pm.s.velocity); if (((step > 126 && step < 130)) && !VectorCompare(tmp, vec3_origin) && (pm.s.pm_flags & PMF_ON_GROUND)) { cl.predicted_step = step * 0.125f; cl.predicted_step_time = cls.realtime - (int)(cls.nframetime * 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_7_10/src/client/cl_screen.c000066400000000000000000000757431321245476300203100ustar00rootroot00000000000000/* * 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.rframetime; 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.height = viddef.height * size / 100; 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; } 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.rframetime; 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.rframetime; 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_draw_loading = false; 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); } R_EndFrame(); } } else { for (i = 0; i < 128; i++) { cl.refdef.viewangles[1] = i / 128.0f * 360.0f; R_BeginFrame(0); R_RenderFrame(&cl.refdef); R_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 + factor*24); 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); } // ---- void SCR_Framecounter(void) { long long newtime; static int frame; static int frametimes[60] = {0}; static long long oldtime; newtime = Sys_Microseconds(); frametimes[frame] = (int)(newtime - oldtime); oldtime = newtime; frame++; if (frame > 59) { frame = 0; } float scale = SCR_GetConsoleScale(); if (cl_drawfps->value == 1) { // Calculate average of frames. int avg = 0; int num = 0; for (int i = 0; i < 60; i++) { if (frametimes[i] != 0) { avg += frametimes[i]; num++; } } char str[10]; snprintf(str, sizeof(str), "%3.2ffps", (1000.0 * 1000.0) / (avg / num)); DrawStringScaled(viddef.width - scale*(strlen(str)*8 + 2), 0, str, scale); } else if (cl_drawfps->value >= 2) { // Calculate average of frames. int avg = 0; int num = 0; for (int i = 0; i < 60; i++) { if (frametimes[i] != 0) { avg += frametimes[i]; num++; } } // Find lowest and highest int min = frametimes[0]; int max = frametimes[1]; for (int i = 1; i < 60; i++) { if ((frametimes[i] > 0) && (min < frametimes[i])) { min = frametimes[i]; } if ((frametimes[i] > 0) && (max > frametimes[i])) { max = frametimes[i]; } } char str[64]; snprintf(str, sizeof(str), "Min: %7.2ffps, Max: %7.2ffps, Avg: %7.2ffps", (1000.0 * 1000.0) / min, (1000.0 * 1000.0) / max, (1000.0 * 1000.0) / (avg / num)); DrawStringScaled(viddef.width - scale*(strlen(str)*8 + 2), 0, str, scale); if (cl_drawfps->value > 2) { snprintf(str, sizeof(str), "Max: %5.2fms, Min: %5.2fms, Avg: %5.2fms", 0.001f*min, 0.001f*max, 0.001f*(avg / num)); DrawStringScaled(viddef.width - scale*(strlen(str)*8 + 2), scale*10, str, scale); } } } // ---- /* * 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 */ } if ( gl_stereo->value ) { numframes = 2; separation[0] = -gl_stereo_separation->value / 2; separation[1] = +gl_stereo_separation->value / 2; } else { 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; if(i == 0){ R_SetPalette(NULL); } if(i == numframes - 1){ 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 (scr_timegraph->value) { SCR_DebugGraph(cls.rframetime * 300, 0); } if (scr_debuggraph->value || scr_timegraph->value || scr_netgraph->value) { SCR_DrawDebugGraph(); } SCR_DrawPause(); SCR_DrawConsole(); M_Draw(); SCR_DrawLoading(); } } SCR_Framecounter(); R_EndFrame(); } static float SCR_ClampScale(float scale) { float f; f = viddef.width / 320.0f; if (scale > f) { scale = f; } f = viddef.height / 240.0f; if (scale > f) { scale = f; } if (scale < 1) { scale = 1; } return scale; } static 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 = SCR_ClampScale(crosshair_scale->value); } Draw_PicScaled(scr_vrect.x + (scr_vrect.width - crosshair_width * scale) / 2, scr_vrect.y + (scr_vrect.height - crosshair_height * scale) / 2, crosshair_pic, scale); } float SCR_GetHUDScale(void) { float scale; if (!scr_initialized) { scale = 1; } else if (gl_hudscale->value < 0) { scale = SCR_GetDefaultScale(); } else if (gl_hudscale->value == 0) /* HACK: allow scale 0 to hide the HUD */ { scale = 0; } else { scale = SCR_ClampScale(gl_hudscale->value); } return scale; } float SCR_GetConsoleScale(void) { float scale; if (!scr_initialized) { scale = 1; } else if (gl_consolescale->value < 0) { scale = SCR_GetDefaultScale(); } else { scale = SCR_ClampScale(gl_consolescale->value); } return scale; } float SCR_GetMenuScale(void) { float scale; if (!scr_initialized) { scale = 1; } else if (gl_menuscale->value < 0) { scale = SCR_GetDefaultScale(); } else { scale = SCR_ClampScale(gl_menuscale->value); } return scale; } yquake2-QUAKE2_7_10/src/client/cl_tempentities.c000066400000000000000000001131641321245476300215310ustar00rootroot00000000000000/* * 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 #include "header/client.h" #include "sound/header/local.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); } num_power_sounds++; /* If too many of these sounds are started in one frame (for * example if the player shoots with the super shotgun into * the power screen of a Brain) things get too loud and OpenAL * is forced to scale the volume of several other sounds and * the background music down. That leads to a noticable and * annoying drop in the overall volume. * * Work around that by limiting the number of sounds started. * 16 was choosen by empirical testing. */ if (sound_started == SS_OAL && num_power_sounds < 16) { 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_7_10/src/client/cl_view.c000066400000000000000000000350771321245476300177770ustar00rootroot00000000000000/* * 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_3d; cvar_t *crosshair_3d_glow; cvar_t *crosshair_scale; cvar_t *cl_testparticles; cvar_t *cl_testentities; cvar_t *cl_testlights; cvar_t *cl_testblend; cvar_t *crosshair_3d_glow_r; cvar_t *crosshair_3d_glow_g; cvar_t *crosshair_3d_glow_b; 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; void V_Render3dCrosshair(void); /* * 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(); // before changing viewport we should trace the crosshair position V_Render3dCrosshair(); 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); } else if (cl.frame.valid && cl_paused->value && gl_stereo->value) { // We need to adjust the refdef in stereo mode when paused. vec3_t tmp; CL_CalcViewValues(); VectorScale( cl.v_right, stereo_separation, tmp ); VectorAdd( cl.refdef.vieworg, tmp, cl.refdef.vieworg ); 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.001; } 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_Render3dCrosshair(void) { trace_t crosshair_trace; vec3_t end; crosshair_3d = Cvar_Get("crosshair_3d", "0", CVAR_ARCHIVE); crosshair_3d_glow = Cvar_Get("crosshair_3d_glow", "0", CVAR_ARCHIVE); if(crosshair_3d->value || crosshair_3d_glow->value){ VectorMA(cl.refdef.vieworg,8192,cl.v_forward,end); crosshair_trace = CL_PMTrace(cl.refdef.vieworg, vec3_origin, vec3_origin, end); if(crosshair_3d_glow->value){ crosshair_3d_glow_r = Cvar_Get("crosshair_3d_glow_r", "5", CVAR_ARCHIVE); crosshair_3d_glow_g = Cvar_Get("crosshair_3d_glow_g", "1", CVAR_ARCHIVE); crosshair_3d_glow_b = Cvar_Get("crosshair_3d_glow_b", "4", CVAR_ARCHIVE); V_AddLight( crosshair_trace.endpos, crosshair_3d_glow->value, crosshair_3d_glow_r->value, crosshair_3d_glow_g->value, crosshair_3d_glow_b->value ); } if(crosshair_3d->value){ entity_t crosshair_ent = {0}; crosshair_ent.origin[0] = crosshair_trace.endpos[0]; crosshair_ent.origin[1] = crosshair_trace.endpos[1]; crosshair_ent.origin[2] = crosshair_trace.endpos[2]; crosshair_ent.model = R_RegisterModel("models/crosshair/tris.md2"); //crosshair_ent.skin = R_RegisterSkin("models/crosshair/skin.pcx"); AngleVectors2(crosshair_trace.plane.normal, crosshair_ent.angles); crosshair_ent.flags = RF_DEPTHHACK | RF_FULLBRIGHT | RF_NOSHADOW; V_AddEntity(&crosshair_ent); } } } 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_7_10/src/client/header/000077500000000000000000000000001321245476300174175ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/header/client.h000066400000000000000000000356431321245476300210610ustar00rootroot00000000000000/* * 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 rframetime; /* seconds since last render frame */ float nframetime; /* network frame time */ /* 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 */ qboolean forcePacket; /* Forces a package to be send at the next frame. */ 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; /*Evil hack against too many power screen and power shield impact sounds. For example if the player fires his shotgun onto a Brain. */ extern int num_power_sounds; /* cvars */ extern cvar_t *gl_stereo_separation; extern cvar_t *gl_stereo_convergence; extern cvar_t *gl_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_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 *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_CalcViewValues(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_RefreshCmd(void); void CL_SendCmd (void); void CL_RefreshMove(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); trace_t CL_PMTrace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end); #endif yquake2-QUAKE2_7_10/src/client/header/console.h000066400000000000000000000036441321245476300212410ustar00rootroot00000000000000/* * 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_7_10/src/client/header/keyboard.h000066400000000000000000000114321321245476300213710ustar00rootroot00000000000000/* * 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 #include "../../common/header/shared.h" /* for qboolean etc */ /* 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_HAT_UP, K_HAT_RIGHT, K_HAT_DOWN, K_HAT_LEFT, K_TRIG_LEFT, K_TRIG_RIGHT, /* Can't be mapped to any action */ K_JOY_BACK, 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); void Haptic_Feedback(char *name); int Key_GetMenuKey(int key); #endif yquake2-QUAKE2_7_10/src/client/header/ref.h000066400000000000000000000214711321245476300203510ustar00rootroot00000000000000/* * 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 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; /* r+g+b */ } 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; // <= 32 (MAX_DLIGHTS) dlight_t *dlights; int num_particles; particle_t *particles; } refdef_t; // FIXME: bump API_VERSION? #define API_VERSION 4 #define EXPORT #define IMPORT // // these are the functions exported by the refresh module // typedef struct { // if api_version is different, the dll cannot be used int api_version; // called when the library is loaded qboolean (EXPORT *Init) (void); // called before the library is unloaded void (EXPORT *Shutdown) (void); // called by GLimp_InitGraphics() before creating window, // returns flags for SDL window creation, returns -1 on error int (EXPORT *PrepareForWindow)(void); // called by GLimp_InitGraphics() *after* creating window, // passing the SDL_Window* (void* so we don't spill SDL.h here) // (or SDL_Surface* for SDL1.2, another reason to use void*) // returns true (1) on success int (EXPORT *InitContext)(void* sdl_window); // shuts down rendering (OpenGL) context, calls // VID_ShutdownWindow() to shut down window as well, if !contextOnly void (EXPORT *ShutdownWindow)(qboolean contextOnly); // returns true if vsync is active, else false qboolean (EXPORT *IsVSyncActive)(void); // All data that will be used in a level should be // registered before rendering any frames to prevent disk hits, // but they can still be registered at a later time // if necessary. // // EndRegistration will free any remaining data that wasn't registered. // Any model_s or skin_s pointers from before the BeginRegistration // are no longer valid after EndRegistration. // // Skins and images need to be differentiated, because skins // are flood filled to eliminate mip map edge errors, and pics have // an implicit "pics/" prepended to the name. (a pic name that starts with a // slash will not use the "pics/" prefix or the ".pcx" postfix) void (EXPORT *BeginRegistration) (char *map); struct model_s * (EXPORT *RegisterModel) (char *name); struct image_s * (EXPORT *RegisterSkin) (char *name); void (EXPORT *SetSky) (char *name, float rotate, vec3_t axis); void (EXPORT *EndRegistration) (void); void (EXPORT *RenderFrame) (refdef_t *fd); struct image_s * (EXPORT *DrawFindPic)(char *name); void (EXPORT *DrawGetPicSize) (int *w, int *h, char *name); // will return 0 0 if not found void (EXPORT *DrawPicScaled) (int x, int y, char *pic, float factor); void (EXPORT *DrawStretchPic) (int x, int y, int w, int h, char *name); void (EXPORT *DrawCharScaled)(int x, int y, int num, float scale); void (EXPORT *DrawTileClear) (int x, int y, int w, int h, char *name); void (EXPORT *DrawFill) (int x, int y, int w, int h, int c); void (EXPORT *DrawFadeScreen) (void); // Draw images for cinematic rendering (which can have a different palette). Note that calls void (EXPORT *DrawStretchRaw) (int x, int y, int w, int h, int cols, int rows, byte *data); /* ** video mode and refresh state management entry points */ void (EXPORT *SetPalette)( const unsigned char *palette); // NULL = game palette void (EXPORT *BeginFrame)( float camera_separation ); void (EXPORT *EndFrame) (void); //void (EXPORT *AppActivate)( qboolean activate ); } refexport_t; typedef struct { void (IMPORT *Sys_Error) (int err_level, char *str, ...) __attribute__ ((format (printf, 2, 3))); void (IMPORT *Cmd_AddCommand) (char *name, void(*cmd)(void)); void (IMPORT *Cmd_RemoveCommand) (char *name); int (IMPORT *Cmd_Argc) (void); char *(IMPORT *Cmd_Argv) (int i); void (IMPORT *Cmd_ExecuteText) (int exec_when, char *text); void (IMPORT *Com_VPrintf) (int print_level, const char *fmt, va_list argptr); // files will be memory mapped read only // the returned buffer may be part of a larger pak file, // or a discrete file from anywhere in the quake search path // a -1 return means the file does not exist // NULL can be passed for buf to just determine existance int (IMPORT *FS_LoadFile) (char *name, void **buf); void (IMPORT *FS_FreeFile) (void *buf); // gamedir will be the current directory that generated // files should be stored to, ie: "f:\quake\id1" char *(IMPORT *FS_Gamedir) (void); cvar_t *(IMPORT *Cvar_Get) (char *name, char *value, int flags); cvar_t *(IMPORT *Cvar_Set) (char *name, char *value); void (IMPORT *Cvar_SetValue) (char *name, float value); qboolean (IMPORT *Vid_GetModeInfo)(int *width, int *height, int mode); void (IMPORT *Vid_MenuInit)( void ); void (IMPORT *Vid_NewWindow)( int width, int height ); // called with image data of width*height pixel which comp bytes per pixel (must be 3 or 4 for RGB or RGBA) // expects the pixels data to be row-wise, starting at top left void (IMPORT *Vid_WriteScreenshot)( int width, int height, int comp, const void* data ); void (IMPORT *Vid_ShutdownWindow)(void); int (IMPORT *GLimp_Init)(void); qboolean (IMPORT *GLimp_InitGraphics)(int fullscreen, int *pwidth, int *pheight); } refimport_t; // this is the only function actually exported at the linker level typedef refexport_t (EXPORT *GetRefAPI_t) (refimport_t); // FIXME: #ifdef client/ref around this extern refexport_t re; extern refimport_t ri; /* * 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_StretchPic(int x, int y, int w, int h, char *name); void Draw_PicScaled(int x, int y, char *pic, float factor); 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 R_EndFrame(void); #endif yquake2-QUAKE2_7_10/src/client/header/screen.h000066400000000000000000000037461321245476300210610ustar00rootroot00000000000000/* * 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_7_10/src/client/header/vid.h000066400000000000000000000027421321245476300203570ustar00rootroot00000000000000/* * 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_NewWindow(int width, int height); qboolean VID_GetModeInfo(int *width, int *height, int mode); #endif yquake2-QUAKE2_7_10/src/client/menu/000077500000000000000000000000001321245476300171335ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/menu/header/000077500000000000000000000000001321245476300203635ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/menu/header/qmenu.h000066400000000000000000000056771321245476300217000ustar00rootroot00000000000000/* * 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_7_10/src/client/menu/menu.c000066400000000000000000003265521321245476300202600ustar00rootroot00000000000000/* * 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 "../sound/header/local.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 qboolean M_IsGame(const char *gamename) { cvar_t *game = Cvar_Get("game", "", CVAR_LATCH | CVAR_SERVERINFO); if (strcmp(game->string, gamename) == 0) { return true; } return false; } 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"); } #ifdef USE_OPENAL if (cl.cinematic_file && sound_started == SS_OAL) { AL_UnqueueRawSamples(); } #endif /* 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; } int Key_GetMenuKey(int key) { switch (key) { case K_KP_UPARROW: case K_UPARROW: case K_HAT_UP: return K_UPARROW; case K_TAB: case K_KP_DOWNARROW: case K_DOWNARROW: case K_HAT_DOWN: return K_DOWNARROW; case K_KP_LEFTARROW: case K_LEFTARROW: case K_HAT_LEFT: case K_TRIG_LEFT: return K_LEFTARROW; case K_KP_RIGHTARROW: case K_RIGHTARROW: case K_HAT_RIGHT: case K_TRIG_RIGHT: return K_RIGHTARROW; case K_MOUSE1: case K_MOUSE2: case K_MOUSE3: case K_MOUSE4: case K_MOUSE5: case K_JOY1: case K_JOY2: case K_JOY3: case K_JOY4: case K_JOY5: case K_JOY6: case K_JOY7: case K_JOY8: case K_JOY9: case K_JOY10: case K_JOY11: case K_JOY12: case K_JOY13: case K_JOY14: case K_JOY15: case K_JOY16: case K_JOY17: case K_JOY18: case K_JOY19: case K_JOY20: case K_JOY21: case K_JOY22: case K_JOY23: case K_JOY24: case K_JOY25: case K_JOY26: case K_JOY27: case K_JOY28: case K_JOY29: case K_JOY30: case K_JOY31: 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: return K_ENTER; case K_ESCAPE: case K_JOY_BACK: return K_ESCAPE; } return key; } const char * Default_MenuKey(menuframework_s *m, int key) { const char *sound = NULL; menucommon_s *item; int menu_key = Key_GetMenuKey(key); if (m) { if ((item = Menu_ItemAtCursor(m)) != 0) { if (item->type == MTYPE_FIELD) { if (Field_Key((menufield_s *)item, key)) { return NULL; } } } } switch (menu_key) { case K_ESCAPE: M_PopMenu(); return menu_out_sound; case K_UPARROW: if (m) { m->cursor--; Menu_AdjustCursor(m, -1); sound = menu_move_sound; } break; case K_DOWNARROW: if (m) { m->cursor++; Menu_AdjustCursor(m, 1); sound = menu_move_sound; } break; case K_LEFTARROW: if (m) { Menu_SlideItem(m, -1); sound = menu_move_sound; } break; case K_RIGHTARROW: if (m) { Menu_SlideItem(m, 1); sound = menu_move_sound; } break; 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 + ((int)(viddef.width - 320 * scale) >> 1), cy + ((int)(viddef.height - 240 * 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; int menu_key = Key_GetMenuKey(key); switch (menu_key) { case K_ESCAPE: M_PopMenu(); break; case K_DOWNARROW: if (++m_main_cursor >= MAIN_ITEMS) { m_main_cursor = 0; } return sound; case K_UPARROW: if (--m_main_cursor < 0) { m_main_cursor = MAIN_ITEMS - 1; } return sound; 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_lookstrafe_box; static menulist_s s_options_crosshair_box; static menuslider_s s_options_sfxvolume_slider; #ifdef SDL2 static menuslider_s s_options_haptic_slider; #endif #if defined(OGG) || defined(CDA) static menulist_s s_options_cdshuffle_box; #endif #ifdef OGG static menuslider_s s_options_oggvolume_slider; 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); } #ifdef SDL2 static void HapticMagnitudeFunc(void *unused) { Cvar_SetValue("joy_haptic_magnitude", s_options_haptic_slider.curvalue / 10.0F); } #endif 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; #if defined(OGG) || defined(CDA) s_options_cdshuffle_box.curvalue = (Cvar_VariableValue("cd_shuffle") != 0); #endif #ifdef OGG s_options_oggvolume_slider.curvalue = Cvar_VariableValue("ogg_volume") * 10; 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_lookstrafe_box.curvalue = (lookstrafe->value != 0); s_options_freelook_box.curvalue = (freelook->value != 0); s_options_crosshair_box.curvalue = ClampCvar(0, 3, crosshair->value); #ifdef SDL2 s_options_haptic_slider.curvalue = Cvar_VariableValue("joy_haptic_magnitude") * 10.0F; #endif } 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 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("?"); } } else { Cvar_Set("ogg_sequence", "loop"); if (ogg->value) { if ((int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10) < 10) { char tmp[3] = {'0', cl.configstrings[CS_CDTRACK][0], '\0'}; OGG_ParseCmd(tmp); } else { OGG_ParseCmd(cl.configstrings[CS_CDTRACK]); } } } #endif } #endif #ifdef OGG static void UpdateOggVolumeFunc(void *unused) { Cvar_SetValue("ogg_volume", s_options_oggvolume_slider.curvalue / 10); } 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 */ R_EndFrame(); CL_Snd_Restart_f(); } static void Options_MenuInit(void) { #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(); #ifdef SDL2 extern qboolean show_haptic; #endif /* 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 OGG s_options_oggvolume_slider.generic.type = MTYPE_SLIDER; s_options_oggvolume_slider.generic.x = 0; s_options_oggvolume_slider.generic.y = 10; s_options_oggvolume_slider.generic.name = "OGG volume"; s_options_oggvolume_slider.generic.callback = UpdateOggVolumeFunc; s_options_oggvolume_slider.minvalue = 0; s_options_oggvolume_slider.maxvalue = 10; 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_lookstrafe_box.generic.type = MTYPE_SPINCONTROL; s_options_lookstrafe_box.generic.x = 0; s_options_lookstrafe_box.generic.y = 90; 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 = 100; 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 = 110; s_options_crosshair_box.generic.name = "crosshair"; s_options_crosshair_box.generic.callback = CrosshairFunc; s_options_crosshair_box.itemnames = crosshair_names; #ifdef SDL2 s_options_haptic_slider.generic.type = MTYPE_SLIDER; s_options_haptic_slider.generic.x = 0; s_options_haptic_slider.generic.y = 120; s_options_haptic_slider.generic.name = "haptic magnitude"; s_options_haptic_slider.generic.callback = HapticMagnitudeFunc; s_options_haptic_slider.minvalue = 0; s_options_haptic_slider.maxvalue = 22; #endif 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 OGG Menu_AddItem(&s_options_menu, (void *)&s_options_oggvolume_slider); 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_lookstrafe_box); Menu_AddItem(&s_options_menu, (void *)&s_options_freelook_box); Menu_AddItem(&s_options_menu, (void *)&s_options_crosshair_box); #ifdef SDL2 if (show_haptic) Menu_AddItem(&s_options_menu, (void *)&s_options_haptic_slider); #endif 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", "Jennell 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", "Jennell 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 SYSTEMS", "+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", "Jennell 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; } static void M_Menu_Credits_f(void) { int n; int count; char *p; 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 { if (M_IsGame("xatrix")) /* Xatrix - The Reckoning */ { credits = xatcredits; } else if (M_IsGame("rogue")) /* 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; int menu_key = Key_GetMenuKey(key); switch (menu_key) { case K_UPARROW: if (m->cursor == 0) { LoadSave_AdjustPage(-1); LoadGame_MenuInit(); } break; case K_DOWNARROW: if (m->cursor == m->nitems - 1) { LoadSave_AdjustPage(1); LoadGame_MenuInit(); } break; case K_LEFTARROW: LoadSave_AdjustPage(-1); LoadGame_MenuInit(); return menu_move_sound; 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; int menu_key = Key_GetMenuKey(key); if (m_popup_string) { m_popup_string = NULL; return NULL; } switch (menu_key) { case K_UPARROW: if (m->cursor == 0) { LoadSave_AdjustPage(-1); SaveGame_MenuInit(); } break; case K_DOWNARROW: if (m->cursor == m->nitems - 1) { LoadSave_AdjustPage(1); SaveGame_MenuInit(); } break; case K_LEFTARROW: LoadSave_AdjustPage(-1); SaveGame_MenuInit(); return menu_move_sound; 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 */ R_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..."; s_joinserver_server_title.generic.y = 30; s_joinserver_server_title.generic.x = 80 * scale; 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 (M_IsGame("rogue")) { 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) || M_IsGame("rogue")) { 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 *s; int length; int i; 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 */ if ((length = FS_LoadFile("maps.lst", (void **)&buffer)) == -1) { Com_Error(ERR_DROP, "couldn't find maps.lst\n"); } 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; 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 (M_IsGame("rogue")) { 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 (M_IsGame("rogue")) { 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 (M_IsGame("rogue")) { 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 (M_IsGame("rogueg")) { 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 / 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 scratch[1024]; int ndirs = 0, npms = 0; char **dirnames = NULL; int i; s_numplayermodels = 0; /* get a list of directories */ if ((dirnames = FS_ListFiles2("players/*", &ndirs, SFF_SUBDIR, 0)) == NULL) { 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; fileHandle_t f; 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 (FS_FOpenFile(scratch, &f, false) == 0) { free(dirnames[i]); dirnames[i] = 0; continue; } else { FS_FCloseFile(f); } /* verify the existence of at least one pcx skin */ strcpy(scratch, dirnames[i]); strcat(scratch, "/*.pcx"); if ((pcxnames = FS_ListFiles2(scratch, &npcxfiles, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM)) == NULL) { 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) { int menu_key = Key_GetMenuKey(key); switch (menu_key) { case K_ESCAPE: case 'n': case 'N': M_PopMenu(); break; case K_ENTER: 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_7_10/src/client/menu/qmenu.c000066400000000000000000000365051321245476300204350ustar00rootroot00000000000000/* * 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_7_10/src/client/menu/videomenu.c000066400000000000000000000306011321245476300212720ustar00rootroot00000000000000/* * 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" #include "header/qmenu.h" extern void M_ForceMenuOff(void); static cvar_t *gl_mode; static cvar_t *gl_hudscale; static cvar_t *gl_consolescale; static cvar_t *gl_menuscale; static cvar_t *crosshair_scale; static cvar_t *fov; extern cvar_t *scr_viewsize; extern cvar_t *vid_gamma; extern cvar_t *vid_fullscreen; extern cvar_t *vid_renderer; 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_renderer_list; static menulist_s s_mode_list; static menulist_s s_uiscale_list; static menuslider_s s_brightness_slider; static menuslider_s s_fov_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 int GetRenderer(void) { /* First element in array is 'OpenGL 1.4' aka gl1. Second element in array is 'OpenGL 3.2' aka gl3. Third element in array is unknown renderer. */ if (Q_stricmp(vid_renderer->string, "gl1") == 0) { return 0; } else if (Q_stricmp(vid_renderer->string, "gl3") == 0) { return 1; } else { return 2; } } static int GetCustomValue(menulist_s *list) { static menulist_s *last; static int i; if (list != last) { last = list; i = list->curvalue; do { i++; } while (list->itemnames[i]); i--; } return i; } static void BrightnessCallback(void *s) { menuslider_s *slider = (menuslider_s *)s; float gamma = slider->curvalue / 10.0; Cvar_SetValue("vid_gamma", gamma); } static void FOVCallback(void *s) { menuslider_s *slider = (menuslider_s *)s; Cvar_SetValue("fov", slider->curvalue); } 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; /* Renderer */ if (s_renderer_list.curvalue != GetRenderer()) { /* First element in array is 'OpenGL 1.4' aka gl1. Second element in array is 'OpenGL 3.2' aka gl3. Third element in array is unknown renderer. */ if (s_renderer_list.curvalue == 0) { Cvar_Set("vid_renderer", "gl1"); restart = true; } else if (s_renderer_list.curvalue == 1) { Cvar_Set("vid_renderer", "gl3"); restart = true; } } /* custom mode */ if (s_mode_list.curvalue != GetCustomValue(&s_mode_list)) { /* Restarts automatically */ Cvar_SetValue("gl_mode", s_mode_list.curvalue); } else { /* Restarts automatically */ Cvar_SetValue("gl_mode", -1); } /* UI scaling */ if (s_uiscale_list.curvalue == 0) { Cvar_SetValue("gl_hudscale", -1); } else if (s_uiscale_list.curvalue < GetCustomValue(&s_uiscale_list)) { Cvar_SetValue("gl_hudscale", s_uiscale_list.curvalue); } if (s_uiscale_list.curvalue != GetCustomValue(&s_uiscale_list)) { Cvar_SetValue("gl_consolescale", gl_hudscale->value); Cvar_SetValue("gl_menuscale", gl_hudscale->value); Cvar_SetValue("crosshair_scale", gl_hudscale->value); } /* 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 *renderers[] = { "[OpenGL 1.4]", "[OpenGL 3.2]", "[Custom ]", 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 720 ]", "[1280 960 ]", "[1280 1024 ]", "[1366 768 ]", "[1440 900 ]", "[1600 1200 ]", "[1680 1050 ]", "[1920 1080 ]", "[1920 1200 ]", "[2048 1536 ]", "[2560x1080 ]", "[2560x1440 ]", "[2560x1600 ]", "[3440x1440 ]", "[3840x1600 ]", "[3840x2160 ]", "[4096x2160 ]", "[5120x2880 ]", "[custom ]", 0 }; static const char *uiscale_names[] = { "auto", "1x", "2x", "3x", "4x", "5x", "6x", "custom", 0 }; static const char *yesno_names[] = { "no", "yes", 0 }; static const char *fullscreen_names[] = { "no", "keep resolution", "switch resolution", 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 (!gl_consolescale) { gl_consolescale = Cvar_Get("gl_consolescale", "-1", CVAR_ARCHIVE); } if (!gl_menuscale) { gl_menuscale = Cvar_Get("gl_menuscale", "-1", CVAR_ARCHIVE); } if (!crosshair_scale) { crosshair_scale = Cvar_Get("crosshair_scale", "-1", CVAR_ARCHIVE); } if (!fov) { fov = Cvar_Get("fov", "90", CVAR_USERINFO | CVAR_ARCHIVE); } if (!vid_gamma) { vid_gamma = Cvar_Get("vid_gamma", "1.2", CVAR_ARCHIVE); } if (!vid_renderer) { vid_renderer = Cvar_Get("vid_renderer", "gl1", 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_renderer_list.generic.type = MTYPE_SPINCONTROL; s_renderer_list.generic.name = "renderer"; s_renderer_list.generic.x = 0; s_renderer_list.generic.y = (y = 0); s_renderer_list.itemnames = renderers; s_renderer_list.curvalue = GetRenderer(); 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 += 10); s_mode_list.itemnames = resolutions; if (gl_mode->value >= 0) { s_mode_list.curvalue = gl_mode->value; } else { s_mode_list.curvalue = GetCustomValue(&s_mode_list); } 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 += 20); 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_fov_slider.generic.type = MTYPE_SLIDER; s_fov_slider.generic.x = 0; s_fov_slider.generic.y = (y += 10); s_fov_slider.generic.name = "field of view"; s_fov_slider.generic.callback = FOVCallback; s_fov_slider.minvalue = 60; s_fov_slider.maxvalue = 120; s_fov_slider.curvalue = fov->value; s_uiscale_list.generic.type = MTYPE_SPINCONTROL; s_uiscale_list.generic.name = "ui scale"; s_uiscale_list.generic.x = 0; s_uiscale_list.generic.y = (y += 10); s_uiscale_list.itemnames = uiscale_names; if (gl_hudscale->value != gl_consolescale->value || gl_hudscale->value != gl_menuscale->value || gl_hudscale->value != crosshair_scale->value) { s_uiscale_list.curvalue = GetCustomValue(&s_uiscale_list); } else if (gl_hudscale->value < 0) { s_uiscale_list.curvalue = 0; } else if (gl_hudscale->value > 0 && gl_hudscale->value < GetCustomValue(&s_uiscale_list) && gl_hudscale->value == (int)gl_hudscale->value) { s_uiscale_list.curvalue = gl_hudscale->value; } else { s_uiscale_list.curvalue = GetCustomValue(&s_uiscale_list); } 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 = fullscreen_names; s_fs_box.curvalue = (int)vid_fullscreen->value; 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 += 10); 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_renderer_list); Menu_AddItem(&s_opengl_menu, (void *)&s_mode_list); Menu_AddItem(&s_opengl_menu, (void *)&s_brightness_slider); Menu_AddItem(&s_opengl_menu, (void *)&s_fov_slider); Menu_AddItem(&s_opengl_menu, (void *)&s_uiscale_list); 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"; int menu_key = Key_GetMenuKey(key); switch (menu_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_7_10/src/client/refresh/000077500000000000000000000000001321245476300176255ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/refresh/constants/000077500000000000000000000000001321245476300216415ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/refresh/constants/anorms.h000066400000000000000000000150451321245476300233160ustar00rootroot00000000000000/* * 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_7_10/src/client/refresh/constants/anormtab.h000066400000000000000000000631341321245476300236240ustar00rootroot00000000000000/* * 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_7_10/src/client/refresh/constants/warpsin.h000066400000000000000000000076501321245476300235050ustar00rootroot00000000000000/* * 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 * * ======================================================================= */ #if 0 // In case you wonder how these were generated/what they mean: for (int i = 0; i < 256; i++) { r_turbsin[i] = 8.0 * sin( (float)i / TURBSCALE ); // TURBSCALE = 256.0 / (2.0 * M_PI) } // so r_turbsin[i] is 8 * sin( i/TURBSCALE ); // however, the values are multiplied with 0.5 in RI_Init() // so what's actually used is 4 * sin(i/TURBSCALE) // in R_EmitWaterPolys() something like s = os + r_turbsin [ (int) ( ( ot * 0.125 + r_newrefdef.time ) * TURBSCALE ) & 255 ]; // is used; which should (except for rounding errors from lookup table) be equivalent to s = os + 4.0*sin( ot*0.125 + r_newrefdef.time ); #endif // 0 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_7_10/src/client/refresh/files/000077500000000000000000000000001321245476300207275ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/refresh/files/pcx.c000066400000000000000000000172011321245476300216660ustar00rootroot00000000000000/* * 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 "../ref_shared.h" // Fix Jennell Jaquays' name in the Quitscreen // this is 98x11 pixels, each value an index // into the standard baseq2/pak0/pics/quit.pcx colormap static unsigned char quitscreenfix[] = { 191,191,191,47,28,39,4,4,39,1,47,28,47,28,29,1, 28,28,47,31,31,1,29,31,1,28,47,47,47,47,29,28, 47,31,30,28,40,40,4,28,28,40,39,40,29,102,102,245, 28,39,4,4,39,103,40,40,1,1,102,94,47,47,1,94, 94,94,94,47,102,245,103,103,103,47,1,102,1,102,29,29, 29,29,47,28,245,31,31,31,47,1,28,1,28,47,1,102, 102,102, 191,191,142,47,4,8,8,8,8,4,47,28,1,28,29,28, 29,29,31,1,47,245,47,47,28,28,31,47,28,1,31,1, 1,245,47,39,8,8,8,40,39,8,8,8,39,1,1,47, 4,8,8,8,8,4,47,29,28,31,28,28,29,28,28,28, 29,28,31,28,47,29,1,28,31,47,1,28,1,1,29,29, 29,47,28,1,28,28,245,28,28,28,28,47,29,28,47,102,102,103, 191,191,142,31,29,36,8,8,36,31,40,39,40,4,1,1, 39,40,39,40,40,31,28,40,40,4,39,40,28,47,31,40, 39,40,4,1,36,8,8,4,47,36,8,8,39,1,1,1, 29,36,8,8,36,4,4,39,40,4,47,1,47,40,40,39, 39,40,28,40,40,47,45,39,40,28,4,39,40,4,39,1, 28,4,40,28,28,4,39,28,47,40,40,39,40,39,28,28,1,103, 1,142,29,142,28,39,8,8,36,36,8,8,8,8,36,1, 8,8,8,8,8,36,39,8,8,8,8,8,36,40,36,8, 8,8,8,36,40,8,8,40,1,4,8,8,40,1,1,31, 28,39,8,8,36,8,8,8,8,8,36,31,36,8,8,8, 8,8,36,8,8,4,40,8,8,36,8,8,8,8,8,36, 40,8,8,40,39,8,8,40,36,8,8,8,8,8,39,29,28,29, 103,191,142,47,28,40,8,8,40,8,8,33,33,8,8,36, 8,8,36,36,8,8,36,8,8,36,36,8,8,36,8,8, 33,33,8,8,36,8,8,4,47,40,8,8,39,47,28,245, 28,40,8,8,40,40,36,36,33,8,8,36,8,8,36,36, 8,8,36,8,8,40,40,8,8,40,4,36,36,33,8,8, 36,8,8,39,39,8,8,36,8,8,33,36,36,39,28,1,47,28, 103,246,1,47,1,39,8,8,40,8,8,8,8,8,8,36, 8,8,4,40,8,8,36,8,8,40,4,8,8,36,8,8, 8,8,8,8,36,8,8,40,29,39,8,8,39,1,1,47, 1,39,8,8,40,36,8,8,8,8,8,36,8,8,4,40, 8,8,36,8,8,40,39,8,8,40,36,8,8,8,8,8, 36,8,8,39,40,8,8,40,36,8,8,8,8,36,28,1,1,29, 103,47,40,40,4,36,8,8,36,8,8,33,36,36,36,4, 8,8,39,4,8,8,36,8,8,4,40,8,8,36,8,8, 33,36,36,36,36,8,8,40,31,40,8,8,40,47,40,40, 4,36,8,8,36,8,8,33,33,8,8,36,8,8,36,36, 8,8,36,8,8,36,36,8,8,36,8,8,33,33,8,8, 36,8,8,36,36,8,8,4,39,36,36,33,8,8,4,40,4,31, 191,40,8,8,8,8,8,36,29,36,8,8,8,8,8,40, 8,8,40,4,8,8,36,8,8,40,39,8,8,39,36,8, 8,8,8,8,39,8,8,39,45,4,8,8,40,40,8,8, 8,8,8,36,29,36,8,8,8,8,8,40,36,8,8,8, 8,8,40,36,8,8,8,8,8,40,36,8,8,8,8,8, 40,36,8,8,8,8,8,36,8,8,8,8,8,36,4,8,8,4, 47,45,40,39,40,39,39,245,246,1,40,40,40,39,4,47, 40,4,28,29,39,40,30,39,39,1,28,40,4,28,1,40, 40,40,39,4,29,40,39,1,1,1,4,4,47,45,40,39, 40,39,39,245,246,29,39,40,40,40,4,47,28,39,39,36, 8,8,4,1,39,40,4,40,40,1,29,4,39,4,40,39, 1,39,36,36,33,8,8,4,39,4,39,4,40,47,36,8,8,40, 1,28,47,28,28,29,1,28,47,28,31,28,28,27,47,28, 45,246,30,28,245,29,47,47,29,30,28,47,27,1,246,47, 47,47,1,28,47,28,47,1,47,47,1,29,29,47,47,28, 28,29,1,47,1,47,47,28,31,47,47,31,47,47,47,4, 8,8,39,245,1,47,28,245,28,47,31,28,47,28,28,28, 40,8,8,8,8,8,36,47,28,1,246,47,1,40,8,8,36,1, 47,1,102,1,102,102,47,94,94,102,47,47,102,102,102,102, 94,1,94,47,102,1,102,47,30,30,102,27,47,102,94,1, 102,47,1,94,102,103,1,102,103,103,47,47,47,29,1,29, 28,28,29,28,1,47,28,31,29,1,47,29,28,1,1,47, 4,39,1,47,47,1,28,28,28,47,1,28,45,28,47,47, 1,40,4,4,40,4,29,28,31,45,47,28,47,47,4,40,28,28 }; static void fixQuitScreen(byte* px) { // overwrite 11 lines, 98 pixels each, from quitscreenfix[] // starting at line 140, column 188 // quitscreen is 320x240 px int r, qsIdx = 0; px += 140*320; // go to line 140 px += 188; // to colum 188 for(r=0; r<11; ++r) { memcpy(px, quitscreenfix+qsIdx, 98); qsIdx += 98; px += 320; } } void LoadPCX(char *origname, byte **pic, byte **palette, int *width, int *height) { byte *raw; pcx_t *pcx; int x, y; int len, full_size; int pcx_width, pcx_height; qboolean image_issues = false; 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; if (palette) { *palette = NULL; } /* load the file */ len = ri.FS_LoadFile(filename, (void **)&raw); if (!raw || len < sizeof(pcx_t)) { R_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; pcx_width = pcx->xmax - pcx->xmin; pcx_height = pcx->ymax - pcx->ymin; if ((pcx->manufacturer != 0x0a) || (pcx->version != 5) || (pcx->encoding != 1) || (pcx->bits_per_pixel != 8) || (pcx_width >= 4096) || (pcx_height >= 4096)) { R_Printf(PRINT_ALL, "Bad pcx file %s\n", filename); return; } full_size = (pcx_height + 1) * (pcx_width + 1); out = malloc(full_size); *pic = out; pix = out; if (palette) { *palette = malloc(768); if (len > 768) { memcpy(*palette, (byte *)pcx + len - 768, 768); } else { image_issues = true; } } if (width) { *width = pcx_width + 1; } if (height) { *height = pcx_height + 1; } for (y = 0; y <= pcx_height; y++, pix += pcx_width + 1) { for (x = 0; x <= pcx_width; ) { if (raw - (byte *)pcx > len) { // no place for read image_issues = true; x = pcx_width; break; } dataByte = *raw++; if ((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; if (raw - (byte *)pcx > len) { // no place for read image_issues = true; x = pcx_width; break; } dataByte = *raw++; } else { runLength = 1; } while (runLength-- > 0) { if ((*pic + full_size) <= (pix + x)) { // no place for write image_issues = true; x += runLength; runLength = 0; } else { pix[x++] = dataByte; } } } } if (raw - (byte *)pcx > len) { R_Printf(PRINT_DEVELOPER, "PCX file %s was malformed", filename); free(*pic); *pic = NULL; } else if(pcx_width == 319 && pcx_height == 239 && Q_strcasecmp(origname, "pics/quit.pcx") == 0 && Com_BlockChecksum(pcx, len) == 3329419434u) { // it's the quit screen, and the baseq2 one (identified by checksum) // so fix it fixQuitScreen(*pic); } if (image_issues) { R_Printf(PRINT_ALL, "PCX file %s has possible size issues.\n", filename); } ri.FS_FreeFile(pcx); } void GetPCXInfo(char *filename, int *width, int *height) { pcx_t *pcx; byte *raw; ri.FS_LoadFile(filename, (void **)&raw); if (!raw) { return; } pcx = (pcx_t *)raw; *width = pcx->xmax + 1; *height = pcx->ymax + 1; ri.FS_FreeFile(raw); return; } yquake2-QUAKE2_7_10/src/client/refresh/files/stb.c000066400000000000000000000051521321245476300216660ustar00rootroot00000000000000/* * 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 "../gl/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 = ri.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) { R_Printf(PRINT_ALL, "stb_image couldn't load data from %s: %s!\n", filename, stbi_failure_reason()); ri.FS_FreeFile(rawdata); return false; } ri.FS_FreeFile(rawdata); R_Printf(PRINT_DEVELOPER, "LoadSTB() loaded: %s\n", filename); *pic = data; *width = w; *height = h; return true; } yquake2-QUAKE2_7_10/src/client/refresh/files/stb_image.h000066400000000000000000007517551321245476300230560ustar00rootroot00000000000000/* stb_image - v2.16 - 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/16-bit-per-channel TGA (not sure what subset, if a subset) BMP non-1bpp, non-RLE PSD (composited view only, no extra channels, 8/16 bit-per-channel) GIF (*comp always reports as 4-channel) HDR (radiance rgbE format) PIC (Softimage PIC) PNM (PPM and PGM binary only) Animated GIF still needs a proper API, but here's one way to do it: http://gist.github.com/urraka/685d9a6340b26b830d49 - 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. LICENSE See end of file for license information. RECENT REVISION HISTORY: 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 RGB-format JPEG; remove white matting in PSD; allocate large structures on the stack; correct channel count for PNG & BMP 2.10 (2016-01-22) avoid warning introduced in 2.09 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED See end of file for full revision history. ============================ Contributors ========================= Image formats Extensions, features Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) github:urraka (animated gif) Junggon Kim (PNM comments) Daniel Gibson (16-bit TGA) socks-the-fox (16-bit PNG) Jeremy Sawicki (handle all ImageNet JPGs) Optimizations & bugfixes Fabian "ryg" Giesen Arseny Kapoulkine John-Mark Allen Bug & warning fixes Marc LeBlanc David Woo Guillaume George Martins Mozeiko Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan Dave Moore Roy Eltham Hayaki Saito Nathan Reed Won Chun Luke Graham Johan Duparc Nick Verigakis the Horde3D community Thomas Ruf Ronny Chevalier Baldur Karlsson Janez Zemva John Bartholomew Michal Cichon github:rlyeh Jonathan Blow Ken Hamada Tero Hanninen github:romigrou Laurent Gomila Cort Stratton Sergio Gonzalez github:svdijk Aruelien Pocheville Thibault Reuille Cass Everitt github:snagar Ryamond Barbiero Paul Du Bois Engin Manap github:Zelex Michaelangel007@github Philipp Wiesemann Dale Weiler github:grim210 Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:sammyhw Blazej Dariusz Roszkowski Gregory Mullen github:phprus Christian Floisand Kevin Schmidt github:poppolopoppo */ #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 *channels_in_file -- outputs # of image components in image file // int desired_channels -- 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 'desired_channels' if desired_channels is non-zero, or // *channels_in_file otherwise. If desired_channels is non-zero, // *channels_in_file has the number of components that _would_ have been // output otherwise. E.g. if you set desired_channels to 4, you will always // get RGBA output, but you can check *channels_in_file 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, *channels_in_file 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 source code 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. // // 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). // // =========================================================================== // // ADDITIONAL CONFIGURATION // // - 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) // // - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still // want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB // #ifndef STBI_NO_STDIO #include #endif // STBI_NO_STDIO #define STBI_VERSION 1 enum { STBI_default = 0, // only used for desired_channels STBI_grey = 1, STBI_grey_alpha = 2, STBI_rgb = 3, STBI_rgb_alpha = 4 }; typedef unsigned char stbi_uc; typedef unsigned short stbi_us; #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; //////////////////////////////////// // // 8-bits-per-channel interface // STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); // for stbi_load_from_file, file pointer is left pointing immediately after image #endif //////////////////////////////////// // // 16-bits-per-channel interface // STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif //////////////////////////////////// // // float-per-channel interface // #ifndef STBI_NO_LINEAR STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #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 // STBI_NO_HDR #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_LINEAR // 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); // flip the image vertically, so the first pixel in the output array is the bottom left STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); // 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 #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) || defined(STBI_REALLOC_SIZED)) // ok #elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) // ok #else #error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." #endif #ifndef STBI_MALLOC #define STBI_MALLOC(sz) malloc(sz) #define STBI_REALLOC(p,newsz) realloc(p,newsz) #define STBI_FREE(p) free(p) #endif #ifndef STBI_REALLOC_SIZED #define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) #endif // x86/x64 detection #if defined(__x86_64__) || defined(_M_X64) #define STBI__X64_TARGET #elif defined(__i386) || defined(_M_IX86) #define STBI__X86_TARGET #endif #if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) // gcc doesn't support sse2 intrinsics unless you compile with -msse2, // which in turn means it gets to use SSE2 everywhere. This is unfortunate, // but previous attempts to provide the SSE2 functions with runtime // detection caused numerous issues. The way architecture extensions are // exposed in GCC/Clang is, sadly, not really suited for one-file libs. // New behavior: if compiled with -msse2, we use SSE2 without any // detection; if not, we don't use it at all. #define STBI_NO_SIMD #endif #if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) // Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET // // 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the // Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. // As a result, enabling SSE2 on 32-bit MinGW is dangerous when not // simultaneously enabling "-mstackrealign". // // See https://github.com/nothings/stb/issues/81 for more information. // // So default to no SSE2 on 32-bit MinGW. If you've read this far and added // -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. #define STBI_NO_SIMD #endif #if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) #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(void) { 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(void) { // If we're even attempting to compile this on GCC/Clang, that means // -msse2 is on, which means the compiler is allowed to use SSE2 // instructions at will, and so are we. return 1; } #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, *img_buffer_original_end; } 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 = s->img_buffer_original_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); s->img_buffer_original_end = s->img_buffer_end; } #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; s->img_buffer_end = s->img_buffer_original_end; } enum { STBI_ORDER_RGB, STBI_ORDER_BGR }; typedef struct { int bits_per_channel; int num_channels; int channel_order; } stbi__result_info; #ifndef STBI_NO_JPEG static int stbi__jpeg_test(stbi__context *s); static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); 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 void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); 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 void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); 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 void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); 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 void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); 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, stbi__result_info *ri); 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 void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); 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 void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); 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 void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); 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); } // stb_image uses ints pervasively, including for offset calculations. // therefore the largest decoded image size we can support with the // current code, even on 64-bit targets, is INT_MAX. this is not a // significant limitation for the intended use case. // // we do, however, need to make sure our size calculations don't // overflow. hence a few helper functions for size calculations that // multiply integers together, making sure that they're non-negative // and no overflow occurs. // return 1 if the sum is valid, 0 on overflow. // negative terms are considered invalid. static int stbi__addsizes_valid(int a, int b) { if (b < 0) return 0; // now 0 <= b <= INT_MAX, hence also // 0 <= INT_MAX - b <= INTMAX. // And "a + b <= INT_MAX" (which might overflow) is the // same as a <= INT_MAX - b (no overflow) return a <= INT_MAX - b; } // returns 1 if the product is valid, 0 on overflow. // negative factors are considered invalid. static int stbi__mul2sizes_valid(int a, int b) { if (a < 0 || b < 0) return 0; if (b == 0) return 1; // mul-by-0 is always safe // portable way to check for no overflows in a*b return a <= INT_MAX/b; } // returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow static int stbi__mad2sizes_valid(int a, int b, int add) { return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); } // returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow static int stbi__mad3sizes_valid(int a, int b, int c, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && stbi__addsizes_valid(a*b*c, add); } // returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); } // mallocs with size overflow checking static void *stbi__malloc_mad2(int a, int b, int add) { if (!stbi__mad2sizes_valid(a, b, add)) return NULL; return stbi__malloc(a*b + add); } static void *stbi__malloc_mad3(int a, int b, int c, int add) { if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; return stbi__malloc(a*b*c + add); } static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) { if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; return stbi__malloc(a*b*c*d + add); } // 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 *)(size_t) (stbi__err(x,y)?NULL:NULL)) #define stbi__errpuc(x,y) ((unsigned char *)(size_t) (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 int stbi__vertically_flip_on_load = 0; STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { stbi__vertically_flip_on_load = flag_true_if_should_flip; } static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order ri->num_channels = 0; #ifndef STBI_NO_JPEG if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNG if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_BMP if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_GIF if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PSD if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); #endif #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNM if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); 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, ri); #endif return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) { int i; int img_len = w * h * channels; stbi_uc *reduced; reduced = (stbi_uc *) stbi__malloc(img_len); if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); for (i = 0; i < img_len; ++i) reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling STBI_FREE(orig); return reduced; } static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) { int i; int img_len = w * h * channels; stbi__uint16 *enlarged; enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); for (i = 0; i < img_len; ++i) enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff STBI_FREE(orig); return enlarged; } static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) { int row; size_t bytes_per_row = (size_t)w * bytes_per_pixel; stbi_uc temp[2048]; stbi_uc *bytes = (stbi_uc *)image; for (row = 0; row < (h>>1); row++) { stbi_uc *row0 = bytes + row*bytes_per_row; stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; // swap row0 with row1 size_t bytes_left = bytes_per_row; while (bytes_left) { size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); memcpy(temp, row0, bytes_copy); memcpy(row0, row1, bytes_copy); memcpy(row1, temp, bytes_copy); row0 += bytes_copy; row1 += bytes_copy; bytes_left -= bytes_copy; } } } static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi__result_info ri; void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); if (result == NULL) return NULL; if (ri.bits_per_channel != 8) { STBI_ASSERT(ri.bits_per_channel == 16); result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 8; } // @TODO: move stbi__convert_format to here if (stbi__vertically_flip_on_load) { int channels = req_comp ? req_comp : *comp; stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); } return (unsigned char *) result; } static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi__result_info ri; void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); if (result == NULL) return NULL; if (ri.bits_per_channel != 16) { STBI_ASSERT(ri.bits_per_channel == 8); result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 16; } // @TODO: move stbi__convert_format16 to here // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision if (stbi__vertically_flip_on_load) { int channels = req_comp ? req_comp : *comp; stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); } return (stbi__uint16 *) result; } #ifndef STBI_NO_HDR static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { int channels = req_comp ? req_comp : *comp; stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); } } #endif #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_and_postprocess_8bit(&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; } STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) { stbi__uint16 *result; stbi__context s; stbi__start_file(&s,f); result = stbi__load_and_postprocess_16bit(&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; } STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) { FILE *f = stbi__fopen(filename, "rb"); stbi__uint16 *result; if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); result = stbi_load_from_file_16(f,x,y,comp,req_comp); fclose(f); return result; } #endif //!STBI_NO_STDIO STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); } STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); } 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_and_postprocess_8bit(&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_and_postprocess_8bit(&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)) { stbi__result_info ri; float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); if (hdr_data) stbi__float_postprocess(hdr_data,x,y,comp,req_comp); return hdr_data; } #endif data = stbi__load_and_postprocess_8bit(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 STBI_NOTUSED(f); 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 STBI_NOTUSED(clbk); STBI_NOTUSED(user); return 0; #endif } #ifndef STBI_NO_LINEAR static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; 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 static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; 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 (n < 0) { s->img_buffer = s->img_buffer_end; return; } 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); } #if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) // nothing #else static int stbi__get16le(stbi__context *s) { int z = stbi__get8(s); return z + (stbi__get8(s) << 8); } #endif #ifndef STBI_NO_BMP static stbi__uint32 stbi__get32le(stbi__context *s) { stbi__uint32 z = stbi__get16le(s); return z + (stbi__get16le(s) << 16); } #endif #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_mad3(req_comp, x, y, 0); 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 STBI__COMBO(a,b) ((a)*8+(b)) #define STBI__CASE(a,b) case STBI__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 (STBI__COMBO(img_n, req_comp)) { STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; default: STBI_ASSERT(0); } #undef STBI__CASE } STBI_FREE(data); return good; } static stbi__uint16 stbi__compute_y_16(int r, int g, int b) { return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); } static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; stbi__uint16 *good; if (req_comp == img_n) return data; STBI_ASSERT(req_comp >= 1 && req_comp <= 4); good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); if (good == NULL) { STBI_FREE(data); return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); } for (j=0; j < (int) y; ++j) { stbi__uint16 *src = data + j * x * img_n ; stbi__uint16 *dest = good + j * x * req_comp; #define STBI__COMBO(a,b) ((a)*8+(b)) #define STBI__CASE(a,b) case STBI__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 (STBI__COMBO(img_n, req_comp)) { STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; default: STBI_ASSERT(0); } #undef STBI__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; if (!data) return NULL; output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); 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; if (!data) return NULL; output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); 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__uint16 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 jfif; int app14_color_transform; // Adobe APP14 tag int rgb; 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 += (~0U << 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); while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes 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); STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); 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__uint16 *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=15 s=0 should write 16 0s, so we just do // a run of 15 0s and then write s (which is 0), // so we don't have to do anything special here } } 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; } } else { if (r == 0) { *p = (short) s; break; } --r; } } } 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); // consume repeated 0xff fill bytes 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 = j->img_comp[3].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__uint16 *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, sixteen = (p != 0); int t = q & 15,i; if (p != 0 && p != 1) 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__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); L -= (sixteen ? 129 : 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) { L = stbi__get16be(z->s); if (L < 2) { if (m == 0xFE) return stbi__err("bad COM len","Corrupt JPEG"); else return stbi__err("bad APP len","Corrupt JPEG"); } L -= 2; if (m == 0xE0 && L >= 5) { // JFIF APP0 segment static const unsigned char tag[5] = {'J','F','I','F','\0'}; int ok = 1; int i; for (i=0; i < 5; ++i) if (stbi__get8(z->s) != tag[i]) ok = 0; L -= 5; if (ok) z->jfif = 1; } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; int ok = 1; int i; for (i=0; i < 6; ++i) if (stbi__get8(z->s) != tag[i]) ok = 0; L -= 6; if (ok) { stbi__get8(z->s); // version stbi__get16be(z->s); // flags0 stbi__get16be(z->s); // flags1 z->app14_color_transform = stbi__get8(z->s); // color transform L -= 6; } } stbi__skip(z->s, L); return 1; } return stbi__err("unknown marker","Corrupt JPEG"); } // 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__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) { int i; for (i=0; i < ncomp; ++i) { if (z->img_comp[i].raw_data) { STBI_FREE(z->img_comp[i].raw_data); z->img_comp[i].raw_data = NULL; z->img_comp[i].data = NULL; } if (z->img_comp[i].raw_coeff) { STBI_FREE(z->img_comp[i].raw_coeff); z->img_comp[i].raw_coeff = 0; z->img_comp[i].coeff = 0; } if (z->img_comp[i].linebuf) { STBI_FREE(z->img_comp[i].linebuf); z->img_comp[i].linebuf = NULL; } } return why; } 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 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); 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"); z->rgb = 0; for (i=0; i < s->img_n; ++i) { static unsigned char rgb[3] = { 'R', 'G', 'B' }; z->img_comp[i].id = stbi__get8(s); if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) ++z->rgb; 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 (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) 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; // these sizes can't be more than 17 bits 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 // // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) // so these muls can't overflow with 32-bit ints (which we require) 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].coeff = 0; z->img_comp[i].raw_coeff = 0; z->img_comp[i].linebuf = NULL; z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); if (z->img_comp[i].raw_data == NULL) return stbi__free_jpeg_components(z, i+1, 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); if (z->progressive) { // w2, h2 are multiples of 8 (see above) z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); if (z->img_comp[i].raw_coeff == NULL) return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); } } 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->jfif = 0; z->app14_color_transform = -1; // valid values are 0,1,2 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; for (m = 0; m < 4; m++) { j->img_comp[m].raw_data = NULL; j->img_comp[m].raw_coeff = NULL; } 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; } } // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } } else if (stbi__DNL(m)) { int Ld = stbi__get16be(j->s); stbi__uint32 NL = stbi__get16be(j->s); if (Ld != 4) stbi__err("bad DNL len", "Corrupt JPEG"); if (NL != j->s->img_y) stbi__err("bad DNL height", "Corrupt JPEG"); } 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; } // 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 stbi__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* stbi__float2fixed(1.40200f); g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); b = y_fixed + cb* stbi__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; } } #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* stbi__float2fixed(1.40200f); g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); b = y_fixed + cb* stbi__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; j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; } #endif #ifdef STBI_NEON j->idct_block_kernel = stbi__idct_simd; j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; 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) { stbi__free_jpeg_components(j, j->s->img_n, 0); } 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; // fast 0..255 * 0..255 => 0..255 rounded multiplication static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) { unsigned int t = x*y + 128; return (stbi_uc) ((t + (t >>8)) >> 8); } static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) { int n, decode_n, is_rgb; 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 >= 3 ? 3 : 1; is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); if (z->s->img_n == 3 && n < 3 && !is_rgb) 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_mad3(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) { if (is_rgb) { for (i=0; i < z->s->img_x; ++i) { out[0] = y[i]; out[1] = coutput[1][i]; out[2] = coutput[2][i]; out[3] = 255; out += n; } } else { z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); } } else if (z->s->img_n == 4) { if (z->app14_color_transform == 0) { // CMYK for (i=0; i < z->s->img_x; ++i) { stbi_uc m = coutput[3][i]; out[0] = stbi__blinn_8x8(coutput[0][i], m); out[1] = stbi__blinn_8x8(coutput[1][i], m); out[2] = stbi__blinn_8x8(coutput[2][i], m); out[3] = 255; out += n; } } else if (z->app14_color_transform == 2) { // YCCK z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); for (i=0; i < z->s->img_x; ++i) { stbi_uc m = coutput[3][i]; out[0] = stbi__blinn_8x8(255 - out[0], m); out[1] = stbi__blinn_8x8(255 - out[1], m); out[2] = stbi__blinn_8x8(255 - out[2], m); out += n; } } else { // YCbCr + alpha? Ignore the fourth channel for now 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 { if (is_rgb) { if (n == 1) for (i=0; i < z->s->img_x; ++i) *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); else { for (i=0; i < z->s->img_x; ++i, out += 2) { out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); out[1] = 255; } } } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { for (i=0; i < z->s->img_x; ++i) { stbi_uc m = coutput[3][i]; stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); out[0] = stbi__compute_y(r, g, b); out[1] = 255; out += n; } } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { for (i=0; i < z->s->img_x; ++i) { out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); out[1] = 255; 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 >= 3 ? 3 : 1; // report original components, not output return output; } } static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { unsigned char* result; stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); STBI_NOTUSED(ri); j->s = s; stbi__setup_jpeg(j); result = load_jpeg_image(j, x,y,comp,req_comp); STBI_FREE(j); return result; } static int stbi__jpeg_test(stbi__context *s) { int r; stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); r = stbi__decode_jpeg_header(j, STBI__SCAN_type); stbi__rewind(s); STBI_FREE(j); 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 >= 3 ? 3 : 1; return 1; } static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { int result; stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); j->s = s; result = stbi__jpeg_info_raw(j, x, y, comp); STBI_FREE(j); return result; } #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, const 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) if (sizes[i] > (1 << i)) return stbi__err("bad sizes", "Corrupt PNG"); 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 PNG"); 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 j = stbi__bit_reverse(next_code[s],s); while (j < (1 << STBI__ZFAST_BITS)) { z->fast[j] = fastv; j += (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 |= (unsigned int) 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, old_limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); cur = (int) (z->zout - z->zout_start); limit = old_limit = (int) (z->zout_end - z->zout_start); while (cur + n > limit) limit *= 2; q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); STBI_NOTUSED(old_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; if (len) { do *zout++ = v; while (--len); } } else { if (len) { 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; int ntot = hlit + hdist; 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 < ntot) { int c = stbi__zhuffman_decode(a, &z_codelength); if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); if (c < 16) lencodes[n++] = (stbi_uc) c; else { stbi_uc fill = 0; if (c == 16) { c = stbi__zreceive(a,2)+3; if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); fill = lencodes[n-1]; } else if (c == 17) c = stbi__zreceive(a,3)+3; else { STBI_ASSERT(c == 18); c = stbi__zreceive(a,7)+11; } if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); memset(lencodes+n, fill, c); n += c; } } if (n != ntot) 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_uncompressed_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; } static const stbi_uc stbi__zdefault_length[288] = { 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 }; static const stbi_uc stbi__zdefault_distance[32] = { 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 }; /* Init algorithm: { 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_uncompressed_block(a)) return 0; } else if (type == 3) { return 0; } else { if (type == 1) { // use fixed code lengths 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; int depth; } 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) { int bytes = (depth == 16? 2 : 1); stbi__context *s = a->s; stbi__uint32 i,j,stride = x*out_n*bytes; stbi__uint32 img_len, img_width_bytes; int k; int img_n = s->img_n; // copy it into a local for later int output_bytes = out_n*bytes; int filter_bytes = img_n*bytes; int width = x; STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // 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; // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), // so just check for raw_len < img_len always. 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; int filter = *raw++; 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; } prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above // 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 if (depth == 16) { if (img_n != out_n) { cur[filter_bytes] = 255; // first pixel top byte cur[filter_bytes+1] = 255; // first pixel bottom byte } raw += filter_bytes; cur += output_bytes; prior += output_bytes; } 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)*filter_bytes; #define STBI__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; STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; } #undef STBI__CASE raw += nk; } else { STBI_ASSERT(img_n+1 == out_n); #define STBI__CASE(f) \ case f: \ for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ for (k=0; k < filter_bytes; ++k) switch (filter) { STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; } #undef STBI__CASE // the loop above sets the high byte of the pixels' alpha, but for // 16 bit png files we also need the low byte set. we'll do that here. if (depth == 16) { cur = a->out + stride*j; // start at the beginning of the row again for (i=0; i < x; ++i,cur+=output_bytes) { cur[filter_bytes+1] = 255; } } } } // 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) { int q; // insert alpha = 255 cur = a->out + stride*j; if (img_n == 1) { for (q=x-1; q >= 0; --q) { cur[q*2+1] = 255; cur[q*2+0] = cur[q]; } } else { STBI_ASSERT(img_n == 3); for (q=x-1; q >= 0; --q) { cur[q*4+3] = 255; cur[q*4+2] = cur[q*3+2]; cur[q*4+1] = cur[q*3+1]; cur[q*4+0] = cur[q*3+0]; } } } } } else if (depth == 16) { // force the image data from big-endian to platform-native. // this is done in a separate pass due to the decoding relying // on the data being untouched, but could probably be done // per-line during decode if care is taken. stbi_uc *cur = a->out; stbi__uint16 *cur16 = (stbi__uint16*)cur; for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { *cur16 = (cur[0] << 8) | cur[1]; } } 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) { int bytes = (depth == 16 ? 2 : 1); int out_bytes = out_n * bytes; 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_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); 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_bytes + out_x*out_bytes, a->out + (j*x+i)*out_bytes, out_bytes); } } 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__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) { stbi__context *s = z->s; stbi__uint32 i, pixel_count = s->img_x * s->img_y; stbi__uint16 *p = (stbi__uint16*) z->out; // compute color-based transparency, assuming we've // already got 65535 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 : 65535); 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_mad2(pixel_count, pal_img_n, 0); 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) { stbi_uc half = a / 2; p[0] = (p[2] * 255 + half) / a; p[1] = (p[1] * 255 + half) / a; p[2] = ( t * 255 + half) / 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__uint16 tc16[3]; stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; int first=1,k,interlace=0, color=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?)"); z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3 && z->depth == 16) 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; if (z->depth == 16) { for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is } else { for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->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 ((int)(ioff + c.length) < (int)ioff) return 0; if (ioff + c.length > idata_limit) { stbi__uint32 idata_limit_old = 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; STBI_NOTUSED(idata_limit_old); p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, 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 * z->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, z->depth, color, interlace)) return 0; if (has_trans) { if (z->depth == 16) { if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; } else { 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; } else if (has_trans) { // non-paletted image with tRNS -> source image has (constant) alpha ++s->img_n; } 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 void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) { void *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)) { if (p->depth < 8) ri->bits_per_channel = 8; else ri->bits_per_channel = p->depth; result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { if (ri->bits_per_channel == 8) result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); else result = stbi__convert_format16((stbi__uint16 *) 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_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 void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi__png p; p.s = s; return stbi__do_png(&p, x,y,comp,req_comp, ri); } 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; } typedef struct { int bpp, offset, hsz; unsigned int mr,mg,mb,ma, all_a; } stbi__bmp_data; static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) { int 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 info->offset = stbi__get32le(s); info->hsz = hsz = stbi__get32le(s); info->mr = info->mg = info->mb = info->ma = 0; 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"); info->bpp = stbi__get16le(s); if (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); if (hsz != 12) { int 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 (info->bpp == 16 || info->bpp == 32) { if (compress == 0) { if (info->bpp == 32) { info->mr = 0xffu << 16; info->mg = 0xffu << 8; info->mb = 0xffu << 0; info->ma = 0xffu << 24; info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 } else { info->mr = 31u << 10; info->mg = 31u << 5; info->mb = 31u << 0; } } else if (compress == 3) { info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); // not documented, but generated by photoshop and handled by mspaint if (info->mr == info->mg && info->mg == info->mb) { // ?!?!? return stbi__errpuc("bad BMP", "bad BMP"); } } else return stbi__errpuc("bad BMP", "bad BMP"); } } else { int i; if (hsz != 108 && hsz != 124) return stbi__errpuc("bad BMP", "bad BMP"); info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); info->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 } } } return (void *) 1; } static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *out; unsigned int mr=0,mg=0,mb=0,ma=0, all_a; stbi_uc pal[256][4]; int psize=0,i,j,width; int flip_vertically, pad, target; stbi__bmp_data info; STBI_NOTUSED(ri); info.all_a = 255; if (stbi__bmp_parse_header(s, &info) == NULL) return NULL; // error code already set flip_vertically = ((int) s->img_y) > 0; s->img_y = abs((int) s->img_y); mr = info.mr; mg = info.mg; mb = info.mb; ma = info.ma; all_a = info.all_a; if (info.hsz == 12) { if (info.bpp < 24) psize = (info.offset - 14 - 24) / 3; } else { if (info.bpp < 16) psize = (info.offset - 14 - info.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 // sanity-check size if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) return stbi__errpuc("too large", "Corrupt BMP"); out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); if (info.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 (info.hsz != 12) stbi__get8(s); pal[i][3] = 255; } stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); if (info.bpp == 4) width = (s->img_x + 1) >> 1; else if (info.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 (info.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 = (info.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, info.offset - 14 - info.hsz); if (info.bpp == 24) width = 3 * s->img_x; else if (info.bpp == 16) width = 2*s->img_x; else /* bpp = 32 and pad = 0 */ width=0; pad = (-width) & 3; if (info.bpp == 24) { easy = 1; } else if (info.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); all_a |= a; if (target == 4) out[z++] = a; } } else { int bpp = info.bpp; for (i=0; i < (int) s->img_x; ++i) { stbi__uint32 v = (bpp == 16 ? (stbi__uint32) 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); all_a |= a; if (target == 4) out[z++] = STBI__BYTECAST(a); } } stbi__skip(s, pad); } } // if alpha channel is all 0s, replace with all 255s if (target == 4 && all_a == 0) for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) out[i] = 255; 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 // returns STBI_rgb or whatever, 0 on error static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) { // only RGB or RGBA (incl. 16bit) or grey allowed if(is_rgb16) *is_rgb16 = 0; switch(bits_per_pixel) { case 8: return STBI_grey; case 16: if(is_grey) return STBI_grey_alpha; // else: fall-through case 15: if(is_rgb16) *is_rgb16 = 1; return STBI_rgb; case 24: // fall-through case 32: return bits_per_pixel/8; default: return 0; } } static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) { int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; int sz, tga_colormap_type; stbi__get8(s); // discard Offset tga_colormap_type = stbi__get8(s); // colormap type if( tga_colormap_type > 1 ) { stbi__rewind(s); return 0; // only RGB or indexed allowed } tga_image_type = stbi__get8(s); // image type if ( tga_colormap_type == 1 ) { // colormapped (paletted) image if (tga_image_type != 1 && tga_image_type != 9) { stbi__rewind(s); return 0; } stbi__skip(s,4); // skip index of first colormap entry and number of entries sz = stbi__get8(s); // check bits per palette color entry if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { stbi__rewind(s); return 0; } stbi__skip(s,4); // skip image x and y origin tga_colormap_bpp = sz; } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { stbi__rewind(s); return 0; // only RGB or grey allowed, +/- RLE } stbi__skip(s,9); // skip colormap specification and image x/y origin tga_colormap_bpp = 0; } 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 } tga_bits_per_pixel = stbi__get8(s); // bits per pixel stbi__get8(s); // ignore alpha bits if (tga_colormap_bpp != 0) { if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { // when using a colormap, tga_bits_per_pixel is the size of the indexes // I don't think anything but 8 or 16bit indexes makes sense stbi__rewind(s); return 0; } tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); } else { tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); } if(!tga_comp) { stbi__rewind(s); return 0; } if (x) *x = tga_w; if (y) *y = tga_h; if (comp) *comp = tga_comp; return 1; // seems to have passed everything } static int stbi__tga_test(stbi__context *s) { int res = 0; int sz, tga_color_type; stbi__get8(s); // discard Offset tga_color_type = stbi__get8(s); // color type if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed sz = stbi__get8(s); // image type if ( tga_color_type == 1 ) { // colormapped (paletted) image if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 stbi__skip(s,4); // skip index of first colormap entry and number of entries sz = stbi__get8(s); // check bits per palette color entry if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; stbi__skip(s,4); // skip image x and y origin } else { // "normal" image w/o colormap if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE stbi__skip(s,9); // skip colormap specification and image x/y origin } if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height sz = stbi__get8(s); // bits per pixel if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; res = 1; // if we got this far, everything's good and we can return 1 instead of 0 errorEnd: stbi__rewind(s); return res; } // read 16bit value and convert to 24bit RGB static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) { stbi__uint16 px = (stbi__uint16)stbi__get16le(s); stbi__uint16 fiveBitMask = 31; // we have 3 channels with 5bits each int r = (px >> 10) & fiveBitMask; int g = (px >> 5) & fiveBitMask; int b = px & fiveBitMask; // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later out[0] = (stbi_uc)((r * 255)/31); out[1] = (stbi_uc)((g * 255)/31); out[2] = (stbi_uc)((b * 255)/31); // some people claim that the most significant bit might be used for alpha // (possibly if an alpha-bit is set in the "image descriptor byte") // but that only made 16bit test images completely translucent.. // so let's treat all 15 and 16bit TGAs as RGB with no alpha. } static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { // 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_rgb16=0; int tga_inverted = stbi__get8(s); // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) // image data unsigned char *tga_data; unsigned char *tga_palette = NULL; int i, j; unsigned char raw_data[4] = {0}; int RLE_count = 0; int RLE_repeating = 0; int read_next_pixel = 1; STBI_NOTUSED(ri); // do a tiny bit of precessing if ( tga_image_type >= 8 ) { tga_image_type -= 8; tga_is_RLE = 1; } tga_inverted = 1 - ((tga_inverted >> 5) & 1); // If I'm paletted, then I'll use the number of bits from the palette if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); // tga info *x = tga_width; *y = tga_height; if (comp) *comp = tga_comp; if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) return stbi__errpuc("too large", "Corrupt TGA"); tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); 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 && !tga_rgb16 ) { for (i=0; i < tga_height; ++i) { int row = tga_inverted ? tga_height -i - 1 : i; stbi_uc *tga_row = tga_data + row*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_mad2(tga_palette_len, tga_comp, 0); if (!tga_palette) { STBI_FREE(tga_data); return stbi__errpuc("outofmem", "Out of memory"); } if (tga_rgb16) { stbi_uc *pal_entry = tga_palette; STBI_ASSERT(tga_comp == STBI_rgb); for (i=0; i < tga_palette_len; ++i) { stbi__tga_read_rgb16(s, pal_entry); pal_entry += tga_comp; } } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { 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 index, then perform the lookup int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); if ( pal_idx >= tga_palette_len ) { // invalid index pal_idx = 0; } pal_idx *= tga_comp; for (j = 0; j < tga_comp; ++j) { raw_data[j] = tga_palette[pal_idx+j]; } } else if(tga_rgb16) { STBI_ASSERT(tga_comp == STBI_rgb); stbi__tga_read_rgb16(s, raw_data); } else { // read in the data raw for (j = 0; j < tga_comp; ++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 the source data was RGB16, it already is in the right order if (tga_comp >= 3 && !tga_rgb16) { 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 int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) { int count, nleft, len; count = 0; while ((nleft = pixelCount - count) > 0) { len = stbi__get8(s); if (len == 128) { // No-op. } else if (len < 128) { // Copy next len+1 bytes literally. len++; if (len > nleft) return 0; // corrupt data 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 = 257 - len; if (len > nleft) return 0; // corrupt data val = stbi__get8(s); count += len; while (len) { *p = val; p += 4; len--; } } } return 1; } static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { int pixelCount; int channelCount, compression; int channel, i; int bitdepth; int w,h; stbi_uc *out; STBI_NOTUSED(ri); // 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. bitdepth = stbi__get16be(s); if (bitdepth != 8 && bitdepth != 16) return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 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"); // Check size if (!stbi__mad3sizes_valid(4, w, h, 0)) return stbi__errpuc("too large", "Corrupt PSD"); // Create the destination image. if (!compression && bitdepth == 16 && bpc == 16) { out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); ri->bits_per_channel = 16; } else 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 += 4) *p = (channel == 3 ? 255 : 0); } else { // Read the RLE data. if (!stbi__psd_decode_rle(s, p, pixelCount)) { STBI_FREE(out); return stbi__errpuc("corrupt", "bad RLE data"); } } } } 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 (or 16-bit) value for each pixel in the image. // Read the data by channel. for (channel = 0; channel < 4; channel++) { if (channel >= channelCount) { // Fill this channel with default data. if (bitdepth == 16 && bpc == 16) { stbi__uint16 *q = ((stbi__uint16 *) out) + channel; stbi__uint16 val = channel == 3 ? 65535 : 0; for (i = 0; i < pixelCount; i++, q += 4) *q = val; } else { stbi_uc *p = out+channel; stbi_uc val = channel == 3 ? 255 : 0; for (i = 0; i < pixelCount; i++, p += 4) *p = val; } } else { if (ri->bits_per_channel == 16) { // output bpc stbi__uint16 *q = ((stbi__uint16 *) out) + channel; for (i = 0; i < pixelCount; i++, q += 4) *q = (stbi__uint16) stbi__get16be(s); } else { stbi_uc *p = out+channel; if (bitdepth == 16) { // input bpc for (i = 0; i < pixelCount; i++, p += 4) *p = (stbi_uc) (stbi__get16be(s) >> 8); } else { for (i = 0; i < pixelCount; i++, p += 4) *p = stbi__get8(s); } } } } } // remove weird white matte from PSD if (channelCount >= 4) { if (ri->bits_per_channel == 16) { for (i=0; i < w*h; ++i) { stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; if (pixel[3] != 0 && pixel[3] != 65535) { float a = pixel[3] / 65535.0f; float ra = 1.0f / a; float inv_a = 65535.0f * (1 - ra); pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); } } } else { for (i=0; i < w*h; ++i) { unsigned char *pixel = out + 4*i; if (pixel[3] != 0 && pixel[3] != 255) { float a = pixel[3] / 255.0f; float ra = 1.0f / a; float inv_a = 255.0f * (1 - ra); pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); } } } } // convert to desired output format if (req_comp && req_comp != 4) { if (ri->bits_per_channel == 16) out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); else 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 = 4; *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]; 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 void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) { stbi_uc *result; int i, x,y, internal_comp; STBI_NOTUSED(ri); if (!comp) comp = &internal_comp; 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 (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC 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_mad3(x, y, 4, 0); 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, *old_out; // output buffer (always 4 components) int flags, bgindex, ratio, transparent, eflags, delay; 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 = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); if (!stbi__gif_header(s, g, comp, 1)) { STBI_FREE(g); stbi__rewind( s ); return 0; } if (x) *x = g->w; if (y) *y = g->h; STBI_FREE(g); 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, init_code; stbi__uint32 first; stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; stbi__gif_lzw *p; lzw_cs = stbi__get8(s); if (lzw_cs > 12) return NULL; clear = 1 << lzw_cs; first = 1; codesize = lzw_cs + 1; codemask = (1 << codesize) - 1; bits = 0; valid_bits = 0; for (init_code = 0; init_code < clear; init_code++) { g->codes[init_code].prefix = -1; g->codes[init_code].first = (stbi_uc) init_code; g->codes[init_code].suffix = (stbi_uc) init_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 x0, int y0, int x1, int y1) { int x, y; stbi_uc *c = g->pal[g->bgindex]; for (y = y0; y < y1; y += 4 * g->w) { for (x = x0; x < x1; x += 4) { stbi_uc *p = &g->out[y + x]; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; p[3] = 0; } } } // 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 *prev_out = 0; if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header if (!stbi__mad3sizes_valid(g->w, g->h, 4, 0)) return stbi__errpuc("too large", "GIF too large"); prev_out = g->out; g->out = (stbi_uc *) stbi__malloc_mad3(4, g->w, g->h, 0); if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); switch ((g->eflags & 0x1C) >> 2) { case 0: // unspecified (also always used on 1st frame) stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); break; case 1: // do not dispose if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); g->old_out = prev_out; break; case 2: // dispose to background if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); break; case 3: // dispose to previous if (g->old_out) { for (i = g->start_y; i < g->max_y; i += 4 * g->w) memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); } break; } for (;;) { switch (stbi__get8(s)) { case 0x2C: /* Image Descriptor */ { int prev_trans = -1; 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) { if (g->transparent >= 0 && (g->eflags & 0x01)) { prev_trans = g->pal[g->transparent][3]; 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 (prev_trans != -1) g->pal[g->transparent][3] = (stbi_uc) prev_trans; 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); g->delay = stbi__get16le(s); 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"); } } STBI_NOTUSED(req_comp); } static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *u = 0; stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); memset(g, 0, sizeof(*g)); STBI_NOTUSED(ri); 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; if (req_comp && req_comp != 4) u = stbi__convert_format(u, 4, req_comp, g->w, g->h); } else if (g->out) STBI_FREE(g->out); STBI_FREE(g); 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) { int i; for (i=0; signature[i]; ++i) if (stbi__get8(s) != signature[i]) return 0; stbi__rewind(s); return 1; } static int stbi__hdr_test(stbi__context* s) { int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); stbi__rewind(s); if(!r) { r = stbi__hdr_test_core(s, "#?RGBE\n"); 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, stbi__result_info *ri) { 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; const char *headerToken; STBI_NOTUSED(ri); // Check identifier headerToken = stbi__hdr_gettoken(s,buffer); if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 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; if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) return stbi__errpf("too large", "HDR image is too large"); // Read data hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); if (!hdr_data) return stbi__errpf("outofmem", "Out of memory"); // 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_mad2(width, 4, 0); if (!scanline) { STBI_FREE(hdr_data); return stbi__errpf("outofmem", "Out of memory"); } } for (k = 0; k < 4; ++k) { int nleft; i = 0; while ((nleft = width - i) > 0) { count = stbi__get8(s); if (count > 128) { // Run value = stbi__get8(s); count -= 128; if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } 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); } if (scanline) 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; int dummy; if (!x) x = &dummy; if (!y) y = &dummy; if (!comp) comp = &dummy; if (stbi__hdr_test(s) == 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) { void *p; stbi__bmp_data info; info.all_a = 255; p = stbi__bmp_parse_header(s, &info); stbi__rewind( s ); if (p == NULL) return 0; if (x) *x = s->img_x; if (y) *y = s->img_y; if (comp) *comp = info.ma ? 4 : 3; return 1; } #endif #ifndef STBI_NO_PSD static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) { int channelCount, dummy; if (!x) x = &dummy; if (!y) y = &dummy; if (!comp) comp = &dummy; 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,dummy; stbi__pic_packet packets[10]; if (!x) x = &dummy; if (!y) y = &dummy; if (!comp) comp = &dummy; if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { stbi__rewind(s); return 0; } stbi__skip(s, 88); *x = stbi__get16be(s); *y = stbi__get16be(s); if (stbi__at_eof(s)) { stbi__rewind( 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 void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *out; STBI_NOTUSED(ri); 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; if (comp) *comp = s->img_n; if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) return stbi__errpuc("too large", "PNM too large"); out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); 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) { for (;;) { while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) *c = (char) stbi__get8(s); if (stbi__at_eof(s) || *c != '#') break; while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) *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, dummy; char c, p, t; if (!x) x = &dummy; if (!y) y = &dummy; if (!comp) comp = &dummy; 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.16 (2017-07-23) all functions have 16-bit variants; STBI_NO_STDIO works again; compilation fixes; fix rounding in unpremultiply; optimize vertical flip; disable raw_len validation; documentation fixes 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; warning fixes; disable run-time SSE detection on gcc; uniform handling of optional "return" values; thread-safe initialization of zlib tables 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes 2.11 (2016-04-02) allocate large structures on the stack remove white matting for transparent PSD fix reported channel count for PNG & BMP re-enable SSE2 in non-gcc 64-bit support RGB-formatted JPEG read 16-bit PNGs (only as 8-bit) 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED 2.09 (2016-01-16) allow comments in PNM files 16-bit-per-pixel TGA (not bit-per-component) info() for TGA could break due to .hdr handling info() for BMP to shares code instead of sloppy parse can use STBI_REALLOC_SIZED if allocator doesn't support realloc code cleanup 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA 2.07 (2015-09-13) fix compiler warnings partial animated GIF support limited 16-bpc PSD support #ifdef unused functions bug with < 92 byte PIC,PNM,HDR,TGA 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit 2.03 (2015-04-12) extra corruption checking (mmozeiko) stbi_set_flip_vertically_on_load (nguillemot) fix NEON support; fix mingw support 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 (2006-11-19) first released version */ /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ yquake2-QUAKE2_7_10/src/client/refresh/files/wal.c000066400000000000000000000024531321245476300216620ustar00rootroot00000000000000/* * 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 "../ref_shared.h" /* * NOTE: LoadWal() is in *_image.c because it needs the renderer-specific image_t */ void GetWalInfo(char *name, int *width, int *height) { miptex_t *mt; ri.FS_LoadFile(name, (void **)&mt); if (!mt) { return; } *width = LittleLong(mt->width); *height = LittleLong(mt->height); ri.FS_FreeFile((void *)mt); return; } yquake2-QUAKE2_7_10/src/client/refresh/gl/000077500000000000000000000000001321245476300202275ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/refresh/gl/header/000077500000000000000000000000001321245476300214575ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/refresh/gl/header/local.h000066400000000000000000000220311321245476300227200ustar00rootroot00000000000000/* * 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 #include "../../ref_shared.h" #include "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_VERSION_1_3 #define GL_TEXTURE0 0x84C0 #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 BACKFACE_EPSILON 0.01 #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 extern viddef_t vid; enum stereo_modes { STEREO_MODE_NONE, STEREO_MODE_OPENGL, STEREO_MODE_ANAGLYPH, STEREO_MODE_ROW_INTERLEAVED, STEREO_MODE_COLUMN_INTERLEAVED, STEREO_MODE_PIXEL_INTERLEAVED, STEREO_SPLIT_HORIZONTAL, STEREO_SPLIT_VERTICAL, }; enum opengl_special_buffer_modes { OPENGL_SPECIAL_BUFFER_MODE_NONE, OPENGL_SPECIAL_BUFFER_MODE_STEREO, OPENGL_SPECIAL_BUFFER_MODE_STENCIL, }; 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_lerpmodels; extern cvar_t *gl_lightlevel; extern cvar_t *gl_overbrightbits; extern cvar_t *gl_palettedtexture; extern cvar_t *gl_pointparameters; 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_mode; extern cvar_t *gl_customwidth; extern cvar_t *gl_customheight; extern cvar_t *gl_retexturing; extern cvar_t *gl_nolerp_list; 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_showtris; extern cvar_t *gl_showbbox; 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_polyblend; extern cvar_t *gl_flashblend; extern cvar_t *gl_modulate; extern cvar_t *gl_drawbuffer; extern cvar_t *gl_swapinterval; extern cvar_t *gl_anisotropic; 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_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_TexEnv(GLenum value); 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); 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 { const char *renderer_string; const char *vendor_string; const char *version_string; const char *extensions_string; int major_version; int minor_version; // ---- qboolean anisotropic; qboolean npottextures; qboolean palettedtexture; qboolean pointparameters; // ---- 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; GLenum currenttarget; float camera_separation; enum stereo_modes stereo_mode; 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; /* * Shuts the render context and SDL window down * (if contextOnly, the window will not be shutdown) */ void RI_ShutdownWindow(qboolean contextOnly); /* * Returns the address of the GL function proc, * or NULL if the function is not found. */ void *GLimp_GetProcAddress (const char* proc); #endif yquake2-QUAKE2_7_10/src/client/refresh/gl/header/model.h000066400000000000000000000117031321245476300227320ustar00rootroot00000000000000/* * 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 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_7_10/src/client/refresh/gl/header/qgl.h000066400000000000000000000066541321245476300224260ustar00rootroot00000000000000/* * 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_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 // ======================================================================= /* * 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 *qglPointParameterfARB ) ( GLenum param, GLfloat value ); extern void ( APIENTRY *qglPointParameterfvARB ) ( GLenum param, const GLfloat *value ); extern void ( APIENTRY *qglColorTableEXT ) ( GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid * ); #endif yquake2-QUAKE2_7_10/src/client/refresh/gl/qgl.c000066400000000000000000000043071321245476300211620ustar00rootroot00000000000000/* * 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 "header/local.h" /* * GL extensions */ void (APIENTRY *qglPointParameterfARB)(GLenum param, GLfloat value); void (APIENTRY *qglPointParameterfvARB)(GLenum param, const GLfloat *value); void (APIENTRY *qglColorTableEXT)(GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *); /* ========================================================================= */ void QGL_EXT_Reset ( void ) { qglPointParameterfARB = NULL; qglPointParameterfvARB = NULL; qglColorTableEXT = 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_7_10/src/client/refresh/gl/r_draw.c000066400000000000000000000236651321245476300216650ustar00rootroot00000000000000/* * 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 */ draw_chars = R_FindImage("pics/conchars.pcx", it_pic); } /* * 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 RDraw_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); GLfloat vtx[] = { x, y, x + scaledSize, y, x + scaledSize, y + scaledSize, x, y + scaledSize }; GLfloat tex[] = { fcol, frow, fcol + size, frow, fcol + size, frow + size, fcol, frow + size }; glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glVertexPointer( 2, GL_FLOAT, 0, vtx ); glTexCoordPointer( 2, GL_FLOAT, 0, tex ); glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); } image_t * RDraw_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 RDraw_GetPicSize(int *w, int *h, char *pic) { image_t *gl; gl = RDraw_FindPic(pic); if (!gl) { *w = *h = -1; return; } *w = gl->width; *h = gl->height; } void RDraw_StretchPic(int x, int y, int w, int h, char *pic) { image_t *gl; gl = RDraw_FindPic(pic); if (!gl) { R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); return; } if (scrap_dirty) { Scrap_Upload(); } R_Bind(gl->texnum); GLfloat vtx[] = { x, y, x + w, y, x + w, y + h, x, y + h }; GLfloat tex[] = { gl->sl, gl->tl, gl->sh, gl->tl, gl->sh, gl->th, gl->sl, gl->th }; glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glVertexPointer( 2, GL_FLOAT, 0, vtx ); glTexCoordPointer( 2, GL_FLOAT, 0, tex ); glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); } void RDraw_PicScaled(int x, int y, char *pic, float factor) { image_t *gl; gl = RDraw_FindPic(pic); if (!gl) { R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); return; } if (scrap_dirty) { Scrap_Upload(); } R_Bind(gl->texnum); GLfloat vtx[] = { x, y, x + gl->width * factor, y, x + gl->width * factor, y + gl->height * factor, x, y + gl->height * factor }; GLfloat tex[] = { gl->sl, gl->tl, gl->sh, gl->tl, gl->sh, gl->th, gl->sl, gl->th }; glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glVertexPointer( 2, GL_FLOAT, 0, vtx ); glTexCoordPointer( 2, GL_FLOAT, 0, tex ); glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); } /* * This repeats a 64*64 tile graphic to fill * the screen around a sized down * refresh window. */ void RDraw_TileClear(int x, int y, int w, int h, char *pic) { image_t *image; image = RDraw_FindPic(pic); if (!image) { R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); return; } R_Bind(image->texnum); GLfloat vtx[] = { x, y, x + w, y, x + w, y + h, x, y + h }; GLfloat tex[] = { x / 64.0, y / 64.0, ( x + w ) / 64.0, y / 64.0, ( x + w ) / 64.0, ( y + h ) / 64.0, x / 64.0, ( y + h ) / 64.0 }; glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glVertexPointer( 2, GL_FLOAT, 0, vtx ); glTexCoordPointer( 2, GL_FLOAT, 0, tex ); glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); } /* * Fills a box of pixels with a single color */ void RDraw_Fill(int x, int y, int w, int h, int c) { union { unsigned c; byte v[4]; } color; if ((unsigned)c > 255) { ri.Sys_Error(ERR_FATAL, "Draw_Fill: bad color"); } glDisable(GL_TEXTURE_2D); color.c = d_8to24table[c]; glColor4f(color.v [ 0 ] / 255.0, color.v [ 1 ] / 255.0, color.v [ 2 ] / 255.0, 1); GLfloat vtx[] = { x, y, x + w, y, x + w, y + h, x, y + h }; glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer( 2, GL_FLOAT, 0, vtx ); glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); glDisableClientState( GL_VERTEX_ARRAY ); glColor4f( 1, 1, 1, 1 ); glEnable(GL_TEXTURE_2D); } void RDraw_FadeScreen(void) { glEnable(GL_BLEND); glDisable(GL_TEXTURE_2D); glColor4f(0, 0, 0, 0.8); GLfloat vtx[] = { 0, 0, vid.width, 0, vid.width, vid.height, 0, vid.height }; glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer( 2, GL_FLOAT, 0, vtx ); glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); glDisableClientState( GL_VERTEX_ARRAY ); glColor4f(1, 1, 1, 1); glEnable(GL_TEXTURE_2D); glDisable(GL_BLEND); } void RDraw_StretchRaw(int x, int y, int w, int h, int cols, int rows, byte *data) { GLfloat tex[8]; byte *source; float hscale = 1.0f; int frac, fracstep; int i, j, trows; int row; R_Bind(0); if(gl_config.npottextures || rows <= 256) { // X, X tex[0] = 0; tex[1] = 0; // X, Y tex[2] = 1; tex[3] = 0; // Y, X tex[4] = 1; tex[5] = 1; // Y, Y tex[6] = 0; tex[7] = 1; } else { // Scale params hscale = rows / 256.0; trows = 256; // X, X tex[0] = 1.0 / 512.0; tex[1] = 1.0 / 512.0; // X, Y tex[2] = 511.0 / 512.0; tex[3] = 1.0 / 512.0; // Y, X tex[4] = 511.0 / 512.0; tex[5] = rows * hscale / 256 - 1.0 / 512.0; // Y, Y tex[6] = 1.0 / 512.0; tex[7] = rows * hscale / 256 - 1.0 / 512.0; } GLfloat vtx[] = { x, y, x + w, y, x + w, y + h, x, y + h }; if (!gl_config.palettedtexture) { unsigned image32[320*240]; /* was 256 * 256, but we want a bit more space */ /* .. because now if non-power-of-2 textures are supported, we just load * the data into a texture in the original format, without skipping any * pixels to fit into a 256x256 texture. * This causes text in videos (which are 320x240) to not look broken anymore. */ if(gl_config.npottextures || rows <= 256) { unsigned* img = image32; if(cols*rows > 320*240) { /* in case there is a bigger video after all, * malloc enough space to hold the frame */ img = (unsigned*)malloc(cols*rows*4); } for(i=0; i 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 image8[256 * 256]; 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); glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glVertexPointer( 2, GL_FLOAT, 0, vtx ); glTexCoordPointer( 2, GL_FLOAT, 0, tex ); glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); } 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) { ri.Sys_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_7_10/src/client/refresh/gl/r_image.c000066400000000000000000000663601321245476300220110ustar00rootroot00000000000000/* * 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 = GL_RGB; int gl_alpha_format = GL_RGBA; int gl_tex_solid_format = GL_RGB; int gl_tex_alpha_format = GL_RGBA; 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", GL_RGBA}, {"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", GL_RGB}, {"GL_RGB", GL_RGB}, {"GL_RGB8", GL_RGB8}, {"GL_RGB5", GL_RGB5}, {"GL_RGB4", GL_RGB4}, {"GL_R3_G3_B2", GL_R3_G3_B2}, }; #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 (gl_config.palettedtexture) { 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_TexEnv(GLenum mode) { static int lastmodes[2] = {-1, -1}; if (mode != lastmodes[gl_state.currenttmu]) { glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode); // FIXME: shouldn't this be glTexEnvi() ? 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_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) { R_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) { ri.Cvar_SetValue("gl_anisotropic", gl_config.max_anisotropy); } else if (gl_anisotropic->value < 1.0) { ri.Cvar_SetValue("gl_anisotropic", 1.0); } } else { ri.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) { R_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) { R_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" }; R_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: R_Printf(PRINT_ALL, "M"); break; case it_sprite: R_Printf(PRINT_ALL, "S"); break; case it_wall: R_Printf(PRINT_ALL, "W"); break; case it_pic: R_Printf(PRINT_ALL, "P"); break; default: R_Printf(PRINT_ALL, " "); break; } R_Printf(PRINT_ALL, " %3i %3i %s: %s\n", image->upload_width, image->upload_height, palstrings[image->paletted], image->name); } R_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_Upload32Soft(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) { ri.Sys_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_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_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_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.npottextures) { res = R_Upload32Native(data, width, height, mipmap); } else { res = R_Upload32Soft(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) { ri.Sys_Error(ERR_DROP, "R_Upload8: too large"); } if (gl_config.palettedtexture && 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; qboolean nolerp = false; if(gl_nolerp_list != NULL && gl_nolerp_list->string != NULL) { nolerp = strstr(gl_nolerp_list->string, name) != NULL; } /* 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) { ri.Sys_Error(ERR_DROP, "MAX_GLTEXTURES"); } numgltextures++; } image = &gltextures[i]; if (strlen(name) >= sizeof(image->name)) { ri.Sys_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 (!nolerp && (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 { R_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; if (nolerp) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } } return image; } static 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)); } ri.FS_LoadFile(name, (void **)&mt); if (!mt) { R_Printf(PRINT_ALL, "LoadWal: 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); ri.FS_FreeFile((void *)mt); 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; 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; 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, NULL, &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, NULL, &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; */ if(LoadSTB(name, ext, &pic, &width, &height)) { image = R_LoadPic(name, pic, width, realwidth, height, realheight, type, 32); } } else { return NULL; } if (pic) { free(pic); } return image; } struct image_s * RI_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; // use 1/gamma so higher value is brighter, to match HW gamma settings float g = 1.0f/vid_gamma->value; registration_sequence = 1; /* init intensity conversions */ intensity = ri.Cvar_Get("intensity", "2", CVAR_ARCHIVE); if (intensity->value <= 1) { ri.Cvar_Set("intensity", "1"); } gl_state.inverse_intensity = 1 / intensity->value; Draw_GetPalette(); // FIXME: I think this is redundant - RI_Init() already calls that! if (gl_config.palettedtexture) { ri.FS_LoadFile("pics/16to8.dat", (void **)&gl_state.d_16to8table); if (!gl_state.d_16to8table) { ri.Sys_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_7_10/src/client/refresh/gl/r_light.c000066400000000000000000000315671321245476300220370ustar00rootroot00000000000000/* * 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; float rad; rad = light->intensity * 0.35; GLfloat vtx[3*18]; GLfloat clr[4*18]; unsigned int index_vtx = 4; unsigned int index_clr = 0; glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_COLOR_ARRAY ); clr[index_clr++] = light->color [ 0 ] * 0.2; clr[index_clr++] = light->color [ 1 ] * 0.2; clr[index_clr++] = light->color [ 2 ] * 0.2; clr[index_clr++] = 1; for ( i = 0; i < 3; i++ ) { vtx [ i ] = light->origin [ i ] - vpn [ i ] * rad; } for ( i = 16; i >= 0; i-- ) { clr[index_clr++] = 0; clr[index_clr++] = 0; clr[index_clr++] = 0; clr[index_clr++] = 1; a = i / 16.0 * M_PI * 2; for ( j = 0; j < 3; j++ ) { vtx[index_vtx++] = light->origin [ j ] + vright [ j ] * cos( a ) * rad + vup [ j ] * sin( a ) * rad; } } glVertexPointer( 3, GL_FLOAT, 0, vtx ); glColorPointer( 4, GL_FLOAT, 0, clr ); glDrawArrays( GL_TRIANGLE_FAN, 0, 18 ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_COLOR_ARRAY ); } 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); } glColor4f(1, 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)) { ri.Sys_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)) { ri.Sys_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_7_10/src/client/refresh/gl/r_lightmap.c000066400000000000000000000144071321245476300225270ustar00rootroot00000000000000/* * 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 { gl_lms.internal_format = GL_LIGHTMAP_FORMAT; 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) { ri.Sys_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)) { ri.Sys_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 */ /* 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_LIGHTMAP_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); } yquake2-QUAKE2_7_10/src/client/refresh/gl/r_main.c000066400000000000000000001321511321245476300216430ustar00rootroot00000000000000/* * 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_lerpmodels; cvar_t *gl_lefthand; cvar_t *gl_farsee; cvar_t *gl_lightlevel; cvar_t *gl_overbrightbits; 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_palettedtexture; cvar_t *gl_pointparameters; 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_nolerp_list; cvar_t *gl_dynamic; cvar_t *gl_modulate; cvar_t *gl_nobind; cvar_t *gl_round_down; cvar_t *gl_picmip; cvar_t *gl_showtris; cvar_t *gl_showbbox; 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_saturatelighting; cvar_t *gl_swapinterval; cvar_t *gl_texturemode; cvar_t *gl_texturealphamode; cvar_t *gl_texturesolidmode; cvar_t *gl_anisotropic; cvar_t *gl_lockpvs; cvar_t *gl_msaa_samples; cvar_t *vid_fullscreen; cvar_t *vid_gamma; cvar_t *gl_stereo; cvar_t *gl_stereo_separation; cvar_t *gl_stereo_anaglyph_colors; cvar_t *gl_stereo_convergence; refimport_t ri; /* * Returns true if the box is completely outside the frustom */ qboolean R_CullBox(vec3_t mins, vec3_t maxs) { int i; if (!gl_cull->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[4]; 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); } GLfloat tex[] = { 0, 1, 0, 0, 1, 0, 1, 1 }; VectorMA( e->origin, -frame->origin_y, up, point[0] ); VectorMA( point[0], -frame->origin_x, right, point[0] ); VectorMA( e->origin, frame->height - frame->origin_y, up, point[1] ); VectorMA( point[1], -frame->origin_x, right, point[1] ); VectorMA( e->origin, frame->height - frame->origin_y, up, point[2] ); VectorMA( point[2], frame->width - frame->origin_x, right, point[2] ); VectorMA( e->origin, -frame->origin_y, up, point[3] ); VectorMA( point[3], frame->width - frame->origin_x, right, point[3] ); glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glVertexPointer( 3, GL_FLOAT, 0, point ); glTexCoordPointer( 2, GL_FLOAT, 0, tex ); glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); 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; 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); glColor4f( shadelight[0], shadelight[1], shadelight[2], 1 ); GLfloat vtxA[] = { 0, 0, -16, 16 * cos( 0 * M_PI / 2 ), 16 * sin( 0 * M_PI / 2 ), 0, 16 * cos( 1 * M_PI / 2 ), 16 * sin( 1 * M_PI / 2 ), 0, 16 * cos( 2 * M_PI / 2 ), 16 * sin( 2 * M_PI / 2 ), 0, 16 * cos( 3 * M_PI / 2 ), 16 * sin( 3 * M_PI / 2 ), 0, 16 * cos( 4 * M_PI / 2 ), 16 * sin( 4 * M_PI / 2 ), 0 }; glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer( 3, GL_FLOAT, 0, vtxA ); glDrawArrays( GL_TRIANGLE_FAN, 0, 6 ); glDisableClientState( GL_VERTEX_ARRAY ); GLfloat vtxB[] = { 0, 0, 16, 16 * cos( 4 * M_PI / 2 ), 16 * sin( 4 * M_PI / 2 ), 0, 16 * cos( 3 * M_PI / 2 ), 16 * sin( 3 * M_PI / 2 ), 0, 16 * cos( 2 * M_PI / 2 ), 16 * sin( 2 * M_PI / 2 ), 0, 16 * cos( 1 * M_PI / 2 ), 16 * sin( 1 * M_PI / 2 ), 0, 16 * cos( 0 * M_PI / 2 ), 16 * sin( 0 * M_PI / 2 ), 0 }; glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer( 3, GL_FLOAT, 0, vtxB ); glDrawArrays( GL_TRIANGLE_FAN, 0, 6 ); glDisableClientState( GL_VERTEX_ARRAY ); glColor4f(1, 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: ri.Sys_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: ri.Sys_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]; GLfloat vtx[3*num_particles*3]; GLfloat tex[2*num_particles*3]; GLfloat clr[4*num_particles*3]; unsigned int index_vtx = 0; unsigned int index_tex = 0; unsigned int index_clr = 0; unsigned int j; R_Bind(r_particletexture->texnum); glDepthMask(GL_FALSE); /* no z buffering */ glEnable(GL_BLEND); R_TexEnv(GL_MODULATE); 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 ]; for (j=0; j<3; j++) // Copy the color for each point { clr[index_clr++] = color[0]/255.0f; clr[index_clr++] = color[1]/255.0f; clr[index_clr++] = color[2]/255.0f; clr[index_clr++] = p->alpha; } // point 0 tex[index_tex++] = 0.0625f; tex[index_tex++] = 0.0625f; vtx[index_vtx++] = p->origin[0]; vtx[index_vtx++] = p->origin[1]; vtx[index_vtx++] = p->origin[2]; // point 1 tex[index_tex++] = 1.0625f; tex[index_tex++] = 0.0625f; vtx[index_vtx++] = p->origin [ 0 ] + up [ 0 ] * scale; vtx[index_vtx++] = p->origin [ 1 ] + up [ 1 ] * scale; vtx[index_vtx++] = p->origin [ 2 ] + up [ 2 ] * scale; // point 2 tex[index_tex++] = 0.0625f; tex[index_tex++] = 1.0625f; vtx[index_vtx++] = p->origin [ 0 ] + right [ 0 ] * scale; vtx[index_vtx++] = p->origin [ 1 ] + right [ 1 ] * scale; vtx[index_vtx++] = p->origin [ 2 ] + right [ 2 ] * scale; } glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glEnableClientState( GL_COLOR_ARRAY ); glVertexPointer( 3, GL_FLOAT, 0, vtx ); glTexCoordPointer( 2, GL_FLOAT, 0, tex ); glColorPointer( 4, GL_FLOAT, 0, clr ); glDrawArrays( GL_TRIANGLES, 0, num_particles*3 ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); glDisableClientState( GL_COLOR_ARRAY ); glDisable(GL_BLEND); glColor4f(1, 1, 1, 1); glDepthMask(1); /* back to normal Z buffering */ R_TexEnv(GL_REPLACE); } void R_DrawParticles(void) { qboolean stereo_split_tb = ((gl_state.stereo_mode == STEREO_SPLIT_VERTICAL) && gl_state.camera_separation); qboolean stereo_split_lr = ((gl_state.stereo_mode == STEREO_SPLIT_HORIZONTAL) && gl_state.camera_separation); if (gl_config.pointparameters && !(stereo_split_tb || stereo_split_lr)) { int i; unsigned char color[4]; const particle_t *p; GLfloat vtx[3*r_newrefdef.num_particles]; GLfloat clr[4*r_newrefdef.num_particles]; unsigned int index_vtx = 0; unsigned int index_clr = 0; glDepthMask(GL_FALSE); glEnable(GL_BLEND); glDisable(GL_TEXTURE_2D); // assume the particle size looks good with window height 480px and scale according to real resolution glPointSize(gl_particle_size->value * (float)r_newrefdef.height/480.0f); for ( i = 0, p = r_newrefdef.particles; i < r_newrefdef.num_particles; i++, p++ ) { *(int *) color = d_8to24table [ p->color & 0xFF ]; clr[index_clr++] = color[0]/255.0f; clr[index_clr++] = color[1]/255.0f; clr[index_clr++] = color[2]/255.0f; clr[index_clr++] = p->alpha; vtx[index_vtx++] = p->origin[0]; vtx[index_vtx++] = p->origin[1]; vtx[index_vtx++] = p->origin[2]; } glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_COLOR_ARRAY ); glVertexPointer( 3, GL_FLOAT, 0, vtx ); glColorPointer( 4, GL_FLOAT, 0, clr ); glDrawArrays( GL_POINTS, 0, r_newrefdef.num_particles ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_COLOR_ARRAY ); glDisable(GL_BLEND); glColor4f( 1, 1, 1, 1 ); 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 */ glColor4f( v_blend[0], v_blend[1], v_blend[2], v_blend[3] ); GLfloat vtx[] = { 10, 100, 100, 10, -100, 100, 10, -100, -100, 10, 100, -100 }; glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer( 3, GL_FLOAT, 0, vtx ); glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); glDisableClientState( GL_VERTEX_ARRAY ); 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 += - gl_stereo_convergence->value * (2 * gl_state.camera_separation) / zNear; xmax += - gl_stereo_convergence->value * (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; qboolean drawing_left_eye = gl_state.camera_separation < 0; qboolean stereo_split_tb = ((gl_state.stereo_mode == STEREO_SPLIT_VERTICAL) && gl_state.camera_separation); qboolean stereo_split_lr = ((gl_state.stereo_mode == STEREO_SPLIT_HORIZONTAL) && gl_state.camera_separation); if(stereo_split_lr) { w = w / 2; x = drawing_left_eye ? (x / 2) : (x + vid.width) / 2; } if(stereo_split_tb) { h = h / 2; y2 = drawing_left_eye ? (y2 + vid.height) / 2 : (y2 / 2); } 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) { // Check whether the stencil buffer needs clearing, and do so if need be. GLbitfield stencilFlags = 0; if (gl_state.stereo_mode >= STEREO_MODE_ROW_INTERLEAVED && gl_state.stereo_mode <= STEREO_MODE_PIXEL_INTERLEAVED) { glClearStencil(0); stencilFlags |= GL_STENCIL_BUFFER_BIT; } if (gl_ztrick->value) { static int trickframe; if (gl_clear->value) { glClear(GL_COLOR_BUFFER_BIT | stencilFlags); } 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 | stencilFlags | GL_DEPTH_BUFFER_BIT); } else { glClear(GL_DEPTH_BUFFER_BIT | stencilFlags); } 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(); } void R_SetGL2D(void) { int x, w, y, h; /* set 2D virtual screen size */ qboolean drawing_left_eye = gl_state.camera_separation < 0; qboolean stereo_split_tb = ((gl_state.stereo_mode == STEREO_SPLIT_VERTICAL) && gl_state.camera_separation); qboolean stereo_split_lr = ((gl_state.stereo_mode == STEREO_SPLIT_HORIZONTAL) && gl_state.camera_separation); x = 0; w = vid.width; y = 0; h = vid.height; if(stereo_split_lr) { w = w / 2; x = drawing_left_eye ? 0 : w; } if(stereo_split_tb) { h = h / 2; y = drawing_left_eye ? h : 0; } glViewport(x, y, w, h); 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); } /* * r_newrefdef must be set before the first call */ void R_RenderView(refdef_t *fd) { if ((gl_state.stereo_mode != STEREO_MODE_NONE) && gl_state.camera_separation) { qboolean drawing_left_eye = gl_state.camera_separation < 0; switch (gl_state.stereo_mode) { case STEREO_MODE_ANAGLYPH: { // Work out the colour for each eye. int anaglyph_colours[] = { 0x4, 0x3 }; // Left = red, right = cyan. if (strlen(gl_stereo_anaglyph_colors->string) == 2) { int eye, colour, missing_bits; // Decode the colour name from its character. for (eye = 0; eye < 2; ++eye) { colour = 0; switch (toupper(gl_stereo_anaglyph_colors->string[eye])) { case 'B': ++colour; // 001 Blue case 'G': ++colour; // 010 Green case 'C': ++colour; // 011 Cyan case 'R': ++colour; // 100 Red case 'M': ++colour; // 101 Magenta case 'Y': ++colour; // 110 Yellow anaglyph_colours[eye] = colour; break; } } // Fill in any missing bits. missing_bits = ~(anaglyph_colours[0] | anaglyph_colours[1]) & 0x3; for (eye = 0; eye < 2; ++eye) { anaglyph_colours[eye] |= missing_bits; } } // Set the current colour. glColorMask( !!(anaglyph_colours[drawing_left_eye] & 0x4), !!(anaglyph_colours[drawing_left_eye] & 0x2), !!(anaglyph_colours[drawing_left_eye] & 0x1), GL_TRUE ); } break; case STEREO_MODE_ROW_INTERLEAVED: case STEREO_MODE_COLUMN_INTERLEAVED: case STEREO_MODE_PIXEL_INTERLEAVED: { qboolean flip_eyes = true; int client_x, client_y; //GLimp_GetClientAreaOffset(&client_x, &client_y); client_x = 0; client_y = 0; R_SetGL2D(); glEnable(GL_STENCIL_TEST); glStencilMask(GL_TRUE); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); glStencilFunc(GL_NEVER, 0, 1); glBegin(GL_QUADS); { glVertex2i(0, 0); glVertex2i(vid.width, 0); glVertex2i(vid.width, vid.height); glVertex2i(0, vid.height); } glEnd(); glStencilOp(GL_INVERT, GL_KEEP, GL_KEEP); glStencilFunc(GL_NEVER, 1, 1); glBegin(GL_LINES); { if (gl_state.stereo_mode == STEREO_MODE_ROW_INTERLEAVED || gl_state.stereo_mode == STEREO_MODE_PIXEL_INTERLEAVED) { int y; for (y = 0; y <= vid.height; y += 2) { glVertex2f(0, y - 0.5f); glVertex2f(vid.width, y - 0.5f); } flip_eyes ^= (client_y & 1); } if (gl_state.stereo_mode == STEREO_MODE_COLUMN_INTERLEAVED || gl_state.stereo_mode == STEREO_MODE_PIXEL_INTERLEAVED) { int x; for (x = 0; x <= vid.width; x += 2) { glVertex2f(x - 0.5f, 0); glVertex2f(x - 0.5f, vid.height); } flip_eyes ^= (client_x & 1); } } glEnd(); glStencilMask(GL_FALSE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glStencilFunc(GL_EQUAL, drawing_left_eye ^ flip_eyes, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); } break; default: break; } } if (gl_norefresh->value) { return; } r_newrefdef = *fd; if (!r_worldmodel && !(r_newrefdef.rdflags & RDF_NOWORLDMODEL)) { ri.Sys_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) { R_Printf(PRINT_ALL, "%4i wpoly %4i epoly %i tex %i lmaps\n", c_brush_polys, c_alias_polys, c_visible_textures, c_visible_lightmaps); } switch (gl_state.stereo_mode) { case STEREO_MODE_NONE: break; case STEREO_MODE_ANAGLYPH: glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); break; case STEREO_MODE_ROW_INTERLEAVED: case STEREO_MODE_COLUMN_INTERLEAVED: case STEREO_MODE_PIXEL_INTERLEAVED: glDisable(GL_STENCIL_TEST); break; default: break; } } enum opengl_special_buffer_modes GL_GetSpecialBufferModeForStereoMode(enum stereo_modes stereo_mode) { switch (stereo_mode) { case STEREO_MODE_NONE: case STEREO_SPLIT_HORIZONTAL: case STEREO_SPLIT_VERTICAL: case STEREO_MODE_ANAGLYPH: return OPENGL_SPECIAL_BUFFER_MODE_NONE; case STEREO_MODE_OPENGL: return OPENGL_SPECIAL_BUFFER_MODE_STEREO; case STEREO_MODE_ROW_INTERLEAVED: case STEREO_MODE_COLUMN_INTERLEAVED: case STEREO_MODE_PIXEL_INTERLEAVED: return OPENGL_SPECIAL_BUFFER_MODE_STENCIL; } return OPENGL_SPECIAL_BUFFER_MODE_NONE; } 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 RI_RenderFrame(refdef_t *fd) { R_RenderView(fd); R_SetLightLevel(); R_SetGL2D(); } void R_Register(void) { gl_lefthand = ri.Cvar_Get("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE); gl_farsee = ri.Cvar_Get("gl_farsee", "0", CVAR_LATCH | CVAR_ARCHIVE); gl_norefresh = ri.Cvar_Get("gl_norefresh", "0", 0); gl_fullbright = ri.Cvar_Get("gl_fullbright", "0", 0); gl_drawentities = ri.Cvar_Get("gl_drawentities", "1", 0); gl_drawworld = ri.Cvar_Get("gl_drawworld", "1", 0); gl_novis = ri.Cvar_Get("gl_novis", "0", 0); gl_lerpmodels = ri.Cvar_Get("gl_lerpmodels", "1", 0); gl_speeds = ri.Cvar_Get("gl_speeds", "0", 0); gl_lightlevel = ri.Cvar_Get("gl_lightlevel", "0", 0); gl_overbrightbits = ri.Cvar_Get("gl_overbrightbits", "0", CVAR_ARCHIVE); gl_particle_min_size = ri.Cvar_Get("gl_particle_min_size", "2", CVAR_ARCHIVE); gl_particle_max_size = ri.Cvar_Get("gl_particle_max_size", "40", CVAR_ARCHIVE); gl_particle_size = ri.Cvar_Get("gl_particle_size", "40", CVAR_ARCHIVE); gl_particle_att_a = ri.Cvar_Get("gl_particle_att_a", "0.01", CVAR_ARCHIVE); gl_particle_att_b = ri.Cvar_Get("gl_particle_att_b", "0.0", CVAR_ARCHIVE); gl_particle_att_c = ri.Cvar_Get("gl_particle_att_c", "0.01", CVAR_ARCHIVE); gl_modulate = ri.Cvar_Get("gl_modulate", "1", CVAR_ARCHIVE); gl_mode = ri.Cvar_Get("gl_mode", "4", CVAR_ARCHIVE); gl_lightmap = ri.Cvar_Get("gl_lightmap", "0", 0); gl_shadows = ri.Cvar_Get("gl_shadows", "0", CVAR_ARCHIVE); gl_stencilshadow = ri.Cvar_Get("gl_stencilshadow", "0", CVAR_ARCHIVE); gl_dynamic = ri.Cvar_Get("gl_dynamic", "1", 0); gl_nobind = ri.Cvar_Get("gl_nobind", "0", 0); gl_round_down = ri.Cvar_Get("gl_round_down", "1", 0); gl_picmip = ri.Cvar_Get("gl_picmip", "0", 0); gl_showtris = ri.Cvar_Get("gl_showtris", "0", 0); gl_showbbox = ri.Cvar_Get("gl_showbbox", "0", 0); gl_ztrick = ri.Cvar_Get("gl_ztrick", "0", 0); gl_zfix = ri.Cvar_Get("gl_zfix", "0", 0); gl_finish = ri.Cvar_Get("gl_finish", "0", CVAR_ARCHIVE); gl_clear = ri.Cvar_Get("gl_clear", "0", 0); gl_cull = ri.Cvar_Get("gl_cull", "1", 0); gl_polyblend = ri.Cvar_Get("gl_polyblend", "1", 0); gl_flashblend = ri.Cvar_Get("gl_flashblend", "0", 0); gl_texturemode = ri.Cvar_Get("gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE); gl_texturealphamode = ri.Cvar_Get("gl_texturealphamode", "default", CVAR_ARCHIVE); gl_texturesolidmode = ri.Cvar_Get("gl_texturesolidmode", "default", CVAR_ARCHIVE); gl_anisotropic = ri.Cvar_Get("gl_anisotropic", "0", CVAR_ARCHIVE); gl_lockpvs = ri.Cvar_Get("gl_lockpvs", "0", 0); gl_palettedtexture = ri.Cvar_Get("gl_palettedtexture", "0", CVAR_ARCHIVE); gl_pointparameters = ri.Cvar_Get("gl_pointparameters", "1", CVAR_ARCHIVE); gl_drawbuffer = ri.Cvar_Get("gl_drawbuffer", "GL_BACK", 0); gl_swapinterval = ri.Cvar_Get("gl_swapinterval", "1", CVAR_ARCHIVE); gl_saturatelighting = ri.Cvar_Get("gl_saturatelighting", "0", 0); vid_fullscreen = ri.Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); vid_gamma = ri.Cvar_Get("vid_gamma", "1.2", CVAR_ARCHIVE); gl_customwidth = ri.Cvar_Get("gl_customwidth", "1024", CVAR_ARCHIVE); gl_customheight = ri.Cvar_Get("gl_customheight", "768", CVAR_ARCHIVE); gl_msaa_samples = ri.Cvar_Get ( "gl_msaa_samples", "0", CVAR_ARCHIVE ); gl_retexturing = ri.Cvar_Get("gl_retexturing", "1", CVAR_ARCHIVE); /* don't bilerp characters and crosshairs */ gl_nolerp_list = ri.Cvar_Get("gl_nolerp_list", "pics/conchars.pcx pics/ch1.pcx pics/ch2.pcx pics/ch3.pcx", 0); gl_stereo = ri.Cvar_Get( "gl_stereo", "0", CVAR_ARCHIVE ); gl_stereo_separation = ri.Cvar_Get( "gl_stereo_separation", "-0.4", CVAR_ARCHIVE ); gl_stereo_anaglyph_colors = ri.Cvar_Get( "gl_stereo_anaglyph_colors", "rc", CVAR_ARCHIVE ); gl_stereo_convergence = ri.Cvar_Get( "gl_stereo_convergence", "1", CVAR_ARCHIVE ); ri.Cmd_AddCommand("imagelist", R_ImageList_f); ri.Cmd_AddCommand("screenshot", R_ScreenShot); ri.Cmd_AddCommand("modellist", Mod_Modellist_f); ri.Cmd_AddCommand("gl_strings", R_Strings); } /* * Changes the video mode */ static int SetMode_impl(int *pwidth, int *pheight, int mode, int fullscreen) { R_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) && !ri.Vid_GetModeInfo(pwidth, pheight, mode)) { R_Printf(PRINT_ALL, " invalid mode\n"); return rserr_invalid_mode; } R_Printf(PRINT_ALL, " %d %d\n", *pwidth, *pheight); if (!ri.GLimp_InitGraphics(fullscreen, pwidth, pheight)) { return rserr_invalid_mode; } return rserr_ok; } qboolean R_SetMode(void) { rserr_t err; int fullscreen; fullscreen = (int)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 = SetMode_impl(&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) { ri.Cvar_SetValue("vid_fullscreen", 0); vid_fullscreen->modified = false; R_Printf(PRINT_ALL, "ref_gl::R_SetMode() - fullscreen unavailable in this mode\n"); if ((err = SetMode_impl(&vid.width, &vid.height, gl_mode->value, 0)) == rserr_ok) { return true; } } else if (err == rserr_invalid_mode) { R_Printf(PRINT_ALL, "ref_gl::R_SetMode() - invalid mode\n"); if (gl_msaa_samples->value != 0.0f) { R_Printf(PRINT_ALL, "gl_msaa_samples was %d - will try again with gl_msaa_samples = 0\n", (int)gl_msaa_samples->value); ri.Cvar_SetValue("gl_msaa_samples", 0.0f); gl_msaa_samples->modified = false; if ((err = SetMode_impl(&vid.width, &vid.height, gl_mode->value, 0)) == rserr_ok) { return true; } } if(gl_mode->value == gl_state.prev_mode) { // trying again would result in a crash anyway, give up already // (this would happen if your initing fails at all and your resolution already was 640x480) return false; } ri.Cvar_SetValue("gl_mode", gl_state.prev_mode); gl_mode->modified = false; } /* try setting it back to something safe */ if ((err = SetMode_impl(&vid.width, &vid.height, gl_state.prev_mode, 0)) != rserr_ok) { R_Printf(PRINT_ALL, "ref_gl::R_SetMode() - could not revert to safe mode\n"); return false; } } return true; } qboolean RI_Init() { int j; extern float r_turbsin[256]; Swap_Init(); for (j = 0; j < 256; j++) { r_turbsin[j] *= 0.5; } R_Printf(PRINT_ALL, "Refresh: " REF_VERSION "\n"); R_Printf(PRINT_ALL, "Client: " YQ2VERSION "\n\n"); /* Options */ R_Printf(PRINT_ALL, "Refresher build options:\n"); R_Printf(PRINT_ALL, " + Retexturing support\n"); #ifdef X11GAMMA R_Printf(PRINT_ALL, " + Gamma via X11\n\n"); #else R_Printf(PRINT_ALL, " - Gamma via X11\n\n"); #endif Draw_GetPalette(); R_Register(); /* initialize our QGL dynamic bindings */ QGL_Init(); /* initialize OS-specific parts of OpenGL */ if (!ri.GLimp_Init()) { QGL_Shutdown(); return false; } /* set our "safe" mode */ gl_state.prev_mode = 4; gl_state.stereo_mode = gl_stereo->value; /* create the window and set up the context */ if (!R_SetMode()) { QGL_Shutdown(); R_Printf(PRINT_ALL, "ref_gl::R_Init() - could not R_SetMode()\n"); return false; } ri.Vid_MenuInit(); // -------- /* get our various GL strings */ R_Printf(PRINT_ALL, "\nOpenGL setting:\n"); gl_config.vendor_string = (char *)glGetString(GL_VENDOR); R_Printf(PRINT_ALL, "GL_VENDOR: %s\n", gl_config.vendor_string); gl_config.renderer_string = (char *)glGetString(GL_RENDERER); R_Printf(PRINT_ALL, "GL_RENDERER: %s\n", gl_config.renderer_string); gl_config.version_string = (char *)glGetString(GL_VERSION); R_Printf(PRINT_ALL, "GL_VERSION: %s\n", gl_config.version_string); gl_config.extensions_string = (char *)glGetString(GL_EXTENSIONS); R_Printf(PRINT_ALL, "GL_EXTENSIONS: %s\n", gl_config.extensions_string); sscanf(gl_config.version_string, "%d.%d", &gl_config.major_version, &gl_config.minor_version); if (gl_config.major_version == 1) { if (gl_config.minor_version < 4) { QGL_Shutdown(); R_Printf(PRINT_ALL, "Support for OpenGL 1.4 is not available\n"); return false; } } R_Printf(PRINT_ALL, "\n\nProbing for OpenGL extensions:\n"); // ---- /* Point parameters */ R_Printf(PRINT_ALL, " - Point parameters: "); if (strstr(gl_config.extensions_string, "GL_ARB_point_parameters")) { qglPointParameterfARB = (void (APIENTRY *)(GLenum, GLfloat))GLimp_GetProcAddress ( "glPointParameterfARB" ); qglPointParameterfvARB = (void (APIENTRY *)(GLenum, const GLfloat *))GLimp_GetProcAddress ( "glPointParameterfvARB" ); } gl_config.pointparameters = false; if (gl_pointparameters->value) { if (qglPointParameterfARB && qglPointParameterfvARB) { gl_config.pointparameters = true; R_Printf(PRINT_ALL, "Okay\n"); } else { R_Printf(PRINT_ALL, "Failed\n"); } } else { R_Printf(PRINT_ALL, "Disabled\n"); } // ---- /* Paletted texture */ R_Printf(PRINT_ALL, " - Paletted texture: "); if (strstr(gl_config.extensions_string, "GL_EXT_paletted_texture") && strstr(gl_config.extensions_string, "GL_EXT_shared_texture_palette")) { qglColorTableEXT = (void (APIENTRY *)(GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid * )) GLimp_GetProcAddress ("glColorTableEXT"); } gl_config.palettedtexture = false; if (gl_palettedtexture->value) { if (qglColorTableEXT) { gl_config.palettedtexture = true; R_Printf(PRINT_ALL, "Okay\n"); } else { R_Printf(PRINT_ALL, "Failed\n"); } } else { R_Printf(PRINT_ALL, "Disabled\n"); } // -------- /* Anisotropic */ R_Printf(PRINT_ALL, " - Anisotropic: "); if (strstr(gl_config.extensions_string, "GL_EXT_texture_filter_anisotropic")) { gl_config.anisotropic = true; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_config.max_anisotropy); R_Printf(PRINT_ALL, "%ux\n", (int)gl_config.max_anisotropy); } else { gl_config.anisotropic = false; gl_config.max_anisotropy = 0.0; R_Printf(PRINT_ALL, "Failed\n"); } // ---- /* Non power of two textures */ R_Printf(PRINT_ALL, " - Non power of two textures: "); if (strstr(gl_config.extensions_string, "GL_ARB_texture_non_power_of_two")) { gl_config.npottextures = true; R_Printf(PRINT_ALL, "Okay\n"); } else { gl_config.npottextures = false; R_Printf(PRINT_ALL, "Failed\n"); } // ---- R_SetDefaultState(); R_InitImages(); Mod_Init(); R_InitParticleTexture(); Draw_InitLocal(); return true; } void RI_Shutdown(void) { ri.Cmd_RemoveCommand("modellist"); ri.Cmd_RemoveCommand("screenshot"); ri.Cmd_RemoveCommand("imagelist"); ri.Cmd_RemoveCommand("gl_strings"); Mod_FreeAll(); R_ShutdownImages(); /* shutdown OS specific OpenGL stuff like contexts, etc. */ RI_ShutdownWindow(false); /* shutdown our QGL subsystem */ QGL_Shutdown(); } extern void UpdateHardwareGamma(); extern void RI_SetSwapInterval(void); void RI_BeginFrame(float camera_separation) { gl_state.camera_separation = camera_separation; /* change modes if necessary */ if (gl_mode->modified) { vid_fullscreen->modified = true; } // force a vid_restart if gl_stereo has been modified. if ( gl_state.stereo_mode != gl_stereo->value ) { // If we've gone from one mode to another with the same special buffer requirements there's no need to restart. if ( GL_GetSpecialBufferModeForStereoMode( gl_state.stereo_mode ) == GL_GetSpecialBufferModeForStereoMode( gl_stereo->value ) ) { gl_state.stereo_mode = gl_stereo->value; } else { R_Printf(PRINT_ALL, "stereo supermode changed, restarting video!\n"); cvar_t *ref; ref = ri.Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); ref->modified = true; } } if (vid_gamma->modified) { vid_gamma->modified = false; if (gl_state.hwgamma) { UpdateHardwareGamma(); } } // Clamp overbrightbits if (gl_overbrightbits->modified) { if (gl_overbrightbits->value > 2 && gl_overbrightbits->value < 4) { ri.Cvar_Set("gl_overbrightbits", "2"); } else if (gl_overbrightbits->value > 4) { ri.Cvar_Set("gl_overbrightbits", "4"); } gl_overbrightbits->modified = false; } /* go into 2D mode */ // FIXME: just call R_SetGL2D(); int x, w, y, h; qboolean drawing_left_eye = gl_state.camera_separation < 0; qboolean stereo_split_tb = ((gl_state.stereo_mode == STEREO_SPLIT_VERTICAL) && gl_state.camera_separation); qboolean stereo_split_lr = ((gl_state.stereo_mode == STEREO_SPLIT_HORIZONTAL) && gl_state.camera_separation); x = 0; w = vid.width; y = 0; h = vid.height; if(stereo_split_lr) { w = w / 2; x = drawing_left_eye ? 0 : w; } if(stereo_split_tb) { h = h / 2; y = drawing_left_eye ? h : 0; } glViewport(x, y, w, h); 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_mode != STEREO_MODE_OPENGL) { 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; } if (gl_swapinterval->modified) { gl_swapinterval->modified = false; RI_SetSwapInterval(); } /* clear screen if desired */ R_Clear(); } void RI_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; GLfloat vtx[3*NUM_BEAM_SEGS*4]; unsigned int index_vtx = 0; unsigned int pointb; 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); for ( i = 0; i < NUM_BEAM_SEGS; i++ ) { vtx[index_vtx++] = start_points [ i ][ 0 ]; vtx[index_vtx++] = start_points [ i ][ 1 ]; vtx[index_vtx++] = start_points [ i ][ 2 ]; vtx[index_vtx++] = end_points [ i ][ 0 ]; vtx[index_vtx++] = end_points [ i ][ 1 ]; vtx[index_vtx++] = end_points [ i ][ 2 ]; pointb = ( i + 1 ) % NUM_BEAM_SEGS; vtx[index_vtx++] = start_points [ pointb ][ 0 ]; vtx[index_vtx++] = start_points [ pointb ][ 1 ]; vtx[index_vtx++] = start_points [ pointb ][ 2 ]; vtx[index_vtx++] = end_points [ pointb ][ 0 ]; vtx[index_vtx++] = end_points [ pointb ][ 1 ]; vtx[index_vtx++] = end_points [ pointb ][ 2 ]; } glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer( 3, GL_FLOAT, 0, vtx ); glDrawArrays( GL_TRIANGLE_STRIP, 0, NUM_BEAM_SEGS*4 ); glDisableClientState( GL_VERTEX_ARRAY ); glEnable(GL_TEXTURE_2D); glDisable(GL_BLEND); glDepthMask(GL_TRUE); } extern int RI_PrepareForWindow(void); extern int RI_InitContext(void* win); extern void RI_BeginRegistration(char *model); extern struct model_s * RI_RegisterModel(char *name); extern struct image_s * RI_RegisterSkin(char *name); extern void RI_SetSky(char *name, float rotate, vec3_t axis); extern void RI_EndRegistration(void); extern void RI_RenderFrame(refdef_t *fd); extern image_t * RDraw_FindPic(char *name); extern void RDraw_GetPicSize(int *w, int *h, char *pic); extern void RDraw_PicScaled(int x, int y, char *pic, float factor); extern void RDraw_StretchPic(int x, int y, int w, int h, char *pic); extern void RDraw_CharScaled(int x, int y, int num, float scale); extern void RDraw_TileClear(int x, int y, int w, int h, char *pic); extern void RDraw_Fill(int x, int y, int w, int h, int c); extern void RDraw_FadeScreen(void); extern void RDraw_StretchRaw(int x, int y, int w, int h, int cols, int rows, byte *data); extern void RI_SetPalette(const unsigned char *palette); extern qboolean RI_IsVSyncActive(void); extern void RI_EndFrame(void); Q2_DLL_EXPORTED refexport_t GetRefAPI(refimport_t imp) { refexport_t re = {0}; ri = imp; re.api_version = API_VERSION; re.Init = RI_Init; re.Shutdown = RI_Shutdown; re.PrepareForWindow = RI_PrepareForWindow; re.InitContext = RI_InitContext; re.ShutdownWindow = RI_ShutdownWindow; re.IsVSyncActive = RI_IsVSyncActive; re.BeginRegistration = RI_BeginRegistration; re.RegisterModel = RI_RegisterModel; re.RegisterSkin = RI_RegisterSkin; re.SetSky = RI_SetSky; re.EndRegistration = RI_EndRegistration; re.RenderFrame = RI_RenderFrame; re.DrawFindPic = RDraw_FindPic; re.DrawGetPicSize = RDraw_GetPicSize; //re.DrawPic = Draw_Pic; re.DrawPicScaled = RDraw_PicScaled; re.DrawStretchPic = RDraw_StretchPic; //re.DrawChar = Draw_Char; re.DrawCharScaled = RDraw_CharScaled; re.DrawTileClear = RDraw_TileClear; re.DrawFill = RDraw_Fill; re.DrawFadeScreen = RDraw_FadeScreen; re.DrawStretchRaw = RDraw_StretchRaw; re.SetPalette = RI_SetPalette; re.BeginFrame = RI_BeginFrame; re.EndFrame = RI_EndFrame; return re; } void R_Printf(int level, const char* msg, ...) { va_list argptr; va_start(argptr, msg); ri.Com_VPrintf(level, msg, argptr); va_end(argptr); } /* * this is only here so the functions in shared source files * (shared.c, rand.c, flash.c, mem.c/hunk.c) can link */ void Sys_Error(char *error, ...) { va_list argptr; char text[4096]; // MAXPRINTMSG == 4096 va_start(argptr, error); vsprintf(text, error, argptr); va_end(argptr); ri.Sys_Error(ERR_FATAL, "%s", text); } void Com_Printf(char *msg, ...) { va_list argptr; va_start(argptr, msg); ri.Com_VPrintf(PRINT_ALL, msg, argptr); va_end(argptr); } yquake2-QUAKE2_7_10/src/client/refresh/gl/r_md2.c000066400000000000000000000103371321245476300214020ustar00rootroot00000000000000/* * 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 "../gl/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) { ri.Sys_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) { ri.Sys_Error(ERR_DROP, "model %s has a skin taller than %d", mod->name, MAX_LBM_HEIGHT); } if (pheader->num_xyz <= 0) { ri.Sys_Error(ERR_DROP, "model %s has no vertices", mod->name); } if (pheader->num_xyz > MAX_VERTS) { ri.Sys_Error(ERR_DROP, "model %s has too many vertices", mod->name); } if (pheader->num_st <= 0) { ri.Sys_Error(ERR_DROP, "model %s has no st vertices", mod->name); } if (pheader->num_tris <= 0) { ri.Sys_Error(ERR_DROP, "model %s has no triangles", mod->name); } if (pheader->num_frames <= 0) { ri.Sys_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_7_10/src/client/refresh/gl/r_mesh.c000066400000000000000000000412661321245476300216610ustar00rootroot00000000000000/* * 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) { unsigned short total; GLenum type; 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); while (1) { /* get the vertex count and primitive type */ count = *order++; if (!count) { break; /* done */ } if (count < 0) { count = -count; type = GL_TRIANGLE_FAN; } else { type = GL_TRIANGLE_STRIP; } total = count; GLfloat vtx[3*total]; GLfloat tex[2*total]; GLfloat clr[4 * total]; unsigned int index_vtx = 0; unsigned int index_tex = 0; unsigned int index_clr = 0; if (currententity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE)) { do { index_xyz = order[2]; order += 3; clr[index_clr++] = shadelight[0]; clr[index_clr++] = shadelight[1]; clr[index_clr++] = shadelight[2]; clr[index_clr++] = alpha; vtx[index_vtx++] = s_lerped[index_xyz][0]; vtx[index_vtx++] = s_lerped[index_xyz][1]; vtx[index_vtx++] = s_lerped[index_xyz][2]; } while (--count); } else { do { /* texture coordinates come from the draw list */ tex[index_tex++] = ((float *) order)[0]; tex[index_tex++] = ((float *) order)[1]; index_xyz = order[2]; order += 3; /* normals and vertexes come from the frame list */ l = shadedots[verts[index_xyz].lightnormalindex]; clr[index_clr++] = l * shadelight[0]; clr[index_clr++] = l * shadelight[1]; clr[index_clr++] = l * shadelight[2]; clr[index_clr++] = alpha; vtx[index_vtx++] = s_lerped[index_xyz][0]; vtx[index_vtx++] = s_lerped[index_xyz][1]; vtx[index_vtx++] = s_lerped[index_xyz][2]; } while (--count); } glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vtx); glTexCoordPointer(2, GL_FLOAT, 0, tex); glColorPointer(4, GL_FLOAT, 0, clr); glDrawArrays(type, 0, total); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_COLOR_ARRAY); } 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) { unsigned short total; GLenum type; 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; type = GL_TRIANGLE_FAN; } else { type = GL_TRIANGLE_STRIP; } total = count; GLfloat vtx[3*total]; unsigned int index_vtx = 0; 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; vtx[index_vtx++] = point [ 0 ]; vtx[index_vtx++] = point [ 1 ]; vtx[index_vtx++] = point [ 2 ]; order += 3; } while (--count); glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer( 3, GL_FLOAT, 0, vtx ); glDrawArrays( type, 0, total ); glDisableClientState( GL_VERTEX_ARRAY ); } /* 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)) { R_Printf(PRINT_DEVELOPER, "R_CullAliasModel %s: no such frame %d\n", currentmodel->name, e->frame); e->frame = 0; } if ((e->oldframe >= paliashdr->num_frames) || (e->oldframe < 0)) { R_Printf(PRINT_DEVELOPER, "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. if (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)) { R_Printf(PRINT_DEVELOPER, "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)) { R_Printf(PRINT_DEVELOPER, "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 (gl_showbbox->value) { glDisable(GL_CULL_FACE); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glDisable(GL_TEXTURE_2D); glBegin(GL_TRIANGLE_STRIP); for (i = 0; i < 8; i++) { glVertex3fv(bbox[i]); } glEnd(); glEnable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glEnable(GL_CULL_FACE); } 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.5f); R_DrawAliasShadow(paliashdr, currententity->frame); glEnable(GL_TEXTURE_2D); glDisable(GL_BLEND); glPopMatrix(); } glColor4f(1, 1, 1, 1); } yquake2-QUAKE2_7_10/src/client/refresh/gl/r_misc.c000066400000000000000000000136031321245476300216520ustar00rootroot00000000000000/* * 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" static byte dottexture[16][16] = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 2, 3, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 3, 3, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 2, 3, 3, 3, 3, 3, 3, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0}, {0, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0}, {0, 2, 3, 3, 3, 3, 3, 3, 2, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 3, 3, 3, 3, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 2, 3, 3, 2, 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} }; static byte notex[4][4] = { {0, 0, 0, 0}, {0, 0, 1, 1}, {0, 1, 1, 1}, {0, 1, 1, 1} }; 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 partData[16][16][4]; byte notexData[8][8][4]; /* particle texture */ for (x = 0; x < 16; x++) { for (y = 0; y < 16; y++) { partData[y][x][0] = 255; partData[y][x][1] = 255; partData[y][x][2] = 255; partData[y][x][3] = dottexture[x][y] * 85; } } r_particletexture = R_LoadPic("***particle***", (byte *)partData, 16, 0, 16, 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++) { notexData[y][x][0] = notex[x & 3][y & 3] * 255; notexData[y][x][1] = 0; notexData[y][x][2] = 0; notexData[y][x][3] = 255; } } r_notexture = R_LoadPic("***r_notexture***", (byte *)notexData, 8, 0, 8, 0, it_wall, 32); } void R_ScreenShot(void) { int w=vid.width, h=vid.height; byte *buffer = malloc(w*h*3); if (!buffer) { R_Printf(PRINT_ALL, "R_ScreenShot: Couldn't malloc %d bytes\n", w*h*3); return; } glPixelStorei(GL_PACK_ALIGNMENT, 1); glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, buffer); // the pixels are now row-wise left to right, bottom to top, // but we need them row-wise left to right, top to bottom. // so swap bottom rows with top rows { size_t bytesPerRow = 3*w; byte rowBuffer[bytesPerRow]; byte *curRowL = buffer; // first byte of first row byte *curRowH = buffer + bytesPerRow*(h-1); // first byte of last row while(curRowL < curRowH) { memcpy(rowBuffer, curRowL, bytesPerRow); memcpy(curRowL, curRowH, bytesPerRow); memcpy(curRowH, rowBuffer, bytesPerRow); curRowL += bytesPerRow; curRowH -= bytesPerRow; } } ri.Vid_WriteScreenshot(w, h, 3, buffer); free(buffer); } void R_Strings(void) { R_Printf(PRINT_ALL, "GL_VENDOR: %s\n", gl_config.vendor_string); R_Printf(PRINT_ALL, "GL_RENDERER: %s\n", gl_config.renderer_string); R_Printf(PRINT_ALL, "GL_VERSION: %s\n", gl_config.version_string); R_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 (gl_config.pointparameters) { 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); qglPointParameterfARB(GL_POINT_SIZE_MIN_EXT, gl_particle_min_size->value); qglPointParameterfARB(GL_POINT_SIZE_MAX_EXT, gl_particle_max_size->value); qglPointParameterfvARB(GL_DISTANCE_ATTENUATION_EXT, attenuations); } if (gl_config.palettedtexture) { 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_7_10/src/client/refresh/gl/r_model.c000066400000000000000000000476501321245476300220300ustar00rootroot00000000000000/* * 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) { ri.Sys_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; R_Printf(PRINT_ALL, "Loaded models:\n"); for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { if (!mod->name[0]) { continue; } R_Printf(PRINT_ALL, "%8i : %s\n", mod->extradatasize, mod->name); total += mod->extradatasize; } R_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]) { ri.Sys_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)) { ri.Sys_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) { ri.Sys_Error(ERR_DROP, "mod_numknown == MAX_MOD_KNOWN"); } mod_numknown++; } strcpy(mod->name, name); /* load the file */ modfilelen = ri.FS_LoadFile(mod->name, (void **)&buf); if (!buf) { if (crash) { ri.Sys_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: ri.Sys_Error(ERR_DROP, "Mod_NumForName: unknown fileid for %s", mod->name); break; } loadmodel->extradatasize = Hunk_End(); ri.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)) { ri.Sys_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)) { ri.Sys_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)) { ri.Sys_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)) { ri.Sys_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) { R_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)) { ri.Sys_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)) { ri.Sys_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)) { ri.Sys_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)) { ri.Sys_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)) { ri.Sys_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)) { ri.Sys_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)) { ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); if ((count < 1) || (count >= MAX_MAP_SURFEDGES)) { ri.Sys_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)) { ri.Sys_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) { ri.Sys_Error(ERR_DROP, "Loaded a brush model after the world"); } header = (dheader_t *)buffer; i = LittleLong(header->version); if (i != BSPVERSION) { ri.Sys_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) { ri.Sys_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 RI_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 = ri.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 * RI_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 RI_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_7_10/src/client/refresh/gl/r_scrap.c000066400000000000000000000043171321245476300220310ustar00rootroot00000000000000/* * 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_7_10/src/client/refresh/gl/r_sdl.c000066400000000000000000000303421321245476300215000ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2016 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. * * ======================================================================= * * SDL-specific OpenGL shit formerly in refresh.c * * ======================================================================= */ #include "header/local.h" #ifdef SDL2 #include #else // SDL1.2 #include #endif //SDL2 #if defined(__APPLE__) #include #else #include #endif /* X.org stuff - put this here as more modern renderers wouldn't need it * e.g. an OpenGL3 renderer could just apply gamma through a shader */ #ifdef X11GAMMA #include #include #include #include #include #include XRRCrtcGamma** gammaRamps = NULL; int noGammaRamps = 0; #endif #if SDL_VERSION_ATLEAST(2, 0, 0) static SDL_Window* window = NULL; static SDL_GLContext context = NULL; #else static SDL_Surface* window = NULL; #endif qboolean have_stencil = false; static qboolean vsyncActive = false; /* * Returns the adress of a GL function */ void * GLimp_GetProcAddress (const char* proc) { return SDL_GL_GetProcAddress ( proc ); } /* * 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 { R_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) { R_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) { R_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 R_Printf(PRINT_ALL, "Setting gamma failed: %s\n", SDL_GetError()); } } #endif // X11GAMMA 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 { R_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) { R_Printf(PRINT_ALL, "Unable to get xrandr screen resources.\n"); return; } noGammaRamps = res->ncrtc; gammaRamps = calloc(noGammaRamps, sizeof(XRRCrtcGamma*)); if(gammaRamps == NULL) { R_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); R_Printf(PRINT_ALL, "Using hardware gamma via X11/xRandR.\n"); #elif __APPLE__ gl_state.hwgamma = false; R_Printf(PRINT_ALL, "Using software gamma (needs vid_restart after changes)\n"); return; #else R_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 { R_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) { R_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) { R_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; R_Printf(PRINT_ALL, "Restored original Gamma\n"); } #endif // X11GAMMA // called by GLimp_InitGraphics() before creating window, // returns flags for SDL window creation int RI_PrepareForWindow(void) { unsigned int flags = 0; int msaa_samples = 0; 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 !SDL_VERSION_ATLEAST(2, 0, 0) /* Set vsync - For SDL1.2, this must be done before creating the window */ SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, gl_swapinterval->value ? 1 : 0); #endif if (gl_msaa_samples->value) { msaa_samples = gl_msaa_samples->value; if (SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1) < 0) { R_Printf(PRINT_ALL, "MSAA is unsupported: %s\n", SDL_GetError()); ri.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) { R_Printf(PRINT_ALL, "MSAA %ix is unsupported: %s\n", msaa_samples, SDL_GetError()); ri.Cvar_SetValue("gl_msaa_samples", 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); } } else { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); } /* Initiate the flags */ #if SDL_VERSION_ATLEAST(2, 0, 0) flags = SDL_WINDOW_OPENGL; #else // SDL 1.2 flags = SDL_OPENGL; #endif return flags; } void RI_SetSwapInterval(void) { #if SDL_VERSION_ATLEAST(2, 0, 0) /* Set vsync - TODO: -1 could be set for "late swap tearing" */ SDL_GL_SetSwapInterval(gl_swapinterval->value ? 1 : 0); vsyncActive = SDL_GL_GetSwapInterval() != 0; #else R_Printf(PRINT_ALL, "SDL1.2 requires a vid_restart to apply changes to gl_swapinterval (vsync)!\n"); #endif } int RI_InitContext(void* win) { int msaa_samples = 0, stencil_bits = 0; char title[40] = {0}; if(win == NULL) { ri.Sys_Error(ERR_FATAL, "R_InitContext() must not be called with NULL argument!"); return false; } #if SDL_VERSION_ATLEAST(2, 0, 0) window = (SDL_Window*)win; context = SDL_GL_CreateContext(window); if(context == NULL) { R_Printf(PRINT_ALL, "R_InitContext(): Creating OpenGL Context failed: %s\n", SDL_GetError()); window = NULL; return false; } #else // SDL 1.2 window = (SDL_Surface*)win; // context is created implicitly with window, nothing to do here #endif const char* glver = (char *)glGetString(GL_VERSION); sscanf(glver, "%d.%d", &gl_config.major_version, &gl_config.minor_version); if (gl_config.major_version < 1 || (gl_config.major_version == 1 && gl_config.minor_version < 4)) { R_Printf(PRINT_ALL, "R_InitContext(): Got an OpenGL version %d.%d context - need (at least) 1.4!\n", gl_config.major_version, gl_config.minor_version); return false; } if (gl_msaa_samples->value) { if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &msaa_samples) == 0) { ri.Cvar_SetValue("gl_msaa_samples", msaa_samples); } } #if SDL_VERSION_ATLEAST(2, 0, 0) /* For SDL2, this must be done after creating the window */ RI_SetSwapInterval(); #else // SDL1.2 - set vsyncActive to whatever is configured, hoping it was actually set vsyncActive = gl_swapinterval->value ? 1 : 0; #endif /* Initialize the stencil buffer */ if (!SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil_bits)) { R_Printf(PRINT_ALL, "Got %d bits of stencil.\n", stencil_bits); if (stencil_bits >= 1) { have_stencil = true; } } /* Initialize hardware gamma */ InitGamma(); /* Window title - set here so we can display renderer name in it */ snprintf(title, sizeof(title), "Yamagi Quake II %s - OpenGL 1.x", YQ2VERSION); #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_SetWindowTitle(window, title); #else SDL_WM_SetCaption(title, title); #endif return true; } qboolean RI_IsVSyncActive(void) { return vsyncActive; } /* * Swaps the buffers to show the new frame */ void RI_EndFrame(void) { #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_GL_SwapWindow(window); #else SDL_GL_SwapBuffers(); #endif } /* * Shuts the SDL render backend down */ void RI_ShutdownWindow(qboolean contextOnly) { #ifdef X11GAMMA RestoreGamma(); #endif /* 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. Only do this if we have a context, though. */ if (window) { #if SDL_VERSION_ATLEAST(2, 0, 0) if(context) { glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); RI_EndFrame(); SDL_GL_DeleteContext(context); context = NULL; } #else // SDL 1.2 glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); RI_EndFrame(); #endif } window = NULL; gl_state.hwgamma = false; if(!contextOnly) { ri.Vid_ShutdownWindow(); } } yquake2-QUAKE2_7_10/src/client/refresh/gl/r_sp2.c000066400000000000000000000041271321245476300214240ustar00rootroot00000000000000/* * 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 "../gl/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) { ri.Sys_Error(ERR_DROP, "%s has wrong version number (%i should be %i)", mod->name, sprout->version, SPRITE_VERSION); } if (sprout->numframes > MAX_MD2SKINS) { ri.Sys_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_7_10/src/client/refresh/gl/r_surf.c000066400000000000000000000524271321245476300217050ustar00rootroot00000000000000/* * 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) { float *v; v = p->verts[0]; glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glVertexPointer( 3, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v ); glTexCoordPointer( 2, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v+3 ); glDrawArrays( GL_TRIANGLE_FAN, 0, p->numverts ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); } 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; } GLfloat tex[2*p->numverts]; unsigned int index_tex = 0; v = p->verts [ 0 ]; for ( i = 0; i < p->numverts; i++, v += VERTEXSIZE ) { tex[index_tex++] = v [ 3 ] + scroll; tex[index_tex++] = v [ 4 ]; } v = p->verts [ 0 ]; glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glVertexPointer( 3, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v ); glTexCoordPointer( 2, GL_FLOAT, 0, tex ); glDrawArrays( GL_TRIANGLE_FAN, 0, p->numverts ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); } 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++) { GLfloat vtx[12]; unsigned int k; for (k=0; k<3; k++) { vtx[0+k] = p->verts [ 0 ][ k ]; vtx[3+k] = p->verts [ j - 1 ][ k ]; vtx[6+k] = p->verts [ j ][ k ]; vtx[9+k] = p->verts [ 0 ][ k ]; } glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer( 3, GL_FLOAT, 0, vtx ); glDrawArrays( GL_LINE_STRIP, 0, 4 ); glDisableClientState( GL_VERTEX_ARRAY ); } } } } 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; v = p->verts[0]; if (v == NULL) { fprintf(stderr, "BUGFIX: R_DrawGLPolyChain: v==NULL\n"); return; } glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glVertexPointer( 3, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v ); glTexCoordPointer( 2, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v+5 ); glDrawArrays( GL_TRIANGLE_FAN, 0, p->numverts ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); } } else { for ( ; p != 0; p = p->chain) { float *v; int j; v = p->verts[0]; GLfloat tex[2*p->numverts]; unsigned int index_tex = 0; for ( j = 0; j < p->numverts; j++, v += VERTEXSIZE ) { tex[index_tex++] = v [ 5 ] - soffset; tex[index_tex++] = v [ 6 ] - toffset; } v = p->verts [ 0 ]; glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glVertexPointer( 3, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v ); glTexCoordPointer( 2, GL_FLOAT, 0, tex ); glDrawArrays( GL_TRIANGLE_FAN, 0, p->numverts ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); } } } /* * 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) { // Apply overbright bits to the static lightmaps if (gl_overbrightbits->value) { R_TexEnv(GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl_overbrightbits->value); } 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) { // Apply overbright bits to the dynamic lightmaps if (gl_overbrightbits->value) { R_TexEnv(GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl_overbrightbits->value); } 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)) { ri.Sys_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) { // Apply overbright bits to the remainder lightmaps if (gl_overbrightbits->value) { R_TexEnv(GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl_overbrightbits->value); } 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); /* This is a hack ontop of a hack. Warping surfaces like those generated by R_EmitWaterPolys() don't have a lightmap. Original Quake II therefore negated the global intensity on those surfaces, because otherwise they would show up much too bright. When we implemented overbright bits this hack modified the global GL state in an incompatible way. So implement a new hack, based on overbright bits... Depending on the value set to gl_overbrightbits the result is different: 0: Old behaviour. 1: No overbright bits on the global scene but correct lightning on warping surfaces. 2: Overbright bits on the global scene but not on warping surfaces. They oversaturate otherwise. */ if (gl_overbrightbits->value) { R_TexEnv(GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 1); } else { 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 (maps < MAXLIGHTMAPS && ((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; 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; } R_TexEnv(GL_REPLACE); } 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 { R_RenderBrushPoly(psurf); } } } if (!(currententity->flags & RF_TRANSLUCENT)) { 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); } glColor4f(1, 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_TexEnv(GL_REPLACE); if (gl_lightmap->value) { R_TexEnv(GL_REPLACE); } else { R_TexEnv(GL_MODULATE); } R_DrawInlineBModel(); 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 { /* 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; glColor4f(1, 1, 1, 1); memset(gl_lms.lightmap_surfaces, 0, sizeof(gl_lms.lightmap_surfaces)); R_ClearSkyBox(); 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_7_10/src/client/refresh/gl/r_warp.c000066400000000000000000000314061321245476300216710ustar00rootroot00000000000000/* * 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}; GLfloat vtx_sky[12]; GLfloat tex_sky[8]; unsigned int index_vtx = 0; unsigned int index_tex = 0; /* 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) { ri.Sys_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; GLfloat tex[2*p->numverts]; unsigned int index_tex = 0; 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; tex[index_tex++] = s * ( 1.0 / 64 ); t = ot + r_turbsin [ (int) ( ( os * 0.125 + rdt ) * TURBSCALE ) & 255 ]; tex[index_tex++] = t * ( 1.0 / 64 ); } v = p->verts [ 0 ]; glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); glVertexPointer( 3, GL_FLOAT, VERTEXSIZE*sizeof(GLfloat), v ); glTexCoordPointer( 2, GL_FLOAT, 0, tex ); glDrawArrays( GL_TRIANGLE_FAN, 0, p->numverts ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); } } 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) { ri.Sys_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; tex_sky[index_tex++] = s; tex_sky[index_tex++] = t; vtx_sky[index_vtx++] = v[ 0 ]; vtx_sky[index_vtx++] = v[ 1 ]; vtx_sky[index_vtx++] = v[ 2 ]; } 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); glEnableClientState( GL_VERTEX_ARRAY ); glEnableClientState( GL_TEXTURE_COORD_ARRAY ); index_vtx = 0; index_tex = 0; 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 ); glVertexPointer( 3, GL_FLOAT, 0, vtx_sky ); glTexCoordPointer( 2, GL_FLOAT, 0, tex_sky ); glDrawArrays( GL_TRIANGLE_FAN, 0, 4 ); glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); } glPopMatrix(); } void RI_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 (gl_config.palettedtexture) { 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_7_10/src/client/refresh/gl3/000077500000000000000000000000001321245476300203125ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/refresh/gl3/gl3_draw.c000066400000000000000000000214551321245476300221670ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2016-2017 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. * * ======================================================================= * * Drawing of all images that are not textures * * ======================================================================= */ #include "header/local.h" unsigned d_8to24table[256]; gl3image_t *draw_chars; static GLuint vbo2D = 0, vao2D = 0, vao2Dcolor = 0; // vao2D is for textured rendering, vao2Dcolor for color-only void GL3_Draw_InitLocal(void) { /* load console characters */ draw_chars = GL3_FindImage("pics/conchars.pcx", it_pic); // set up attribute layout for 2D textured rendering glGenVertexArrays(1, &vao2D); glBindVertexArray(vao2D); glGenBuffers(1, &vbo2D); GL3_BindVBO(vbo2D); GL3_UseProgram(gl3state.si2D.shaderProgram); glEnableVertexAttribArray(GL3_ATTRIB_POSITION); // Note: the glVertexAttribPointer() configuration is stored in the VAO, not the shader or sth // (that's why I use one VAO per 2D shader) qglVertexAttribPointer(GL3_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 0); glEnableVertexAttribArray(GL3_ATTRIB_TEXCOORD); qglVertexAttribPointer(GL3_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 2*sizeof(float)); // set up attribute layout for 2D flat color rendering glGenVertexArrays(1, &vao2Dcolor); glBindVertexArray(vao2Dcolor); GL3_BindVBO(vbo2D); // yes, both VAOs share the same VBO GL3_UseProgram(gl3state.si2Dcolor.shaderProgram); glEnableVertexAttribArray(GL3_ATTRIB_POSITION); qglVertexAttribPointer(GL3_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), 0); GL3_BindVAO(0); } void GL3_Draw_ShutdownLocal(void) { glDeleteBuffers(1, &vbo2D); vbo2D = 0; glDeleteVertexArrays(1, &vao2D); vao2D = 0; glDeleteVertexArrays(1, &vao2Dcolor); vao2Dcolor = 0; } // bind the texture before calling this static void drawTexturedRectangle(float x, float y, float w, float h, float sl, float tl, float sh, float th) { /* * x,y+h x+w,y+h * sl,th--------sh,th * | | * | | * | | * sl,tl--------sh,tl * x,y x+w,y */ GLfloat vBuf[16] = { // X, Y, S, T x, y+h, sl, th, x, y, sl, tl, x+w, y+h, sh, th, x+w, y, sh, tl }; GL3_BindVAO(vao2D); // Note: while vao2D "remembers" its vbo for drawing, binding the vao does *not* // implicitly bind the vbo, so I need to explicitly bind it before glBufferData() GL3_BindVBO(vbo2D); glBufferData(GL_ARRAY_BUFFER, sizeof(vBuf), vBuf, GL_STREAM_DRAW); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); //glMultiDrawArrays(mode, first, count, drawcount) ?? } /* * 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 GL3_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; // TODO: batchen? GL3_UseProgram(gl3state.si2D.shaderProgram); GL3_Bind(draw_chars->texnum); drawTexturedRectangle(x, y, scaledSize, scaledSize, fcol, frow, fcol+size, frow+size); } gl3image_t * GL3_Draw_FindPic(char *name) { gl3image_t *gl; char fullname[MAX_QPATH]; if ((name[0] != '/') && (name[0] != '\\')) { Com_sprintf(fullname, sizeof(fullname), "pics/%s.pcx", name); gl = GL3_FindImage(fullname, it_pic); } else { gl = GL3_FindImage(name + 1, it_pic); } return gl; } void GL3_Draw_GetPicSize(int *w, int *h, char *pic) { gl3image_t *gl; gl = GL3_Draw_FindPic(pic); if (!gl) { *w = *h = -1; return; } *w = gl->width; *h = gl->height; } void GL3_Draw_StretchPic(int x, int y, int w, int h, char *pic) { gl3image_t *gl = GL3_Draw_FindPic(pic); if (!gl) { R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); return; } GL3_UseProgram(gl3state.si2D.shaderProgram); GL3_Bind(gl->texnum); drawTexturedRectangle(x, y, w, h, gl->sl, gl->tl, gl->sh, gl->th); } void GL3_Draw_PicScaled(int x, int y, char *pic, float factor) { gl3image_t *gl = GL3_Draw_FindPic(pic); if (!gl) { R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); return; } GL3_UseProgram(gl3state.si2D.shaderProgram); GL3_Bind(gl->texnum); drawTexturedRectangle(x, y, gl->width*factor, gl->height*factor, gl->sl, gl->tl, gl->sh, gl->th); } /* * This repeats a 64*64 tile graphic to fill * the screen around a sized down * refresh window. */ void GL3_Draw_TileClear(int x, int y, int w, int h, char *pic) { gl3image_t *image = GL3_Draw_FindPic(pic); if (!image) { R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); return; } GL3_UseProgram(gl3state.si2D.shaderProgram); GL3_Bind(image->texnum); drawTexturedRectangle(x, y, w, h, x/64.0f, y/64.0f, (x+w)/64.0f, (y+h)/64.0f); } /* * Fills a box of pixels with a single color */ void GL3_Draw_Fill(int x, int y, int w, int h, int c) { union { unsigned c; byte v[4]; } color; int i; if ((unsigned)c > 255) { ri.Sys_Error(ERR_FATAL, "Draw_Fill: bad color"); } color.c = d_8to24table[c]; GLfloat vBuf[8] = { // X, Y x, y+h, x, y, x+w, y+h, x+w, y }; for(i=0; i<3; ++i) { gl3state.uniCommonData.color.Elements[i] = color.v[i] * (1.0f/255.0f); } gl3state.uniCommonData.color.A = 1.0f; GL3_UpdateUBOCommon(); GL3_UseProgram(gl3state.si2Dcolor.shaderProgram); GL3_BindVAO(vao2Dcolor); GL3_BindVBO(vbo2D); glBufferData(GL_ARRAY_BUFFER, sizeof(vBuf), vBuf, GL_STREAM_DRAW); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } // in GL1 this is called R_Flash() (which just calls R_PolyBlend()) // now implemented in 2D mode and called after SetGL2D() because // it's pretty similar to GL3_Draw_FadeScreen() void GL3_Draw_Flash(const float color[4], float x, float y, float w, float h) { int i=0; GLfloat vBuf[8] = { // X, Y x, y+h, x, y, x+w, y+h, x+w, y }; glEnable(GL_BLEND); for(i=0; i<4; ++i) gl3state.uniCommonData.color.Elements[i] = color[i]; GL3_UpdateUBOCommon(); GL3_UseProgram(gl3state.si2Dcolor.shaderProgram); GL3_BindVAO(vao2Dcolor); GL3_BindVBO(vbo2D); glBufferData(GL_ARRAY_BUFFER, sizeof(vBuf), vBuf, GL_STREAM_DRAW); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDisable(GL_BLEND); } void GL3_Draw_FadeScreen(void) { float color[4] = {0, 0, 0, 0.6f}; GL3_Draw_Flash(color, 0, 0, vid.width, vid.height); } void GL3_Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, byte *data) { int i, j; GL3_Bind(0); unsigned image32[320*240]; /* was 256 * 256, but we want a bit more space */ unsigned* img = image32; if(cols*rows > 320*240) { /* in case there is a bigger video after all, * malloc enough space to hold the frame */ img = (unsigned*)malloc(cols*rows*4); } for(i=0; ivalue > gl3config.max_anisotropy) { ri.Cvar_SetValue("gl_anisotropic", gl3config.max_anisotropy); } else if (gl_anisotropic->value < 1.0) { ri.Cvar_SetValue("gl_anisotropic", 1.0); } } else { ri.Cvar_SetValue("gl_anisotropic", 0.0); } gl3image_t *glt; /* change all the existing mipmap texture objects */ for (i = 0, glt = gl3textures; i < numgl3textures; i++, glt++) { if ((glt->type != it_pic) && (glt->type != it_sky)) { GL3_SelectTMU(GL_TEXTURE0); GL3_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 (gl3config.anisotropic && gl_anisotropic->value) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropic->value); } } } } void GL3_Bind(GLuint texnum) { extern gl3image_t *draw_chars; if (gl_nobind->value && draw_chars) /* performance evaluation option */ { texnum = draw_chars->texnum; } if (gl3state.currenttexture == texnum) { return; } gl3state.currenttexture = texnum; GL3_SelectTMU(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texnum); } void GL3_BindLightmap(int lightmapnum) { int i=0; if(lightmapnum < 0 || lightmapnum >= MAX_LIGHTMAPS) { R_Printf(PRINT_ALL, "WARNING: Invalid lightmapnum %i used!\n", lightmapnum); return; } if (gl3state.currentlightmap == lightmapnum) { return; } gl3state.currentlightmap = lightmapnum; for(i=0; i = GL_TEXTURE + 1 // at least for GL_TEXTURE0 .. GL_TEXTURE31 that's true GL3_SelectTMU(GL_TEXTURE1+i); glBindTexture(GL_TEXTURE_2D, gl3state.lightmap_textureIDs[lightmapnum][i]); } } /* * Returns has_alpha */ qboolean GL3_Upload32(unsigned *data, int width, int height, qboolean mipmap) { qboolean res; int samples; int i, c; byte *scan; int comp; c = width * height; scan = ((byte *)data) + 3; samples = gl3_solid_format; comp = gl3_tex_solid_format; for (i = 0; i < c; i++, scan += 4) { if (*scan != 255) { samples = gl3_alpha_format; comp = gl3_tex_alpha_format; break; } } glTexImage2D(GL_TEXTURE_2D, 0, comp, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); res = (samples == gl3_alpha_format); if (mipmap) { // TODO: some hardware may require mipmapping disabled for NPOT textures! glGenerateMipmap(GL_TEXTURE_2D); 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 && gl3config.anisotropic && gl_anisotropic->value) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_anisotropic->value); } return res; } /* * Returns has_alpha */ qboolean GL3_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) { ri.Sys_Error(ERR_DROP, "GL3_Upload8: too large"); } 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 GL3_Upload32(trans, width, height, mipmap); } 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]; \ } \ } /* * Fill background pixels so mipmapping doesn't have haloes */ static void 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; } } /* * This is also used as an entry point for the generated r_notexture */ gl3image_t * GL3_LoadPic(char *name, byte *pic, int width, int realwidth, int height, int realheight, imagetype_t type, int bits) { gl3image_t *image = NULL; GLuint texNum=0; int i; qboolean nolerp = false; if (gl_nolerp_list != NULL && gl_nolerp_list->string != NULL) { nolerp = strstr(gl_nolerp_list->string, name) != NULL; } /* find a free gl3image_t */ for (i = 0, image = gl3textures; i < numgl3textures; i++, image++) { if (image->texnum == 0) { break; } } if (i == numgl3textures) { if (numgl3textures == MAX_GL3TEXTURES) { ri.Sys_Error(ERR_DROP, "MAX_GLTEXTURES"); } numgl3textures++; } image = &gl3textures[i]; if (strlen(name) >= sizeof(image->name)) { ri.Sys_Error(ERR_DROP, "GL3_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)) { FloodFillSkin(pic, width, height); } // image->scrap = false; // TODO: reintroduce scrap? would allow optimizations in 2D rendering.. glGenTextures(1, &texNum); image->texnum = texNum; GL3_SelectTMU(GL_TEXTURE0); GL3_Bind(texNum); if (bits == 8) { image->has_alpha = GL3_Upload8(pic, width, height, (image->type != it_pic && image->type != it_sky), image->type == it_sky); } else { image->has_alpha = GL3_Upload32((unsigned *)pic, width, height, (image->type != it_pic && image->type != it_sky)); } if (realwidth && realheight) { if ((realwidth <= image->width) && (realheight <= image->height)) { image->width = realwidth; image->height = realheight; } else { R_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; if (nolerp) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } #if 0 // TODO: the scrap could allow batch rendering 2D stuff? not sure it's worth the hassle.. /* load little pics into the scrap */ if (!nolerp && (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 { R_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; if (nolerp) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } } #endif // 0 return image; } static gl3image_t * LoadWal(char *origname) { miptex_t *mt; int width, height, ofs; gl3image_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)); } ri.FS_LoadFile(name, (void **)&mt); if (!mt) { R_Printf(PRINT_ALL, "LoadWal: can't load %s\n", name); return gl3_notexture; } width = LittleLong(mt->width); height = LittleLong(mt->height); ofs = LittleLong(mt->offsets[0]); image = GL3_LoadPic(name, (byte *)mt + ofs, width, 0, height, 0, it_wall, 8); ri.FS_FreeFile((void *)mt); return image; } /* * Finds or loads the given image */ gl3image_t * GL3_FindImage(char *name, imagetype_t type) { gl3image_t *image; int i, len; byte *pic; 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 = gl3textures; i < numgl3textures; i++, image++) { if (!strcmp(name, image->name)) { image->registration_sequence = registration_sequence; return image; } } /* load the pic from disk */ pic = 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 = GL3_LoadPic(name, pic, width, realwidth, height, realheight, type, 32); } else { /* PCX if no TGA/PNG/JPEG available (exists always) */ LoadPCX(name, &pic, NULL, &width, &height); if (!pic) { /* No texture found */ return NULL; } /* Upload the PCX */ image = GL3_LoadPic(name, pic, width, 0, height, 0, type, 8); } } else /* gl_retexture is not set */ { LoadPCX(name, &pic, NULL, &width, &height); if (!pic) { return NULL; } image = GL3_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 = GL3_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; */ if(LoadSTB(name, ext, &pic, &width, &height)) { image = GL3_LoadPic(name, pic, width, realwidth, height, realheight, type, 32); } } else { return NULL; } if (pic) { free(pic); } return image; } gl3image_t * GL3_RegisterSkin(char *name) { return GL3_FindImage(name, it_skin); } /* * Any image that was not touched on * this registration sequence * will be freed. */ void GL3_FreeUnusedImages(void) { int i; gl3image_t *image; /* never free r_notexture or particle texture */ gl3_notexture->registration_sequence = registration_sequence; gl3_particletexture->registration_sequence = registration_sequence; for (i = 0, image = gl3textures; i < numgl3textures; 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, &image->texnum); memset(image, 0, sizeof(*image)); } } void GL3_ShutdownImages(void) { int i; gl3image_t *image; for (i = 0, image = gl3textures; i < numgl3textures; i++, image++) { if (!image->registration_sequence) { continue; /* free image_t slot */ } /* free it */ glDeleteTextures(1, &image->texnum); memset(image, 0, sizeof(*image)); } } static qboolean IsNPOT(int v) { unsigned int uv = v; // just try all the power of two values between 1 and 1 << 15 (32k) for(unsigned int i=0; i<16; ++i) { unsigned int pot = (1u << i); if(uv & pot) { return uv != pot; } } return true; } void GL3_ImageList_f(void) { int i, texels=0; gl3image_t *image; const char *formatstrings[2] = { "RGB ", "RGBA" }; const char* potstrings[2] = { " POT", "NPOT" }; R_Printf(PRINT_ALL, "------------------\n"); for (i = 0, image = gl3textures; i < numgl3textures; i++, image++) { int w, h; qboolean isNPOT = false; if (image->texnum == 0) { continue; } w = image->width; h = image->height; isNPOT = IsNPOT(w) || IsNPOT(h); texels += w*h; switch (image->type) { case it_skin: R_Printf(PRINT_ALL, "M"); break; case it_sprite: R_Printf(PRINT_ALL, "S"); break; case it_wall: R_Printf(PRINT_ALL, "W"); break; case it_pic: R_Printf(PRINT_ALL, "P"); break; case it_sky: R_Printf(PRINT_ALL, "Y"); break; default: R_Printf(PRINT_ALL, "?"); break; } R_Printf(PRINT_ALL, " %3i %3i %s %s: %s\n", w, h, formatstrings[image->has_alpha], potstrings[isNPOT], image->name); } R_Printf(PRINT_ALL, "Total texel count (not counting mipmaps): %i\n", texels); } yquake2-QUAKE2_7_10/src/client/refresh/gl3/gl3_light.c000066400000000000000000000231401321245476300223320ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2016-2017 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. * * ======================================================================= * * Lightmaps and dynamic lighting * * ======================================================================= */ #include "header/local.h" extern gl3lightmapstate_t gl3_lms; #define DLIGHT_CUTOFF 64 static int r_dlightframecount; static vec3_t pointcolor; static cplane_t *lightplane; /* used as shadow plane */ vec3_t lightspot; void // bit: 1 << i for light number i, will be or'ed into msurface_t::dlightbits if surface is affected by this light GL3_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) { GL3_MarkLights(light, bit, node->children[0]); return; } if (dist < -light->intensity + DLIGHT_CUTOFF) { GL3_MarkLights(light, bit, node->children[1]); return; } /* mark the polygons */ surf = gl3_worldmodel->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { if (surf->dlightframe != r_dlightframecount) { surf->dlightbits = 0; surf->dlightframe = r_dlightframecount; } 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; } surf->dlightbits |= bit; } GL3_MarkLights(light, bit, node->children[0]); GL3_MarkLights(light, bit, node->children[1]); } void GL3_PushDlights(void) { int i; dlight_t *l; /* because the count hasn't advanced yet for this frame */ r_dlightframecount = gl3_framecount + 1; l = gl3_newrefdef.dlights; gl3state.uniLightsData.numDynLights = gl3_newrefdef.num_dlights; for (i = 0; i < gl3_newrefdef.num_dlights; i++, l++) { gl3UniDynLight* udl = &gl3state.uniLightsData.dynLights[i]; GL3_MarkLights(l, 1 << i, gl3_worldmodel->nodes); VectorCopy(l->origin, udl->origin); VectorCopy(l->color, udl->color); udl->intensity = l->intensity; } assert(MAX_DLIGHTS == 32 && "If MAX_DLIGHTS changes, remember to adjust the uniform buffer definition in the shader!"); if(i < MAX_DLIGHTS) { memset(&gl3state.uniLightsData.dynLights[i], 0, (MAX_DLIGHTS-i)*sizeof(gl3state.uniLightsData.dynLights[0])); } GL3_UpdateUBOLights(); } static int 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 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 = 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 = gl3_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 < MAX_LIGHTMAPS_PER_SURFACE && surf->styles[maps] != 255; maps++) { for (i = 0; i < 3; i++) { scale[i] = gl_modulate->value * gl3_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 RecursiveLightPoint(node->children[!side], mid, end); } void GL3_LightPoint(vec3_t p, vec3_t color) { vec3_t end; float r; int lnum; dlight_t *dl; vec3_t dist; float add; if (!gl3_worldmodel->lightdata || !currententity) { color[0] = color[1] = color[2] = 1.0; return; } end[0] = p[0]; end[1] = p[1]; end[2] = p[2] - 2048; // TODO: don't just aggregate the color, but also save position of brightest+nearest light // for shadow position and maybe lighting on model? r = RecursiveLightPoint(gl3_worldmodel->nodes, p, end); if (r == -1) { VectorCopy(vec3_origin, color); } else { VectorCopy(pointcolor, color); } /* add dynamic lights */ dl = gl3_newrefdef.dlights; for (lnum = 0; lnum < gl3_newrefdef.num_dlights; lnum++, dl++) { VectorSubtract(currententity->origin, dl->origin, dist); add = dl->intensity - VectorLength(dist); add *= (1.0f / 256.0f); if (add > 0) { VectorMA(color, add, dl->color, color); } } VectorScale(color, gl_modulate->value, color); } /* * Combine and scale multiple lightmaps into the floating format in blocklights */ void GL3_BuildLightMap(msurface_t *surf, int offsetInLMbuf, int stride) { int smax, tmax; int r, g, b, a, max; int i, j, size, map, numMaps; byte *lightmap; if (surf->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP)) { ri.Sys_Error(ERR_DROP, "GL3_BuildLightMap called for non-lit surface"); } smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; size = smax * tmax; stride -= (smax << 2); if (size > 34*34*3) { ri.Sys_Error(ERR_DROP, "Bad s_blocklights size"); } // count number of lightmaps surf actually has for (numMaps = 0; numMaps < MAX_LIGHTMAPS_PER_SURFACE && surf->styles[numMaps] != 255; ++numMaps) {} if (!surf->samples) { // no lightmap samples? set at least one lightmap to fullbright, rest to 0 as normal if (numMaps == 0) numMaps = 1; // make sure at least one lightmap is set to fullbright for (map = 0; map < MAX_LIGHTMAPS_PER_SURFACE; ++map) { // we always create 4 (MAX_LIGHTMAPS_PER_SURFACE) lightmaps. // if surf has less (numMaps < 4), the remaining ones are zeroed out. // this makes sure that all 4 lightmap textures in gl3state.lightmap_textureIDs[i] have the same layout // and the shader can use the same texture coordinates for all of them int c = (map < numMaps) ? 255 : 0; byte* dest = gl3_lms.lightmap_buffers[map] + offsetInLMbuf; for (i = 0; i < tmax; i++, dest += stride) { memset(dest, c, 4*smax); dest += 4*smax; } } return; } /* add all the lightmaps */ // Note: dynamic lights aren't handled here anymore, they're handled in the shader // as we don't apply scale here anymore, nor blend the numMaps lightmaps together, // the code has gotten a lot easier and we can copy directly from surf->samples to dest // without converting to float first etc lightmap = surf->samples; for(map=0; map 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; dest[0] = r; dest[1] = g; dest[2] = b; dest[3] = a; dest += 4; ++idxInLightmap; } } lightmap += size * 3; /* skip to next lightmap */ } for ( ; map < MAX_LIGHTMAPS_PER_SURFACE; ++map) { // like above, fill up remaining lightmaps with 0 byte* dest = gl3_lms.lightmap_buffers[map] + offsetInLMbuf; for (i = 0; i < tmax; i++, dest += stride) { memset(dest, 0, 4*smax); dest += 4*smax; } } } yquake2-QUAKE2_7_10/src/client/refresh/gl3/gl3_lightmap.c000066400000000000000000000143431321245476300230350ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2016-2017 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. * * ======================================================================= * * Lightmap handling * * ======================================================================= */ #include "header/local.h" #define TEXNUM_LIGHTMAPS 1024 extern gl3lightmapstate_t gl3_lms; void GL3_LM_InitBlock(void) { memset(gl3_lms.allocated, 0, sizeof(gl3_lms.allocated)); } void GL3_LM_UploadBlock(void) { int map; // NOTE: we don't use the dynamic lightmap anymore - all lightmaps are loaded at level load // and not changed after that. they're blended dynamically depending on light styles // though, and dynamic lights are (will be) applied in shader, hopefully per fragment. GL3_BindLightmap(gl3_lms.current_lightmap_texture); // upload all 4 lightmaps for(map=0; map < MAX_LIGHTMAPS_PER_SURFACE; ++map) { GL3_SelectTMU(GL_TEXTURE1+map); // this relies on GL_TEXTURE2 being GL_TEXTURE1+1 etc glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gl3_lms.internal_format = GL_LIGHTMAP_FORMAT; glTexImage2D(GL_TEXTURE_2D, 0, gl3_lms.internal_format, BLOCK_WIDTH, BLOCK_HEIGHT, 0, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, gl3_lms.lightmap_buffers[map]); } if (++gl3_lms.current_lightmap_texture == MAX_LIGHTMAPS) { ri.Sys_Error(ERR_DROP, "LM_UploadBlock() - MAX_LIGHTMAPS exceeded\n"); } } /* * returns a texture number and the position inside it */ qboolean GL3_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 (gl3_lms.allocated[i + j] >= best) { break; } if (gl3_lms.allocated[i + j] > best2) { best2 = gl3_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++) { gl3_lms.allocated[*x + i] = best + h; } return true; } void GL3_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; vec3_t normal; /* reconstruct the polygon */ pedges = currentmodel->edges; lnumverts = fa->numedges; VectorClear(total); /* draw texture */ poly = Hunk_Alloc(sizeof(glpoly_t) + (lnumverts - 4) * sizeof(gl3_3D_vtx_t)); poly->next = fa->polys; poly->flags = fa->flags; fa->polys = poly; poly->numverts = lnumverts; VectorCopy(fa->plane->normal, normal); if(fa->flags & SURF_PLANEBACK) { // if for some reason the normal sticks to the back of the plane, invert it // so it's usable for the shader for (i=0; i<3; ++i) normal[i] = -normal[i]; } for (i = 0; i < lnumverts; i++) { gl3_3D_vtx_t* vert = &poly->vertices[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, vert->pos); vert->texCoord[0] = s; vert->texCoord[1] = t; /* lightmap texture coordinates */ s = DotProduct(vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s -= fa->texturemins[0]; s += fa->light_s * 16; s += 8; s /= BLOCK_WIDTH * 16; /* fa->texinfo->texture->width; */ t = DotProduct(vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t -= fa->texturemins[1]; t += fa->light_t * 16; t += 8; t /= BLOCK_HEIGHT * 16; /* fa->texinfo->texture->height; */ vert->lmTexCoord[0] = s; vert->lmTexCoord[1] = t; VectorCopy(normal, vert->normal); vert->lightFlags = 0; } poly->numverts = lnumverts; } void GL3_LM_CreateSurfaceLightmap(msurface_t *surf) { int smax, tmax; if (surf->flags & (SURF_DRAWSKY | SURF_DRAWTURB)) { return; } smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; if (!GL3_LM_AllocBlock(smax, tmax, &surf->light_s, &surf->light_t)) { GL3_LM_UploadBlock(); GL3_LM_InitBlock(); if (!GL3_LM_AllocBlock(smax, tmax, &surf->light_s, &surf->light_t)) { ri.Sys_Error(ERR_FATAL, "Consecutive calls to LM_AllocBlock(%d,%d) failed\n", smax, tmax); } } surf->lightmaptexturenum = gl3_lms.current_lightmap_texture; GL3_BuildLightMap(surf, (surf->light_t * BLOCK_WIDTH + surf->light_s) * LIGHTMAP_BYTES, BLOCK_WIDTH * LIGHTMAP_BYTES); } void GL3_LM_BeginBuildingLightmaps(gl3model_t *m) { static lightstyle_t lightstyles[MAX_LIGHTSTYLES]; int i; memset(gl3_lms.allocated, 0, sizeof(gl3_lms.allocated)); gl3_framecount = 1; /* no dlightcache */ /* 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; } gl3_newrefdef.lightstyles = lightstyles; gl3_lms.current_lightmap_texture = 0; gl3_lms.internal_format = GL_LIGHTMAP_FORMAT; // Note: the dynamic lightmap used to be initialized here, we don't use that anymore. } void GL3_LM_EndBuildingLightmaps(void) { GL3_LM_UploadBlock(); } yquake2-QUAKE2_7_10/src/client/refresh/gl3/gl3_main.c000066400000000000000000001357701321245476300221640ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2016-2017 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. * * ======================================================================= * * Refresher setup and main part of the frame generation, for OpenGL3 * * ======================================================================= */ #include "../../header/ref.h" #include "header/local.h" #define HANDMADE_MATH_IMPLEMENTATION #include "header/HandmadeMath.h" #define DG_DYNARR_IMPLEMENTATION #include "header/DG_dynarr.h" #define REF_VERSION "Yamagi Quake II OpenGL3 Refresher" refimport_t ri; gl3config_t gl3config; gl3state_t gl3state; unsigned gl3_rawpalette[256]; /* screen size info */ refdef_t gl3_newrefdef; viddef_t vid; gl3model_t *gl3_worldmodel; gl3model_t *currentmodel; entity_t *currententity; float gl3depthmin=0.0f, gl3depthmax=1.0f; cplane_t frustum[4]; /* view origin */ vec3_t vup; vec3_t vpn; vec3_t vright; vec3_t gl3_origin; hmm_mat4 gl3_projectionMatrix; // eye cord -> clip coord hmm_mat4 gl3_world_matrix; // the view matrix: world coord -> eye coord int gl3_visframecount; /* bumped when going to a new PVS */ int gl3_framecount; /* used for dlight push checking */ int c_brush_polys, c_alias_polys; static float v_blend[4]; /* final blending color */ int gl3_viewcluster, gl3_viewcluster2, gl3_oldviewcluster, gl3_oldviewcluster2; const hmm_mat4 gl3_identityMat4 = {{ {1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, }}; cvar_t *gl_msaa_samples; cvar_t *gl_swapinterval; cvar_t *gl_retexturing; cvar_t *vid_fullscreen; cvar_t *gl_mode; cvar_t *gl_customwidth; cvar_t *gl_customheight; cvar_t *vid_gamma; cvar_t *gl_anisotropic; cvar_t *gl_texturemode; cvar_t *gl_drawbuffer; cvar_t *gl_clear; cvar_t *gl3_particle_size; cvar_t *gl3_particle_fade_factor; cvar_t *gl3_particle_square; cvar_t *gl_lefthand; cvar_t *gl_farsee; cvar_t *gl3_intensity; cvar_t *gl3_intensity_2D; cvar_t *gl_lightlevel; cvar_t *gl3_overbrightbits; cvar_t *gl_norefresh; cvar_t *gl_drawentities; cvar_t *gl_drawworld; cvar_t *gl_nolerp_list; cvar_t *gl_nobind; cvar_t *gl_lockpvs; cvar_t *gl_novis; cvar_t *gl_speeds; cvar_t *gl_finish; cvar_t *gl_cull; cvar_t *gl_zfix; cvar_t *gl_fullbright; cvar_t *gl_modulate; cvar_t *gl_lightmap; cvar_t *gl_shadows; // no gl_stencilshadows, always use stencil (if available) cvar_t *gl_dynamic; cvar_t *gl3_debugcontext; // Yaw-Pitch-Roll // equivalent to R_z * R_y * R_x where R_x is the trans matrix for rotating around X axis for aroundXdeg static hmm_mat4 rotAroundAxisZYX(float aroundZdeg, float aroundYdeg, float aroundXdeg) { // Naming of variables is consistent with http://planning.cs.uiuc.edu/node102.html // and https://de.wikipedia.org/wiki/Roll-Nick-Gier-Winkel#.E2.80.9EZY.E2.80.B2X.E2.80.B3-Konvention.E2.80.9C float alpha = HMM_ToRadians(aroundZdeg); float beta = HMM_ToRadians(aroundYdeg); float gamma = HMM_ToRadians(aroundXdeg); float sinA = HMM_SinF(alpha); float cosA = HMM_CosF(alpha); // TODO: or sincosf(alpha, &sinA, &cosA); ?? (not a standard function) float sinB = HMM_SinF(beta); float cosB = HMM_CosF(beta); float sinG = HMM_SinF(gamma); float cosG = HMM_CosF(gamma); hmm_mat4 ret = {{ { cosA*cosB, sinA*cosB, -sinB, 0 }, // first *column* { cosA*sinB*sinG - sinA*cosG, sinA*sinB*sinG + cosA*cosG, cosB*sinG, 0 }, { cosA*sinB*cosG + sinA*sinG, sinA*sinB*cosG - cosA*sinG, cosB*cosG, 0 }, { 0, 0, 0, 1 } }}; return ret; } void GL3_RotateForEntity(entity_t *e) { // angles: pitch (around y), yaw (around z), roll (around x) // rot matrices to be multiplied in order Z, Y, X (yaw, pitch, roll) hmm_mat4 transMat = rotAroundAxisZYX(e->angles[1], -e->angles[0], -e->angles[2]); for(int i=0; i<3; ++i) { transMat.Elements[3][i] = e->origin[i]; // set translation } gl3state.uni3DData.transModelMat4 = HMM_MultiplyMat4(gl3state.uni3DData.transModelMat4, transMat); GL3_UpdateUBO3D(); } static void GL3_Strings(void) { GLint i, numExtensions; R_Printf(PRINT_ALL, "GL_VENDOR: %s\n", gl3config.vendor_string); R_Printf(PRINT_ALL, "GL_RENDERER: %s\n", gl3config.renderer_string); R_Printf(PRINT_ALL, "GL_VERSION: %s\n", gl3config.version_string); R_Printf(PRINT_ALL, "GL_SHADING_LANGUAGE_VERSION: %s\n", gl3config.glsl_version_string); glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); R_Printf(PRINT_ALL, "GL_EXTENSIONS:"); for(i = 0; i < numExtensions; i++) { R_Printf(PRINT_ALL, " %s", (const char*)glGetStringi(GL_EXTENSIONS, i)); } R_Printf(PRINT_ALL, "\n"); } static void GL3_Register(void) { gl_lefthand = ri.Cvar_Get("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE); gl_farsee = ri.Cvar_Get("gl_farsee", "0", CVAR_LATCH | CVAR_ARCHIVE); gl_drawbuffer = ri.Cvar_Get("gl_drawbuffer", "GL_BACK", 0); gl_swapinterval = ri.Cvar_Get("gl_swapinterval", "1", CVAR_ARCHIVE); gl_msaa_samples = ri.Cvar_Get ( "gl_msaa_samples", "0", CVAR_ARCHIVE ); gl_retexturing = ri.Cvar_Get("gl_retexturing", "1", CVAR_ARCHIVE); gl3_debugcontext = ri.Cvar_Get("gl3_debugcontext", "0", 0); gl_mode = ri.Cvar_Get("gl_mode", "4", CVAR_ARCHIVE); gl_customwidth = ri.Cvar_Get("gl_customwidth", "1024", CVAR_ARCHIVE); gl_customheight = ri.Cvar_Get("gl_customheight", "768", CVAR_ARCHIVE); gl3_particle_size = ri.Cvar_Get("gl3_particle_size", "40", CVAR_ARCHIVE); gl3_particle_fade_factor = ri.Cvar_Get("gl3_particle_fade_factor", "1.2", CVAR_ARCHIVE); gl3_particle_square = ri.Cvar_Get("gl3_particle_square", "0", CVAR_ARCHIVE); gl_norefresh = ri.Cvar_Get("gl_norefresh", "0", 0); gl_drawentities = ri.Cvar_Get("gl_drawentities", "1", 0); gl_drawworld = ri.Cvar_Get("gl_drawworld", "1", 0); gl_fullbright = ri.Cvar_Get("gl_fullbright", "0", 0); /* don't bilerp characters and crosshairs */ gl_nolerp_list = ri.Cvar_Get("gl_nolerp_list", "pics/conchars.pcx pics/ch1.pcx pics/ch2.pcx pics/ch3.pcx", 0); gl_nobind = ri.Cvar_Get("gl_nobind", "0", 0); gl_texturemode = ri.Cvar_Get("gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE); gl_anisotropic = ri.Cvar_Get("gl_anisotropic", "0", CVAR_ARCHIVE); vid_fullscreen = ri.Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); vid_gamma = ri.Cvar_Get("vid_gamma", "1.2", CVAR_ARCHIVE); gl3_intensity = ri.Cvar_Get("gl3_intensity", "1.5", CVAR_ARCHIVE); gl3_intensity_2D = ri.Cvar_Get("gl3_intensity_2D", "1.5", CVAR_ARCHIVE); gl_lightlevel = ri.Cvar_Get("gl_lightlevel", "0", 0); gl3_overbrightbits = ri.Cvar_Get("gl3_overbrightbits", "1.3", CVAR_ARCHIVE); gl_lightmap = ri.Cvar_Get("gl_lightmap", "0", 0); gl_shadows = ri.Cvar_Get("gl_shadows", "0", CVAR_ARCHIVE); gl_modulate = ri.Cvar_Get("gl_modulate", "1", CVAR_ARCHIVE); gl_zfix = ri.Cvar_Get("gl_zfix", "0", 0); gl_clear = ri.Cvar_Get("gl_clear", "0", 0); gl_cull = ri.Cvar_Get("gl_cull", "1", 0); gl_lockpvs = ri.Cvar_Get("gl_lockpvs", "0", 0); gl_novis = ri.Cvar_Get("gl_novis", "0", 0); gl_speeds = ri.Cvar_Get("gl_speeds", "0", 0); gl_finish = ri.Cvar_Get("gl_finish", "0", CVAR_ARCHIVE); gl_dynamic = ri.Cvar_Get("gl_dynamic", "1", 0); #if 0 // TODO! //gl_lefthand = ri.Cvar_Get("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE); //gl_farsee = ri.Cvar_Get("gl_farsee", "0", CVAR_LATCH | CVAR_ARCHIVE); //gl_norefresh = ri.Cvar_Get("gl_norefresh", "0", 0); //gl_fullbright = ri.Cvar_Get("gl_fullbright", "0", 0); //gl_drawentities = ri.Cvar_Get("gl_drawentities", "1", 0); //gl_drawworld = ri.Cvar_Get("gl_drawworld", "1", 0); //gl_novis = ri.Cvar_Get("gl_novis", "0", 0); //gl_lerpmodels = ri.Cvar_Get("gl_lerpmodels", "1", 0); NOTE: screw this, it looks horrible without //gl_speeds = ri.Cvar_Get("gl_speeds", "0", 0); //gl_lightlevel = ri.Cvar_Get("gl_lightlevel", "0", 0); //gl_overbrightbits = ri.Cvar_Get("gl_overbrightbits", "0", CVAR_ARCHIVE); gl_particle_min_size = ri.Cvar_Get("gl_particle_min_size", "2", CVAR_ARCHIVE); gl_particle_max_size = ri.Cvar_Get("gl_particle_max_size", "40", CVAR_ARCHIVE); //gl_particle_size = ri.Cvar_Get("gl_particle_size", "40", CVAR_ARCHIVE); gl_particle_att_a = ri.Cvar_Get("gl_particle_att_a", "0.01", CVAR_ARCHIVE); gl_particle_att_b = ri.Cvar_Get("gl_particle_att_b", "0.0", CVAR_ARCHIVE); gl_particle_att_c = ri.Cvar_Get("gl_particle_att_c", "0.01", CVAR_ARCHIVE); //gl_modulate = ri.Cvar_Get("gl_modulate", "1", CVAR_ARCHIVE); //gl_mode = ri.Cvar_Get("gl_mode", "4", CVAR_ARCHIVE); //gl_lightmap = ri.Cvar_Get("gl_lightmap", "0", 0); //gl_shadows = ri.Cvar_Get("gl_shadows", "0", CVAR_ARCHIVE); //gl_stencilshadow = ri.Cvar_Get("gl_stencilshadow", "0", CVAR_ARCHIVE); //gl_dynamic = ri.Cvar_Get("gl_dynamic", "1", 0); //gl_nobind = ri.Cvar_Get("gl_nobind", "0", 0); gl_round_down = ri.Cvar_Get("gl_round_down", "1", 0); gl_picmip = ri.Cvar_Get("gl_picmip", "0", 0); gl_showtris = ri.Cvar_Get("gl_showtris", "0", 0); gl_showbbox = Cvar_Get("gl_showbbox", "0", 0); //gl_ztrick = ri.Cvar_Get("gl_ztrick", "0", 0); NOTE: dump this. //gl_zfix = ri.Cvar_Get("gl_zfix", "0", 0); //gl_finish = ri.Cvar_Get("gl_finish", "0", CVAR_ARCHIVE); gl_clear = ri.Cvar_Get("gl_clear", "0", 0); // gl_cull = ri.Cvar_Get("gl_cull", "1", 0); gl_polyblend = ri.Cvar_Get("gl_polyblend", "1", 0); //gl_flashblend = ri.Cvar_Get("gl_flashblend", "0", 0); //gl_texturemode = ri.Cvar_Get("gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE); gl_texturealphamode = ri.Cvar_Get("gl_texturealphamode", "default", CVAR_ARCHIVE); gl_texturesolidmode = ri.Cvar_Get("gl_texturesolidmode", "default", CVAR_ARCHIVE); //gl_anisotropic = ri.Cvar_Get("gl_anisotropic", "0", CVAR_ARCHIVE); //gl_lockpvs = ri.Cvar_Get("gl_lockpvs", "0", 0); //gl_palettedtexture = ri.Cvar_Get("gl_palettedtexture", "0", CVAR_ARCHIVE); NOPE. gl_pointparameters = ri.Cvar_Get("gl_pointparameters", "1", CVAR_ARCHIVE); //gl_drawbuffer = ri.Cvar_Get("gl_drawbuffer", "GL_BACK", 0); //gl_swapinterval = ri.Cvar_Get("gl_swapinterval", "1", CVAR_ARCHIVE); gl_saturatelighting = ri.Cvar_Get("gl_saturatelighting", "0", 0); //vid_fullscreen = ri.Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); //vid_gamma = ri.Cvar_Get("vid_gamma", "1.0", CVAR_ARCHIVE); //gl_customwidth = ri.Cvar_Get("gl_customwidth", "1024", CVAR_ARCHIVE); //gl_customheight = ri.Cvar_Get("gl_customheight", "768", CVAR_ARCHIVE); //gl_msaa_samples = ri.Cvar_Get ( "gl_msaa_samples", "0", CVAR_ARCHIVE ); //gl_retexturing = ri.Cvar_Get("gl_retexturing", "1", CVAR_ARCHIVE); gl_stereo = ri.Cvar_Get( "gl_stereo", "0", CVAR_ARCHIVE ); gl_stereo_separation = ri.Cvar_Get( "gl_stereo_separation", "-0.4", CVAR_ARCHIVE ); gl_stereo_anaglyph_colors = ri.Cvar_Get( "gl_stereo_anaglyph_colors", "rc", CVAR_ARCHIVE ); gl_stereo_convergence = ri.Cvar_Get( "gl_stereo_convergence", "1", CVAR_ARCHIVE ); #endif // 0 ri.Cmd_AddCommand("imagelist", GL3_ImageList_f); ri.Cmd_AddCommand("screenshot", GL3_ScreenShot); ri.Cmd_AddCommand("modellist", GL3_Mod_Modellist_f); ri.Cmd_AddCommand("gl_strings", GL3_Strings); } /* * Changes the video mode */ // the following is only used in the next to functions, // no need to put it in a header enum { rserr_ok, rserr_invalid_fullscreen, rserr_invalid_mode, rserr_unknown }; static int SetMode_impl(int *pwidth, int *pheight, int mode, int fullscreen) { R_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) && !ri.Vid_GetModeInfo(pwidth, pheight, mode)) { R_Printf(PRINT_ALL, " invalid mode\n"); return rserr_invalid_mode; } R_Printf(PRINT_ALL, " %d %d\n", *pwidth, *pheight); if (!ri.GLimp_InitGraphics(fullscreen, pwidth, pheight)) { return rserr_invalid_mode; } return rserr_ok; } static qboolean GL3_SetMode(void) { int err; int fullscreen; fullscreen = (int)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 = SetMode_impl(&vid.width, &vid.height, gl_mode->value, fullscreen)) == rserr_ok) { if (gl_mode->value == -1) { gl3state.prev_mode = 4; /* safe default for custom mode */ } else { gl3state.prev_mode = gl_mode->value; } } else { if (err == rserr_invalid_fullscreen) { ri.Cvar_SetValue("vid_fullscreen", 0); vid_fullscreen->modified = false; R_Printf(PRINT_ALL, "ref_gl3::GL3_SetMode() - fullscreen unavailable in this mode\n"); if ((err = SetMode_impl(&vid.width, &vid.height, gl_mode->value, 0)) == rserr_ok) { return true; } } else if (err == rserr_invalid_mode) { R_Printf(PRINT_ALL, "ref_gl3::GL3_SetMode() - invalid mode\n"); if (gl_msaa_samples->value != 0.0f) { R_Printf(PRINT_ALL, "gl_msaa_samples was %d - will try again with gl_msaa_samples = 0\n", (int)gl_msaa_samples->value); ri.Cvar_SetValue("gl_msaa_samples", 0.0f); gl_msaa_samples->modified = false; if ((err = SetMode_impl(&vid.width, &vid.height, gl_mode->value, 0)) == rserr_ok) { return true; } } if(gl_mode->value == gl3state.prev_mode) { // trying again would result in a crash anyway, give up already // (this would happen if your initing fails at all and your resolution already was 640x480) return false; } ri.Cvar_SetValue("gl_mode", gl3state.prev_mode); gl_mode->modified = false; } /* try setting it back to something safe */ if ((err = SetMode_impl(&vid.width, &vid.height, gl3state.prev_mode, 0)) != rserr_ok) { R_Printf(PRINT_ALL, "ref_gl3::GL3_SetMode() - could not revert to safe mode\n"); return false; } } return true; } // only needed (and allowed!) if using OpenGL compatibility profile, it's not in 3.2 core enum { QGL_POINT_SPRITE = 0x8861 }; static qboolean GL3_Init(void) { Swap_Init(); // FIXME: for fucks sake, this doesn't have to be done at runtime! R_Printf(PRINT_ALL, "Refresh: " REF_VERSION "\n"); R_Printf(PRINT_ALL, "Client: " YQ2VERSION "\n\n"); /* Options */ R_Printf(PRINT_ALL, "Refresher build options:\n"); R_Printf(PRINT_ALL, " + Retexturing support\n\n"); if(sizeof(float) != sizeof(GLfloat)) { // if this ever happens, things would explode because we feed vertex arrays and UBO data // using floats to OpenGL, which expects GLfloat (can't easily change, those floats are from HMM etc) // (but to be honest I very much doubt this will ever happen.) R_Printf(PRINT_ALL, "ref_gl3: sizeof(float) != sizeof(GLfloat) - we're in real trouble here.\n"); return false; } GL3_Draw_GetPalette(); GL3_Register(); /* initialize OS-specific parts of OpenGL */ if (!ri.GLimp_Init()) { //QGL_Shutdown(); return false; } /* set our "safe" mode */ gl3state.prev_mode = 4; //gl_state.stereo_mode = gl_stereo->value; /* create the window and set up the context */ if (!GL3_SetMode()) { R_Printf(PRINT_ALL, "ref_gl3::R_Init() - could not R_SetMode()\n"); return false; } ri.Vid_MenuInit(); /* get our various GL strings */ gl3config.vendor_string = (const char*)glGetString(GL_VENDOR); gl3config.renderer_string = (const char*)glGetString(GL_RENDERER); gl3config.version_string = (const char*)glGetString(GL_VERSION); gl3config.glsl_version_string = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION); R_Printf(PRINT_ALL, "\nOpenGL setting:\n"); GL3_Strings(); /* if (gl_config.major_version < 3) { // if (gl_config.major_version == 3 && gl_config.minor_version < 2) { QGL_Shutdown(); R_Printf(PRINT_ALL, "Support for OpenGL 3.2 is not available\n"); return false; } } */ R_Printf(PRINT_ALL, "\n\nProbing for OpenGL extensions:\n"); /* Anisotropic */ R_Printf(PRINT_ALL, " - Anisotropic Filtering: "); if(gl3config.anisotropic) { glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl3config.max_anisotropy); R_Printf(PRINT_ALL, "Max level: %ux\n", (int)gl3config.max_anisotropy); } else { gl3config.max_anisotropy = 0.0; R_Printf(PRINT_ALL, "Not supported\n"); } #ifdef SDL2 if(gl3config.debug_output) { R_Printf(PRINT_ALL, " - OpenGL Debug Output: Supported "); if(gl3_debugcontext->value == 0.0f) { R_Printf(PRINT_ALL, "(but disabled with gl3_debugcontext = 0)\n"); } else { R_Printf(PRINT_ALL, "and enabled with gl3_debugcontext = %i\n", (int)gl3_debugcontext->value); } } else { R_Printf(PRINT_ALL, " - OpenGL Debug Output: Not Supported\n"); } #else // SDL1.2 - no debug output R_Printf(PRINT_ALL, " - OpenGL Debug Output: Not Supported when using SDL1.2\n"); #endif if(gl3config.compat_profile) { // for some fucking reason particles (GL_POINT) don't work in compatibility profiles // without setting this.. SDL1.2 only gives compat profiles and we might wanna support // them for SDL2 as well, so broken screengrab software etc that uses GL1 functions still works // (GL_POINT_SPRITE is not even part of 3.2core, it was only in GL2 and was deprecated afterwards) glEnable(QGL_POINT_SPRITE); } // generate texture handles for all possible lightmaps glGenTextures(MAX_LIGHTMAPS*MAX_LIGHTMAPS_PER_SURFACE, gl3state.lightmap_textureIDs[0]); GL3_SetDefaultState(); if(GL3_InitShaders()) { R_Printf(PRINT_ALL, "Loading shaders succeeded!\n"); } else { R_Printf(PRINT_ALL, "Loading shaders failed!\n"); return false; } registration_sequence = 1; // from R_InitImages() (everything else from there shouldn't be needed anymore) GL3_Mod_Init(); GL3_InitParticleTexture(); GL3_Draw_InitLocal(); GL3_SurfInit(); R_Printf(PRINT_ALL, "\n"); return true; } void GL3_Shutdown(void) { ri.Cmd_RemoveCommand("modellist"); ri.Cmd_RemoveCommand("screenshot"); ri.Cmd_RemoveCommand("imagelist"); ri.Cmd_RemoveCommand("gl_strings"); // only call all these if we have an OpenGL context and the gl function pointers // randomly chose one function that should always be there to test.. if(glDeleteBuffers != NULL) { GL3_Mod_FreeAll(); GL3_ShutdownMeshes(); GL3_ShutdownImages(); GL3_SurfShutdown(); GL3_Draw_ShutdownLocal(); GL3_ShutdownShaders(); } /* shutdown OS specific OpenGL stuff like contexts, etc. */ GL3_ShutdownWindow(false); } static void GL3_DrawBeam(entity_t *e) { int i; float r, g, b; enum { NUM_BEAM_SEGS = 6 }; vec3_t perpvec; vec3_t direction, normalized_direction; vec3_t start_points[NUM_BEAM_SEGS], end_points[NUM_BEAM_SEGS]; vec3_t oldorigin, origin; gl3_3D_vtx_t verts[NUM_BEAM_SEGS*4]; unsigned int pointb; 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); GL3_UseProgram(gl3state.si3DcolorOnly.shaderProgram); 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; gl3state.uniCommonData.color = HMM_Vec4(r, g, b, e->alpha); GL3_UpdateUBOCommon(); for ( i = 0; i < NUM_BEAM_SEGS; i++ ) { VectorCopy(start_points[i], verts[4*i+0].pos); VectorCopy(end_points[i], verts[4*i+1].pos); pointb = ( i + 1 ) % NUM_BEAM_SEGS; VectorCopy(start_points[pointb], verts[4*i+2].pos); VectorCopy(end_points[pointb], verts[4*i+3].pos); } GL3_BindVAO(gl3state.vao3D); GL3_BindVBO(gl3state.vbo3D); glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STREAM_DRAW); glDrawArrays( GL_TRIANGLE_STRIP, 0, NUM_BEAM_SEGS*4 ); glDisable(GL_BLEND); glDepthMask(GL_TRUE); } static void GL3_DrawSpriteModel(entity_t *e) { float alpha = 1.0F; gl3_3D_vtx_t verts[4]; 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 != gl3state.uni3DData.alpha) { gl3state.uni3DData.alpha = alpha; GL3_UpdateUBO3D(); } GL3_Bind(currentmodel->skins[e->frame]->texnum); if (alpha == 1.0) { // use shader with alpha test GL3_UseProgram(gl3state.si3DspriteAlpha.shaderProgram); } else { glEnable(GL_BLEND); GL3_UseProgram(gl3state.si3Dsprite.shaderProgram); } verts[0].texCoord[0] = 0; verts[0].texCoord[1] = 1; verts[1].texCoord[0] = 0; verts[1].texCoord[1] = 0; verts[2].texCoord[0] = 1; verts[2].texCoord[1] = 0; verts[3].texCoord[0] = 1; verts[3].texCoord[1] = 1; VectorMA( e->origin, -frame->origin_y, up, verts[0].pos ); VectorMA( verts[0].pos, -frame->origin_x, right, verts[0].pos ); VectorMA( e->origin, frame->height - frame->origin_y, up, verts[1].pos ); VectorMA( verts[1].pos, -frame->origin_x, right, verts[1].pos ); VectorMA( e->origin, frame->height - frame->origin_y, up, verts[2].pos ); VectorMA( verts[2].pos, frame->width - frame->origin_x, right, verts[2].pos ); VectorMA( e->origin, -frame->origin_y, up, verts[3].pos ); VectorMA( verts[3].pos, frame->width - frame->origin_x, right, verts[3].pos ); GL3_BindVAO(gl3state.vao3D); GL3_BindVBO(gl3state.vbo3D); glBufferData(GL_ARRAY_BUFFER, 4*sizeof(gl3_3D_vtx_t), verts, GL_STREAM_DRAW); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); if (alpha != 1.0F) { glDisable(GL_BLEND); gl3state.uni3DData.alpha = 1.0f; GL3_UpdateUBO3D(); } } static void GL3_DrawNullModel(void) { vec3_t shadelight; if (currententity->flags & RF_FULLBRIGHT) { shadelight[0] = shadelight[1] = shadelight[2] = 1.0F; } else { GL3_LightPoint(currententity->origin, shadelight); } hmm_mat4 origModelMat = gl3state.uni3DData.transModelMat4; GL3_RotateForEntity(currententity); gl3state.uniCommonData.color = HMM_Vec4( shadelight[0], shadelight[1], shadelight[2], 1 ); GL3_UpdateUBOCommon(); GL3_UseProgram(gl3state.si3DcolorOnly.shaderProgram); GL3_BindVAO(gl3state.vao3D); GL3_BindVBO(gl3state.vbo3D); gl3_3D_vtx_t vtxA[6] = { {{0, 0, -16}, {0,0}, {0,0}}, {{16 * cos( 0 * M_PI / 2 ), 16 * sin( 0 * M_PI / 2 ), 0}, {0,0}, {0,0}}, {{16 * cos( 1 * M_PI / 2 ), 16 * sin( 1 * M_PI / 2 ), 0}, {0,0}, {0,0}}, {{16 * cos( 2 * M_PI / 2 ), 16 * sin( 2 * M_PI / 2 ), 0}, {0,0}, {0,0}}, {{16 * cos( 3 * M_PI / 2 ), 16 * sin( 3 * M_PI / 2 ), 0}, {0,0}, {0,0}}, {{16 * cos( 4 * M_PI / 2 ), 16 * sin( 4 * M_PI / 2 ), 0}, {0,0}, {0,0}} }; glBufferData(GL_ARRAY_BUFFER, sizeof(vtxA), vtxA, GL_STREAM_DRAW); glDrawArrays(GL_TRIANGLE_FAN, 0, 6); gl3_3D_vtx_t vtxB[6] = { {{0, 0, 16}, {0,0}, {0,0}}, vtxA[5], vtxA[4], vtxA[3], vtxA[2], vtxA[1] }; glBufferData(GL_ARRAY_BUFFER, sizeof(vtxB), vtxB, GL_STREAM_DRAW); glDrawArrays(GL_TRIANGLE_FAN, 0, 6); gl3state.uni3DData.transModelMat4 = origModelMat; GL3_UpdateUBO3D(); } static void GL3_DrawParticles(void) { // TODO: stereo //qboolean stereo_split_tb = ((gl_state.stereo_mode == STEREO_SPLIT_VERTICAL) && gl_state.camera_separation); //qboolean stereo_split_lr = ((gl_state.stereo_mode == STEREO_SPLIT_HORIZONTAL) && gl_state.camera_separation); //if (!(stereo_split_tb || stereo_split_lr)) { int i; int numParticles = gl3_newrefdef.num_particles; unsigned char color[4]; const particle_t *p; // assume the size looks good with window height 480px and scale according to real resolution float pointSize = gl3_particle_size->value * (float)gl3_newrefdef.height/480.0f; typedef struct part_vtx { GLfloat pos[3]; GLfloat size; GLfloat dist; GLfloat color[4]; } part_vtx; assert(sizeof(part_vtx)==9*sizeof(float)); // remember to update GL3_SurfInit() if this changes! part_vtx buf[numParticles]; // TODO: viewOrg could be in UBO vec3_t viewOrg; VectorCopy(gl3_newrefdef.vieworg, viewOrg); glDepthMask(GL_FALSE); glEnable(GL_BLEND); glEnable(GL_PROGRAM_POINT_SIZE); GL3_UseProgram(gl3state.siParticle.shaderProgram); for ( i = 0, p = gl3_newrefdef.particles; i < numParticles; i++, p++ ) { *(int *) color = d_8to24table [ p->color & 0xFF ]; part_vtx* cur = &buf[i]; vec3_t offset; // between viewOrg and particle position VectorSubtract(viewOrg, p->origin, offset); VectorCopy(p->origin, cur->pos); cur->size = pointSize; cur->dist = VectorLength(offset); for(int j=0; j<3; ++j) cur->color[j] = color[j]/255.0f; cur->color[3] = p->alpha; } GL3_BindVAO(gl3state.vaoParticle); GL3_BindVBO(gl3state.vboParticle); glBufferData(GL_ARRAY_BUFFER, sizeof(part_vtx)*numParticles, buf, GL_STREAM_DRAW); glDrawArrays(GL_POINTS, 0, numParticles); glDisable(GL_BLEND); glDepthMask(GL_TRUE); glDisable(GL_PROGRAM_POINT_SIZE); } } static void GL3_DrawEntitiesOnList(void) { int i; if (!gl_drawentities->value) { return; } GL3_ResetShadowAliasModels(); /* draw non-transparent first */ for (i = 0; i < gl3_newrefdef.num_entities; i++) { currententity = &gl3_newrefdef.entities[i]; if (currententity->flags & RF_TRANSLUCENT) { continue; /* solid */ } if (currententity->flags & RF_BEAM) { GL3_DrawBeam(currententity); } else { currentmodel = currententity->model; if (!currentmodel) { GL3_DrawNullModel(); continue; } switch (currentmodel->type) { case mod_alias: GL3_DrawAliasModel(currententity); break; case mod_brush: GL3_DrawBrushModel(currententity); break; case mod_sprite: GL3_DrawSpriteModel(currententity); break; default: ri.Sys_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 < gl3_newrefdef.num_entities; i++) { currententity = &gl3_newrefdef.entities[i]; if (!(currententity->flags & RF_TRANSLUCENT)) { continue; /* solid */ } if (currententity->flags & RF_BEAM) { GL3_DrawBeam(currententity); } else { currentmodel = currententity->model; if (!currentmodel) { GL3_DrawNullModel(); continue; } switch (currentmodel->type) { case mod_alias: GL3_DrawAliasModel(currententity); break; case mod_brush: GL3_DrawBrushModel(currententity); break; case mod_sprite: GL3_DrawSpriteModel(currententity); break; default: ri.Sys_Error(ERR_DROP, "Bad modeltype"); break; } } } GL3_DrawAliasShadows(); glDepthMask(1); /* back to writing */ } static int 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; } static void SetFrustum(void) { int i; /* rotate VPN right by FOV_X/2 degrees */ RotatePointAroundVector(frustum[0].normal, vup, vpn, -(90 - gl3_newrefdef.fov_x / 2)); /* rotate VPN left by FOV_X/2 degrees */ RotatePointAroundVector(frustum[1].normal, vup, vpn, 90 - gl3_newrefdef.fov_x / 2); /* rotate VPN up by FOV_X/2 degrees */ RotatePointAroundVector(frustum[2].normal, vright, vpn, 90 - gl3_newrefdef.fov_y / 2); /* rotate VPN down by FOV_X/2 degrees */ RotatePointAroundVector(frustum[3].normal, vright, vpn, -(90 - gl3_newrefdef.fov_y / 2)); for (i = 0; i < 4; i++) { frustum[i].type = PLANE_ANYZ; frustum[i].dist = DotProduct(gl3_origin, frustum[i].normal); frustum[i].signbits = SignbitsForPlane(&frustum[i]); } } static void SetupFrame(void) { int i; mleaf_t *leaf; gl3_framecount++; /* build the transformation matrix for the given view angles */ VectorCopy(gl3_newrefdef.vieworg, gl3_origin); AngleVectors(gl3_newrefdef.viewangles, vpn, vright, vup); /* current viewcluster */ if (!(gl3_newrefdef.rdflags & RDF_NOWORLDMODEL)) { gl3_oldviewcluster = gl3_viewcluster; gl3_oldviewcluster2 = gl3_viewcluster2; leaf = GL3_Mod_PointInLeaf(gl3_origin, gl3_worldmodel); gl3_viewcluster = gl3_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(gl3_origin, temp); temp[2] -= 16; leaf = GL3_Mod_PointInLeaf(temp, gl3_worldmodel); if (!(leaf->contents & CONTENTS_SOLID) && (leaf->cluster != gl3_viewcluster2)) { gl3_viewcluster2 = leaf->cluster; } } else { /* look up a bit */ vec3_t temp; VectorCopy(gl3_origin, temp); temp[2] += 16; leaf = GL3_Mod_PointInLeaf(temp, gl3_worldmodel); if (!(leaf->contents & CONTENTS_SOLID) && (leaf->cluster != gl3_viewcluster2)) { gl3_viewcluster2 = leaf->cluster; } } } for (i = 0; i < 4; i++) { v_blend[i] = gl3_newrefdef.blend[i]; } c_brush_polys = 0; c_alias_polys = 0; /* clear out the portion of the screen that the NOWORLDMODEL defines */ if (gl3_newrefdef.rdflags & RDF_NOWORLDMODEL) { glEnable(GL_SCISSOR_TEST); glClearColor(0.3, 0.3, 0.3, 1); glScissor(gl3_newrefdef.x, vid.height - gl3_newrefdef.height - gl3_newrefdef.y, gl3_newrefdef.width, gl3_newrefdef.height); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClearColor(1, 0, 0.5, 0.5); glDisable(GL_SCISSOR_TEST); } } static void GL3_SetGL2D(void) { int x = 0; int w = vid.width; int y = 0; int h = vid.height; #if 0 // TODO: stereo /* set 2D virtual screen size */ qboolean drawing_left_eye = gl_state.camera_separation < 0; qboolean stereo_split_tb = ((gl_state.stereo_mode == STEREO_SPLIT_VERTICAL) && gl_state.camera_separation); qboolean stereo_split_lr = ((gl_state.stereo_mode == STEREO_SPLIT_HORIZONTAL) && gl_state.camera_separation); if(stereo_split_lr) { w = w / 2; x = drawing_left_eye ? 0 : w; } if(stereo_split_tb) { h = h / 2; y = drawing_left_eye ? h : 0; } #endif // 0 glViewport(x, y, w, h); hmm_mat4 transMatr = HMM_Orthographic(0, vid.width, vid.height, 0, -99999, 99999); gl3state.uni2DData.transMat4 = transMatr; GL3_UpdateUBO2D(); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glDisable(GL_BLEND); } // equivalent to R_x * R_y * R_z where R_x is the trans matrix for rotating around X axis for aroundXdeg static hmm_mat4 rotAroundAxisXYZ(float aroundXdeg, float aroundYdeg, float aroundZdeg) { float alpha = HMM_ToRadians(aroundXdeg); float beta = HMM_ToRadians(aroundYdeg); float gamma = HMM_ToRadians(aroundZdeg); float sinA = HMM_SinF(alpha); float cosA = HMM_CosF(alpha); float sinB = HMM_SinF(beta); float cosB = HMM_CosF(beta); float sinG = HMM_SinF(gamma); float cosG = HMM_CosF(gamma); hmm_mat4 ret = {{ { cosB*cosG, sinA*sinB*cosG + cosA*sinG, -cosA*sinB*cosG + sinA*sinG, 0 }, // first *column* { -cosB*sinG, -sinA*sinB*sinG + cosA*cosG, cosA*sinB*sinG + sinA*cosG, 0 }, { sinB, -sinA*cosB, cosA*cosB, 0 }, { 0, 0, 0, 1 } }}; return ret; } // equivalent to R_MYgluPerspective() but returning a matrix instead of setting internal OpenGL state static hmm_mat4 GL3_MYgluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar) { // calculation of left, right, bottom, top is from R_MYgluPerspective() of old gl backend // which seems to be slightly different from the real gluPerspective() // and thus also from HMM_Perspective() GLdouble left, right, bottom, top; float A, B, C, D; top = zNear * tan(fovy * M_PI / 360.0); bottom = -top; left = bottom * aspect; right = top * aspect; // TODO: stereo stuff // left += - gl_stereo_convergence->value * (2 * gl_state.camera_separation) / zNear; // right += - gl_stereo_convergence->value * (2 * gl_state.camera_separation) / zNear; // the following emulates glFrustum(left, right, bottom, top, zNear, zFar) // see https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glFrustum.xml A = (right+left)/(right-left); B = (top+bottom)/(top-bottom); C = -(zFar+zNear)/(zFar-zNear); D = -(2.0*zFar*zNear)/(zFar-zNear); hmm_mat4 ret = {{ { (2.0*zNear)/(right-left), 0, 0, 0 }, // first *column* { 0, (2.0*zNear)/(top-bottom), 0, 0 }, { A, B, C, -1.0 }, { 0, 0, D, 0 } }}; return ret; } static void SetupGL(void) { int x, x2, y2, y, w, h; /* set up viewport */ x = floor(gl3_newrefdef.x * vid.width / vid.width); x2 = ceil((gl3_newrefdef.x + gl3_newrefdef.width) * vid.width / vid.width); y = floor(vid.height - gl3_newrefdef.y * vid.height / vid.height); y2 = ceil(vid.height - (gl3_newrefdef.y + gl3_newrefdef.height) * vid.height / vid.height); w = x2 - x; h = y - y2; #if 0 // TODO: stereo stuff qboolean drawing_left_eye = gl_state.camera_separation < 0; qboolean stereo_split_tb = ((gl_state.stereo_mode == STEREO_SPLIT_VERTICAL) && gl_state.camera_separation); qboolean stereo_split_lr = ((gl_state.stereo_mode == STEREO_SPLIT_HORIZONTAL) && gl_state.camera_separation); if(stereo_split_lr) { w = w / 2; x = drawing_left_eye ? (x / 2) : (x + vid.width) / 2; } if(stereo_split_tb) { h = h / 2; y2 = drawing_left_eye ? (y2 + vid.height) / 2 : (y2 / 2); } #endif // 0 glViewport(x, y2, w, h); /* set up projection matrix (eye coordinates -> clip coordinates) */ { float screenaspect = (float)gl3_newrefdef.width / gl3_newrefdef.height; float dist = (gl_farsee->value == 0) ? 4096.0f : 8192.0f; gl3_projectionMatrix = GL3_MYgluPerspective(gl3_newrefdef.fov_y, screenaspect, 4, dist); } glCullFace(GL_FRONT); /* set up view matrix (world coordinates -> eye coordinates) */ { // first put Z axis going up hmm_mat4 viewMat = {{ { 0, 0, -1, 0 }, // first *column* (the matrix is colum-major) { -1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 0, 1 } }}; // now rotate by view angles hmm_mat4 rotMat = rotAroundAxisXYZ(-gl3_newrefdef.viewangles[2], -gl3_newrefdef.viewangles[0], -gl3_newrefdef.viewangles[1]); viewMat = HMM_MultiplyMat4( viewMat, rotMat ); // .. and apply translation for current position hmm_vec3 trans = HMM_Vec3(-gl3_newrefdef.vieworg[0], -gl3_newrefdef.vieworg[1], -gl3_newrefdef.vieworg[2]); viewMat = HMM_MultiplyMat4( viewMat, HMM_Translate(trans) ); gl3_world_matrix = viewMat; } gl3state.uni3DData.transProjMat4 = gl3_projectionMatrix; gl3state.uni3DData.transViewMat4 = gl3_world_matrix; gl3state.uni3DData.transModelMat4 = gl3_identityMat4; gl3state.uni3DData.time = gl3_newrefdef.time; GL3_UpdateUBO3D(); /* set drawing parms */ if (gl_cull->value) { glEnable(GL_CULL_FACE); } else { glDisable(GL_CULL_FACE); } glEnable(GL_DEPTH_TEST); } extern int c_visible_lightmaps, c_visible_textures; /* * gl3_newrefdef must be set before the first call */ static void GL3_RenderView(refdef_t *fd) { #if 0 // TODO: keep stereo stuff? if ((gl_state.stereo_mode != STEREO_MODE_NONE) && gl_state.camera_separation) { qboolean drawing_left_eye = gl_state.camera_separation < 0; switch (gl_state.stereo_mode) { case STEREO_MODE_ANAGLYPH: { // Work out the colour for each eye. int anaglyph_colours[] = { 0x4, 0x3 }; // Left = red, right = cyan. if (strlen(gl_stereo_anaglyph_colors->string) == 2) { int eye, colour, missing_bits; // Decode the colour name from its character. for (eye = 0; eye < 2; ++eye) { colour = 0; switch (toupper(gl_stereo_anaglyph_colors->string[eye])) { case 'B': ++colour; // 001 Blue case 'G': ++colour; // 010 Green case 'C': ++colour; // 011 Cyan case 'R': ++colour; // 100 Red case 'M': ++colour; // 101 Magenta case 'Y': ++colour; // 110 Yellow anaglyph_colours[eye] = colour; break; } } // Fill in any missing bits. missing_bits = ~(anaglyph_colours[0] | anaglyph_colours[1]) & 0x3; for (eye = 0; eye < 2; ++eye) { anaglyph_colours[eye] |= missing_bits; } } // Set the current colour. glColorMask( !!(anaglyph_colours[drawing_left_eye] & 0x4), !!(anaglyph_colours[drawing_left_eye] & 0x2), !!(anaglyph_colours[drawing_left_eye] & 0x1), GL_TRUE ); } break; case STEREO_MODE_ROW_INTERLEAVED: case STEREO_MODE_COLUMN_INTERLEAVED: case STEREO_MODE_PIXEL_INTERLEAVED: { qboolean flip_eyes = true; int client_x, client_y; //GLimp_GetClientAreaOffset(&client_x, &client_y); client_x = 0; client_y = 0; GL3_SetGL2D(); glEnable(GL_STENCIL_TEST); glStencilMask(GL_TRUE); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); glStencilFunc(GL_NEVER, 0, 1); glBegin(GL_QUADS); { glVertex2i(0, 0); glVertex2i(vid.width, 0); glVertex2i(vid.width, vid.height); glVertex2i(0, vid.height); } glEnd(); glStencilOp(GL_INVERT, GL_KEEP, GL_KEEP); glStencilFunc(GL_NEVER, 1, 1); glBegin(GL_LINES); { if (gl_state.stereo_mode == STEREO_MODE_ROW_INTERLEAVED || gl_state.stereo_mode == STEREO_MODE_PIXEL_INTERLEAVED) { int y; for (y = 0; y <= vid.height; y += 2) { glVertex2f(0, y - 0.5f); glVertex2f(vid.width, y - 0.5f); } flip_eyes ^= (client_y & 1); } if (gl_state.stereo_mode == STEREO_MODE_COLUMN_INTERLEAVED || gl_state.stereo_mode == STEREO_MODE_PIXEL_INTERLEAVED) { int x; for (x = 0; x <= vid.width; x += 2) { glVertex2f(x - 0.5f, 0); glVertex2f(x - 0.5f, vid.height); } flip_eyes ^= (client_x & 1); } } glEnd(); glStencilMask(GL_FALSE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glStencilFunc(GL_EQUAL, drawing_left_eye ^ flip_eyes, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); } break; default: break; } } #endif // 0 (stereo stuff) if (gl_norefresh->value) { return; } gl3_newrefdef = *fd; if (!gl3_worldmodel && !(gl3_newrefdef.rdflags & RDF_NOWORLDMODEL)) { ri.Sys_Error(ERR_DROP, "R_RenderView: NULL worldmodel"); } if (gl_speeds->value) { c_brush_polys = 0; c_alias_polys = 0; } GL3_PushDlights(); if (gl_finish->value) { glFinish(); } SetupFrame(); SetFrustum(); SetupGL(); GL3_MarkLeaves(); /* done here so we know if we're in water */ GL3_DrawWorld(); GL3_DrawEntitiesOnList(); // kick the silly gl_flashblend poly lights // GL3_RenderDlights(); GL3_DrawParticles(); GL3_DrawAlphaSurfaces(); // Note: R_Flash() is now GL3_Draw_Flash() and called from GL3_RenderFrame() if (gl_speeds->value) { R_Printf(PRINT_ALL, "%4i wpoly %4i epoly %i tex %i lmaps\n", c_brush_polys, c_alias_polys, c_visible_textures, c_visible_lightmaps); } #if 0 // TODO: stereo stuff switch (gl_state.stereo_mode) { case STEREO_MODE_NONE: break; case STEREO_MODE_ANAGLYPH: glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); break; case STEREO_MODE_ROW_INTERLEAVED: case STEREO_MODE_COLUMN_INTERLEAVED: case STEREO_MODE_PIXEL_INTERLEAVED: glDisable(GL_STENCIL_TEST); break; default: break; } #endif // 0 } #if 0 // TODO: stereo enum opengl_special_buffer_modes GL3_GetSpecialBufferModeForStereoMode(enum stereo_modes stereo_mode) { switch (stereo_mode) { case STEREO_MODE_NONE: case STEREO_SPLIT_HORIZONTAL: case STEREO_SPLIT_VERTICAL: case STEREO_MODE_ANAGLYPH: return OPENGL_SPECIAL_BUFFER_MODE_NONE; case STEREO_MODE_OPENGL: return OPENGL_SPECIAL_BUFFER_MODE_STEREO; case STEREO_MODE_ROW_INTERLEAVED: case STEREO_MODE_COLUMN_INTERLEAVED: case STEREO_MODE_PIXEL_INTERLEAVED: return OPENGL_SPECIAL_BUFFER_MODE_STENCIL; } return OPENGL_SPECIAL_BUFFER_MODE_NONE; } #endif // 0 static void GL3_SetLightLevel(void) { vec3_t shadelight = {0}; if (gl3_newrefdef.rdflags & RDF_NOWORLDMODEL) { return; } /* save off light value for server to look at */ GL3_LightPoint(gl3_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]; } } } static void GL3_RenderFrame(refdef_t *fd) { GL3_RenderView(fd); GL3_SetLightLevel(); GL3_SetGL2D(); if(v_blend[3] != 0.0f) { int x = (vid.width - gl3_newrefdef.width)/2; int y = (vid.height - gl3_newrefdef.height)/2; GL3_Draw_Flash(v_blend, x, y, gl3_newrefdef.width, gl3_newrefdef.height); } } static void GL3_Clear(void) { // Check whether the stencil buffer needs clearing, and do so if need be. GLbitfield stencilFlags = 0; #if 0 // TODO: stereo stuff if (gl3state.stereo_mode >= STEREO_MODE_ROW_INTERLEAVED && gl_state.stereo_mode <= STEREO_MODE_PIXEL_INTERLEAVED) { glClearStencil(0); stencilFlags |= GL_STENCIL_BUFFER_BIT; } #endif // 0 if (gl_clear->value) { glClear(GL_COLOR_BUFFER_BIT | stencilFlags | GL_DEPTH_BUFFER_BIT); } else { glClear(GL_DEPTH_BUFFER_BIT | stencilFlags); } gl3depthmin = 0; gl3depthmax = 1; glDepthFunc(GL_LEQUAL); glDepthRange(gl3depthmin, gl3depthmax); if (gl_zfix->value) { if (gl3depthmax > gl3depthmin) { glPolygonOffset(0.05, 1); } else { glPolygonOffset(-0.05, -1); } } /* stencilbuffer shadows */ if (gl_shadows->value && have_stencil) { glClearStencil(1); glClear(GL_STENCIL_BUFFER_BIT); } } void GL3_BeginFrame(float camera_separation) { /* change modes if necessary */ if (gl_mode->modified) { vid_fullscreen->modified = true; } #if 0 // TODO: stereo stuff gl_state.camera_separation = camera_separation; // force a vid_restart if gl_stereo has been modified. if ( gl_state.stereo_mode != gl_stereo->value ) { // If we've gone from one mode to another with the same special buffer requirements there's no need to restart. if ( GL_GetSpecialBufferModeForStereoMode( gl_state.stereo_mode ) == GL_GetSpecialBufferModeForStereoMode( gl_stereo->value ) ) { gl_state.stereo_mode = gl_stereo->value; } else { R_Printf(PRINT_ALL, "stereo supermode changed, restarting video!\n"); vid_fullscreen->modified = true; } } #endif // 0 if (vid_gamma->modified || gl3_intensity->modified || gl3_intensity_2D->modified) { vid_gamma->modified = false; gl3_intensity->modified = false; gl3_intensity_2D->modified = false; gl3state.uniCommonData.gamma = 1.0f/vid_gamma->value; gl3state.uniCommonData.intensity = gl3_intensity->value; gl3state.uniCommonData.intensity2D = gl3_intensity_2D->value; GL3_UpdateUBOCommon(); } // in GL3, overbrightbits can have any positive value if (gl3_overbrightbits->modified) { gl3_overbrightbits->modified = false; if(gl3_overbrightbits->value < 0.0f) { ri.Cvar_Set("gl_overbrightbits", "0"); } gl3state.uni3DData.overbrightbits = (gl3_overbrightbits->value <= 0.0f) ? 1.0f : gl3_overbrightbits->value; GL3_UpdateUBO3D(); } if(gl3_particle_fade_factor->modified) { gl3_particle_fade_factor->modified = false; gl3state.uni3DData.particleFadeFactor = gl3_particle_fade_factor->value; GL3_UpdateUBO3D(); } if(gl3_particle_square->modified) { gl3_particle_square->modified = false; GL3_RecreateShaders(); } /* go into 2D mode */ GL3_SetGL2D(); /* draw buffer stuff */ if (gl_drawbuffer->modified) { gl_drawbuffer->modified = false; // TODO: stereo stuff //if ((gl3state.camera_separation == 0) || gl3state.stereo_mode != STEREO_MODE_OPENGL) { if (Q_stricmp(gl_drawbuffer->string, "GL_FRONT") == 0) { glDrawBuffer(GL_FRONT); } else { glDrawBuffer(GL_BACK); } } } /* texturemode stuff */ if (gl_texturemode->modified || (gl3config.anisotropic && gl_anisotropic->modified)) { GL3_TextureMode(gl_texturemode->string); gl_texturemode->modified = false; gl_anisotropic->modified = false; } if(gl_swapinterval->modified) { gl_swapinterval->modified = false; GL3_SetSwapInterval(); } /* clear screen if desired */ GL3_Clear(); } static void GL3_SetPalette(const unsigned char *palette) { int i; byte *rp = (byte *)gl3_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; } } glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT); glClearColor(1, 0, 0.5, 0.5); } Q2_DLL_EXPORTED refexport_t GetRefAPI(refimport_t imp) { refexport_t re = {0}; ri = imp; re.api_version = API_VERSION; re.Init = GL3_Init; re.Shutdown = GL3_Shutdown; re.PrepareForWindow = GL3_PrepareForWindow; re.InitContext = GL3_InitContext; re.ShutdownWindow = GL3_ShutdownWindow; re.IsVSyncActive = GL3_IsVsyncActive; re.BeginRegistration = GL3_BeginRegistration; re.RegisterModel = GL3_RegisterModel; re.RegisterSkin = GL3_RegisterSkin; re.SetSky = GL3_SetSky; re.EndRegistration = GL3_EndRegistration; re.RenderFrame = GL3_RenderFrame; re.DrawFindPic = GL3_Draw_FindPic; re.DrawGetPicSize = GL3_Draw_GetPicSize; re.DrawPicScaled = GL3_Draw_PicScaled; re.DrawStretchPic = GL3_Draw_StretchPic; re.DrawCharScaled = GL3_Draw_CharScaled; re.DrawTileClear = GL3_Draw_TileClear; re.DrawFill = GL3_Draw_Fill; re.DrawFadeScreen = GL3_Draw_FadeScreen; re.DrawStretchRaw = GL3_Draw_StretchRaw; re.SetPalette = GL3_SetPalette; re.BeginFrame = GL3_BeginFrame; re.EndFrame = GL3_EndFrame; return re; } void R_Printf(int level, const char* msg, ...) { va_list argptr; va_start(argptr, msg); ri.Com_VPrintf(level, msg, argptr); va_end(argptr); } /* * this is only here so the functions in shared source files * (shared.c, rand.c, flash.c, mem.c/hunk.c) can link */ void Sys_Error(char *error, ...) { va_list argptr; char text[4096]; // MAXPRINTMSG == 4096 va_start(argptr, error); vsprintf(text, error, argptr); va_end(argptr); ri.Sys_Error(ERR_FATAL, "%s", text); } void Com_Printf(char *msg, ...) { va_list argptr; va_start(argptr, msg); ri.Com_VPrintf(PRINT_ALL, msg, argptr); va_end(argptr); } yquake2-QUAKE2_7_10/src/client/refresh/gl3/gl3_md2.c000066400000000000000000000103421321245476300217050ustar00rootroot00000000000000/* * 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 GL3_LoadMD2(gl3model_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) { ri.Sys_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) { ri.Sys_Error(ERR_DROP, "model %s has a skin taller than %d", mod->name, MAX_LBM_HEIGHT); } if (pheader->num_xyz <= 0) { ri.Sys_Error(ERR_DROP, "model %s has no vertices", mod->name); } if (pheader->num_xyz > MAX_VERTS) { ri.Sys_Error(ERR_DROP, "model %s has too many vertices", mod->name); } if (pheader->num_st <= 0) { ri.Sys_Error(ERR_DROP, "model %s has no st vertices", mod->name); } if (pheader->num_tris <= 0) { ri.Sys_Error(ERR_DROP, "model %s has no triangles", mod->name); } if (pheader->num_frames <= 0) { ri.Sys_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] = GL3_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_7_10/src/client/refresh/gl3/gl3_mesh.c000066400000000000000000000526351321245476300221720ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2016-2017 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. * * ======================================================================= * * Mesh handling * * ======================================================================= */ #include "header/local.h" #include "header/DG_dynarr.h" #define NUMVERTEXNORMALS 162 #define SHADEDOT_QUANT 16 static float r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "../constants/anorms.h" }; /* precalculated dot products for quantized angles */ static float r_avertexnormal_dots[SHADEDOT_QUANT][256] = { #include "../constants/anormtab.h" }; typedef float vec4_t[4]; static vec4_t s_lerped[MAX_VERTS]; extern vec3_t lightspot; extern qboolean have_stencil; typedef struct gl3_shadowinfo_s { vec3_t lightspot; vec3_t shadevector; dmdl_t* paliashdr; entity_t* entity; } gl3_shadowinfo_t; DA_TYPEDEF(gl3_shadowinfo_t, ShadowInfoArray_t); // collect all models casting shadows (each frame) // to draw shadows last static ShadowInfoArray_t shadowModels = {0}; DA_TYPEDEF(gl3_alias_vtx_t, AliasVtxArray_t); DA_TYPEDEF(GLushort, UShortArray_t); // dynamic arrays to batch all the data of a model, so we can render a model in one draw call static AliasVtxArray_t vtxBuf = {0}; static UShortArray_t idxBuf = {0}; void GL3_ShutdownMeshes(void) { da_free(vtxBuf); da_free(idxBuf); da_free(shadowModels); } static void LerpVerts(qboolean powerUpEffect, 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 (powerUpEffect) { 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 */ static void DrawAliasFrameLerp(dmdl_t *paliashdr, entity_t* entity, vec3_t shadelight) { GLenum type; float l; daliasframe_t *frame, *oldframe; dtrivertx_t *v, *ov, *verts; int *order; int count; float alpha; vec3_t move, delta, vectors[3]; vec3_t frontv, backv; int i; int index_xyz; float backlerp = entity->backlerp; float frontlerp = 1.0 - backlerp; float *lerp; // draw without texture? used for quad damage effect etc, I think qboolean colorOnly = 0 != (entity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)); // TODO: maybe we could somehow store the non-rotated normal and do the dot in shader? float* shadedots = r_avertexnormal_dots[((int)(entity->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; frame = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames + entity->frame * paliashdr->framesize); verts = v = frame->verts; oldframe = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames + entity->oldframe * paliashdr->framesize); ov = oldframe->verts; order = (int *)((byte *)paliashdr + paliashdr->ofs_glcmds); if (entity->flags & RF_TRANSLUCENT) { alpha = entity->alpha * 0.666f; } else { alpha = 1.0; } if (colorOnly) { GL3_UseProgram(gl3state.si3DaliasColor.shaderProgram); } else { GL3_UseProgram(gl3state.si3Dalias.shaderProgram); } /* move should be the delta back to the previous frame * backlerp */ VectorSubtract(entity->oldorigin, entity->origin, delta); AngleVectors(entity->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]; frontv[i] = frontlerp * frame->scale[i]; backv[i] = backlerp * oldframe->scale[i]; } lerp = s_lerped[0]; LerpVerts(colorOnly, paliashdr->num_xyz, v, ov, verts, lerp, move, frontv, backv); assert(sizeof(gl3_alias_vtx_t) == 9*sizeof(GLfloat)); // all the triangle fans and triangle strips of this model will be converted to // just triangles: the vertices stay the same and are batched in vtxBuf, // but idxBuf will contain indices to draw them all as GL_TRIANGLE // this way there's only one draw call (and two glBufferData() calls) // instead of (at least) dozens. *greatly* improves performance. // so first clear out the data from last call to this function // (the buffers are static global so we don't have malloc()/free() for each rendered model) da_clear(vtxBuf); da_clear(idxBuf); while (1) { GLushort nextVtxIdx = da_count(vtxBuf); /* get the vertex count and primitive type */ count = *order++; if (!count) { break; /* done */ } if (count < 0) { count = -count; type = GL_TRIANGLE_FAN; } else { type = GL_TRIANGLE_STRIP; } gl3_alias_vtx_t* buf = da_addn_uninit(vtxBuf, count); if (colorOnly) { int i; for(i=0; ipos[j] = s_lerped[index_xyz][j]; cur->color[j] = shadelight[j]; } cur->color[3] = alpha; } } else { int i; for(i=0; itexCoord[0] = ((float *) order)[0]; cur->texCoord[1] = ((float *) order)[1]; index_xyz = order[2]; order += 3; /* normals and vertexes come from the frame list */ // shadedots is set above according to rotation (around Z axis I think) // to one of 16 (SHADEDOT_QUANT) presets in r_avertexnormal_dots l = shadedots[verts[index_xyz].lightnormalindex]; for(j=0; j<3; ++j) { cur->pos[j] = s_lerped[index_xyz][j]; cur->color[j] = l * shadelight[j]; } cur->color[3] = alpha; } } // translate triangle fan/strip to just triangle indices if(type == GL_TRIANGLE_FAN) { GLushort i; for(i=1; i < count-1; ++i) { GLushort* add = da_addn_uninit(idxBuf, 3); add[0] = nextVtxIdx; add[1] = nextVtxIdx+i; add[2] = nextVtxIdx+i+1; } } else // triangle strip { GLushort i; for(i=1; i < count-2; i+=2) { // add two triangles at once, because the vertex order is different // for odd vs even triangles GLushort* add = da_addn_uninit(idxBuf, 6); add[0] = nextVtxIdx + i-1; add[1] = nextVtxIdx + i; add[2] = nextVtxIdx + i+1; add[3] = nextVtxIdx + i; add[4] = nextVtxIdx + i+2; add[5] = nextVtxIdx + i+1; } // add remaining triangle, if any if(i < count-1) { GLushort* add = da_addn_uninit(idxBuf, 3); add[0] = nextVtxIdx + i-1; add[1] = nextVtxIdx + i; add[2] = nextVtxIdx + i+1; } } } GL3_BindVAO(gl3state.vaoAlias); GL3_BindVBO(gl3state.vboAlias); glBufferData(GL_ARRAY_BUFFER, da_count(vtxBuf)*sizeof(gl3_alias_vtx_t), vtxBuf.p, GL_STREAM_DRAW); GL3_BindEBO(gl3state.eboAlias); glBufferData(GL_ELEMENT_ARRAY_BUFFER, da_count(idxBuf)*sizeof(GLushort), idxBuf.p, GL_STREAM_DRAW); glDrawElements(GL_TRIANGLES, da_count(idxBuf), GL_UNSIGNED_SHORT, NULL); } static void DrawAliasShadow(gl3_shadowinfo_t* shadowInfo) { GLenum type; int *order; vec3_t point; float height = 0, lheight; int count; dmdl_t* paliashdr = shadowInfo->paliashdr; entity_t* entity = shadowInfo->entity; vec3_t shadevector; VectorCopy(shadowInfo->shadevector, shadevector); // all in this scope is to set s_lerped { daliasframe_t *frame, *oldframe; dtrivertx_t *v, *ov, *verts; float backlerp = entity->backlerp; float frontlerp = 1.0f - backlerp; vec3_t move, delta, vectors[3]; vec3_t frontv, backv; int i; frame = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames + entity->frame * paliashdr->framesize); verts = v = frame->verts; oldframe = (daliasframe_t *)((byte *)paliashdr + paliashdr->ofs_frames + entity->oldframe * paliashdr->framesize); ov = oldframe->verts; /* move should be the delta back to the previous frame * backlerp */ VectorSubtract(entity->oldorigin, entity->origin, delta); AngleVectors(entity->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]; frontv[i] = frontlerp * frame->scale[i]; backv[i] = backlerp * oldframe->scale[i]; } // false: don't extrude vertices for powerup - this means the powerup shell // is not seen in the shadow, only the underlying model.. LerpVerts(false, paliashdr->num_xyz, v, ov, verts, s_lerped[0], move, frontv, backv); } lheight = entity->origin[2] - shadowInfo->lightspot[2]; order = (int *)((byte *)paliashdr + paliashdr->ofs_glcmds); height = -lheight + 0.1f; // GL1 uses alpha 0.5, but in GL3 0.3 looks better GLfloat color[4] = {0, 0, 0, 0.3}; // draw the shadow in a single draw call, just like the model da_clear(vtxBuf); da_clear(idxBuf); while (1) { int i, j; GLushort nextVtxIdx = da_count(vtxBuf); /* get the vertex count and primitive type */ count = *order++; if (!count) { break; /* done */ } if (count < 0) { count = -count; type = GL_TRIANGLE_FAN; } else { type = GL_TRIANGLE_STRIP; } gl3_alias_vtx_t* buf = da_addn_uninit(vtxBuf, count); for(i=0; imodel; paliashdr = (dmdl_t *)model->extradata; if ((e->frame >= paliashdr->num_frames) || (e->frame < 0)) { R_Printf(PRINT_DEVELOPER, "R_CullAliasModel %s: no such frame %d\n", model->name, e->frame); e->frame = 0; } if ((e->oldframe >= paliashdr->num_frames) || (e->oldframe < 0)) { R_Printf(PRINT_DEVELOPER, "R_CullAliasModel %s: no such oldframe %d\n", model->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 GL3_DrawAliasModel(entity_t *entity) { int i; dmdl_t *paliashdr; float an; vec3_t bbox[8]; vec3_t shadelight; vec3_t shadevector; gl3image_t *skin; hmm_mat4 origProjMat = {0}; // use for left-handed rendering // used to restore ModelView matrix after changing it for this entities position/rotation hmm_mat4 origModelMat = {0}; if (!(entity->flags & RF_WEAPONMODEL)) { if (CullAliasModel(bbox, entity)) { return; } } if (entity->flags & RF_WEAPONMODEL) { if (gl_lefthand->value == 2) { return; } } gl3model_t* model = entity->model; paliashdr = (dmdl_t *)model->extradata; /* get lighting information */ if (entity->flags & (RF_SHELL_HALF_DAM | RF_SHELL_GREEN | RF_SHELL_RED | RF_SHELL_BLUE | RF_SHELL_DOUBLE)) { VectorClear(shadelight); if (entity->flags & RF_SHELL_HALF_DAM) { shadelight[0] = 0.56; shadelight[1] = 0.59; shadelight[2] = 0.45; } if (entity->flags & RF_SHELL_DOUBLE) { shadelight[0] = 0.9; shadelight[1] = 0.7; } if (entity->flags & RF_SHELL_RED) { shadelight[0] = 1.0; } if (entity->flags & RF_SHELL_GREEN) { shadelight[1] = 1.0; } if (entity->flags & RF_SHELL_BLUE) { shadelight[2] = 1.0; } } else if (entity->flags & RF_FULLBRIGHT) { for (i = 0; i < 3; i++) { shadelight[i] = 1.0; } } else { GL3_LightPoint(entity->origin, shadelight); /* player lighting hack for communication back to server */ if (entity->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 (entity->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 (entity->flags & RF_GLOW) { /* bonus items will pulse with time */ float scale; float min; scale = 0.1 * sin(gl3_newrefdef.time * 7); for (i = 0; i < 3; i++) { min = shadelight[i] * 0.8; shadelight[i] += scale; if (shadelight[i] < min) { shadelight[i] = min; } } } // Note: gl_overbrightbits are now applied in shader. /* ir goggles color override */ if ((gl3_newrefdef.rdflags & RDF_IRGOGGLES) && (entity->flags & RF_IR_VISIBLE)) { shadelight[0] = 1.0; shadelight[1] = 0.0; shadelight[2] = 0.0; } an = entity->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 (entity->flags & RF_DEPTHHACK) { /* hack the depth range to prevent view model from poking into walls */ glDepthRange(gl3depthmin, gl3depthmin + 0.3 * (gl3depthmax - gl3depthmin)); } if ((entity->flags & RF_WEAPONMODEL) && (gl_lefthand->value == 1.0F)) { origProjMat = gl3state.uni3DData.transProjMat4; // to mirror gun so it's rendered left-handed, just invert X-axis column // of projection matrix for(int i=0; i<4; ++i) { gl3state.uni3DData.transProjMat4.Elements[0][i] = -gl3state.uni3DData.transProjMat4.Elements[0][i]; } //GL3_UpdateUBO3D(); Note: GL3_RotateForEntity() will call this,no need to do it twice before drawing glCullFace(GL_BACK); } //glPushMatrix(); origModelMat = gl3state.uni3DData.transModelMat4; entity->angles[PITCH] = -entity->angles[PITCH]; GL3_RotateForEntity(entity); entity->angles[PITCH] = -entity->angles[PITCH]; /* select skin */ if (entity->skin) { skin = entity->skin; /* custom player skin */ } else { if (entity->skinnum >= MAX_MD2SKINS) { skin = model->skins[0]; } else { skin = model->skins[entity->skinnum]; if (!skin) { skin = model->skins[0]; } } } if (!skin) { skin = gl3_notexture; /* fallback... */ } GL3_Bind(skin->texnum); if (entity->flags & RF_TRANSLUCENT) { glEnable(GL_BLEND); } if ((entity->frame >= paliashdr->num_frames) || (entity->frame < 0)) { R_Printf(PRINT_DEVELOPER, "R_DrawAliasModel %s: no such frame %d\n", model->name, entity->frame); entity->frame = 0; entity->oldframe = 0; } if ((entity->oldframe >= paliashdr->num_frames) || (entity->oldframe < 0)) { R_Printf(PRINT_DEVELOPER, "R_DrawAliasModel %s: no such oldframe %d\n", model->name, entity->oldframe); entity->frame = 0; entity->oldframe = 0; } DrawAliasFrameLerp(paliashdr, entity, shadelight); //glPopMatrix(); gl3state.uni3DData.transModelMat4 = origModelMat; GL3_UpdateUBO3D(); if ((entity->flags & RF_WEAPONMODEL) && (gl_lefthand->value == 1.0F)) { gl3state.uni3DData.transProjMat4 = origProjMat; GL3_UpdateUBO3D(); glCullFace(GL_FRONT); } if (entity->flags & RF_TRANSLUCENT) { glDisable(GL_BLEND); } if (entity->flags & RF_DEPTHHACK) { glDepthRange(gl3depthmin, gl3depthmax); } if (gl_shadows->value && !(entity->flags & (RF_TRANSLUCENT | RF_WEAPONMODEL | RF_NOSHADOW))) { gl3_shadowinfo_t si = {0}; VectorCopy(lightspot, si.lightspot); VectorCopy(shadevector, si.shadevector); si.paliashdr = paliashdr; si.entity = entity; da_push(shadowModels, si); } } void GL3_ResetShadowAliasModels(void) { da_clear(shadowModels); } void GL3_DrawAliasShadows(void) { size_t numShadowModels = da_count(shadowModels); if(numShadowModels == 0) { return; } //glPushMatrix(); hmm_mat4 oldMat = gl3state.uni3DData.transModelMat4; glEnable(GL_BLEND); GL3_UseProgram(gl3state.si3DaliasColor.shaderProgram); if (have_stencil) { glEnable(GL_STENCIL_TEST); glStencilFunc(GL_EQUAL, 1, 2); glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); } for(size_t i=0; ientity; /* don't rotate shadows on ungodly axes */ //glTranslatef(e->origin[0], e->origin[1], e->origin[2]); //glRotatef(e->angles[1], 0, 0, 1); hmm_mat4 rotTransMat = HMM_Rotate(e->angles[1], HMM_Vec3(0, 0, 1)); VectorCopy(e->origin, rotTransMat.Elements[3]); gl3state.uni3DData.transModelMat4 = HMM_MultiplyMat4(oldMat, rotTransMat); GL3_UpdateUBO3D(); DrawAliasShadow(si); } if (have_stencil) { glDisable(GL_STENCIL_TEST); } glDisable(GL_BLEND); //glPopMatrix(); gl3state.uni3DData.transModelMat4 = oldMat; GL3_UpdateUBO3D(); } yquake2-QUAKE2_7_10/src/client/refresh/gl3/gl3_misc.c000066400000000000000000000076521321245476300221700ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2016-2017 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. * * ======================================================================= * * Misc OpenGL3 refresher functions * * ======================================================================= */ #include "header/local.h" gl3image_t *gl3_notexture; /* use for bad textures */ gl3image_t *gl3_particletexture; /* little dot for particles */ void GL3_SetDefaultState(void) { glClearColor(1, 0, 0.5, 0.5); glDisable(GL_MULTISAMPLE); glCullFace(GL_FRONT); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glDisable(GL_BLEND); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // TODO: gl_texturemode, gl_texturealphamode? //GL3_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); if (gl_msaa_samples->value) { glEnable(GL_MULTISAMPLE); // glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST); TODO what is this for? } } static 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}, }; void GL3_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; } } gl3_particletexture = GL3_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; } } gl3_notexture = GL3_LoadPic("***r_notexture***", (byte *)data, 8, 0, 8, 0, it_wall, 32); } void GL3_ScreenShot(void) { int w=vid.width, h=vid.height; byte *buffer = malloc(w*h*3); if (!buffer) { R_Printf(PRINT_ALL, "GL3_ScreenShot: Couldn't malloc %d bytes\n", w*h*3); return; } glPixelStorei(GL_PACK_ALIGNMENT, 1); glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, buffer); // the pixels are now row-wise left to right, bottom to top, // but we need them row-wise left to right, top to bottom. // so swap bottom rows with top rows { size_t bytesPerRow = 3*w; byte rowBuffer[bytesPerRow]; byte *curRowL = buffer; // first byte of first row byte *curRowH = buffer + bytesPerRow*(h-1); // first byte of last row while(curRowL < curRowH) { memcpy(rowBuffer, curRowL, bytesPerRow); memcpy(curRowL, curRowH, bytesPerRow); memcpy(curRowH, rowBuffer, bytesPerRow); curRowL += bytesPerRow; curRowH -= bytesPerRow; } } ri.Vid_WriteScreenshot(w, h, 3, buffer); free(buffer); } yquake2-QUAKE2_7_10/src/client/refresh/gl3/gl3_model.c000066400000000000000000000501561321245476300223320ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2016-2017 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. * * ======================================================================= * * Model loading and caching for OpenGL3. Includes the .bsp file format * * ======================================================================= */ #include "header/local.h" enum { MAX_MOD_KNOWN = 512 }; static gl3model_t *loadmodel; static byte mod_novis[MAX_MAP_LEAFS / 8]; gl3model_t mod_known[MAX_MOD_KNOWN]; static int mod_numknown; int registration_sequence; static byte *mod_base; /* the inline * models from the current map are kept seperate */ gl3model_t mod_inline[MAX_MOD_KNOWN]; mleaf_t * GL3_Mod_PointInLeaf(vec3_t p, gl3model_t *model) { mnode_t *node; float d; cplane_t *plane; if (!model || !model->nodes) { ri.Sys_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 */ } static byte * Mod_DecompressVis(byte *in, gl3model_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* GL3_Mod_ClusterPVS(int cluster, gl3model_t *model) { if ((cluster == -1) || !model->vis) { return mod_novis; } return Mod_DecompressVis((byte *)model->vis + model->vis->bitofs[cluster][DVIS_PVS], model); } void GL3_Mod_Modellist_f(void) { int i; gl3model_t *mod; int total; total = 0; R_Printf(PRINT_ALL, "Loaded models:\n"); for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { if (!mod->name[0]) { continue; } R_Printf(PRINT_ALL, "%8i : %s\n", mod->extradatasize, mod->name); total += mod->extradatasize; } R_Printf(PRINT_ALL, "Total resident: %i\n", total); } void GL3_Mod_Init(void) { memset(mod_novis, 0xff, sizeof(mod_novis)); } static 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); } static 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]); } } static 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)) { ri.Sys_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]); } } static 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); } static 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)) { ri.Sys_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); } } static 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)) { ri.Sys_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]); } } static 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)) { ri.Sys_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 = GL3_FindImage(name, it_wall); if (!out->image) { R_Printf(PRINT_ALL, "Couldn't load %s\n", name); out->image = gl3_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[] */ static 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; } } extern void GL3_SubdivideSurface(msurface_t *fa, gl3model_t* loadmodel); static 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)) { ri.Sys_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; GL3_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)) { ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: bad texinfo number"); } out->texinfo = loadmodel->texinfo + ti; Mod_CalcSurfaceExtents(out); /* lighting info */ for (i = 0; i < MAX_LIGHTMAPS_PER_SURFACE; 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; } GL3_SubdivideSurface(out, loadmodel); /* cut up polygon for warps */ } /* create lightmaps and polygons */ if (!(out->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP))) { GL3_LM_CreateSurfaceLightmap(out); } if (!(out->texinfo->flags & SURF_WARP)) { GL3_LM_BuildPolygonFromSurface(out); } } GL3_LM_EndBuildingLightmaps(); } static 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); } static 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)) { ri.Sys_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 */ } static 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)) { ri.Sys_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); } } static 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)) { ri.Sys_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)) { ri.Sys_Error(ERR_DROP, "Mod_ParseMarksurfaces: bad surface number"); } out[i] = loadmodel->surfaces + j; } } static void Mod_LoadSurfedges(lump_t *l) { int i, count; int *in, *out; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); if ((count < 1) || (count >= MAX_MAP_SURFEDGES)) { ri.Sys_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]); } } static 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)) { ri.Sys_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; } } static void Mod_LoadBrushModel(gl3model_t *mod, void *buffer) { int i; dheader_t *header; mmodel_t *bm; loadmodel->type = mod_brush; if (loadmodel != mod_known) { ri.Sys_Error(ERR_DROP, "Loaded a brush model after the world"); } header = (dheader_t *)buffer; i = LittleLong(header->version); if (i != BSPVERSION) { ri.Sys_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++) { gl3model_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) { ri.Sys_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; } } static void Mod_Free(gl3model_t *mod) { Hunk_Free(mod->extradata); memset(mod, 0, sizeof(*mod)); } void GL3_Mod_FreeAll(void) { int i; for (i = 0; i < mod_numknown; i++) { if (mod_known[i].extradatasize) { Mod_Free(&mod_known[i]); } } } extern void GL3_LoadMD2(gl3model_t *mod, void *buffer); extern void GL3_LoadSP2(gl3model_t *mod, void *buffer, int modfilelen); /* * Loads in a model for the given name */ static gl3model_t * Mod_ForName(char *name, qboolean crash) { gl3model_t *mod; unsigned *buf; int i; if (!name[0]) { ri.Sys_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) || !gl3_worldmodel || (i >= gl3_worldmodel->numsubmodels)) { ri.Sys_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) { ri.Sys_Error(ERR_DROP, "mod_numknown == MAX_MOD_KNOWN"); } mod_numknown++; } strcpy(mod->name, name); /* load the file */ int modfilelen = ri.FS_LoadFile(mod->name, (void **)&buf); if (!buf) { if (crash) { ri.Sys_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); GL3_LoadMD2(mod, buf); break; case IDSPRITEHEADER: loadmodel->extradata = Hunk_Begin(0x10000); GL3_LoadSP2(mod, buf, modfilelen); break; case IDBSPHEADER: loadmodel->extradata = Hunk_Begin(0x1000000); Mod_LoadBrushModel(mod, buf); break; default: ri.Sys_Error(ERR_DROP, "Mod_NumForName: unknown fileid for %s", mod->name); break; } loadmodel->extradatasize = Hunk_End(); ri.FS_FreeFile(buf); return mod; } /* * Specifies the model that will be used as the world */ void GL3_BeginRegistration(char *model) { char fullname[MAX_QPATH]; cvar_t *flushmap; registration_sequence++; gl3_oldviewcluster = -1; /* force markleafs */ gl3state.currentlightmap = -1; 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 = ri.Cvar_Get("flushmap", "0", 0); if (strcmp(mod_known[0].name, fullname) || flushmap->value) { Mod_Free(&mod_known[0]); } gl3_worldmodel = Mod_ForName(fullname, true); gl3_viewcluster = -1; } struct model_s * GL3_RegisterModel(char *name) { gl3model_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] = GL3_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] = GL3_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 GL3_EndRegistration(void) { int i; gl3model_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); } } GL3_FreeUnusedImages(); } yquake2-QUAKE2_7_10/src/client/refresh/gl3/gl3_sdl.c000066400000000000000000000233771321245476300220210ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2016-2017 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. * * ======================================================================= * * SDL-specific refresher things, for OpenGL3 * Also all glad (or whatever OpenGL loader I end up using) specific things * (I'd like to keep the OpenGL loader easily exchangable if possible) * * ======================================================================= */ #include "header/local.h" #ifdef SDL2 #include #else // SDL1.2 #include #endif //SDL2 #if SDL_VERSION_ATLEAST(2, 0, 0) static SDL_Window* window = NULL; static SDL_GLContext context = NULL; #else static SDL_Surface* window = NULL; #endif qboolean have_stencil = false; static qboolean vsyncActive = false; // called by GLimp_InitGraphics() before creating window, // returns flags for SDL window creation, -1 on error int GL3_PrepareForWindow(void) { unsigned int flags = 0; int msaa_samples = 0; if(SDL_GL_LoadLibrary(NULL) < 0) // Default OpenGL is fine. { // TODO: is there a better way? ri.Sys_Error(ERR_FATAL, "Couldn't load libGL: %s!", SDL_GetError()); return -1; } 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 SDL_VERSION_ATLEAST(2, 0, 0) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); int contextFlags = SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG; if(gl3_debugcontext && gl3_debugcontext->value) { contextFlags |= SDL_GL_CONTEXT_DEBUG_FLAG; } if(contextFlags != 0) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, contextFlags); } gl3config.compat_profile = false; #else // SDL1.2 doesn't have all this, so we'll have some kind of compatibility profile gl3config.compat_profile = true; #endif #if !SDL_VERSION_ATLEAST(2, 0, 0) /* Set vsync - For SDL1.2, this must be done before creating the window */ SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, gl_swapinterval->value ? 1 : 0); #endif if (gl_msaa_samples->value) { msaa_samples = gl_msaa_samples->value; if (SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1) < 0) { R_Printf(PRINT_ALL, "MSAA is unsupported: %s\n", SDL_GetError()); ri.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) { R_Printf(PRINT_ALL, "MSAA %ix is unsupported: %s\n", msaa_samples, SDL_GetError()); ri.Cvar_SetValue("gl_msaa_samples", 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); } } else { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); } /* Initiate the flags */ #if SDL_VERSION_ATLEAST(2, 0, 0) flags = SDL_WINDOW_OPENGL; #else // SDL 1.2 flags = SDL_OPENGL; #endif return flags; } enum { // for some reason my driver calls the DebugCallback with the following severity // even though I think it shouldn't for the extension I'm using? // anyway, my gl headers don't know GL_DEBUG_SEVERITY_NOTIFICATION_* so I define it here QGL_DEBUG_SEVERITY_NOTIFICATION = 0x826B }; static void APIENTRY DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) { const char* sourceStr = "Source: Unknown"; const char* typeStr = "Type: Unknown"; const char* severityStr = "Severity: Unknown"; switch(severity) { case QGL_DEBUG_SEVERITY_NOTIFICATION: // severityStr = "Severity: Note"; break; return; // ignore these case GL_DEBUG_SEVERITY_HIGH_ARB: severityStr = "Severity: High"; break; case GL_DEBUG_SEVERITY_MEDIUM_ARB: severityStr = "Severity: Medium"; break; case GL_DEBUG_SEVERITY_LOW_ARB: severityStr = "Severity: Low"; break; } switch(source) { #define SRCCASE(X) case GL_DEBUG_SOURCE_ ## X ## _ARB: sourceStr = "Source: " #X; break; SRCCASE(API); SRCCASE(WINDOW_SYSTEM); SRCCASE(SHADER_COMPILER); SRCCASE(THIRD_PARTY); SRCCASE(APPLICATION); SRCCASE(OTHER); #undef SRCCASE } switch(type) { #define TYPECASE(X) case GL_DEBUG_TYPE_ ## X ## _ARB: typeStr = "Type: " #X; break; TYPECASE(ERROR); TYPECASE(DEPRECATED_BEHAVIOR); TYPECASE(UNDEFINED_BEHAVIOR); TYPECASE(PORTABILITY); TYPECASE(PERFORMANCE); TYPECASE(OTHER); #undef TYPECASE } // use PRINT_ALL - this is only called with gl3_debugcontext != 0 anyway. R_Printf(PRINT_ALL, "GLDBG %s %s %s: %s\n", sourceStr, typeStr, severityStr, message); } int GL3_InitContext(void* win) { int msaa_samples = 0, stencil_bits = 0; char title[40] = {0}; if(win == NULL) { ri.Sys_Error(ERR_FATAL, "R_InitContext() must not be called with NULL argument!"); return false; } #if SDL_VERSION_ATLEAST(2, 0, 0) window = (SDL_Window*)win; context = SDL_GL_CreateContext(window); if(context == NULL) { R_Printf(PRINT_ALL, "GL3_InitContext(): Creating OpenGL Context failed: %s\n", SDL_GetError()); window = NULL; return false; } #else // SDL 1.2 window = (SDL_Surface*)win; // context is created implicitly with window, nothing to do here #endif if (gl_msaa_samples->value) { if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &msaa_samples) == 0) { ri.Cvar_SetValue("gl_msaa_samples", msaa_samples); } } #if SDL_VERSION_ATLEAST(2, 0, 0) /* For SDL2, this must be done after creating the window */ GL3_SetSwapInterval(); #else // SDL1.2 - set vsyncActive to whatever is configured, hoping it was actually set vsyncActive = gl_swapinterval->value ? 1 : 0; #endif /* Initialize the stencil buffer */ if (SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil_bits) == 0) { R_Printf(PRINT_ALL, "Got %d bits of stencil.\n", stencil_bits); if (stencil_bits >= 1) { have_stencil = true; } } if(!gladLoadGLLoader(SDL_GL_GetProcAddress)) { R_Printf(PRINT_ALL, "GL3_InitContext(): ERROR: loading OpenGL function pointers failed!\n"); return false; } else if (GLVersion.major < 3 || (GLVersion.major == 3 && GLVersion.minor < 2)) { R_Printf(PRINT_ALL, "GL3_InitContext(): ERROR: glad only got GL version %d.%d!\n", GLVersion.major, GLVersion.minor); return false; } else { R_Printf(PRINT_ALL, "Successfully loaded OpenGL function pointers using glad, got version %d.%d!\n", GLVersion.major, GLVersion.minor); } #if SDL_VERSION_ATLEAST(2, 0, 0) gl3config.debug_output = GLAD_GL_ARB_debug_output != 0; #else gl3config.debug_output = 0; // no debug contexts with SDL1.2 - can't set the context flag! #endif gl3config.anisotropic = GLAD_GL_EXT_texture_filter_anisotropic != 0; gl3config.major_version = GLVersion.major; gl3config.minor_version = GLVersion.minor; if(gl3_debugcontext && gl3_debugcontext->value && gl3config.debug_output) { glDebugMessageCallbackARB(DebugCallback, NULL); // call GL3_DebugCallback() synchronously, i.e. directly when and where the error happens // (so we can get the cause in a backtrace) glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); // TODO: only do this if gl3_debugcontext->value >= 2 ? // TODO: the following line could control verboseness (in that case we'd get all the low prio messages) // glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW_ARB, 0, NULL, true); } /* Window title - set here so we can display renderer name in it */ snprintf(title, sizeof(title), "Yamagi Quake II %s - OpenGL 3.2", YQ2VERSION); #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_SetWindowTitle(window, title); #else SDL_WM_SetCaption(title, title); #endif return true; } void GL3_SetSwapInterval(void) { #if SDL_VERSION_ATLEAST(2, 0, 0) /* Set vsync - TODO: -1 could be set for "late swap tearing" */ SDL_GL_SetSwapInterval(gl_swapinterval->value ? 1 : 0); vsyncActive = SDL_GL_GetSwapInterval() != 0; #else R_Printf(PRINT_ALL, "SDL1.2 requires a vid_restart to apply changes to gl_swapinterval (vsync)!\n"); #endif } qboolean GL3_IsVsyncActive(void) { return vsyncActive; } /* * Swaps the buffers to show the new frame */ void GL3_EndFrame(void) { #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_GL_SwapWindow(window); #else SDL_GL_SwapBuffers(); #endif } /* * Shuts the SDL render backend down */ void GL3_ShutdownWindow(qboolean contextOnly) { /* 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. Only do this if we have a context, though. */ if (window) { #if SDL_VERSION_ATLEAST(2, 0, 0) if(context) { glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); GL3_EndFrame(); SDL_GL_DeleteContext(context); context = NULL; } #else // SDL1.2 glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); GL3_EndFrame(); #endif } window = NULL; if(!contextOnly) { ri.Vid_ShutdownWindow(); } } yquake2-QUAKE2_7_10/src/client/refresh/gl3/gl3_shaders.c000066400000000000000000001026661321245476300226670ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2016-2017 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. * * ======================================================================= * * OpenGL3 refresher: Handling shaders * * ======================================================================= */ #include "header/local.h" // TODO: remove eprintf() usage #define eprintf(...) R_Printf(PRINT_ALL, __VA_ARGS__) static GLuint CompileShader(GLenum shaderType, const char* shaderSrc, const char* shaderSrc2) { GLuint shader = glCreateShader(shaderType); const char* sources[2] = { shaderSrc, shaderSrc2 }; int numSources = shaderSrc2 != NULL ? 2 : 1; glShaderSource(shader, numSources, sources, NULL); glCompileShader(shader); GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if(status != GL_TRUE) { char buf[2048]; char* bufPtr = buf; int bufLen = sizeof(buf); GLint infoLogLength; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); if(infoLogLength >= bufLen) { bufPtr = malloc(infoLogLength+1); bufLen = infoLogLength+1; if(bufPtr == NULL) { bufPtr = buf; bufLen = sizeof(buf); eprintf("WARN: In CompileShader(), malloc(%d) failed!\n", infoLogLength+1); } } glGetShaderInfoLog(shader, bufLen, NULL, bufPtr); const char* shaderTypeStr = ""; switch(shaderType) { case GL_VERTEX_SHADER: shaderTypeStr = "Vertex"; break; case GL_FRAGMENT_SHADER: shaderTypeStr = "Fragment"; break; case GL_GEOMETRY_SHADER: shaderTypeStr = "Geometry"; break; /* not supported in OpenGL3.2 and we're unlikely to need/use them anyway case GL_COMPUTE_SHADER: shaderTypeStr = "Compute"; break; case GL_TESS_CONTROL_SHADER: shaderTypeStr = "TessControl"; break; case GL_TESS_EVALUATION_SHADER: shaderTypeStr = "TessEvaluation"; break; */ } eprintf("ERROR: Compiling %s Shader failed: %s\n", shaderTypeStr, bufPtr); glDeleteShader(shader); if(bufPtr != buf) free(bufPtr); return 0; } return shader; } static GLuint CreateShaderProgram(int numShaders, const GLuint* shaders) { int i=0; GLuint shaderProgram = glCreateProgram(); if(shaderProgram == 0) { eprintf("ERROR: Couldn't create a new Shader Program!\n"); return 0; } for(i=0; i= bufLen) { bufPtr = malloc(infoLogLength+1); bufLen = infoLogLength+1; if(bufPtr == NULL) { bufPtr = buf; bufLen = sizeof(buf); eprintf("WARN: In CreateShaderProgram(), malloc(%d) failed!\n", infoLogLength+1); } } glGetProgramInfoLog(shaderProgram, bufLen, NULL, bufPtr); eprintf("ERROR: Linking shader program failed: %s\n", bufPtr); glDeleteProgram(shaderProgram); if(bufPtr != buf) free(bufPtr); return 0; } for(i=0; i 1.0) // this makes sure the particle is round discard; vec4 texel = passColor; // apply gamma correction and intensity //texel.rgb *= intensity; TODO: intensity? Probably not? outColor.rgb = pow(texel.rgb, vec3(gamma)); // I want the particles to fade out towards the edge, the following seems to look nice texel.a *= min(1.0, particleFadeFactor*(1.0 - distSquared)); outColor.a = texel.a; // I think alpha shouldn't be modified by gamma and intensity } ); static const char* fragmentSrcParticlesSquare = MULTILINE_STRING( // it gets attributes and uniforms from fragmentCommon3D in vec4 passColor; void main() { outColor = passColor; } ); #undef MULTILINE_STRING enum { GL3_BINDINGPOINT_UNICOMMON, GL3_BINDINGPOINT_UNI2D, GL3_BINDINGPOINT_UNI3D, GL3_BINDINGPOINT_UNILIGHTS }; static qboolean initShader2D(gl3ShaderInfo_t* shaderInfo, const char* vertSrc, const char* fragSrc) { GLuint shaders2D[2] = {0}; GLuint prog = 0; if(shaderInfo->shaderProgram != 0) { R_Printf(PRINT_ALL, "WARNING: calling initShader2D for gl3ShaderInfo_t that already has a shaderProgram!\n"); glDeleteProgram(shaderInfo->shaderProgram); } //shaderInfo->uniColor = shaderInfo->uniProjMatrix = shaderInfo->uniModelViewMatrix = -1; shaderInfo->shaderProgram = 0; shaderInfo->uniLmScales = -1; shaders2D[0] = CompileShader(GL_VERTEX_SHADER, vertSrc, NULL); if(shaders2D[0] == 0) return false; shaders2D[1] = CompileShader(GL_FRAGMENT_SHADER, fragSrc, NULL); if(shaders2D[1] == 0) { glDeleteShader(shaders2D[0]); return false; } prog = CreateShaderProgram(2, shaders2D); // I think the shaders aren't needed anymore once they're linked into the program glDeleteShader(shaders2D[0]); glDeleteShader(shaders2D[1]); if(prog == 0) { return false; } shaderInfo->shaderProgram = prog; GL3_UseProgram(prog); // Bind the buffer object to the uniform blocks GLuint blockIndex = glGetUniformBlockIndex(prog, "uniCommon"); if(blockIndex != GL_INVALID_INDEX) { GLint blockSize; glGetActiveUniformBlockiv(prog, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize); if(blockSize != sizeof(gl3state.uniCommonData)) { R_Printf(PRINT_ALL, "WARNING: OpenGL driver disagrees with us about UBO size of 'uniCommon': %i vs %i\n", blockSize, (int)sizeof(gl3state.uniCommonData)); goto err_cleanup; } glUniformBlockBinding(prog, blockIndex, GL3_BINDINGPOINT_UNICOMMON); } else { R_Printf(PRINT_ALL, "WARNING: Couldn't find uniform block index 'uniCommon'\n"); // TODO: clean up? return false; } blockIndex = glGetUniformBlockIndex(prog, "uni2D"); if(blockIndex != GL_INVALID_INDEX) { GLint blockSize; glGetActiveUniformBlockiv(prog, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize); if(blockSize != sizeof(gl3state.uni2DData)) { R_Printf(PRINT_ALL, "WARNING: OpenGL driver disagrees with us about UBO size of 'uni2D'\n"); goto err_cleanup; } glUniformBlockBinding(prog, blockIndex, GL3_BINDINGPOINT_UNI2D); } else { R_Printf(PRINT_ALL, "WARNING: Couldn't find uniform block index 'uni2D'\n"); goto err_cleanup; } return true; err_cleanup: if(shaders2D[0] != 0) glDeleteShader(shaders2D[0]); if(shaders2D[1] != 0) glDeleteShader(shaders2D[1]); if(prog != 0) glDeleteProgram(prog); return false; } static qboolean initShader3D(gl3ShaderInfo_t* shaderInfo, const char* vertSrc, const char* fragSrc) { GLuint shaders3D[2] = {0}; GLuint prog = 0; int i=0; if(shaderInfo->shaderProgram != 0) { R_Printf(PRINT_ALL, "WARNING: calling initShader3D for gl3ShaderInfo_t that already has a shaderProgram!\n"); glDeleteProgram(shaderInfo->shaderProgram); } shaderInfo->shaderProgram = 0; shaderInfo->uniLmScales = -1; shaders3D[0] = CompileShader(GL_VERTEX_SHADER, vertexCommon3D, vertSrc); if(shaders3D[0] == 0) return false; shaders3D[1] = CompileShader(GL_FRAGMENT_SHADER, fragmentCommon3D, fragSrc); if(shaders3D[1] == 0) { glDeleteShader(shaders3D[0]); return false; } prog = CreateShaderProgram(2, shaders3D); if(prog == 0) { goto err_cleanup; } GL3_UseProgram(prog); // Bind the buffer object to the uniform blocks GLuint blockIndex = glGetUniformBlockIndex(prog, "uniCommon"); if(blockIndex != GL_INVALID_INDEX) { GLint blockSize; glGetActiveUniformBlockiv(prog, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize); if(blockSize != sizeof(gl3state.uniCommonData)) { R_Printf(PRINT_ALL, "WARNING: OpenGL driver disagrees with us about UBO size of 'uniCommon'\n"); goto err_cleanup; } glUniformBlockBinding(prog, blockIndex, GL3_BINDINGPOINT_UNICOMMON); } else { R_Printf(PRINT_ALL, "WARNING: Couldn't find uniform block index 'uniCommon'\n"); goto err_cleanup; } blockIndex = glGetUniformBlockIndex(prog, "uni3D"); if(blockIndex != GL_INVALID_INDEX) { GLint blockSize; glGetActiveUniformBlockiv(prog, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize); if(blockSize != sizeof(gl3state.uni3DData)) { R_Printf(PRINT_ALL, "WARNING: OpenGL driver disagrees with us about UBO size of 'uni3D'\n"); R_Printf(PRINT_ALL, " driver says %d, we expect %d\n", blockSize, (int)sizeof(gl3state.uni3DData)); goto err_cleanup; } glUniformBlockBinding(prog, blockIndex, GL3_BINDINGPOINT_UNI3D); } else { R_Printf(PRINT_ALL, "WARNING: Couldn't find uniform block index 'uni3D'\n"); goto err_cleanup; } blockIndex = glGetUniformBlockIndex(prog, "uniLights"); if(blockIndex != GL_INVALID_INDEX) { GLint blockSize; glGetActiveUniformBlockiv(prog, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize); if(blockSize != sizeof(gl3state.uniLightsData)) { R_Printf(PRINT_ALL, "WARNING: OpenGL driver disagrees with us about UBO size of 'uniLights'\n"); R_Printf(PRINT_ALL, " OpenGL says %d, we say %d\n", blockSize, (int)sizeof(gl3state.uniLightsData)); goto err_cleanup; } glUniformBlockBinding(prog, blockIndex, GL3_BINDINGPOINT_UNILIGHTS); } // else: as uniLights is only used in the LM shaders, it's ok if it's missing // make sure texture is GL_TEXTURE0 GLint texLoc = glGetUniformLocation(prog, "tex"); if(texLoc != -1) { glUniform1i(texLoc, 0); } // .. and the 4 lightmap texture use GL_TEXTURE1..4 char lmName[10] = "lightmapX"; for(i=0; i<4; ++i) { lmName[8] = '0'+i; GLint lmLoc = glGetUniformLocation(prog, lmName); if(lmLoc != -1) { glUniform1i(lmLoc, i+1); // lightmap0 belongs to GL_TEXTURE1, lightmap1 to GL_TEXTURE2 etc } } GLint lmScalesLoc = glGetUniformLocation(prog, "lmScales"); shaderInfo->uniLmScales = lmScalesLoc; if(lmScalesLoc != -1) { shaderInfo->lmScales[0] = HMM_Vec4(1.0f, 1.0f, 1.0f, 1.0f); for(i=1; i<4; ++i) shaderInfo->lmScales[i] = HMM_Vec4(0.0f, 0.0f, 0.0f, 0.0f); glUniform4fv(lmScalesLoc, 4, shaderInfo->lmScales[0].Elements); } shaderInfo->shaderProgram = prog; // I think the shaders aren't needed anymore once they're linked into the program glDeleteShader(shaders3D[0]); glDeleteShader(shaders3D[1]); return true; err_cleanup: if(shaders3D[0] != 0) glDeleteShader(shaders3D[0]); if(shaders3D[1] != 0) glDeleteShader(shaders3D[1]); if(prog != 0) glDeleteProgram(prog); return false; } static void initUBOs(void) { gl3state.uniCommonData.gamma = 1.0f/vid_gamma->value; gl3state.uniCommonData.intensity = gl3_intensity->value; gl3state.uniCommonData.intensity2D = gl3_intensity_2D->value; gl3state.uniCommonData.color = HMM_Vec4(1, 1, 1, 1); glGenBuffers(1, &gl3state.uniCommonUBO); glBindBuffer(GL_UNIFORM_BUFFER, gl3state.uniCommonUBO); glBindBufferBase(GL_UNIFORM_BUFFER, GL3_BINDINGPOINT_UNICOMMON, gl3state.uniCommonUBO); glBufferData(GL_UNIFORM_BUFFER, sizeof(gl3state.uniCommonData), &gl3state.uniCommonData, GL_DYNAMIC_DRAW); // the matrix will be set to something more useful later, before being used gl3state.uni2DData.transMat4 = HMM_Mat4(); glGenBuffers(1, &gl3state.uni2DUBO); glBindBuffer(GL_UNIFORM_BUFFER, gl3state.uni2DUBO); glBindBufferBase(GL_UNIFORM_BUFFER, GL3_BINDINGPOINT_UNI2D, gl3state.uni2DUBO); glBufferData(GL_UNIFORM_BUFFER, sizeof(gl3state.uni2DData), &gl3state.uni2DData, GL_DYNAMIC_DRAW); // the matrices will be set to something more useful later, before being used gl3state.uni3DData.transProjMat4 = HMM_Mat4(); gl3state.uni3DData.transViewMat4 = HMM_Mat4(); gl3state.uni3DData.transModelMat4 = gl3_identityMat4; gl3state.uni3DData.scroll = 0.0f; gl3state.uni3DData.time = 0.0f; gl3state.uni3DData.alpha = 1.0f; // gl_overbrightbits 0 means "no scaling" which is equivalent to multiplying with 1 gl3state.uni3DData.overbrightbits = (gl3_overbrightbits->value <= 0.0f) ? 1.0f : gl3_overbrightbits->value; gl3state.uni3DData.particleFadeFactor = gl3_particle_fade_factor->value; glGenBuffers(1, &gl3state.uni3DUBO); glBindBuffer(GL_UNIFORM_BUFFER, gl3state.uni3DUBO); glBindBufferBase(GL_UNIFORM_BUFFER, GL3_BINDINGPOINT_UNI3D, gl3state.uni3DUBO); glBufferData(GL_UNIFORM_BUFFER, sizeof(gl3state.uni3DData), &gl3state.uni3DData, GL_DYNAMIC_DRAW); glGenBuffers(1, &gl3state.uniLightsUBO); glBindBuffer(GL_UNIFORM_BUFFER, gl3state.uniLightsUBO); glBindBufferBase(GL_UNIFORM_BUFFER, GL3_BINDINGPOINT_UNILIGHTS, gl3state.uniLightsUBO); glBufferData(GL_UNIFORM_BUFFER, sizeof(gl3state.uniLightsData), &gl3state.uniLightsData, GL_DYNAMIC_DRAW); gl3state.currentUBO = gl3state.uniLightsUBO; } static qboolean createShaders(void) { if(!initShader2D(&gl3state.si2D, vertexSrc2D, fragmentSrc2D)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for textured 2D rendering!\n"); return false; } if(!initShader2D(&gl3state.si2Dcolor, vertexSrc2Dcolor, fragmentSrc2Dcolor)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for color-only 2D rendering!\n"); return false; } if(!initShader3D(&gl3state.si3Dlm, vertexSrc3Dlm, fragmentSrc3Dlm)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for textured 3D rendering with lightmap!\n"); return false; } if(!initShader3D(&gl3state.si3Dtrans, vertexSrc3D, fragmentSrc3D)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for rendering translucent 3D things!\n"); return false; } if(!initShader3D(&gl3state.si3DcolorOnly, vertexSrc3D, fragmentSrc3Dcolor)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for flat-colored 3D rendering!\n"); return false; } /* if(!initShader3D(&gl3state.si3Dlm, vertexSrc3Dlm, fragmentSrc3D)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for blending 3D lightmaps rendering!\n"); return false; } */ if(!initShader3D(&gl3state.si3Dturb, vertexSrc3Dwater, fragmentSrc3Dwater)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for water rendering!\n"); return false; } if(!initShader3D(&gl3state.si3DlmFlow, vertexSrc3DlmFlow, fragmentSrc3Dlm)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for scrolling textured 3D rendering with lightmap!\n"); return false; } if(!initShader3D(&gl3state.si3DtransFlow, vertexSrc3Dflow, fragmentSrc3D)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for scrolling textured translucent 3D rendering!\n"); return false; } if(!initShader3D(&gl3state.si3Dsky, vertexSrc3D, fragmentSrc3Dsky)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for sky rendering!\n"); return false; } if(!initShader3D(&gl3state.si3Dsprite, vertexSrc3D, fragmentSrc3Dsprite)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for sprite rendering!\n"); return false; } if(!initShader3D(&gl3state.si3DspriteAlpha, vertexSrc3D, fragmentSrc3DspriteAlpha)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for alpha-tested sprite rendering!\n"); return false; } if(!initShader3D(&gl3state.si3Dalias, vertexSrcAlias, fragmentSrcAlias)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for rendering textured models!\n"); return false; } if(!initShader3D(&gl3state.si3DaliasColor, vertexSrcAlias, fragmentSrcAliasColor)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for rendering flat-colored models!\n"); return false; } const char* particleFrag = fragmentSrcParticles; if(gl3_particle_square->value != 0.0f) { particleFrag = fragmentSrcParticlesSquare; } if(!initShader3D(&gl3state.siParticle, vertexSrcParticles, particleFrag)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program for rendering particles!\n"); return false; } gl3state.currentShaderProgram = 0; return true; } qboolean GL3_InitShaders(void) { initUBOs(); return createShaders(); } static void deleteShaders(void) { const gl3ShaderInfo_t siZero = {0}; for(gl3ShaderInfo_t* si = &gl3state.si2D; si <= &gl3state.siParticle; ++si) { if(si->shaderProgram != 0) glDeleteProgram(si->shaderProgram); *si = siZero; } } void GL3_ShutdownShaders(void) { deleteShaders(); // let's (ab)use the fact that all 4 UBO handles are consecutive fields // of the gl3state struct glDeleteBuffers(4, &gl3state.uniCommonUBO); gl3state.uniCommonUBO = gl3state.uni2DUBO = gl3state.uni3DUBO = gl3state.uniLightsUBO = 0; } qboolean GL3_RecreateShaders(void) { // delete and recreate the existing shaders (but not the UBOs) deleteShaders(); return createShaders(); } static inline void updateUBO(GLuint ubo, GLsizeiptr size, void* data) { if(gl3state.currentUBO != ubo) { gl3state.currentUBO = ubo; glBindBuffer(GL_UNIFORM_BUFFER, ubo); } // http://docs.gl/gl3/glBufferSubData says "When replacing the entire data store, // consider using glBufferSubData rather than completely recreating the data store // with glBufferData. This avoids the cost of reallocating the data store." // no idea why glBufferData() doesn't just do that when size doesn't change, but whatever.. // however, it also says glBufferSubData() might cause a stall so I DON'T KNOW! // on Linux/nvidia, by just looking at the fps, glBufferData() and glBufferSubData() make no difference // TODO: STREAM instead DYNAMIC? #if 1 // this seems to be reasonably fast everywhere.. glMapBuffer() seems to be a bit faster on OSX though.. glBufferData(GL_UNIFORM_BUFFER, size, data, GL_DYNAMIC_DRAW); #elif 0 // on OSX this is super slow (200fps instead of 470-500), BUT it is as fast as glBufferData() when orphaning first // nvidia/linux-blob doesn't care about this vs glBufferData() // AMD open source linux (R3 370) is also slower here (not as bad as OSX though) // intel linux doesn't seem to care either (maybe 3% faster, but that might be imagination) // AMD Windows legacy driver (Radeon HD 6950) doesn't care, all 3 alternatives seem to be equally fast //glBufferData(GL_UNIFORM_BUFFER, size, NULL, GL_DYNAMIC_DRAW); // orphan glBufferSubData(GL_UNIFORM_BUFFER, 0, size, data); #else // with my current nvidia-driver (GTX 770, 375.39), the following *really* makes it slower. (<140fps instead of ~850) // on OSX (Intel Haswell Iris Pro, OSX 10.11) this is fastest (~500fps instead of ~470) // on Linux/intel (Ivy Bridge HD-4000, Linux 4.4) this might be a tiny bit faster than the alternatives.. glBufferData(GL_UNIFORM_BUFFER, size, NULL, GL_DYNAMIC_DRAW); // orphan GLvoid* ptr = glMapBuffer(GL_UNIFORM_BUFFER, GL_WRITE_ONLY); memcpy(ptr, data, size); glUnmapBuffer(GL_UNIFORM_BUFFER); #endif // TODO: another alternative: glMapBufferRange() and each time update a different part // of buffer asynchronously (GL_MAP_UNSYNCHRONIZED_BIT) => ringbuffer style // when starting again from the beginning, synchronization must happen I guess.. // also, orphaning might be necessary // and somehow make sure the new range is used by the UBO => glBindBufferRange() // see http://git.quintin.ninja/mjones/Dolphin/blob/4a463f4588e2968c499236458c5712a489622633/Source/Plugins/Plugin_VideoOGL/Src/ProgramShaderCache.cpp#L207 // or https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp } void GL3_UpdateUBOCommon(void) { updateUBO(gl3state.uniCommonUBO, sizeof(gl3state.uniCommonData), &gl3state.uniCommonData); } void GL3_UpdateUBO2D(void) { updateUBO(gl3state.uni2DUBO, sizeof(gl3state.uni2DData), &gl3state.uni2DData); } void GL3_UpdateUBO3D(void) { updateUBO(gl3state.uni3DUBO, sizeof(gl3state.uni3DData), &gl3state.uni3DData); } void GL3_UpdateUBOLights(void) { updateUBO(gl3state.uniLightsUBO, sizeof(gl3state.uniLightsData), &gl3state.uniLightsData); } yquake2-QUAKE2_7_10/src/client/refresh/gl3/gl3_sp2.c000066400000000000000000000041161321245476300217310ustar00rootroot00000000000000/* * 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" void GL3_LoadSP2(gl3model_t *mod, void *buffer, int modfilelen) { 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) { ri.Sys_Error(ERR_DROP, "%s has wrong version number (%i should be %i)", mod->name, sprout->version, SPRITE_VERSION); } if (sprout->numframes > MAX_MD2SKINS) { ri.Sys_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] = GL3_FindImage(sprout->frames[i].name, it_sprite); } mod->type = mod_sprite; } yquake2-QUAKE2_7_10/src/client/refresh/gl3/gl3_surf.c000066400000000000000000000473151321245476300222140ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2016-2017 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. * * ======================================================================= * * Surface generation and drawing * * ======================================================================= */ #include #include "header/local.h" int c_visible_lightmaps; int c_visible_textures; static vec3_t modelorg; /* relative to viewpoint */ static msurface_t *gl3_alpha_surfaces; gl3lightmapstate_t gl3_lms; #define BACKFACE_EPSILON 0.01 extern gl3image_t gl3textures[MAX_GL3TEXTURES]; extern int numgl3textures; void GL3_SurfInit(void) { // init the VAO and VBO for the standard vertexdata: 10 floats and 1 uint // (X, Y, Z), (S, T), (LMS, LMT), (normX, normY, normZ) - last two groups for lightmap/dynlights glGenVertexArrays(1, &gl3state.vao3D); GL3_BindVAO(gl3state.vao3D); glGenBuffers(1, &gl3state.vbo3D); GL3_BindVBO(gl3state.vbo3D); glEnableVertexAttribArray(GL3_ATTRIB_POSITION); qglVertexAttribPointer(GL3_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(gl3_3D_vtx_t), 0); glEnableVertexAttribArray(GL3_ATTRIB_TEXCOORD); qglVertexAttribPointer(GL3_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(gl3_3D_vtx_t), offsetof(gl3_3D_vtx_t, texCoord)); glEnableVertexAttribArray(GL3_ATTRIB_LMTEXCOORD); qglVertexAttribPointer(GL3_ATTRIB_LMTEXCOORD, 2, GL_FLOAT, GL_FALSE, sizeof(gl3_3D_vtx_t), offsetof(gl3_3D_vtx_t, lmTexCoord)); glEnableVertexAttribArray(GL3_ATTRIB_NORMAL); qglVertexAttribPointer(GL3_ATTRIB_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(gl3_3D_vtx_t), offsetof(gl3_3D_vtx_t, normal)); glEnableVertexAttribArray(GL3_ATTRIB_LIGHTFLAGS); qglVertexAttribIPointer(GL3_ATTRIB_LIGHTFLAGS, 1, GL_UNSIGNED_INT, sizeof(gl3_3D_vtx_t), offsetof(gl3_3D_vtx_t, lightFlags)); // init VAO and VBO for model vertexdata: 9 floats // (X,Y,Z), (S,T), (R,G,B,A) glGenVertexArrays(1, &gl3state.vaoAlias); GL3_BindVAO(gl3state.vaoAlias); glGenBuffers(1, &gl3state.vboAlias); GL3_BindVBO(gl3state.vboAlias); glEnableVertexAttribArray(GL3_ATTRIB_POSITION); qglVertexAttribPointer(GL3_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 0); glEnableVertexAttribArray(GL3_ATTRIB_TEXCOORD); qglVertexAttribPointer(GL3_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 3*sizeof(GLfloat)); glEnableVertexAttribArray(GL3_ATTRIB_COLOR); qglVertexAttribPointer(GL3_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 5*sizeof(GLfloat)); glGenBuffers(1, &gl3state.eboAlias); // init VAO and VBO for particle vertexdata: 9 floats // (X,Y,Z), (point_size,distace_to_camera), (R,G,B,A) glGenVertexArrays(1, &gl3state.vaoParticle); GL3_BindVAO(gl3state.vaoParticle); glGenBuffers(1, &gl3state.vboParticle); GL3_BindVBO(gl3state.vboParticle); glEnableVertexAttribArray(GL3_ATTRIB_POSITION); qglVertexAttribPointer(GL3_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 0); // TODO: maybe move point size and camera origin to UBO and calculate distance in vertex shader glEnableVertexAttribArray(GL3_ATTRIB_TEXCOORD); // it's abused for (point_size, distance) here.. qglVertexAttribPointer(GL3_ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 3*sizeof(GLfloat)); glEnableVertexAttribArray(GL3_ATTRIB_COLOR); qglVertexAttribPointer(GL3_ATTRIB_COLOR, 4, GL_FLOAT, GL_FALSE, 9*sizeof(GLfloat), 5*sizeof(GLfloat)); } void GL3_SurfShutdown(void) { glDeleteBuffers(1, &gl3state.vbo3D); gl3state.vbo3D = 0; glDeleteVertexArrays(1, &gl3state.vao3D); gl3state.vao3D = 0; glDeleteBuffers(1, &gl3state.eboAlias); gl3state.eboAlias = 0; glDeleteBuffers(1, &gl3state.vboAlias); gl3state.vboAlias = 0; glDeleteVertexArrays(1, &gl3state.vaoAlias); gl3state.vaoAlias = 0; } /* * Returns true if the box is completely outside the frustom */ static qboolean CullBox(vec3_t mins, vec3_t maxs) { int i; if (!gl_cull->value) { return false; } for (i = 0; i < 4; i++) { if (BOX_ON_PLANE_SIDE(mins, maxs, &frustum[i]) == 2) { return true; } } return false; } /* * Returns the proper texture for a given time and base texture */ static gl3image_t * 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; } static void SetLightFlags(msurface_t *surf) { unsigned int lightFlags = 0; if (surf->dlightframe == gl3_framecount) { lightFlags = surf->dlightbits; } gl3_3D_vtx_t* verts = surf->polys->vertices; int numVerts = surf->polys->numverts; for(int i=0; ipolys->vertices; int numVerts = surf->polys->numverts; for(int i=0; ipolys; GL3_BindVAO(gl3state.vao3D); GL3_BindVBO(gl3state.vbo3D); glBufferData(GL_ARRAY_BUFFER, sizeof(gl3_3D_vtx_t)*p->numverts, p->vertices, GL_STREAM_DRAW); glDrawArrays(GL_TRIANGLE_FAN, 0, p->numverts); } void GL3_DrawGLFlowingPoly(msurface_t *fa) { glpoly_t *p; float scroll; p = fa->polys; scroll = -64.0f * ((gl3_newrefdef.time / 40.0f) - (int)(gl3_newrefdef.time / 40.0f)); if (scroll == 0.0f) { scroll = -64.0f; } if(gl3state.uni3DData.scroll != scroll) { gl3state.uni3DData.scroll = scroll; GL3_UpdateUBO3D(); } GL3_BindVAO(gl3state.vao3D); GL3_BindVBO(gl3state.vbo3D); glBufferData(GL_ARRAY_BUFFER, sizeof(gl3_3D_vtx_t)*p->numverts, p->vertices, GL_STREAM_DRAW); glDrawArrays(GL_TRIANGLE_FAN, 0, p->numverts); } static void DrawTriangleOutlines(void) { STUB_ONCE("TODO: Implement for gl_showtris support!"); #if 0 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 = gl3_lms.lightmap_surfaces[i]; surf != 0; surf = surf->lightmapchain) { p = surf->polys; for ( ; p; p = p->chain) { for (j = 2; j < p->numverts; j++) { GLfloat vtx[12]; unsigned int k; for (k=0; k<3; k++) { vtx[0+k] = p->vertices [ 0 ][ k ]; vtx[3+k] = p->vertices [ j - 1 ][ k ]; vtx[6+k] = p->vertices [ j ][ k ]; vtx[9+k] = p->vertices [ 0 ][ k ]; } glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer( 3, GL_FLOAT, 0, vtx ); glDrawArrays( GL_LINE_STRIP, 0, 4 ); glDisableClientState( GL_VERTEX_ARRAY ); } } } } glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); #endif // 0 } static void UpdateLMscales(const hmm_vec4 lmScales[MAX_LIGHTMAPS_PER_SURFACE], gl3ShaderInfo_t* si) { int i; qboolean hasChanged = false; for(i=0; ilmScales[i] = lmScales[i]; } else if( si->lmScales[i].R != lmScales[i].R || si->lmScales[i].G != lmScales[i].G || si->lmScales[i].B != lmScales[i].B || si->lmScales[i].A != lmScales[i].A ) { si->lmScales[i] = lmScales[i]; hasChanged = true; } } if(hasChanged) { glUniform4fv(si->uniLmScales, MAX_LIGHTMAPS_PER_SURFACE, si->lmScales[0].Elements); } } static void RenderBrushPoly(msurface_t *fa) { int map; gl3image_t *image; c_brush_polys++; image = TextureAnimation(fa->texinfo); if (fa->flags & SURF_DRAWTURB) { GL3_Bind(image->texnum); GL3_EmitWaterPolys(fa); return; } else { GL3_Bind(image->texnum); } hmm_vec4 lmScales[MAX_LIGHTMAPS_PER_SURFACE] = {0}; lmScales[0] = HMM_Vec4(1.0f, 1.0f, 1.0f, 1.0f); GL3_BindLightmap(fa->lightmaptexturenum); // Any dynamic lights on this surface? for (map = 0; map < MAX_LIGHTMAPS_PER_SURFACE && fa->styles[map] != 255; map++) { lmScales[map].R = gl3_newrefdef.lightstyles[fa->styles[map]].rgb[0]; lmScales[map].G = gl3_newrefdef.lightstyles[fa->styles[map]].rgb[1]; lmScales[map].B = gl3_newrefdef.lightstyles[fa->styles[map]].rgb[2]; lmScales[map].A = 1.0f; } if (fa->texinfo->flags & SURF_FLOWING) { GL3_UseProgram(gl3state.si3DlmFlow.shaderProgram); UpdateLMscales(lmScales, &gl3state.si3DlmFlow); GL3_DrawGLFlowingPoly(fa); } else { GL3_UseProgram(gl3state.si3Dlm.shaderProgram); UpdateLMscales(lmScales, &gl3state.si3Dlm); GL3_DrawGLPoly(fa); } // Note: lightmap chains are gone, lightmaps are rendered together with normal texture in one pass } /* * 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 GL3_DrawAlphaSurfaces(void) { msurface_t *s; /* go back to the world matrix */ gl3state.uni3DData.transModelMat4 = gl3_identityMat4; GL3_UpdateUBO3D(); glEnable(GL_BLEND); for (s = gl3_alpha_surfaces; s != NULL; s = s->texturechain) { GL3_Bind(s->texinfo->image->texnum); c_brush_polys++; float alpha = 1.0f; if (s->texinfo->flags & SURF_TRANS33) { alpha = 0.333f; } else if (s->texinfo->flags & SURF_TRANS66) { alpha = 0.666f; } if(alpha != gl3state.uni3DData.alpha) { gl3state.uni3DData.alpha = alpha; GL3_UpdateUBO3D(); } if (s->flags & SURF_DRAWTURB) { GL3_EmitWaterPolys(s); } else if (s->texinfo->flags & SURF_FLOWING) { GL3_UseProgram(gl3state.si3DtransFlow.shaderProgram); GL3_DrawGLFlowingPoly(s); } else { GL3_UseProgram(gl3state.si3Dtrans.shaderProgram); GL3_DrawGLPoly(s); } } gl3state.uni3DData.alpha = 1.0f; GL3_UpdateUBO3D(); glDisable(GL_BLEND); gl3_alpha_surfaces = NULL; } static void DrawTextureChains(void) { int i; msurface_t *s; gl3image_t *image; c_visible_textures = 0; for (i = 0, image = gl3textures; i < numgl3textures; i++, image++) { if (!image->registration_sequence) { continue; } s = image->texturechain; if (!s) { continue; } c_visible_textures++; for ( ; s; s = s->texturechain) { SetLightFlags(s); RenderBrushPoly(s); } image->texturechain = NULL; } // TODO: maybe one loop for normal faces and one for SURF_DRAWTURB ??? } static void RenderLightmappedPoly(msurface_t *surf) { int map; gl3image_t *image = TextureAnimation(surf->texinfo); hmm_vec4 lmScales[MAX_LIGHTMAPS_PER_SURFACE] = {0}; lmScales[0] = HMM_Vec4(1.0f, 1.0f, 1.0f, 1.0f); assert((surf->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP)) == 0 && "RenderLightMappedPoly mustn't be called with transparent, sky or warping surfaces!"); // Any dynamic lights on this surface? for (map = 0; map < MAX_LIGHTMAPS_PER_SURFACE && surf->styles[map] != 255; map++) { lmScales[map].R = gl3_newrefdef.lightstyles[surf->styles[map]].rgb[0]; lmScales[map].G = gl3_newrefdef.lightstyles[surf->styles[map]].rgb[1]; lmScales[map].B = gl3_newrefdef.lightstyles[surf->styles[map]].rgb[2]; lmScales[map].A = 1.0f; } c_brush_polys++; GL3_Bind(image->texnum); GL3_BindLightmap(surf->lightmaptexturenum); if (surf->texinfo->flags & SURF_FLOWING) { GL3_UseProgram(gl3state.si3DlmFlow.shaderProgram); UpdateLMscales(lmScales, &gl3state.si3DlmFlow); GL3_DrawGLFlowingPoly(surf); } else { GL3_UseProgram(gl3state.si3Dlm.shaderProgram); UpdateLMscales(lmScales, &gl3state.si3Dlm); GL3_DrawGLPoly(surf); } } static void DrawInlineBModel(void) { int i, k; cplane_t *pplane; float dot; msurface_t *psurf; dlight_t *lt; /* calculate dynamic lighting for bmodel */ lt = gl3_newrefdef.dlights; for (k = 0; k < gl3_newrefdef.num_dlights; k++, lt++) { GL3_MarkLights(lt, 1 << k, currentmodel->nodes + currentmodel->firstnode); } psurf = ¤tmodel->surfaces[currentmodel->firstmodelsurface]; if (currententity->flags & RF_TRANSLUCENT) { glEnable(GL_BLEND); /* TODO: should I care about the 0.25 part? we'll just set alpha to 0.33 or 0.66 depending on surface flag.. 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 = gl3_alpha_surfaces; gl3_alpha_surfaces = psurf; } else if(!(psurf->flags & SURF_DRAWTURB)) { SetAllLightFlags(psurf); RenderLightmappedPoly(psurf); } else { RenderBrushPoly(psurf); } } } if (currententity->flags & RF_TRANSLUCENT) { glDisable(GL_BLEND); } } void GL3_DrawBrushModel(entity_t *e) { vec3_t mins, maxs; int i; qboolean rotated; if (currentmodel->nummodelsurfaces == 0) { return; } currententity = e; gl3state.currenttexture = -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 (CullBox(mins, maxs)) { return; } if (gl_zfix->value) { glEnable(GL_POLYGON_OFFSET_FILL); } VectorSubtract(gl3_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(); hmm_mat4 oldMat = gl3state.uni3DData.transModelMat4; e->angles[0] = -e->angles[0]; e->angles[2] = -e->angles[2]; GL3_RotateForEntity(e); e->angles[0] = -e->angles[0]; e->angles[2] = -e->angles[2]; DrawInlineBModel(); // glPopMatrix(); gl3state.uni3DData.transModelMat4 = oldMat; GL3_UpdateUBO3D(); if (gl_zfix->value) { glDisable(GL_POLYGON_OFFSET_FILL); } } static void RecursiveWorldNode(mnode_t *node) { int c, side, sidebit; cplane_t *plane; msurface_t *surf, **mark; mleaf_t *pleaf; float dot; gl3image_t *image; if (node->contents == CONTENTS_SOLID) { return; /* solid */ } if (node->visframe != gl3_visframecount) { return; } if (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 (gl3_newrefdef.areabits) { if (!(gl3_newrefdef.areabits[pleaf->area >> 3] & (1 << (pleaf->area & 7)))) { return; /* not visible */ } } mark = pleaf->firstmarksurface; c = pleaf->nummarksurfaces; if (c) { do { (*mark)->visframe = gl3_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 */ RecursiveWorldNode(node->children[side]); /* draw stuff */ for (c = node->numsurfaces, surf = gl3_worldmodel->surfaces + node->firstsurface; c; c--, surf++) { if (surf->visframe != gl3_framecount) { continue; } if ((surf->flags & SURF_PLANEBACK) != sidebit) { continue; /* wrong side */ } if (surf->texinfo->flags & SURF_SKY) { /* just adds to visible sky bounds */ GL3_AddSkySurface(surf); } else if (surf->texinfo->flags & (SURF_TRANS33 | SURF_TRANS66)) { /* add to the translucent chain */ surf->texturechain = gl3_alpha_surfaces; gl3_alpha_surfaces = surf; gl3_alpha_surfaces->texinfo->image = TextureAnimation(surf->texinfo); } else { // calling RenderLightmappedPoly() here probably isn't optimal, rendering everything // through texturechains should be faster, because far less glBindTexture() is needed // (and it might allow batching the drawcalls of surfaces with the same texture) #if 0 if(!(surf->flags & SURF_DRAWTURB)) { RenderLightmappedPoly(surf); } else #endif // 0 { /* the polygon is visible, so add it to the texture sorted chain */ image = TextureAnimation(surf->texinfo); surf->texturechain = image->texturechain; image->texturechain = surf; } } } /* recurse down the back side */ RecursiveWorldNode(node->children[!side]); } void GL3_DrawWorld(void) { entity_t ent; if (!gl_drawworld->value) { return; } if (gl3_newrefdef.rdflags & RDF_NOWORLDMODEL) { return; } currentmodel = gl3_worldmodel; VectorCopy(gl3_newrefdef.vieworg, modelorg); /* auto cycle the world frame for texture animation */ memset(&ent, 0, sizeof(ent)); ent.frame = (int)(gl3_newrefdef.time * 2); currententity = &ent; gl3state.currenttexture = -1; GL3_ClearSkyBox(); RecursiveWorldNode(gl3_worldmodel->nodes); DrawTextureChains(); GL3_DrawSkyBox(); DrawTriangleOutlines(); currententity = NULL; } /* * Mark the leaves and nodes that are * in the PVS for the current cluster */ void GL3_MarkLeaves(void) { byte *vis; byte fatvis[MAX_MAP_LEAFS / 8]; mnode_t *node; int i, c; mleaf_t *leaf; int cluster; if ((gl3_oldviewcluster == gl3_viewcluster) && (gl3_oldviewcluster2 == gl3_viewcluster2) && !gl_novis->value && (gl3_viewcluster != -1)) { return; } /* development aid to let you run around and see exactly where the pvs ends */ if (gl_lockpvs->value) { return; } gl3_visframecount++; gl3_oldviewcluster = gl3_viewcluster; gl3_oldviewcluster2 = gl3_viewcluster2; if (gl_novis->value || (gl3_viewcluster == -1) || !gl3_worldmodel->vis) { /* mark everything */ for (i = 0; i < gl3_worldmodel->numleafs; i++) { gl3_worldmodel->leafs[i].visframe = gl3_visframecount; } for (i = 0; i < gl3_worldmodel->numnodes; i++) { gl3_worldmodel->nodes[i].visframe = gl3_visframecount; } return; } vis = GL3_Mod_ClusterPVS(gl3_viewcluster, gl3_worldmodel); /* may have to combine two clusters because of solid water boundaries */ if (gl3_viewcluster2 != gl3_viewcluster) { memcpy(fatvis, vis, (gl3_worldmodel->numleafs + 7) / 8); vis = GL3_Mod_ClusterPVS(gl3_viewcluster2, gl3_worldmodel); c = (gl3_worldmodel->numleafs + 31) / 32; for (i = 0; i < c; i++) { ((int *)fatvis)[i] |= ((int *)vis)[i]; } vis = fatvis; } for (i = 0, leaf = gl3_worldmodel->leafs; i < gl3_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 == gl3_visframecount) { break; } node->visframe = gl3_visframecount; node = node->parent; } while (node); } } } yquake2-QUAKE2_7_10/src/client/refresh/gl3/gl3_warp.c000066400000000000000000000322631321245476300222020ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2016-2017 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. * * ======================================================================= * * Warps. Used on water surfaces und for skybox rotation. * * ======================================================================= */ #include "header/local.h" static 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; } } } } static const float SUBDIVIDE_SIZE = 64.0f; static void R_SubdividePolygon(int numverts, float *verts, msurface_t *warpface) { 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; vec3_t normal; VectorCopy(warpface->plane->normal, normal); if (numverts > 60) { ri.Sys_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], warpface); R_SubdividePolygon(b, back[0], warpface); return; } /* add a point in the center to help keep warp valid */ poly = Hunk_Alloc(sizeof(glpoly_t) + ((numverts - 4) + 2) * sizeof(gl3_3D_vtx_t)); 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->vertices[i + 1].pos); 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->vertices[i + 1].texCoord[0] = s; poly->vertices[i + 1].texCoord[1] = t; VectorCopy(normal, poly->vertices[i + 1].normal); poly->vertices[i + 1].lightFlags = 0; } VectorScale(total, (1.0 / numverts), poly->vertices[0].pos); poly->vertices[0].texCoord[0] = total_s / numverts; poly->vertices[0].texCoord[1] = total_t / numverts; VectorCopy(normal, poly->vertices[0].normal); /* copy first vertex to last */ //memcpy(poly->vertices[i + 1], poly->vertices[1], sizeof(poly->vertices[0])); poly->vertices[i + 1] = poly->vertices[1]; } /* * Breaks a polygon up along axial 64 unit * boundaries so that turbulent and sky warps * can be done reasonably. */ void GL3_SubdivideSurface(msurface_t *fa, gl3model_t* loadmodel) { vec3_t verts[64]; int numverts; int i; int lindex; float *vec; /* 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], fa); } /* * Does a water warp on the pre-fragmented glpoly_t chain */ void GL3_EmitWaterPolys(msurface_t *fa) { glpoly_t *bp; float scroll = 0.0f; if (fa->texinfo->flags & SURF_FLOWING) { scroll = -64.0f * ((gl3_newrefdef.time * 0.5) - (int)(gl3_newrefdef.time * 0.5)); if (scroll == 0.0f) // this is done in GL3_DrawGLFlowingPoly() TODO: keep? { scroll = -64.0f; } } if(gl3state.uni3DData.scroll != scroll) { gl3state.uni3DData.scroll = scroll; GL3_UpdateUBO3D(); } GL3_UseProgram(gl3state.si3Dturb.shaderProgram); GL3_BindVAO(gl3state.vao3D); GL3_BindVBO(gl3state.vbo3D); for (bp = fa->polys; bp != NULL; bp = bp->next) { int numverts = bp->numverts; glBufferData(GL_ARRAY_BUFFER, sizeof(gl3_3D_vtx_t)*numverts, bp->vertices, GL_STREAM_DRAW); glDrawArrays(GL_TRIANGLE_FAN, 0, numverts); } } // ########### below: Sky-specific stuff ########## #define ON_EPSILON 0.1 /* point on plane side epsilon */ enum { MAX_CLIP_VERTS = 64 }; static const int skytexorder[6] = {0, 2, 1, 3, 4, 5}; static float skymins[2][6], skymaxs[2][6]; static float sky_min, sky_max; static float skyrotate; static vec3_t skyaxis; static gl3image_t* sky_images[6]; /* 3dstudio environment map names */ static const char* suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; 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} }; void GL3_SetSky(char *name, float rotate, vec3_t axis) { int i; char pathname[MAX_QPATH]; char skyname[MAX_QPATH]; Q_strlcpy(skyname, name, sizeof(skyname)); skyrotate = rotate; VectorCopy(axis, skyaxis); for (i = 0; i < 6; i++) { // NOTE: there might be a paletted .pcx version, which was only used // if gl_config.palettedtexture so it *shouldn't* be relevant for he GL3 renderer Com_sprintf(pathname, sizeof(pathname), "env/%s%s.tga", skyname, suf[i]); sky_images[i] = GL3_FindImage(pathname, it_sky); if (sky_images[i] == NULL) { sky_images[i] = gl3_notexture; } sky_min = 1.0 / 512; sky_max = 511.0 / 512; } } static void 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; } } } static void ClipSkyPolygon(int nump, vec3_t vecs, int stage) { float *norm; float *v; qboolean front, back; float d, e; float dists[MAX_CLIP_VERTS]; int sides[MAX_CLIP_VERTS]; vec3_t newv[2][MAX_CLIP_VERTS]; int newc[2]; int i, j; if (nump > MAX_CLIP_VERTS - 2) { ri.Sys_Error(ERR_DROP, "R_ClipSkyPolygon: MAX_CLIP_VERTS"); } if (stage == 6) { /* fully clipped, so draw it */ 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 */ 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 */ ClipSkyPolygon(newc[0], newv[0][0], stage + 1); ClipSkyPolygon(newc[1], newv[1][0], stage + 1); } void GL3_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->vertices[i].pos, gl3_origin, verts[i]); } ClipSkyPolygon(p->numverts, verts[0], 0); } } void GL3_ClearSkyBox(void) { int i; for (i = 0; i < 6; i++) { skymins[0][i] = skymins[1][i] = 9999; skymaxs[0][i] = skymaxs[1][i] = -9999; } } static void MakeSkyVec(float s, float t, int axis, gl3_3D_vtx_t* vert) { vec3_t v, b; int j, k; float dist = (gl_farsee->value == 0) ? 2300.0f : 4096.0f; b[0] = s * dist; b[1] = t * dist; b[2] = dist; 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; VectorCopy(v, vert->pos); vert->texCoord[0] = s; vert->texCoord[1] = t; vert->lmTexCoord[0] = vert->lmTexCoord[1] = 0.0f; } void GL3_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(); hmm_mat4 origModelMat = gl3state.uni3DData.transModelMat4; // glTranslatef(gl3_origin[0], gl3_origin[1], gl3_origin[2]); hmm_vec3 transl = HMM_Vec3(gl3_origin[0], gl3_origin[1], gl3_origin[2]); hmm_mat4 modMVmat = HMM_MultiplyMat4(origModelMat, HMM_Translate(transl)); if(skyrotate != 0.0f) { // glRotatef(gl3_newrefdef.time * skyrotate, skyaxis[0], skyaxis[1], skyaxis[2]); hmm_vec3 rotAxis = HMM_Vec3(skyaxis[0], skyaxis[1], skyaxis[2]); modMVmat = HMM_MultiplyMat4(modMVmat, HMM_Rotate(gl3_newrefdef.time * skyrotate, rotAxis)); } gl3state.uni3DData.transModelMat4 = modMVmat; GL3_UpdateUBO3D(); GL3_UseProgram(gl3state.si3Dsky.shaderProgram); GL3_BindVAO(gl3state.vao3D); GL3_BindVBO(gl3state.vbo3D); // TODO: this could all be done in one drawcall.. but.. whatever, it's <= 6 drawcalls/frame gl3_3D_vtx_t skyVertices[4]; for (i = 0; i < 6; i++) { if (skyrotate != 0.0f) { 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; } GL3_Bind(sky_images[skytexorder[i]]->texnum); MakeSkyVec( skymins [ 0 ] [ i ], skymins [ 1 ] [ i ], i, &skyVertices[0] ); MakeSkyVec( skymins [ 0 ] [ i ], skymaxs [ 1 ] [ i ], i, &skyVertices[1] ); MakeSkyVec( skymaxs [ 0 ] [ i ], skymaxs [ 1 ] [ i ], i, &skyVertices[2] ); MakeSkyVec( skymaxs [ 0 ] [ i ], skymins [ 1 ] [ i ], i, &skyVertices[3] ); glBufferData(GL_ARRAY_BUFFER, sizeof(skyVertices), skyVertices, GL_STREAM_DRAW); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } // glPopMatrix(); gl3state.uni3DData.transModelMat4 = origModelMat; GL3_UpdateUBO3D(); } yquake2-QUAKE2_7_10/src/client/refresh/gl3/glad/000077500000000000000000000000001321245476300212215ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/refresh/gl3/glad/include/000077500000000000000000000000001321245476300226445ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/refresh/gl3/glad/include/KHR/000077500000000000000000000000001321245476300232705ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/refresh/gl3/glad/include/KHR/khrplatform.h000066400000000000000000000236521321245476300260020ustar00rootroot00000000000000#ifndef __khrplatform_h_ #define __khrplatform_h_ /* ** Copyright (c) 2008-2009 The Khronos Group Inc. ** ** Permission is hereby granted, free of charge, to any person obtaining a ** copy of this software and/or associated documentation files (the ** "Materials"), to deal in the Materials without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Materials, and to ** permit persons to whom the Materials are furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be included ** in all copies or substantial portions of the Materials. ** ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. */ /* Khronos platform-specific types and definitions. * * $Revision: 32517 $ on $Date: 2016-03-11 02:41:19 -0800 (Fri, 11 Mar 2016) $ * * Adopters may modify this file to suit their platform. Adopters are * encouraged to submit platform specific modifications to the Khronos * group so that they can be included in future versions of this file. * Please submit changes by sending them to the public Khronos Bugzilla * (http://khronos.org/bugzilla) by filing a bug against product * "Khronos (general)" component "Registry". * * A predefined template which fills in some of the bug fields can be * reached using http://tinyurl.com/khrplatform-h-bugreport, but you * must create a Bugzilla login first. * * * See the Implementer's Guidelines for information about where this file * should be located on your system and for more details of its use: * http://www.khronos.org/registry/implementers_guide.pdf * * This file should be included as * #include * by Khronos client API header files that use its types and defines. * * The types in khrplatform.h should only be used to define API-specific types. * * Types defined in khrplatform.h: * khronos_int8_t signed 8 bit * khronos_uint8_t unsigned 8 bit * khronos_int16_t signed 16 bit * khronos_uint16_t unsigned 16 bit * khronos_int32_t signed 32 bit * khronos_uint32_t unsigned 32 bit * khronos_int64_t signed 64 bit * khronos_uint64_t unsigned 64 bit * khronos_intptr_t signed same number of bits as a pointer * khronos_uintptr_t unsigned same number of bits as a pointer * khronos_ssize_t signed size * khronos_usize_t unsigned size * khronos_float_t signed 32 bit floating point * khronos_time_ns_t unsigned 64 bit time in nanoseconds * khronos_utime_nanoseconds_t unsigned time interval or absolute time in * nanoseconds * khronos_stime_nanoseconds_t signed time interval in nanoseconds * khronos_boolean_enum_t enumerated boolean type. This should * only be used as a base type when a client API's boolean type is * an enum. Client APIs which use an integer or other type for * booleans cannot use this as the base type for their boolean. * * Tokens defined in khrplatform.h: * * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. * * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. * * Calling convention macros defined in this file: * KHRONOS_APICALL * KHRONOS_APIENTRY * KHRONOS_APIATTRIBUTES * * These may be used in function prototypes as: * * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( * int arg1, * int arg2) KHRONOS_APIATTRIBUTES; */ /*------------------------------------------------------------------------- * Definition of KHRONOS_APICALL *------------------------------------------------------------------------- * This precedes the return type of the function in the function prototype. */ #if defined(_WIN32) && !defined(__SCITECH_SNAP__) # define KHRONOS_APICALL __declspec(dllimport) #elif defined (__SYMBIAN32__) # define KHRONOS_APICALL IMPORT_C #elif defined(__ANDROID__) # include # define KHRONOS_APICALL __attribute__((visibility("default"))) __NDK_FPABI__ #else # define KHRONOS_APICALL #endif /*------------------------------------------------------------------------- * Definition of KHRONOS_APIENTRY *------------------------------------------------------------------------- * This follows the return type of the function and precedes the function * name in the function prototype. */ #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) /* Win32 but not WinCE */ # define KHRONOS_APIENTRY __stdcall #else # define KHRONOS_APIENTRY #endif /*------------------------------------------------------------------------- * Definition of KHRONOS_APIATTRIBUTES *------------------------------------------------------------------------- * This follows the closing parenthesis of the function prototype arguments. */ #if defined (__ARMCC_2__) #define KHRONOS_APIATTRIBUTES __softfp #else #define KHRONOS_APIATTRIBUTES #endif /*------------------------------------------------------------------------- * basic type definitions *-----------------------------------------------------------------------*/ #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) /* * Using */ #include typedef int32_t khronos_int32_t; typedef uint32_t khronos_uint32_t; typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif defined(__VMS ) || defined(__sgi) /* * Using */ #include typedef int32_t khronos_int32_t; typedef uint32_t khronos_uint32_t; typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif defined(_WIN32) && !defined(__SCITECH_SNAP__) /* * Win32 */ typedef __int32 khronos_int32_t; typedef unsigned __int32 khronos_uint32_t; typedef __int64 khronos_int64_t; typedef unsigned __int64 khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif defined(__sun__) || defined(__digital__) /* * Sun or Digital */ typedef int khronos_int32_t; typedef unsigned int khronos_uint32_t; #if defined(__arch64__) || defined(_LP64) typedef long int khronos_int64_t; typedef unsigned long int khronos_uint64_t; #else typedef long long int khronos_int64_t; typedef unsigned long long int khronos_uint64_t; #endif /* __arch64__ */ #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif 0 /* * Hypothetical platform with no float or int64 support */ typedef int khronos_int32_t; typedef unsigned int khronos_uint32_t; #define KHRONOS_SUPPORT_INT64 0 #define KHRONOS_SUPPORT_FLOAT 0 #else /* * Generic fallback */ #include typedef int32_t khronos_int32_t; typedef uint32_t khronos_uint32_t; typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #endif /* * Types that are (so far) the same on all platforms */ typedef signed char khronos_int8_t; typedef unsigned char khronos_uint8_t; typedef signed short int khronos_int16_t; typedef unsigned short int khronos_uint16_t; /* * Types that differ between LLP64 and LP64 architectures - in LLP64, * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears * to be the only LLP64 architecture in current use. */ #ifdef _WIN64 typedef signed long long int khronos_intptr_t; typedef unsigned long long int khronos_uintptr_t; typedef signed long long int khronos_ssize_t; typedef unsigned long long int khronos_usize_t; #else typedef signed long int khronos_intptr_t; typedef unsigned long int khronos_uintptr_t; typedef signed long int khronos_ssize_t; typedef unsigned long int khronos_usize_t; #endif #if KHRONOS_SUPPORT_FLOAT /* * Float type */ typedef float khronos_float_t; #endif #if KHRONOS_SUPPORT_INT64 /* Time types * * These types can be used to represent a time interval in nanoseconds or * an absolute Unadjusted System Time. Unadjusted System Time is the number * of nanoseconds since some arbitrary system event (e.g. since the last * time the system booted). The Unadjusted System Time is an unsigned * 64 bit value that wraps back to 0 every 584 years. Time intervals * may be either signed or unsigned. */ typedef khronos_uint64_t khronos_utime_nanoseconds_t; typedef khronos_int64_t khronos_stime_nanoseconds_t; #endif /* * Dummy value used to pad enum types to 32 bits. */ #ifndef KHRONOS_MAX_ENUM #define KHRONOS_MAX_ENUM 0x7FFFFFFF #endif /* * Enumerated boolean type * * Values other than zero should be considered to be true. Therefore * comparisons should not be made against KHRONOS_TRUE. */ typedef enum { KHRONOS_FALSE = 0, KHRONOS_TRUE = 1, KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM } khronos_boolean_enum_t; #endif /* __khrplatform_h_ */ yquake2-QUAKE2_7_10/src/client/refresh/gl3/glad/include/glad/000077500000000000000000000000001321245476300235535ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/refresh/gl3/glad/include/glad/glad.h000066400000000000000000003034541321245476300246440ustar00rootroot00000000000000/* OpenGL loader generated by glad 0.1.12a0 on Wed Dec 21 00:44:13 2016. Language/Generator: C/C++ Specification: gl APIs: gl=3.2 Profile: core Extensions: GL_ARB_debug_output, GL_EXT_texture_filter_anisotropic Loader: False Local files: False Omit khrplatform: False Commandline: --profile="core" --api="gl=3.2" --generator="c" --spec="gl" --no-loader --extensions="GL_ARB_debug_output,GL_EXT_texture_filter_anisotropic" Online: http://glad.dav1d.de/#profile=core&language=c&specification=gl&api=gl%3D3.2&extensions=GL_ARB_debug_output&extensions=GL_EXT_texture_filter_anisotropic */ #ifndef __glad_h_ #define __glad_h_ #ifdef __gl_h_ #error OpenGL header already included, remove this include, glad already provides it #endif #define __gl_h_ #if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 #endif #include #endif #ifndef APIENTRY #define APIENTRY #endif #ifndef APIENTRYP #define APIENTRYP APIENTRY * #endif #ifdef __cplusplus extern "C" { #endif struct gladGLversionStruct { int major; int minor; }; typedef void* (* GLADloadproc)(const char *name); #ifndef GLAPI # if defined(GLAD_GLAPI_EXPORT) # if defined(WIN32) || defined(__CYGWIN__) # if defined(GLAD_GLAPI_EXPORT_BUILD) # if defined(__GNUC__) # define GLAPI __attribute__ ((dllexport)) extern # else # define GLAPI __declspec(dllexport) extern # endif # else # if defined(__GNUC__) # define GLAPI __attribute__ ((dllimport)) extern # else # define GLAPI __declspec(dllimport) extern # endif # endif # elif defined(__GNUC__) && defined(GLAD_GLAPI_EXPORT_BUILD) # define GLAPI __attribute__ ((visibility ("default"))) extern # else # define GLAPI extern # endif # else # define GLAPI extern # endif #endif GLAPI struct gladGLversionStruct GLVersion; GLAPI int gladLoadGLLoader(GLADloadproc); #include #include #ifndef GLEXT_64_TYPES_DEFINED /* This code block is duplicated in glxext.h, so must be protected */ #define GLEXT_64_TYPES_DEFINED /* Define int32_t, int64_t, and uint64_t types for UST/MSC */ /* (as used in the GL_EXT_timer_query extension). */ #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #include #elif defined(__sun__) || defined(__digital__) #include #if defined(__STDC__) #if defined(__arch64__) || defined(_LP64) typedef long int int64_t; typedef unsigned long int uint64_t; #else typedef long long int int64_t; typedef unsigned long long int uint64_t; #endif /* __arch64__ */ #endif /* __STDC__ */ #elif defined( __VMS ) || defined(__sgi) #include #elif defined(__SCO__) || defined(__USLC__) #include #elif defined(__UNIXOS2__) || defined(__SOL64__) typedef long int int32_t; typedef long long int int64_t; typedef unsigned long long int uint64_t; #elif defined(_WIN32) && defined(__GNUC__) #include #elif defined(_WIN32) typedef __int32 int32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #else /* Fallback if nothing above works */ #include #endif #endif typedef unsigned int GLenum; typedef unsigned char GLboolean; typedef unsigned int GLbitfield; typedef void GLvoid; typedef signed char GLbyte; typedef short GLshort; typedef int GLint; typedef int GLclampx; typedef unsigned char GLubyte; typedef unsigned short GLushort; typedef unsigned int GLuint; typedef int GLsizei; typedef float GLfloat; typedef float GLclampf; typedef double GLdouble; typedef double GLclampd; typedef void *GLeglImageOES; typedef char GLchar; typedef char GLcharARB; #ifdef __APPLE__ typedef void *GLhandleARB; #else typedef unsigned int GLhandleARB; #endif typedef unsigned short GLhalfARB; typedef unsigned short GLhalf; typedef GLint GLfixed; typedef ptrdiff_t GLintptr; typedef ptrdiff_t GLsizeiptr; typedef int64_t GLint64; typedef uint64_t GLuint64; typedef ptrdiff_t GLintptrARB; typedef ptrdiff_t GLsizeiptrARB; typedef int64_t GLint64EXT; typedef uint64_t GLuint64EXT; typedef struct __GLsync *GLsync; struct _cl_context; struct _cl_event; typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); typedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); typedef unsigned short GLhalfNV; typedef GLintptr GLvdpauSurfaceNV; #define GL_DEPTH_BUFFER_BIT 0x00000100 #define GL_STENCIL_BUFFER_BIT 0x00000400 #define GL_COLOR_BUFFER_BIT 0x00004000 #define GL_FALSE 0 #define GL_TRUE 1 #define GL_POINTS 0x0000 #define GL_LINES 0x0001 #define GL_LINE_LOOP 0x0002 #define GL_LINE_STRIP 0x0003 #define GL_TRIANGLES 0x0004 #define GL_TRIANGLE_STRIP 0x0005 #define GL_TRIANGLE_FAN 0x0006 #define GL_NEVER 0x0200 #define GL_LESS 0x0201 #define GL_EQUAL 0x0202 #define GL_LEQUAL 0x0203 #define GL_GREATER 0x0204 #define GL_NOTEQUAL 0x0205 #define GL_GEQUAL 0x0206 #define GL_ALWAYS 0x0207 #define GL_ZERO 0 #define GL_ONE 1 #define GL_SRC_COLOR 0x0300 #define GL_ONE_MINUS_SRC_COLOR 0x0301 #define GL_SRC_ALPHA 0x0302 #define GL_ONE_MINUS_SRC_ALPHA 0x0303 #define GL_DST_ALPHA 0x0304 #define GL_ONE_MINUS_DST_ALPHA 0x0305 #define GL_DST_COLOR 0x0306 #define GL_ONE_MINUS_DST_COLOR 0x0307 #define GL_SRC_ALPHA_SATURATE 0x0308 #define GL_NONE 0 #define GL_FRONT_LEFT 0x0400 #define GL_FRONT_RIGHT 0x0401 #define GL_BACK_LEFT 0x0402 #define GL_BACK_RIGHT 0x0403 #define GL_FRONT 0x0404 #define GL_BACK 0x0405 #define GL_LEFT 0x0406 #define GL_RIGHT 0x0407 #define GL_FRONT_AND_BACK 0x0408 #define GL_NO_ERROR 0 #define GL_INVALID_ENUM 0x0500 #define GL_INVALID_VALUE 0x0501 #define GL_INVALID_OPERATION 0x0502 #define GL_OUT_OF_MEMORY 0x0505 #define GL_CW 0x0900 #define GL_CCW 0x0901 #define GL_POINT_SIZE 0x0B11 #define GL_POINT_SIZE_RANGE 0x0B12 #define GL_POINT_SIZE_GRANULARITY 0x0B13 #define GL_LINE_SMOOTH 0x0B20 #define GL_LINE_WIDTH 0x0B21 #define GL_LINE_WIDTH_RANGE 0x0B22 #define GL_LINE_WIDTH_GRANULARITY 0x0B23 #define GL_POLYGON_MODE 0x0B40 #define GL_POLYGON_SMOOTH 0x0B41 #define GL_CULL_FACE 0x0B44 #define GL_CULL_FACE_MODE 0x0B45 #define GL_FRONT_FACE 0x0B46 #define GL_DEPTH_RANGE 0x0B70 #define GL_DEPTH_TEST 0x0B71 #define GL_DEPTH_WRITEMASK 0x0B72 #define GL_DEPTH_CLEAR_VALUE 0x0B73 #define GL_DEPTH_FUNC 0x0B74 #define GL_STENCIL_TEST 0x0B90 #define GL_STENCIL_CLEAR_VALUE 0x0B91 #define GL_STENCIL_FUNC 0x0B92 #define GL_STENCIL_VALUE_MASK 0x0B93 #define GL_STENCIL_FAIL 0x0B94 #define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 #define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 #define GL_STENCIL_REF 0x0B97 #define GL_STENCIL_WRITEMASK 0x0B98 #define GL_VIEWPORT 0x0BA2 #define GL_DITHER 0x0BD0 #define GL_BLEND_DST 0x0BE0 #define GL_BLEND_SRC 0x0BE1 #define GL_BLEND 0x0BE2 #define GL_LOGIC_OP_MODE 0x0BF0 #define GL_COLOR_LOGIC_OP 0x0BF2 #define GL_DRAW_BUFFER 0x0C01 #define GL_READ_BUFFER 0x0C02 #define GL_SCISSOR_BOX 0x0C10 #define GL_SCISSOR_TEST 0x0C11 #define GL_COLOR_CLEAR_VALUE 0x0C22 #define GL_COLOR_WRITEMASK 0x0C23 #define GL_DOUBLEBUFFER 0x0C32 #define GL_STEREO 0x0C33 #define GL_LINE_SMOOTH_HINT 0x0C52 #define GL_POLYGON_SMOOTH_HINT 0x0C53 #define GL_UNPACK_SWAP_BYTES 0x0CF0 #define GL_UNPACK_LSB_FIRST 0x0CF1 #define GL_UNPACK_ROW_LENGTH 0x0CF2 #define GL_UNPACK_SKIP_ROWS 0x0CF3 #define GL_UNPACK_SKIP_PIXELS 0x0CF4 #define GL_UNPACK_ALIGNMENT 0x0CF5 #define GL_PACK_SWAP_BYTES 0x0D00 #define GL_PACK_LSB_FIRST 0x0D01 #define GL_PACK_ROW_LENGTH 0x0D02 #define GL_PACK_SKIP_ROWS 0x0D03 #define GL_PACK_SKIP_PIXELS 0x0D04 #define GL_PACK_ALIGNMENT 0x0D05 #define GL_MAX_TEXTURE_SIZE 0x0D33 #define GL_MAX_VIEWPORT_DIMS 0x0D3A #define GL_SUBPIXEL_BITS 0x0D50 #define GL_TEXTURE_1D 0x0DE0 #define GL_TEXTURE_2D 0x0DE1 #define GL_POLYGON_OFFSET_UNITS 0x2A00 #define GL_POLYGON_OFFSET_POINT 0x2A01 #define GL_POLYGON_OFFSET_LINE 0x2A02 #define GL_POLYGON_OFFSET_FILL 0x8037 #define GL_POLYGON_OFFSET_FACTOR 0x8038 #define GL_TEXTURE_BINDING_1D 0x8068 #define GL_TEXTURE_BINDING_2D 0x8069 #define GL_TEXTURE_WIDTH 0x1000 #define GL_TEXTURE_HEIGHT 0x1001 #define GL_TEXTURE_INTERNAL_FORMAT 0x1003 #define GL_TEXTURE_BORDER_COLOR 0x1004 #define GL_TEXTURE_RED_SIZE 0x805C #define GL_TEXTURE_GREEN_SIZE 0x805D #define GL_TEXTURE_BLUE_SIZE 0x805E #define GL_TEXTURE_ALPHA_SIZE 0x805F #define GL_DONT_CARE 0x1100 #define GL_FASTEST 0x1101 #define GL_NICEST 0x1102 #define GL_BYTE 0x1400 #define GL_UNSIGNED_BYTE 0x1401 #define GL_SHORT 0x1402 #define GL_UNSIGNED_SHORT 0x1403 #define GL_INT 0x1404 #define GL_UNSIGNED_INT 0x1405 #define GL_FLOAT 0x1406 #define GL_DOUBLE 0x140A #define GL_CLEAR 0x1500 #define GL_AND 0x1501 #define GL_AND_REVERSE 0x1502 #define GL_COPY 0x1503 #define GL_AND_INVERTED 0x1504 #define GL_NOOP 0x1505 #define GL_XOR 0x1506 #define GL_OR 0x1507 #define GL_NOR 0x1508 #define GL_EQUIV 0x1509 #define GL_INVERT 0x150A #define GL_OR_REVERSE 0x150B #define GL_COPY_INVERTED 0x150C #define GL_OR_INVERTED 0x150D #define GL_NAND 0x150E #define GL_SET 0x150F #define GL_TEXTURE 0x1702 #define GL_COLOR 0x1800 #define GL_DEPTH 0x1801 #define GL_STENCIL 0x1802 #define GL_STENCIL_INDEX 0x1901 #define GL_DEPTH_COMPONENT 0x1902 #define GL_RED 0x1903 #define GL_GREEN 0x1904 #define GL_BLUE 0x1905 #define GL_ALPHA 0x1906 #define GL_RGB 0x1907 #define GL_RGBA 0x1908 #define GL_POINT 0x1B00 #define GL_LINE 0x1B01 #define GL_FILL 0x1B02 #define GL_KEEP 0x1E00 #define GL_REPLACE 0x1E01 #define GL_INCR 0x1E02 #define GL_DECR 0x1E03 #define GL_VENDOR 0x1F00 #define GL_RENDERER 0x1F01 #define GL_VERSION 0x1F02 #define GL_EXTENSIONS 0x1F03 #define GL_NEAREST 0x2600 #define GL_LINEAR 0x2601 #define GL_NEAREST_MIPMAP_NEAREST 0x2700 #define GL_LINEAR_MIPMAP_NEAREST 0x2701 #define GL_NEAREST_MIPMAP_LINEAR 0x2702 #define GL_LINEAR_MIPMAP_LINEAR 0x2703 #define GL_TEXTURE_MAG_FILTER 0x2800 #define GL_TEXTURE_MIN_FILTER 0x2801 #define GL_TEXTURE_WRAP_S 0x2802 #define GL_TEXTURE_WRAP_T 0x2803 #define GL_PROXY_TEXTURE_1D 0x8063 #define GL_PROXY_TEXTURE_2D 0x8064 #define GL_REPEAT 0x2901 #define GL_R3_G3_B2 0x2A10 #define GL_RGB4 0x804F #define GL_RGB5 0x8050 #define GL_RGB8 0x8051 #define GL_RGB10 0x8052 #define GL_RGB12 0x8053 #define GL_RGB16 0x8054 #define GL_RGBA2 0x8055 #define GL_RGBA4 0x8056 #define GL_RGB5_A1 0x8057 #define GL_RGBA8 0x8058 #define GL_RGB10_A2 0x8059 #define GL_RGBA12 0x805A #define GL_RGBA16 0x805B #define GL_UNSIGNED_BYTE_3_3_2 0x8032 #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 #define GL_UNSIGNED_INT_8_8_8_8 0x8035 #define GL_UNSIGNED_INT_10_10_10_2 0x8036 #define GL_TEXTURE_BINDING_3D 0x806A #define GL_PACK_SKIP_IMAGES 0x806B #define GL_PACK_IMAGE_HEIGHT 0x806C #define GL_UNPACK_SKIP_IMAGES 0x806D #define GL_UNPACK_IMAGE_HEIGHT 0x806E #define GL_TEXTURE_3D 0x806F #define GL_PROXY_TEXTURE_3D 0x8070 #define GL_TEXTURE_DEPTH 0x8071 #define GL_TEXTURE_WRAP_R 0x8072 #define GL_MAX_3D_TEXTURE_SIZE 0x8073 #define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 #define GL_UNSIGNED_SHORT_5_6_5 0x8363 #define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 #define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 #define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 #define GL_BGR 0x80E0 #define GL_BGRA 0x80E1 #define GL_MAX_ELEMENTS_VERTICES 0x80E8 #define GL_MAX_ELEMENTS_INDICES 0x80E9 #define GL_CLAMP_TO_EDGE 0x812F #define GL_TEXTURE_MIN_LOD 0x813A #define GL_TEXTURE_MAX_LOD 0x813B #define GL_TEXTURE_BASE_LEVEL 0x813C #define GL_TEXTURE_MAX_LEVEL 0x813D #define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 #define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 #define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 #define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 #define GL_ALIASED_LINE_WIDTH_RANGE 0x846E #define GL_TEXTURE0 0x84C0 #define GL_TEXTURE1 0x84C1 #define GL_TEXTURE2 0x84C2 #define GL_TEXTURE3 0x84C3 #define GL_TEXTURE4 0x84C4 #define GL_TEXTURE5 0x84C5 #define GL_TEXTURE6 0x84C6 #define GL_TEXTURE7 0x84C7 #define GL_TEXTURE8 0x84C8 #define GL_TEXTURE9 0x84C9 #define GL_TEXTURE10 0x84CA #define GL_TEXTURE11 0x84CB #define GL_TEXTURE12 0x84CC #define GL_TEXTURE13 0x84CD #define GL_TEXTURE14 0x84CE #define GL_TEXTURE15 0x84CF #define GL_TEXTURE16 0x84D0 #define GL_TEXTURE17 0x84D1 #define GL_TEXTURE18 0x84D2 #define GL_TEXTURE19 0x84D3 #define GL_TEXTURE20 0x84D4 #define GL_TEXTURE21 0x84D5 #define GL_TEXTURE22 0x84D6 #define GL_TEXTURE23 0x84D7 #define GL_TEXTURE24 0x84D8 #define GL_TEXTURE25 0x84D9 #define GL_TEXTURE26 0x84DA #define GL_TEXTURE27 0x84DB #define GL_TEXTURE28 0x84DC #define GL_TEXTURE29 0x84DD #define GL_TEXTURE30 0x84DE #define GL_TEXTURE31 0x84DF #define GL_ACTIVE_TEXTURE 0x84E0 #define GL_MULTISAMPLE 0x809D #define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E #define GL_SAMPLE_ALPHA_TO_ONE 0x809F #define GL_SAMPLE_COVERAGE 0x80A0 #define GL_SAMPLE_BUFFERS 0x80A8 #define GL_SAMPLES 0x80A9 #define GL_SAMPLE_COVERAGE_VALUE 0x80AA #define GL_SAMPLE_COVERAGE_INVERT 0x80AB #define GL_TEXTURE_CUBE_MAP 0x8513 #define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 #define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A #define GL_PROXY_TEXTURE_CUBE_MAP 0x851B #define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C #define GL_COMPRESSED_RGB 0x84ED #define GL_COMPRESSED_RGBA 0x84EE #define GL_TEXTURE_COMPRESSION_HINT 0x84EF #define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 #define GL_TEXTURE_COMPRESSED 0x86A1 #define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 #define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 #define GL_CLAMP_TO_BORDER 0x812D #define GL_BLEND_DST_RGB 0x80C8 #define GL_BLEND_SRC_RGB 0x80C9 #define GL_BLEND_DST_ALPHA 0x80CA #define GL_BLEND_SRC_ALPHA 0x80CB #define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 #define GL_DEPTH_COMPONENT16 0x81A5 #define GL_DEPTH_COMPONENT24 0x81A6 #define GL_DEPTH_COMPONENT32 0x81A7 #define GL_MIRRORED_REPEAT 0x8370 #define GL_MAX_TEXTURE_LOD_BIAS 0x84FD #define GL_TEXTURE_LOD_BIAS 0x8501 #define GL_INCR_WRAP 0x8507 #define GL_DECR_WRAP 0x8508 #define GL_TEXTURE_DEPTH_SIZE 0x884A #define GL_TEXTURE_COMPARE_MODE 0x884C #define GL_TEXTURE_COMPARE_FUNC 0x884D #define GL_FUNC_ADD 0x8006 #define GL_FUNC_SUBTRACT 0x800A #define GL_FUNC_REVERSE_SUBTRACT 0x800B #define GL_MIN 0x8007 #define GL_MAX 0x8008 #define GL_CONSTANT_COLOR 0x8001 #define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 #define GL_CONSTANT_ALPHA 0x8003 #define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 #define GL_BUFFER_SIZE 0x8764 #define GL_BUFFER_USAGE 0x8765 #define GL_QUERY_COUNTER_BITS 0x8864 #define GL_CURRENT_QUERY 0x8865 #define GL_QUERY_RESULT 0x8866 #define GL_QUERY_RESULT_AVAILABLE 0x8867 #define GL_ARRAY_BUFFER 0x8892 #define GL_ELEMENT_ARRAY_BUFFER 0x8893 #define GL_ARRAY_BUFFER_BINDING 0x8894 #define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 #define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F #define GL_READ_ONLY 0x88B8 #define GL_WRITE_ONLY 0x88B9 #define GL_READ_WRITE 0x88BA #define GL_BUFFER_ACCESS 0x88BB #define GL_BUFFER_MAPPED 0x88BC #define GL_BUFFER_MAP_POINTER 0x88BD #define GL_STREAM_DRAW 0x88E0 #define GL_STREAM_READ 0x88E1 #define GL_STREAM_COPY 0x88E2 #define GL_STATIC_DRAW 0x88E4 #define GL_STATIC_READ 0x88E5 #define GL_STATIC_COPY 0x88E6 #define GL_DYNAMIC_DRAW 0x88E8 #define GL_DYNAMIC_READ 0x88E9 #define GL_DYNAMIC_COPY 0x88EA #define GL_SAMPLES_PASSED 0x8914 #define GL_SRC1_ALPHA 0x8589 #define GL_BLEND_EQUATION_RGB 0x8009 #define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 #define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 #define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 #define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 #define GL_CURRENT_VERTEX_ATTRIB 0x8626 #define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 #define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 #define GL_STENCIL_BACK_FUNC 0x8800 #define GL_STENCIL_BACK_FAIL 0x8801 #define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 #define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 #define GL_MAX_DRAW_BUFFERS 0x8824 #define GL_DRAW_BUFFER0 0x8825 #define GL_DRAW_BUFFER1 0x8826 #define GL_DRAW_BUFFER2 0x8827 #define GL_DRAW_BUFFER3 0x8828 #define GL_DRAW_BUFFER4 0x8829 #define GL_DRAW_BUFFER5 0x882A #define GL_DRAW_BUFFER6 0x882B #define GL_DRAW_BUFFER7 0x882C #define GL_DRAW_BUFFER8 0x882D #define GL_DRAW_BUFFER9 0x882E #define GL_DRAW_BUFFER10 0x882F #define GL_DRAW_BUFFER11 0x8830 #define GL_DRAW_BUFFER12 0x8831 #define GL_DRAW_BUFFER13 0x8832 #define GL_DRAW_BUFFER14 0x8833 #define GL_DRAW_BUFFER15 0x8834 #define GL_BLEND_EQUATION_ALPHA 0x883D #define GL_MAX_VERTEX_ATTRIBS 0x8869 #define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A #define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 #define GL_FRAGMENT_SHADER 0x8B30 #define GL_VERTEX_SHADER 0x8B31 #define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 #define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A #define GL_MAX_VARYING_FLOATS 0x8B4B #define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D #define GL_SHADER_TYPE 0x8B4F #define GL_FLOAT_VEC2 0x8B50 #define GL_FLOAT_VEC3 0x8B51 #define GL_FLOAT_VEC4 0x8B52 #define GL_INT_VEC2 0x8B53 #define GL_INT_VEC3 0x8B54 #define GL_INT_VEC4 0x8B55 #define GL_BOOL 0x8B56 #define GL_BOOL_VEC2 0x8B57 #define GL_BOOL_VEC3 0x8B58 #define GL_BOOL_VEC4 0x8B59 #define GL_FLOAT_MAT2 0x8B5A #define GL_FLOAT_MAT3 0x8B5B #define GL_FLOAT_MAT4 0x8B5C #define GL_SAMPLER_1D 0x8B5D #define GL_SAMPLER_2D 0x8B5E #define GL_SAMPLER_3D 0x8B5F #define GL_SAMPLER_CUBE 0x8B60 #define GL_SAMPLER_1D_SHADOW 0x8B61 #define GL_SAMPLER_2D_SHADOW 0x8B62 #define GL_DELETE_STATUS 0x8B80 #define GL_COMPILE_STATUS 0x8B81 #define GL_LINK_STATUS 0x8B82 #define GL_VALIDATE_STATUS 0x8B83 #define GL_INFO_LOG_LENGTH 0x8B84 #define GL_ATTACHED_SHADERS 0x8B85 #define GL_ACTIVE_UNIFORMS 0x8B86 #define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 #define GL_SHADER_SOURCE_LENGTH 0x8B88 #define GL_ACTIVE_ATTRIBUTES 0x8B89 #define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A #define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B #define GL_SHADING_LANGUAGE_VERSION 0x8B8C #define GL_CURRENT_PROGRAM 0x8B8D #define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 #define GL_LOWER_LEFT 0x8CA1 #define GL_UPPER_LEFT 0x8CA2 #define GL_STENCIL_BACK_REF 0x8CA3 #define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 #define GL_STENCIL_BACK_WRITEMASK 0x8CA5 #define GL_PIXEL_PACK_BUFFER 0x88EB #define GL_PIXEL_UNPACK_BUFFER 0x88EC #define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED #define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF #define GL_FLOAT_MAT2x3 0x8B65 #define GL_FLOAT_MAT2x4 0x8B66 #define GL_FLOAT_MAT3x2 0x8B67 #define GL_FLOAT_MAT3x4 0x8B68 #define GL_FLOAT_MAT4x2 0x8B69 #define GL_FLOAT_MAT4x3 0x8B6A #define GL_SRGB 0x8C40 #define GL_SRGB8 0x8C41 #define GL_SRGB_ALPHA 0x8C42 #define GL_SRGB8_ALPHA8 0x8C43 #define GL_COMPRESSED_SRGB 0x8C48 #define GL_COMPRESSED_SRGB_ALPHA 0x8C49 #define GL_COMPARE_REF_TO_TEXTURE 0x884E #define GL_CLIP_DISTANCE0 0x3000 #define GL_CLIP_DISTANCE1 0x3001 #define GL_CLIP_DISTANCE2 0x3002 #define GL_CLIP_DISTANCE3 0x3003 #define GL_CLIP_DISTANCE4 0x3004 #define GL_CLIP_DISTANCE5 0x3005 #define GL_CLIP_DISTANCE6 0x3006 #define GL_CLIP_DISTANCE7 0x3007 #define GL_MAX_CLIP_DISTANCES 0x0D32 #define GL_MAJOR_VERSION 0x821B #define GL_MINOR_VERSION 0x821C #define GL_NUM_EXTENSIONS 0x821D #define GL_CONTEXT_FLAGS 0x821E #define GL_COMPRESSED_RED 0x8225 #define GL_COMPRESSED_RG 0x8226 #define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 #define GL_RGBA32F 0x8814 #define GL_RGB32F 0x8815 #define GL_RGBA16F 0x881A #define GL_RGB16F 0x881B #define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD #define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF #define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 #define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 #define GL_CLAMP_READ_COLOR 0x891C #define GL_FIXED_ONLY 0x891D #define GL_MAX_VARYING_COMPONENTS 0x8B4B #define GL_TEXTURE_1D_ARRAY 0x8C18 #define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 #define GL_TEXTURE_2D_ARRAY 0x8C1A #define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B #define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C #define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D #define GL_R11F_G11F_B10F 0x8C3A #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B #define GL_RGB9_E5 0x8C3D #define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E #define GL_TEXTURE_SHARED_SIZE 0x8C3F #define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 #define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F #define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 #define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 #define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 #define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 #define GL_PRIMITIVES_GENERATED 0x8C87 #define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 #define GL_RASTERIZER_DISCARD 0x8C89 #define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A #define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B #define GL_INTERLEAVED_ATTRIBS 0x8C8C #define GL_SEPARATE_ATTRIBS 0x8C8D #define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E #define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F #define GL_RGBA32UI 0x8D70 #define GL_RGB32UI 0x8D71 #define GL_RGBA16UI 0x8D76 #define GL_RGB16UI 0x8D77 #define GL_RGBA8UI 0x8D7C #define GL_RGB8UI 0x8D7D #define GL_RGBA32I 0x8D82 #define GL_RGB32I 0x8D83 #define GL_RGBA16I 0x8D88 #define GL_RGB16I 0x8D89 #define GL_RGBA8I 0x8D8E #define GL_RGB8I 0x8D8F #define GL_RED_INTEGER 0x8D94 #define GL_GREEN_INTEGER 0x8D95 #define GL_BLUE_INTEGER 0x8D96 #define GL_RGB_INTEGER 0x8D98 #define GL_RGBA_INTEGER 0x8D99 #define GL_BGR_INTEGER 0x8D9A #define GL_BGRA_INTEGER 0x8D9B #define GL_SAMPLER_1D_ARRAY 0x8DC0 #define GL_SAMPLER_2D_ARRAY 0x8DC1 #define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 #define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 #define GL_SAMPLER_CUBE_SHADOW 0x8DC5 #define GL_UNSIGNED_INT_VEC2 0x8DC6 #define GL_UNSIGNED_INT_VEC3 0x8DC7 #define GL_UNSIGNED_INT_VEC4 0x8DC8 #define GL_INT_SAMPLER_1D 0x8DC9 #define GL_INT_SAMPLER_2D 0x8DCA #define GL_INT_SAMPLER_3D 0x8DCB #define GL_INT_SAMPLER_CUBE 0x8DCC #define GL_INT_SAMPLER_1D_ARRAY 0x8DCE #define GL_INT_SAMPLER_2D_ARRAY 0x8DCF #define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 #define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 #define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 #define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 #define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 #define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 #define GL_QUERY_WAIT 0x8E13 #define GL_QUERY_NO_WAIT 0x8E14 #define GL_QUERY_BY_REGION_WAIT 0x8E15 #define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 #define GL_BUFFER_ACCESS_FLAGS 0x911F #define GL_BUFFER_MAP_LENGTH 0x9120 #define GL_BUFFER_MAP_OFFSET 0x9121 #define GL_DEPTH_COMPONENT32F 0x8CAC #define GL_DEPTH32F_STENCIL8 0x8CAD #define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD #define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 #define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 #define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 #define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 #define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 #define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 #define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 #define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 #define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 #define GL_FRAMEBUFFER_DEFAULT 0x8218 #define GL_FRAMEBUFFER_UNDEFINED 0x8219 #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A #define GL_MAX_RENDERBUFFER_SIZE 0x84E8 #define GL_DEPTH_STENCIL 0x84F9 #define GL_UNSIGNED_INT_24_8 0x84FA #define GL_DEPTH24_STENCIL8 0x88F0 #define GL_TEXTURE_STENCIL_SIZE 0x88F1 #define GL_TEXTURE_RED_TYPE 0x8C10 #define GL_TEXTURE_GREEN_TYPE 0x8C11 #define GL_TEXTURE_BLUE_TYPE 0x8C12 #define GL_TEXTURE_ALPHA_TYPE 0x8C13 #define GL_TEXTURE_DEPTH_TYPE 0x8C16 #define GL_UNSIGNED_NORMALIZED 0x8C17 #define GL_FRAMEBUFFER_BINDING 0x8CA6 #define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 #define GL_RENDERBUFFER_BINDING 0x8CA7 #define GL_READ_FRAMEBUFFER 0x8CA8 #define GL_DRAW_FRAMEBUFFER 0x8CA9 #define GL_READ_FRAMEBUFFER_BINDING 0x8CAA #define GL_RENDERBUFFER_SAMPLES 0x8CAB #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 #define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB #define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD #define GL_MAX_COLOR_ATTACHMENTS 0x8CDF #define GL_COLOR_ATTACHMENT0 0x8CE0 #define GL_COLOR_ATTACHMENT1 0x8CE1 #define GL_COLOR_ATTACHMENT2 0x8CE2 #define GL_COLOR_ATTACHMENT3 0x8CE3 #define GL_COLOR_ATTACHMENT4 0x8CE4 #define GL_COLOR_ATTACHMENT5 0x8CE5 #define GL_COLOR_ATTACHMENT6 0x8CE6 #define GL_COLOR_ATTACHMENT7 0x8CE7 #define GL_COLOR_ATTACHMENT8 0x8CE8 #define GL_COLOR_ATTACHMENT9 0x8CE9 #define GL_COLOR_ATTACHMENT10 0x8CEA #define GL_COLOR_ATTACHMENT11 0x8CEB #define GL_COLOR_ATTACHMENT12 0x8CEC #define GL_COLOR_ATTACHMENT13 0x8CED #define GL_COLOR_ATTACHMENT14 0x8CEE #define GL_COLOR_ATTACHMENT15 0x8CEF #define GL_COLOR_ATTACHMENT16 0x8CF0 #define GL_COLOR_ATTACHMENT17 0x8CF1 #define GL_COLOR_ATTACHMENT18 0x8CF2 #define GL_COLOR_ATTACHMENT19 0x8CF3 #define GL_COLOR_ATTACHMENT20 0x8CF4 #define GL_COLOR_ATTACHMENT21 0x8CF5 #define GL_COLOR_ATTACHMENT22 0x8CF6 #define GL_COLOR_ATTACHMENT23 0x8CF7 #define GL_COLOR_ATTACHMENT24 0x8CF8 #define GL_COLOR_ATTACHMENT25 0x8CF9 #define GL_COLOR_ATTACHMENT26 0x8CFA #define GL_COLOR_ATTACHMENT27 0x8CFB #define GL_COLOR_ATTACHMENT28 0x8CFC #define GL_COLOR_ATTACHMENT29 0x8CFD #define GL_COLOR_ATTACHMENT30 0x8CFE #define GL_COLOR_ATTACHMENT31 0x8CFF #define GL_DEPTH_ATTACHMENT 0x8D00 #define GL_STENCIL_ATTACHMENT 0x8D20 #define GL_FRAMEBUFFER 0x8D40 #define GL_RENDERBUFFER 0x8D41 #define GL_RENDERBUFFER_WIDTH 0x8D42 #define GL_RENDERBUFFER_HEIGHT 0x8D43 #define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 #define GL_STENCIL_INDEX1 0x8D46 #define GL_STENCIL_INDEX4 0x8D47 #define GL_STENCIL_INDEX8 0x8D48 #define GL_STENCIL_INDEX16 0x8D49 #define GL_RENDERBUFFER_RED_SIZE 0x8D50 #define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 #define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 #define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 #define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 #define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 #define GL_MAX_SAMPLES 0x8D57 #define GL_INDEX 0x8222 #define GL_FRAMEBUFFER_SRGB 0x8DB9 #define GL_HALF_FLOAT 0x140B #define GL_MAP_READ_BIT 0x0001 #define GL_MAP_WRITE_BIT 0x0002 #define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 #define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 #define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 #define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 #define GL_COMPRESSED_RED_RGTC1 0x8DBB #define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC #define GL_COMPRESSED_RG_RGTC2 0x8DBD #define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE #define GL_RG 0x8227 #define GL_RG_INTEGER 0x8228 #define GL_R8 0x8229 #define GL_R16 0x822A #define GL_RG8 0x822B #define GL_RG16 0x822C #define GL_R16F 0x822D #define GL_R32F 0x822E #define GL_RG16F 0x822F #define GL_RG32F 0x8230 #define GL_R8I 0x8231 #define GL_R8UI 0x8232 #define GL_R16I 0x8233 #define GL_R16UI 0x8234 #define GL_R32I 0x8235 #define GL_R32UI 0x8236 #define GL_RG8I 0x8237 #define GL_RG8UI 0x8238 #define GL_RG16I 0x8239 #define GL_RG16UI 0x823A #define GL_RG32I 0x823B #define GL_RG32UI 0x823C #define GL_VERTEX_ARRAY_BINDING 0x85B5 #define GL_SAMPLER_2D_RECT 0x8B63 #define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 #define GL_SAMPLER_BUFFER 0x8DC2 #define GL_INT_SAMPLER_2D_RECT 0x8DCD #define GL_INT_SAMPLER_BUFFER 0x8DD0 #define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 #define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 #define GL_TEXTURE_BUFFER 0x8C2A #define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B #define GL_TEXTURE_BINDING_BUFFER 0x8C2C #define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D #define GL_TEXTURE_RECTANGLE 0x84F5 #define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 #define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 #define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 #define GL_R8_SNORM 0x8F94 #define GL_RG8_SNORM 0x8F95 #define GL_RGB8_SNORM 0x8F96 #define GL_RGBA8_SNORM 0x8F97 #define GL_R16_SNORM 0x8F98 #define GL_RG16_SNORM 0x8F99 #define GL_RGB16_SNORM 0x8F9A #define GL_RGBA16_SNORM 0x8F9B #define GL_SIGNED_NORMALIZED 0x8F9C #define GL_PRIMITIVE_RESTART 0x8F9D #define GL_PRIMITIVE_RESTART_INDEX 0x8F9E #define GL_COPY_READ_BUFFER 0x8F36 #define GL_COPY_WRITE_BUFFER 0x8F37 #define GL_UNIFORM_BUFFER 0x8A11 #define GL_UNIFORM_BUFFER_BINDING 0x8A28 #define GL_UNIFORM_BUFFER_START 0x8A29 #define GL_UNIFORM_BUFFER_SIZE 0x8A2A #define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B #define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C #define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D #define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E #define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F #define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 #define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 #define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 #define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 #define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 #define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 #define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 #define GL_UNIFORM_TYPE 0x8A37 #define GL_UNIFORM_SIZE 0x8A38 #define GL_UNIFORM_NAME_LENGTH 0x8A39 #define GL_UNIFORM_BLOCK_INDEX 0x8A3A #define GL_UNIFORM_OFFSET 0x8A3B #define GL_UNIFORM_ARRAY_STRIDE 0x8A3C #define GL_UNIFORM_MATRIX_STRIDE 0x8A3D #define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E #define GL_UNIFORM_BLOCK_BINDING 0x8A3F #define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 #define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 #define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 #define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 #define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 #define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 #define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 #define GL_INVALID_INDEX 0xFFFFFFFF #define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 #define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 #define GL_LINES_ADJACENCY 0x000A #define GL_LINE_STRIP_ADJACENCY 0x000B #define GL_TRIANGLES_ADJACENCY 0x000C #define GL_TRIANGLE_STRIP_ADJACENCY 0x000D #define GL_PROGRAM_POINT_SIZE 0x8642 #define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 #define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 #define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 #define GL_GEOMETRY_SHADER 0x8DD9 #define GL_GEOMETRY_VERTICES_OUT 0x8916 #define GL_GEOMETRY_INPUT_TYPE 0x8917 #define GL_GEOMETRY_OUTPUT_TYPE 0x8918 #define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF #define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 #define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 #define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 #define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 #define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 #define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 #define GL_CONTEXT_PROFILE_MASK 0x9126 #define GL_DEPTH_CLAMP 0x864F #define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C #define GL_FIRST_VERTEX_CONVENTION 0x8E4D #define GL_LAST_VERTEX_CONVENTION 0x8E4E #define GL_PROVOKING_VERTEX 0x8E4F #define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F #define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 #define GL_OBJECT_TYPE 0x9112 #define GL_SYNC_CONDITION 0x9113 #define GL_SYNC_STATUS 0x9114 #define GL_SYNC_FLAGS 0x9115 #define GL_SYNC_FENCE 0x9116 #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 #define GL_UNSIGNALED 0x9118 #define GL_SIGNALED 0x9119 #define GL_ALREADY_SIGNALED 0x911A #define GL_TIMEOUT_EXPIRED 0x911B #define GL_CONDITION_SATISFIED 0x911C #define GL_WAIT_FAILED 0x911D #define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF #define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 #define GL_SAMPLE_POSITION 0x8E50 #define GL_SAMPLE_MASK 0x8E51 #define GL_SAMPLE_MASK_VALUE 0x8E52 #define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 #define GL_TEXTURE_2D_MULTISAMPLE 0x9100 #define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 #define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 #define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 #define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 #define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 #define GL_TEXTURE_SAMPLES 0x9106 #define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 #define GL_SAMPLER_2D_MULTISAMPLE 0x9108 #define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 #define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A #define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B #define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C #define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D #define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E #define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F #define GL_MAX_INTEGER_SAMPLES 0x9110 #ifndef GL_VERSION_1_0 #define GL_VERSION_1_0 1 GLAPI int GLAD_GL_VERSION_1_0; typedef void (APIENTRYP PFNGLCULLFACEPROC)(GLenum mode); GLAPI PFNGLCULLFACEPROC glad_glCullFace; #define glCullFace glad_glCullFace typedef void (APIENTRYP PFNGLFRONTFACEPROC)(GLenum mode); GLAPI PFNGLFRONTFACEPROC glad_glFrontFace; #define glFrontFace glad_glFrontFace typedef void (APIENTRYP PFNGLHINTPROC)(GLenum target, GLenum mode); GLAPI PFNGLHINTPROC glad_glHint; #define glHint glad_glHint typedef void (APIENTRYP PFNGLLINEWIDTHPROC)(GLfloat width); GLAPI PFNGLLINEWIDTHPROC glad_glLineWidth; #define glLineWidth glad_glLineWidth typedef void (APIENTRYP PFNGLPOINTSIZEPROC)(GLfloat size); GLAPI PFNGLPOINTSIZEPROC glad_glPointSize; #define glPointSize glad_glPointSize typedef void (APIENTRYP PFNGLPOLYGONMODEPROC)(GLenum face, GLenum mode); GLAPI PFNGLPOLYGONMODEPROC glad_glPolygonMode; #define glPolygonMode glad_glPolygonMode typedef void (APIENTRYP PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); GLAPI PFNGLSCISSORPROC glad_glScissor; #define glScissor glad_glScissor typedef void (APIENTRYP PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); GLAPI PFNGLTEXPARAMETERFPROC glad_glTexParameterf; #define glTexParameterf glad_glTexParameterf typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat *params); GLAPI PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv; #define glTexParameterfv glad_glTexParameterfv typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); GLAPI PFNGLTEXPARAMETERIPROC glad_glTexParameteri; #define glTexParameteri glad_glTexParameteri typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint *params); GLAPI PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv; #define glTexParameteriv glad_glTexParameteriv typedef void (APIENTRYP PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXIMAGE1DPROC glad_glTexImage1D; #define glTexImage1D glad_glTexImage1D typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXIMAGE2DPROC glad_glTexImage2D; #define glTexImage2D glad_glTexImage2D typedef void (APIENTRYP PFNGLDRAWBUFFERPROC)(GLenum buf); GLAPI PFNGLDRAWBUFFERPROC glad_glDrawBuffer; #define glDrawBuffer glad_glDrawBuffer typedef void (APIENTRYP PFNGLCLEARPROC)(GLbitfield mask); GLAPI PFNGLCLEARPROC glad_glClear; #define glClear glad_glClear typedef void (APIENTRYP PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); GLAPI PFNGLCLEARCOLORPROC glad_glClearColor; #define glClearColor glad_glClearColor typedef void (APIENTRYP PFNGLCLEARSTENCILPROC)(GLint s); GLAPI PFNGLCLEARSTENCILPROC glad_glClearStencil; #define glClearStencil glad_glClearStencil typedef void (APIENTRYP PFNGLCLEARDEPTHPROC)(GLdouble depth); GLAPI PFNGLCLEARDEPTHPROC glad_glClearDepth; #define glClearDepth glad_glClearDepth typedef void (APIENTRYP PFNGLSTENCILMASKPROC)(GLuint mask); GLAPI PFNGLSTENCILMASKPROC glad_glStencilMask; #define glStencilMask glad_glStencilMask typedef void (APIENTRYP PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); GLAPI PFNGLCOLORMASKPROC glad_glColorMask; #define glColorMask glad_glColorMask typedef void (APIENTRYP PFNGLDEPTHMASKPROC)(GLboolean flag); GLAPI PFNGLDEPTHMASKPROC glad_glDepthMask; #define glDepthMask glad_glDepthMask typedef void (APIENTRYP PFNGLDISABLEPROC)(GLenum cap); GLAPI PFNGLDISABLEPROC glad_glDisable; #define glDisable glad_glDisable typedef void (APIENTRYP PFNGLENABLEPROC)(GLenum cap); GLAPI PFNGLENABLEPROC glad_glEnable; #define glEnable glad_glEnable typedef void (APIENTRYP PFNGLFINISHPROC)(); GLAPI PFNGLFINISHPROC glad_glFinish; #define glFinish glad_glFinish typedef void (APIENTRYP PFNGLFLUSHPROC)(); GLAPI PFNGLFLUSHPROC glad_glFlush; #define glFlush glad_glFlush typedef void (APIENTRYP PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); GLAPI PFNGLBLENDFUNCPROC glad_glBlendFunc; #define glBlendFunc glad_glBlendFunc typedef void (APIENTRYP PFNGLLOGICOPPROC)(GLenum opcode); GLAPI PFNGLLOGICOPPROC glad_glLogicOp; #define glLogicOp glad_glLogicOp typedef void (APIENTRYP PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); GLAPI PFNGLSTENCILFUNCPROC glad_glStencilFunc; #define glStencilFunc glad_glStencilFunc typedef void (APIENTRYP PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); GLAPI PFNGLSTENCILOPPROC glad_glStencilOp; #define glStencilOp glad_glStencilOp typedef void (APIENTRYP PFNGLDEPTHFUNCPROC)(GLenum func); GLAPI PFNGLDEPTHFUNCPROC glad_glDepthFunc; #define glDepthFunc glad_glDepthFunc typedef void (APIENTRYP PFNGLPIXELSTOREFPROC)(GLenum pname, GLfloat param); GLAPI PFNGLPIXELSTOREFPROC glad_glPixelStoref; #define glPixelStoref glad_glPixelStoref typedef void (APIENTRYP PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); GLAPI PFNGLPIXELSTOREIPROC glad_glPixelStorei; #define glPixelStorei glad_glPixelStorei typedef void (APIENTRYP PFNGLREADBUFFERPROC)(GLenum src); GLAPI PFNGLREADBUFFERPROC glad_glReadBuffer; #define glReadBuffer glad_glReadBuffer typedef void (APIENTRYP PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); GLAPI PFNGLREADPIXELSPROC glad_glReadPixels; #define glReadPixels glad_glReadPixels typedef void (APIENTRYP PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean *data); GLAPI PFNGLGETBOOLEANVPROC glad_glGetBooleanv; #define glGetBooleanv glad_glGetBooleanv typedef void (APIENTRYP PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble *data); GLAPI PFNGLGETDOUBLEVPROC glad_glGetDoublev; #define glGetDoublev glad_glGetDoublev typedef GLenum (APIENTRYP PFNGLGETERRORPROC)(); GLAPI PFNGLGETERRORPROC glad_glGetError; #define glGetError glad_glGetError typedef void (APIENTRYP PFNGLGETFLOATVPROC)(GLenum pname, GLfloat *data); GLAPI PFNGLGETFLOATVPROC glad_glGetFloatv; #define glGetFloatv glad_glGetFloatv typedef void (APIENTRYP PFNGLGETINTEGERVPROC)(GLenum pname, GLint *data); GLAPI PFNGLGETINTEGERVPROC glad_glGetIntegerv; #define glGetIntegerv glad_glGetIntegerv typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGPROC)(GLenum name); GLAPI PFNGLGETSTRINGPROC glad_glGetString; #define glGetString glad_glGetString typedef void (APIENTRYP PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void *pixels); GLAPI PFNGLGETTEXIMAGEPROC glad_glGetTexImage; #define glGetTexImage glad_glGetTexImage typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat *params); GLAPI PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv; #define glGetTexParameterfv glad_glGetTexParameterfv typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv; #define glGetTexParameteriv glad_glGetTexParameteriv typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat *params); GLAPI PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv; #define glGetTexLevelParameterfv glad_glGetTexLevelParameterfv typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint *params); GLAPI PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv; #define glGetTexLevelParameteriv glad_glGetTexLevelParameteriv typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum cap); GLAPI PFNGLISENABLEDPROC glad_glIsEnabled; #define glIsEnabled glad_glIsEnabled typedef void (APIENTRYP PFNGLDEPTHRANGEPROC)(GLdouble near, GLdouble far); GLAPI PFNGLDEPTHRANGEPROC glad_glDepthRange; #define glDepthRange glad_glDepthRange typedef void (APIENTRYP PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height); GLAPI PFNGLVIEWPORTPROC glad_glViewport; #define glViewport glad_glViewport #endif #ifndef GL_VERSION_1_1 #define GL_VERSION_1_1 1 GLAPI int GLAD_GL_VERSION_1_1; typedef void (APIENTRYP PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); GLAPI PFNGLDRAWARRAYSPROC glad_glDrawArrays; #define glDrawArrays glad_glDrawArrays typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices); GLAPI PFNGLDRAWELEMENTSPROC glad_glDrawElements; #define glDrawElements glad_glDrawElements typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); GLAPI PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset; #define glPolygonOffset glad_glPolygonOffset typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); GLAPI PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D; #define glCopyTexImage1D glad_glCopyTexImage1D typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); GLAPI PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D; #define glCopyTexImage2D glad_glCopyTexImage2D typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); GLAPI PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D; #define glCopyTexSubImage1D glad_glCopyTexSubImage1D typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); GLAPI PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D; #define glCopyTexSubImage2D glad_glCopyTexSubImage2D typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D; #define glTexSubImage1D glad_glTexSubImage1D typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D; #define glTexSubImage2D glad_glTexSubImage2D typedef void (APIENTRYP PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); GLAPI PFNGLBINDTEXTUREPROC glad_glBindTexture; #define glBindTexture glad_glBindTexture typedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint *textures); GLAPI PFNGLDELETETEXTURESPROC glad_glDeleteTextures; #define glDeleteTextures glad_glDeleteTextures typedef void (APIENTRYP PFNGLGENTEXTURESPROC)(GLsizei n, GLuint *textures); GLAPI PFNGLGENTEXTURESPROC glad_glGenTextures; #define glGenTextures glad_glGenTextures typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC)(GLuint texture); GLAPI PFNGLISTEXTUREPROC glad_glIsTexture; #define glIsTexture glad_glIsTexture #endif #ifndef GL_VERSION_1_2 #define GL_VERSION_1_2 1 GLAPI int GLAD_GL_VERSION_1_2; typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); GLAPI PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements; #define glDrawRangeElements glad_glDrawRangeElements typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXIMAGE3DPROC glad_glTexImage3D; #define glTexImage3D glad_glTexImage3D typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); GLAPI PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D; #define glTexSubImage3D glad_glTexSubImage3D typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); GLAPI PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D; #define glCopyTexSubImage3D glad_glCopyTexSubImage3D #endif #ifndef GL_VERSION_1_3 #define GL_VERSION_1_3 1 GLAPI int GLAD_GL_VERSION_1_3; typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum texture); GLAPI PFNGLACTIVETEXTUREPROC glad_glActiveTexture; #define glActiveTexture glad_glActiveTexture typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); GLAPI PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage; #define glSampleCoverage glad_glSampleCoverage typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D; #define glCompressedTexImage3D glad_glCompressedTexImage3D typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D; #define glCompressedTexImage2D glad_glCompressedTexImage2D typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D; #define glCompressedTexImage1D glad_glCompressedTexImage1D typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D; #define glCompressedTexSubImage3D glad_glCompressedTexSubImage3D typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D; #define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D; #define glCompressedTexSubImage1D glad_glCompressedTexSubImage1D typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void *img); GLAPI PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage; #define glGetCompressedTexImage glad_glGetCompressedTexImage #endif #ifndef GL_VERSION_1_4 #define GL_VERSION_1_4 1 GLAPI int GLAD_GL_VERSION_1_4; typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); GLAPI PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate; #define glBlendFuncSeparate glad_glBlendFuncSeparate typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); GLAPI PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays; #define glMultiDrawArrays glad_glMultiDrawArrays typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); GLAPI PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements; #define glMultiDrawElements glad_glMultiDrawElements typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param); GLAPI PFNGLPOINTPARAMETERFPROC glad_glPointParameterf; #define glPointParameterf glad_glPointParameterf typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat *params); GLAPI PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv; #define glPointParameterfv glad_glPointParameterfv typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param); GLAPI PFNGLPOINTPARAMETERIPROC glad_glPointParameteri; #define glPointParameteri glad_glPointParameteri typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint *params); GLAPI PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv; #define glPointParameteriv glad_glPointParameteriv typedef void (APIENTRYP PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); GLAPI PFNGLBLENDCOLORPROC glad_glBlendColor; #define glBlendColor glad_glBlendColor typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC)(GLenum mode); GLAPI PFNGLBLENDEQUATIONPROC glad_glBlendEquation; #define glBlendEquation glad_glBlendEquation #endif #ifndef GL_VERSION_1_5 #define GL_VERSION_1_5 1 GLAPI int GLAD_GL_VERSION_1_5; typedef void (APIENTRYP PFNGLGENQUERIESPROC)(GLsizei n, GLuint *ids); GLAPI PFNGLGENQUERIESPROC glad_glGenQueries; #define glGenQueries glad_glGenQueries typedef void (APIENTRYP PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint *ids); GLAPI PFNGLDELETEQUERIESPROC glad_glDeleteQueries; #define glDeleteQueries glad_glDeleteQueries typedef GLboolean (APIENTRYP PFNGLISQUERYPROC)(GLuint id); GLAPI PFNGLISQUERYPROC glad_glIsQuery; #define glIsQuery glad_glIsQuery typedef void (APIENTRYP PFNGLBEGINQUERYPROC)(GLenum target, GLuint id); GLAPI PFNGLBEGINQUERYPROC glad_glBeginQuery; #define glBeginQuery glad_glBeginQuery typedef void (APIENTRYP PFNGLENDQUERYPROC)(GLenum target); GLAPI PFNGLENDQUERYPROC glad_glEndQuery; #define glEndQuery glad_glEndQuery typedef void (APIENTRYP PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETQUERYIVPROC glad_glGetQueryiv; #define glGetQueryiv glad_glGetQueryiv typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint *params); GLAPI PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv; #define glGetQueryObjectiv glad_glGetQueryObjectiv typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint *params); GLAPI PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv; #define glGetQueryObjectuiv glad_glGetQueryObjectuiv typedef void (APIENTRYP PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); GLAPI PFNGLBINDBUFFERPROC glad_glBindBuffer; #define glBindBuffer glad_glBindBuffer typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers); GLAPI PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers; #define glDeleteBuffers glad_glDeleteBuffers typedef void (APIENTRYP PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers); GLAPI PFNGLGENBUFFERSPROC glad_glGenBuffers; #define glGenBuffers glad_glGenBuffers typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC)(GLuint buffer); GLAPI PFNGLISBUFFERPROC glad_glIsBuffer; #define glIsBuffer glad_glIsBuffer typedef void (APIENTRYP PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void *data, GLenum usage); GLAPI PFNGLBUFFERDATAPROC glad_glBufferData; #define glBufferData glad_glBufferData typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data); GLAPI PFNGLBUFFERSUBDATAPROC glad_glBufferSubData; #define glBufferSubData glad_glBufferSubData typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void *data); GLAPI PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData; #define glGetBufferSubData glad_glGetBufferSubData typedef void * (APIENTRYP PFNGLMAPBUFFERPROC)(GLenum target, GLenum access); GLAPI PFNGLMAPBUFFERPROC glad_glMapBuffer; #define glMapBuffer glad_glMapBuffer typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC)(GLenum target); GLAPI PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer; #define glUnmapBuffer glad_glUnmapBuffer typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv; #define glGetBufferParameteriv glad_glGetBufferParameteriv typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void **params); GLAPI PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv; #define glGetBufferPointerv glad_glGetBufferPointerv #endif #ifndef GL_VERSION_2_0 #define GL_VERSION_2_0 1 GLAPI int GLAD_GL_VERSION_2_0; typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); GLAPI PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate; #define glBlendEquationSeparate glad_glBlendEquationSeparate typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum *bufs); GLAPI PFNGLDRAWBUFFERSPROC glad_glDrawBuffers; #define glDrawBuffers glad_glDrawBuffers typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); GLAPI PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate; #define glStencilOpSeparate glad_glStencilOpSeparate typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); GLAPI PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate; #define glStencilFuncSeparate glad_glStencilFuncSeparate typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); GLAPI PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate; #define glStencilMaskSeparate glad_glStencilMaskSeparate typedef void (APIENTRYP PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); GLAPI PFNGLATTACHSHADERPROC glad_glAttachShader; #define glAttachShader glad_glAttachShader typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar *name); GLAPI PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation; #define glBindAttribLocation glad_glBindAttribLocation typedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader); GLAPI PFNGLCOMPILESHADERPROC glad_glCompileShader; #define glCompileShader glad_glCompileShader typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)(); GLAPI PFNGLCREATEPROGRAMPROC glad_glCreateProgram; #define glCreateProgram glad_glCreateProgram typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC)(GLenum type); GLAPI PFNGLCREATESHADERPROC glad_glCreateShader; #define glCreateShader glad_glCreateShader typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint program); GLAPI PFNGLDELETEPROGRAMPROC glad_glDeleteProgram; #define glDeleteProgram glad_glDeleteProgram typedef void (APIENTRYP PFNGLDELETESHADERPROC)(GLuint shader); GLAPI PFNGLDELETESHADERPROC glad_glDeleteShader; #define glDeleteShader glad_glDeleteShader typedef void (APIENTRYP PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); GLAPI PFNGLDETACHSHADERPROC glad_glDetachShader; #define glDetachShader glad_glDetachShader typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); GLAPI PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray; #define glDisableVertexAttribArray glad_glDisableVertexAttribArray typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); GLAPI PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray; #define glEnableVertexAttribArray glad_glEnableVertexAttribArray typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); GLAPI PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib; #define glGetActiveAttrib glad_glGetActiveAttrib typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); GLAPI PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform; #define glGetActiveUniform glad_glGetActiveUniform typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); GLAPI PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders; #define glGetAttachedShaders glad_glGetAttachedShaders typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar *name); GLAPI PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation; #define glGetAttribLocation glad_glGetAttribLocation typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint *params); GLAPI PFNGLGETPROGRAMIVPROC glad_glGetProgramiv; #define glGetProgramiv glad_glGetProgramiv typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); GLAPI PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog; #define glGetProgramInfoLog glad_glGetProgramInfoLog typedef void (APIENTRYP PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint *params); GLAPI PFNGLGETSHADERIVPROC glad_glGetShaderiv; #define glGetShaderiv glad_glGetShaderiv typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); GLAPI PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog; #define glGetShaderInfoLog glad_glGetShaderInfoLog typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); GLAPI PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource; #define glGetShaderSource glad_glGetShaderSource typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar *name); GLAPI PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation; #define glGetUniformLocation glad_glGetUniformLocation typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat *params); GLAPI PFNGLGETUNIFORMFVPROC glad_glGetUniformfv; #define glGetUniformfv glad_glGetUniformfv typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint *params); GLAPI PFNGLGETUNIFORMIVPROC glad_glGetUniformiv; #define glGetUniformiv glad_glGetUniformiv typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble *params); GLAPI PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv; #define glGetVertexAttribdv glad_glGetVertexAttribdv typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat *params); GLAPI PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv; #define glGetVertexAttribfv glad_glGetVertexAttribfv typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint *params); GLAPI PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv; #define glGetVertexAttribiv glad_glGetVertexAttribiv typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void **pointer); GLAPI PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv; #define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC)(GLuint program); GLAPI PFNGLISPROGRAMPROC glad_glIsProgram; #define glIsProgram glad_glIsProgram typedef GLboolean (APIENTRYP PFNGLISSHADERPROC)(GLuint shader); GLAPI PFNGLISSHADERPROC glad_glIsShader; #define glIsShader glad_glIsShader typedef void (APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint program); GLAPI PFNGLLINKPROGRAMPROC glad_glLinkProgram; #define glLinkProgram glad_glLinkProgram typedef void (APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); GLAPI PFNGLSHADERSOURCEPROC glad_glShaderSource; #define glShaderSource glad_glShaderSource typedef void (APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint program); GLAPI PFNGLUSEPROGRAMPROC glad_glUseProgram; #define glUseProgram glad_glUseProgram typedef void (APIENTRYP PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); GLAPI PFNGLUNIFORM1FPROC glad_glUniform1f; #define glUniform1f glad_glUniform1f typedef void (APIENTRYP PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); GLAPI PFNGLUNIFORM2FPROC glad_glUniform2f; #define glUniform2f glad_glUniform2f typedef void (APIENTRYP PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); GLAPI PFNGLUNIFORM3FPROC glad_glUniform3f; #define glUniform3f glad_glUniform3f typedef void (APIENTRYP PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); GLAPI PFNGLUNIFORM4FPROC glad_glUniform4f; #define glUniform4f glad_glUniform4f typedef void (APIENTRYP PFNGLUNIFORM1IPROC)(GLint location, GLint v0); GLAPI PFNGLUNIFORM1IPROC glad_glUniform1i; #define glUniform1i glad_glUniform1i typedef void (APIENTRYP PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); GLAPI PFNGLUNIFORM2IPROC glad_glUniform2i; #define glUniform2i glad_glUniform2i typedef void (APIENTRYP PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); GLAPI PFNGLUNIFORM3IPROC glad_glUniform3i; #define glUniform3i glad_glUniform3i typedef void (APIENTRYP PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); GLAPI PFNGLUNIFORM4IPROC glad_glUniform4i; #define glUniform4i glad_glUniform4i typedef void (APIENTRYP PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM1FVPROC glad_glUniform1fv; #define glUniform1fv glad_glUniform1fv typedef void (APIENTRYP PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM2FVPROC glad_glUniform2fv; #define glUniform2fv glad_glUniform2fv typedef void (APIENTRYP PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM3FVPROC glad_glUniform3fv; #define glUniform3fv glad_glUniform3fv typedef void (APIENTRYP PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM4FVPROC glad_glUniform4fv; #define glUniform4fv glad_glUniform4fv typedef void (APIENTRYP PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM1IVPROC glad_glUniform1iv; #define glUniform1iv glad_glUniform1iv typedef void (APIENTRYP PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM2IVPROC glad_glUniform2iv; #define glUniform2iv glad_glUniform2iv typedef void (APIENTRYP PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM3IVPROC glad_glUniform3iv; #define glUniform3iv glad_glUniform3iv typedef void (APIENTRYP PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM4IVPROC glad_glUniform4iv; #define glUniform4iv glad_glUniform4iv typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv; #define glUniformMatrix2fv glad_glUniformMatrix2fv typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv; #define glUniformMatrix3fv glad_glUniformMatrix3fv typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv; #define glUniformMatrix4fv glad_glUniformMatrix4fv typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC)(GLuint program); GLAPI PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram; #define glValidateProgram glad_glValidateProgram typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x); GLAPI PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d; #define glVertexAttrib1d glad_glVertexAttrib1d typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble *v); GLAPI PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv; #define glVertexAttrib1dv glad_glVertexAttrib1dv typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); GLAPI PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f; #define glVertexAttrib1f glad_glVertexAttrib1f typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat *v); GLAPI PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv; #define glVertexAttrib1fv glad_glVertexAttrib1fv typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x); GLAPI PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s; #define glVertexAttrib1s glad_glVertexAttrib1s typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv; #define glVertexAttrib1sv glad_glVertexAttrib1sv typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y); GLAPI PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d; #define glVertexAttrib2d glad_glVertexAttrib2d typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble *v); GLAPI PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv; #define glVertexAttrib2dv glad_glVertexAttrib2dv typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); GLAPI PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f; #define glVertexAttrib2f glad_glVertexAttrib2f typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat *v); GLAPI PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv; #define glVertexAttrib2fv glad_glVertexAttrib2fv typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y); GLAPI PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s; #define glVertexAttrib2s glad_glVertexAttrib2s typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv; #define glVertexAttrib2sv glad_glVertexAttrib2sv typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); GLAPI PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d; #define glVertexAttrib3d glad_glVertexAttrib3d typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble *v); GLAPI PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv; #define glVertexAttrib3dv glad_glVertexAttrib3dv typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); GLAPI PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f; #define glVertexAttrib3f glad_glVertexAttrib3f typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat *v); GLAPI PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv; #define glVertexAttrib3fv glad_glVertexAttrib3fv typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z); GLAPI PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s; #define glVertexAttrib3s glad_glVertexAttrib3s typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv; #define glVertexAttrib3sv glad_glVertexAttrib3sv typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte *v); GLAPI PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv; #define glVertexAttrib4Nbv glad_glVertexAttrib4Nbv typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv; #define glVertexAttrib4Niv glad_glVertexAttrib4Niv typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv; #define glVertexAttrib4Nsv glad_glVertexAttrib4Nsv typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); GLAPI PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub; #define glVertexAttrib4Nub glad_glVertexAttrib4Nub typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte *v); GLAPI PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv; #define glVertexAttrib4Nubv glad_glVertexAttrib4Nubv typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv; #define glVertexAttrib4Nuiv glad_glVertexAttrib4Nuiv typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort *v); GLAPI PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv; #define glVertexAttrib4Nusv glad_glVertexAttrib4Nusv typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte *v); GLAPI PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv; #define glVertexAttrib4bv glad_glVertexAttrib4bv typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); GLAPI PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d; #define glVertexAttrib4d glad_glVertexAttrib4d typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble *v); GLAPI PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv; #define glVertexAttrib4dv glad_glVertexAttrib4dv typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); GLAPI PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f; #define glVertexAttrib4f glad_glVertexAttrib4f typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat *v); GLAPI PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv; #define glVertexAttrib4fv glad_glVertexAttrib4fv typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv; #define glVertexAttrib4iv glad_glVertexAttrib4iv typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); GLAPI PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s; #define glVertexAttrib4s glad_glVertexAttrib4s typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv; #define glVertexAttrib4sv glad_glVertexAttrib4sv typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte *v); GLAPI PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv; #define glVertexAttrib4ubv glad_glVertexAttrib4ubv typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv; #define glVertexAttrib4uiv glad_glVertexAttrib4uiv typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort *v); GLAPI PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv; #define glVertexAttrib4usv glad_glVertexAttrib4usv typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); GLAPI PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer; #define glVertexAttribPointer glad_glVertexAttribPointer #endif #ifndef GL_VERSION_2_1 #define GL_VERSION_2_1 1 GLAPI int GLAD_GL_VERSION_2_1; typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv; #define glUniformMatrix2x3fv glad_glUniformMatrix2x3fv typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv; #define glUniformMatrix3x2fv glad_glUniformMatrix3x2fv typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv; #define glUniformMatrix2x4fv glad_glUniformMatrix2x4fv typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv; #define glUniformMatrix4x2fv glad_glUniformMatrix4x2fv typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv; #define glUniformMatrix3x4fv glad_glUniformMatrix3x4fv typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); GLAPI PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv; #define glUniformMatrix4x3fv glad_glUniformMatrix4x3fv #endif #ifndef GL_VERSION_3_0 #define GL_VERSION_3_0 1 GLAPI int GLAD_GL_VERSION_3_0; typedef void (APIENTRYP PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); GLAPI PFNGLCOLORMASKIPROC glad_glColorMaski; #define glColorMaski glad_glColorMaski typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean *data); GLAPI PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v; #define glGetBooleani_v glad_glGetBooleani_v typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint *data); GLAPI PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v; #define glGetIntegeri_v glad_glGetIntegeri_v typedef void (APIENTRYP PFNGLENABLEIPROC)(GLenum target, GLuint index); GLAPI PFNGLENABLEIPROC glad_glEnablei; #define glEnablei glad_glEnablei typedef void (APIENTRYP PFNGLDISABLEIPROC)(GLenum target, GLuint index); GLAPI PFNGLDISABLEIPROC glad_glDisablei; #define glDisablei glad_glDisablei typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC)(GLenum target, GLuint index); GLAPI PFNGLISENABLEDIPROC glad_glIsEnabledi; #define glIsEnabledi glad_glIsEnabledi typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode); GLAPI PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback; #define glBeginTransformFeedback glad_glBeginTransformFeedback typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC)(); GLAPI PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback; #define glEndTransformFeedback glad_glEndTransformFeedback typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); GLAPI PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange; #define glBindBufferRange glad_glBindBufferRange typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer); GLAPI PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase; #define glBindBufferBase glad_glBindBufferBase typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); GLAPI PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings; #define glTransformFeedbackVaryings glad_glTransformFeedbackVaryings typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); GLAPI PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying; #define glGetTransformFeedbackVarying glad_glGetTransformFeedbackVarying typedef void (APIENTRYP PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp); GLAPI PFNGLCLAMPCOLORPROC glad_glClampColor; #define glClampColor glad_glClampColor typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode); GLAPI PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender; #define glBeginConditionalRender glad_glBeginConditionalRender typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC)(); GLAPI PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender; #define glEndConditionalRender glad_glEndConditionalRender typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); GLAPI PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer; #define glVertexAttribIPointer glad_glVertexAttribIPointer typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint *params); GLAPI PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv; #define glGetVertexAttribIiv glad_glGetVertexAttribIiv typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint *params); GLAPI PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv; #define glGetVertexAttribIuiv glad_glGetVertexAttribIuiv typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x); GLAPI PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i; #define glVertexAttribI1i glad_glVertexAttribI1i typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC)(GLuint index, GLint x, GLint y); GLAPI PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i; #define glVertexAttribI2i glad_glVertexAttribI2i typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC)(GLuint index, GLint x, GLint y, GLint z); GLAPI PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i; #define glVertexAttribI3i glad_glVertexAttribI3i typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w); GLAPI PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i; #define glVertexAttribI4i glad_glVertexAttribI4i typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC)(GLuint index, GLuint x); GLAPI PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui; #define glVertexAttribI1ui glad_glVertexAttribI1ui typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC)(GLuint index, GLuint x, GLuint y); GLAPI PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui; #define glVertexAttribI2ui glad_glVertexAttribI2ui typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z); GLAPI PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui; #define glVertexAttribI3ui glad_glVertexAttribI3ui typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); GLAPI PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui; #define glVertexAttribI4ui glad_glVertexAttribI4ui typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv; #define glVertexAttribI1iv glad_glVertexAttribI1iv typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv; #define glVertexAttribI2iv glad_glVertexAttribI2iv typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv; #define glVertexAttribI3iv glad_glVertexAttribI3iv typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv; #define glVertexAttribI4iv glad_glVertexAttribI4iv typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv; #define glVertexAttribI1uiv glad_glVertexAttribI1uiv typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv; #define glVertexAttribI2uiv glad_glVertexAttribI2uiv typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv; #define glVertexAttribI3uiv glad_glVertexAttribI3uiv typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv; #define glVertexAttribI4uiv glad_glVertexAttribI4uiv typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte *v); GLAPI PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv; #define glVertexAttribI4bv glad_glVertexAttribI4bv typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort *v); GLAPI PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv; #define glVertexAttribI4sv glad_glVertexAttribI4sv typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte *v); GLAPI PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv; #define glVertexAttribI4ubv glad_glVertexAttribI4ubv typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort *v); GLAPI PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv; #define glVertexAttribI4usv glad_glVertexAttribI4usv typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint *params); GLAPI PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv; #define glGetUniformuiv glad_glGetUniformuiv typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar *name); GLAPI PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation; #define glBindFragDataLocation glad_glBindFragDataLocation typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar *name); GLAPI PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation; #define glGetFragDataLocation glad_glGetFragDataLocation typedef void (APIENTRYP PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0); GLAPI PFNGLUNIFORM1UIPROC glad_glUniform1ui; #define glUniform1ui glad_glUniform1ui typedef void (APIENTRYP PFNGLUNIFORM2UIPROC)(GLint location, GLuint v0, GLuint v1); GLAPI PFNGLUNIFORM2UIPROC glad_glUniform2ui; #define glUniform2ui glad_glUniform2ui typedef void (APIENTRYP PFNGLUNIFORM3UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2); GLAPI PFNGLUNIFORM3UIPROC glad_glUniform3ui; #define glUniform3ui glad_glUniform3ui typedef void (APIENTRYP PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); GLAPI PFNGLUNIFORM4UIPROC glad_glUniform4ui; #define glUniform4ui glad_glUniform4ui typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint *value); GLAPI PFNGLUNIFORM1UIVPROC glad_glUniform1uiv; #define glUniform1uiv glad_glUniform1uiv typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint *value); GLAPI PFNGLUNIFORM2UIVPROC glad_glUniform2uiv; #define glUniform2uiv glad_glUniform2uiv typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint *value); GLAPI PFNGLUNIFORM3UIVPROC glad_glUniform3uiv; #define glUniform3uiv glad_glUniform3uiv typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint *value); GLAPI PFNGLUNIFORM4UIVPROC glad_glUniform4uiv; #define glUniform4uiv glad_glUniform4uiv typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint *params); GLAPI PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv; #define glTexParameterIiv glad_glTexParameterIiv typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint *params); GLAPI PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv; #define glTexParameterIuiv glad_glTexParameterIuiv typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv; #define glGetTexParameterIiv glad_glGetTexParameterIiv typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint *params); GLAPI PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv; #define glGetTexParameterIuiv glad_glGetTexParameterIuiv typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint *value); GLAPI PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv; #define glClearBufferiv glad_glClearBufferiv typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint *value); GLAPI PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv; #define glClearBufferuiv glad_glClearBufferuiv typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat *value); GLAPI PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv; #define glClearBufferfv glad_glClearBufferfv typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); GLAPI PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi; #define glClearBufferfi glad_glClearBufferfi typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGIPROC)(GLenum name, GLuint index); GLAPI PFNGLGETSTRINGIPROC glad_glGetStringi; #define glGetStringi glad_glGetStringi typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); GLAPI PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer; #define glIsRenderbuffer glad_glIsRenderbuffer typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); GLAPI PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer; #define glBindRenderbuffer glad_glBindRenderbuffer typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint *renderbuffers); GLAPI PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers; #define glDeleteRenderbuffers glad_glDeleteRenderbuffers typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers); GLAPI PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers; #define glGenRenderbuffers glad_glGenRenderbuffers typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); GLAPI PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage; #define glRenderbufferStorage glad_glRenderbufferStorage typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv; #define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); GLAPI PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer; #define glIsFramebuffer glad_glIsFramebuffer typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); GLAPI PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer; #define glBindFramebuffer glad_glBindFramebuffer typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint *framebuffers); GLAPI PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers; #define glDeleteFramebuffers glad_glDeleteFramebuffers typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers); GLAPI PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers; #define glGenFramebuffers glad_glGenFramebuffers typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); GLAPI PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus; #define glCheckFramebufferStatus glad_glCheckFramebufferStatus typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); GLAPI PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D; #define glFramebufferTexture1D glad_glFramebufferTexture1D typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); GLAPI PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D; #define glFramebufferTexture2D glad_glFramebufferTexture2D typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); GLAPI PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D; #define glFramebufferTexture3D glad_glFramebufferTexture3D typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); GLAPI PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer; #define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint *params); GLAPI PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv; #define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC)(GLenum target); GLAPI PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap; #define glGenerateMipmap glad_glGenerateMipmap typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); GLAPI PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer; #define glBlitFramebuffer glad_glBlitFramebuffer typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); GLAPI PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample; #define glRenderbufferStorageMultisample glad_glRenderbufferStorageMultisample typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); GLAPI PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer; #define glFramebufferTextureLayer glad_glFramebufferTextureLayer typedef void * (APIENTRYP PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); GLAPI PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange; #define glMapBufferRange glad_glMapBufferRange typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length); GLAPI PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange; #define glFlushMappedBufferRange glad_glFlushMappedBufferRange typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC)(GLuint array); GLAPI PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray; #define glBindVertexArray glad_glBindVertexArray typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint *arrays); GLAPI PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays; #define glDeleteVertexArrays glad_glDeleteVertexArrays typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint *arrays); GLAPI PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays; #define glGenVertexArrays glad_glGenVertexArrays typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC)(GLuint array); GLAPI PFNGLISVERTEXARRAYPROC glad_glIsVertexArray; #define glIsVertexArray glad_glIsVertexArray #endif #ifndef GL_VERSION_3_1 #define GL_VERSION_3_1 1 GLAPI int GLAD_GL_VERSION_3_1; typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); GLAPI PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced; #define glDrawArraysInstanced glad_glDrawArraysInstanced typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); GLAPI PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced; #define glDrawElementsInstanced glad_glDrawElementsInstanced typedef void (APIENTRYP PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer); GLAPI PFNGLTEXBUFFERPROC glad_glTexBuffer; #define glTexBuffer glad_glTexBuffer typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index); GLAPI PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex; #define glPrimitiveRestartIndex glad_glPrimitiveRestartIndex typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); GLAPI PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData; #define glCopyBufferSubData glad_glCopyBufferSubData typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); GLAPI PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices; #define glGetUniformIndices glad_glGetUniformIndices typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); GLAPI PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv; #define glGetActiveUniformsiv glad_glGetActiveUniformsiv typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); GLAPI PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName; #define glGetActiveUniformName glad_glGetActiveUniformName typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar *uniformBlockName); GLAPI PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex; #define glGetUniformBlockIndex glad_glGetUniformBlockIndex typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); GLAPI PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv; #define glGetActiveUniformBlockiv glad_glGetActiveUniformBlockiv typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); GLAPI PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName; #define glGetActiveUniformBlockName glad_glGetActiveUniformBlockName typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); GLAPI PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding; #define glUniformBlockBinding glad_glUniformBlockBinding #endif #ifndef GL_VERSION_3_2 #define GL_VERSION_3_2 1 GLAPI int GLAD_GL_VERSION_3_2; typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); GLAPI PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex; #define glDrawElementsBaseVertex glad_glDrawElementsBaseVertex typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); GLAPI PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex; #define glDrawRangeElementsBaseVertex glad_glDrawRangeElementsBaseVertex typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); GLAPI PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex; #define glDrawElementsInstancedBaseVertex glad_glDrawElementsInstancedBaseVertex typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); GLAPI PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex; #define glMultiDrawElementsBaseVertex glad_glMultiDrawElementsBaseVertex typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC)(GLenum mode); GLAPI PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex; #define glProvokingVertex glad_glProvokingVertex typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC)(GLenum condition, GLbitfield flags); GLAPI PFNGLFENCESYNCPROC glad_glFenceSync; #define glFenceSync glad_glFenceSync typedef GLboolean (APIENTRYP PFNGLISSYNCPROC)(GLsync sync); GLAPI PFNGLISSYNCPROC glad_glIsSync; #define glIsSync glad_glIsSync typedef void (APIENTRYP PFNGLDELETESYNCPROC)(GLsync sync); GLAPI PFNGLDELETESYNCPROC glad_glDeleteSync; #define glDeleteSync glad_glDeleteSync typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); GLAPI PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync; #define glClientWaitSync glad_glClientWaitSync typedef void (APIENTRYP PFNGLWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); GLAPI PFNGLWAITSYNCPROC glad_glWaitSync; #define glWaitSync glad_glWaitSync typedef void (APIENTRYP PFNGLGETINTEGER64VPROC)(GLenum pname, GLint64 *data); GLAPI PFNGLGETINTEGER64VPROC glad_glGetInteger64v; #define glGetInteger64v glad_glGetInteger64v typedef void (APIENTRYP PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); GLAPI PFNGLGETSYNCIVPROC glad_glGetSynciv; #define glGetSynciv glad_glGetSynciv typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64 *data); GLAPI PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v; #define glGetInteger64i_v glad_glGetInteger64i_v typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum target, GLenum pname, GLint64 *params); GLAPI PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v; #define glGetBufferParameteri64v glad_glGetBufferParameteri64v typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); GLAPI PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture; #define glFramebufferTexture glad_glFramebufferTexture typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); GLAPI PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample; #define glTexImage2DMultisample glad_glTexImage2DMultisample typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); GLAPI PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample; #define glTexImage3DMultisample glad_glTexImage3DMultisample typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC)(GLenum pname, GLuint index, GLfloat *val); GLAPI PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv; #define glGetMultisamplefv glad_glGetMultisamplefv typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC)(GLuint maskNumber, GLbitfield mask); GLAPI PFNGLSAMPLEMASKIPROC glad_glSampleMaski; #define glSampleMaski glad_glSampleMaski #endif #define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 #define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 #define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 #define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 #define GL_DEBUG_SOURCE_API_ARB 0x8246 #define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 #define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 #define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 #define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A #define GL_DEBUG_SOURCE_OTHER_ARB 0x824B #define GL_DEBUG_TYPE_ERROR_ARB 0x824C #define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E #define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F #define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 #define GL_DEBUG_TYPE_OTHER_ARB 0x8251 #define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 #define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 #define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 #define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 #define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 #define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF #ifndef GL_ARB_debug_output #define GL_ARB_debug_output 1 GLAPI int GLAD_GL_ARB_debug_output; typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); GLAPI PFNGLDEBUGMESSAGECONTROLARBPROC glad_glDebugMessageControlARB; #define glDebugMessageControlARB glad_glDebugMessageControlARB typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); GLAPI PFNGLDEBUGMESSAGEINSERTARBPROC glad_glDebugMessageInsertARB; #define glDebugMessageInsertARB glad_glDebugMessageInsertARB typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC)(GLDEBUGPROCARB callback, const void *userParam); GLAPI PFNGLDEBUGMESSAGECALLBACKARBPROC glad_glDebugMessageCallbackARB; #define glDebugMessageCallbackARB glad_glDebugMessageCallbackARB typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); GLAPI PFNGLGETDEBUGMESSAGELOGARBPROC glad_glGetDebugMessageLogARB; #define glGetDebugMessageLogARB glad_glGetDebugMessageLogARB #endif #ifndef GL_EXT_texture_filter_anisotropic #define GL_EXT_texture_filter_anisotropic 1 GLAPI int GLAD_GL_EXT_texture_filter_anisotropic; #endif #ifdef __cplusplus } #endif #endif yquake2-QUAKE2_7_10/src/client/refresh/gl3/glad/src/000077500000000000000000000000001321245476300220105ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/refresh/gl3/glad/src/glad.c000066400000000000000000001346451321245476300231000ustar00rootroot00000000000000/* OpenGL loader generated by glad 0.1.12a0 on Wed Dec 21 00:44:13 2016. Language/Generator: C/C++ Specification: gl APIs: gl=3.2 Profile: core Extensions: GL_ARB_debug_output, GL_EXT_texture_filter_anisotropic Loader: False Local files: False Omit khrplatform: False Commandline: --profile="core" --api="gl=3.2" --generator="c" --spec="gl" --no-loader --extensions="GL_ARB_debug_output,GL_EXT_texture_filter_anisotropic" Online: http://glad.dav1d.de/#profile=core&language=c&specification=gl&api=gl%3D3.2&extensions=GL_ARB_debug_output&extensions=GL_EXT_texture_filter_anisotropic */ #include #include #include #include struct gladGLversionStruct GLVersion; #if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) #define _GLAD_IS_SOME_NEW_VERSION 1 #endif static int max_loaded_major; static int max_loaded_minor; static const char *exts = NULL; static int num_exts_i = 0; static const char **exts_i = NULL; static int get_exts(void) { #ifdef _GLAD_IS_SOME_NEW_VERSION if(max_loaded_major < 3) { #endif exts = (const char *)glGetString(GL_EXTENSIONS); #ifdef _GLAD_IS_SOME_NEW_VERSION } else { int index; num_exts_i = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts_i); if (num_exts_i > 0) { exts_i = (const char **)realloc((void *)exts_i, num_exts_i * sizeof *exts_i); } if (exts_i == NULL) { return 0; } for(index = 0; index < num_exts_i; index++) { exts_i[index] = (const char*)glGetStringi(GL_EXTENSIONS, index); } } #endif return 1; } static void free_exts(void) { if (exts_i != NULL) { free((char **)exts_i); exts_i = NULL; } } static int has_ext(const char *ext) { #ifdef _GLAD_IS_SOME_NEW_VERSION if(max_loaded_major < 3) { #endif const char *extensions; const char *loc; const char *terminator; extensions = exts; if(extensions == NULL || ext == NULL) { return 0; } while(1) { loc = strstr(extensions, ext); if(loc == NULL) { return 0; } terminator = loc + strlen(ext); if((loc == extensions || *(loc - 1) == ' ') && (*terminator == ' ' || *terminator == '\0')) { return 1; } extensions = terminator; } #ifdef _GLAD_IS_SOME_NEW_VERSION } else { int index; for(index = 0; index < num_exts_i; index++) { const char *e = exts_i[index]; if(strcmp(e, ext) == 0) { return 1; } } } #endif return 0; } int GLAD_GL_VERSION_1_0; int GLAD_GL_VERSION_1_1; int GLAD_GL_VERSION_1_2; int GLAD_GL_VERSION_1_3; int GLAD_GL_VERSION_1_4; int GLAD_GL_VERSION_1_5; int GLAD_GL_VERSION_2_0; int GLAD_GL_VERSION_2_1; int GLAD_GL_VERSION_3_0; int GLAD_GL_VERSION_3_1; int GLAD_GL_VERSION_3_2; PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays; PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback; PFNGLFLUSHPROC glad_glFlush; PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D; PFNGLCLEARCOLORPROC glad_glClearColor; PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui; PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex; PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv; PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv; PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate; PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv; PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv; PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation; PFNGLLINKPROGRAMPROC glad_glLinkProgram; PFNGLBINDTEXTUREPROC glad_glBindTexture; PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName; PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv; PFNGLFENCESYNCPROC glad_glFenceSync; PFNGLUNIFORM3UIPROC glad_glUniform3ui; PFNGLUNIFORM2UIVPROC glad_glUniform2uiv; PFNGLGETSTRINGPROC glad_glGetString; PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D; PFNGLDETACHSHADERPROC glad_glDetachShader; PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv; PFNGLGENBUFFERSPROC glad_glGenBuffers; PFNGLENDQUERYPROC glad_glEndQuery; PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv; PFNGLLINEWIDTHPROC glad_glLineWidth; PFNGLUNIFORM2FVPROC glad_glUniform2fv; PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced; PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v; PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation; PFNGLCOMPILESHADERPROC glad_glCompileShader; PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying; PFNGLDELETETEXTURESPROC glad_glDeleteTextures; PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate; PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv; PFNGLPOLYGONMODEPROC glad_glPolygonMode; PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange; PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f; PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers; PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d; PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f; PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v; PFNGLISSYNCPROC glad_glIsSync; PFNGLCLAMPCOLORPROC glad_glClampColor; PFNGLUNIFORM4IVPROC glad_glUniform4iv; PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv; PFNGLCLEARSTENCILPROC glad_glClearStencil; PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture; PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv; PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s; PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex; PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv; PFNGLENABLEIPROC glad_glEnablei; PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage; PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv; PFNGLGENTEXTURESPROC glad_glGenTextures; PFNGLDEPTHFUNCPROC glad_glDepthFunc; PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D; PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv; PFNGLUNIFORM1FPROC glad_glUniform1f; PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv; PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv; PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv; PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv; PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage; PFNGLISVERTEXARRAYPROC glad_glIsVertexArray; PFNGLCREATESHADERPROC glad_glCreateShader; PFNGLISBUFFERPROC glad_glIsBuffer; PFNGLUNIFORM1IPROC glad_glUniform1i; PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s; PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib; PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D; PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv; PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D; PFNGLDISABLEPROC glad_glDisable; PFNGLUNIFORM2IPROC glad_glUniform2i; PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate; PFNGLLOGICOPPROC glad_glLogicOp; PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv; PFNGLGETPROGRAMIVPROC glad_glGetProgramiv; PFNGLCOLORMASKPROC glad_glColorMask; PFNGLHINTPROC glad_glHint; PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s; PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer; PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv; PFNGLBLENDEQUATIONPROC glad_glBlendEquation; PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation; PFNGLSAMPLEMASKIPROC glad_glSampleMaski; PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer; PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback; PFNGLCULLFACEPROC glad_glCullFace; PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv; PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv; PFNGLUNIFORM4FVPROC glad_glUniform4fv; PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv; PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers; PFNGLUNIFORM1UIVPROC glad_glUniform1uiv; PFNGLPOINTSIZEPROC glad_glPointSize; PFNGLGETSTRINGIPROC glad_glGetStringi; PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv; PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv; PFNGLDELETEPROGRAMPROC glad_glDeleteProgram; PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv; PFNGLGENQUERIESPROC glad_glGenQueries; PFNGLWAITSYNCPROC glad_glWaitSync; PFNGLATTACHSHADERPROC glad_glAttachShader; PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer; PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage; PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv; PFNGLUNIFORM3IPROC glad_glUniform3i; PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D; PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D; PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex; PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus; PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D; PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v; PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex; PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv; PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D; PFNGLUNIFORM3FPROC glad_glUniform3f; PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv; PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv; PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv; PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender; PFNGLDRAWELEMENTSPROC glad_glDrawElements; PFNGLCOLORMASKIPROC glad_glColorMaski; PFNGLISENABLEDIPROC glad_glIsEnabledi; PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv; PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements; PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv; PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays; PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase; PFNGLBUFFERSUBDATAPROC glad_glBufferSubData; PFNGLUNIFORM1IVPROC glad_glUniform1iv; PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv; PFNGLUNIFORM4UIVPROC glad_glUniform4uiv; PFNGLREADBUFFERPROC glad_glReadBuffer; PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData; PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray; PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync; PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender; PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv; PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui; PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap; PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays; PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D; PFNGLGETSHADERIVPROC glad_glGetShaderiv; PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv; PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv; PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f; PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv; PFNGLPOINTPARAMETERIPROC glad_glPointParameteri; PFNGLBLENDCOLORPROC glad_glBlendColor; PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray; PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer; PFNGLDEPTHMASKPROC glad_glDepthMask; PFNGLPOINTPARAMETERFPROC glad_glPointParameterf; PFNGLDISABLEIPROC glad_glDisablei; PFNGLGETDOUBLEVPROC glad_glGetDoublev; PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv; PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv; PFNGLSHADERSOURCEPROC glad_glShaderSource; PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer; PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D; PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv; PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv; PFNGLDRAWARRAYSPROC glad_glDrawArrays; PFNGLUNIFORM1UIPROC glad_glUniform1ui; PFNGLISPROGRAMPROC glad_glIsProgram; PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv; PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation; PFNGLGETSYNCIVPROC glad_glGetSynciv; PFNGLGETUNIFORMIVPROC glad_glGetUniformiv; PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i; PFNGLUNIFORM4IPROC glad_glUniform4i; PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d; PFNGLCLEARPROC glad_glClear; PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv; PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName; PFNGLUNIFORM2FPROC glad_glUniform2f; PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample; PFNGLACTIVETEXTUREPROC glad_glActiveTexture; PFNGLBEGINQUERYPROC glad_glBeginQuery; PFNGLUNIFORM2IVPROC glad_glUniform2iv; PFNGLBINDBUFFERPROC glad_glBindBuffer; PFNGLISENABLEDPROC glad_glIsEnabled; PFNGLSTENCILOPPROC glad_glStencilOp; PFNGLREADPIXELSPROC glad_glReadPixels; PFNGLCLEARDEPTHPROC glad_glClearDepth; PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv; PFNGLUNIFORM4FPROC glad_glUniform4f; PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D; PFNGLMAPBUFFERPROC glad_glMapBuffer; PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d; PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub; PFNGLUNIFORM3FVPROC glad_glUniform3fv; PFNGLGETUNIFORMFVPROC glad_glGetUniformfv; PFNGLBUFFERDATAPROC glad_glBufferData; PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv; PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D; PFNGLTEXIMAGE1DPROC glad_glTexImage1D; PFNGLDELETESYNCPROC glad_glDeleteSync; PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D; PFNGLGETERRORPROC glad_glGetError; PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers; PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv; PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv; PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements; PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv; PFNGLGETFLOATVPROC glad_glGetFloatv; PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D; PFNGLUNIFORM3IVPROC glad_glUniform3iv; PFNGLGETTEXIMAGEPROC glad_glGetTexImage; PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv; PFNGLUSEPROGRAMPROC glad_glUseProgram; PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv; PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv; PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i; PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers; PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer; PFNGLDRAWBUFFERSPROC glad_glDrawBuffers; PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv; PFNGLSTENCILFUNCPROC glad_glStencilFunc; PFNGLGETINTEGERVPROC glad_glGetIntegerv; PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders; PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding; PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer; PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv; PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv; PFNGLDRAWBUFFERPROC glad_glDrawBuffer; PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D; PFNGLUNIFORM1FVPROC glad_glUniform1fv; PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample; PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange; PFNGLISQUERYPROC glad_glIsQuery; PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv; PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv; PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray; PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv; PFNGLGETQUERYIVPROC glad_glGetQueryiv; PFNGLTEXIMAGE2DPROC glad_glTexImage2D; PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog; PFNGLSTENCILMASKPROC glad_glStencilMask; PFNGLUNIFORM4UIPROC glad_glUniform4ui; PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv; PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog; PFNGLISTEXTUREPROC glad_glIsTexture; PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices; PFNGLISSHADERPROC glad_glIsShader; PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource; PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv; PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv; PFNGLGETINTEGER64VPROC glad_glGetInteger64v; PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer; PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv; PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData; PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv; PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv; PFNGLENABLEPROC glad_glEnable; PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv; PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced; PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i; PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate; PFNGLDELETEQUERIESPROC glad_glDeleteQueries; PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv; PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate; PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui; PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D; PFNGLFINISHPROC glad_glFinish; PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation; PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv; PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv; PFNGLDELETESHADERPROC glad_glDeleteShader; PFNGLBLENDFUNCPROC glad_glBlendFunc; PFNGLCREATEPROGRAMPROC glad_glCreateProgram; PFNGLTEXIMAGE3DPROC glad_glTexImage3D; PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv; PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer; PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui; PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange; PFNGLVIEWPORTPROC glad_glViewport; PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv; PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d; PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings; PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f; PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv; PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex; PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex; PFNGLUNIFORM2UIPROC glad_glUniform2ui; PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset; PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv; PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i; PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s; PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample; PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv; PFNGLDEPTHRANGEPROC glad_glDepthRange; PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform; PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv; PFNGLTEXPARAMETERFPROC glad_glTexParameterf; PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi; PFNGLTEXPARAMETERIPROC glad_glTexParameteri; PFNGLFRONTFACEPROC glad_glFrontFace; PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers; PFNGLSCISSORPROC glad_glScissor; PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex; PFNGLGETBOOLEANVPROC glad_glGetBooleanv; PFNGLTEXBUFFERPROC glad_glTexBuffer; PFNGLPIXELSTOREIPROC glad_glPixelStorei; PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram; PFNGLPIXELSTOREFPROC glad_glPixelStoref; PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv; PFNGLUNIFORM3UIVPROC glad_glUniform3uiv; PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v; PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer; int GLAD_GL_ARB_debug_output; int GLAD_GL_EXT_texture_filter_anisotropic; PFNGLDEBUGMESSAGECONTROLARBPROC glad_glDebugMessageControlARB; PFNGLDEBUGMESSAGEINSERTARBPROC glad_glDebugMessageInsertARB; PFNGLDEBUGMESSAGECALLBACKARBPROC glad_glDebugMessageCallbackARB; PFNGLGETDEBUGMESSAGELOGARBPROC glad_glGetDebugMessageLogARB; static void load_GL_VERSION_1_0(GLADloadproc load) { if(!GLAD_GL_VERSION_1_0) return; glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace"); glad_glFrontFace = (PFNGLFRONTFACEPROC)load("glFrontFace"); glad_glHint = (PFNGLHINTPROC)load("glHint"); glad_glLineWidth = (PFNGLLINEWIDTHPROC)load("glLineWidth"); glad_glPointSize = (PFNGLPOINTSIZEPROC)load("glPointSize"); glad_glPolygonMode = (PFNGLPOLYGONMODEPROC)load("glPolygonMode"); glad_glScissor = (PFNGLSCISSORPROC)load("glScissor"); glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC)load("glTexParameterf"); glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC)load("glTexParameterfv"); glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC)load("glTexParameteri"); glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC)load("glTexParameteriv"); glad_glTexImage1D = (PFNGLTEXIMAGE1DPROC)load("glTexImage1D"); glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC)load("glTexImage2D"); glad_glDrawBuffer = (PFNGLDRAWBUFFERPROC)load("glDrawBuffer"); glad_glClear = (PFNGLCLEARPROC)load("glClear"); glad_glClearColor = (PFNGLCLEARCOLORPROC)load("glClearColor"); glad_glClearStencil = (PFNGLCLEARSTENCILPROC)load("glClearStencil"); glad_glClearDepth = (PFNGLCLEARDEPTHPROC)load("glClearDepth"); glad_glStencilMask = (PFNGLSTENCILMASKPROC)load("glStencilMask"); glad_glColorMask = (PFNGLCOLORMASKPROC)load("glColorMask"); glad_glDepthMask = (PFNGLDEPTHMASKPROC)load("glDepthMask"); glad_glDisable = (PFNGLDISABLEPROC)load("glDisable"); glad_glEnable = (PFNGLENABLEPROC)load("glEnable"); glad_glFinish = (PFNGLFINISHPROC)load("glFinish"); glad_glFlush = (PFNGLFLUSHPROC)load("glFlush"); glad_glBlendFunc = (PFNGLBLENDFUNCPROC)load("glBlendFunc"); glad_glLogicOp = (PFNGLLOGICOPPROC)load("glLogicOp"); glad_glStencilFunc = (PFNGLSTENCILFUNCPROC)load("glStencilFunc"); glad_glStencilOp = (PFNGLSTENCILOPPROC)load("glStencilOp"); glad_glDepthFunc = (PFNGLDEPTHFUNCPROC)load("glDepthFunc"); glad_glPixelStoref = (PFNGLPIXELSTOREFPROC)load("glPixelStoref"); glad_glPixelStorei = (PFNGLPIXELSTOREIPROC)load("glPixelStorei"); glad_glReadBuffer = (PFNGLREADBUFFERPROC)load("glReadBuffer"); glad_glReadPixels = (PFNGLREADPIXELSPROC)load("glReadPixels"); glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC)load("glGetBooleanv"); glad_glGetDoublev = (PFNGLGETDOUBLEVPROC)load("glGetDoublev"); glad_glGetError = (PFNGLGETERRORPROC)load("glGetError"); glad_glGetFloatv = (PFNGLGETFLOATVPROC)load("glGetFloatv"); glad_glGetIntegerv = (PFNGLGETINTEGERVPROC)load("glGetIntegerv"); glad_glGetString = (PFNGLGETSTRINGPROC)load("glGetString"); glad_glGetTexImage = (PFNGLGETTEXIMAGEPROC)load("glGetTexImage"); glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC)load("glGetTexParameterfv"); glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC)load("glGetTexParameteriv"); glad_glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC)load("glGetTexLevelParameterfv"); glad_glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC)load("glGetTexLevelParameteriv"); glad_glIsEnabled = (PFNGLISENABLEDPROC)load("glIsEnabled"); glad_glDepthRange = (PFNGLDEPTHRANGEPROC)load("glDepthRange"); glad_glViewport = (PFNGLVIEWPORTPROC)load("glViewport"); } static void load_GL_VERSION_1_1(GLADloadproc load) { if(!GLAD_GL_VERSION_1_1) return; glad_glDrawArrays = (PFNGLDRAWARRAYSPROC)load("glDrawArrays"); glad_glDrawElements = (PFNGLDRAWELEMENTSPROC)load("glDrawElements"); glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC)load("glPolygonOffset"); glad_glCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC)load("glCopyTexImage1D"); glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC)load("glCopyTexImage2D"); glad_glCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC)load("glCopyTexSubImage1D"); glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC)load("glCopyTexSubImage2D"); glad_glTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC)load("glTexSubImage1D"); glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC)load("glTexSubImage2D"); glad_glBindTexture = (PFNGLBINDTEXTUREPROC)load("glBindTexture"); glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC)load("glDeleteTextures"); glad_glGenTextures = (PFNGLGENTEXTURESPROC)load("glGenTextures"); glad_glIsTexture = (PFNGLISTEXTUREPROC)load("glIsTexture"); } static void load_GL_VERSION_1_2(GLADloadproc load) { if(!GLAD_GL_VERSION_1_2) return; glad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)load("glDrawRangeElements"); glad_glTexImage3D = (PFNGLTEXIMAGE3DPROC)load("glTexImage3D"); glad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)load("glTexSubImage3D"); glad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)load("glCopyTexSubImage3D"); } static void load_GL_VERSION_1_3(GLADloadproc load) { if(!GLAD_GL_VERSION_1_3) return; glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC)load("glActiveTexture"); glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)load("glSampleCoverage"); glad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)load("glCompressedTexImage3D"); glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)load("glCompressedTexImage2D"); glad_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC)load("glCompressedTexImage1D"); glad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)load("glCompressedTexSubImage3D"); glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)load("glCompressedTexSubImage2D"); glad_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)load("glCompressedTexSubImage1D"); glad_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)load("glGetCompressedTexImage"); } static void load_GL_VERSION_1_4(GLADloadproc load) { if(!GLAD_GL_VERSION_1_4) return; glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)load("glBlendFuncSeparate"); glad_glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC)load("glMultiDrawArrays"); glad_glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC)load("glMultiDrawElements"); glad_glPointParameterf = (PFNGLPOINTPARAMETERFPROC)load("glPointParameterf"); glad_glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC)load("glPointParameterfv"); glad_glPointParameteri = (PFNGLPOINTPARAMETERIPROC)load("glPointParameteri"); glad_glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC)load("glPointParameteriv"); glad_glBlendColor = (PFNGLBLENDCOLORPROC)load("glBlendColor"); glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC)load("glBlendEquation"); } static void load_GL_VERSION_1_5(GLADloadproc load) { if(!GLAD_GL_VERSION_1_5) return; glad_glGenQueries = (PFNGLGENQUERIESPROC)load("glGenQueries"); glad_glDeleteQueries = (PFNGLDELETEQUERIESPROC)load("glDeleteQueries"); glad_glIsQuery = (PFNGLISQUERYPROC)load("glIsQuery"); glad_glBeginQuery = (PFNGLBEGINQUERYPROC)load("glBeginQuery"); glad_glEndQuery = (PFNGLENDQUERYPROC)load("glEndQuery"); glad_glGetQueryiv = (PFNGLGETQUERYIVPROC)load("glGetQueryiv"); glad_glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC)load("glGetQueryObjectiv"); glad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)load("glGetQueryObjectuiv"); glad_glBindBuffer = (PFNGLBINDBUFFERPROC)load("glBindBuffer"); glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)load("glDeleteBuffers"); glad_glGenBuffers = (PFNGLGENBUFFERSPROC)load("glGenBuffers"); glad_glIsBuffer = (PFNGLISBUFFERPROC)load("glIsBuffer"); glad_glBufferData = (PFNGLBUFFERDATAPROC)load("glBufferData"); glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC)load("glBufferSubData"); glad_glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC)load("glGetBufferSubData"); glad_glMapBuffer = (PFNGLMAPBUFFERPROC)load("glMapBuffer"); glad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)load("glUnmapBuffer"); glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load("glGetBufferParameteriv"); glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)load("glGetBufferPointerv"); } static void load_GL_VERSION_2_0(GLADloadproc load) { if(!GLAD_GL_VERSION_2_0) return; glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)load("glBlendEquationSeparate"); glad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC)load("glDrawBuffers"); glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)load("glStencilOpSeparate"); glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)load("glStencilFuncSeparate"); glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC)load("glStencilMaskSeparate"); glad_glAttachShader = (PFNGLATTACHSHADERPROC)load("glAttachShader"); glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)load("glBindAttribLocation"); glad_glCompileShader = (PFNGLCOMPILESHADERPROC)load("glCompileShader"); glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC)load("glCreateProgram"); glad_glCreateShader = (PFNGLCREATESHADERPROC)load("glCreateShader"); glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC)load("glDeleteProgram"); glad_glDeleteShader = (PFNGLDELETESHADERPROC)load("glDeleteShader"); glad_glDetachShader = (PFNGLDETACHSHADERPROC)load("glDetachShader"); glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load("glDisableVertexAttribArray"); glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load("glEnableVertexAttribArray"); glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)load("glGetActiveAttrib"); glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)load("glGetActiveUniform"); glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC)load("glGetAttachedShaders"); glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)load("glGetAttribLocation"); glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC)load("glGetProgramiv"); glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)load("glGetProgramInfoLog"); glad_glGetShaderiv = (PFNGLGETSHADERIVPROC)load("glGetShaderiv"); glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)load("glGetShaderInfoLog"); glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC)load("glGetShaderSource"); glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)load("glGetUniformLocation"); glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC)load("glGetUniformfv"); glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC)load("glGetUniformiv"); glad_glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC)load("glGetVertexAttribdv"); glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC)load("glGetVertexAttribfv"); glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC)load("glGetVertexAttribiv"); glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC)load("glGetVertexAttribPointerv"); glad_glIsProgram = (PFNGLISPROGRAMPROC)load("glIsProgram"); glad_glIsShader = (PFNGLISSHADERPROC)load("glIsShader"); glad_glLinkProgram = (PFNGLLINKPROGRAMPROC)load("glLinkProgram"); glad_glShaderSource = (PFNGLSHADERSOURCEPROC)load("glShaderSource"); glad_glUseProgram = (PFNGLUSEPROGRAMPROC)load("glUseProgram"); glad_glUniform1f = (PFNGLUNIFORM1FPROC)load("glUniform1f"); glad_glUniform2f = (PFNGLUNIFORM2FPROC)load("glUniform2f"); glad_glUniform3f = (PFNGLUNIFORM3FPROC)load("glUniform3f"); glad_glUniform4f = (PFNGLUNIFORM4FPROC)load("glUniform4f"); glad_glUniform1i = (PFNGLUNIFORM1IPROC)load("glUniform1i"); glad_glUniform2i = (PFNGLUNIFORM2IPROC)load("glUniform2i"); glad_glUniform3i = (PFNGLUNIFORM3IPROC)load("glUniform3i"); glad_glUniform4i = (PFNGLUNIFORM4IPROC)load("glUniform4i"); glad_glUniform1fv = (PFNGLUNIFORM1FVPROC)load("glUniform1fv"); glad_glUniform2fv = (PFNGLUNIFORM2FVPROC)load("glUniform2fv"); glad_glUniform3fv = (PFNGLUNIFORM3FVPROC)load("glUniform3fv"); glad_glUniform4fv = (PFNGLUNIFORM4FVPROC)load("glUniform4fv"); glad_glUniform1iv = (PFNGLUNIFORM1IVPROC)load("glUniform1iv"); glad_glUniform2iv = (PFNGLUNIFORM2IVPROC)load("glUniform2iv"); glad_glUniform3iv = (PFNGLUNIFORM3IVPROC)load("glUniform3iv"); glad_glUniform4iv = (PFNGLUNIFORM4IVPROC)load("glUniform4iv"); glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)load("glUniformMatrix2fv"); glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)load("glUniformMatrix3fv"); glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)load("glUniformMatrix4fv"); glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)load("glValidateProgram"); glad_glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC)load("glVertexAttrib1d"); glad_glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC)load("glVertexAttrib1dv"); glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)load("glVertexAttrib1f"); glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC)load("glVertexAttrib1fv"); glad_glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC)load("glVertexAttrib1s"); glad_glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC)load("glVertexAttrib1sv"); glad_glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC)load("glVertexAttrib2d"); glad_glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC)load("glVertexAttrib2dv"); glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)load("glVertexAttrib2f"); glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)load("glVertexAttrib2fv"); glad_glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC)load("glVertexAttrib2s"); glad_glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC)load("glVertexAttrib2sv"); glad_glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC)load("glVertexAttrib3d"); glad_glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC)load("glVertexAttrib3dv"); glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)load("glVertexAttrib3f"); glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)load("glVertexAttrib3fv"); glad_glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC)load("glVertexAttrib3s"); glad_glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC)load("glVertexAttrib3sv"); glad_glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC)load("glVertexAttrib4Nbv"); glad_glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC)load("glVertexAttrib4Niv"); glad_glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC)load("glVertexAttrib4Nsv"); glad_glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC)load("glVertexAttrib4Nub"); glad_glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC)load("glVertexAttrib4Nubv"); glad_glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC)load("glVertexAttrib4Nuiv"); glad_glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC)load("glVertexAttrib4Nusv"); glad_glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC)load("glVertexAttrib4bv"); glad_glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC)load("glVertexAttrib4d"); glad_glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC)load("glVertexAttrib4dv"); glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)load("glVertexAttrib4f"); glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)load("glVertexAttrib4fv"); glad_glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC)load("glVertexAttrib4iv"); glad_glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC)load("glVertexAttrib4s"); glad_glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC)load("glVertexAttrib4sv"); glad_glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC)load("glVertexAttrib4ubv"); glad_glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC)load("glVertexAttrib4uiv"); glad_glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC)load("glVertexAttrib4usv"); glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load("glVertexAttribPointer"); } static void load_GL_VERSION_2_1(GLADloadproc load) { if(!GLAD_GL_VERSION_2_1) return; glad_glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC)load("glUniformMatrix2x3fv"); glad_glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC)load("glUniformMatrix3x2fv"); glad_glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC)load("glUniformMatrix2x4fv"); glad_glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC)load("glUniformMatrix4x2fv"); glad_glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC)load("glUniformMatrix3x4fv"); glad_glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC)load("glUniformMatrix4x3fv"); } static void load_GL_VERSION_3_0(GLADloadproc load) { if(!GLAD_GL_VERSION_3_0) return; glad_glColorMaski = (PFNGLCOLORMASKIPROC)load("glColorMaski"); glad_glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC)load("glGetBooleani_v"); glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load("glGetIntegeri_v"); glad_glEnablei = (PFNGLENABLEIPROC)load("glEnablei"); glad_glDisablei = (PFNGLDISABLEIPROC)load("glDisablei"); glad_glIsEnabledi = (PFNGLISENABLEDIPROC)load("glIsEnabledi"); glad_glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC)load("glBeginTransformFeedback"); glad_glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC)load("glEndTransformFeedback"); glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load("glBindBufferRange"); glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load("glBindBufferBase"); glad_glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)load("glTransformFeedbackVaryings"); glad_glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)load("glGetTransformFeedbackVarying"); glad_glClampColor = (PFNGLCLAMPCOLORPROC)load("glClampColor"); glad_glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC)load("glBeginConditionalRender"); glad_glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC)load("glEndConditionalRender"); glad_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)load("glVertexAttribIPointer"); glad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC)load("glGetVertexAttribIiv"); glad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC)load("glGetVertexAttribIuiv"); glad_glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC)load("glVertexAttribI1i"); glad_glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC)load("glVertexAttribI2i"); glad_glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC)load("glVertexAttribI3i"); glad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC)load("glVertexAttribI4i"); glad_glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC)load("glVertexAttribI1ui"); glad_glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC)load("glVertexAttribI2ui"); glad_glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC)load("glVertexAttribI3ui"); glad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC)load("glVertexAttribI4ui"); glad_glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC)load("glVertexAttribI1iv"); glad_glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC)load("glVertexAttribI2iv"); glad_glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC)load("glVertexAttribI3iv"); glad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC)load("glVertexAttribI4iv"); glad_glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC)load("glVertexAttribI1uiv"); glad_glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC)load("glVertexAttribI2uiv"); glad_glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC)load("glVertexAttribI3uiv"); glad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC)load("glVertexAttribI4uiv"); glad_glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC)load("glVertexAttribI4bv"); glad_glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC)load("glVertexAttribI4sv"); glad_glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC)load("glVertexAttribI4ubv"); glad_glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC)load("glVertexAttribI4usv"); glad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC)load("glGetUniformuiv"); glad_glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC)load("glBindFragDataLocation"); glad_glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC)load("glGetFragDataLocation"); glad_glUniform1ui = (PFNGLUNIFORM1UIPROC)load("glUniform1ui"); glad_glUniform2ui = (PFNGLUNIFORM2UIPROC)load("glUniform2ui"); glad_glUniform3ui = (PFNGLUNIFORM3UIPROC)load("glUniform3ui"); glad_glUniform4ui = (PFNGLUNIFORM4UIPROC)load("glUniform4ui"); glad_glUniform1uiv = (PFNGLUNIFORM1UIVPROC)load("glUniform1uiv"); glad_glUniform2uiv = (PFNGLUNIFORM2UIVPROC)load("glUniform2uiv"); glad_glUniform3uiv = (PFNGLUNIFORM3UIVPROC)load("glUniform3uiv"); glad_glUniform4uiv = (PFNGLUNIFORM4UIVPROC)load("glUniform4uiv"); glad_glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC)load("glTexParameterIiv"); glad_glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC)load("glTexParameterIuiv"); glad_glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC)load("glGetTexParameterIiv"); glad_glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC)load("glGetTexParameterIuiv"); glad_glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)load("glClearBufferiv"); glad_glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC)load("glClearBufferuiv"); glad_glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)load("glClearBufferfv"); glad_glClearBufferfi = (PFNGLCLEARBUFFERFIPROC)load("glClearBufferfi"); glad_glGetStringi = (PFNGLGETSTRINGIPROC)load("glGetStringi"); glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)load("glIsRenderbuffer"); glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)load("glBindRenderbuffer"); glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)load("glDeleteRenderbuffers"); glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)load("glGenRenderbuffers"); glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)load("glRenderbufferStorage"); glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)load("glGetRenderbufferParameteriv"); glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)load("glIsFramebuffer"); glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)load("glBindFramebuffer"); glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)load("glDeleteFramebuffers"); glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)load("glGenFramebuffers"); glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)load("glCheckFramebufferStatus"); glad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)load("glFramebufferTexture1D"); glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)load("glFramebufferTexture2D"); glad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)load("glFramebufferTexture3D"); glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)load("glFramebufferRenderbuffer"); glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load("glGetFramebufferAttachmentParameteriv"); glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)load("glGenerateMipmap"); glad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)load("glBlitFramebuffer"); glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)load("glRenderbufferStorageMultisample"); glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)load("glFramebufferTextureLayer"); glad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)load("glMapBufferRange"); glad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)load("glFlushMappedBufferRange"); glad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)load("glBindVertexArray"); glad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)load("glDeleteVertexArrays"); glad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)load("glGenVertexArrays"); glad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC)load("glIsVertexArray"); } static void load_GL_VERSION_3_1(GLADloadproc load) { if(!GLAD_GL_VERSION_3_1) return; glad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)load("glDrawArraysInstanced"); glad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)load("glDrawElementsInstanced"); glad_glTexBuffer = (PFNGLTEXBUFFERPROC)load("glTexBuffer"); glad_glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC)load("glPrimitiveRestartIndex"); glad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC)load("glCopyBufferSubData"); glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)load("glGetUniformIndices"); glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)load("glGetActiveUniformsiv"); glad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC)load("glGetActiveUniformName"); glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)load("glGetUniformBlockIndex"); glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)load("glGetActiveUniformBlockiv"); glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)load("glGetActiveUniformBlockName"); glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)load("glUniformBlockBinding"); glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load("glBindBufferRange"); glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load("glBindBufferBase"); glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load("glGetIntegeri_v"); } static void load_GL_VERSION_3_2(GLADloadproc load) { if(!GLAD_GL_VERSION_3_2) return; glad_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC)load("glDrawElementsBaseVertex"); glad_glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)load("glDrawRangeElementsBaseVertex"); glad_glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)load("glDrawElementsInstancedBaseVertex"); glad_glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)load("glMultiDrawElementsBaseVertex"); glad_glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC)load("glProvokingVertex"); glad_glFenceSync = (PFNGLFENCESYNCPROC)load("glFenceSync"); glad_glIsSync = (PFNGLISSYNCPROC)load("glIsSync"); glad_glDeleteSync = (PFNGLDELETESYNCPROC)load("glDeleteSync"); glad_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)load("glClientWaitSync"); glad_glWaitSync = (PFNGLWAITSYNCPROC)load("glWaitSync"); glad_glGetInteger64v = (PFNGLGETINTEGER64VPROC)load("glGetInteger64v"); glad_glGetSynciv = (PFNGLGETSYNCIVPROC)load("glGetSynciv"); glad_glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC)load("glGetInteger64i_v"); glad_glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC)load("glGetBufferParameteri64v"); glad_glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC)load("glFramebufferTexture"); glad_glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)load("glTexImage2DMultisample"); glad_glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC)load("glTexImage3DMultisample"); glad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC)load("glGetMultisamplefv"); glad_glSampleMaski = (PFNGLSAMPLEMASKIPROC)load("glSampleMaski"); } static void load_GL_ARB_debug_output(GLADloadproc load) { if(!GLAD_GL_ARB_debug_output) return; glad_glDebugMessageControlARB = (PFNGLDEBUGMESSAGECONTROLARBPROC)load("glDebugMessageControlARB"); glad_glDebugMessageInsertARB = (PFNGLDEBUGMESSAGEINSERTARBPROC)load("glDebugMessageInsertARB"); glad_glDebugMessageCallbackARB = (PFNGLDEBUGMESSAGECALLBACKARBPROC)load("glDebugMessageCallbackARB"); glad_glGetDebugMessageLogARB = (PFNGLGETDEBUGMESSAGELOGARBPROC)load("glGetDebugMessageLogARB"); } static int find_extensionsGL(void) { if (!get_exts()) return 0; GLAD_GL_ARB_debug_output = has_ext("GL_ARB_debug_output"); GLAD_GL_EXT_texture_filter_anisotropic = has_ext("GL_EXT_texture_filter_anisotropic"); free_exts(); return 1; } static void find_coreGL(void) { /* Thank you @elmindreda * https://github.com/elmindreda/greg/blob/master/templates/greg.c.in#L176 * https://github.com/glfw/glfw/blob/master/src/context.c#L36 */ int i, major, minor; const char* version; const char* prefixes[] = { "OpenGL ES-CM ", "OpenGL ES-CL ", "OpenGL ES ", NULL }; version = (const char*) glGetString(GL_VERSION); if (!version) return; for (i = 0; prefixes[i]; i++) { const size_t length = strlen(prefixes[i]); if (strncmp(version, prefixes[i], length) == 0) { version += length; break; } } /* PR #18 */ #ifdef _MSC_VER sscanf_s(version, "%d.%d", &major, &minor); #else sscanf(version, "%d.%d", &major, &minor); #endif GLVersion.major = major; GLVersion.minor = minor; max_loaded_major = major; max_loaded_minor = minor; GLAD_GL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1; GLAD_GL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1; GLAD_GL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1; GLAD_GL_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1; GLAD_GL_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1; GLAD_GL_VERSION_1_5 = (major == 1 && minor >= 5) || major > 1; GLAD_GL_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2; GLAD_GL_VERSION_2_1 = (major == 2 && minor >= 1) || major > 2; GLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3; GLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3; GLAD_GL_VERSION_3_2 = (major == 3 && minor >= 2) || major > 3; if (GLVersion.major > 3 || (GLVersion.major >= 3 && GLVersion.minor >= 2)) { max_loaded_major = 3; max_loaded_minor = 2; } } int gladLoadGLLoader(GLADloadproc load) { GLVersion.major = 0; GLVersion.minor = 0; glGetString = (PFNGLGETSTRINGPROC)load("glGetString"); if(glGetString == NULL) return 0; if(glGetString(GL_VERSION) == NULL) return 0; find_coreGL(); load_GL_VERSION_1_0(load); load_GL_VERSION_1_1(load); load_GL_VERSION_1_2(load); load_GL_VERSION_1_3(load); load_GL_VERSION_1_4(load); load_GL_VERSION_1_5(load); load_GL_VERSION_2_0(load); load_GL_VERSION_2_1(load); load_GL_VERSION_3_0(load); load_GL_VERSION_3_1(load); load_GL_VERSION_3_2(load); if (!find_extensionsGL()) return 0; load_GL_ARB_debug_output(load); return GLVersion.major != 0 || GLVersion.minor != 0; } yquake2-QUAKE2_7_10/src/client/refresh/gl3/header/000077500000000000000000000000001321245476300215425ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/refresh/gl3/header/DG_dynarr.h000066400000000000000000001052371321245476300235740ustar00rootroot00000000000000/* * A header-only typesafe dynamic array implementation for plain C, * kinda like C++ std::vector. This code is compatible with C++, but should * only be used with POD (plain old data) types, as it uses memcpy() etc * instead of copy/move construction/assignment. * It requires a new type (created with the DA_TYPEDEF(ELEMENT_TYPE, ARRAY_TYPE_NAME) * macro) for each kind of element you want to put in a dynamic array; however * the "functions" to manipulate the array are actually macros and the same * for all element types. * The array elements are accessed via dynArr.p[i] or da_get(dynArr, i) * - the latter checks whether i is a valid index and asserts if not. * * One thing to keep in mind is that, because of using macros, the arguments to * the "functions" are usually evaluated more than once, so you should avoid * putting things with side effect (like function-calls with side effects or i++) * into them. Notable exceptions are the value arguments (v) of da_push() * and da_insert(), so it's still ok to do da_push(arr, fun_with_sideffects()); * or da_insert(a, 3, x++); * * The function-like da_* macros are short aliases of dg_dynarr_* macros. * If the short names clash with anything in your code or other headers * you are using, you can, before #including this header, do * #define DG_DYNARR_NO_SHORTNAMES * and use the long dg_dynarr_* forms of the macros instead. * * Using this library in your project: * Put this file somewhere in your project. * In *one* of your .c/.cpp files, do * #define DG_DYNARR_IMPLEMENTATION * #include "DG_dynarr.h" * to create the implementation of this library in that file. * You can just #include "DG_dynarr.h" (without the #define) in other source * files to use it there. * * See below this comment block for a usage example. * * You can #define your own allocators, assertion and the amount of runtime * checking of indexes, see CONFIGURATION section in the code for more information. * * * This is heavily inspired by Sean Barrett's stretchy_buffer.h * ( see: https://github.com/nothings/stb/blob/master/stretchy_buffer.h ) * However I wanted to have a struct that holds the array pointer and the length * and capacity, so that struct always remains at the same address while the * array memory might be reallocated. * I can live with arr.p[i] instead of arr[i], but I like how he managed to use * macros to create an API that doesn't force the user to specify the stored * type over and over again, so I stole some of his tricks :-) * * This has been tested with GCC 4.8 and clang 3.8 (-std=gnu89, -std=c99 and as C++; * -std=c89 works if you convert the C++-style comments to C comments) and * Microsoft Visual Studio 6 and 2010 (32bit) and 2013 (32bit and 64bit). * I guess it works with all (recentish) C++ compilers and C compilers supporting * C99 or even C89 + C++ comments (otherwise converting the comments should help). * * (C) 2016 Daniel Gibson * * LICENSE * This software is dual-licensed to the public domain and under the following * license: you are granted a perpetual, irrevocable license to copy, modify, * publish, and distribute this file as you see fit. * No warranty implied; use at your own risk. */ #if 0 // Usage Example: #define DG_DYNARR_IMPLEMENTATION // this define is only needed in *one* .c/.cpp file! #include "DG_dynarr.h" DA_TYPEDEF(int, MyIntArrType); // creates MyIntArrType - a dynamic array for ints void printIntArr(MyIntArrType* arr, const char* name) { // note that arr is a pointer here, so use *arr in the da_*() functions. printf("%s = {", name); if(da_count(*arr) > 0) printf(" %d", arr->p[0]); for(int i=1; ip[i]); printf(" }\n"); } void myFunction() { MyIntArrType a1 = {0}; // make sure to zero out the struct // instead of = {0}; you could also call da_init(a1); da_push(a1, 42); assert(da_count(a1) == 1 && a1.p[0] == 42); int* addedElements = da_addn_uninit(a1, 3); assert(da_count(a1) == 4); for(size_t i=0; i<3; ++i) addedElements[i] = i+5; printIntArr(&a1, "a1"); // "a1 = { 42, 5, 6, 7 }" MyIntArrType a2; da_init(a2); da_addn(a2, a1.p, da_count(a1)); // copy all elements from a1 to a2 assert(da_count(a2) == 4); da_insert(a2, 1, 11); printIntArr(&a2, "a2"); // "a2 = { 42, 11, 5, 6, 7 }" da_delete(a2, 2); printIntArr(&a2, "a2"); // "a2 = { 42, 11, 6, 7 }" da_deletefast(a2, 0); printIntArr(&a2, "a2"); // "a2 = { 7, 11, 6 }" da_push(a1, 3); printIntArr(&a1, "a1"); // "a1 = { 42, 5, 6, 7, 3 }" int x=da_pop(a1); printf("x = %d\n", x); // "x = 3" printIntArr(&a1, "a1"); // "a1 = { 42, 5, 6, 7 }" da_free(a1); // make sure not to leak memory! da_free(a2); } #endif // 0 (usage example) #ifndef DG__DYNARR_H #define DG__DYNARR_H // ######### CONFIGURATION ######### // following: some #defines that you can tweak to your liking // you can reduce some overhead by defining DG_DYNARR_INDEX_CHECK_LEVEL to 2, 1 or 0 #ifndef DG_DYNARR_INDEX_CHECK_LEVEL // 0: (almost) no index checking // 1: macros "returning" something return a.p[0] or NULL if the index was invalid // 2: assertions in all macros taking indexes that make sure they're valid // 3: 1 and 2 #define DG_DYNARR_INDEX_CHECK_LEVEL 3 #endif // DG_DYNARR_INDEX_CHECK_LEVEL // you can #define your own DG_DYNARR_ASSERT(condition, msgstring) // that will be used for all assertions in this code. #ifndef DG_DYNARR_ASSERT #include #define DG_DYNARR_ASSERT(cond, msg) assert((cond) && msg) #endif // you can #define DG_DYNARR_OUT_OF_MEMORY to some code that will be executed // if allocating memory fails // it's needed only before the #define DG_DYNARR_IMPLEMENTATION #include of // this header, so the following is here only for reference and commented out /* #ifndef DG_DYNARR_OUT_OF_MEMORY #define DG_DYNARR_OUT_OF_MEMORY DG_DYNARR_ASSERT(0, "Out of Memory!"); #endif */ // By default, C's malloc(), realloc() and free() is used to allocate/free heap memory // (see beginning of "#ifdef DG_DYNARR_IMPLEMENTATION" block below). // You can #define DG_DYNARR_MALLOC, DG_DYNARR_REALLOC and DG_DYNARR_FREE yourself // to provide alternative implementations like Win32 Heap(Re)Alloc/HeapFree // it's needed only before the #define DG_DYNARR_IMPLEMENTATION #include of // this header, so the following is here only for reference and commented out /* #define DG_DYNARR_MALLOC(elemSize, numElems) malloc(elemSize*numElems) // oldNumElems is not used for C's realloc, but maybe you need it for // your allocator to copy the old elements over #define DG_DYNARR_REALLOC(ptr, elemSize, oldNumElems, newCapacity) \ realloc(ptr, elemSize*newCapacity); #define DG_DYNARR_FREE(ptr) free(ptr) */ // if you want to prepend something to the non inline (DG_DYNARR_INLINE) functions, // like "__declspec(dllexport)" or whatever, #define DG_DYNARR_DEF #ifndef DG_DYNARR_DEF // by defaults it's empty. #define DG_DYNARR_DEF #endif // some functions are inline, in case your compiler doesn't like "static inline" // but wants "__inline__" or something instead, #define DG_DYNARR_INLINE accordingly. #ifndef DG_DYNARR_INLINE // for pre-C99 compilers you might have to use something compiler-specific (or maybe only "static") #ifdef _MSC_VER #define DG_DYNARR_INLINE static __inline #else #define DG_DYNARR_INLINE static inline #endif #endif // ############### Short da_* aliases for the long names ############### #ifndef DG_DYNARR_NO_SHORTNAMES // this macro is used to create an array type (struct) for elements of TYPE // use like DA_TYPEDEF(int, MyIntArrType); MyIntArrType ia = {0}; da_push(ia, 42); ... #define DA_TYPEDEF(TYPE, NewArrayTypeName) \ DG_DYNARR_TYPEDEF(TYPE, NewArrayTypeName) // makes sure the array is initialized and can be used. // either do YourArray arr = {0}; or YourArray arr; da_init(arr); #define da_init(a) \ dg_dynarr_init(a) /* * This allows you to provide an external buffer that'll be used as long as it's big enough * once you add more elements than buf can hold, fresh memory will be allocated on the heap * Use like: * DA_TYPEDEF(double, MyDoubleArrType); * MyDoubleArrType arr; * double buf[8]; * dg_dynarr_init_external(arr, buf, 8); * dg_dynarr_push(arr, 1.23); * ... */ #define da_init_external(a, buf, buf_cap) \ dg_dynarr_init_external(a, buf, buf_cap) // use this to free the memory allocated by dg_dynarr once you don't need the array anymore // Note: it is safe to add new elements to the array after da_free() // it will allocate new memory, just like it would directly after da_init() #define da_free(a) \ dg_dynarr_free(a) // add an element to the array (appended at the end) #define da_push(a, v) \ dg_dynarr_push(a, v) // add an element to the array (appended at the end) // does the same as push, just for consistency with addn (like insert and insertn) #define da_add(a, v) \ dg_dynarr_add(a, v) // append n elements to a and initialize them from array vals, doesn't return anything // ! vals (and all other args) are evaluated multiple times ! #define da_addn(a, vals, n) \ dg_dynarr_addn(a, vals, n) // add n elements to the end of the array and zeroes them with memset() // returns pointer to first added element, NULL if out of memory (array is empty then) #define da_addn_zeroed(a, n) \ dg_dynarr_addn_zeroed(a, n) // add n elements to the end of the array, will remain uninitialized // returns pointer to first added element, NULL if out of memory (array is empty then) #define da_addn_uninit(a, n) \ dg_dynarr_addn_uninit(a, n) // insert a single value v at index idx #define da_insert(a, idx, v) \ dg_dynarr_insert(a, idx, v) // insert n elements into a at idx, initialize them from array vals // doesn't return anything // ! vals (and all other args) is evaluated multiple times ! #define da_insertn(a, idx, vals, n) \ dg_dynarr_insertn(a, idx, vals, n) // insert n elements into a at idx and zeroe them with memset() // returns pointer to first inserted element or NULL if out of memory #define da_insertn_zeroed(a, idx, n) \ dg_dynarr_insertn_zeroed(a, idx, n) // insert n uninitialized elements into a at idx; // returns pointer to first inserted element or NULL if out of memory #define da_insertn_uninit(a, idx, n) \ dg_dynarr_insertn_uninit(a, idx, n) // set a single value v at index idx - like "a.p[idx] = v;" but with checks (unless disabled) #define da_set(a, idx, v) \ dg_dynarr_set(a, idx, v) // overwrite n elements of a, starting at idx, with values from array vals // doesn't return anything // ! vals (and all other args) is evaluated multiple times ! #define da_setn(a, idx, vals, n) \ dg_dynarr_setn(a, idx, vals, n) // delete the element at idx, moving all following elements (=> keeps order) #define da_delete(a, idx) \ dg_dynarr_delete(a, idx) // delete n elements starting at idx, moving all following elements (=> keeps order) #define da_deleten(a, idx, n) \ dg_dynarr_deleten(a, idx, n) // delete the element at idx, move the last element there (=> doesn't keep order) #define da_deletefast(a, idx) \ dg_dynarr_deletefast(a, idx) // delete n elements starting at idx, move the last n elements there (=> doesn't keep order) #define da_deletenfast(a, idx, n) \ dg_dynarr_deletenfast(a, idx, n) // removes all elements from the array, but does not free the buffer // (if you want to free the buffer too, just use da_free()) #define da_clear(a) \ dg_dynarr_clear(a) // sets the logical number of elements in the array // if cnt > dg_dynarr_count(a), the logical count will be increased accordingly // and the new elements will be uninitialized #define da_setcount(a, cnt) \ dg_dynarr_setcount(a, cnt) // make sure the array can store cap elements without reallocating // logical count remains unchanged #define da_reserve(a, cap) \ dg_dynarr_reserve(a, cap) // this makes sure a only uses as much memory as for its elements // => maybe useful if a used to contain a huge amount of elements, // but you deleted most of them and want to free some memory // Note however that this implies an allocation and copying the remaining // elements, so only do this if it frees enough memory to be worthwhile! #define da_shrink_to_fit(a) \ dg_dynarr_shrink_to_fit(a) // removes and returns the last element of the array #define da_pop(a) \ dg_dynarr_pop(a) // returns the last element of the array #define da_last(a) \ dg_dynarr_last(a) // returns the pointer *to* the last element of the array // (in contrast to dg_dynarr_end() which returns a pointer *after* the last element) // returns NULL if array is empty #define da_lastptr(a) \ dg_dynarr_lastptr(a) // get element at index idx (like a.p[idx]), but with checks // (unless you disabled them with #define DG_DYNARR_INDEX_CHECK_LEVEL 0) #define da_get(a, idx) \ dg_dynarr_get(a,idx) // get pointer to element at index idx (like &a.p[idx]), but with checks // and it returns NULL if idx is invalid #define da_getptr(a, idx) \ dg_dynarr_getptr(a, idx) // returns a pointer to the first element of the array // (together with dg_dynarr_end() you can do C++-style iterating) #define da_begin(a) \ dg_dynarr_begin(a) // returns a pointer to the past-the-end element of the array // Allows C++-style iterating, in case you're into that kind of thing: // for(T *it=da_begin(a), *end=da_end(a); it!=end; ++it) foo(*it); // (see da_lastptr() to get a pointer *to* the last element) #define da_end(a) \ dg_dynarr_end(a) // returns (logical) number of elements currently in the array #define da_count(a) \ dg_dynarr_count(a) // get the current reserved capacity of the array #define da_capacity(a) \ dg_dynarr_capacity(a) // returns 1 if the array is empty, else 0 #define da_empty(a) \ dg_dynarr_empty(a) // returns 1 if the last (re)allocation when inserting failed (Out Of Memory) // or if the array has never allocated any memory yet, else 0 // deleting the contents when growing fails instead of keeping old may seem // a bit uncool, but it's simple and OOM should rarely happen on modern systems // anyway - after all you need to deplete both RAM and swap/pagefile.sys #define da_oom(a) \ dg_dynarr_oom(a) // sort a using the given qsort()-comparator cmp // (just a slim wrapper around qsort()) #define da_sort(a, cmp) \ dg_dynarr_sort(a, cmp) #endif // DG_DYNARR_NO_SHORTNAMES // ######### Implementation of the actual macros (using the long names) ########## // use like DG_DYNARR_TYPEDEF(int, MyIntArrType); MyIntArrType ia = {0}; dg_dynarr_push(ia, 42); ... #define DG_DYNARR_TYPEDEF(TYPE, NewArrayTypeName) \ typedef struct { TYPE* p; dg__dynarr_md md; } NewArrayTypeName; // makes sure the array is initialized and can be used. // either do YourArray arr = {0}; or YourArray arr; dg_dynarr_init(arr); #define dg_dynarr_init(a) \ dg__dynarr_init((void**)&(a).p, &(a).md, NULL, 0) // this allows you to provide an external buffer that'll be used as long as it's big enough // once you add more elements than buf can hold, fresh memory will be allocated on the heap #define dg_dynarr_init_external(a, buf, buf_cap) \ dg__dynarr_init((void**)&(a).p, &(a).md, (buf), (buf_cap)) // use this to free the memory allocated by dg_dynarr // Note: it is safe to add new elements to the array after dg_dynarr_free() // it will allocate new memory, just like it would directly after dg_dynarr_init() #define dg_dynarr_free(a) \ dg__dynarr_free((void**)&(a).p, &(a).md) // add an element to the array (appended at the end) #define dg_dynarr_push(a, v) \ (dg__dynarr_maybegrowadd(dg__dynarr_unp(a), 1) ? (((a).p[(a).md.cnt++] = (v)),0) : 0) // add an element to the array (appended at the end) // does the same as push, just for consistency with addn (like insert and insertn) #define dg_dynarr_add(a, v) \ dg_dynarr_push((a), (v)) // append n elements to a and initialize them from array vals, doesn't return anything // ! vals (and all other args) are evaluated multiple times ! #define dg_dynarr_addn(a, vals, n) do { \ DG_DYNARR_ASSERT((vals)!=NULL, "Don't pass NULL als vals to dg_dynarr_addn!"); \ if((vals)!=NULL && dg__dynarr_add(dg__dynarr_unp(a), n, 0)) { \ size_t i_=(a).md.cnt-(n), v_=0; \ while(i_<(a).md.cnt) (a).p[i_++]=(vals)[v_++]; \ } } DG__DYNARR_WHILE0 // add n elements to the end of the array and zeroe them with memset() // returns pointer to first added element, NULL if out of memory (array is empty then) #define dg_dynarr_addn_zeroed(a, n) \ (dg__dynarr_add(dg__dynarr_unp(a), (n), 1) ? &(a).p[(a).md.cnt-(size_t)(n)] : NULL) // add n elements to the end of the array, which are uninitialized // returns pointer to first added element, NULL if out of memory (array is empty then) #define dg_dynarr_addn_uninit(a, n) \ (dg__dynarr_add(dg__dynarr_unp(a), (n), 0) ? &(a).p[(a).md.cnt-(size_t)(n)] : NULL) // insert a single value v at index idx #define dg_dynarr_insert(a, idx, v) \ (dg__dynarr_checkidxle((a),(idx)), \ dg__dynarr_insert(dg__dynarr_unp(a), (idx), 1, 0), \ (a).p[dg__dynarr_idx((a).md, (idx))] = (v)) // insert n elements into a at idx, initialize them from array vals // doesn't return anything // ! vals (and all other args) is evaluated multiple times ! #define dg_dynarr_insertn(a, idx, vals, n) do { \ DG_DYNARR_ASSERT((vals)!=NULL, "Don't pass NULL as vals to dg_dynarr_insertn!"); \ dg__dynarr_checkidxle((a),(idx)); \ if((vals)!=NULL && dg__dynarr_insert(dg__dynarr_unp(a), (idx), (n), 0)){ \ size_t i_=(idx), v_=0, e_=(idx)+(n); \ while(i_ < e_) (a).p[i_++] = (vals)[v_++]; \ }} DG__DYNARR_WHILE0 // insert n elements into a at idx and zeroe them with memset() // returns pointer to first inserted element or NULL if out of memory #define dg_dynarr_insertn_zeroed(a, idx, n) \ (dg__dynarr_checkidxle((a),(idx)), \ dg__dynarr_insert(dg__dynarr_unp(a), (idx), (n), 1) \ ? &(a).p[dg__dynarr_idx((a).md, (idx))] : NULL) // insert n uninitialized elements into a at idx; // returns pointer to first inserted element or NULL if out of memory #define dg_dynarr_insertn_uninit(a, idx, n) \ (dg__dynarr_checkidxle((a),(idx)), \ dg__dynarr_insert(dg__dynarr_unp(a), idx, n, 0) \ ? &(a).p[dg__dynarr_idx((a).md, (idx))] : NULL) // set a single value v at index idx - like "a.p[idx] = v;" but with checks (unless disabled) #define dg_dynarr_set(a, idx, v) \ (dg__dynarr_checkidx((a),(idx)), \ (a).p[dg__dynarr_idx((a).md, (idx))] = (v)) // overwrite n elements of a, starting at idx, with values from array vals // doesn't return anything // ! vals (and all other args) is evaluated multiple times ! #define dg_dynarr_setn(a, idx, vals, n) do { \ DG_DYNARR_ASSERT((vals)!=NULL, "Don't pass NULL as vals to dg_dynarr_setn!"); \ size_t idx_=(idx); size_t end_=idx_+(size_t)n; \ dg__dynarr_checkidx((a),idx_); dg__dynarr_checkidx((a),end_-1); \ if((vals)!=NULL && idx_ < (a).md.cnt && end_ <= (a).md.cnt) { \ size_t v_=0; \ while(idx_ < end_) (a).p[idx_++] = (vals)[v_++]; \ }} DG__DYNARR_WHILE0 // delete the element at idx, moving all following elements (=> keeps order) #define dg_dynarr_delete(a, idx) \ (dg__dynarr_checkidx((a),(idx)), dg__dynarr_delete(dg__dynarr_unp(a), (idx), 1)) // delete n elements starting at idx, moving all following elements (=> keeps order) #define dg_dynarr_deleten(a, idx, n) \ (dg__dynarr_checkidx((a),(idx)), dg__dynarr_delete(dg__dynarr_unp(a), (idx), (n))) // TODO: check whether idx+n < count? // delete the element at idx, move the last element there (=> doesn't keep order) #define dg_dynarr_deletefast(a, idx) \ (dg__dynarr_checkidx((a),(idx)), dg__dynarr_deletefast(dg__dynarr_unp(a), (idx), 1)) // delete n elements starting at idx, move the last n elements there (=> doesn't keep order) #define dg_dynarr_deletenfast(a, idx, n) \ (dg__dynarr_checkidx((a),(idx)), dg__dynarr_deletefast(dg__dynarr_unp(a), idx, n)) // TODO: check whether idx+n < count? // removes all elements from the array, but does not free the buffer // (if you want to free the buffer too, just use dg_dynarr_free()) #define dg_dynarr_clear(a) \ ((a).md.cnt=0) // sets the logical number of elements in the array // if cnt > dg_dynarr_count(a), the logical count will be increased accordingly // and the new elements will be uninitialized #define dg_dynarr_setcount(a, n) \ (dg__dynarr_maybegrow(dg__dynarr_unp(a), (n)) ? ((a).md.cnt = (n)) : 0) // make sure the array can store cap elements without reallocating // logical count remains unchanged #define dg_dynarr_reserve(a, cap) \ dg__dynarr_maybegrow(dg__dynarr_unp(a), (cap)) // this makes sure a only uses as much memory as for its elements // => maybe useful if a used to contain a huge amount of elements, // but you deleted most of them and want to free some memory // Note however that this implies an allocation and copying the remaining // elements, so only do this if it frees enough memory to be worthwhile! #define dg_dynarr_shrink_to_fit(a) \ dg__dynarr_shrink_to_fit(dg__dynarr_unp(a)) #if (DG_DYNARR_INDEX_CHECK_LEVEL == 1) || (DG_DYNARR_INDEX_CHECK_LEVEL == 3) // removes and returns the last element of the array #define dg_dynarr_pop(a) \ (dg__dynarr_check_notempty((a), "Don't pop an empty array!"), \ (a).p[((a).md.cnt > 0) ? (--(a).md.cnt) : 0]) // returns the last element of the array #define dg_dynarr_last(a) \ (dg__dynarr_check_notempty((a), "Don't call da_last() on an empty array!"), \ (a).p[((a).md.cnt > 0) ? ((a).md.cnt-1) : 0]) #elif (DG_DYNARR_INDEX_CHECK_LEVEL == 0) || (DG_DYNARR_INDEX_CHECK_LEVEL == 2) // removes and returns the last element of the array #define dg_dynarr_pop(a) \ (dg__dynarr_check_notempty((a), "Don't pop an empty array!"), \ (a).p[--(a).md.cnt]) // returns the last element of the array #define dg_dynarr_last(a) \ (dg__dynarr_check_notempty((a), "Don't call da_last() on an empty array!"), \ (a).p[(a).md.cnt-1]) #else // invalid DG_DYNARR_INDEX_CHECK_LEVEL #error Invalid index check level DG_DYNARR_INDEX_CHECK_LEVEL (must be 0-3) ! #endif // DG_DYNARR_INDEX_CHECK_LEVEL // returns the pointer *to* the last element of the array // (in contrast to dg_dynarr_end() which returns a pointer *after* the last element) // returns NULL if array is empty #define dg_dynarr_lastptr(a) \ (((a).md.cnt > 0) ? ((a).p + (a).md.cnt - 1) : NULL) // get element at index idx (like a.p[idx]), but with checks // (unless you disabled them with #define DG_DYNARR_INDEX_CHECK_LEVEL 0) #define dg_dynarr_get(a, idx) \ (dg__dynarr_checkidx((a),(idx)), (a).p[dg__dynarr_idx((a).md, (idx))]) // get pointer to element at index idx (like &a.p[idx]), but with checks // (unless you disabled them with #define DG_DYNARR_INDEX_CHECK_LEVEL 0) // if index-checks are disabled, it returns NULL on invalid index (else it asserts() before returning) #define dg_dynarr_getptr(a, idx) \ (dg__dynarr_checkidx((a),(idx)), \ ((size_t)(idx) < (a).md.cnt) ? ((a).p+(size_t)(idx)) : NULL) // returns a pointer to the first element of the array // (together with dg_dynarr_end() you can do C++-style iterating) #define dg_dynarr_begin(a) \ ((a).p) // returns a pointer to the past-the-end element of the array // Allows C++-style iterating, in case you're into that kind of thing: // for(T *it=dg_dynarr_begin(a), *end=dg_dynarr_end(a); it!=end; ++it) foo(*it); // (see dg_dynarr_lastptr() to get a pointer *to* the last element) #define dg_dynarr_end(a) \ ((a).p + (a).md.cnt) // returns (logical) number of elements currently in the array #define dg_dynarr_count(a) \ ((a).md.cnt) // get the current reserved capacity of the array #define dg_dynarr_capacity(a) \ ((a).md.cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB) // returns 1 if the array is empty, else 0 #define dg_dynarr_empty(a) \ ((a).md.cnt == 0) // returns 1 if the last (re)allocation when inserting failed (Out Of Memory) // or if the array has never allocated any memory yet, else 0 // deleting the contents when growing fails instead of keeping old may seem // a bit uncool, but it's simple and OOM should rarely happen on modern systems // anyway - after all you need to deplete both RAM and swap/pagefile.sys // or deplete the address space, which /might/ happen with 32bit applications // but probably not with 64bit (at least in the foreseeable future) #define dg_dynarr_oom(a) \ ((a).md.cap == 0) // sort a using the given qsort()-comparator cmp // (just a slim wrapper around qsort()) #define dg_dynarr_sort(a, cmp) \ qsort((a).p, (a).md.cnt, sizeof((a).p[0]), (cmp)) // ######### Implementation-Details that are not part of the API ########## #include // size_t, malloc(), free(), realloc() #include // memset(), memcpy(), memmove() #ifdef __cplusplus extern "C" { #endif typedef struct { size_t cnt; // logical number of elements size_t cap; // cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB is actual capacity (in elements, *not* bytes!) // if(cap & DG__DYNARR_SIZE_T_MSB) the current memory is not allocated by dg_dynarr, // but was set with dg_dynarr_init_external() // that's handy to give an array a base-element storage on the stack, for example // TODO: alternatively, we could introduce a flag field to this struct and use that, // so we don't have to calculate & everytime cap is needed } dg__dynarr_md; // I used to have the following in an enum, but MSVC assumes enums are always 32bit ints static const size_t DG__DYNARR_SIZE_T_MSB = ((size_t)1) << (sizeof(size_t)*8 - 1); static const size_t DG__DYNARR_SIZE_T_ALL_BUT_MSB = (((size_t)1) << (sizeof(size_t)*8 - 1))-1; // "unpack" the elements of an array struct for use with helper functions // (to void** arr, dg__dynarr_md* md, size_t itemsize) #define dg__dynarr_unp(a) \ (void**)&(a).p, &(a).md, sizeof((a).p[0]) // MSVC warns about "conditional expression is constant" when using the // do { ... } while(0) idiom in macros.. #ifdef _MSC_VER #if _MSC_VER >= 1400 // MSVC 2005 and newer // people claim MSVC 2005 and newer support __pragma, even though it's only documented // for 2008+ (https://msdn.microsoft.com/en-us/library/d9x1s805%28v=vs.90%29.aspx) // the following workaround is based on // http://cnicholson.net/2009/03/stupid-c-tricks-dowhile0-and-c4127/ #define DG__DYNARR_WHILE0 \ __pragma(warning(push)) \ __pragma(warning(disable:4127)) \ while(0) \ __pragma(warning(pop)) #else // older MSVC versions don't support __pragma - I heard this helps for them #define DG__DYNARR_WHILE0 while(0,0) #endif #else // other compilers #define DG__DYNARR_WHILE0 while(0) #endif // _MSC_VER #if (DG_DYNARR_INDEX_CHECK_LEVEL == 2) || (DG_DYNARR_INDEX_CHECK_LEVEL == 3) #define dg__dynarr_checkidx(a,i) \ DG_DYNARR_ASSERT((size_t)i < a.md.cnt, "index out of bounds!") // special case for insert operations: == cnt is also ok, insert will append then #define dg__dynarr_checkidxle(a,i) \ DG_DYNARR_ASSERT((size_t)i <= a.md.cnt, "index out of bounds!") #define dg__dynarr_check_notempty(a, msg) \ DG_DYNARR_ASSERT(a.md.cnt > 0, msg) #elif (DG_DYNARR_INDEX_CHECK_LEVEL == 0) || (DG_DYNARR_INDEX_CHECK_LEVEL == 1) // no assertions that check if index is valid #define dg__dynarr_checkidx(a,i) (void)0 #define dg__dynarr_checkidxle(a,i) (void)0 #define dg__dynarr_check_notempty(a, msg) (void)0 #else // invalid DG_DYNARR_INDEX_CHECK_LEVEL #error Invalid index check level DG_DYNARR_INDEX_CHECK_LEVEL (must be 0-3) ! #endif // DG_DYNARR_INDEX_CHECK_LEVEL #if (DG_DYNARR_INDEX_CHECK_LEVEL == 1) || (DG_DYNARR_INDEX_CHECK_LEVEL == 3) // the given index, if valid, else 0 #define dg__dynarr_idx(md,i) \ (((size_t)(i) < md.cnt) ? (size_t)(i) : 0) #elif (DG_DYNARR_INDEX_CHECK_LEVEL == 0) || (DG_DYNARR_INDEX_CHECK_LEVEL == 2) // don't check and default to 0 if invalid, but just use the given value #define dg__dynarr_idx(md,i) (size_t)(i) #else // invalid DG_DYNARR_INDEX_CHECK_LEVEL #error Invalid index check level DG_DYNARR_INDEX_CHECK_LEVEL (must be 0-3) ! #endif // DG_DYNARR_INDEX_CHECK_LEVEL // the functions allocating/freeing memory are not implemented inline, but // in the #ifdef DG_DYNARR_IMPLEMENTATION section // one reason is that dg__dynarr_grow has the most code in it, the other is // that windows has weird per-dll heaps so free() or realloc() should be // called from code in the same dll that allocated the memory - these kind // of wrapper functions that end up compiled into the exe or *one* dll // (instead of inline functions compiled into everything) should ensure that. DG_DYNARR_DEF void dg__dynarr_free(void** p, dg__dynarr_md* md); DG_DYNARR_DEF void dg__dynarr_shrink_to_fit(void** arr, dg__dynarr_md* md, size_t itemsize); // grow array to have enough space for at least min_needed elements // if it fails (OOM), the array will be deleted, a.p will be NULL, a.md.cap and a.md.cnt will be 0 // and the functions returns 0; else (on success) it returns 1 DG_DYNARR_DEF int dg__dynarr_grow(void** arr, dg__dynarr_md* md, size_t itemsize, size_t min_needed); // the following functions are implemented inline, because they're quite short // and mosty implemented in functions so the macros don't get too ugly DG_DYNARR_INLINE void dg__dynarr_init(void** p, dg__dynarr_md* md, void* buf, size_t buf_cap) { *p = buf; md->cnt = 0; if(buf == NULL) md->cap = 0; else md->cap = (DG__DYNARR_SIZE_T_MSB | buf_cap); } DG_DYNARR_INLINE int dg__dynarr_maybegrow(void** arr, dg__dynarr_md* md, size_t itemsize, size_t min_needed) { if((md->cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB) >= min_needed) return 1; else return dg__dynarr_grow(arr, md, itemsize, min_needed); } DG_DYNARR_INLINE int dg__dynarr_maybegrowadd(void** arr, dg__dynarr_md* md, size_t itemsize, size_t num_add) { size_t min_needed = md->cnt+num_add; if((md->cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB) >= min_needed) return 1; else return dg__dynarr_grow(arr, md, itemsize, min_needed); } DG_DYNARR_INLINE int dg__dynarr_insert(void** arr, dg__dynarr_md* md, size_t itemsize, size_t idx, size_t n, int init0) { // allow idx == md->cnt to append size_t oldCount = md->cnt; size_t newCount = oldCount+n; if(idx <= oldCount && dg__dynarr_maybegrow(arr, md, itemsize, newCount)) { unsigned char* p = (unsigned char*)*arr; // *arr might have changed in dg__dynarr_grow()! // move all existing items after a[idx] to a[idx+n] if(idx < oldCount) memmove(p+(idx+n)*itemsize, p+idx*itemsize, itemsize*(oldCount - idx)); // if the memory is supposed to be zeroed, do that if(init0) memset(p+idx*itemsize, 0, n*itemsize); md->cnt = newCount; return 1; } return 0; } DG_DYNARR_INLINE int dg__dynarr_add(void** arr, dg__dynarr_md* md, size_t itemsize, size_t n, int init0) { size_t cnt = md->cnt; if(dg__dynarr_maybegrow(arr, md, itemsize, cnt+n)) { unsigned char* p = (unsigned char*)*arr; // *arr might have changed in dg__dynarr_grow()! // if the memory is supposed to be zeroed, do that if(init0) memset(p+cnt*itemsize, 0, n*itemsize); md->cnt += n; return 1; } return 0; } DG_DYNARR_INLINE void dg__dynarr_delete(void** arr, dg__dynarr_md* md, size_t itemsize, size_t idx, size_t n) { size_t cnt = md->cnt; if(idx < cnt) { if(idx+n >= cnt) md->cnt = idx; // removing last element(s) => just reduce count else { unsigned char* p = (unsigned char*)*arr; // move all items following a[idx+n] to a[idx] memmove(p+itemsize*idx, p+itemsize*(idx+n), itemsize*(cnt - (idx+n))); md->cnt -= n; } } } DG_DYNARR_INLINE void dg__dynarr_deletefast(void** arr, dg__dynarr_md* md, size_t itemsize, size_t idx, size_t n) { size_t cnt = md->cnt; if(idx < cnt) { if(idx+n >= cnt) md->cnt = idx; // removing last element(s) => just reduce count else { unsigned char* p = (unsigned char*)*arr; // copy the last n items to a[idx] - but handle the case that // the array has less than n elements left after the deleted elements size_t numItemsAfterDeleted = cnt - (idx+n); size_t m = (n < numItemsAfterDeleted) ? n : numItemsAfterDeleted; memcpy(p+itemsize*idx, p+itemsize*(cnt - m), itemsize*m); md->cnt -= n; } } } #ifdef __cplusplus } // extern "C" #endif #endif // DG__DYNARR_H // ############## Implementation of non-inline functions ############## #ifdef DG_DYNARR_IMPLEMENTATION // by default, C's malloc(), realloc() and free() is used to allocate/free heap memory. // you can #define DG_DYNARR_MALLOC, DG_DYNARR_REALLOC and DG_DYNARR_FREE // to provide alternative implementations like Win32 Heap(Re)Alloc/HeapFree // #ifndef DG_DYNARR_MALLOC #define DG_DYNARR_MALLOC(elemSize, numElems) malloc(elemSize*numElems) // oldNumElems is not used here, but maybe you need it for your allocator // to copy the old elements over #define DG_DYNARR_REALLOC(ptr, elemSize, oldNumElems, newCapacity) \ realloc(ptr, elemSize*newCapacity); #define DG_DYNARR_FREE(ptr) free(ptr) #endif // you can #define DG_DYNARR_OUT_OF_MEMORY to some code that will be executed // if allocating memory fails #ifndef DG_DYNARR_OUT_OF_MEMORY #define DG_DYNARR_OUT_OF_MEMORY DG_DYNARR_ASSERT(0, "Out of Memory!"); #endif #ifdef __cplusplus extern "C" { #endif DG_DYNARR_DEF void dg__dynarr_free(void** p, dg__dynarr_md* md) { // only free memory if it doesn't point to external memory if(!(md->cap & DG__DYNARR_SIZE_T_MSB)) { DG_DYNARR_FREE(*p); *p = NULL; md->cap = 0; } md->cnt = 0; } DG_DYNARR_DEF int dg__dynarr_grow(void** arr, dg__dynarr_md* md, size_t itemsize, size_t min_needed) { size_t cap = md->cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB; DG_DYNARR_ASSERT(min_needed > cap, "dg__dynarr_grow() should only be called if storage actually needs to grow!"); if(min_needed < DG__DYNARR_SIZE_T_MSB) { size_t newcap = (cap > 4) ? (2*cap) : 8; // allocate for at least 8 elements // make sure not to set DG__DYNARR_SIZE_T_MSB (unlikely anyway) if(newcap >= DG__DYNARR_SIZE_T_MSB) newcap = DG__DYNARR_SIZE_T_MSB-1; if(min_needed > newcap) newcap = min_needed; // the memory was allocated externally, don't free it, just copy contents if(md->cap & DG__DYNARR_SIZE_T_MSB) { void* p = DG_DYNARR_MALLOC(itemsize, newcap); if(p != NULL) memcpy(p, *arr, itemsize*md->cnt); *arr = p; } else { void* p = DG_DYNARR_REALLOC(*arr, itemsize, md->cnt, newcap); if(p == NULL) DG_DYNARR_FREE(*arr); // realloc failed, at least don't leak memory *arr = p; } // TODO: handle OOM by setting highest bit of count and keeping old data? if(*arr) md->cap = newcap; else { md->cap = 0; md->cnt = 0; DG_DYNARR_OUT_OF_MEMORY ; return 0; } return 1; } DG_DYNARR_ASSERT(min_needed < DG__DYNARR_SIZE_T_MSB, "Arrays must stay below SIZE_T_MAX/2 elements!"); return 0; } DG_DYNARR_DEF void dg__dynarr_shrink_to_fit(void** arr, dg__dynarr_md* md, size_t itemsize) { // only do this if we allocated the memory ourselves if(!(md->cap & DG__DYNARR_SIZE_T_MSB)) { size_t cnt = md->cnt; if(cnt == 0) dg__dynarr_free(arr, md); else if((md->cap & DG__DYNARR_SIZE_T_ALL_BUT_MSB) > cnt) { void* p = DG_DYNARR_MALLOC(itemsize, cnt); if(p != NULL) { memcpy(p, *arr, cnt*itemsize); md->cap = cnt; DG_DYNARR_FREE(*arr); *arr = p; } } } } #ifdef __cplusplus } // extern "C" #endif #endif // DG_DYNARR_IMPLEMENTATION yquake2-QUAKE2_7_10/src/client/refresh/gl3/header/HandmadeMath.h000066400000000000000000001645461321245476300242460ustar00rootroot00000000000000/* HandmadeMath.h v1.1.2 This is a single header file with a bunch of useful functions for basic game math operations. ========================================================================== You MUST #define HANDMADE_MATH_IMPLEMENTATION in EXACTLY one C or C++ file that includes this header, BEFORE the include, like this: #define HANDMADE_MATH_IMPLEMENTATION #include "HandmadeMath.h" All other files should just #include "HandmadeMath.h" without the #define. ========================================================================== For overloaded and operator overloaded versions of the base C functions, you MUST #define HANDMADE_MATH_CPP_MODE in EXACTLY one C or C++ file that includes this header, BEFORE the include, like this: #define HANDMADE_MATH_IMPLEMENTATION #define HANDMADE_MATH_CPP_MODE #include "HandmadeMath.h" All other files should just #include "HandmadeMath.h" without the #define. ========================================================================== To disable SSE intrinsics, you MUST #define HANDMADE_MATH_NO_SSE in EXACTLY one C or C++ file that includes this header, BEFORE the include, like this: #define HANDMADE_MATH_IMPLEMENTATION #define HANDMADE_MATH_CPP_MODE #define HANDMADE_MATH_NO_SSE #include "HandmadeMath.h" or #define HANDMADE_MATH_IMPLEMENTATION #define HANDMADE_MATH_NO_SSE #include "HandmadeMath.h" ========================================================================== To disable inlining functions, you MUST #define HANDMADE_MATH_NO_INLINE in EXACTLY one C or C++ file that includes this header, BEFORE the include, like this: #define HANDMADE_MATH_IMPLEMENTATION #define HANDMADE_MATH_CPP_MODE #define HANDMADE_MATH_NO_INLINE #include "HandmadeMath.h" All other files should just #include "HandmadeMath.h" without the #define. ========================================================================== To Disable the CRT, you MUST #define HMM_SINF MySinF #define HMM_COSF MyCosF #define HMM_TANF MyTanF #define HMM_EXPF MyExpF #define HMM_LOGF MyLogF #define HMM_ACOSF MyACosF #define HMM_ATANF MyATanF #define HMM_ATAN2F MYATan2F Provide your own implementations of SinF, CosF, TanF, ACosF, ATanF, ATan2F, ExpF and LogF in EXACTLY one C or C++ file that includes this header, BEFORE the include, like this: #define HMM_SINF MySinF #define HMM_COSF MyCosF #define HMM_TANF MyTanF #define HMM_EXPF MyExpF #define HMM_LOGF MyLogF #define HMM_ACOSF MyACosF #define HMM_ATANF MyATanF #define HMM_ATAN2F MyATan2F #define HANDMADE_MATH_IMPLEMENTATION #define HANDMADE_MATH_CPP_MODE #include "HandmadeMath.h" If you do not define all five of these, HandmadeMath.h will use the versions of these functions that are provided by the CRT. ========================================================================== Version History: 0.2 (*) Updated documentation (*) Better C compliance (*) Prefix all handmade math functions (*) Better operator overloading 0.2a (*) Prefixed Macros 0.2b (*) Disabled warning 4201 on MSVC as it is legal is C11 (*) Removed the f at the end of HMM_PI to get 64bit precision 0.3 (*) Added +=, -=, *=, /= for hmm_vec2, hmm_vec3, hmm_vec4 0.4 (*) SSE Optimized HMM_SqrtF (*) SSE Optimized HMM_RSqrtF (*) Removed CRT 0.5 (*) Added scalar multiplication and division for vectors and matrices (*) Added matrix subtraction and += for hmm_mat4 (*) Reconciled all headers and implementations (*) Tidied up, and filled in a few missing operators 0.5.1 (*) Ensured column-major order for matrices throughout (*) Fixed HMM_Translate producing row-major matrices 0.5.2 (*) Fixed SSE code in HMM_SqrtF (*) Fixed SSE code in HMM_RSqrtF 0.6 (*) Added Unit testing (*) Made HMM_Power faster (*) Fixed possible efficiency problem with HMM_Normalize (*) RENAMED HMM_LengthSquareRoot to HMM_LengthSquared (*) RENAMED HMM_RSqrtF to HMM_RSquareRootF (*) RENAMED HMM_SqrtF to HMM_SquareRootF (*) REMOVED Inner function (user should use Dot now) (*) REMOVED HMM_FastInverseSquareRoot function declaration 0.7 (*) REMOVED HMM_LengthSquared in HANDMADE_MATH_IMPLEMENTATION (should use HMM_LengthSquaredVec3, or HANDMADE_MATH_CPP_MODE for function overloaded version) (*) REMOVED HMM_Length in HANDMADE_MATH_IMPLEMENTATION (should use HMM_LengthVec3, HANDMADE_MATH_CPP_MODE for function overloaded version) (*) REMOVED HMM_Normalize in HANDMADE_MATH_IMPLEMENTATION (should use HMM_NormalizeVec3, or HANDMADE_MATH_CPP_MODE for function overloaded version) (*) Added HMM_LengthSquaredVec2 (*) Added HMM_LengthSquaredVec4 (*) Addd HMM_LengthVec2 (*) Added HMM_LengthVec4 (*) Added HMM_NormalizeVec2 (*) Added HMM_NormalizeVec4 1.0 (*) Lots of testing! 1.1 (*) Quaternion support (*) Added type hmm_quaternion (*) Added HMM_Quaternion (*) Added HMM_QuaternionV4 (*) Added HMM_AddQuaternion (*) Added HMM_SubtractQuaternion (*) Added HMM_MultiplyQuaternion (*) Added HMM_MultiplyQuaternionF (*) Added HMM_DivideQuaternionF (*) Added HMM_InverseQuaternion (*) Added HMM_DotQuaternion (*) Added HMM_NormalizeQuaternion (*) Added HMM_Slerp (*) Added HMM_QuaternionToMat4 (*) Added HMM_QuaternionFromAxisAngle 1.1.1 (*) Resolved compiler warnings on gcc and g++ 1.1.2 (*) Fixed invalid HMMDEF's in the function definitions 1.1.3 (*) Fixed compile error in C mode LICENSE This software is in the public domain. Where that dedication is not recognized, you are granted a perpetual, irrevocable license to copy, distribute, and modify this file as you see fit. CREDITS Written by Zakary Strange (zak@handmade.network && @strangezak) Functionality: Matt Mascarenhas (@miblo_) Aleph FieryDrake (@fierydrake) Gingerbill (@TheGingerBill) Ben Visness (@bvisness) Trinton Bullard (@Peliex_Dev) Fixes: Jeroen van Rijn (@J_vanRijn) Kiljacken (@Kiljacken) Insofaras (@insofaras) */ // let's figure out if SSE is really available (unless disabled anyway) // (it isn't on non-x86/x86_64 platforms or even x86 without explicit SSE support) // => only use "#ifdef HANDMADE_MATH__USE_SSE" to check for SSE support below this block! #ifndef HANDMADE_MATH_NO_SSE # ifdef _MSC_VER // MSVC supports SSE in amd64 mode or _M_IX86_FP >= 1 (2 means SSE2) # if defined(_M_AMD64) || ( defined(_M_IX86_FP) && _M_IX86_FP >= 1 ) # define HANDMADE_MATH__USE_SSE 1 # endif # else // not MSVC, probably GCC, clang, icc or something that doesn't support SSE anyway # ifdef __SSE__ // they #define __SSE__ if it's supported # define HANDMADE_MATH__USE_SSE 1 # endif // __SSE__ # endif // not _MSC_VER #endif // #ifndef HANDMADE_MATH_NO_SSE #ifdef HANDMADE_MATH__USE_SSE #include #endif #ifndef HANDMADE_MATH_H #define HANDMADE_MATH_H #ifdef _MSC_VER #pragma warning(disable:4201) #endif #ifdef __clang__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wgnu-anonymous-struct" #endif #ifdef __cplusplus extern "C" { #endif #ifdef HANDMADE_MATH_STATIC #define HMMDEF static #else #define HMMDEF extern #endif #ifdef HANDMADE_MATH_NO_INLINE #define HINLINE #elif _MSC_VER && !__INTEL_COMPILER #define HINLINE __inline #else #define HINLINE inline #endif #if !defined(HMM_SINF) || !defined(HMM_COSF) || !defined(HMM_TANF) || \ !defined(HMM_EXPF) || !defined(HMM_LOGF) || !defined(HMM_ACOSF) || \ !defined(HMM_ATANF)|| !defined(HMM_ATAN2F) #include #endif #ifndef HMM_SINF #define HMM_SINF sinf #endif #ifndef HMM_COSF #define HMM_COSF cosf #endif #ifndef HMM_TANF #define HMM_TANF tanf #endif #ifndef HMM_EXPF #define HMM_EXPF expf #endif #ifndef HMM_LOGF #define HMM_LOGF logf #endif #ifndef HMM_ACOSF #define HMM_ACOSF acosf #endif #ifndef HMM_ATANF #define HMM_ATANF atanf #endif #ifndef HMM_ATAN2F #define HMM_ATAN2F atan2f #endif #define HMM_PI32 3.14159265359f #define HMM_PI 3.14159265358979323846 #define HMM_MIN(a, b) (a) > (b) ? (b) : (a) #define HMM_MAX(a, b) (a) < (b) ? (b) : (a) #define HMM_ABS(a) ((a) > 0 ? (a) : -(a)) #define HMM_MOD(a, m) ((a) % (m)) >= 0 ? ((a) % (m)) : (((a) % (m)) + (m)) #define HMM_SQUARE(x) ((x) * (x)) typedef union hmm_vec2 { struct { float X, Y; }; struct { float U, V; }; struct { float Left, Right; }; float Elements[2]; } hmm_vec2; typedef union hmm_vec3 { struct { float X, Y, Z; }; struct { float U, V, W; }; struct { float R, G, B; }; struct { hmm_vec2 XY; float Ignored0_; }; struct { float Ignored1_; hmm_vec2 YZ; }; struct { hmm_vec2 UV; float Ignored2_; }; struct { float Ignored3_; hmm_vec2 VW; }; float Elements[3]; } hmm_vec3; typedef union hmm_vec4 { struct { union { hmm_vec3 XYZ; struct { float X, Y, Z; }; }; float W; }; struct { union { hmm_vec3 RGB; struct { float R, G, B; }; }; float A; }; struct { hmm_vec2 XY; float Ignored0_; float Ignored1_; }; struct { float Ignored2_; hmm_vec2 YZ; float Ignored3_; }; struct { float Ignored4_; float Ignored5_; hmm_vec2 ZW; }; float Elements[4]; } hmm_vec4; typedef union hmm_mat4 { float Elements[4][4]; } hmm_mat4; typedef union hmm_quaternion { struct { union { hmm_vec3 XYZ; struct { float X, Y, Z; }; }; float W; }; float Elements[4]; } hmm_quaternion; typedef hmm_vec2 hmm_v2; typedef hmm_vec3 hmm_v3; typedef hmm_vec4 hmm_v4; typedef hmm_mat4 hmm_m4; HMMDEF float HMM_SinF(float Angle); HMMDEF float HMM_TanF(float Angle); HMMDEF float HMM_ATanF(float Theta); HMMDEF float HMM_ATan2F(float Theta, float Theta2); HMMDEF float HMM_CosF(float Angle); HMMDEF float HMM_ACosF(float Theta); HMMDEF float HMM_ExpF(float Float); HMMDEF float HMM_LogF(float Float); HMMDEF float HMM_ToRadians(float Degrees); HMMDEF float HMM_SquareRootF(float Float); HMMDEF float HMM_RSquareRootF(float Float); HMMDEF float HMM_LengthSquaredVec2(hmm_vec2 A); HMMDEF float HMM_LengthSquaredVec3(hmm_vec3 A); HMMDEF float HMM_LengthSquaredVec4(hmm_vec4 A); HMMDEF float HMM_LengthVec2(hmm_vec2 A); HMMDEF float HMM_LengthVec3(hmm_vec3 A); HMMDEF float HMM_LengthVec4(hmm_vec4 A); HMMDEF float HMM_Power(float Base, int Exponent); HMMDEF float HMM_PowerF(float Base, float Exponent); HMMDEF float HMM_Lerp(float A, float Time, float B); HMMDEF float HMM_Clamp(float Min, float Value, float Max); HMMDEF hmm_vec2 HMM_NormalizeVec2(hmm_vec2 A); HMMDEF hmm_vec3 HMM_NormalizeVec3(hmm_vec3 A); HMMDEF hmm_vec4 HMM_NormalizeVec4(hmm_vec4 A); HMMDEF float HMM_DotVec2(hmm_vec2 VecOne, hmm_vec2 VecTwo); HMMDEF float HMM_DotVec3(hmm_vec3 VecOne, hmm_vec3 VecTwo); HMMDEF float HMM_DotVec4(hmm_vec4 VecOne, hmm_vec4 VecTwo); HMMDEF hmm_vec3 HMM_Cross(hmm_vec3 VecOne, hmm_vec3 VecTwo); HMMDEF hmm_vec2 HMM_Vec2(float X, float Y); HMMDEF hmm_vec2 HMM_Vec2i(int X, int Y); HMMDEF hmm_vec3 HMM_Vec3(float X, float Y, float Z); HMMDEF hmm_vec3 HMM_Vec3i(int X, int Y, int Z); HMMDEF hmm_vec4 HMM_Vec4(float X, float Y, float Z, float W); HMMDEF hmm_vec4 HMM_Vec4i(int X, int Y, int Z, int W); HMMDEF hmm_vec4 HMM_Vec4v(hmm_vec3 Vector, float W); HMMDEF hmm_vec2 HMM_AddVec2(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec3 HMM_AddVec3(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec4 HMM_AddVec4(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_vec2 HMM_SubtractVec2(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec3 HMM_SubtractVec3(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec4 HMM_SubtractVec4(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_vec2 HMM_MultiplyVec2(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec2 HMM_MultiplyVec2f(hmm_vec2 Left, float Right); HMMDEF hmm_vec3 HMM_MultiplyVec3(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec3 HMM_MultiplyVec3f(hmm_vec3 Left, float Right); HMMDEF hmm_vec4 HMM_MultiplyVec4(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_vec4 HMM_MultiplyVec4f(hmm_vec4 Left, float Right); HMMDEF hmm_vec2 HMM_DivideVec2(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec2 HMM_DivideVec2f(hmm_vec2 Left, float Right); HMMDEF hmm_vec3 HMM_DivideVec3(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec3 HMM_DivideVec3f(hmm_vec3 Left, float Right); HMMDEF hmm_vec4 HMM_DivideVec4(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_vec4 HMM_DivideVec4f(hmm_vec4 Left, float Right); HMMDEF hmm_mat4 HMM_Mat4(void); HMMDEF hmm_mat4 HMM_Mat4d(float Diagonal); HMMDEF hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right); HMMDEF hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right); HMMDEF hmm_mat4 HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right); HMMDEF hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar); HMMDEF hmm_vec4 HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector); HMMDEF hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar); HMMDEF hmm_mat4 HMM_Transpose(hmm_mat4 Matrix); HMMDEF hmm_mat4 HMM_Orthographic(float Left, float Right, float Bottom, float Top, float Near, float Far); HMMDEF hmm_mat4 HMM_Perspective(float FOV, float AspectRatio, float Near, float Far); HMMDEF hmm_mat4 HMM_Translate(hmm_vec3 Translation); HMMDEF hmm_mat4 HMM_Rotate(float Angle, hmm_vec3 Axis); HMMDEF hmm_mat4 HMM_Scale(hmm_vec3 Scale); HMMDEF hmm_mat4 HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up); HMMDEF hmm_quaternion HMM_Quaternion(float X, float Y, float Z, float W); HMMDEF hmm_quaternion HMM_QuaternionV4(hmm_vec4 Vector); HMMDEF hmm_quaternion HMM_AddQuaternion(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_quaternion HMM_SubtractQuaternion(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_quaternion HMM_MultiplyQuaternion(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_quaternion HMM_MultiplyQuaternionF(hmm_quaternion Left, float Multiplicative); HMMDEF hmm_quaternion HMM_DivideQuaternionF(hmm_quaternion Left, float Dividend); HMMDEF hmm_quaternion HMM_InverseQuaternion(hmm_quaternion Left); HMMDEF float HMM_DotQuaternion(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_quaternion HMM_NormalizeQuaternion(hmm_quaternion Left); HMMDEF hmm_quaternion HMM_NLerp(hmm_quaternion Left, float Time, hmm_quaternion Right); HMMDEF hmm_quaternion HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right); HMMDEF hmm_mat4 HMM_QuaternionToMat4(hmm_quaternion Left); HMMDEF hmm_quaternion HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotation); #ifdef __cplusplus } #endif #ifdef HANDMADE_MATH_CPP_MODE HMMDEF float HMM_Length(hmm_vec2 A); HMMDEF float HMM_Length(hmm_vec3 A); HMMDEF float HMM_Length(hmm_vec4 A); HMMDEF float HMM_LengthSquared(hmm_vec2 A); HMMDEF float HMM_LengthSquared(hmm_vec3 A); HMMDEF float HMM_LengthSquared(hmm_vec4 A); HMMDEF hmm_vec2 HMM_Normalize(hmm_vec2 A); HMMDEF hmm_vec3 HMM_Normalize(hmm_vec3 A); HMMDEF hmm_vec4 HMM_Normalize(hmm_vec4 A); HMMDEF hmm_quaternion HMM_Normalize(hmm_quaternion A); HMMDEF float HMM_Dot(hmm_vec2 VecOne, hmm_vec2 VecTwo); HMMDEF float HMM_Dot(hmm_vec3 VecOne, hmm_vec3 VecTwo); HMMDEF float HMM_Dot(hmm_vec4 VecOne, hmm_vec4 VecTwo); HMMDEF float HMM_Dot(hmm_quaternion QuatOne, hmm_quaternion QuatTwo); HMMDEF hmm_vec2 HMM_Add(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec3 HMM_Add(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec4 HMM_Add(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_mat4 HMM_Add(hmm_mat4 Left, hmm_mat4 Right); HMMDEF hmm_quaternion HMM_Add(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_vec2 HMM_Subtract(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec3 HMM_Subtract(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec4 HMM_Subtract(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_mat4 HMM_Subtract(hmm_mat4 Left, hmm_mat4 Right); HMMDEF hmm_quaternion HMM_Subtract(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_vec2 HMM_Multiply(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec2 HMM_Multiply(hmm_vec2 Left, float Right); HMMDEF hmm_vec3 HMM_Multiply(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec3 HMM_Multiply(hmm_vec3 Left, float Right); HMMDEF hmm_vec4 HMM_Multiply(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_vec4 HMM_Multiply(hmm_vec4 Left, float Right); HMMDEF hmm_mat4 HMM_Multiply(hmm_mat4 Left, hmm_mat4 Right); HMMDEF hmm_mat4 HMM_Multiply(hmm_mat4 Left, float Right); HMMDEF hmm_vec4 HMM_Multiply(hmm_mat4 Matrix, hmm_vec4 Vector); HMMDEF hmm_quaternion HMM_Multiply(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_quaternion HMM_Multiply(hmm_quaternion Left, float Right); HMMDEF hmm_vec2 HMM_Divide(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec2 HMM_Divide(hmm_vec2 Left, float Right); HMMDEF hmm_vec3 HMM_Divide(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec3 HMM_Divide(hmm_vec3 Left, float Right); HMMDEF hmm_vec4 HMM_Divide(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_vec4 HMM_Divide(hmm_vec4 Left, float Right); HMMDEF hmm_mat4 HMM_Divide(hmm_mat4 Left, float Right); HMMDEF hmm_quaternion HMM_Divide(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_quaternion HMM_Divide(hmm_quaternion Left, float Right); HMMDEF hmm_vec2 operator+(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec3 operator+(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec4 operator+(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_mat4 operator+(hmm_mat4 Left, hmm_mat4 Right); HMMDEF hmm_quaternion operator+(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_vec2 operator-(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec3 operator-(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec4 operator-(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_mat4 operator-(hmm_mat4 Left, hmm_mat4 Right); HMMDEF hmm_quaternion operator-(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_vec2 operator*(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec3 operator*(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec4 operator*(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_mat4 operator*(hmm_mat4 Left, hmm_mat4 Right); HMMDEF hmm_quaternion operator*(hmm_quaternion Left, hmm_quaternion Right); HMMDEF hmm_vec2 operator*(hmm_vec2 Left, float Right); HMMDEF hmm_vec3 operator*(hmm_vec3 Left, float Right); HMMDEF hmm_vec4 operator*(hmm_vec4 Left, float Right); HMMDEF hmm_mat4 operator*(hmm_mat4 Left, float Right); HMMDEF hmm_quaternion operator*(hmm_quaternion Left, float Right); HMMDEF hmm_vec2 operator*(float Left, hmm_vec2 Right); HMMDEF hmm_vec3 operator*(float Left, hmm_vec3 Right); HMMDEF hmm_vec4 operator*(float Left, hmm_vec4 Right); HMMDEF hmm_mat4 operator*(float Left, hmm_mat4 Right); HMMDEF hmm_quaternion operator*(float Left, hmm_quaternion Right); HMMDEF hmm_vec4 operator*(hmm_mat4 Matrix, hmm_vec4 Vector); HMMDEF hmm_vec2 operator/(hmm_vec2 Left, hmm_vec2 Right); HMMDEF hmm_vec3 operator/(hmm_vec3 Left, hmm_vec3 Right); HMMDEF hmm_vec4 operator/(hmm_vec4 Left, hmm_vec4 Right); HMMDEF hmm_vec2 operator/(hmm_vec2 Left, float Right); HMMDEF hmm_vec3 operator/(hmm_vec3 Left, float Right); HMMDEF hmm_vec4 operator/(hmm_vec4 Left, float Right); HMMDEF hmm_mat4 operator/(hmm_mat4 Left, float Right); HMMDEF hmm_quaternion operator/(hmm_quaternion Left, float Right); HMMDEF hmm_vec2 &operator+=(hmm_vec2 &Left, hmm_vec2 Right); HMMDEF hmm_vec3 &operator+=(hmm_vec3 &Left, hmm_vec3 Right); HMMDEF hmm_vec4 &operator+=(hmm_vec4 &Left, hmm_vec4 Right); HMMDEF hmm_mat4 &operator+=(hmm_mat4 &Left, hmm_mat4 Right); HMMDEF hmm_quaternion &operator+=(hmm_quaternion &Left, hmm_quaternion Right); HMMDEF hmm_vec2 &operator-=(hmm_vec2 &Left, hmm_vec2 Right); HMMDEF hmm_vec3 &operator-=(hmm_vec3 &Left, hmm_vec3 Right); HMMDEF hmm_vec4 &operator-=(hmm_vec4 &Left, hmm_vec4 Right); HMMDEF hmm_mat4 &operator-=(hmm_mat4 &Left, hmm_mat4 Right); HMMDEF hmm_quaternion &operator-=(hmm_quaternion &Left, hmm_quaternion Right); HMMDEF hmm_vec2 &operator*=(hmm_vec2 &Left, hmm_vec2 Right); HMMDEF hmm_vec3 &operator*=(hmm_vec3 &Left, hmm_vec3 Right); HMMDEF hmm_vec4 &operator*=(hmm_vec4 &Left, hmm_vec4 Right); HMMDEF hmm_vec2 &operator*=(hmm_vec2 &Left, float Right); HMMDEF hmm_vec3 &operator*=(hmm_vec3 &Left, float Right); HMMDEF hmm_vec4 &operator*=(hmm_vec4 &Left, float Right); HMMDEF hmm_mat4 &operator*=(hmm_mat4 &Left, float Right); HMMDEF hmm_quaternion &operator*=(hmm_quaternion &Left, float Right); HMMDEF hmm_vec2 &operator/=(hmm_vec2 &Left, hmm_vec2 Right); HMMDEF hmm_vec3 &operator/=(hmm_vec3 &Left, hmm_vec3 Right); HMMDEF hmm_vec4 &operator/=(hmm_vec4 &Left, hmm_vec4 Right); HMMDEF hmm_vec2 &operator/=(hmm_vec2 &Left, float Right); HMMDEF hmm_vec3 &operator/=(hmm_vec3 &Left, float Right); HMMDEF hmm_vec4 &operator/=(hmm_vec4 &Left, float Right); HMMDEF hmm_mat4 &operator/=(hmm_mat4 &Left, float Right); HMMDEF hmm_quaternion &operator/=(hmm_quaternion &Left, float Right); #endif /* HANDMADE_MATH_CPP */ #ifdef __clang__ #pragma GCC diagnostic pop #endif #endif /* HANDMADE_MATH_H */ #ifdef HANDMADE_MATH_IMPLEMENTATION HINLINE float HMM_SinF(float Angle) { float Result = 0.0f; Result = HMM_SINF(Angle); return (Result); } HINLINE float HMM_CosF(float Angle) { float Result = 0.0f; Result = HMM_COSF(Angle); return (Result); } HINLINE float HMM_TanF(float Radians) { float Result = 0.0f; Result = HMM_TANF(Radians); return (Result); } HINLINE float HMM_ACosF(float Radians) { float Result = 0.0f; Result = HMM_ACOSF(Radians); return (Result); } HINLINE float HMM_ATanF(float Radians) { float Result = 0.0f; Result = HMM_ATANF(Radians); return (Result); } HINLINE float HMM_Atan2F(float Left, float Right) { float Result = 0.0f; Result = HMM_ATAN2F(Left, Right); return (Result); } HINLINE float HMM_ExpF(float Float) { float Result = 0.0f; Result = HMM_EXPF(Float); return (Result); } HINLINE float HMM_LogF(float Float) { float Result = 0.0f; Result = HMM_LOGF(Float); return (Result); } HINLINE float HMM_ToRadians(float Degrees) { float Result = 0.0f; Result = Degrees * (HMM_PI32 / 180.0f); return (Result); } HINLINE float HMM_SquareRootF(float Value) { float Result = 0.0f; #ifdef HANDMADE_MATH__USE_SSE __m128 In = _mm_set_ss(Value); __m128 Out = _mm_sqrt_ss(In); Result = _mm_cvtss_f32(Out); #else Result = sqrtf(Value); #endif return(Result); } HINLINE float HMM_RSquareRootF(float Value) { float Result = 0.0f; #ifdef HANDMADE_MATH__USE_SSE __m128 In = _mm_set_ss(Value); __m128 Out = _mm_rsqrt_ss(In); Result = _mm_cvtss_f32(Out); #else Result = 1.0f/HMM_SquareRootF(Value); #endif return(Result); } HINLINE float HMM_LengthSquaredVec2(hmm_vec2 A) { float Result = 0.0f; Result = HMM_DotVec2(A, A); return(Result); } HINLINE float HMM_LengthSquaredVec3(hmm_vec3 A) { float Result = 0.0f; Result = HMM_DotVec3(A, A); return (Result); } HINLINE float HMM_LengthSquaredVec4(hmm_vec4 A) { float Result = 0.0f; Result = HMM_DotVec4(A, A); return(Result); } HINLINE float HMM_LengthVec2(hmm_vec2 A) { float Result = 0.0f; Result = HMM_SquareRootF(HMM_LengthSquaredVec2(A)); return(Result); } HINLINE float HMM_LengthVec3(hmm_vec3 A) { float Result = 0.0f; Result = HMM_SquareRootF(HMM_LengthSquaredVec3(A)); return (Result); } HINLINE float HMM_LengthVec4(hmm_vec4 A) { float Result = 0.0f; Result = HMM_SquareRootF(HMM_LengthSquaredVec4(A)); return(Result); } HINLINE float HMM_Power(float Base, int Exponent) { float Result = 1.0f; float Mul = Exponent < 0 ? 1.f / Base : Base; unsigned int X = Exponent < 0 ? -Exponent : Exponent; while (X) { if (X & 1) { Result *= Mul; } Mul *= Mul; X >>= 1; } return (Result); } HINLINE float HMM_PowerF(float Base, float Exponent) { return HMM_EXPF(Exponent * HMM_LOGF(Base)); } HINLINE float HMM_Lerp(float A, float Time, float B) { float Result = 0; Result = (1.0f - Time) * A + Time * B; return (Result); } HINLINE float HMM_Clamp(float Min, float Value, float Max) { float Result = Value; if(Result < Min) { Result = Min; } else if(Result > Max) { Result = Max; } return (Result); } HINLINE hmm_vec2 HMM_NormalizeVec2(hmm_vec2 A) { hmm_vec2 Result = {0}; float VectorLength = HMM_LengthVec2(A); Result.X = A.X * (1.0f / VectorLength); Result.Y = A.Y * (1.0f / VectorLength); return (Result); } HINLINE hmm_vec3 HMM_NormalizeVec3(hmm_vec3 A) { hmm_vec3 Result = {0}; float VectorLength = HMM_LengthVec3(A); Result.X = A.X * (1.0f / VectorLength); Result.Y = A.Y * (1.0f / VectorLength); Result.Z = A.Z * (1.0f / VectorLength); return (Result); } HINLINE hmm_vec4 HMM_NormalizeVec4(hmm_vec4 A) { hmm_vec4 Result = {0}; float VectorLength = HMM_LengthVec4(A); Result.X = A.X * (1.0f / VectorLength); Result.Y = A.Y * (1.0f / VectorLength); Result.Z = A.Z * (1.0f / VectorLength); Result.W = A.W * (1.0f / VectorLength); return (Result); } HINLINE float HMM_DotVec2(hmm_vec2 VecOne, hmm_vec2 VecTwo) { float Result = 0.0f; Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y); return (Result); } HINLINE float HMM_DotVec3(hmm_vec3 VecOne, hmm_vec3 VecTwo) { float Result = 0.0f; Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y) + (VecOne.Z * VecTwo.Z); return (Result); } HINLINE float HMM_DotVec4(hmm_vec4 VecOne, hmm_vec4 VecTwo) { float Result = 0.0f; Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y) + (VecOne.Z * VecTwo.Z) + (VecOne.W * VecTwo.W); return (Result); } HINLINE hmm_vec3 HMM_Cross(hmm_vec3 VecOne, hmm_vec3 VecTwo) { hmm_vec3 Result = {0}; Result.X = (VecOne.Y * VecTwo.Z) - (VecOne.Z * VecTwo.Y); Result.Y = (VecOne.Z * VecTwo.X) - (VecOne.X * VecTwo.Z); Result.Z = (VecOne.X * VecTwo.Y) - (VecOne.Y * VecTwo.X); return (Result); } HINLINE hmm_vec2 HMM_Vec2(float X, float Y) { hmm_vec2 Result = {0}; Result.X = X; Result.Y = Y; return (Result); } HINLINE hmm_vec2 HMM_Vec2i(int X, int Y) { hmm_vec2 Result = {0}; Result.X = (float)X; Result.Y = (float)Y; return (Result); } HINLINE hmm_vec3 HMM_Vec3(float X, float Y, float Z) { hmm_vec3 Result = {0}; Result.X = X; Result.Y = Y; Result.Z = Z; return (Result); } HINLINE hmm_vec3 HMM_Vec3i(int X, int Y, int Z) { hmm_vec3 Result = {0}; Result.X = (float)X; Result.Y = (float)Y; Result.Z = (float)Z; return (Result); } HINLINE hmm_vec4 HMM_Vec4(float X, float Y, float Z, float W) { hmm_vec4 Result = {0}; Result.X = X; Result.Y = Y; Result.Z = Z; Result.W = W; return (Result); } HINLINE hmm_vec4 HMM_Vec4i(int X, int Y, int Z, int W) { hmm_vec4 Result = {0}; Result.X = (float)X; Result.Y = (float)Y; Result.Z = (float)Z; Result.W = (float)W; return (Result); } HINLINE hmm_vec4 HMM_Vec4v(hmm_vec3 Vector, float W) { hmm_vec4 Result = {0}; Result.XYZ = Vector; Result.W = W; return (Result); } HINLINE hmm_vec2 HMM_AddVec2(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = {0}; Result.X = Left.X + Right.X; Result.Y = Left.Y + Right.Y; return (Result); } HINLINE hmm_vec3 HMM_AddVec3(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = {0}; Result.X = Left.X + Right.X; Result.Y = Left.Y + Right.Y; Result.Z = Left.Z + Right.Z; return (Result); } HINLINE hmm_vec4 HMM_AddVec4(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = {0}; Result.X = Left.X + Right.X; Result.Y = Left.Y + Right.Y; Result.Z = Left.Z + Right.Z; Result.W = Left.W + Right.W; return (Result); } HINLINE hmm_vec2 HMM_SubtractVec2(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = {0}; Result.X = Left.X - Right.X; Result.Y = Left.Y - Right.Y; return (Result); } HINLINE hmm_vec3 HMM_SubtractVec3(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = {0}; Result.X = Left.X - Right.X; Result.Y = Left.Y - Right.Y; Result.Z = Left.Z - Right.Z; return (Result); } HINLINE hmm_vec4 HMM_SubtractVec4(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = {0}; Result.X = Left.X - Right.X; Result.Y = Left.Y - Right.Y; Result.Z = Left.Z - Right.Z; Result.W = Left.W - Right.W; return (Result); } HINLINE hmm_vec2 HMM_MultiplyVec2(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = {0}; Result.X = Left.X * Right.X; Result.Y = Left.Y * Right.Y; return (Result); } HINLINE hmm_vec2 HMM_MultiplyVec2f(hmm_vec2 Left, float Right) { hmm_vec2 Result = {0}; Result.X = Left.X * Right; Result.Y = Left.Y * Right; return (Result); } HINLINE hmm_vec3 HMM_MultiplyVec3(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = {0}; Result.X = Left.X * Right.X; Result.Y = Left.Y * Right.Y; Result.Z = Left.Z * Right.Z; return (Result); } HINLINE hmm_vec3 HMM_MultiplyVec3f(hmm_vec3 Left, float Right) { hmm_vec3 Result = {0}; Result.X = Left.X * Right; Result.Y = Left.Y * Right; Result.Z = Left.Z * Right; return (Result); } HINLINE hmm_vec4 HMM_MultiplyVec4(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = {0}; Result.X = Left.X * Right.X; Result.Y = Left.Y * Right.Y; Result.Z = Left.Z * Right.Z; Result.W = Left.W * Right.W; return (Result); } HINLINE hmm_vec4 HMM_MultiplyVec4f(hmm_vec4 Left, float Right) { hmm_vec4 Result = {0}; Result.X = Left.X * Right; Result.Y = Left.Y * Right; Result.Z = Left.Z * Right; Result.W = Left.W * Right; return (Result); } HINLINE hmm_vec2 HMM_DivideVec2(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = {0}; Result.X = Left.X / Right.X; Result.Y = Left.Y / Right.Y; return (Result); } HINLINE hmm_vec2 HMM_DivideVec2f(hmm_vec2 Left, float Right) { hmm_vec2 Result = {0}; Result.X = Left.X / Right; Result.Y = Left.Y / Right; return (Result); } HINLINE hmm_vec3 HMM_DivideVec3(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = {0}; Result.X = Left.X / Right.X; Result.Y = Left.Y / Right.Y; Result.Z = Left.Z / Right.Z; return (Result); } HINLINE hmm_vec3 HMM_DivideVec3f(hmm_vec3 Left, float Right) { hmm_vec3 Result = {0}; Result.X = Left.X / Right; Result.Y = Left.Y / Right; Result.Z = Left.Z / Right; return (Result); } HINLINE hmm_vec4 HMM_DivideVec4(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = {0}; Result.X = Left.X / Right.X; Result.Y = Left.Y / Right.Y; Result.Z = Left.Z / Right.Z; Result.W = Left.W / Right.W; return (Result); } HINLINE hmm_vec4 HMM_DivideVec4f(hmm_vec4 Left, float Right) { hmm_vec4 Result = {0}; Result.X = Left.X / Right; Result.Y = Left.Y / Right; Result.Z = Left.Z / Right; Result.W = Left.W / Right; return (Result); } HINLINE hmm_mat4 HMM_Mat4(void) { hmm_mat4 Result = {0}; return (Result); } HINLINE hmm_mat4 HMM_Mat4d(float Diagonal) { hmm_mat4 Result = HMM_Mat4(); Result.Elements[0][0] = Diagonal; Result.Elements[1][1] = Diagonal; Result.Elements[2][2] = Diagonal; Result.Elements[3][3] = Diagonal; return (Result); } HINLINE hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = HMM_Mat4(); int Columns; for(Columns = 0; Columns < 4; ++Columns) { int Rows; for(Rows = 0; Rows < 4; ++Rows) { Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] + Right.Elements[Columns][Rows]; } } return (Result); } HINLINE hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = HMM_Mat4(); int Columns; for(Columns = 0; Columns < 4; ++Columns) { int Rows; for(Rows = 0; Rows < 4; ++Rows) { Result.Elements[Columns][Rows] = Left.Elements[Columns][Rows] - Right.Elements[Columns][Rows]; } } return (Result); } HINLINE hmm_mat4 HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = HMM_Mat4(); int Columns; for(Columns = 0; Columns < 4; ++Columns) { int Rows; for(Rows = 0; Rows < 4; ++Rows) { float Sum = 0; int CurrentMatrice; for(CurrentMatrice = 0; CurrentMatrice < 4; ++CurrentMatrice) { Sum += Left.Elements[CurrentMatrice][Rows] * Right.Elements[Columns][CurrentMatrice]; } Result.Elements[Columns][Rows] = Sum; } } return (Result); } HINLINE hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar) { hmm_mat4 Result = HMM_Mat4(); int Columns; for(Columns = 0; Columns < 4; ++Columns) { int Rows; for(Rows = 0; Rows < 4; ++Rows) { Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] * Scalar; } } return (Result); } HINLINE hmm_vec4 HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector) { hmm_vec4 Result = {0}; int Columns, Rows; for(Rows = 0; Rows < 4; ++Rows) { float Sum = 0; for(Columns = 0; Columns < 4; ++Columns) { Sum += Matrix.Elements[Columns][Rows] * Vector.Elements[Columns]; } Result.Elements[Rows] = Sum; } return (Result); } HINLINE hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar) { hmm_mat4 Result = HMM_Mat4(); int Columns; for(Columns = 0; Columns < 4; ++Columns) { int Rows; for(Rows = 0; Rows < 4; ++Rows) { Result.Elements[Columns][Rows] = Matrix.Elements[Columns][Rows] / Scalar; } } return (Result); } HINLINE hmm_mat4 HMM_Transpose(hmm_mat4 Matrix) { hmm_mat4 Result = HMM_Mat4(); int Columns; for(Columns = 0; Columns < 4; ++Columns) { int Rows; for(Rows = 0; Rows < 4; ++Rows) { Result.Elements[Rows][Columns] = Matrix.Elements[Columns][Rows]; } } return (Result); } HINLINE hmm_mat4 HMM_Orthographic(float Left, float Right, float Bottom, float Top, float Near, float Far) { hmm_mat4 Result = HMM_Mat4d(1.0f); Result.Elements[0][0] = 2.0f / (Right - Left); Result.Elements[1][1] = 2.0f / (Top - Bottom); Result.Elements[2][2] = 2.0f / (Near - Far); Result.Elements[3][0] = (Left + Right) / (Left - Right); Result.Elements[3][1] = (Bottom + Top) / (Bottom - Top); Result.Elements[3][2] = (Far + Near) / (Near - Far); return (Result); } HINLINE hmm_mat4 HMM_Perspective(float FOV, float AspectRatio, float Near, float Far) { hmm_mat4 Result = HMM_Mat4d(1.0f); float TanThetaOver2 = HMM_TanF(FOV * (HMM_PI32 / 360.0f)); Result.Elements[0][0] = 1.0f / TanThetaOver2; Result.Elements[1][1] = AspectRatio / TanThetaOver2; Result.Elements[2][3] = -1.0f; Result.Elements[2][2] = (Near + Far) / (Near - Far); Result.Elements[3][2] = (2.0f * Near * Far) / (Near - Far); Result.Elements[3][3] = 0.0f; return (Result); } HINLINE hmm_mat4 HMM_Translate(hmm_vec3 Translation) { hmm_mat4 Result = HMM_Mat4d(1.0f); Result.Elements[3][0] = Translation.X; Result.Elements[3][1] = Translation.Y; Result.Elements[3][2] = Translation.Z; return (Result); } HINLINE hmm_mat4 HMM_Rotate(float Angle, hmm_vec3 Axis) { hmm_mat4 Result = HMM_Mat4d(1.0f); Axis = HMM_NormalizeVec3(Axis); float SinTheta = HMM_SinF(HMM_ToRadians(Angle)); float CosTheta = HMM_CosF(HMM_ToRadians(Angle)); float CosValue = 1.0f - CosTheta; Result.Elements[0][0] = (Axis.X * Axis.X * CosValue) + CosTheta; Result.Elements[0][1] = (Axis.X * Axis.Y * CosValue) + (Axis.Z * SinTheta); Result.Elements[0][2] = (Axis.X * Axis.Z * CosValue) - (Axis.Y * SinTheta); Result.Elements[1][0] = (Axis.Y * Axis.X * CosValue) - (Axis.Z * SinTheta); Result.Elements[1][1] = (Axis.Y * Axis.Y * CosValue) + CosTheta; Result.Elements[1][2] = (Axis.Y * Axis.Z * CosValue) + (Axis.X * SinTheta); Result.Elements[2][0] = (Axis.Z * Axis.X * CosValue) + (Axis.Y * SinTheta); Result.Elements[2][1] = (Axis.Z * Axis.Y * CosValue) - (Axis.X * SinTheta); Result.Elements[2][2] = (Axis.Z * Axis.Z * CosValue) + CosTheta; return (Result); } HINLINE hmm_mat4 HMM_Scale(hmm_vec3 Scale) { hmm_mat4 Result = HMM_Mat4d(1.0f); Result.Elements[0][0] = Scale.X; Result.Elements[1][1] = Scale.Y; Result.Elements[2][2] = Scale.Z; return (Result); } HINLINE hmm_mat4 HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up) { hmm_mat4 Result = {0}; hmm_vec3 F = HMM_NormalizeVec3(HMM_SubtractVec3(Center, Eye)); hmm_vec3 S = HMM_NormalizeVec3(HMM_Cross(F, Up)); hmm_vec3 U = HMM_Cross(S, F); Result.Elements[0][0] = S.X; Result.Elements[0][1] = U.X; Result.Elements[0][2] = -F.X; Result.Elements[1][0] = S.Y; Result.Elements[1][1] = U.Y; Result.Elements[1][2] = -F.Y; Result.Elements[2][0] = S.Z; Result.Elements[2][1] = U.Z; Result.Elements[2][2] = -F.Z; Result.Elements[3][0] = -HMM_DotVec3(S, Eye); Result.Elements[3][1] = -HMM_DotVec3(U, Eye); Result.Elements[3][2] = HMM_DotVec3(F, Eye); Result.Elements[3][3] = 1.0f; return (Result); } HINLINE hmm_quaternion HMM_Quaternion(float X, float Y, float Z, float W) { hmm_quaternion Result = {0}; Result.X = X; Result.Y = Y; Result.Z = Z; Result.W = W; return(Result); } HINLINE hmm_quaternion HMM_QuaternionV4(hmm_vec4 Vector) { hmm_quaternion Result = {0}; Result.X = Vector.X; Result.Y = Vector.Y; Result.Z = Vector.Z; Result.W = Vector.W; return(Result); } HINLINE hmm_quaternion HMM_AddQuaternion(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = {0}; Result.X = Left.X + Right.X; Result.Y = Left.Y + Right.Y; Result.Z = Left.Z + Right.Z; Result.W = Left.W + Right.W; return(Result); } HINLINE hmm_quaternion HMM_SubtractQuaternion(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = {0}; Result.X = Left.X - Right.X; Result.Y = Left.Y - Right.Y; Result.Z = Left.Z - Right.Z; Result.W = Left.W - Right.W; return(Result); } HINLINE hmm_quaternion HMM_MultiplyQuaternion(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = {0}; Result.X = (Left.X * Right.W) + (Left.Y * Right.Z) - (Left.Z * Right.Y) + (Left.W * Right.X); Result.Y = (-Left.X * Right.Z) + (Left.Y * Right.W) + (Left.Z * Right.X) + (Left.W * Right.Y); Result.Z = (Left.X * Right.Y) - (Left.Y * Right.X) + (Left.Z * Right.W) + (Left.W * Right.Z); Result.W = (-Left.X * Right.X) - (Left.Y * Right.Y) - (Left.Z * Right.Z) + (Left.W * Right.W); return(Result); } HINLINE hmm_quaternion HMM_MultiplyQuaternionF(hmm_quaternion Left, float Multiplicative) { hmm_quaternion Result = {0}; Result.X = Left.X * Multiplicative; Result.Y = Left.Y * Multiplicative; Result.Z = Left.Z * Multiplicative; Result.W = Left.W * Multiplicative; return(Result); } HINLINE hmm_quaternion HMM_DivideQuaternionF(hmm_quaternion Left, float Dividend) { hmm_quaternion Result = {0}; Result.X = Left.X / Dividend; Result.Y = Left.Y / Dividend; Result.Z = Left.Z / Dividend; Result.W = Left.W / Dividend; return(Result); } HINLINE hmm_quaternion HMM_InverseQuaternion(hmm_quaternion Left) { hmm_quaternion Conjugate = {0}; hmm_quaternion Result = {0}; float Norm = 0; float NormSquared = 0; Conjugate.X = -Left.X; Conjugate.Y = -Left.Y; Conjugate.Z = -Left.Z; Conjugate.W = Left.W; Norm = HMM_SquareRootF(HMM_DotQuaternion(Left, Left)); NormSquared = Norm * Norm; Result.X = Conjugate.X / NormSquared; Result.Y = Conjugate.Y / NormSquared; Result.Z = Conjugate.Z / NormSquared; Result.W = Conjugate.W / NormSquared; return(Result); } HINLINE float HMM_DotQuaternion(hmm_quaternion Left, hmm_quaternion Right) { float Result = 0.0f; Result = (Left.X * Right.X) + (Left.Y * Right.Y) + (Left.Z * Right.Z) + (Left.W * Right.W); return(Result); } HINLINE hmm_quaternion HMM_NormalizeQuaternion(hmm_quaternion Left) { hmm_quaternion Result = {0}; float Length = HMM_SquareRootF(HMM_DotQuaternion(Left, Left)); Result = HMM_DivideQuaternionF(Left, Length); return(Result); } HINLINE hmm_quaternion HMM_NLerp(hmm_quaternion Left, float Time, hmm_quaternion Right) { hmm_quaternion Result = {0}; Result.X = HMM_Lerp(Left.X, Time, Right.X); Result.Y = HMM_Lerp(Left.Y, Time, Right.Y); Result.Z = HMM_Lerp(Left.Z, Time, Right.Z); Result.W = HMM_Lerp(Left.W, Time, Right.W); Result = HMM_NormalizeQuaternion(Result); return(Result); } HINLINE hmm_quaternion HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right) { hmm_quaternion Result = {0}; hmm_quaternion QuaternionLeft = {0}; hmm_quaternion QuaternionRight = {0}; float Cos_Theta = HMM_DotQuaternion(Left, Right); float Angle = HMM_ACosF(Cos_Theta); float S1 = HMM_SinF((1.0f - Time) * Angle); float S2 = HMM_SinF(Time * Angle); float Is = 1.0f / HMM_SinF(Angle); QuaternionLeft = HMM_MultiplyQuaternionF(Left, S1); QuaternionRight = HMM_MultiplyQuaternionF(Right, S2); Result = HMM_AddQuaternion(QuaternionLeft, QuaternionRight); Result = HMM_MultiplyQuaternionF(Result, Is); return(Result); } HINLINE hmm_mat4 HMM_QuaternionToMat4(hmm_quaternion Left) { hmm_mat4 Result = {0}; Result = HMM_Mat4d(1); hmm_quaternion NormalizedQuaternion = HMM_NormalizeQuaternion(Left); float XX, YY, ZZ, XY, XZ, YZ, WX, WY, WZ; XX = NormalizedQuaternion.X * NormalizedQuaternion.X; YY = NormalizedQuaternion.Y * NormalizedQuaternion.Y; ZZ = NormalizedQuaternion.Z * NormalizedQuaternion.Z; XY = NormalizedQuaternion.X * NormalizedQuaternion.Y; XZ = NormalizedQuaternion.X * NormalizedQuaternion.Z; YZ = NormalizedQuaternion.Y * NormalizedQuaternion.Z; WX = NormalizedQuaternion.W * NormalizedQuaternion.X; WY = NormalizedQuaternion.W * NormalizedQuaternion.Y; WZ = NormalizedQuaternion.W * NormalizedQuaternion.Z; Result.Elements[0][0] = 1.0f - 2.0f * (YY + ZZ); Result.Elements[0][1] = 2.0f * (XY + WZ); Result.Elements[0][2] = 2.0f * (XZ - WY); Result.Elements[1][0] = 2.0f * (XY - WZ); Result.Elements[1][1] = 1.0f - 2.0f * (XX + ZZ); Result.Elements[1][2] = 2.0f * (YZ + WX); Result.Elements[2][0] = 2.0f * (XZ + WY); Result.Elements[2][1] = 2.0f * (YZ - WX); Result.Elements[2][2] = 1.0f - 2.0f * (XX + YY); return(Result); } HINLINE hmm_quaternion HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotation) { hmm_quaternion Result = {0}; float AxisNorm = 0; float SineOfRotation = 0; hmm_vec3 RotatedVector = {0}; AxisNorm = HMM_SquareRootF(HMM_DotVec3(Axis, Axis)); SineOfRotation = HMM_SinF(AngleOfRotation / 2.0f); RotatedVector = HMM_MultiplyVec3f(Axis, SineOfRotation); Result.W = HMM_CosF(AngleOfRotation / 2.0f); Result.XYZ = HMM_DivideVec3f(RotatedVector, AxisNorm); return(Result); } #ifdef HANDMADE_MATH_CPP_MODE HINLINE float HMM_Length(hmm_vec2 A) { float Result = 0.0f; Result = HMM_LengthVec2(A); return(Result); } HINLINE float HMM_Length(hmm_vec3 A) { float Result = 0.0f; Result = HMM_LengthVec3(A); return(Result); } HINLINE float HMM_Length(hmm_vec4 A) { float Result = 0.0f; Result = HMM_LengthVec4(A); return(Result); } HINLINE float HMM_LengthSquared(hmm_vec2 A) { float Result = 0.0f; Result = HMM_LengthSquaredVec2(A); return(Result); } HINLINE float HMM_LengthSquared(hmm_vec3 A) { float Result = 0.0f; Result = HMM_LengthSquaredVec3(A); return(Result); } HINLINE float HMM_LengthSquared(hmm_vec4 A) { float Result = 0.0f; Result = HMM_LengthSquaredVec4(A); return(Result); } HINLINE hmm_vec2 HMM_Normalize(hmm_vec2 A) { hmm_vec2 Result = {0}; Result = HMM_NormalizeVec2(A); return(Result); } HINLINE hmm_vec3 HMM_Normalize(hmm_vec3 A) { hmm_vec3 Result = {0}; Result = HMM_NormalizeVec3(A); return(Result); } HINLINE hmm_vec4 HMM_Normalize(hmm_vec4 A) { hmm_vec4 Result = {0}; Result = HMM_NormalizeVec4(A); return(Result); } HINLINE hmm_quaternion HMM_Normalize(hmm_quaternion A) { hmm_quaternion Result = {0}; Result = HMM_NormalizeQuaternion(A); return(Result); } HINLINE float HMM_Dot(hmm_vec2 VecOne, hmm_vec2 VecTwo) { float Result = 0; Result = HMM_DotVec2(VecOne, VecTwo); return(Result); } HINLINE float HMM_Dot(hmm_vec3 VecOne, hmm_vec3 VecTwo) { float Result = 0; Result = HMM_DotVec3(VecOne, VecTwo); return(Result); } HINLINE float HMM_Dot(hmm_vec4 VecOne, hmm_vec4 VecTwo) { float Result = 0; Result = HMM_DotVec4(VecOne, VecTwo); return(Result); } HINLINE float HMM_Dot(hmm_quaternion QuatOne, hmm_quaternion QuatTwo) { float Result = 0; Result = HMM_DotQuaternion(QuatOne, QuatTwo); return(Result); } HINLINE hmm_vec2 HMM_Add(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = {0}; Result = HMM_AddVec2(Left, Right); return (Result); } HINLINE hmm_vec3 HMM_Add(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = {0}; Result = HMM_AddVec3(Left, Right); return (Result); } HINLINE hmm_vec4 HMM_Add(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = {0}; Result = HMM_AddVec4(Left, Right); return (Result); } HINLINE hmm_mat4 HMM_Add(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = {0}; Result = HMM_AddMat4(Left, Right); return (Result); } HINLINE hmm_quaternion HMM_Add(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = {0}; Result = HMM_AddQuaternion(Left, Right); return(Result); } HINLINE hmm_vec2 HMM_Subtract(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = {0}; Result = HMM_SubtractVec2(Left, Right); return (Result); } HINLINE hmm_vec3 HMM_Subtract(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = {0}; Result = HMM_SubtractVec3(Left, Right); return (Result); } HINLINE hmm_vec4 HMM_Subtract(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = {0}; Result = HMM_SubtractVec4(Left, Right); return (Result); } HINLINE hmm_mat4 HMM_Subtract(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = {0}; Result = HMM_SubtractMat4(Left, Right); return (Result); } HINLINE hmm_quaternion HMM_Subtract(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = {0}; Result = HMM_SubtractQuaternion(Left, Right); return (Result); } HINLINE hmm_vec2 HMM_Multiply(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = {0}; Result = HMM_MultiplyVec2(Left, Right); return (Result); } HINLINE hmm_vec2 HMM_Multiply(hmm_vec2 Left, float Right) { hmm_vec2 Result = {0}; Result = HMM_MultiplyVec2f(Left, Right); return (Result); } HINLINE hmm_vec3 HMM_Multiply(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = {0}; Result = HMM_MultiplyVec3(Left, Right); return (Result); } HINLINE hmm_vec3 HMM_Multiply(hmm_vec3 Left, float Right) { hmm_vec3 Result = {0}; Result = HMM_MultiplyVec3f(Left, Right); return (Result); } HINLINE hmm_vec4 HMM_Multiply(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = {0}; Result = HMM_MultiplyVec4(Left, Right); return (Result); } HINLINE hmm_vec4 HMM_Multiply(hmm_vec4 Left, float Right) { hmm_vec4 Result = {0}; Result = HMM_MultiplyVec4f(Left, Right); return (Result); } HINLINE hmm_mat4 HMM_Multiply(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = {0}; Result = HMM_MultiplyMat4(Left, Right); return (Result); } HINLINE hmm_mat4 HMM_Multiply(hmm_mat4 Left, float Right) { hmm_mat4 Result = {0}; Result = HMM_MultiplyMat4f(Left, Right); return (Result); } HINLINE hmm_vec4 HMM_Multiply(hmm_mat4 Matrix, hmm_vec4 Vector) { hmm_vec4 Result = {0}; Result = HMM_MultiplyMat4ByVec4(Matrix, Vector); return (Result); } HINLINE hmm_quaternion HMM_Multiply(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = {0}; Result = HMM_MultiplyQuaternion(Left, Right); return (Result); } HINLINE hmm_quaternion HMM_Multiply(hmm_quaternion Left, float Right) { hmm_quaternion Result = {0}; Result = HMM_MultiplyQuaternionF(Left, Right); return (Result); } HINLINE hmm_quaternion HMM_Multiply(float Left, hmm_quaternion Right) { hmm_quaternion Result = {0}; Result = HMM_MultiplyQuaternionF(Right, Left); return (Result); } HINLINE hmm_vec2 HMM_Divide(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = {0}; Result = HMM_DivideVec2(Left, Right); return (Result); } HINLINE hmm_vec2 HMM_Divide(hmm_vec2 Left, float Right) { hmm_vec2 Result = {0}; Result = HMM_DivideVec2f(Left, Right); return (Result); } HINLINE hmm_vec3 HMM_Divide(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = {0}; Result = HMM_DivideVec3(Left, Right); return (Result); } HINLINE hmm_vec3 HMM_Divide(hmm_vec3 Left, float Right) { hmm_vec3 Result = {0}; Result = HMM_DivideVec3f(Left, Right); return (Result); } HINLINE hmm_vec4 HMM_Divide(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = {0}; Result = HMM_DivideVec4(Left, Right); return (Result); } HINLINE hmm_vec4 HMM_Divide(hmm_vec4 Left, float Right) { hmm_vec4 Result = {0}; Result = HMM_DivideVec4f(Left, Right); return (Result); } HINLINE hmm_mat4 HMM_Divide(hmm_mat4 Left, float Right) { hmm_mat4 Result = {0}; Result = HMM_DivideMat4f(Left, Right); return (Result); } HINLINE hmm_quaternion HMM_Divide(hmm_quaternion Left, float Right) { hmm_quaternion Result = {0}; Result = HMM_DivideQuaternionF(Left, Right); return (Result); } HINLINE hmm_vec2 operator+(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = {0}; Result = HMM_Add(Left, Right); return (Result); } HINLINE hmm_vec3 operator+(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = {0}; Result = HMM_Add(Left, Right); return (Result); } HINLINE hmm_vec4 operator+(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = {0}; Result = HMM_Add(Left, Right); return (Result); } HINLINE hmm_mat4 operator+(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = {0}; Result = HMM_Add(Left, Right); return (Result); } HINLINE hmm_quaternion operator+(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = {0}; Result = HMM_Add(Left, Right); return (Result); } HINLINE hmm_vec2 operator-(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = {0}; Result = HMM_Subtract(Left, Right); return (Result); } HINLINE hmm_vec3 operator-(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = {0}; Result = HMM_Subtract(Left, Right); return (Result); } HINLINE hmm_vec4 operator-(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = {0}; Result = HMM_Subtract(Left, Right); return (Result); } HINLINE hmm_mat4 operator-(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = {0}; Result = HMM_Subtract(Left, Right); return (Result); } HINLINE hmm_quaternion operator-(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = {0}; Result = HMM_Subtract(Left, Right); return (Result); } HINLINE hmm_vec2 operator*(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = {0}; Result = HMM_Multiply(Left, Right); return (Result); } HINLINE hmm_vec3 operator*(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = {0}; Result = HMM_Multiply(Left, Right); return (Result); } HINLINE hmm_vec4 operator*(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = HMM_Multiply(Left, Right); return (Result); } HINLINE hmm_vec2 operator*(hmm_vec2 Left, float Right) { hmm_vec2 Result = {0}; Result = HMM_Multiply(Left, Right); return (Result); } HINLINE hmm_vec3 operator*(hmm_vec3 Left, float Right) { hmm_vec3 Result = {0}; Result = HMM_Multiply(Left, Right); return (Result); } HINLINE hmm_vec4 operator*(hmm_vec4 Left, float Right) { hmm_vec4 Result = {0}; Result = HMM_Multiply(Left, Right); return (Result); } HINLINE hmm_mat4 operator*(hmm_mat4 Left, float Right) { hmm_mat4 Result = {0}; Result = HMM_Multiply(Left, Right); return (Result); } HINLINE hmm_vec2 operator*(float Left, hmm_vec2 Right) { hmm_vec2 Result = {0}; Result = HMM_Multiply(Right, Left); return (Result); } HINLINE hmm_vec3 operator*(float Left, hmm_vec3 Right) { hmm_vec3 Result = {0}; Result = HMM_Multiply(Right, Left); return (Result); } HINLINE hmm_vec4 operator*(float Left, hmm_vec4 Right) { hmm_vec4 Result = {0}; Result = HMM_Multiply(Right, Left); return (Result); } HINLINE hmm_mat4 operator*(float Left, hmm_mat4 Right) { hmm_mat4 Result = {0}; Result = HMM_Multiply(Right, Left); return (Result); } HINLINE hmm_mat4 operator*(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = {0}; Result = HMM_Multiply(Left, Right); return (Result); } HINLINE hmm_vec4 operator*(hmm_mat4 Matrix, hmm_vec4 Vector) { hmm_vec4 Result = {0}; Result = HMM_Multiply(Matrix, Vector); return (Result); } HINLINE hmm_quaternion operator*(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = {0}; Result = HMM_Multiply(Left, Right); return (Result); } HINLINE hmm_quaternion operator*(hmm_quaternion Left, float Right) { hmm_quaternion Result = {0}; Result = HMM_Multiply(Left, Right); return (Result); } HINLINE hmm_quaternion operator*(float Left, hmm_quaternion Right) { hmm_quaternion Result = {0}; Result = HMM_Multiply(Right, Left); return (Result); } HINLINE hmm_vec2 operator/(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = {0}; Result = HMM_Divide(Left, Right); return (Result); } HINLINE hmm_vec3 operator/(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = {0}; Result = HMM_Divide(Left, Right); return (Result); } HINLINE hmm_vec4 operator/(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = {0}; Result = HMM_Divide(Left, Right); return (Result); } HINLINE hmm_vec2 operator/(hmm_vec2 Left, float Right) { hmm_vec2 Result = {0}; Result = HMM_Divide(Left, Right); return (Result); } HINLINE hmm_vec3 operator/(hmm_vec3 Left, float Right) { hmm_vec3 Result = {0}; Result = HMM_Divide(Left, Right); return (Result); } HINLINE hmm_vec4 operator/(hmm_vec4 Left, float Right) { hmm_vec4 Result = {0}; Result = HMM_Divide(Left, Right); return (Result); } HINLINE hmm_mat4 operator/(hmm_mat4 Left, float Right) { hmm_mat4 Result = {0}; Result = HMM_Divide(Left, Right); return (Result); } HINLINE hmm_quaternion operator/(hmm_quaternion Left, float Right) { hmm_quaternion Result = {0}; Result = HMM_Divide(Left, Right); return (Result); } HINLINE hmm_vec2 & operator+=(hmm_vec2 &Left, hmm_vec2 Right) { return (Left = Left + Right); } HINLINE hmm_vec3 & operator+=(hmm_vec3 &Left, hmm_vec3 Right) { return (Left = Left + Right); } HINLINE hmm_vec4 & operator+=(hmm_vec4 &Left, hmm_vec4 Right) { return (Left = Left + Right); } HINLINE hmm_mat4 & operator+=(hmm_mat4 &Left, hmm_mat4 Right) { return (Left = Left + Right); } HINLINE hmm_quaternion & operator+=(hmm_quaternion &Left, hmm_quaternion Right) { return (Left = Left + Right); } HINLINE hmm_vec2 & operator-=(hmm_vec2 &Left, hmm_vec2 Right) { return (Left = Left - Right); } HINLINE hmm_vec3 & operator-=(hmm_vec3 &Left, hmm_vec3 Right) { return (Left = Left - Right); } HINLINE hmm_vec4 & operator-=(hmm_vec4 &Left, hmm_vec4 Right) { return (Left = Left - Right); } HINLINE hmm_mat4 & operator-=(hmm_mat4 &Left, hmm_mat4 Right) { return (Left = Left - Right); } HINLINE hmm_quaternion & operator-=(hmm_quaternion &Left, hmm_quaternion Right) { return (Left = Left - Right); } HINLINE hmm_vec2 & operator/=(hmm_vec2 &Left, hmm_vec2 Right) { return (Left = Left / Right); } HINLINE hmm_vec3 & operator/=(hmm_vec3 &Left, hmm_vec3 Right) { return (Left = Left / Right); } HINLINE hmm_vec4 & operator/=(hmm_vec4 &Left, hmm_vec4 Right) { return (Left = Left / Right); } HINLINE hmm_vec2 & operator/=(hmm_vec2 &Left, float Right) { return (Left = Left / Right); } HINLINE hmm_vec3 & operator/=(hmm_vec3 &Left, float Right) { return (Left = Left / Right); } HINLINE hmm_vec4 & operator/=(hmm_vec4 &Left, float Right) { return (Left = Left / Right); } HINLINE hmm_mat4 & operator/=(hmm_mat4 &Left, float Right) { return (Left = Left / Right); } HINLINE hmm_quaternion & operator/=(hmm_quaternion &Left, float Right) { return (Left = Left / Right); } HINLINE hmm_vec2 & operator*=(hmm_vec2 &Left, hmm_vec2 Right) { return (Left = Left * Right); } HINLINE hmm_vec3 & operator*=(hmm_vec3 &Left, hmm_vec3 Right) { return (Left = Left * Right); } HINLINE hmm_vec4 & operator*=(hmm_vec4 &Left, hmm_vec4 Right) { return (Left = Left * Right); } HINLINE hmm_vec2 & operator*=(hmm_vec2 &Left, float Right) { return (Left = Left * Right); } HINLINE hmm_vec3 & operator*=(hmm_vec3 &Left, float Right) { return (Left = Left * Right); } HINLINE hmm_vec4 & operator*=(hmm_vec4 &Left, float Right) { return (Left = Left * Right); } HINLINE hmm_mat4 & operator*=(hmm_mat4 &Left, float Right) { return (Left = Left * Right); } HINLINE hmm_quaternion & operator*=(hmm_quaternion &Left, float Right) { return (Left = Left * Right); } #endif /* HANDMADE_MATH_CPP_MODE */ #endif /* HANDMADE_MATH_IMPLEMENTATION */ yquake2-QUAKE2_7_10/src/client/refresh/gl3/header/local.h000066400000000000000000000372101321245476300230100ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2016-2017 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. * * ======================================================================= * * Local header for the OpenGL3 refresher. * * ======================================================================= */ #ifndef SRC_CLIENT_REFRESH_GL3_HEADER_LOCAL_H_ #define SRC_CLIENT_REFRESH_GL3_HEADER_LOCAL_H_ #ifdef IN_IDE_PARSER // this is just a hack to get proper auto-completion in IDEs: // using system headers for their parsers/indexers but glad for real build // (in glad glFoo is just a #define to glad_glFoo or sth, which screws up autocompletion) // (you may have to configure your IDE to #define IN_IDE_PARSER, but not for building!) #define GL_GLEXT_PROTOTYPES 1 #include #include #else #include "../glad/include/glad/glad.h" #endif #include "../../ref_shared.h" #include "HandmadeMath.h" #if 0 // only use this for development .. #define STUB_ONCE(msg) do { \ static int show=1; \ if(show) { \ show = 0; \ R_Printf(PRINT_ALL, "STUB: %s() %s\n", __FUNCTION__, msg); \ } \ } while(0); #else // .. so make this a no-op in released code #define STUB_ONCE(msg) #endif // a wrapper around glVertexAttribPointer() to stay sane // (caller doesn't have to cast to GLintptr and then void*) static inline void qglVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset) { glVertexAttribPointer(index, size, type, normalized, stride, (const void*)offset); } static inline void qglVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset) { glVertexAttribIPointer(index, size, type, stride, (void*)offset); } // attribute locations for vertex shaders enum { GL3_ATTRIB_POSITION = 0, GL3_ATTRIB_TEXCOORD = 1, // for normal texture GL3_ATTRIB_LMTEXCOORD = 2, // for lightmap GL3_ATTRIB_COLOR = 3, // per-vertex color GL3_ATTRIB_NORMAL = 4, // vertex normal GL3_ATTRIB_LIGHTFLAGS = 5 // uint, each set bit means "dyn light i affects this surface" }; // TODO: do we need the following configurable? static const int gl3_solid_format = GL_RGB; static const int gl3_alpha_format = GL_RGBA; static const int gl3_tex_solid_format = GL_RGB; static const int gl3_tex_alpha_format = GL_RGBA; extern unsigned gl3_rawpalette[256]; extern unsigned d_8to24table[256]; typedef struct { const char *renderer_string; const char *vendor_string; const char *version_string; const char *glsl_version_string; //const char *extensions_string; deprecated in GL3 int major_version; int minor_version; qboolean compat_profile; // ---- qboolean anisotropic; // is GL_EXT_texture_filter_anisotropic supported? qboolean debug_output; // is GL_ARB_debug_output supported? // ---- float max_anisotropy; } gl3config_t; typedef struct { GLuint shaderProgram; GLint uniLmScales; hmm_vec4 lmScales[4]; } gl3ShaderInfo_t; typedef struct { GLfloat gamma; GLfloat intensity; GLfloat intensity2D; // for HUD, menus etc // entries of std140 UBOs are aligned to multiples of their own size // so we'll need to pad accordingly for following vec4 GLfloat _padding; hmm_vec4 color; } gl3UniCommon_t; typedef struct { hmm_mat4 transMat4; } gl3Uni2D_t; typedef struct { hmm_mat4 transProjMat4; hmm_mat4 transViewMat4; hmm_mat4 transModelMat4; GLfloat scroll; // for SURF_FLOWING GLfloat time; // for warping surfaces like water & possibly other things GLfloat alpha; // for translucent surfaces (water, glass, ..) GLfloat overbrightbits; // gl3_overbrightbits, applied to lightmaps (and elsewhere to models) GLfloat particleFadeFactor; // gl3_particle_fade_factor, higher => less fading out towards edges GLfloat _padding[3]; // again, some padding to ensure this has right size } gl3Uni3D_t; extern const hmm_mat4 gl3_identityMat4; typedef struct { vec3_t origin; GLfloat _padding; vec3_t color; GLfloat intensity; } gl3UniDynLight; typedef struct { gl3UniDynLight dynLights[MAX_DLIGHTS]; GLuint numDynLights; GLfloat _padding[3]; } gl3UniLights_t; enum { // width and height used to be 128, so now we should be able to get the same lightmap data // that used 32 lightmaps before into one, so 4 lightmaps should be enough BLOCK_WIDTH = 1024, BLOCK_HEIGHT = 512, LIGHTMAP_BYTES = 4, MAX_LIGHTMAPS = 4, MAX_LIGHTMAPS_PER_SURFACE = MAXLIGHTMAPS // 4 }; typedef struct { // TODO: what of this do we need? qboolean fullscreen; int prev_mode; unsigned char *d_16to8table; // each lightmap consists of 4 sub-lightmaps allowing changing shadows on the same surface // used for switching on/off light and stuff like that. // most surfaces only have one really and the remaining for are filled with dummy data GLuint lightmap_textureIDs[MAX_LIGHTMAPS][MAX_LIGHTMAPS_PER_SURFACE]; // instead of lightmap_textures+i use lightmap_textureIDs[i] GLuint currenttexture; // bound to GL_TEXTURE0 int currentlightmap; // lightmap_textureIDs[currentlightmap] bound to GL_TEXTURE1 GLuint currenttmu; // GL_TEXTURE0 or GL_TEXTURE1 //float camera_separation; //enum stereo_modes stereo_mode; GLuint currentVAO; GLuint currentVBO; GLuint currentEBO; GLuint currentShaderProgram; GLuint currentUBO; // NOTE: make sure si2D is always the first shaderInfo (or adapt GL3_ShutdownShaders()) gl3ShaderInfo_t si2D; // shader for rendering 2D with textures gl3ShaderInfo_t si2Dcolor; // shader for rendering 2D with flat colors gl3ShaderInfo_t si3Dlm; // a regular opaque face (e.g. from brush) with lightmap // TODO: lm-only variants for gl_lightmap 1 gl3ShaderInfo_t si3Dtrans; // transparent is always w/o lightmap gl3ShaderInfo_t si3DcolorOnly; // used for beams - no lightmaps gl3ShaderInfo_t si3Dturb; // for water etc - always without lightmap gl3ShaderInfo_t si3DlmFlow; // for flowing/scrolling things with lightmap (conveyor, ..?) gl3ShaderInfo_t si3DtransFlow; // for transparent flowing/scrolling things (=> no lightmap) gl3ShaderInfo_t si3Dsky; // guess what.. gl3ShaderInfo_t si3Dsprite; // for sprites gl3ShaderInfo_t si3DspriteAlpha; // for sprites with alpha-testing gl3ShaderInfo_t si3Dalias; // for models gl3ShaderInfo_t si3DaliasColor; // for models w/ flat colors // NOTE: make sure siParticle is always the last shaderInfo (or adapt GL3_ShutdownShaders()) gl3ShaderInfo_t siParticle; // for particles. surprising, right? GLuint vao3D, vbo3D; // for brushes etc, using 1 floats as vertex input (x,y,z, s,t, lms,lmt, normX,normY,normZ) GLuint vaoAlias, vboAlias, eboAlias; // for models, using 9 floats as (x,y,z, s,t, r,g,b,a) GLuint vaoParticle, vboParticle; // for particles, using 9 floats (x,y,z, size,distance, r,g,b,a) // UBOs and their data gl3UniCommon_t uniCommonData; gl3Uni2D_t uni2DData; gl3Uni3D_t uni3DData; gl3UniLights_t uniLightsData; GLuint uniCommonUBO; GLuint uni2DUBO; GLuint uni3DUBO; GLuint uniLightsUBO; } gl3state_t; extern gl3config_t gl3config; extern gl3state_t gl3state; extern viddef_t vid; extern refdef_t gl3_newrefdef; extern int gl3_visframecount; /* bumped when going to a new PVS */ extern int gl3_framecount; /* used for dlight push checking */ extern int gl3_viewcluster, gl3_viewcluster2, gl3_oldviewcluster, gl3_oldviewcluster2; extern int c_brush_polys, c_alias_polys; /* NOTE: struct image_s* is what re.RegisterSkin() etc return so no gl3image_s! * (I think the client only passes the pointer around and doesn't know the * definition of this struct, so this being different from struct image_s * in ref_gl should be ok) */ 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 */ GLuint texnum; /* gl texture binding */ float sl, tl, sh, th; /* 0,0 - 1,1 unless part of the scrap */ // qboolean scrap; // currently unused qboolean has_alpha; } gl3image_t; enum {MAX_GL3TEXTURES = 1024}; // include this down here so it can use gl3image_t #include "model.h" typedef struct { int internal_format; int current_lightmap_texture; // index into gl3state.lightmap_textureIDs[] //msurface_t *lightmap_surfaces[MAX_LIGHTMAPS]; - no more lightmap chains, lightmaps are rendered multitextured int allocated[BLOCK_WIDTH]; /* the lightmap texture data needs to be kept in main memory so texsubimage can update properly */ byte lightmap_buffers[MAX_LIGHTMAPS_PER_SURFACE][4 * BLOCK_WIDTH * BLOCK_HEIGHT]; } gl3lightmapstate_t; extern gl3model_t *gl3_worldmodel; extern gl3model_t *currentmodel; extern entity_t *currententity; extern float gl3depthmin, gl3depthmax; extern cplane_t frustum[4]; extern vec3_t gl3_origin; hmm_mat4 gl3_projectionMatrix; // eye cord -> clip coord hmm_mat4 gl3_world_matrix; // the view matrix: world coord -> eye coord extern gl3image_t *gl3_notexture; /* use for bad textures */ extern gl3image_t *gl3_particletexture; /* little dot for particles */ extern int gl_filter_min; extern int gl_filter_max; static inline void GL3_UseProgram(GLuint shaderProgram) { if(shaderProgram != gl3state.currentShaderProgram) { gl3state.currentShaderProgram = shaderProgram; glUseProgram(shaderProgram); } } static inline void GL3_BindVAO(GLuint vao) { if(vao != gl3state.currentVAO) { gl3state.currentVAO = vao; glBindVertexArray(vao); } } static inline void GL3_BindVBO(GLuint vbo) { if(vbo != gl3state.currentVBO) { gl3state.currentVBO = vbo; glBindBuffer(GL_ARRAY_BUFFER, vbo); } } static inline void GL3_BindEBO(GLuint ebo) { if(ebo != gl3state.currentEBO) { gl3state.currentEBO = ebo; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); } } extern qboolean GL3_CullBox(vec3_t mins, vec3_t maxs); extern void GL3_RotateForEntity(entity_t *e); // gl3_sdl.c extern qboolean have_stencil; extern int GL3_PrepareForWindow(void); extern int GL3_InitContext(void* win); extern void GL3_SetSwapInterval(void); extern qboolean GL3_IsVsyncActive(void); extern void GL3_EndFrame(void); extern void GL3_ShutdownWindow(qboolean contextOnly); // gl3_misc.c extern void GL3_InitParticleTexture(void); extern void GL3_ScreenShot(void); extern void GL3_SetDefaultState(void); // gl3_model.c extern int registration_sequence; extern void GL3_Mod_Init(void); extern void GL3_Mod_FreeAll(void); extern void GL3_BeginRegistration(char *model); extern struct model_s * GL3_RegisterModel(char *name); extern void GL3_EndRegistration(void); extern void GL3_Mod_Modellist_f(void); extern byte* GL3_Mod_ClusterPVS(int cluster, gl3model_t *model); extern mleaf_t* GL3_Mod_PointInLeaf(vec3_t p, gl3model_t *model); // gl3_draw.c extern void GL3_Draw_InitLocal(void); extern void GL3_Draw_ShutdownLocal(void); extern gl3image_t * GL3_Draw_FindPic(char *name); extern void GL3_Draw_GetPicSize(int *w, int *h, char *pic); extern int GL3_Draw_GetPalette(void); extern void GL3_Draw_PicScaled(int x, int y, char *pic, float factor); extern void GL3_Draw_StretchPic(int x, int y, int w, int h, char *pic); extern void GL3_Draw_CharScaled(int x, int y, int num, float scale); extern void GL3_Draw_TileClear(int x, int y, int w, int h, char *pic); extern void GL3_Draw_Fill(int x, int y, int w, int h, int c); extern void GL3_Draw_FadeScreen(void); extern void GL3_Draw_Flash(const float color[4], float x, float y, float w, float h); extern void GL3_Draw_StretchRaw(int x, int y, int w, int h, int cols, int rows, byte *data); // gl3_image.c static inline void GL3_SelectTMU(GLenum tmu) { if(gl3state.currenttmu != tmu) { glActiveTexture(tmu); gl3state.currenttmu = tmu; } } extern void GL3_TextureMode(char *string); extern void GL3_Bind(GLuint texnum); extern void GL3_BindLightmap(int lightmapnum); extern gl3image_t *GL3_LoadPic(char *name, byte *pic, int width, int realwidth, int height, int realheight, imagetype_t type, int bits); extern gl3image_t *GL3_FindImage(char *name, imagetype_t type); extern gl3image_t *GL3_RegisterSkin(char *name); extern void GL3_ShutdownImages(void); extern void GL3_FreeUnusedImages(void); extern void GL3_ImageList_f(void); // gl3_light.c extern void GL3_MarkLights(dlight_t *light, int bit, mnode_t *node); extern void GL3_PushDlights(void); extern void GL3_LightPoint(vec3_t p, vec3_t color); extern void GL3_BuildLightMap(msurface_t *surf, int offsetInLMbuf, int stride); // gl3_lightmap.c #define GL_LIGHTMAP_FORMAT GL_RGBA extern void GL3_LM_InitBlock(void); extern void GL3_LM_UploadBlock(void); extern qboolean GL3_LM_AllocBlock(int w, int h, int *x, int *y); extern void GL3_LM_BuildPolygonFromSurface(msurface_t *fa); extern void GL3_LM_CreateSurfaceLightmap(msurface_t *surf); extern void GL3_LM_BeginBuildingLightmaps(gl3model_t *m); extern void GL3_LM_EndBuildingLightmaps(void); // gl3_warp.c extern void GL3_EmitWaterPolys(msurface_t *fa); extern void GL3_SubdivideSurface(msurface_t *fa, gl3model_t* loadmodel); extern void GL3_SetSky(char *name, float rotate, vec3_t axis); extern void GL3_DrawSkyBox(void); extern void GL3_ClearSkyBox(void); extern void GL3_AddSkySurface(msurface_t *fa); // gl3_surf.c extern void GL3_SurfInit(void); extern void GL3_SurfShutdown(void); extern void GL3_DrawGLPoly(msurface_t *fa); extern void GL3_DrawGLFlowingPoly(msurface_t *fa); extern void GL3_DrawTriangleOutlines(void); extern void GL3_DrawAlphaSurfaces(void); extern void GL3_DrawBrushModel(entity_t *e); extern void GL3_DrawWorld(void); extern void GL3_MarkLeaves(void); // gl3_mesh.c extern void GL3_DrawAliasModel(entity_t *e); extern void GL3_ResetShadowAliasModels(void); extern void GL3_DrawAliasShadows(void); extern void GL3_ShutdownMeshes(void); // gl3_shaders.c extern qboolean GL3_RecreateShaders(void); extern qboolean GL3_InitShaders(void); extern void GL3_ShutdownShaders(void); extern void GL3_UpdateUBOCommon(void); extern void GL3_UpdateUBO2D(void); extern void GL3_UpdateUBO3D(void); extern void GL3_UpdateUBOLights(void); // ############ Cvars ########### extern cvar_t *gl_msaa_samples; extern cvar_t *gl_swapinterval; extern cvar_t *gl_retexturing; extern cvar_t *vid_fullscreen; extern cvar_t *gl_mode; extern cvar_t *gl_customwidth; extern cvar_t *gl_customheight; extern cvar_t *gl_nolerp_list; extern cvar_t *gl_nobind; extern cvar_t *gl_lockpvs; extern cvar_t *gl_novis; extern cvar_t *gl_cull; extern cvar_t *gl_zfix; extern cvar_t *gl_fullbright; extern cvar_t *gl_norefresh; extern cvar_t *gl_lefthand; extern cvar_t *gl_farsee; extern cvar_t *gl_drawworld; extern cvar_t *vid_gamma; extern cvar_t *gl3_intensity; extern cvar_t *gl3_intensity_2D; extern cvar_t *gl_anisotropic; extern cvar_t *gl_lightlevel; extern cvar_t *gl3_overbrightbits; extern cvar_t *gl3_particle_fade_factor; extern cvar_t *gl3_particle_square; extern cvar_t *gl_modulate; extern cvar_t *gl_lightmap; extern cvar_t *gl_shadows; extern cvar_t *gl_dynamic; extern cvar_t *gl3_debugcontext; #endif /* SRC_CLIENT_REFRESH_GL3_HEADER_LOCAL_H_ */ yquake2-QUAKE2_7_10/src/client/refresh/gl3/header/model.h000066400000000000000000000126361321245476300230230ustar00rootroot00000000000000/* * 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 SRC_CLIENT_REFRESH_GL3_HEADER_MODEL_H_ #define SRC_CLIENT_REFRESH_GL3_HEADER_MODEL_H_ enum { SIDE_FRONT = 0, SIDE_BACK = 1, SIDE_ON = 2 }; enum { SURF_PLANEBACK = 2, SURF_DRAWSKY = 4, SURF_DRAWTURB = 0x10, SURF_DRAWBACKGROUND = 0x40, SURF_UNDERWATER = 0x80 }; // used for vertex array elements when drawing brushes, sprites, sky and more // (ok, it has the layout used for rendering brushes, but is not used there) typedef struct gl3_3D_vtx_s { vec3_t pos; float texCoord[2]; float lmTexCoord[2]; // lightmap texture coordinate (sometimes unused) vec3_t normal; GLuint lightFlags; // bit i set means: dynlight i affects surface } gl3_3D_vtx_t; // used for vertex array elements when drawing models typedef struct gl3_alias_vtx_s { GLfloat pos[3]; GLfloat texCoord[2]; GLfloat color[4]; } gl3_alias_vtx_t; /* 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 */ gl3image_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?) */ gl3_3D_vtx_t vertices[4]; /* variable sized */ } 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; not used/needed anymore mtexinfo_t *texinfo; /* lighting info */ int dlightframe; int dlightbits; int lightmaptexturenum; byte styles[MAXLIGHTMAPS]; // MAXLIGHTMAPS = MAX_LIGHTMAPS_PER_SURFACE (defined in local.h) // I think cached_light is not used/needed anymore //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 */ // this, must be struct model_s, not gl3model_s, // because struct model_s* is returned by re.RegisterModel() 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 */ gl3image_t *skins[MAX_MD2SKINS]; int extradatasize; void *extradata; } gl3model_t; #endif /* SRC_CLIENT_REFRESH_GL3_HEADER_MODEL_H_ */ yquake2-QUAKE2_7_10/src/client/refresh/ref_shared.h000066400000000000000000000036521321245476300221060ustar00rootroot00000000000000/* * 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 shared between different refreshers (but not with client) * * ======================================================================= */ #ifndef SRC_CLIENT_REFRESH_REF_SHARED_H_ #define SRC_CLIENT_REFRESH_REF_SHARED_H_ #include "../header/ref.h" /* * 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 enum { mod_bad, mod_brush, mod_sprite, mod_alias } modtype_t; #define MAX_LBM_HEIGHT 480 extern void R_Printf(int level, const char* msg, ...) __attribute__ ((format (printf, 2, 3))); extern void LoadPCX(char *origname, byte **pic, byte **palette, int *width, int *height); extern void GetPCXInfo(char *filename, int *width, int *height); extern qboolean LoadSTB(const char *origname, const char* type, byte **pic, int *width, int *height); extern void GetWalInfo(char *name, int *width, int *height); #endif /* SRC_CLIENT_REFRESH_REF_SHARED_H_ */ yquake2-QUAKE2_7_10/src/client/sound/000077500000000000000000000000001321245476300173175ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/sound/header/000077500000000000000000000000001321245476300205475ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/client/sound/header/cdaudio.h000066400000000000000000000024161321245476300223330ustar00rootroot00000000000000/* * 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_7_10/src/client/sound/header/local.h000066400000000000000000000160311321245476300220130ustar00rootroot00000000000000/* * 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; extern cvar_t* s_doppler; /* * 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_7_10/src/client/sound/header/sound.h000066400000000000000000000040601321245476300220500ustar00rootroot00000000000000/* * 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); void CL_GetEntitySoundVelocity(int ent, vec3_t vel); void CL_GetViewVelocity(vec3_t vel); #endif yquake2-QUAKE2_7_10/src/client/sound/header/vorbis.h000066400000000000000000000036671321245476300222400ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_7_10/src/client/sound/ogg.c000066400000000000000000000376011321245476300202460ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 and add 256. 256 are about 12 seconds worth of sound, more than enough to be resilent against underruns. */ if (ogg_numbufs == 0 || active_buffers < ogg_numbufs - 256) { ogg_numbufs = active_buffers + 256; } /* 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; } #ifdef USE_OPENAL if (sound_started == SS_OAL) { AL_UnqueueRawSamples(); } #endif } /* * 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_7_10/src/client/sound/openal.c000066400000000000000000000417661321245476300207570ustar00rootroot00000000000000/* * 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]) // avg US male height / q2PlayerHeight = 1.764f / 56.0f = 0.0315f // see: https://en.wikipedia.org/wiki/List_of_average_human_height_worldwide // see: PutClientInServer(edict_t *ent) bbox #define AL_METER_OF_Q2_UNIT 0.0315f /* The OpenAL implementation should support at least this number of sources */ #define MIN_CHANNELS 16 /* Globals */ 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; qalGetSourcei(streamSource, AL_SOURCE_STATE, &state); if (state == AL_STOPPED) { streamPlaying = false; } else { /* Un-queue any already pleyed 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); 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; vec3_t velocity; 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)); if (s_doppler->value) { CL_GetEntitySoundVelocity(ch->entnum, velocity); VectorScale(velocity, AL_METER_OF_Q2_UNIT, velocity); qalSource3f(ch->srcnum, AL_VELOCITY, AL_UnpackVector(velocity)); } 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; 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; /* it seems like looped sounds are always played at full volume * see SDL_AddLoopSounds() which calls SDL_SpatializeOrigin() with volume 255.0f * so set it to full volume (1.0 * s_volume). */ ch->oal_vol = s_volume->value; 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]; vec3_t listener_velocity; paintedtime = cls.realtime; /* set listener (player) parameters */ AL_CopyVector(listener_forward, orientation); AL_CopyVector(listener_up, orientation + 3); qalDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); qalListener3f(AL_POSITION, AL_UnpackVector(listener_origin)); qalListenerfv(AL_ORIENTATION, orientation); if (s_doppler->value) { CL_GetViewVelocity(listener_velocity); VectorScale(listener_velocity, AL_METER_OF_Q2_UNIT, listener_velocity); qalListener3f(AL_VELOCITY, AL_UnpackVector(listener_velocity)); } /* 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; } /* 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); // exaggerate 2x because realistic is barely noticeable if (s_doppler->value) { qalDopplerFactor(2.0f); } return true; } /* * Shuts the OpenAL backend down */ void AL_Shutdown(void) { Com_Printf("Shutting down OpenAL.\n"); AL_StopAllChannels(); 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_7_10/src/client/sound/sound.c000066400000000000000000000460051321245476300206200ustar00rootroot00000000000000/* * 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; cvar_t* s_doppler; 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; } if (sfx->name[0]) { // with !fixed we have all sounds related directly to player, // e.g. players fire, pain, menu if (!ps->fixed_origin) { Haptic_Feedback(sfx->name); } } 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) { 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); s_doppler = Cvar_Get("s_doppler", "1", 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_7_10/src/client/sound/wave.c000066400000000000000000000071431321245476300204320ustar00rootroot00000000000000/* * 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_7_10/src/common/000077500000000000000000000000001321245476300162015ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/common/argproc.c000066400000000000000000000056711321245476300200130ustar00rootroot00000000000000/* * 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_7_10/src/common/clientserver.c000066400000000000000000000133331321245476300210550ustar00rootroot00000000000000/* * 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_VPrintf(int print_level, const char *fmt, va_list argptr) { if((print_level == PRINT_DEVELOPER) && (!developer || !developer->value)) { return; /* don't confuse non-developers with techie stuff... */ } else { int i; char msg[MAXPRINTMSG]; int msgLen = vsnprintf(msg, MAXPRINTMSG, fmt, argptr); if(msgLen >= MAXPRINTMSG) msgLen = MAXPRINTMSG-1; if (rd_target) { if ((msgLen + 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 // remove unprintable characters for(i=0; i '\r')) { switch(c) { // no idea if the following two are ever sent here, but in conchars.pcx they look like this // so do the replacements.. won't hurt I guess.. case 0x10: msg[i] = '['; break; case 0x11: msg[i] = ']'; break; // horizontal line chars case 0x1D: case 0x1F: msg[i] = '-'; break; case 0x1E: msg[i] = '='; break; default: // just replace all other unprintable chars with space, should be good enough msg[i] = ' '; } } } /* also echo to debugging console */ Sys_ConsoleOutput(msg); /* logfile */ if (logfile_active && logfile_active->value) { char name[MAX_OSPATH]; 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 */ } } } } /* * Both client and server can use this, and it will output * to the apropriate place. */ void Com_Printf(char *fmt, ...) { va_list argptr; va_start(argptr, fmt); Com_VPrintf(PRINT_ALL, fmt, argptr); va_end(argptr); } /* * A Com_Printf that only shows up if the "developer" cvar is set */ void Com_DPrintf(char *fmt, ...) { va_list argptr; va_start(argptr, fmt); Com_VPrintf(PRINT_DEVELOPER, fmt, argptr); va_end(argptr); } /* * 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_7_10/src/common/cmdparser.c000066400000000000000000000377731321245476300203460ustar00rootroot00000000000000/* * 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"); 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_7_10/src/common/collision.c000066400000000000000000001016611321245476300203450ustar00rootroot00000000000000/* * 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_LoadSubmodels: 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_LoadSurfaces: 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_LoadNodes: 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_LoadBrushes: 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_LoadLeafs: 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_LoadPlanes: 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_LoadLeafBrushes: 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_LoadBrushSides: 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_LoadAreas: 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_LoadAreaPortals: 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_7_10/src/common/crc.c000066400000000000000000000235641321245476300171260ustar00rootroot00000000000000/* * 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 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 }; 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 }; void CRC_Init(unsigned short *crcvalue) { *crcvalue = CRC_INIT_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; } 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; } yquake2-QUAKE2_7_10/src/common/cvar.c000066400000000000000000000216271321245476300173100ustar00rootroot00000000000000/* * 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_BuildGameSpecificSearchPath(var->string); } } 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_BuildGameSpecificSearchPath(var->string); } } } /* * 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); } } fflush(f); 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); } /* * Free list of cvars */ void Cvar_Fini(void) { cvar_t *var; for (var = cvar_vars; var;) { cvar_t *c = var; Z_Free(var->string); Z_Free(var->name); Z_Free(var); var = c->next; } Cmd_RemoveCommand("cvarlist"); Cmd_RemoveCommand("set"); } yquake2-QUAKE2_7_10/src/common/filesystem.c000066400000000000000000000756711321245476300205510ustar00rootroot00000000000000/* * 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]; qboolean file_from_pak; cvar_t *fs_basedir; cvar_t *fs_cddir; cvar_t *fs_gamedirvar; cvar_t *fs_debug; fsHandle_t *FS_GetFileByHandle(fileHandle_t f); // -------- // Raw search path, the actual search // bath is build from this one. typedef struct fsRawPath_s { char path[MAX_OSPATH]; qboolean create; struct fsRawPath_s *next; } fsRawPath_t; fsRawPath_t *fs_rawPath; // -------- /* * 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]; } /* * 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)); } /* * 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 = false; 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; } } // Evil hack for maps.lst and players/ // TODO: A flag to ignore paks would be better if ((strcmp(fs_gamedirvar->string, "") == 0) && search->pack) { if ((strcmp(name, "maps.lst") == 0)|| (strncmp(name, "players/", 8) == 0)) { 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! */ if (fs_debug->value) { Com_Printf("FS_FOpenFile: '%s' (found in '%s').\n", handle->name, pack->name); } if (pack->pak) { /* PAK */ file_from_pak = true; 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_pak = true; 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) { if (fs_debug->value) { Com_Printf("FS_FOpenFile: '%s' (found in '%s').\n", handle->name, search->path); } return FS_FileLength(handle->file); } } } 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->name, 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->name, numFiles); return pack; } #endif /* * 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 } /* * 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_AddDirToSearchPath(char *dir, qboolean create) { char **list; char path[MAX_OSPATH]; int i, j; int nfiles; fsPack_t *pack = NULL; fsSearchPath_t *search; size_t len = strlen(dir); // The directory must not end with an /. It would // f*ck up the logic in other parts of the game... if (dir[len - 1] == '/') { dir[len - 1] = '\0'; } else if (dir[len - 1] == '\\') { dir[len - 1] = '\0'; } // Set the current directory as game directory. This // is somewhat fragile since the game directory MUST // be the last directory added to the search path. Q_strlcpy(fs_gamedir, dir, sizeof(fs_gamedir)); if (create) { FS_CreatePath(fs_gamedir); } // Add the directory itself. search = Z_Malloc(sizeof(fsSearchPath_t)); Q_strlcpy(search->path, dir, sizeof(search->path)); search->next = fs_searchPaths; fs_searchPaths = search; // We need to add numbered paks in te directory in // sequence and all other paks after them. Otherwise // the gamedata may break. 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; } } // And as said above all other pak 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); // Nothing here, next pak type please. 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++) { // If the pak starts with the string 'pak' it's ignored. // This is somewhat stupid, it would be better to ignore // just pak%d... 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); } } void FS_BuildGenericSearchPath(void) { // We may not use the va() function from shared.c // since it's buffersize is 1024 while most OS have // a maximum path size of 4096... char path[MAX_OSPATH]; fsRawPath_t *search = fs_rawPath; while (search != NULL) { Com_sprintf(path, sizeof(path), "%s/%s", search->path, BASEDIRNAME); FS_AddDirToSearchPath(path, search->create); search = search->next; } // Until here we've added the generic directories to the // search path. Save the current head node so we can // distinguish generic and specialized directories. fs_baseSearchPaths = fs_searchPaths; // We need to create the game directory. Sys_Mkdir(fs_gamedir); // We need to create the screenshot directory since the // render dll doesn't link the filesystem stuff. Com_sprintf(path, sizeof(path), "%s/scrnshot", fs_gamedir); Sys_Mkdir(path); } void FS_BuildGameSpecificSearchPath(char *dir) { // We may not use the va() function from shared.c // since it's buffersize is 1024 while most OS have // a maximum path size of 4096... char path[MAX_OSPATH]; int i; fsRawPath_t *search; fsSearchPath_t *next; // This is against PEBCAK. The user may give us paths like // xatrix/ or even /home/stupid/quake2/xatrix. if (!*dir || !strcmp(dir, ".") || strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\")) { Com_Printf("Gamedir should be a single filename, not a path.\n"); return; } // BASEDIR is already added as a generic directory. Adding it // again as a specialised directory breaks the logic in other // parts of the code. This may happen if the user does something // like ./quake2 +set game baseq2 if(!Q_stricmp(dir, BASEDIRNAME)) { return; } // We may already have specialised directories in our search // path. This can happen if the server changes the mod. Let's // remove them. 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); } } // Enforce a renderer and sound backend restart to // purge all internal caches. This is rather hacky // but Quake II doesn't have a better mechanism... if ((dedicated != NULL) && (dedicated->value != 1)) { Cbuf_AddText("vid_restart\nsnd_restart\n"); } // The game was reset to baseq2. Nothing to do here. if ((Q_stricmp(dir, BASEDIRNAME) == 0) || (*dir == 0)) { Cvar_FullSet("gamedir", "", CVAR_SERVERINFO | CVAR_NOSET); Cvar_FullSet("game", "", CVAR_LATCH | CVAR_SERVERINFO); // fs_gamedir must be reset to the last // dir of the generic search path. Q_strlcpy(fs_gamedir, fs_baseSearchPaths->path, sizeof(fs_gamedir)); } else { Cvar_FullSet("gamedir", dir, CVAR_SERVERINFO | CVAR_NOSET); search = fs_rawPath; while (search != NULL) { Com_sprintf(path, sizeof(path), "%s/%s", search->path, dir); FS_AddDirToSearchPath(path, search->create); search = search->next; } } // Create the game directory. Sys_Mkdir(fs_gamedir); // We need to create the screenshot directory since the // render dll doesn't link the filesystem stuff. Com_sprintf(path, sizeof(path), "%s/scrnshot", fs_gamedir); Sys_Mkdir(path); } // -------- void FS_AddDirToRawPath (const char *dir, qboolean create) { fsRawPath_t *search; // Add the directory search = Z_Malloc(sizeof(fsRawPath_t)); Q_strlcpy(search->path, dir, sizeof(search->path)); search->create = create; search->next = fs_rawPath; fs_rawPath = search; } void FS_BuildRawPath(void) { // Add $HOME/.yq2 (MUST be the last dir!) if (!is_portable) { const char *homedir = Sys_GetHomeDir(); if (homedir != NULL) { FS_AddDirToRawPath(homedir, true); } } // Add $binarydir const char *binarydir = Sys_GetBinaryDir(); if(binarydir[0] != '\0') { FS_AddDirToRawPath(binarydir, false); } // Add $basedir/ FS_AddDirToRawPath(fs_basedir->string, false); // Add SYSTEMDIR #ifdef SYSTEMWIDE FS_AddDirToRawPath(SYSTEMDIR, false); #endif // The CD must be the last directory of the path, // otherwise we cannot be sure that the game won't // stream the videos from the CD. if (fs_cddir->string[0] != '\0') { FS_AddDirToRawPath(fs_cddir->string, false); } } // -------- 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); // Register cvars fs_basedir = Cvar_Get("basedir", ".", CVAR_NOSET); fs_cddir = Cvar_Get("cddir", "", CVAR_NOSET); fs_gamedirvar = Cvar_Get("game", "", CVAR_LATCH | CVAR_SERVERINFO); fs_debug = Cvar_Get("fs_debug", "0", 0); // Build search path FS_BuildRawPath(); FS_BuildGenericSearchPath(); if (fs_gamedirvar->string[0] != '\0') { FS_BuildGameSpecificSearchPath(fs_gamedirvar->string); } // Debug output Com_Printf("Using '%s' for writing.\n", fs_gamedir); } yquake2-QUAKE2_7_10/src/common/frame.c000066400000000000000000000245511321245476300174460ustar00rootroot00000000000000/* * 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. * * ======================================================================= * * Generic frame handling. * * ======================================================================= */ #include "header/common.h" #include "header/zone.h" #include cvar_t *developer; cvar_t *modder; cvar_t *timescale; cvar_t *fixedtime; cvar_t *cl_maxfps; 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; #ifndef DEDICATED_ONLY FILE *log_stats_file; cvar_t *cl_async; cvar_t *cl_timedemo; cvar_t *gl_maxfps; cvar_t *host_speeds; cvar_t *log_stats; cvar_t *showtrace; #endif // Forward declarations #ifndef DEDICATED_ONLY int GLimp_GetRefreshRate(void); qboolean R_IsVSyncActive(void); #endif /* host_speeds times */ int time_before_game; int time_after_game; int time_before_ref; int time_after_ref; // Used in the network- and input pathes. int curtime; #ifndef DEDICATED_ONLY void Key_Init(void); void SCR_EndLoadingPlaque(void); #endif // ---- void Qcommon_Init(int argc, char **argv) { // Jump point used in emergency situations. if (setjmp(abortframe)) { Sys_Error("Error during initialization"); } // Initialize zone malloc(). z_chain.next = z_chain.prev = &z_chain; // Start early subsystems. 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(); // The filesystems needs to be initialized after the cvars. FS_InitFilesystem(); // Add and execute configuration files. Cbuf_AddText("exec default.cfg\n"); Cbuf_AddText("exec yq2.cfg\n"); Cbuf_AddText("exec config.cfg\n"); Cbuf_AddText("exec autoexec.cfg\n"); Cbuf_AddEarlyCommands(true); Cbuf_Execute(); // Zone malloc statistics. Cmd_AddCommand("z_stats", Z_Stats_f); // cvars cl_maxfps = Cvar_Get("cl_maxfps", "60", CVAR_ARCHIVE); developer = Cvar_Get("developer", "0", 0); fixedtime = Cvar_Get("fixedtime", "0", 0); logfile_active = Cvar_Get("logfile", "1", CVAR_ARCHIVE); modder = Cvar_Get("modder", "0", 0); timescale = Cvar_Get("timescale", "1", 0); char *s; s = va("%s %s %s %s", YQ2VERSION, YQ2ARCH, BUILD_DATE, YQ2OSTYPE); Cvar_Get("version", s, CVAR_SERVERINFO | CVAR_NOSET); #ifndef DEDICATED_ONLY cl_async = Cvar_Get("cl_async", "1", CVAR_ARCHIVE); cl_timedemo = Cvar_Get("timedemo", "0", 0); dedicated = Cvar_Get("dedicated", "0", CVAR_NOSET); gl_maxfps = Cvar_Get("gl_maxfps", "95", CVAR_ARCHIVE); host_speeds = Cvar_Get("host_speeds", "0", 0); log_stats = Cvar_Get("log_stats", "0", 0); showtrace = Cvar_Get("showtrace", "0", 0); #else dedicated = Cvar_Get("dedicated", "1", CVAR_NOSET); #endif // We can't use the clients "quit" command when running dedicated. if (dedicated->value) { Cmd_AddCommand("quit", Com_Quit); } // Start late subsystem. Sys_Init(); NET_Init(); Netchan_Init(); SV_Init(); #ifndef DEDICATED_ONLY CL_Init(); #endif // Everythings up, let's add + cmds from command line. if (!Cbuf_AddLateCommands()) { if (!dedicated->value) { // Start demo loop... Cbuf_AddText("d1\n"); } else { // ...or dedicated server. 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"); } #ifndef DEDICATED_ONLY void Qcommon_Frame(int msec) { // Used for the dedicated server console. char *s; // Statistics. int time_before = 0; int time_between = 0; int time_after; // Target packetframerate. int pfps; //Target renderframerate. int rfps; // Time since last packetframe in microsec. static int packetdelta = 1000000; // Time since last renderframe in microsec. static int renderdelta = 1000000; // Accumulated time since last client run. static int clienttimedelta = 0; // Accumulated time since last server run. static int servertimedelta = 0; /* A packetframe runs the server and the client, but not the renderer. The minimal interval of packetframes is about 10.000 microsec. If run more often the movement prediction in pmove.c breaks. That's the Q2 variant if the famous 125hz bug. */ qboolean packetframe = true; /* A rendererframe runs the renderer, but not the client. The minimal interval is about 1000 microseconds. */ qboolean renderframe = true; /* In case of ERR_DROP we're jumping here. Don't know if that' really save but it seems to work. So leave it alone. */ if (setjmp(abortframe)) { return; } 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; } } } // Timing debug crap. Just for historical reasons. if (fixedtime->value) { msec = (int)fixedtime->value; } else if (timescale->value) { msec *= timescale->value; } 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; } // gl_maxfps > 1000 breaks things, and so does <= 0 // so cap to 1000 and treat <= 0 as "as fast as possible", which is 1000 if (gl_maxfps->value > 1000 || gl_maxfps->value < 1) { Cvar_SetValue("gl_maxfps", 1000); } if(cl_maxfps->value > 250) { Cvar_SetValue("cl_maxfps", 130); } else if(cl_maxfps->value < 1) { Cvar_SetValue("cl_maxfps", 60); } // Save global time for network- und input code. curtime = Sys_Milliseconds(); // Calculate target packet- and renderframerate. if (R_IsVSyncActive()) { rfps = GLimp_GetRefreshRate(); if (rfps > gl_maxfps->value) { rfps = (int)gl_maxfps->value; } } else { rfps = (int)gl_maxfps->value; } pfps = (cl_maxfps->value > rfps) ? rfps : cl_maxfps->value; // Calculate timings. packetdelta += msec; renderdelta += msec; clienttimedelta += msec; servertimedelta += msec; if (!cl_timedemo->value) { if (cl_async->value) { // Network frames.. if (packetdelta < (1000000.0f / pfps)) { packetframe = false; } // Render frames. if (renderdelta < (1000000.0f / rfps)) { renderframe = false; } } else { // Cap frames at target framerate. if (renderdelta < (1000000.0f / rfps)) { renderframe = false; packetframe = false; } } } else if (clienttimedelta < 1000 || servertimedelta < 1000) { return; } // Dedicated server terminal console. do { s = Sys_ConsoleInput(); if (s) { Cbuf_AddText(va("%s\n", s)); } } while (s); Cbuf_Execute(); if (host_speeds->value) { time_before = Sys_Milliseconds(); } // Run the serverframe. if (packetframe) { SV_Frame(servertimedelta); servertimedelta = 0; } if (host_speeds->value) { time_between = Sys_Milliseconds(); } // Run the client frame. if (packetframe || renderframe) { CL_Frame(packetdelta, renderdelta, clienttimedelta, packetframe, renderframe); clienttimedelta = 0; } 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); } // Reset deltas if necessary. if (packetframe) { packetdelta = 0; } if (renderframe) { renderdelta = 0; } } #else void Qcommon_Frame(int msec) { // For the dedicated server terminal console. char *s; // Target packetframerate. int pfps; // Time since last packetframe in microsec. static int packetdelta = 1000000; // Accumulated time since last server run. static int servertimedelta = 0; /* A packetframe runs the server and the client, but not the renderer. The minimal interval of packetframes is about 10.000 microsec. If run more often the movement prediction in pmove.c breaks. That's the Q2 variant if the famous 125hz bug. */ qboolean packetframe = true; /* In case of ERR_DROP we're jumping here. Don't know if that' really save but it seems to work. So leave it alone. */ if (setjmp(abortframe)) { return; } // Timing debug crap. Just for historical reasons. if (fixedtime->value) { msec = (int)fixedtime->value; } else if (timescale->value) { msec *= timescale->value; } // Save global time for network- und input code. curtime = Sys_Milliseconds(); // Target framerate. pfps = (int)cl_maxfps->value; // Calculate timings. packetdelta += msec; servertimedelta += msec; // Network frame time. if (packetdelta < (1000000.0f / pfps)) { packetframe = false; } // Dedicated server terminal console. do { s = Sys_ConsoleInput(); if (s) { Cbuf_AddText(va("%s\n", s)); } } while (s); Cbuf_Execute(); // Run the serverframe. if (packetframe) { SV_Frame(servertimedelta); servertimedelta = 0; } // Reset deltas if necessary. if (packetframe) { packetdelta = 0; } } #endif void Qcommon_Shutdown(void) { Cvar_Fini(); } yquake2-QUAKE2_7_10/src/common/glob.c000066400000000000000000000075151321245476300173000ustar00rootroot00000000000000/* * 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_7_10/src/common/header/000077500000000000000000000000001321245476300174315ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/common/header/common.h000066400000000000000000000560721321245476300211040ustar00rootroot00000000000000/* * 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" #define YQ2VERSION "7.10" #define BASEDIRNAME "baseq2" #ifndef YQ2OSTYPE #error YQ2OSTYPE should be defined by the build system #endif #ifndef YQ2ARCH #error YQ2ARCH should be defined by the build system #endif #ifndef BUILD_DATE #define BUILD_DATE __DATE__ #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); void Cvar_Fini(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 qboolean 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_BuildGameSpecificSearchPath(char *dir); char *FS_Gamedir(void); char *FS_NextPath(char *prevpath); 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, ...) __attribute__ ((format (printf, 1, 2))); void Com_DPrintf(char *fmt, ...) __attribute__ ((format (printf, 1, 2))); void Com_VPrintf(int print_level, const char *fmt, va_list argptr); /* print_level is PRINT_ALL or PRINT_DEVELOPER */ 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; /* Hack for portable client */ extern qboolean is_portable; 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 OSTYPE 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); long long Sys_Microseconds(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); void Sys_RedirectStdout(void); void Sys_SetupFPU(void); /* CLIENT / SERVER SYSTEMS */ void CL_Init(void); void CL_Drop(void); void CL_Shutdown(void); void CL_Frame(int packetdelta, int renderdelta, int timedelta, qboolean packetframe, qboolean renderframe); 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_7_10/src/common/header/crc.h000066400000000000000000000021161321245476300203510ustar00rootroot00000000000000/* * 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); unsigned short CRC_Block(byte *start, int count); #endif yquake2-QUAKE2_7_10/src/common/header/files.h000066400000000000000000000243501321245476300207100ustar00rootroot00000000000000/* * 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_7_10/src/common/header/glob.h000066400000000000000000000020541321245476300205260ustar00rootroot00000000000000/* * 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_7_10/src/common/header/shared.h000066400000000000000000001054471321245476300210630ustar00rootroot00000000000000/* * 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 // by default dlls don't export any functions, use this to // make a function visible (for GetGameAPI(), GetRefAPI() and similar) #define Q2_DLL_EXPORTED __declspec(dllexport) #else // not Win32 (Linux, BSD, Mac, ..) #define MAX_OSPATH 4096 /* max length of a filesystem pathname */ #define LATCH_CVAR_SAVELENGTH 128 // by default our .so/.dylibs don't export any functions, use this to // make a function visible (for GetGameAPI(), GetRefAPI() and similar) #define Q2_DLL_EXPORTED __attribute__((__visibility__("default"))) #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<<2) */ 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; /* SURF_* */ int value; /* unused */ } 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_7_10/src/common/header/zone.h000066400000000000000000000022121321245476300205520ustar00rootroot00000000000000/* * 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_7_10/src/common/md4.c000066400000000000000000000101511321245476300170270ustar00rootroot00000000000000/* * 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_7_10/src/common/movemsg.c000066400000000000000000000441111321245476300200230ustar00rootroot00000000000000/* * 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_7_10/src/common/netchan.c000066400000000000000000000236371321245476300200000ustar00rootroot00000000000000/* * 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_7_10/src/common/pmove.c000066400000000000000000000633771321245476300175130ustar00rootroot00000000000000/* * 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_7_10/src/common/shared/000077500000000000000000000000001321245476300174475ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/common/shared/flash.c000066400000000000000000000321671321245476300207210ustar00rootroot00000000000000/* * 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_7_10/src/common/shared/rand.c000066400000000000000000000027511321245476300205440ustar00rootroot00000000000000/* * 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_7_10/src/common/shared/shared.c000066400000000000000000000524041321245476300210660ustar00rootroot00000000000000/* * 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_7_10/src/common/szone.c000066400000000000000000000042701321245476300175060ustar00rootroot00000000000000/* * 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_7_10/src/common/unzip/000077500000000000000000000000001321245476300173465ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/common/unzip/ioapi.c000066400000000000000000000072111321245476300206140ustar00rootroot00000000000000/* 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_7_10/src/common/unzip/ioapi.h000066400000000000000000000050571321245476300206270ustar00rootroot00000000000000/* 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_7_10/src/common/unzip/unzip.c000066400000000000000000001477221321245476300206740ustar00rootroot00000000000000/* 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_7_10/src/common/unzip/unzip.h000066400000000000000000000313531321245476300206710ustar00rootroot00000000000000/* 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_7_10/src/common/zone.c000066400000000000000000000041401321245476300173170ustar00rootroot00000000000000/* * 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_7_10/src/game/000077500000000000000000000000001321245476300156225ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/g_ai.c000066400000000000000000000601241321245476300166700ustar00rootroot00000000000000/* * 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; } if(self->enemy) { 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); } if(visible(self, self->enemy)) { 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|| !self->enemy || !self->enemy->inuse) { 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 = (int)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 < (int)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 || !self->enemy || !self->enemy->inuse) { 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)) { if (self->monsterinfo.melee) { 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)) { if (self->monsterinfo.attack) { 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 || !self->enemy || !self->enemy->inuse) { enemy_vis = false; 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) && !visible(self, self->goalentity)) { if ((level.time - self->enemy->last_sound_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 = (int)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 = (int)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); } /* look for other coop players here */ if (coop->value && (self->monsterinfo.search_time < level.time)) { if (FindTarget(self)) { return true; } } if (self->enemy) { 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 || !self->enemy || !self->enemy->inuse) { 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_7_10/src/game/g_chase.c000066400000000000000000000107651321245476300173700ustar00rootroot00000000000000/* * 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_7_10/src/game/g_cmds.c000066400000000000000000000470151321245476300172310ustar00rootroot00000000000000/* * 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_7_10/src/game/g_combat.c000066400000000000000000000354671321245476300175600ustar00rootroot00000000000000/* * 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 (attacker && attacker->classname && 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_7_10/src/game/g_func.c000066400000000000000000001576071321245476300172470ustar00rootroot00000000000000/* * 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; } /* Map quirk for waste3 (to make that secret armor behind * the secret wall - this func_door - count, #182) */ if (Q_stricmp(level.mapname, "waste3") == 0 && Q_stricmp(ent->model, "*12") == 0) { ent->target = "t117"; } } /* * 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_7_10/src/game/g_items.c000066400000000000000000001342241321245476300174230ustar00rootroot00000000000000/* * 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_7_10/src/game/g_main.c000066400000000000000000000214721321245476300172260ustar00rootroot00000000000000/* * 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 */ Q2_DLL_EXPORTED 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_7_10/src/game/g_misc.c000066400000000000000000001443361321245476300172420ustar00rootroot00000000000000/* * 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_BBOX; 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_BBOX; 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_BBOX; 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_BBOX; 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_BBOX; 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_BBOX; 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_7_10/src/game/g_monster.c000066400000000000000000000445001321245476300177660ustar00rootroot00000000000000/* * 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; } 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_7_10/src/game/g_phys.c000066400000000000000000000557021321245476300172700ustar00rootroot00000000000000/* * 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_7_10/src/game/g_spawn.c000066400000000000000000000576261321245476300174440ustar00rootroot00000000000000/* * 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; if (!string) { return NULL; } 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_strcasecmp(f->name, (char *)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) = (float)strtod(value, (char **)NULL); break; case F_ANGLEHACK: v = (float)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; if (!ent) { return NULL; } 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; static qboolean monster_count_city3 = false; 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; } /* * The 'monsters' count in city3.bsp is wrong. * There're two monsters triggered in a hidden * and unreachable room next to the security * pass. * * We need to make sure that this hack is only * applied once! */ if(!Q_stricmp(level.mapname, "city3") && !monster_count_city3) { level.total_monsters = level.total_monsters - 2; monster_count_city3 = true; } /* 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: THIS ORDER MUST MATCH THE DEFINES IN g_local.h you can add more, max 19 (pete change)these models are only loaded in coop or deathmatch. not singleplayer. */ if (coop->value || deathmatch->value) { 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_7_10/src/game/g_svcmds.c000066400000000000000000000142141321245476300175750ustar00rootroot00000000000000/* * 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_7_10/src/game/g_target.c000066400000000000000000000552531321245476300175740ustar00rootroot00000000000000/* * 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_7_10/src/game/g_trigger.c000066400000000000000000000346531321245476300177520ustar00rootroot00000000000000/* * 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_7_10/src/game/g_turret.c000066400000000000000000000276401321245476300176320ustar00rootroot00000000000000/* * 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_7_10/src/game/g_utils.c000066400000000000000000000266601321245476300174460ustar00rootroot00000000000000/* * 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_7_10/src/game/g_weapon.c000066400000000000000000000611351321245476300175730ustar00rootroot00000000000000/* * 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_7_10/src/game/header/000077500000000000000000000000001321245476300170525ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/header/game.h000066400000000000000000000174771321245476300201540ustar00rootroot00000000000000/* * 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_7_10/src/game/header/local.h000066400000000000000000000670741321245476300203330ustar00rootroot00000000000000/* * 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; int 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 last_sound_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_7_10/src/game/monster/000077500000000000000000000000001321245476300173115ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/berserker/000077500000000000000000000000001321245476300212755ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/berserker/berserker.c000066400000000000000000000247251321245476300234370ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/berserker/berserker.h000066400000000000000000000156411321245476300234410ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/boss2/000077500000000000000000000000001321245476300203415ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/boss2/boss2.c000066400000000000000000000400651321245476300215420ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/boss2/boss2.h000066400000000000000000000126441321245476300215510ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/boss3/000077500000000000000000000000001321245476300203425ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/boss3/boss3.c000066400000000000000000000043331321245476300215420ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/boss3/boss31.c000066400000000000000000000432341321245476300216260ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/boss3/boss31.h000066400000000000000000000132321321245476300216260ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/boss3/boss32.c000066400000000000000000000536641321245476300216370ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/boss3/boss32.h000066400000000000000000000330741321245476300216350ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/brain/000077500000000000000000000000001321245476300204045ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/brain/brain.c000066400000000000000000000357121321245476300216530ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/brain/brain.h000066400000000000000000000150611321245476300216530ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/chick/000077500000000000000000000000001321245476300203725ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/chick/chick.c000066400000000000000000000416751321245476300216340ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/chick/chick.h000066400000000000000000000205561321245476300216340ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/flipper/000077500000000000000000000000001321245476300207525ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/flipper/flipper.c000066400000000000000000000245031321245476300225630ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/flipper/flipper.h000066400000000000000000000121241321245476300225640ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/float/000077500000000000000000000000001321245476300204165ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/float/float.c000066400000000000000000000404041321245476300216710ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/float/float.h000066400000000000000000000165571321245476300217120ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/flyer/000077500000000000000000000000001321245476300204325ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/flyer/flyer.c000066400000000000000000000366141321245476300217310ustar00rootroot00000000000000/* * 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 || !self->enemy) { 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_7_10/src/game/monster/flyer/flyer.h000066400000000000000000000115351321245476300217310ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/gladiator/000077500000000000000000000000001321245476300212575ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/gladiator/gladiator.c000066400000000000000000000250111321245476300233700ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/gladiator/gladiator.h000066400000000000000000000061151321245476300234010ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/gunner/000077500000000000000000000000001321245476300206075ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/gunner/gunner.c000066400000000000000000000373331321245476300222620ustar00rootroot00000000000000/* * 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; /* 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_7_10/src/game/monster/gunner/gunner.h000066400000000000000000000142431321245476300222620ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/hover/000077500000000000000000000000001321245476300204345ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/hover/hover.c000066400000000000000000000360751321245476300217360ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/hover/hover.h000066400000000000000000000142511321245476300217330ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/infantry/000077500000000000000000000000001321245476300211435ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/infantry/infantry.c000066400000000000000000000367131321245476300231530ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/infantry/infantry.h000066400000000000000000000142261321245476300231530ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/insane/000077500000000000000000000000001321245476300205665ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/insane/insane.c000066400000000000000000000437671321245476300222300ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/insane/insane.h000066400000000000000000000201311321245476300222110ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/medic/000077500000000000000000000000001321245476300203725ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/medic/medic.c000066400000000000000000000460621321245476300216270ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/medic/medic.h000066400000000000000000000153631321245476300216340ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/misc/000077500000000000000000000000001321245476300202445ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/misc/move.c000066400000000000000000000273371321245476300213720ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/misc/player.h000066400000000000000000000136541321245476300217220ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/mutant/000077500000000000000000000000001321245476300206215ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/mutant/mutant.c000066400000000000000000000367351321245476300223130ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/mutant/mutant.h000066400000000000000000000113151321245476300223030ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/parasite/000077500000000000000000000000001321245476300211215ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/parasite/parasite.c000066400000000000000000000347331321245476300231070ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/parasite/parasite.h000066400000000000000000000075231321245476300231110ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/soldier/000077500000000000000000000000001321245476300207525ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/soldier/soldier.c000066400000000000000000000707221321245476300225670ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/soldier/soldier.h000066400000000000000000000322621321245476300225710ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/supertank/000077500000000000000000000000001321245476300213255ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/supertank/supertank.c000066400000000000000000000421311321245476300235060ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/supertank/supertank.h000066400000000000000000000171101321245476300235120ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/tank/000077500000000000000000000000001321245476300202465ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/monster/tank/tank.c000066400000000000000000000514261321245476300213570ustar00rootroot00000000000000/* * 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_7_10/src/game/monster/tank/tank.h000066400000000000000000000210261321245476300213550ustar00rootroot00000000000000/* * 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_7_10/src/game/player/000077500000000000000000000000001321245476300171165ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/player/client.c000066400000000000000000001410111321245476300205360ustar00rootroot00000000000000/* * 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_7_10/src/game/player/hud.c000066400000000000000000000315401321245476300200450ustar00rootroot00000000000000/* * 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; gi.linkentity(ent); /* 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_7_10/src/game/player/trail.c000066400000000000000000000057601321245476300204050ustar00rootroot00000000000000/* * 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_7_10/src/game/player/view.c000066400000000000000000000664331321245476300202500ustar00rootroot00000000000000/* * 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 moove^^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_7_10/src/game/player/weapon.c000066400000000000000000001050041321245476300205530ustar00rootroot00000000000000/* * 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->last_sound_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_7_10/src/game/savegame/000077500000000000000000000000001321245476300174125ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/savegame/savegame.c000066400000000000000000000544041321245476300213550ustar00rootroot00000000000000/* * 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-2" #ifndef BUILD_DATE #define BUILD_DATE __DATE__ #endif /* * This macros are used to prohibit loading of savegames * created on other systems or architectures. This will * crash q2 in spectacular ways */ #ifndef YQ2OSTYPE #error YQ2OSTYPE should be defined by the build system #endif #ifndef YQ2ARCH #error YQ2ARCH should be defined by the build system #endif /* * Older operating systen and architecture detection * macros, implemented by savegame version YQ2-1. */ #if defined(__APPLE__) #define OSTYPE_1 "MacOS X" #elif defined(__FreeBSD__) #define OSTYPE_1 "FreeBSD" #elif defined(__OpenBSD__) #define OSTYPE_1 "OpenBSD" #elif defined(__linux__) #define OSTYPE_1 "Linux" #elif defined(_WIN32) #define OSTYPE_1 "Windows" #else #define OSTYPE_1 "Unknown" #endif #if defined(__i386__) #define ARCH_1 "i386" #elif defined(__x86_64__) #define ARCH_1 "amd64" #elif defined(__sparc__) #define ARCH_1 "sparc64" #elif defined(__ia64__) #define ARCH_1 "ia64" #else #define ARCH_1 "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, BUILD_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", BUILD_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) - 1); Q_strlcpy(str_game, GAMEVERSION, sizeof(str_game) - 1); Q_strlcpy(str_os, YQ2OSTYPE, sizeof(str_os) - 1); Q_strlcpy(str_arch, YQ2ARCH, sizeof(str_arch) - 1); 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)) { if (strcmp(str_game, GAMEVERSION)) { fclose(f); gi.error("Savegame from another game.so.\n"); } else if (strcmp(str_os, YQ2OSTYPE)) { fclose(f); gi.error("Savegame from another os.\n"); } else if (strcmp(str_arch, YQ2ARCH)) { fclose(f); gi.error("Savegame from another architecture.\n"); } } else if (!strcmp(str_ver, "YQ2-1")) { if (strcmp(str_game, GAMEVERSION)) { fclose(f); gi.error("Savegame from another game.so.\n"); } else if (strcmp(str_os, OSTYPE_1)) { fclose(f); gi.error("Savegame from another os.\n"); } if (!strcmp(str_os, "Windows")) { /* Windows was forced to i386 */ if (strcmp(str_arch, "i386")) { fclose(f); gi.error("Savegame from another architecture.\n"); } } else { if (strcmp(str_arch, ARCH_1)) { fclose(f); gi.error("Savegame from another architecture.\n"); } } } else { fclose(f); gi.error("Savegame from an incompatible version.\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_7_10/src/game/savegame/tables/000077500000000000000000000000001321245476300206645ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/game/savegame/tables/clientfields.h000066400000000000000000000022171321245476300235040ustar00rootroot00000000000000/* * 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_7_10/src/game/savegame/tables/fields.h000066400000000000000000000112641321245476300223070ustar00rootroot00000000000000/* * 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_7_10/src/game/savegame/tables/gamefunc_decs.h000066400000000000000000001713161321245476300236310ustar00rootroot00000000000000/* * 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_7_10/src/game/savegame/tables/gamefunc_list.h000066400000000000000000001325171321245476300236660ustar00rootroot00000000000000/* * 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_7_10/src/game/savegame/tables/gamemmove_decs.h000066400000000000000000000257171321245476300240240ustar00rootroot00000000000000/* * 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_7_10/src/game/savegame/tables/gamemmove_list.h000066400000000000000000000331411321245476300240470ustar00rootroot00000000000000/* * 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_7_10/src/game/savegame/tables/levelfields.h000066400000000000000000000024241321245476300233350ustar00rootroot00000000000000/* * 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_7_10/src/server/000077500000000000000000000000001321245476300162175ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/server/header/000077500000000000000000000000001321245476300174475ustar00rootroot00000000000000yquake2-QUAKE2_7_10/src/server/header/server.h000066400000000000000000000214231321245476300211300ustar00rootroot00000000000000/* * 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_7_10/src/server/sv_cmd.c000066400000000000000000000302361321245476300176420ustar00rootroot00000000000000/* * 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_7_10/src/server/sv_conless.c000066400000000000000000000215421321245476300205450ustar00rootroot00000000000000/* * 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_7_10/src/server/sv_entities.c000066400000000000000000000351521321245476300207250ustar00rootroot00000000000000/* * 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_7_10/src/server/sv_game.c000066400000000000000000000202221321245476300200020ustar00rootroot00000000000000/* * 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_7_10/src/server/sv_init.c000066400000000000000000000255761321245476300200550ustar00rootroot00000000000000/* * 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_7_10/src/server/sv_main.c000066400000000000000000000352451321245476300200300ustar00rootroot00000000000000/* * 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 / 1000; /* 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_7_10/src/server/sv_save.c000066400000000000000000000214461321245476300200400ustar00rootroot00000000000000/* * 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_7_10/src/server/sv_send.c000066400000000000000000000271641321245476300200360ustar00rootroot00000000000000/* * 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_7_10/src/server/sv_user.c000066400000000000000000000340701321245476300200550ustar00rootroot00000000000000/* * 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 qboolean 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_7_10/src/server/sv_world.c000066400000000000000000000326661321245476300202370ustar00rootroot00000000000000/* * 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_7_10/stuff/000077500000000000000000000000001321245476300152515ustar00rootroot00000000000000yquake2-QUAKE2_7_10/stuff/cdripper.sh000077500000000000000000000010161321245476300174160ustar00rootroot00000000000000#!/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_7_10/stuff/cmake/000077500000000000000000000000001321245476300163315ustar00rootroot00000000000000yquake2-QUAKE2_7_10/stuff/cmake/modules/000077500000000000000000000000001321245476300200015ustar00rootroot00000000000000yquake2-QUAKE2_7_10/stuff/cmake/modules/FindOggVorbis.cmake000066400000000000000000000070041321245476300235060ustar00rootroot00000000000000# - 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_7_10/stuff/cmake/modules/FindSDL2.cmake000066400000000000000000000142151321245476300223130ustar00rootroot00000000000000# 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_7_10/stuff/cvarlist.md000066400000000000000000000217541321245476300174330ustar00rootroot00000000000000Yamagi Quake II Console Variables ================================= This lists explains most console variables (cvars) added by Yamagi Quake II. Most of the original clients (Vanilla Quake II) cvars are still in place, however the `r_*` renderer cvars have been renamed to `gl_*` and there are cvars specific to the OpenGL3.2 renderer that start with `gl3_`. Please note: There's normally no need to change any cvar! Use the menu instead. General: -------- * **basedir**: Directory from which the game data is loaded. Can be used in startup scripts, to test binaries, etc. If not set, the directory containing the binaries is used. To use this cvar, set it at startup, like `./quake2 +set basedir /path/to/quake2` * **cl_async**: If set to `1` (the default) the client is asynchronous. The client framerate is fixed, the renderer framerate is variable. This makes it possible to renderer as many frames as desired without any physics and movement problems. The client framerate is controlled by *cl_maxfps*, set to `60` by defaut. The renderer framerate is controlled by *gl_maxfps*. There are two constraints: *gl_maxfps* must be the same or greater than *cl_maxfps*. In case that the vsync is active, *gl_maxfps* must not be lower than the display refresh rate. If *cl_async* is set to `0` *gl_maxfps* is the same as *cl_maxfps*, use *cl_maxfps* to set the framerate. * **cl_drawfps**: Shows the framecounter. The shown value is rather inaccurate and gets less precise with higher framerates, as it only measures full milliseconds. * **in_grab**: Defines how the mouse is grabbed by Quake IIs window. If set to `0` the mouse is never grabbed and if set to `1` it's always grabbed. If set to `2` (the default) the mouse is grabbed during gameplay and released otherwise (in menu, console or if game is paused). Audio: ------ * **al_device**: OpenAL device to use. In most cases there's no need to change this, since the default device is normally the right choice. * **al_driver**: OpenAL library to use. This is usefull if for some reasons several OpenAL libraries are available and Quake II picks the wrong one. The given value is the name of the library, for example `libopenal.so.1`. * **ogg_enable**: Enable Ogg/Vorbis music playback. * **ogg_ignoretrack0**: Normally Quake II disabled the background music if a major objective has been archived by setting the music track to 0. Setting this cvar to `1` disables this behavior, the music keeps playing. * **s_doppler**: If set to `1` (the default) doppler effects are enabled. This is only supported by the OpenAL sound backend. * **s_openal**: Use OpenAL for sound playback. This is enabled by default. OpenAL gives a huge quality boost over the classic sound system and supports surround speakers and HRTF. * **s_underwater**: Dampen sounds if submerged. Enabled by default. Graphics (all renderers): ------------------------- * Most old `r_*` cvars, but renamed to `gl_*` * **vid_renderer**: Selects the renderer library. Possible options are `gl1` (the default) for the old OpenGL 1.4 renderer and `gl3` for the new OpenGL 3.2 renderer. * **cin_force43**: If set to `1` (the default) cinematics are displayed with an aspect ratio of 4:3, regardless what the actual windows size or resolution is. * **cl_gun**: Decides whether the gun is drawn. If set to `0` the gun is omitted. If set to `1` the gun is only drawn if the FOV is equal or smaller than 90. This was the default with Vanilla Quake II. If set to `2` the gun is drawn regardless of the FOV. This is the default in Yamagi Quake II. * **fov**: Sets the field of view. If the *horplus* cvar is set to `1`, this is forced to 90. * **horplus**: If set to 1 (the default) the horplus algorithm is used to calculate an optimal horizontal and vertical field of view, independent of the window or screen aspect ratio or resolution. If enabled *fov* is forced to `90`. * **vid_gamma**: The value used for gamma correction. Higher value looks brighter. The GL1 renderer uses "Hardware Gamma", setting the Gamma of your whole screen to this value in realtime (except on OSX where it's applied to textures on load and thus needs a `vid_restart` after changing). The GL3 renderer applies this to the window in realtime via shaders (on all platforms). This is also set by the brightness slider in the video menu. * **gl_anisotropic**: Anisotropic filtering. Possible values are dependent on the GPU driver, most of them support `1`, `2`, `4`, `8` and `16`. Anisotropic filtering gives a huge improvement to texture quality by a negligible performance impact. * **gl_consolescale** / **gl_hudscale** / **gl_menuscale**, **crosshair_scale**: Scale the console, the HUD, the menu and the crosshair. The value given is the scale factor, a factor of `1` means no scaling. Values greater `1` make the objects bigger, values lower 1 smaller. The special value `-1` sets the optimal scaling factor for the current resolution. * **gl_customheight** / **gl_customwidth**: Specifies a custom resolution, the windows will be *gl_customheight* pixels high and *gl_customwidth* pixels wide. Set *gl_mode* to `-1` to use the custom resolution. * **gl_farsee**: Normally Quake II renders only up to 4096 units. If set to `1` the limit is increased to 8192 units. * **gl_maxfps**: The maximum framerate, if `cl_async` is `1`. Otherwise `cl_maxfps` is used as maximum framerate. See `cl_async` description above for more information. *Note* that vsync (`gl_swapinterval`) also restricts the framerate to the monitor refresh rate, so if vsync is enabled, you won't get more than 60fps on most displays (or 120 on a 120hz display etc). * **gl_msaa_samples**: Full scene anti aliasing samples. The number of samples depends on the GPU driver, most drivers support at least `2`, `4` and `8` samples. If an invalid value is set, the value is reverted to the highest number of samples supported. Especially on OpenGL 3.2 anti aliasing is expensive and can lead to a huge performance hit, so try setting it to a lower value if your framerate is too low. * **gl_nolerp_list**: list seperate by spaces of textures ommitted from bilinear filtering. Used by default to exclude the console and HUD fonts. Make sure to include the default values when extending the list. * **gl_retexturing**: If set to `1` (the default) and a retexturing pack is installed, the high resolution textures are used. * **gl_shadows**: Enables rendering of shadows. Quake IIs shadows are very simple and are prone to render errors. * **gl_swapinterval**: Enables the vsync: frames are synchronized with display refresh rate, should (but doesn't always) prevent tearing. * **gl_zfix**: Sometimes two or even more surfaces overlap and flicker. If this cvar is set to `1` the renderer inserts a small gap between the overlapping surfaces to mitigate the flickering. This may lead to small render errors. Graphics (GL1 only): -------------------- * **intensity**: Sets the color intensity used for 3D rendering. Must be a floating point value, at least `1.0` - default is `2.0`. Applied when textures are loaded, so it needs a `vid_restart`! * **gl_overbrightbits**: Enables overbright bits, brightness scaling of lightmaps and models. Higher values make shadows less dark. Possible values are `0` (no overbright bits), `1` (correct lighting for water), `2` (scale by factor 2) and `3` (scale by factor 3). Applied in realtime, does not need `vid_restart`. * **gl_stencilshadow**: If `gl_shadows` is set to `1`, this makes them look a bit better (no flickering) by using the stencil buffer. (This is always done in GL3, so not configurable there) Graphics (GL3 only): -------------------- * **gl3_debugcontext**: Enables the OpenGL 3.2 renderers debug context, e.g. prints warnings and errors emmitted by the GPU driver. Not supported on OSX. This is a pure debug cvar and slows down rendering. * **gl3_intensity**: Sets the color intensity used for 3D rendering. Similar to GL1 `intensity`, but more flexible: can be any value between 0.0 (completely dark) and 256.0 (very bright). Good values are between `1.0` and `2.0`, default is `1.5`. Applied in realtime via shader, so it does *not* need a `vid_restart`. * **gl3_intensity_2D**: The same for 2D rendering (HUD, menu, console, videos) * **gl3_overbrightbits**: Enables overbright bits, brightness scaling of lightmaps and models. Higher values make shadows less dark. Similar to GL1's `gl_overbrightbits`, but allows any floating point number. Default is `1.3`. In the OpenGL3.2 renderer, no lighting fixes for water are needed, so `1.0` has no special meaning. * **gl3_particle_size**: The size of particles - Default is `40`. * **gl3_particle_fade_factor**: "softness" of particles: higher values look less soft. Defaults to `1.2`. A value of `10` looks similar to the GL1 particles. * **gl3_particle_square**: If set to `1`, particles are rendered as squares, like in the old software renderer or Quake1. Default is `0`. yquake2-QUAKE2_7_10/stuff/misc/000077500000000000000000000000001321245476300162045ustar00rootroot00000000000000yquake2-QUAKE2_7_10/stuff/misc/uncrustify.cfg000066400000000000000000001130621321245476300211030ustar00rootroot00000000000000# # General options # # The type of line endings newlines = lf # auto/lf/crlf/cr # The original size of tabs in the input input_tab_size = 4 # number # The size of tabs in the output (only used if align_with_tabs=true) output_tab_size = 4 # number # The ascii value of the string escape char, usually 92 (\) or 94 (^). (Pawn) string_escape_char = 92 # number # Alternate string escape char for Pawn. Only works right before the quote char. string_escape_char2 = 0 # number # # Indenting # # The number of columns to indent per level. # Usually 2, 3, 4, or 8. indent_columns = 4 # number # How to use tabs when indenting code # 0=spaces only # 1=indent with tabs, align with spaces # 2=indent and align with tabs indent_with_tabs = 2 # number # Whether to indent strings broken by '\' so that they line up indent_align_string = true # false/true # The number of spaces to indent multi-line XML strings. # Requires indent_align_string=True indent_xml_string = 0 # number # Spaces to indent '{' from level indent_brace = 0 # number # Whether braces are indented to the body level indent_braces = false # false/true # Disabled indenting function braces if indent_braces is true indent_braces_no_func = false # false/true # Indent based on the size of the brace parent, ie 'if' => 3 spaces, 'for' => 4 spaces, etc. indent_brace_parent = false # false/true # Whether the 'namespace' body is indented indent_namespace = true # false/true # Whether the 'class' body is indented indent_class = true # false/true # Whether to indent the stuff after a leading class colon indent_class_colon = false # false/true # True: indent continued function call parameters one indent level # False: align parameters under the open paren indent_func_call_param = true # false/true # Same as indent_func_call_param, but for function defs indent_func_def_param = true # false/true # Same as indent_func_call_param, but for function protos indent_func_proto_param = true # false/true # Same as indent_func_call_param, but for class declarations indent_func_class_param = true # false/true # Same as indent_func_call_param, but for class variable constructors indent_func_ctor_var_param = true # false/true # Double the indent for indent_func_xxx_param options indent_func_param_double = true # false/true # The number of spaces to indent a continued '->' or '.' # Usually set to 0, 1, or indent_columns. indent_member = 4 # number # Spaces to indent single line ('//') comments on lines before code indent_sing_line_comments = 0 # number # Spaces to indent 'case' from 'switch' # Usually 0 or indent_columns. indent_switch_case = 4 # number # Spaces to shift the 'case' line, without affecting any other lines # Usually 0. indent_case_shift = 0 # number # Spaces to indent '{' from 'case'. # By default, the brace will appear under the 'c' in case. # Usually set to 0 or indent_columns. indent_case_brace = 0 # number # Whether to indent comments found in first column indent_col1_comment = false # false/true # How to indent goto labels # >0 : absolute column where 1 is the leftmost column # <=0 : subtract from brace indent indent_label = -4 # number # Same as indent_label, but for access specifiers that are followed by a colon indent_access_spec = -4 # number # If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended) indent_paren_nl = false # false/true # Controls the indent of a close paren after a newline. # 0: Indent to body level # 1: Align under the open paren # 2: Indent to the brace level indent_paren_close = 0 # number # Controls the indent of a comma when inside a paren.If TRUE, aligns under the open paren indent_comma_paren = false # false/true # Controls the indent of a BOOL operator when inside a paren.If TRUE, aligns under the open paren indent_bool_paren = false # false/true # If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended) indent_square_nl = false # false/true # Don't change the relative indent of ESQL/C 'EXEC SQL' bodies indent_preserve_sql = false # false/true # # Spacing options # # Add or remove space around arithmetic operator '+', '-', '/', '*', etc sp_arith = force # ignore/add/remove/force # Add or remove space around assignment operator '=', '+=', etc sp_assign = force # ignore/add/remove/force # Add or remove space around boolean operators '&&' and '||' sp_bool = force # ignore/add/remove/force # Add or remove space around compare operator '<', '>', '==', etc sp_compare = force # ignore/add/remove/force # Add or remove space inside '(' and ')' sp_inside_paren = remove # ignore/add/remove/force # Add or remove space between nested parens sp_paren_paren = remove # ignore/add/remove/force # Whether to balance spaces inside nested parens sp_balance_nested_parens = false # false/true # Add or remove space between ')' and '{' sp_paren_brace = force # ignore/add/remove/force # Add or remove space before pointer star '*' sp_before_ptr_star = force # ignore/add/remove/force # Add or remove space between pointer stars '*' sp_between_ptr_star = remove # ignore/add/remove/force # Add or remove space after pointer star '*', if followed by a word. sp_after_ptr_star = remove # ignore/add/remove/force # Add or remove space before reference sign '&' sp_before_byref = force # ignore/add/remove/force # Add or remove space after reference sign '&', if followed by a word. sp_after_byref = remove # ignore/add/remove/force # Add or remove space before '<>' sp_before_angle = remove # ignore/add/remove/force # Add or remove space after '<>' sp_after_angle = remove # ignore/add/remove/force # Add or remove space between '<>' and '(' as found in 'new List();' sp_angle_paren = force # ignore/add/remove/force # Add or remove space between '<>' and a word as in 'List m;' sp_angle_word = force # ignore/add/remove/force # Add or remove space before '(' of 'if', 'for', 'switch', and 'while' sp_before_sparen = force # ignore/add/remove/force # Add or remove space inside if-condition '(' and ')' sp_inside_sparen = remove # ignore/add/remove/force # Add or remove space after ')' of 'if', 'for', 'switch', and 'while' sp_after_sparen = remove # ignore/add/remove/force # Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while' sp_sparen_brace = force # ignore/add/remove/force # Add or remove space before empty statement ';' on 'if', 'for' and 'while' sp_special_semi = force # ignore/add/remove/force # Add or remove space before ';' sp_before_semi = remove # ignore/add/remove/force # Add or remove space before ';' in non-empty 'for' statements sp_before_semi_for = remove # ignore/add/remove/force # Add or remove space before a semicolon of an empty part of a for statment. sp_before_semi_for_empty = add # ignore/add/remove/force # Add or remove space after the final semicolon of an empty part of a for statment: for ( ; ; ). sp_after_semi_for_empty = add # ignore/add/remove/force # Add or remove space before '[' (except '[]') sp_before_square = remove # ignore/add/remove/force # Add or remove space before '[]' sp_before_squares = remove # ignore/add/remove/force # Add or remove space inside '[' and ']' sp_inside_square = remove # ignore/add/remove/force # Add or remove space after ',' sp_after_comma = force # ignore/add/remove/force # Add or remove space before ',' sp_before_comma = remove # ignore/add/remove/force # Add or remove space between 'operator' and operator sign sp_after_operator = force # ignore/add/remove/force # Add or remove space after cast sp_after_cast = remove # ignore/add/remove/force # Add or remove spaces inside cast parens sp_inside_paren_cast = remove # ignore/add/remove/force # Add or remove space between 'sizeof' and '(' sp_sizeof_paren = remove # ignore/add/remove/force # Add or remove space after the tag keyword (Pawn) sp_after_tag = remove # ignore/add/remove/force # Add or remove space inside enum '{' and '}' sp_inside_braces_enum = remove # ignore/add/remove/force # Add or remove space inside struct/union '{' and '}' sp_inside_braces_struct = remove # ignore/add/remove/force # Add or remove space inside '{' and '}' sp_inside_braces = remove # ignore/add/remove/force # Add or remove space inside '<' and '>' sp_inside_angle = remove # ignore/add/remove/force # Add or remove space between return type and function name # A minimum of 1 is forced except for pointer return types. sp_type_func = remove # ignore/add/remove/force # Add or remove space between function name and '(' on function declaration sp_func_proto_paren = remove # ignore/add/remove/force # Add or remove space between function name and '(' on function definition sp_func_def_paren = remove # ignore/add/remove/force # Add or remove space inside empty function '()' sp_inside_fparens = remove # ignore/add/remove/force # Add or remove space inside function '(' and ')' sp_inside_fparen = remove # ignore/add/remove/force # Add or remove space between ']' and '(' when part of a function call. sp_square_fparen = remove # ignore/add/remove/force # Add or remove space between ')' and '{' of function sp_fparen_brace = remove # ignore/add/remove/force # Add or remove space between function name and '(' on function calls sp_func_call_paren = remove # ignore/add/remove/force # Add or remove space between a constructor/destructor and the open paren sp_func_class_paren = remove # ignore/add/remove/force # Add or remove space between 'return' and '(' sp_return_paren = remove # ignore/add/remove/force # Add or remove space between '__attribute__' and '(' sp_attribute_paren = remove # ignore/add/remove/force # Add or remove space between macro and value sp_macro = remove # ignore/add/remove/force # Add or remove space between macro function ')' and value sp_macro_func = force # ignore/add/remove/force # Add or remove space between 'else' and '{' if on the same line sp_else_brace = force # ignore/add/remove/force # Add or remove space between '}' and 'else' if on the same line sp_brace_else = force # ignore/add/remove/force # Add or remove space between 'catch' and '{' if on the same line sp_catch_brace = force # ignore/add/remove/force # Add or remove space between '}' and 'catch' if on the same line sp_brace_catch = force # ignore/add/remove/force # Add or remove space between 'finally' and '{' if on the same line sp_finally_brace = force # ignore/add/remove/force # Add or remove space between '}' and 'finally' if on the same line sp_brace_finally = force # ignore/add/remove/force # Add or remove space between 'try' and '{' if on the same line sp_try_brace = force # ignore/add/remove/force # Add or remove space between get/set and '{' if on the same line sp_getset_brace = force # ignore/add/remove/force # Add or remove space before the '::' operator sp_before_dc = remove # ignore/add/remove/force # Add or remove space after the '::' operator sp_after_dc = remove # ignore/add/remove/force # Add or remove around the D named array initializer ':' operator sp_d_array_colon = remove # ignore/add/remove/force # Add or remove space after the '!' (not) operator. sp_not = remove # ignore/add/remove/force # Add or remove space after the '~' (invert) operator. sp_inv = remove # ignore/add/remove/force # Add or remove space after the '&' (address-of) operator. # This does not affect the spacing after a '&' that is part of a type. sp_addr = remove # ignore/add/remove/force # Add or remove space around the '.' or '->' operators sp_member = remove # ignore/add/remove/force # Add or remove space after the '*' (dereference) operator. # This does not affect the spacing after a '*' that is part of a type. sp_deref = remove # ignore/add/remove/force # Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7' sp_sign = remove # ignore/add/remove/force # Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;' sp_incdec = remove # ignore/add/remove/force # Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;' sp_after_oc_scope = remove # ignore/add/remove/force # Add or remove space after the colon in message specs # '-(int) f: (int) x;' vs '+(int) f : (int) x;' sp_before_oc_colon = force # ignore/add/remove/force # Add or remove space after the (type) in message specs # '-(int) f: (int) x;' vs '+(int)f : (int)x;' sp_after_oc_type = remove # ignore/add/remove/force # # Code alignment (not left column spaces/tabs) # # Whether to keep non-indenting tabs align_keep_tabs = false # false/true # Whether to use tabs for alinging align_with_tabs = true # false/true # Whether to bump out to the next tab when aligning align_on_tabstop = false # false/true # Whether to left-align numbers align_number_left = false # false/true # Align variable definitions in prototypes and functions align_func_params = false # false/true # The span for aligning variable definitions (0=don't align) align_var_def_span = 0 # number # How to align the star in variable definitions. # 0=Part of the type # 1=Part of the variable # 2=Dangling align_var_def_star_style = 0 # number # How to align the '&' in variable definitions. # 0=Part of the type # 1=Part of the variable # 2=Dangling align_var_def_amp_style = 0 # number # The threshold for aligning variable definitions (0=no limit) align_var_def_thresh = 100 # number # Whether to align the colon in struct bit fields align_var_def_colon = true # false/true # Whether to align inline struct/enum/union variable definitions align_var_def_inline = true # false/true # The span for aligning on '=' in assignments (0=don't align) align_assign_span = 0 # number # The threshold for aligning on '=' in assignments (0=no limit) align_assign_thresh = 0 # number # The span for aligning on '=' in enums (0=don't align) align_enum_equ_span = 0 # number # The threshold for aligning on '=' in enums (0=no limit) align_enum_equ_thresh = 0 # number # The span for aligning struct/union (0=don't align) align_var_struct_span = 0 # number # The threshold for aligning struct/union member definitions (0=no limit) align_var_struct_thresh = 0 # number # The span for aligning struct initializer values (0=don't align) align_struct_init_span = 0 # number # The minimum space between the type and the synonym of a typedef align_typedef_gap = 0 # number # The span for aligning single-line typedefs (0=don't align) align_typedef_span = 0 # number # How to align typedef'd functions with other typedefs # 0: Don't mix them at all # 1: align the open paren with the types # 2: align the function type name with the other type names align_typedef_func = 0 # number # Controls the positioning of the '*' in typedefs. Just try it. # 0: Align on typdef type, ignore '*' # 1: The '*' is part of type name: typedef int *pint; # 2: The '*' is part of the type, but dangling: typedef int *pint; align_typedef_star_style = 0 # number # Controls the positioning of the '&' in typedefs. Just try it. # 0: Align on typdef type, ignore '&' # 1: The '&' is part of type name: typedef int &pint; # 2: The '&' is part of the type, but dangling: typedef int &pint; align_typedef_amp_style = 0 # number # The span for aligning comments that end lines (0=don't align) align_right_cmt_span = 0 # number # If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment align_right_cmt_mix = false # false/true # The span for aligning function prototypes (0=don't align) align_func_proto_span = 0 # number # The span for aligning function prototypes (0=don't align) align_oc_msg_spec_span = 0 # number # Whether to align macros wrapped with a backslash and a newline. # This will not work right if the macro contains a multi-line comment. align_nl_cont = false # false/true # The minimum space between label and value of a preprocessor define align_pp_define_gap = 0 # number # The span for aligning on '#define' bodies (0=don't align) align_pp_define_span = 0 # number # # Newline adding and removing options # # Whether to collapse empty blocks between '{' and '}' nl_collapse_empty_body = false # false/true # Don't split one-line braced assignments - 'foo_t f = { 1, 2 };' nl_assign_leave_one_liners = true # false/true # Don't split one-line braced statements inside a class xx { } body nl_class_leave_one_liners = false # false/true # Don't split one-line enums: 'enum foo { BAR = 15 };' nl_enum_leave_one_liners = true # false/true # Don't split one-line get or set functions nl_getset_leave_one_liners = true # false/true # Don't split one-line function definitions - 'int foo() { return 0; }' nl_func_leave_one_liners = true # false/true # Don't split one-line if/else statements - 'if(a) b++;' nl_if_leave_one_liners = false # false/true # Add or remove newlines at the start of the file nl_start_of_file = remove # ignore/add/remove/force # The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force' nl_start_of_file_min = 0 # number # Add or remove newline at the end of the file nl_end_of_file = add # ignore/add/remove/force # The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force') nl_end_of_file_min = 1 # number # Add or remove newline between '=' and '{' nl_assign_brace = remove # ignore/add/remove/force # Add or remove newline between '=' and '[' (D only) nl_assign_square = ignore # ignore/add/remove/force # Add or remove newline after '= [' (D only). Will also affect the newline before the ']' nl_after_square_assign = ignore # ignore/add/remove/force # The number of newlines after a block of variable definitions nl_func_var_def_blk = 1 # number # Add or remove newline between a function call's ')' and '{', as in: # list_for_each(item, &list) { } nl_fcall_brace = add # ignore/add/remove/force # Add or remove newline between 'enum' and '{' nl_enum_brace = add # ignore/add/remove/force # Add or remove newline between 'struct and '{' nl_struct_brace = add # ignore/add/remove/force # Add or remove newline between 'union' and '{' nl_union_brace = add # ignore/add/remove/force # Add or remove newline between 'if' and '{' nl_if_brace = add # ignore/add/remove/force # Add or remove newline between '}' and 'else' nl_brace_else = add # ignore/add/remove/force # Add or remove newline between 'else if' and '{' # If set to ignore, nl_if_brace is used instead nl_elseif_brace = add # ignore/add/remove/force # Add or remove newline between 'else' and '{' nl_else_brace = add # ignore/add/remove/force # Add or remove newline between '}' and 'finally' nl_brace_finally = add # ignore/add/remove/force # Add or remove newline between 'finally' and '{' nl_finally_brace = add # ignore/add/remove/force # Add or remove newline between 'try' and '{' nl_try_brace = add # ignore/add/remove/force # Add or remove newline between get/set and '{' nl_getset_brace = add # ignore/add/remove/force # Add or remove newline between 'for' and '{' nl_for_brace = add # ignore/add/remove/force # Add or remove newline between 'catch' and '{' nl_catch_brace = add # ignore/add/remove/force # Add or remove newline between '}' and 'catch' nl_brace_catch = add # ignore/add/remove/force # Add or remove newline between 'while' and '{' nl_while_brace = add # ignore/add/remove/force # Add or remove newline between 'do' and '{' nl_do_brace = add # ignore/add/remove/force # Add or remove newline between '}' and 'while' of 'do' statement nl_brace_while = add # ignore/add/remove/force # Add or remove newline between 'switch' and '{' nl_switch_brace = add # ignore/add/remove/force # Add or remove newline when condition spans two or more lines nl_multi_line_cond = true # false/true # Force a newline in a define after the macro name for multi-line defines. nl_multi_line_define = true # false/true # Whether to put a newline before 'case' statement nl_before_case = false # false/true # Whether to put a newline after 'case' statement nl_after_case = true # false/true # Newline between namespace and { nl_namespace_brace = add # ignore/add/remove/force # Add or remove newline between 'template<>' and 'class' nl_template_class = add # ignore/add/remove/force # Add or remove newline between 'class' and '{' nl_class_brace = add # ignore/add/remove/force # Add or remove newline after each ',' in the constructor member initialization nl_class_init_args = remove # ignore/add/remove/force # Add or remove newline between return type and function name in definition nl_func_type_name = add # ignore/add/remove/force # Add or remove newline between return type and function name in a prototype nl_func_proto_type_name = remove # ignore/add/remove/force # Add or remove newline between a function name and the opening '(' nl_func_paren = remove # ignore/add/remove/force # Add or remove newline after '(' in a function declaration nl_func_decl_start = remove # ignore/add/remove/force # Add or remove newline after each ',' in a function declaration nl_func_decl_args = remove # ignore/add/remove/force # Add or remove newline before the ')' in a function declaration nl_func_decl_end = remove # ignore/add/remove/force # Add or remove newline between function signature and '{' nl_fdef_brace = add # ignore/add/remove/force # Whether to put a newline after 'return' statement nl_after_return = false # false/true # Whether to put a newline after semicolons, except in 'for' statements nl_after_semicolon = true # false/true # Whether to put a newline after brace open. # This also adds a newline before the matching brace close. nl_after_brace_open = false # false/true # If nl_after_brace_open and nl_after_brace_open_cmt are true, a newline is # placed between the open brace and a trailing single-line comment. nl_after_brace_open_cmt = false # false/true # Whether to put a newline after a virtual brace open. # These occur in un-braced if/while/do/for statement bodies. nl_after_vbrace_open = false # false/true # Whether to alter newlines in '#define' macros nl_define_macro = true # false/true # Whether to not put blanks after '#ifxx', '#elxx', or before '#endif' nl_squeeze_ifdef = false # false/true # Add or remove newline before 'if' nl_before_if = add # ignore/add/remove/force # Add or remove newline after 'if' nl_after_if = add # ignore/add/remove/force # Add or remove newline before 'for' nl_before_for = add # ignore/add/remove/force # Add or remove newline after 'for' nl_after_for = add # ignore/add/remove/force # Add or remove newline before 'while' nl_before_while = add # ignore/add/remove/force # Add or remove newline after 'while' nl_after_while = add # ignore/add/remove/force # Add or remove newline before 'switch' nl_before_switch = add # ignore/add/remove/force # Add or remove newline after 'switch' nl_after_switch = add # ignore/add/remove/force # Add or remove newline before 'do' nl_before_do = add # ignore/add/remove/force # Add or remove newline after 'do' nl_after_do = add # ignore/add/remove/force # Whether to double-space commented-entries in struct/enum nl_ds_struct_enum_cmt = false # false/true # Whether to double-space before the close brace of a struct/union/enum nl_ds_struct_enum_close_brace = false # false/true # Add or remove a newline around a class colon. # Related to pos_class_colon, nl_class_init_args, and pos_comma. nl_class_colon = remove # ignore/add/remove/force # Change simple unbraced if statements into a one-liner # 'if(b)\n i++;' => 'if(b) i++;' nl_create_if_one_liner = false # false/true # Change simple unbraced for statements into a one-liner # 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);' nl_create_for_one_liner = false # false/true # Change simple unbraced while statements into a one-liner # 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);' nl_create_while_one_liner = false # false/true # # Positioning options # # The position of boolean operators in wrapped expressions pos_bool = trail # ignore/lead/trail # The position of the comma in wrapped expressions pos_comma = trail # ignore/lead/trail # The position of the comma in the constructor initialization list pos_class_comma = trail # ignore/lead/trail # The position of colons between constructor and member initialization pos_class_colon = trail # ignore/lead/trail # # Line Splitting options # # Try to limit code width to N number of columns code_width = 80 # number # Whether to fully split long 'for' statements at semi-colons ls_for_split_full = true # false/true # Whether to fully split long function protos/calls at commas ls_func_split_full = true # false/true # # Blank line options # # The maximum consecutive newlines nl_max = 2 # number # The number of newlines after a function prototype, if followed by another function prototype nl_after_func_proto = 1 # number # The number of newlines after a function prototype, if not followed by another function prototype nl_after_func_proto_group = 2 # number # The number of newlines after '}' of a multi-line function body nl_after_func_body = 2 # number # The number of newlines after '}' of a single line function body nl_after_func_body_one_liner = 1 # number # The minimum number of newlines before a multi-line comment. # Doesn't apply if after a brace open or another multi-line comment. nl_before_block_comment = 2 # number # The minimum number of newlines before a single-line C comment. # Doesn't apply if after a brace open or other single-line C comments. nl_before_c_comment = 0 # number # The minimum number of newlines before a CPP comment. # Doesn't apply if after a brace open or other CPP comments. nl_before_cpp_comment = 0 # number # Whether to force a newline after a mulit-line comment. nl_after_multiline_comment = false # false/true # The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. # Will not change the newline count if after a brace open. # 0 = No change. nl_before_access_spec = 1 # number # The number of newlines after a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. # 0 = No change. nl_after_access_spec = 1 # number # Whether to remove blank lines after '{' eat_blanks_after_open_brace = true # false/true # Whether to remove blank lines before '}' eat_blanks_before_close_brace = true # false/true # # Code modifying options (non-whitespace) # # Add or remove braces on single-line 'do' statement mod_full_brace_do = add # ignore/add/remove/force # Add or remove braces on single-line 'for' statement mod_full_brace_for = add # ignore/add/remove/force # Add or remove braces on single-line function defintions. (Pawn) mod_full_brace_function = add # ignore/add/remove/force # Add or remove braces on single-line 'if' statement mod_full_brace_if = add # ignore/add/remove/force # Don't remove braces around statements that span N newlines mod_full_brace_nl = 0 # number # Add or remove braces on single-line 'while' statement mod_full_brace_while = add # ignore/add/remove/force # Add or remove unnecessary paren on 'return' statement mod_paren_on_return = remove # ignore/add/remove/force # Whether to change optional semicolons to real semicolons mod_pawn_semicolon = false # false/true # Add parens on 'while' and 'if' statement around bools mod_full_paren_if_bool = true # false/true # Whether to remove superfluous semicolons mod_remove_extra_semicolon = true # false/true # If a function body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. mod_add_long_function_closebrace_comment = 0 # number # If a switch body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. mod_add_long_switch_closebrace_comment = 0 # number # If TRUE, will sort consecutive single-line 'import' statements [Java, D] mod_sort_import = false # false/true # If TRUE, will sort consecutive single-line 'using' statements [C#] mod_sort_using = false # false/true # If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C] # This is generally a bad idea, as it may break your code. mod_sort_include = false # false/true # # Comment modifications # # Try to wrap comments at cmt_witdth columns cmt_width = 0 # number # Whether to group c-comments that look like they are in a block cmt_c_group = false # false/true # Whether to put an empty '/*' on the first line of the combined c-comment cmt_c_nl_start = true # false/true # Whether to put a newline before the closing '*/' of the combined c-comment cmt_c_nl_end = true # false/true # Whether to group cpp-comments that look like they are in a block cmt_cpp_group = false # false/true # Whether to put an empty '/*' on the first line of the combined cpp-comment cmt_cpp_nl_start = false # false/true # Whether to put a newline before the closing '*/' of the combined cpp-comment cmt_cpp_nl_end = false # false/true # Whether to change cpp-comments into c-comments cmt_cpp_to_c = true # false/true # Whether to put a star on subsequent comment lines cmt_star_cont = true # false/true # The number of spaces to insert at the start of subsequent comment lines cmt_sp_before_star_cont = 0 # number # The number of spaces to insert after the star on subsequent comment lines cmt_sp_after_star_cont = 0 # number # The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment. # Will substitue $(filename) with the current file's name. cmt_insert_file_header = "" # string # The filename that contains text to insert before a function implementation if the function isn't preceeded with a C/C++ comment. # Will substitue $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff. # Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... } cmt_insert_func_header = "" # string # The filename that contains text to insert before a class if the class isn't preceeded with a C/C++ comment. # Will substitue $(class) with the class name. cmt_insert_class_header = "" # string # # Preprocessor options # # Control indent of preprocessors inside #if blocks at brace level 0 pp_indent = add # ignore/add/remove/force # Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false) pp_indent_at_level = false # false/true # Add or remove space after # based on pp_level of #if blocks pp_space = remove # ignore/add/remove/force # Sets the number of spaces added with pp_space pp_space_count = 0 # number # The indent for #region and #endregion in C# and '#pragma region' in C/C++ pp_indent_region = 0 # number # Whether to indent the code between #region and #endregion pp_region_indent_code = false # false/true # If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level pp_indent_if = 0 # number # Control whether to indent the code between #if, #else and #endif when not at file-level pp_if_indent_code = false # false/true # Whether to indent '#define' at the brace level (true) or from column 1 (false) pp_define_at_level = false # false/true yquake2-QUAKE2_7_10/stuff/models/000077500000000000000000000000001321245476300165345ustar00rootroot00000000000000yquake2-QUAKE2_7_10/stuff/models/crosshair/000077500000000000000000000000001321245476300205315ustar00rootroot00000000000000yquake2-QUAKE2_7_10/stuff/models/crosshair/skin.pcx000066400000000000000000000102671321245476300222170ustar00rootroot00000000000000 ,,hhhhhh@kkkkjkkkjh@kikkj@@kikkjh@kikjh@khkj@@khkj@@jkhkji@@jkhj@@ijkhjih@@hijkhih@@hijhih@@hA@@AA@h@h@hjhih@hkj@hkkjh@hkkkjh@@kkj@@kkjh@@kkjh@@kkjh@@kkj@@kkj@jkjjנkjk@khhhhhhhhhhhhh@jhkA@A@h@h@hjh@jkjjhhkjjhhkkjhhkkjhhk@jhhk@hhhjhhjk@hj@k@hj@hkhj@hkjkjk@hkjh@hkjh@hk@@hk@@hk@hkj@hkjhhhk@hhk@hhkjhhhkjh@ ///???KKK[[[kkk{{{cK#[CS?O;G7?/;+3'/#+'#   __o[[g[S_WO[SKSOGKG?C?;;;773///++'''###wS{cCs[;gO/ϗK{;g/oS''ˋ#wcwO[;?' #;+/#+'s gWKC;3 + # {_KsWCkS?gO;_G7WC3S?/K7+C3'?/#7'/#'  o;_7S/C+7#'  [O{o˛׻߳ӟÇs[Gw/gSoKgC[ ?S7K/?'3+ WWOO{GGsCCk;;c33[//W++K##?3+ {skcw{_ssWkkOccG[[COO;CC377+//### K?C7;/7'w/#k+c#WOC 7 +  w{osgkcc[[SWKOGGs??g77W//K''?#/# {oc{WsKgwC_o;Wg3K['?O7C/; #/# #?S'_/_3_{3S'kWG;+s_G/77++#sSW3{?ǫwkW[Syquake2-QUAKE2_7_10/stuff/models/crosshair/tris.md2000066400000000000000000000022001321245476300221100ustar00rootroot00000000000000IDP24$ yDmodels/crosshair/skin.pcxEPdpdpr~dppdpdbcbrdbd89cc*8c8TTbTTT! "#$!"#!&%' ()& '(&+*,-.+,-+0/1230 120!"# 9 ==Z\Frame000osshaiypvptqrrptowoyo|oqsux{~|ywtrqppp|qyrwttwryq||p 4g4o4[p4p4D4e`4bo4p`4zp4p4`p44`4p4p4p44{44H?`?!H?h> ?h>"~?`?#d?|?$H?`?!?h>"~?`?#H?`?!=H?&D?F?%D?~?'=~?(d?)=H?&D?~?'=~?(=H?&>=+>F?*h>F?,h>=-><.>=+h>F?,h>=->=+(?0/`>1X>(?2=D?3(?0`>1X>(?2(?0yquake2-QUAKE2_7_10/stuff/packaging.md000066400000000000000000000062671321245476300175320ustar00rootroot00000000000000# Notes for Package Maintainers Our 7.00 release caused some trouble for package maintainers (see https://github.com/yquake2/yquake2/issues/214), so we decided to finally properly document how we think it should be done and what assumptions Yamagi Quake II makes regarding binary locations etc. ## Where you should put the executables Yamagi Quake II expects all binaries (executables and libs) to be in the same directory (or, in the case of game.so/.dll/dylib, in the mod-specific subdirectory). So the binary directory should look somehow like this *(on Unix-like systems; on Windows and OSX it's very similar but with different extensions: .dll or .dylib instead of .so, and the executables have .exe file extension on Windows of course)*: * /path/to/yamagi-quake2/ - quake2 - q2ded - ref_gl1.so - ref_gl3.so - baseq2/ * game.so - xatrix/ * game.so - ... *(the same for other addons/mods)* Yamagi Quake2 will get the directory the `quake2` executable is in from the system and then look in that directory (and nowhere else!) for `ref_*.so`. It will look for `game.so` there first, but if it's not found in the binary directory, it will look for it in all directories that are also searched for game data (SYSTEMDIR, basedir, $HOME/.yq2/). This is for better compatibility with mods that might ship their own game.so. You can **just symlink the executables to a directory in your $PATH**, like /usr/bin/. (*Except on OpenBSD, which does not provide a way to get the executable directory, there you'll need a shellscript that first does a `cd /path/to/yamagi-quake2/` and then executes `./quake2`*) We want all binaries to be in the same directory to ensure that people don't accidentally update only parts of their Yamagi Quake II installtion, so they'd end up with a new quake2 executable and old render libraries (`ref_*.so`) and report weird bugs. ## The SYSTEMWIDE and SYSTEMDIR options The Makefile allows you to enable the `SYSTEMWIDE` feature (`WITH_SYSTEMWIDE=yes`) and lets you specify the directory that will be used (`SYSTEMDIR`, `WITH_SYSTEMDIR=/your/custom/path/`). If you don't set SYSTEMDIR, it defaults to `/usr/share/games/quake2`, which is what debian uses. The `SYSTEMDIR` was meant to contain just the game data, *not* the binaries, and allows several Quake2 source ports to share the same game data. Unfortunately, we didn't document this assumption, so some packages used it for both binaries and data, just binaries or - which causes most trouble - only for the game libs, but not the executables. The latter case doesn't work anymore since we (re)introduced the render libs, as they need to be located next to the executable, and if the executable is in /usr/bin/ you don't want to put libs next to it. Anyway: If you use `SYSTEMWIDE`/`SYSTEMDIR`, please use it for game data. You *can* also put the binaries in there, but in that case please put all of them (including executables) in there, as explained above. ## Alternative startup config Yamagi Quake II has support for an alternative startup config. It may be a good idea to install it, since it sets some global options to sane defaults. Copy yq2.cfg to the baseq2/ subdirectory in the gamedata (`SYSTEMDIR`) directory. yquake2-QUAKE2_7_10/stuff/quake2-start.sh000077500000000000000000000013611321245476300201340ustar00rootroot00000000000000#!/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_7_10/stuff/yq2.cfg000066400000000000000000000025621321245476300164520ustar00rootroot00000000000000// // 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 w "use power shield" 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 jpg 90 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