yquake2-QUAKE2_8_40/000077500000000000000000000000001465112212000141115ustar00rootroot00000000000000yquake2-QUAKE2_8_40/.gitignore000066400000000000000000000000421465112212000160750ustar00rootroot00000000000000/build/ /release/ *.mk *.user *.d yquake2-QUAKE2_8_40/CHANGELOG000066400000000000000000000713461465112212000153360ustar00rootroot00000000000000Quake II 8.30 to 8.40: - Implement `g_quick_weap`. If set to 1, both weapprev and weapnext commands will count how many times they have been called, making possible to skip weapons by quickly tapping one of these keys. (by protocultor) - Fix `gl1_overbrightbits`, allowed values are now 0, 1, 2 and 4. (by protocultor) - Sort player skins case insensitive. (by apartfromtime) - Implement `cl_showspeed` to show the current player speed (by Feels Duck Man) - Add texture filtering options to the video menu (by apartfromtime) - Implement `cl_laseralpha` to control how transparent laser beams are. (by BjossiAlfreds) - Experimental support for SDL 3. Requires SDL 3.1.2. - Reimplement multitexturing for the OpenGL 1.4 renderer. Controlled by `gl1_multitexture`, enabled by default. (by protocultor) - Optimize dynamic lights and texture allocation in the OpenGL 1.4 renderer. (by protocultor) - Implement gyro tightening for gamepads and joysticks. (by protocultor) - Support long player skin names. (by 0lvin) - Add a very simple download filter. Files ending in .dll, .dylib and .so are always filtered to prevent code injection. - Don't load OpenAL and cURL libraries if thy are configured with a full or relative path. - Work around naggy help icons. (by BjossiAlfreds) - Group draw call in GL1. This yields huge performance gains on slow GPUs. (by protocultor) Quake II 8.20 to 8.30: - Use the same image loading code in all renderers. (by 0lvin) - Use the same model loading code in all renderers. (by 0lvin) - Remove the unused `msg` cvar and the corresponding server side infrastructure. The cvar was never implemented in Quake II, but existing bits could be used for attacks against the client. - Add `cl_audiopaused`. When set to `1` the audio stops when the game is paused. (by David Carlier) - Add `ogg_pausewithgame`. When set to `1` the background music stops when the game is paused. (by apartfromtime) - New logo files, matching the original Quake II logo. (by SirYodaJedi) - Support for RISCV64 (by David Carlier) - Fix resetting Mods back to baseq2 when running with `-portable`. - Alternative playback modes for OGG/Vorbis background music: once, sequential, random. (by apartfromtime) - Support gyro aiming for Switch controllers with SDL < 2.0.14. (by protocultor) - Fixed stand-ground gladiators not attacking within a certain range. (by BjossiAlfreds) - Fixed monsters seeing players during intermissions. (by BjossiAlfreds) - Several fixes to Makron. (by BjossiAlfreds) - Optional high dpi awareness when running under Wayland. Requires a Wayland compositor supporting fractional-scale-v1 and at least SDL 2.26. Set `vid_highdpiawareness 1` to enable. - Fix lava surfaces not glowing. - Add a cheat protected cvar `g_swap_speed`. Allows to skip frames of "putting down weapon" and "raising weapon" animations, speeding them up. (by protocultor) - Support of RGBA png/jpg image with r_retexturing as cinematic. (by 0lvin) Quake II 8.10 to 8.20: - This release marks Quake IIs 25th anniversary. Happy Birthday! - Various improvements to the menu. This includes updates to the menu framework itself, to the game controller menu, the savegame menu and the video menus. (by apartfromtime, protocultor, David Carlier and 0lvin) - A lot of fixes and improvements to the controller support. Support for gyro aiming, more precise stick handling and rumble improvements. (by protocultor) - Implement faster weapon switching with the new 'cycleweap' command. (by protocultor). - Fixes pusher delta yaw manipulation. This fixes the infamous bug were a player standing on a blocked elevator gets turned around (by skuller). - Add a command 'spawnentity' to spawn entities at the given coordinates. Can be used for debugging. (by 0lvin) - Ported monster footstep sounds from SkacikPLs Q2RTX fork. Needs a pak file with assets to work, see the documentation for details. (by 0lvin) - Ported reverb effects from SkacikPLs Q2RTX fork. (by 0lvin) - Fix several coop related bugs with the powercubes. (by BjossiAlfreds) - A way better fix for dead bodies obstructing elevators or falling through the worldmodel. (by BjossiAlfreds) - Fix items already in water playing a splash sound at level start. (by BjossiAlfreds) - Pause the game when getting minimized or hidden. (by David Carlier) - Fix Bugs related to 'ogg_ignoretrack0'. (by BjossiAlfreds) - Share model loading code between renderers. (by 0lvin) - Restore stair step smoothing for step sizes other than 16. - Fix playback of long audio samples (>10 seconds) through OpenAL. Quake II 8.01 to 8.10: - The OpenGL 3.2 renderer now supports the classic underwater effect. Until now it was only available in the Software renderer. - Add 'ref_gles3', an OpenGL ES 3.0 renderer lib. This is an variant of the OpenGL 3.2 renderer, using OpenGL ES instead of desktop OpenGL. It greatly enhances support for embedded GPUs like the RaspberryPI 4. - The Software renderer now supports colored lighting. It can be enabled through `sw_colorlight`. (by 0lvin) - Another round of timing fixes. Implement `cl_maxfps -1` (the new default, existing configs are not altered) to automatically select a known to be good packet framerate depending on the current renderer framerate. This solves some subtile problems with stuttering entities. - Greatly improved support for joysticks and controllers. Devices can now be hotplugged at runtime, binding should be portable between different controllers and there's a new menu for controller bindings. (by protocultor) - Add the `r_2D_unfiltered` and `r_videos_unfiltered` cvars. When enabled 2D elements like the HUD and videos are not filtered, they appear pixeled. - Add 'prefweap' command to select weapon by priority. (by protocultor) - Support building with Microsoft Visual Studio 2015 and newer. - Several game code fixes, mostly for stuck mechanics in fact2. (by BjossiAlfreds and maraakate) - Fix two stuck monsters in hangar1 and jail5. (by Dremor8484 and BjossiAlfreds) - Fix the `viewsize` cvar. Quake II 8.00 to 8.01: - Fix the game not starting when nonexistent dirs in the search path. - Sync haptic joystick effects to the sound. (by 0lvin) - Move several map bug fixes to entity files, add some more map bug fixes. Without entity files the maps are the same as in Vanilla Quake II. (by BjossiAlfreds) - Play the correct demo loop after changing the current mod. - Fix numbered paks with upper case letters added twice to the search path. Only Windows was affected. - `g_footsteps 2` now only generate footstep sound when the player is on the ground entity and no longer when in water or air. - Add a cvar `g_machinegun_norecoil' which disables the machinegun recoil effect. This is cheat protected. (by De-Deppe) - Scale 8 bit textures when `r_scale8bittextures` is set. (by 0lvin) - The game is no longer limited to 1000 FPS when running in timedemo mode. (by 0lvin) - Optimise command line parser and fix several subtle bugs. (by BjossiAlfreds) - Add `r_2D_unfiltered`, when set to `1` 2D elements aren't filtered. - Fix soldiers never showing their pain skin as long as they're alive. - Fix relative paths in ZIP files not being found. - Add `gamemode` command for changing between singleplayer, deathmatch and coop. (by BjossiAlfreds) - Show a download progress bar when `cl_http_show_dw_progress` is set to `1`. (by David Carlier) Quake II 7.45 to 8.00: - Client side support for the optional Vulkan renderer library. - Non existent renderer libraries are now skipped over by the menu. - Fix several bugs when loading autosaves. - Bump the maximal number of OGG/Vorbis files to 128. - Several fixes to the Barracuda Shark. (by BjossiAlfreds) - 'vid_fullscreen' and 'r_mode' are no longer special, they require an explicit 'vid_restart' like every other cvar. - Remove hardcoded map fixes and replace them by optional entity files. Add several newly discovered map fixes. (by BjossiAlfreds) - Send the network protocol version to the server. This can be used by the server to support clients with different network protocol version. (by Knightmare) - Force SDL to minimize the window when its focus is lost. This fixes several problem under Windows with SDL 2.0.14 and newer. - Switch the semantics of the 'vid_fullscreen' cvar. '1' is now native fullscreen, like it was in Vanilla Quake II. '2' is desktop fullscreen. When desktop fullscreen is selected through the menu, 'r_mode' is forced to '-2'. - Add 'g_footsteps' to control the generation of footstep sound. This cvar is cheat protected. '1' is Vanilla Quake II behavior and the default. '0' never generates footstep sound, '2' always generates them. - Support stereo wave files. (by 0lvin) - Add 'cl_r1q2_lighstyle'. When set to '1' Yamagi Quake II uses the Vanilla Quake II light styles (for example yellow light for the Hyperblaster) instead of the default r1q2 light styles. - Add a submenu to configure gamepad and joystick sensitivities. (by Larry Davis) - Ensure that the config file is written before changing the active mod. This prevents config changes from getting lost. - Overhaul the search path logic. Make sure that each directory is added only once. Quake II 7.44 to 7.45: - Fix a crash under windows when opening the games menu with mods installed. Quake II 7.43 to 7.44: - Fix some input option not getting saved. - Limit busywaits to the full client. This lowers the cpu consumption of q2ded considerably. - Rework the build system to be more distribution friendly. The base CFLAGS and LDFLAGS can now be overridden by the environment and by options passed to make. (by Simon McVittie) - Fix some corner cases of broken IPv6 connectivity. - Fix qport colliding between several Yamagi Quake II clients. - Keyboard keys unknown to Yamagi Quake II can now be bound. - Adaptive vsync is now supported by setting 'r_vsync' to '1'. - Implement 'coop_pickup_weapons'. When set to '1', a weapon may be picked up by coop players if the player doesn't have the weapon in their inventory or no other player has already picked it up. - In coop elevators wait for 'coop_elevator_delay' seconds. - If 'cl_anglekick' is set '1' angle kicks are ignored. This breaks the gameplay a little bit, but helps against motion sickness. This cvar is cheat protected. - Add 'listmaps' command and autocompletion for maps. (by JBerg) - Make 'wait' in scripts wait for 17 ms. This fixes some movement makros. - Support for Haiku. (by David Carlier) - Add a 'mods' submenu. (by earth-metal) - Add the 'vstr' command and 'nextdemo' cvar. Ported from ioquake3. (by Denis Pauk) Quake II 7.42 to 7.43: - Recover from a lost sound device, do not crash of no sound device is available. This fixes several problem with DisplayPort and HDMI audio, especially with the Intel Windows GPU drivers. - Several small game logic fixes. This includes a fix for a potential progress blocker in 'The Torture Chambers' introduced in the last release. (by BjossiAlfreds) - Add the 'gl1_particle_square' cvar, it forces square particles in the GL1 renderer. (by Mason UnixBeard) - The software renderer is no longer experimental. - Add an option to configure the gun Z offset in the software renderer. Quake II 7.41 to 7.42: - The console can now be scrolled with the mouse wheel. (by maxcrofts) - Fix entities on non-horizontal surfaces rendered in full black. - Add an option to choose the display Quake II runs on. (by Spirrwell) - Add an option to specify the display refresh rate used in fullscreen. - Allow mouse 'sensitivity' to be set to non-integral values. - Port cvar operations from q2pro. These allow the manipulation of cvar values, supported are: dec, inc, reset, resetall and toggle - Put the client into pause mode during savegame load. This prevents the world getting forwarded about 100 frames during load, time in which the player might be hurt by monsters or the environment. - New commands: 'listentities' allows listing of entities. 'teleport' teleports the player to the given coordinates. - Fix loading of config files not ending in newlines. - A lot of fixes for subtle, long standing AI and game play bugs. (by BjossiAlfreds) - Quicksaves can now be loaded and saved throught the savegame menus. - The software renderer now skips frames with no changes. This improves performance quite a bit, especially on slow CPUs. (by Denis Pauk) Quake II 7.40 to 7.41: - Some bugfixes to HTTP downloads introduced in 7.40. - Fix several crashes when loading savegames in coop. - Fix some out of memory aborts when loading maps with a lot surfaces. - Allow autodetection of the desktop resolution. Set 'r_mode' to '-2' to enable that. (by Denis Pauk) - Several fixes to the OpenGL 3.2 renderer. Some dynamic lights were missing, for example for most explosions. Stencil shadows were broken under some conditions. Performance was bad with the AMD driver under Windows. Intel Ivy Bridge didn't work. Under some conditions lights were too bright. - Add an optional fix for lighting problems in maps that misuse sky surfaces for interior lighting. Set 'gl_fixsurfsky' to '1' to enable it. - Another bunch of timing fixes. The game should now hold the framerate under all conditions, even with crappy Windows GPU drivers. - The quake2.exe wrapper forces the Quake II window into focus. This prevents Quake II starting in background. Quake II 7.30 to 7.40: - Add support for HTTP downloads. Both the r1q2 and q2pro URL schemes are supported, if a file is unavailable over HTTP the download code falls back to UDP. - Savegames can be removed through the menu by pressing 'del' on the selected savegame. (by Jonathan Bergeron) - Support external entity files. This was submitted by @NeonKnightOA. - Some fixes to OGG/Vorbis music playback. The music keeps playing after s_restart and ogg_shuffle is handled correctly by the menu. - Another round of timing fixes. Average frame times are now taken into account when taking an educated guess on the next frames render time. And the display refresh rate detection no longer cripple working GPU drivers in an efford to work around bugs in older version of AMDs. - A lot of fixes to the internal memory management. The game is now much more memory efficient, this allows playing of extremely big maps without crashes. This is largely based upon work done by Denis Pauk. - New and much more detailed documentation. - Enhancements to the software renderer. Retexturing packs are now supported, general cleanup and bugfixes. (by Denis Pauk) Quake II 7.21 to 7.30: - Removed support for SDL 1.2. - Removed static dependencies to libogg, libvorbis and libz. - Fixed several bugs regarding render- and fullscreen switch. - A lot of fixes and improvements to the software renderer. It's now able to render the whole game without artifacts and much faster than before. (by Denis Pauk) Quake II 7.20 to 7.21: - Fix some render glitches in the software renderer. (by Denis Pauk) - Render the weapon independent of the current field of view, otherwise the weapon distorts with very high FOV settings. The weapons FOV can be set through r_gunfov, it defaults to 90. - Rework the OGG/Vorbis backend. This fixes several annoying bugs, and adds support for alternative track <=> file mappings. Use this to implement support for the audio tracks supplied by the GOG.com version of Quake II. Quake II 7.10 to 7.20: - Add the soft renderer back. This feature is considered experimental. The porting of the old soft renderer code to SDL and it's cleanup were done by Denis Pauk. - Rename several cvars to be consistent across different renderers. The configuration file will be converted at the first start, when an old cvar name is used a message is printed. - Make the client unicode compatible. Yamagi Quake II can now be installed into paths with unicode characters in it. On Windows the user name of the current account may contain unicode characters. As a side effect the game can run on ReFS volumes. While '+set basedir' is still supported, the unicode compatible '-datadir' should be used. - Another round of timing fixes. The game is now capable of holding the requestes or vsync framerate even on slow machines and with problematic GL drivers. - Fix server side mod handling, their configs are now saved to the correct directories and the configs are reexeced at mod startup. Quake 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 mispredictions 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 gibs 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 newer 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 regarding 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: - Automagically 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 Reckoning' 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_8_40/CMakeLists.txt000066400000000000000000000710331465112212000166550ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.1 FATAL_ERROR) # Print a message that using the Makefiles is recommended. message(NOTICE: " The CMakeLists.txt is unmaintained. Use the Makefile if possible.") # 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 C) # Cmake module search path. set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/stuff/cmake/modules ${CMAKE_MODULE_PATH}) set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD_REQUIRED OFF) 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{SDL2DIR} ${YQUAKE2LIBS}) endif() # Add extended path for FreeBSD and Homebrew on OS X. list(APPEND CMAKE_PREFIX_PATH /usr/local) if (MSVC) add_compile_options(/MP) # parallel build (use all cores, or as many as configured in VS) # ignore some compiler warnings add_compile_options(/wd4244 /wd4305) # possible loss of data/truncation (double to float etc; ignore) add_compile_options(/wd4018) # signed/unsigned mismatch add_compile_options(/wd4996) # 'function': was declared deprecated (like all that secure CRT stuff) # don't show me warnings for system headers, why the fuck isn't this default add_compile_options(/experimental:external /external:W0) else() # GCC/clang/mingw # Enforce compiler flags: # -Wall -> More warnings # -fno-strict-aliasing -> Quake 2 is far away from strict aliasing # -fwrapv -> Make signed integer overflows defined # -fvisibility=hidden -> Force defaultsymbol visibility to hidden set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fno-strict-aliasing -fwrapv -fvisibility=hidden") # 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}") endif() # MSVC'S else-case # Switch off some annoying warnings if (${CMAKE_C_COMPILER_ID} STREQUAL "Clang") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-missing-braces") elseif (${CMAKE_C_COMPILER_ID} STREQUAL "GNU") if (CMAKE_C_COMPILER_VERSION GREATER 7.99) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format-truncation -Wno-format-overflow") endif() endif() # Compilation time options. option(CURL_SUPPORT "cURL support" ON) option(OPENAL_SUPPORT "OpenAL support" ON) option(SYSTEMWIDE_SUPPORT "Enable systemwide installation of game assets" OFF) option(SDL3_SUPPORT "Build against SDL 3 instead of SDL2" OFF) set(SYSTEMDIR "" CACHE STRING "Override the system default directory") # These variables will act as our list of include folders and linker flags. set(yquake2IncludeDirectories) set(yquake2LinkerDirectories) set(yquake2LinkerFlags) set(yquake2ClientLinkerFlags) set(yquake2ServerLinkerFlags) set(yquake2OpenGLLinkerFlags) set(yquake2VulkanLinkerFlags) 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(REF_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 # work around CMake's useless/broken CMAKE_SYSTEM_PROCESSOR (taken from dhewm3) set(cpu ${CMAKE_SYSTEM_PROCESSOR}) # Originally, ${CMAKE_SYSTEM_PROCESSOR} was supposed to contain the *target* CPU, according to CMake's documentation. # As far as I can tell this has always been broken (always returns host CPU) at least on Windows # (see e.g. https://cmake.org/pipermail/cmake-developers/2014-September/011405.html) and wasn't reliable on # other systems either, for example on Linux with 32bit userland but 64bit kernel it returned the kernel CPU type # (e.g. x86_64 instead of i686). Instead of fixing this, CMake eventually updated their documentation in 3.20, # now it's officially the same as CMAKE_HOST_SYSTEM_PROCESSOR except when cross-compiling (where it's explicitly set) # So we gotta figure out the actual target CPU type ourselves.. if(NOT (CMAKE_SYSTEM_PROCESSOR STREQUAL CMAKE_HOST_SYSTEM_PROCESSOR)) # special case: cross-compiling, here CMAKE_SYSTEM_PROCESSOR should be correct, hopefully # (just leave cpu at ${CMAKE_SYSTEM_PROCESSOR}) elseif(MSVC) # because all this wasn't ugly enough, it turned out that, unlike standalone CMake, Visual Studio's # integrated CMake doesn't set CMAKE_GENERATOR_PLATFORM, so I gave up on guessing the CPU arch here # and moved the CPU detection to MSVC-specific code in neo/sys/platform.h else() # not MSVC and not cross-compiling, assume GCC or clang (-compatible), seems to work for MinGW as well execute_process(COMMAND ${CMAKE_C_COMPILER} "-dumpmachine" RESULT_VARIABLE cc_dumpmachine_res OUTPUT_VARIABLE cc_dumpmachine_out) if(cc_dumpmachine_res EQUAL 0) string(STRIP ${cc_dumpmachine_out} cc_dumpmachine_out) # get rid of trailing newline message(DEBUG "`${CMAKE_C_COMPILER} -dumpmachine` says: \"${cc_dumpmachine_out}\"") # gcc -dumpmachine and clang -dumpmachine seem to print something like "x86_64-linux-gnu" (gcc) # or "x64_64-pc-linux-gnu" (clang) or "i686-w64-mingw32" (32bit mingw-w64) i.e. starting with the CPU, # then "-" and then OS or whatever - so use everything up to first "-" string(REGEX MATCH "^[^-]+" cpu ${cc_dumpmachine_out}) message(DEBUG " => CPU architecture extracted from that: \"${cpu}\"") else() message(WARNING "${CMAKE_C_COMPILER} -dumpmachine failed with error (code) ${cc_dumpmachine_res}") message(WARNING "will use the (sometimes incorrect) CMAKE_SYSTEM_PROCESSOR (${cpu}) to determine YQ2ARCH") endif() endif() if(cpu STREQUAL "powerpc") set(cpu "ppc") elseif(cpu STREQUAL "aarch64") # "arm64" is more obvious, and some operating systems (like macOS) use it instead of "aarch64" set(cpu "arm64") elseif(cpu MATCHES "[aA][mM][dD]64" OR cpu MATCHES "[xX].*64") set(cpu "x86_64") elseif(cpu MATCHES "i.86" OR cpu MATCHES "[xX]86") set(cpu "i386") elseif(cpu MATCHES "[aA][rR][mM].*") # some kind of arm.. # On 32bit Raspbian gcc -dumpmachine returns sth starting with "arm-", # while clang -dumpmachine says "arm6k-..." - try to unify that to "arm" if(CMAKE_SIZEOF_VOID_P EQUAL 8) # sizeof(void*) == 8 => must be arm64 set(cpu "arm64") else() # should be 32bit arm then (probably "armv7l" "armv6k" or sth like that) set(cpu "arm") endif() endif() if(MSVC) # for MSVC YQ2ARCH is set in code (in src/common/header/common.h) message(STATUS "Setting YQ2OSTYPE to \"${YQ2OSTYPE}\" - NOT setting YQ2ARCH, because we're targeting MSVC (VisualC++)") else() set(ARCH "${cpu}") add_definitions(-DYQ2ARCH="${ARCH}") message(STATUS "Setting YQ2OSTYPE to \"${YQ2OSTYPE}\" and YQ2ARCH to \"${ARCH}\".") endif() # make sure that ${cpu} isn't used below - if at all use ${ARCH}, but not when compiling with MSVC! unset(cpu) # END OF workarounds for CMake's poor choices regarding CPU architecture detection # Systemwide installation of game assets. if(${SYSTEMWIDE_SUPPORT}) add_definitions(-DSYSTEMWIDE) if(NOT ${SYSTEMDIR} STREQUAL "") add_definitions(-DSYSTEMDIR="${SYSTEMDIR}") endif() endif() # We need to pass some options to minizip / unzip. add_definitions(-DNOUNCRYPT) if(NOT (CMAKE_SYSTEM_NAME MATCHES "Linux") AND NOT (CMAKE_SYSTEM_NAME MATCHES "Windows")) add_definitions(-DIOAPI_NO_64) 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). if (SDL3_SUPPORT) find_package(SDL3 REQUIRED) add_definitions(-DUSE_SDL3) else() find_package(SDL2 REQUIRED) list(APPEND yquake2IncludeDirectories "${SDL2_INCLUDE_DIR}/..") list(APPEND yquake2SDLLinkerFlags ${SDL2_LIBRARY}) endif() # We need an OpenGL implementation. set(OpenGL_GL_PREFERENCE GLVND) find_package(OpenGL REQUIRED) list(APPEND yquake2IncludeDirectories ${OPENGL_INCLUDE_DIR}) list(APPEND yquake2OpenGLLinkerFlags ${OPENGL_LIBRARIES}) # backtrace lookup # Some systems like Linux has it within the libc some like the BSD, Haiku ... # into an external libexecinfo library include(CheckFunctionExists) include(CheckLibraryExists) check_function_exists(backtrace HAVE_EXECINFO_SYS) IF (NOT HAVE_EXECINFO_SYS) check_library_exists(execinfo backtrace "" HAVE_EXECINFO_LIB) if (HAVE_EXECINFO_LIB) list(APPEND yquake2ClientLinkerFlags execinfo) list(APPEND yquake2ServerLinkerFlags execinfo) add_definitions(-DHAVE_EXECINFO) endif() else() add_definitions(-DHAVE_EXECINFO) endif() # cURL support. if (${CURL_SUPPORT}) find_package(CURL REQUIRED) add_definitions(-DUSE_CURL) endif() # OpenAL support. if(${OPENAL_SUPPORT}) find_package(OpenAL) if(${OPENAL_FOUND}) 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() # General linker flags. if(NOT MSVC) list(APPEND yquake2LinkerFlags m) endif() list(APPEND yquake2LinkerFlags ${CMAKE_DL_LIBS}) if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") if(!MSVC) list(APPEND yquake2LinkerFlags "-static-libgcc") endif() else() if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Haiku") list(APPEND yquake2LinkerFlags "-rdynamic") else() list(APPEND yquake2LinkerFlags "-lnetwork") set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() if (${CMAKE_SYSTEM_NAME} MATCHES "SunOS") list(APPEND yquake2LinkerFlags "-lsocket -lnsl") endif() endif() if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD" AND NOT WIN32) list(APPEND yquake2LinkerFlags "-Wl,--no-undefined") endif() # With all of those libraries and user defined paths # added, lets give them to the compiler and linker. include_directories(${yquake2IncludeDirectories}) link_directories(${yquake2LinkerDirectories}) # these settings only work for GCC and clang if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang") # 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" AND ${CMAKE_C_COMPILER_ID} STREQUAL "GNU") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffloat-store") 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() if ("${ARCH}" STREQUAL "arm") if (CMAKE_SIZEOF_VOID_P EQUAL 4) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv6k") endif() endif() endif() set(Backends-Generic-Source ${BACKENDS_SRC_DIR}/generic/misc.c ) set(Backends-Unix-Source ${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-Windows-Source ${BACKENDS_SRC_DIR}/windows/icon.rc ${BACKENDS_SRC_DIR}/windows/main.c ${BACKENDS_SRC_DIR}/windows/network.c ${BACKENDS_SRC_DIR}/windows/system.c ${BACKENDS_SRC_DIR}/windows/shared/hunk.c ) set(Backends-Windows-Header ${BACKENDS_SRC_DIR}/windows/header/resource.h ) set(REF-Windows-Source ${BACKENDS_SRC_DIR}/windows/shared/hunk.c ) set(REF-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(REF-Platform-Specific-Source ${REF-Windows-Source}) else() set(Platform-Specific-Source ${Backends-Unix-Source}) set(REF-Platform-Specific-Source ${REF-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}/curl/download.c ${CLIENT_SRC_DIR}/curl/qcurl.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/qal.c ${CLIENT_SRC_DIR}/sound/sdl.c ${CLIENT_SRC_DIR}/sound/sound.c ${CLIENT_SRC_DIR}/sound/wave.c ${CLIENT_SRC_DIR}/vid/vid.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 ${COMMON_SRC_DIR}/unzip/miniz/miniz.c ${COMMON_SRC_DIR}/unzip/miniz/miniz_tdef.c ${COMMON_SRC_DIR}/unzip/miniz/miniz_tinfl.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 ) if(SDL3_SUPPORT) set(Client-SDL-Source ${CLIENT_SRC_DIR}/input/sdl3.c ${CLIENT_SRC_DIR}/vid/glimp_sdl3.c ) else() set(Client-SDL-Source ${CLIENT_SRC_DIR}/input/sdl2.c ${CLIENT_SRC_DIR}/vid/glimp_sdl2.c ) endif() 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/screen.h ${CLIENT_SRC_DIR}/curl/header/download.h ${CLIENT_SRC_DIR}/curl/header/qcurl.h ${CLIENT_SRC_DIR}/input/header/input.h ${CLIENT_SRC_DIR}/menu/header/qmenu.h ${CLIENT_SRC_DIR}/sound/header/local.h ${CLIENT_SRC_DIR}/sound/header/qal.h ${CLIENT_SRC_DIR}/sound/header/sound.h ${CLIENT_SRC_DIR}/sound/header/stb_vorbis.h ${CLIENT_SRC_DIR}/sound/header/vorbis.h ${CLIENT_SRC_DIR}/vid/header/ref.h ${CLIENT_SRC_DIR}/vid/header/stb_image_write.h ${CLIENT_SRC_DIR}/vid/header/vid.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 ${COMMON_SRC_DIR}/unzip/miniz/miniz.h ${COMMON_SRC_DIR}/unzip/miniz/miniz_tdef.h ${COMMON_SRC_DIR}/unzip/miniz/miniz_tinfl.h ${COMMON_SRC_DIR}/unzip/miniz/minizconf.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 ${COMMON_SRC_DIR}/unzip/miniz/miniz.c ${COMMON_SRC_DIR}/unzip/miniz/miniz_tdef.c ${COMMON_SRC_DIR}/unzip/miniz/miniz_tinfl.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 ${COMMON_SRC_DIR}/unzip/miniz/miniz.h ${COMMON_SRC_DIR}/unzip/miniz/miniz_tdef.h ${COMMON_SRC_DIR}/unzip/miniz/miniz_tinfl.h ${COMMON_SRC_DIR}/unzip/miniz/minizconf.h ${SERVER_SRC_DIR}/header/server.h ) set(GL1-Source ${REF_SRC_DIR}/gl1/qgl.c ${REF_SRC_DIR}/gl1/gl1_draw.c ${REF_SRC_DIR}/gl1/gl1_image.c ${REF_SRC_DIR}/gl1/gl1_light.c ${REF_SRC_DIR}/gl1/gl1_lightmap.c ${REF_SRC_DIR}/gl1/gl1_main.c ${REF_SRC_DIR}/gl1/gl1_mesh.c ${REF_SRC_DIR}/gl1/gl1_misc.c ${REF_SRC_DIR}/gl1/gl1_model.c ${REF_SRC_DIR}/gl1/gl1_scrap.c ${REF_SRC_DIR}/gl1/gl1_surf.c ${REF_SRC_DIR}/gl1/gl1_warp.c ${REF_SRC_DIR}/gl1/gl1_sdl.c ${REF_SRC_DIR}/gl1/gl1_buffer.c ${REF_SRC_DIR}/files/models.c ${REF_SRC_DIR}/files/pcx.c ${REF_SRC_DIR}/files/stb.c ${REF_SRC_DIR}/files/surf.c ${REF_SRC_DIR}/files/wal.c ${REF_SRC_DIR}/files/pvs.c ${COMMON_SRC_DIR}/shared/shared.c ${COMMON_SRC_DIR}/md4.c ) set(GL1-Header ${REF_SRC_DIR}/ref_shared.h ${REF_SRC_DIR}/constants/anorms.h ${REF_SRC_DIR}/constants/anormtab.h ${REF_SRC_DIR}/constants/warpsin.h ${REF_SRC_DIR}/files/stb_image.h ${REF_SRC_DIR}/files/surf.c ${REF_SRC_DIR}/gl1/header/local.h ${REF_SRC_DIR}/gl1/header/model.h ${REF_SRC_DIR}/gl1/header/qgl.h ${COMMON_SRC_DIR}/header/shared.h ) set(GL3-Source ${REF_SRC_DIR}/gl3/gl3_draw.c ${REF_SRC_DIR}/gl3/gl3_image.c ${REF_SRC_DIR}/gl3/gl3_light.c ${REF_SRC_DIR}/gl3/gl3_lightmap.c ${REF_SRC_DIR}/gl3/gl3_main.c ${REF_SRC_DIR}/gl3/gl3_mesh.c ${REF_SRC_DIR}/gl3/gl3_misc.c ${REF_SRC_DIR}/gl3/gl3_model.c ${REF_SRC_DIR}/gl3/gl3_sdl.c ${REF_SRC_DIR}/gl3/gl3_surf.c ${REF_SRC_DIR}/gl3/gl3_warp.c ${REF_SRC_DIR}/gl3/gl3_shaders.c ${REF_SRC_DIR}/files/models.c ${REF_SRC_DIR}/files/pcx.c ${REF_SRC_DIR}/files/stb.c ${REF_SRC_DIR}/files/surf.c ${REF_SRC_DIR}/files/wal.c ${REF_SRC_DIR}/files/pvs.c ${COMMON_SRC_DIR}/shared/shared.c ${COMMON_SRC_DIR}/md4.c ) set(Glad-GL3-Source ${REF_SRC_DIR}/gl3/glad/src/glad.c) set(Glad-GLES3-Source ${REF_SRC_DIR}/gl3/glad-gles3/src/glad.c) set(GL3-Header ${REF_SRC_DIR}/ref_shared.h ${REF_SRC_DIR}/constants/anorms.h ${REF_SRC_DIR}/constants/anormtab.h ${REF_SRC_DIR}/constants/warpsin.h ${REF_SRC_DIR}/files/stb_image.h ${REF_SRC_DIR}/gl3/header/DG_dynarr.h ${REF_SRC_DIR}/gl3/header/HandmadeMath.h ${REF_SRC_DIR}/gl3/header/local.h ${REF_SRC_DIR}/gl3/header/model.h ${COMMON_SRC_DIR}/header/shared.h ) set(Glad-GL3-Header ${REF_SRC_DIR}/gl3/glad/include/glad/glad.h ${REF_SRC_DIR}/gl3/glad/include/KHR/khrplatform.h ) set(Glad-GLES3-Header ${REF_SRC_DIR}/gl3/glad-gles3/include/glad/glad.h ${REF_SRC_DIR}/gl3/glad-gles3/include/KHR/khrplatform.h ) set(SOFT-Source ${REF_SRC_DIR}/soft/sw_aclip.c ${REF_SRC_DIR}/soft/sw_alias.c ${REF_SRC_DIR}/soft/sw_bsp.c ${REF_SRC_DIR}/soft/sw_draw.c ${REF_SRC_DIR}/soft/sw_edge.c ${REF_SRC_DIR}/soft/sw_image.c ${REF_SRC_DIR}/soft/sw_light.c ${REF_SRC_DIR}/soft/sw_main.c ${REF_SRC_DIR}/soft/sw_misc.c ${REF_SRC_DIR}/soft/sw_model.c ${REF_SRC_DIR}/soft/sw_part.c ${REF_SRC_DIR}/soft/sw_poly.c ${REF_SRC_DIR}/soft/sw_polyset.c ${REF_SRC_DIR}/soft/sw_rast.c ${REF_SRC_DIR}/soft/sw_scan.c ${REF_SRC_DIR}/soft/sw_sprite.c ${REF_SRC_DIR}/soft/sw_surf.c ${REF_SRC_DIR}/files/models.c ${REF_SRC_DIR}/files/pcx.c ${REF_SRC_DIR}/files/stb.c ${REF_SRC_DIR}/files/surf.c ${REF_SRC_DIR}/files/wal.c ${REF_SRC_DIR}/files/pvs.c ${COMMON_SRC_DIR}/shared/shared.c ${COMMON_SRC_DIR}/md4.c ) set(SOFT-Header ${REF_SRC_DIR}/ref_shared.h ${REF_SRC_DIR}/files/stb_image.h ${REF_SRC_DIR}/files/stb_image_resize.h ${REF_SRC_DIR}/soft/header/local.h ${REF_SRC_DIR}/soft/header/model.h ${COMMON_SRC_DIR}/header/shared.h ) # Main Quake 2 executable if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") add_executable(yquake2 WIN32 ${Client-Source} ${Client-SDL-Source} ${Client-Header} ${Platform-Specific-Source} ${Backends-Generic-Source}) set_target_properties(yquake2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release ) target_link_libraries(yquake2 ${yquake2LinkerFlags} ${yquake2ClientLinkerFlags} ${yquake2SDLLinkerFlags} ${yquake2ZLibLinkerFlags} ws2_32 winmm) if(SDL3_SUPPORT) target_link_libraries(yquake2 SDL3::SDL3) endif() if(MSVC AND CMAKE_MAJOR_VERSION GREATER 3 OR ( CMAKE_MAJOR_VERSION EQUAL 3 AND CMAKE_MINOR_VERSION GREATER_EQUAL 6 )) # CMake >= 3.6 supports setting the default project started for debugging (instead of trying to launch ALL_BUILD ...) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yquake2) set_target_properties(yquake2 PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/release) endif() # Wrapper for the Windows binary set(Wrapper-Source src/win-wrapper/wrapper.c ${BACKENDS_SRC_DIR}/windows/icon.rc ) add_executable(quake2 WIN32 ${Wrapper-Source}) set_target_properties(quake2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release) else() add_executable(quake2 ${Client-Source} ${Client-SDL-Source} ${Client-Header} ${Platform-Specific-Source} ${Backends-Generic-Source}) set_target_properties(quake2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release ) target_link_libraries(quake2 ${yquake2LinkerFlags} ${yquake2ClientLinkerFlags} ${yquake2SDLLinkerFlags} ${yquake2ZLibLinkerFlags}) if(SDL3_SUPPORT) target_link_libraries(quake2 SDL3::SDL3) endif() endif() # Quake 2 Dedicated Server add_executable(q2ded ${Server-Source} ${Server-Header} ${Platform-Specific-Source} ${Backends-Generic-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}) else() target_link_libraries(q2ded ${yquake2LinkerFlags} ${yquake2ServerLinkerFlags} ${yquake2ZLibLinkerFlags}) endif() # Build the game dynamic library add_library(game MODULE ${Game-Source} ${Game-Header}) set_target_properties(game PROPERTIES PREFIX "" SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX} ) get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(isMultiConfig) # multi-config, like Visual Studio solution set_target_properties(game PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release/$/baseq2 RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release/$/baseq2 ) else() # single-config, like normal Makefiles set_target_properties(game PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release/baseq2 RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release/baseq2 ) endif() target_link_libraries(game ${yquake2LinkerFlags}) # Build the GL1 dynamic library add_library(ref_gl1 MODULE ${GL1-Source} ${GL1-Header} ${REF-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}) if(SDL3_SUPPORT) target_link_libraries(ref_gl1 SDL3::SDL3) endif() # Build the GL3 dynamic library add_library(ref_gl3 MODULE ${GL3-Source} ${Glad-GL3-Source} ${GL3-Header} ${Glad-GL3-Header} ${REF-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_include_directories(ref_gl3 PRIVATE ${CMAKE_SOURCE_DIR}/src/client/refresh/gl3/glad/include) target_link_libraries(ref_gl3 ${yquake2LinkerFlags} ${yquake2SDLLinkerFlags}) if(SDL3_SUPPORT) target_link_libraries(ref_gl3 SDL3::SDL3) endif() # Build the GLES3 dynamic library add_library(ref_gles3 MODULE ${GL3-Source} ${Glad-GLES3-Source} ${GL3-Header} ${Glad-GLES3-Header} ${REF-Platform-Specific-Source}) set_target_properties(ref_gles3 PROPERTIES PREFIX "" #COMPILE_DEFINITIONS "YQ2_GL3_GLES3=1;YQ2_GL3_GLES=1" LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/release SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX} ) target_include_directories(ref_gles3 PRIVATE ${CMAKE_SOURCE_DIR}/src/client/refresh/gl3/glad-gles3/include) target_compile_definitions(ref_gles3 PRIVATE YQ2_GL3_GLES3=1 YQ2_GL3_GLES=1) target_link_libraries(ref_gles3 ${yquake2LinkerFlags} ${yquake2SDLLinkerFlags}) if(SDL3_SUPPORT) target_link_libraries(ref_gles3 SDL3::SDL3) endif() # Build the soft renderer dynamic library add_library(ref_soft MODULE ${SOFT-Source} ${SOFT-Header} ${REF-Platform-Specific-Source}) set_target_properties(ref_soft 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_soft ${yquake2LinkerFlags} ${yquake2SDLLinkerFlags}) if(SDL3_SUPPORT) target_link_libraries(ref_soft SDL3::SDL3) endif() yquake2-QUAKE2_8_40/LICENSE000066400000000000000000000644031465112212000151250ustar00rootroot00000000000000Yamagi Quake II contains software developed by multiple individuals, projects and organisations. Following is a list of this software and copys of each license: - Quake II - Info-ZIP - Cocoa SDL entry points - stb_image.h, stb_image_write.h, stb_image_resize.h, stb_vorbis.h - miniz Parts of other Quake II Clients were included into the source. They're 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: - cURL - libGL - libopenal - 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 =============================================================================== stb single-file public domain libraries (stb_image.h, stb_image_resize.h, stb_image_write.h, stb_vorbis.h) https://github.com/nothings/stb/ Dual-Licensed: Public Domain (Unlicense) or MIT License: 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. =============================================================================== Copyright 2013-2014 RAD Game Tools and Valve Software Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 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. =============================================================================== yquake2-QUAKE2_8_40/Makefile000066400000000000000000001011761465112212000155570ustar00rootroot00000000000000# ------------------------------------------------------ # # Makefile for the "Yamagi Quake 2 Client" # # # # Just type "make" to compile the # # - Client (quake2) # # - Server (q2ded) # # - Quake II Game (baseq2) # # - Renderer libraries (gl1, gl3, soft) # # # # Base dependencies: # # - SDL 2 or SDL 3 # # - libGL # # - Vulkan headers # # # # Optional dependencies: # # - CURL # # - OpenAL # # # # Platforms: # # - FreeBSD # # - Linux # # - NetBSD # # - OpenBSD # # - OS X # # - Windows (MinGW) # # ------------------------------------------------------ # # Variables # --------- # - ASAN: Builds with address sanitizer, includes DEBUG. # - DEBUG: Builds a debug build, forces -O0 and adds debug symbols. # - VERBOSE: Prints full compile, linker and misc commands. # - UBSAN: Builds with undefined behavior sanitizer, includes DEBUG. # ---------- # User configurable options # ------------------------- # Enables HTTP support through cURL. Used for # HTTP download. WITH_CURL:=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 # Sets an RPATH to $ORIGIN/lib. It can be used to # inject custom libraries, e.g. a patches libSDL.so # or libopenal.so. Not supported on Windows. WITH_RPATH:=yes # Builds with SDL 3 instead of SDL 2. WITH_SDL3:=no # 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 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 MINGW_CHOST ifeq ($(MINGW_CHOST), x86_64-w64-mingw32) YQ2_ARCH ?= x86_64 else # i686-w64-mingw32 YQ2_ARCH ?= i386 endif else # windows, but MINGW_CHOST not defined ifdef PROCESSOR_ARCHITEW6432 # 64 bit Windows YQ2_ARCH ?= $(PROCESSOR_ARCHITEW6432) else # 32 bit Windows YQ2_ARCH ?= $(PROCESSOR_ARCHITECTURE) endif endif # windows but MINGW_CHOST not defined else ifneq ($(YQ2_OSTYPE), Darwin) # Normalize some abiguous YQ2_ARCH strings YQ2_ARCH ?= $(shell uname -m | sed -e 's/i.86/i386/' -e 's/amd64/x86_64/' -e 's/arm64/aarch64/' -e 's/^arm.*/arm/') else YQ2_ARCH ?= $(shell uname -m) endif endif # Detect the compiler ifeq ($(shell $(CC) -v 2>&1 | grep -c "clang version"), 1) COMPILER := clang COMPILERVER := $(shell $(CC) -dumpversion | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/') else ifeq ($(shell $(CC) -v 2>&1 | grep -c -E "(gcc version|gcc-Version)"), 1) COMPILER := gcc COMPILERVER := $(shell $(CC) -dumpversion | sed -e 's/\.\([0-9][0-9]\)/\1/g' -e 's/\.\([0-9]\)/0\1/g' -e 's/^[0-9]\{3,4\}$$/&00/') else COMPILER := unknown endif # ASAN includes DEBUG ifdef ASAN DEBUG=1 endif # UBSAN includes DEBUG ifdef UBSAN DEBUG=1 endif # ---------- # Base CFLAGS. These may be overridden by the environment. # Highest supported optimizations are -O2, higher levels # will likely break this crappy code. ifdef DEBUG CFLAGS ?= -O0 -g -Wall -pipe -DDEBUG ifdef ASAN override CFLAGS += -fsanitize=address -DUSE_SANITIZER endif ifdef UBSAN override CFLAGS += -fsanitize=undefined -DUSE_SANITIZER endif else CFLAGS ?= -O2 -Wall -pipe -fomit-frame-pointer endif # Always needed are: # -fno-strict-aliasing since the source doesn't comply # with strict aliasing rules and it's next to impossible # to get it there... # -fwrapv for defined integer wrapping. MSVC6 did this # and the game code requires it. # -fvisibility=hidden to keep symbols hidden. This is # mostly best practice and not really necessary. override CFLAGS += -fno-strict-aliasing -fwrapv -fvisibility=hidden # -MMD to generate header dependencies. Unsupported by # the Clang shipped with OS X. ifneq ($(YQ2_OSTYPE), Darwin) override CFLAGS += -MMD endif # OS X architecture. ifeq ($(YQ2_OSTYPE), Darwin) override CFLAGS += -arch $(YQ2_ARCH) endif # ---------- # ARM needs a sane minimum architecture. We need the `yield` # opcode, arm6k is the first iteration that supports it. arm6k # is also the first Raspberry PI generation and older hardware # is likely too slow to run the game. We're not enforcing the # minimum architecture, but if you're build for something older # like arm5 the `yield` opcode isn't compiled in and the game # (especially q2ded) will consume more CPU time than necessary. ifeq ($(YQ2_ARCH), arm) CFLAGS += -march=armv6k endif # ---------- # Switch of some annoying warnings. ifeq ($(COMPILER), clang) # -Wno-missing-braces because otherwise clang complains # about totally valid 'vec3_t bla = {0}' constructs. override CFLAGS += -Wno-missing-braces else ifeq ($(COMPILER), gcc) # GCC 8.0 or higher. ifeq ($(shell test $(COMPILERVER) -ge 80000; echo $$?),0) # -Wno-format-truncation and -Wno-format-overflow # because GCC spams about 50 false positives. override CFLAGS += -Wno-format-truncation -Wno-format-overflow endif endif # ---------- # Defines the operating system and architecture override CFLAGS += -DYQ2OSTYPE=\"$(YQ2_OSTYPE)\" -DYQ2ARCH=\"$(YQ2_ARCH)\" # ---------- # For reproduceable builds, look here for details: # https://reproducible-builds.org/specs/source-date-epoch/ ifdef SOURCE_DATE_EPOCH override CFLAGS += -DBUILD_DATE=\"$(shell date --utc --date="@${SOURCE_DATE_EPOCH}" +"%b %_d %Y" | sed -e 's/ /\\ /g')\" endif # ---------- # Using the default x87 float math on 32bit x86 causes rounding trouble # -ffloat-store could work around that, but the better solution is to # just enforce SSE - every x86 CPU since Pentium3 supports that # and this should even improve the performance on old CPUs ifeq ($(YQ2_ARCH), i386) override CFLAGS += -msse -mfpmath=sse 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) override CFLAGS += -mfpmath=sse endif # Disable floating-point expression contraction. While this shouldn't be # a problem for C (only for C++) better be safe than sorry. See # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100839 for details. ifeq ($(COMPILER), gcc) override CFLAGS += -ffp-contract=off endif # ---------- # Systemwide installation. ifeq ($(WITH_SYSTEMWIDE),yes) override CFLAGS += -DSYSTEMWIDE ifneq ($(WITH_SYSTEMDIR),"") override CFLAGS += -DSYSTEMDIR=\"$(WITH_SYSTEMDIR)\" endif endif # ---------- # We don't support encrypted ZIP files. ZIPCFLAGS := -DNOUNCRYPT # Just set IOAPI_NO_64 on everything that's not Linux or Windows, # otherwise minizip will use fopen64(), fseek64() and friends that # may be unavailable. This is - of course - not really correct, in # a better world we would set -DIOAPI_NO_64 to force everything to # fopen(), fseek() and so on and -D_FILE_OFFSET_BITS=64 to let the # libc headers do their work. Currently we can't do that because # Quake II uses nearly everywere int instead of off_t... # # This may have the side effect that ZIP files larger than 2GB are # unsupported. But I doubt that anyone has such large files, they # would likely hit other internal limits. ifneq ($(YQ2_OSTYPE),Windows) ifneq ($(YQ2_OSTYPE),Linux) ZIPCFLAGS += -DIOAPI_NO_64 endif endif # ---------- # Extra CFLAGS for SDL. ifeq ($(WITH_SDL3),yes) SDLCFLAGS := $(shell pkgconf --cflags sdl3) SDLCFLAGS += -DUSE_SDL3 else SDLCFLAGS := $(shell sdl2-config --cflags) 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),NetBSD) INCLUDE ?= -I/usr/X11R7/include -I/usr/pkg/include else ifeq ($(YQ2_OSTYPE),OpenBSD) INCLUDE ?= -I/usr/local/include else ifeq ($(YQ2_OSTYPE),Windows) INCLUDE ?= -I/usr/include else ifeq ($(YQ2_OSTYPE),Darwin) INCLUDE ?= -I/usr/local/include -I/opt/homebrew/include endif # ---------- # Base LDFLAGS. This is just the library path. ifeq ($(YQ2_OSTYPE),Linux) LDFLAGS ?= -L/usr/lib else ifeq ($(YQ2_OSTYPE),FreeBSD) LDFLAGS ?= -L/usr/local/lib else ifeq ($(YQ2_OSTYPE),NetBSD) LDFLAGS ?= -L/usr/X11R7/lib -Wl,-R/usr/X11R7/lib -L/usr/pkg/lib -Wl,-R/usr/pkg/lib else ifeq ($(YQ2_OSTYPE),OpenBSD) LDFLAGS ?= -L/usr/local/lib else ifeq ($(YQ2_OSTYPE),Windows) LDFLAGS ?= -L/usr/lib else ifeq ($(YQ2_OSTYPE),Darwin) LDFLAGS ?= -L/usr/local/lib -L/opt/homebrew/lib endif # Link address sanitizer if requested. ifdef ASAN override LDFLAGS += -fsanitize=address endif # Link undefined behavior sanitizer if requested. ifdef UBSAN override LDFLAGS += -fsanitize=undefined endif # Required libraries. ifeq ($(YQ2_OSTYPE),Linux) LDLIBS ?= -lm -ldl -rdynamic else ifeq ($(YQ2_OSTYPE),FreeBSD) LDLIBS ?= -lm else ifeq ($(YQ2_OSTYPE),NetBSD) LDLIBS ?= -lm else ifeq ($(YQ2_OSTYPE),OpenBSD) LDLIBS ?= -lm else ifeq ($(YQ2_OSTYPE),Windows) LDLIBS ?= -lws2_32 -lwinmm -static-libgcc else ifeq ($(YQ2_OSTYPE), Darwin) LDLIBS ?= -arch $(YQ2_ARCH) else ifeq ($(YQ2_OSTYPE), Haiku) LDLIBS ?= -lm -lnetwork else ifeq ($(YQ2_OSTYPE), SunOS) LDLIBS ?= -lm -lsocket -lnsl endif # ASAN and UBSAN must not be linked # with --no-undefined. OSX and OpenBSD # don't support it at all. ifndef ASAN ifndef UBSAN ifneq ($(YQ2_OSTYPE), Darwin) ifneq ($(YQ2_OSTYPE), OpenBSD) override LDFLAGS += -Wl,--no-undefined endif endif endif endif # ---------- # Extra LDFLAGS for SDL ifeq ($(WITH_SDL3),yes) ifeq ($(YQ2_OSTYPE), Darwin) SDLLDFLAGS := -lSDL3 else SDLLDFLAGS := $(shell pkgconf --libs sdl3) endif else ifeq ($(YQ2_OSTYPE), Darwin) SDLLDFLAGS := -lSDL2 else SDLLDFLAGS := $(shell sdl2-config --libs) endif endif # The renderer libs don't need libSDL2main, libmingw32 or -mwindows. ifeq ($(YQ2_OSTYPE), Windows) DLL_SDLLDFLAGS = $(subst -mwindows,,$(subst -lmingw32,,$(subst -lSDL2main,,$(SDLLDFLAGS)))) 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 ref_gles3 ref_soft # ---------- # Builds everything all: config client server game ref_gl1 ref_gl3 ref_gles3 ref_soft # ---------- # Print config values config: @echo "Build configuration" @echo "============================" @echo "YQ2_ARCH = $(YQ2_ARCH) COMPILER = $(COMPILER)" @echo "WITH_CURL = $(WITH_CURL)" @echo "WITH_OPENAL = $(WITH_OPENAL)" @echo "WITH_RPATH = $(WITH_RPATH)" @echo "WITH_SDL3 = $(WITH_SDL3)" @echo "WITH_SYSTEMWIDE = $(WITH_SYSTEMWIDE)" @echo "WITH_SYSTEMDIR = $(WITH_SYSTEMDIR)" @echo "============================" @echo "" # ---------- # 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 yquake2.exe" ${Q}mkdir -p release $(MAKE) release/yquake2.exe @echo "===> Building quake2.exe Wrapper" $(MAKE) release/quake2.exe build/client/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(SDLCFLAGS) $(ZIPCFLAGS) $(INCLUDE) -o $@ $< release/yquake2.exe : LDFLAGS += -mwindows ifeq ($(WITH_CURL),yes) release/yquake2.exe : CFLAGS += -DUSE_CURL endif ifeq ($(WITH_OPENAL),yes) release/yquake2.exe : CFLAGS += -DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER='"openal32.dll"' endif else # not Windows client: @echo "===> Building quake2" ${Q}mkdir -p release $(MAKE) release/quake2 ifeq ($(YQ2_OSTYPE), Darwin) build/client/%.o : %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -arch $(YQ2_ARCH) -x objective-c -c $(CFLAGS) $(SDLCFLAGS) $(ZIPCFLAGS) $(INCLUDE) $< -o $@ else build/client/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(SDLCFLAGS) $(ZIPCFLAGS) $(INCLUDE) -o $@ $< endif release/quake2 : CFLAGS += -Wno-unused-result ifeq ($(WITH_CURL),yes) release/quake2 : CFLAGS += -DUSE_CURL endif ifeq ($(WITH_OPENAL),yes) ifeq ($(YQ2_OSTYPE), OpenBSD) release/quake2 : CFLAGS += -DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER='"libopenal.so"' else ifeq ($(YQ2_OSTYPE), Darwin) OPENAL_PATH ?= $(shell brew --prefix openal-soft) release/quake2 : CFLAGS += -DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER='"libopenal.dylib"' -I$(OPENAL_PATH)/include release/quake2 : LDFLAGS += -L$(OPENAL_PATH)/lib -rpath $(OPENAL_PATH)/lib else release/quake2 : CFLAGS += -DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER='"libopenal.so.1"' endif endif ifeq ($(YQ2_OSTYPE), Linux) release/quake2 : CFLAGS += -DHAVE_EXECINFO endif ifeq ($(YQ2_OSTYPE), Darwin) release/quake2 : CFLAGS += -DHAVE_EXECINFO endif ifeq ($(YQ2_OSTYPE), SunOS) release/quake2 : CFLAGS += -DHAVE_EXECINFO endif ifeq ($(YQ2_OSTYPE), FreeBSD) release/quake2 : CFLAGS += -DHAVE_EXECINFO release/quake2 : LDLIBS += -lexecinfo endif ifeq ($(YQ2_OSTYPE), NetBSD) release/quake2 : CFLAGS += -DHAVE_EXECINFO release/quake2 : LDLIBS += -lexecinfo endif ifeq ($(YQ2_OSTYPE), OpenBSD) release/quake2 : CFLAGS += -DHAVE_EXECINFO release/quake2 : LDLIBS += -lexecinfo endif ifeq ($(YQ2_OSTYPE), Haiku) release/quake2 : CFLAGS += -DHAVE_EXECINFO release/quake2 : LDLIBS += -lexecinfo endif ifeq ($(WITH_RPATH),yes) ifeq ($(YQ2_OSTYPE), Darwin) release/quake2 : LDFLAGS += -Wl,-rpath,'@executable_path/lib' else release/quake2 : LDFLAGS += -Wl,-z,origin,-rpath='$$ORIGIN/lib' 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) $(ZIPCFLAGS) $(INCLUDE) -o $@ $< release/q2ded.exe : CFLAGS += -DDEDICATED_ONLY 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) $(ZIPCFLAGS) $(INCLUDE) -o $@ $< release/q2ded : CFLAGS += -DDEDICATED_ONLY -Wno-unused-result ifeq ($(YQ2_OSTYPE), FreeBSD) release/q2ded : LDLIBS += -lexecinfo endif endif # ---------- # The OpenGL 1.x renderer lib ifeq ($(YQ2_OSTYPE), Windows) ref_gl1: @echo "===> Building ref_gl1.dll" $(MAKE) release/ref_gl1.dll release/ref_gl1.dll : LDFLAGS += -shared release/ref_gl1.dll : LDLIBS += -lopengl32 else ifeq ($(YQ2_OSTYPE), Darwin) ref_gl1: @echo "===> Building ref_gl1.dylib" $(MAKE) release/ref_gl1.dylib 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 release/ref_gl1.so : LDLIBS += -lGL endif # OS specific ref_gl1 stuff build/ref_gl1/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(SDLCFLAGS) $(INCLUDE) -o $@ $< # ---------- # The OpenGL 3.x renderer lib ifeq ($(YQ2_OSTYPE), Windows) ref_gl3: @echo "===> Building ref_gl3.dll" $(MAKE) release/ref_gl3.dll release/ref_gl3.dll : GLAD_INCLUDE = -Isrc/client/refresh/gl3/glad/include release/ref_gl3.dll : LDFLAGS += -shared else ifeq ($(YQ2_OSTYPE), Darwin) ref_gl3: @echo "===> Building ref_gl3.dylib" $(MAKE) release/ref_gl3.dylib release/ref_gl3.dylib : GLAD_INCLUDE = -Isrc/client/refresh/gl3/glad/include 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 : GLAD_INCLUDE = -Isrc/client/refresh/gl3/glad/include release/ref_gl3.so : CFLAGS += -fPIC release/ref_gl3.so : LDFLAGS += -shared endif # OS specific ref_gl3 stuff build/ref_gl3/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(SDLCFLAGS) $(INCLUDE) $(GLAD_INCLUDE) -o $@ $< # ---------- # The OpenGL ES 3.0 renderer lib ifeq ($(YQ2_OSTYPE), Windows) ref_gles3: @echo "===> Building ref_gles3.dll" $(MAKE) release/ref_gles3.dll release/ref_gles3.dll : GLAD_INCLUDE = -Isrc/client/refresh/gl3/glad-gles3/include # YQ2_GL3_GLES3 is for GLES3, DYQ2_GL3_GLES is for things that are identical # in both GLES3 and GLES2 (in case we ever support that) release/ref_gles3.dll : CFLAGS += -DYQ2_GL3_GLES3 -DYQ2_GL3_GLES release/ref_gles3.dll : LDFLAGS += -shared else ifeq ($(YQ2_OSTYPE), Darwin) ref_gles3: @echo "===> Building ref_gles3.dylib" $(MAKE) release/ref_gles3.dylib release/ref_gles3.dylib : GLAD_INCLUDE = -Isrc/client/refresh/gl3/glad-gles3/include # YQ2_GL3_GLES3 is for GLES3, DYQ2_GL3_GLES is for things that are identical # in both GLES3 and GLES2 (in case we ever support that) release/ref_gles3.dylib : CFLAGS += -DYQ2_GL3_GLES3 -DYQ2_GL3_GLES release/ref_gles3.dylib : LDFLAGS += -shared else # not Windows or Darwin ref_gles3: @echo "===> Building ref_gles3.so" $(MAKE) release/ref_gles3.so release/ref_gles3.so : GLAD_INCLUDE = -Isrc/client/refresh/gl3/glad-gles3/include # YQ2_GL3_GLES3 is for GLES3, DYQ2_GL3_GLES is for things that are identical # in both GLES3 and GLES2 (in case we ever support that) release/ref_gles3.so : CFLAGS += -DYQ2_GL3_GLES3 -DYQ2_GL3_GLES -fPIC release/ref_gles3.so : LDFLAGS += -shared GLAD_INCLUDE = -Isrc/client/refresh/gl3/glad-gles3/include endif # OS specific ref_gl3 stuff build/ref_gles3/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(SDLCFLAGS) $(INCLUDE) $(GLAD_INCLUDE) -o $@ $< # ---------- # The soft renderer lib ifeq ($(YQ2_OSTYPE), Windows) ref_soft: @echo "===> Building ref_soft.dll" $(MAKE) release/ref_soft.dll release/ref_soft.dll : LDFLAGS += -shared else ifeq ($(YQ2_OSTYPE), Darwin) ref_soft: @echo "===> Building ref_soft.dylib" $(MAKE) release/ref_soft.dylib release/ref_soft.dylib : LDFLAGS += -shared else # not Windows or Darwin ref_soft: @echo "===> Building ref_soft.so" $(MAKE) release/ref_soft.so release/ref_soft.so : CFLAGS += -fPIC release/ref_soft.so : LDFLAGS += -shared endif # OS specific ref_soft stuff build/ref_soft/%.o: %.c @echo "===> CC $<" ${Q}mkdir -p $(@D) ${Q}$(CC) -c $(CFLAGS) $(SDLCFLAGS) $(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/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/curl/download.o \ src/client/curl/qcurl.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/qal.o \ src/client/sound/sdl.o \ src/client/sound/sound.o \ src/client/sound/wave.o \ src/client/vid/vid.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/common/unzip/miniz/miniz.o \ src/common/unzip/miniz/miniz_tdef.o \ src/common/unzip/miniz/miniz_tinfl.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 ($(WITH_SDL3),yes) CLIENT_OBJS_ += \ src/client/input/sdl3.o \ src/client/vid/glimp_sdl3.o else CLIENT_OBJS_ += \ src/client/input/sdl2.o \ src/client/vid/glimp_sdl2.o endif ifeq ($(YQ2_OSTYPE), Windows) CLIENT_OBJS_ += \ src/backends/windows/main.o \ src/backends/windows/network.o \ src/backends/windows/system.o \ src/backends/windows/shared/hunk.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/gl1/qgl.o \ src/client/refresh/gl1/gl1_draw.o \ src/client/refresh/gl1/gl1_image.o \ src/client/refresh/gl1/gl1_light.o \ src/client/refresh/gl1/gl1_lightmap.o \ src/client/refresh/gl1/gl1_main.o \ src/client/refresh/gl1/gl1_mesh.o \ src/client/refresh/gl1/gl1_misc.o \ src/client/refresh/gl1/gl1_model.o \ src/client/refresh/gl1/gl1_scrap.o \ src/client/refresh/gl1/gl1_surf.o \ src/client/refresh/gl1/gl1_warp.o \ src/client/refresh/gl1/gl1_sdl.o \ src/client/refresh/gl1/gl1_buffer.o \ src/client/refresh/files/surf.o \ src/client/refresh/files/models.o \ src/client/refresh/files/pcx.o \ src/client/refresh/files/stb.o \ src/client/refresh/files/wal.o \ src/client/refresh/files/pvs.o \ src/common/shared/shared.o \ src/common/md4.o ifeq ($(YQ2_OSTYPE), Windows) REFGL1_OBJS_ += \ src/backends/windows/shared/hunk.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/files/surf.o \ src/client/refresh/files/models.o \ src/client/refresh/files/pcx.o \ src/client/refresh/files/stb.o \ src/client/refresh/files/wal.o \ src/client/refresh/files/pvs.o \ src/common/shared/shared.o \ src/common/md4.o REFGL3_OBJS_GLADE_ := \ src/client/refresh/gl3/glad/src/glad.o REFGL3_OBJS_GLADEES_ := \ src/client/refresh/gl3/glad-gles3/src/glad.o ifeq ($(YQ2_OSTYPE), Windows) REFGL3_OBJS_ += \ src/backends/windows/shared/hunk.o else # not Windows REFGL3_OBJS_ += \ src/backends/unix/shared/hunk.o endif # ---------- REFSOFT_OBJS_ := \ src/client/refresh/soft/sw_aclip.o \ src/client/refresh/soft/sw_alias.o \ src/client/refresh/soft/sw_bsp.o \ src/client/refresh/soft/sw_draw.o \ src/client/refresh/soft/sw_edge.o \ src/client/refresh/soft/sw_image.o \ src/client/refresh/soft/sw_light.o \ src/client/refresh/soft/sw_main.o \ src/client/refresh/soft/sw_misc.o \ src/client/refresh/soft/sw_model.o \ src/client/refresh/soft/sw_part.o \ src/client/refresh/soft/sw_poly.o \ src/client/refresh/soft/sw_polyset.o \ src/client/refresh/soft/sw_rast.o \ src/client/refresh/soft/sw_scan.o \ src/client/refresh/soft/sw_sprite.o \ src/client/refresh/soft/sw_surf.o \ src/client/refresh/files/surf.o \ src/client/refresh/files/models.o \ src/client/refresh/files/pcx.o \ src/client/refresh/files/stb.o \ src/client/refresh/files/wal.o \ src/client/refresh/files/pvs.o \ src/common/shared/shared.o \ src/common/md4.o ifeq ($(YQ2_OSTYPE), Windows) REFSOFT_OBJS_ += \ src/backends/windows/shared/hunk.o else # not Windows REFSOFT_OBJS_ += \ src/backends/unix/shared/hunk.o endif # ---------- # Used by the server SERVER_OBJS_ := \ src/backends/generic/misc.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/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/common/unzip/miniz/miniz.o \ src/common/unzip/miniz/miniz_tdef.o \ src/common/unzip/miniz/miniz_tinfl.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) SERVER_OBJS_ += \ src/backends/windows/main.o \ src/backends/windows/network.o \ src/backends/windows/system.o \ src/backends/windows/shared/hunk.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 paths 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_)) REFGL3_OBJS += $(patsubst %,build/ref_gl3/%,$(REFGL3_OBJS_GLADE_)) REFGLES3_OBJS = $(patsubst %,build/ref_gles3/%,$(REFGL3_OBJS_)) REFGLES3_OBJS += $(patsubst %,build/ref_gles3/%,$(REFGL3_OBJS_GLADEES_)) REFSOFT_OBJS = $(patsubst %,build/ref_soft/%,$(REFSOFT_OBJS_)) SERVER_OBJS = $(patsubst %,build/server/%,$(SERVER_OBJS_)) GAME_OBJS = $(patsubst %,build/baseq2/%,$(GAME_OBJS_)) # ---------- # Generate header dependencies. CLIENT_DEPS= $(CLIENT_OBJS:.o=.d) GAME_DEPS= $(GAME_OBJS:.o=.d) REFGL1_DEPS= $(REFGL1_OBJS:.o=.d) REFGL3_DEPS= $(REFGL3_OBJS:.o=.d) REFGLES3_DEPS= $(REFGLES3_OBJS:.o=.d) REFSOFT_DEPS= $(REFSOFT_OBJS:.o=.d) SERVER_DEPS= $(SERVER_OBJS:.o=.d) # Suck header dependencies in. -include $(CLIENT_DEPS) -include $(GAME_DEPS) -include $(REFGL1_DEPS) -include $(REFGL3_DEPS) -include $(REFGLES3_DEPS) -include $(SERVER_DEPS) # ---------- # release/quake2 ifeq ($(YQ2_OSTYPE), Windows) release/yquake2.exe : $(CLIENT_OBJS) icon @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) build/icon/icon.res $(CLIENT_OBJS) $(LDLIBS) $(SDLLDFLAGS) -o $@ $(Q)strip $@ release/quake2.exe : src/win-wrapper/wrapper.c icon $(Q)$(CC) -Wall -mwindows build/icon/icon.res src/win-wrapper/wrapper.c -o $@ $(Q)strip $@ else release/quake2 : $(CLIENT_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(CLIENT_OBJS) $(LDLIBS) $(SDLLDFLAGS) -o $@ endif # release/q2ded ifeq ($(YQ2_OSTYPE), Windows) release/q2ded.exe : $(SERVER_OBJS) icon @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) build/icon/icon.res $(SERVER_OBJS) $(LDLIBS) -o $@ $(Q)strip $@ else release/q2ded : $(SERVER_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(SERVER_OBJS) $(LDLIBS) -o $@ endif # release/ref_gl1.so ifeq ($(YQ2_OSTYPE), Windows) release/ref_gl1.dll : $(REFGL1_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(REFGL1_OBJS) $(LDLIBS) $(DLL_SDLLDFLAGS) -o $@ $(Q)strip $@ else ifeq ($(YQ2_OSTYPE), Darwin) release/ref_gl1.dylib : $(REFGL1_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(REFGL1_OBJS) $(LDLIBS) $(SDLLDFLAGS) -o $@ else release/ref_gl1.so : $(REFGL1_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(REFGL1_OBJS) $(LDLIBS) $(SDLLDFLAGS) -o $@ endif # release/ref_gl3.so ifeq ($(YQ2_OSTYPE), Windows) release/ref_gl3.dll : $(REFGL3_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(REFGL3_OBJS) $(LDLIBS) $(DLL_SDLLDFLAGS) -o $@ $(Q)strip $@ else ifeq ($(YQ2_OSTYPE), Darwin) release/ref_gl3.dylib : $(REFGL3_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(REFGL3_OBJS) $(LDLIBS) $(SDLLDFLAGS) -o $@ else release/ref_gl3.so : $(REFGL3_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(REFGL3_OBJS) $(LDLIBS) $(SDLLDFLAGS) -o $@ endif # release/ref_gles3.so ifeq ($(YQ2_OSTYPE), Windows) release/ref_gles3.dll : $(REFGLES3_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(REFGLES3_OBJS) $(LDLIBS) $(DLL_SDLLDFLAGS) -o $@ $(Q)strip $@ else ifeq ($(YQ2_OSTYPE), Darwin) release/ref_gles3.dylib : $(REFGLES3_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(REFGLES3_OBJS) $(LDLIBS) $(SDLLDFLAGS) -o $@ else release/ref_gles3.so : $(REFGLES3_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(REFGLES3_OBJS) $(LDLIBS) $(SDLLDFLAGS) -o $@ endif # release/ref_soft.so ifeq ($(YQ2_OSTYPE), Windows) release/ref_soft.dll : $(REFSOFT_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(REFSOFT_OBJS) $(LDLIBS) $(DLL_SDLLDFLAGS) -o $@ $(Q)strip $@ else ifeq ($(YQ2_OSTYPE), Darwin) release/ref_soft.dylib : $(REFSOFT_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(REFSOFT_OBJS) $(LDLIBS) $(SDLLDFLAGS) -o $@ else release/ref_soft.so : $(REFSOFT_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(REFSOFT_OBJS) $(LDLIBS) $(SDLLDFLAGS) -o $@ endif # release/baseq2/game.so ifeq ($(YQ2_OSTYPE), Windows) release/baseq2/game.dll : $(GAME_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(GAME_OBJS) $(LDLIBS) -o $@ $(Q)strip $@ else ifeq ($(YQ2_OSTYPE), Darwin) release/baseq2/game.dylib : $(GAME_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(GAME_OBJS) $(LDLIBS) -o $@ else release/baseq2/game.so : $(GAME_OBJS) @echo "===> LD $@" ${Q}$(CC) $(LDFLAGS) $(GAME_OBJS) $(LDLIBS) -o $@ endif # ---------- yquake2-QUAKE2_8_40/README.md000066400000000000000000000022311465112212000153660ustar00rootroot00000000000000# Yamagi Quake II Yamagi Quake II is an enhanced client for id Software's Quake II with focus on offline and coop gameplay. Both the gameplay and the graphics are unchanged, but many bugs in 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, NetBSD, OpenBSD, Windows and macOS (experimental). This code is built 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. ## Documentation Before asking any question, read through the documentation! The current version can be found here: [doc/010_index.md](doc/010_index.md) ## Releases The official releases (including Windows binaries) can be found at our homepage: https://www.yamagi.org/quake2 **Unsupported** preview builds for Windows can be found at https://deponie.yamagi.org/quake2/misc/ yquake2-QUAKE2_8_40/doc/000077500000000000000000000000001465112212000146565ustar00rootroot00000000000000yquake2-QUAKE2_8_40/doc/010_index.md000066400000000000000000000021641465112212000166720ustar00rootroot00000000000000# Yamagi Quake II Documentation Welcome to the Yamagi Quake II documentation. Here is documented how Yamagi Quake II is installed and configured. Please make sure to read through the whole guide before asking questions! Some general hints: * No matter what's the current keyboard layout or on which platform the game is running, the console is always opened and closed by pressing **Left Shift + Escape**. * Yamagi Quake II saves its configuration, savegames and screenshots to *~/.yq2* (unixoid platforms) or *My Documents\YamagiQ2* (Windows). If Yamagi Quake II is started with the `-portable` switch all data is saved in the same directory as the executables. The documentation is split into several documents: * [Index File](010_index.md) * [Installation Guide](020_installation.md) * [Configuration Guide](030_configuration.md) * [List Of All New Console Variables](040_cvarlist.md) * [List Of All New Commands](050_commands.md) * [Multiplayer Server Configuration](060_multiplayer.md) * [Packaging Guide For Maintainers](070_packaging.md) * [Contributing Guide](080_contributing.md) * [File Listings](090_filelists.md) yquake2-QUAKE2_8_40/doc/020_installation.md000066400000000000000000000334241465112212000202700ustar00rootroot00000000000000# Installation This guide shows how to install Yamagi Quake II from scratch. All fully supported platforms, both the full and the demo version are covered. ## The Full Version Over the years Quake II was distributed in a myriad of ways: * As retail release on CD. * As part of Quake IV. * Through Steam. * Through GOG.com. * etc. Yamagi Quake II is compatible with all of these versions. While some of these versions come with all patches applied, it's highly recommended to follow this guide step by step and to reapply the patch by hand. Not all distributors patched the game correctly, leading to severe problems like missing assets or even crashes. ### Game Data Setup The easiest way to install Yamagi Quake II is to start with the patch. Please note that the patch is **required** for all full versions of the game. Without the patch the game will not work correctly! 1. Download the patch from our mirror or somewhere else. The MD5 checksum is `490557d4a90ff346a175d865a2bade87`: https://deponie.yamagi.org/quake2/idstuff/q2-3.20-x86-full-ctf.exe 2. Extract the patch into an empty directory. The patch comes as a self-extracting ZIP file. On Windows it can be extracted by double clicking on it, on other systems an archiver or the *unzip* command can be used. Now remove the following files from the extracted patch. They're the original executables, documentation and so on. They aren't needed for Yamagi Quake II and just waste space: * *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 the Quake II distribution (CD, Steam or GOG download, etc) into the *baseq2/* subdirectory of the extracted patch. If the optional addons are available their gamedata must be copied too: * For The Reckoning (also know as "xatrix") copy the *pak0.pak* and the *video/* subdirectory from the addon distribution into the *xatrix/* subdirectory. * For Ground Zero (also known as "rogue") copy the *pak0.pak* and the *video/* subdirectory from the addon distribution into the *rogue/* subdirectory. The MD5 checksums of the pakfiles are: * *baseq2/pak0.pak*: `1ec55a724dc3109fd50dde71ab581d70` * *baseq2/pak1.pak*: `42663ea709b7cd3eb9b634b36cfecb1a` * *baseq2/pak2.pak*: `c8217cc5557b672a87fc210c2347d98d` * *ctf/pak0.pak*: `1f6bd3d4c08f7ed8c037b12fcffd2eb5` * *rogue/pak0.pak*: `5e2ecbe9287152a1e6e0d77b3f47dcb2` * *xatrix/pak0.pak*: `f5e7b04f7d6b9530c59c5e1daa873b51` ### Music Extraction The retail releases of Quake II and both addons contain up to 11 Audio CD tracks as soundtrack. Since modern computers lack the ability for direct CD playback, Yamagi Quake II reads the music from OGG/Vorbis files. Later Quake II releases, 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 the files are copied into the directories mentioned below. Some digital distributed versions are special, they includes the soundtrack as OGG/Vorbis files, but in a non standard format. Yamagi Quake II can read this format for the GOG.com release. Other releases may be supported in the future. #### Using a Generic CD Extractor 1. Install a CD extractor (for example CDex) and set it to OGG/Vorbis files. Quality factor 6 (192 kbit/s) is usually more than enough. 2. Put the Quake II CD into the CD drive and extract the files. 3. The files must be named after the corresponding CD track: CD track 02 becomes the file *02.ogg*, CD track 03 becomes the file *03.ogg* and so on. On both the Quake II and the Addon CDs track 01 is the data track and thus 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. #### Using a Shell Script An easy way to extract the music on unixoid platforms (BSD, Linux and MacOS) is to use *stuff/cdripper.sh*, a simple shellscript. It needs *cdparanoia* and *oggenc* (from the *vorbis-tools* package) installed. Use the package manager (apt, dnf, homebrew, pacman, pkg, ...) 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. #### The GOG.com Release The Quake II distributed by GOG.com contains the soundtrack, it just needs to be copied into the game data directory. The target directory is just *music/*, next to *baseq2/*. **Not** inside *baseq2/*. Unixoid systems are case sensitive. On them the files should be named *TrackXX.ogg* with a capital T. Otherwise the game won't find them. ### Alternate Startup Configuration Yamagi Quake II ships with an alternative startup config that overrides some global settings to saner defaults. To use it copy *stuff/yq2.cfg* into the *baseq2/* directory. ### Fixed Map Data Files As an optional feature, Yamagi Quake II provides fixed map data files (.ent files). These files provide a replacement entity list in order to fix some map bugs that have been discovered by players over the years. These fixes include fixes for bad monster counts in some maps / difficulty settings, fixing broken spawn chains that made it impossible to spawn some monsters, removing DM-only items that spawn in unreachable areas in single player /co-op, and so on. You can find detailed changelogs for each map by opening the .ent files with a text editor and reading the comment section at the top (lines starting with "//"). #### Download And Setup 1. Download the .ent files from the yquake2 repositories on GitHub. * **baseq2**: yquake2/yquake2/stuff/mapfixes/baseq2/ * **juggernaut**: yquake2/yquake2/stuff/mapfixes/juggernaut/ * **xatrix**: yquake2/xatrix/stuff/mapfixes/ * **rogue**: yquake2/rogue/stuff/mapfixes/ * **zaero**: yquake2/zaero/stuff/mapfixes/ 2. Once you have the .ent files you want, put them in the respective *maps/* sub-folder. So *xatrix* .ent files should go into your local *xatrix/maps/* folder (create this folder if it does not exist). 3. You will see a notification message in the console if an .ent file was loaded. If you see this message, you know the map fixes are in effect. #### Reporting Map Bugs If you know of any map bugs that are not addressed here, by all means report them to us through our GitHub repositories. ### Monster Footstep Sounds Yamagi Quake II has optional support for monster footstep sounds. These require some custom audio files. Download the pak file with the sounds from https://deponie.yamagi.org/quake2/assets/footsteps.pkz and copy the file into the *baseq2/* directory. The footstep sounds are included with the official Windows release. ## The Demo Version A free demo version of Quake II is available and supported by Yamagi Quake II. This demo contains the first few levels, but no videos and no soundtrack. ### Game Data Setup 1. Download the demo from our mirror or somewhere else. Its MD5 checksum is `4d1cd4618e80a38db59304132ea0856c`: https://deponie.yamagi.org/quake2/idstuff/q2-314-demo-x86.exe 2. Extract the downloaded file. It's a self-extract ZIP archive. On Windows it can be extracted by double clicking on it, on other system an archiver or the *unzip* command can be used. 3. Create a new directory and a subdirectory *baseq2/* in it. 4. Copy the *pak0.pak* and the *players/* subdirectory from the extracted archive into the newly created *baseq2/* subdirectory. The demo **must not** be patched! Patching the demo will break it! The MD5 checksums of the pakfiles are: * *baseq2/pak0.pak*: `27d77240466ec4f3253256832b54db8a` ## Download and Extract the Executables How the Yamagi Quake II executables are installed depends on the platform: - For Windows a prebuild package with all Yamagi Quake II executables and the required libraries is provided. - Most Linux distributions and BSD systems provide Yamagi Quake II packages. Theses packages may be outdated, see below for compiling the executables. **Please note:** The addons needs their own game library. The libraries are included with the official Windows release. Linux distributions often package them in a distinced package. If you want to compile from source, have a look at the README files supplied with the addons. ### Windows 1. Get the latest release from https://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 the Yamagi Quake II installation is fully portable, the installation directory can be moved the installation directory wherever and whenever it's necessary. To update Yamagi Quake II just overwrite the old files with the new ones. There're two executables: * *yquake2.exe*: This is main executable and should be preferred. * *quake.exe*: This is just a wrapper to stay compatible with existing setups. For technical reasons *quake.exe* may not start in foreground, but in background! If Windows Defender is activated, that's the default on Windows 8 and Windows 10, it may complain that Yamagi Quake II is untrusted and should not be started. That's because we're shipping unsigned binaries. You can force Windows to start it anyways. ### Binary Package from Linux distributions or BSD systems Most Linux distributions and BSD systems provide Yamagi Quake II packages. Please refer to the documentation of the distribution or system. The gamedata is searched at: - A global directory specified by the package. - The same directory as the quake2 executable. - A directory given with the *-datadir /path/to/quake2_installation/* commandline argument. - In *$HOME/.yq2* If you're a package maintainer, please look at our packaging guide at the [Packaging Guide](05_packaging.md). ## Compiling from source To compile Yamagi Quake II from source the following dependencies (including development headers) are needed: * *MinGW-w64* or *Visual Studio (2015 or newer)* for Windows or a GCC compatible compiler like *gcc* or *clang* for other platforms. * A LibGL implementation with system headers. * An OpenAL implementation, *openal-soft* is highly recommended. * libcurl. * SDL 2.0. ### Prerequisites on Windows when using MinGW To compile Yamagi Quake II under Windows, using a MinGW environment is recommended. A preconfigured environment based upon MSYS2 with all necessary dependencies and compatibles compilers can be found at: https://deponie.yamagi.org/quake2/windows/buildenv/ The environment can be extracted anywhere. Either the 32 bit version can be started through *C:\MSYS2\msys32.exe* or the 64 bit version through *C:\MSYS2\msys64.exe*. ### Prerequisites on Unixoid Platforms The build dependencies can be installed with: * On Arch Linux based distributions: `pacman -S base-devel mesa openal curl sdl2` * On Debian based distributions: `apt install build-essential libgl1-mesa-dev libsdl2-dev libopenal-dev libcurl4-openssl-dev` * On FreeBSD: `pkg install gmake libGL sdl2 openal-soft curl` * On NetBSD: `pkgin install gmake SDL2 openal-soft curl` * On OpenBSD: `pkg_add gmake sdl2 openal curl` * On Solaris/Illumos: `pkg install sdl2 openal curl` * On Haiku: `pkgman libsdl2_devel openal_devel curl_devel` * On MacOS the dependencies can be installed with Homebrew (from https://brew.sh): `brew install sdl2 openal-soft` Other distributions or platforms often have package named similar to the Debian or FreeBSD packages. ### Compiling with GCC, Clang or MinGW Download the latest release from https://www.yamagi.org/quake2 or clone the source from https://github.com/yquake2/yquake2.git, change into the *yquake2/* source directory and type *make* (Linux, MacOS, Haiku and Windows) or *gmake* (FreeBSD, NetBSD, OpenBSD). Note on Solaris systems, *make* or *gmake* can be used, the latter provides in addition parallel build. After the build finished, copy everything from the *release/* directory to the Yamagi Quake II installation directory. For the addons download or clone their source, change into the source directory and type *make* (Linux, MacOS and Windows) or *gmake* (FreeBSD, NetBSD, OpenBSD). After the compilation finishes the *release/game.so* is copied to the corresponding directory in the Quake II installation. ### Compiling on Windows with Visual Studio (2015 and newer) To compile with Visual Studio, CMake is required. We only support VS2015 and newer, though VS2019 version 16.8 or newer is recommended. Furthermore you'll need **SDL2**, **openal-soft** and **libcurl** to link against. The easiest way to get those dependencies is using the [dhewm3-libs](https://github.com/dhewm/dhewm3-libs/) together with the `YQUAKE2LIBS` CMake variable. It might also be possible to manually install the dependencies or to use [vcpkg](https://vcpkg.io) or similar to install them, but that's untested. Create a build directory outside the yquake2 directory, open a terminal, change to that directory and use CMake to generate a Visual Studio solution. For Win32 (32bit x86) and VS2019 the commandline should look like: `cmake -G "Visual Studio 16 2019" -A Win32 -DYQUAKE2LIBS="C:/dev/dhewm3-libs/i686-w64-mingw32" path/to/yquake2` Of course you need to adjust `C:/dev/dhewm3-libs/` to the directory you put the *dhewm3-libs* in, and `path/to/yquake2` to your Yamagi Quake II source checkout (the directory the `CMakeLists.txt` is in). For x64/Win64 (64bit x86) it should look like: `cmake -G "Visual Studio 16 2019" -A x64 -DYQUAKE2LIBS="C:/dev/dhewm3-libs/x86_64-w64-mingw32" path/to/yquake2` After successfully running this command, there should be a `yquake2.sln` in your build directory, you can open it with Visual Studio to compile. If you prefer using `cmake-gui`, you can specify the `YQUAKE2LIBS` with the `Add Entry` option (Name: `YQUAKE2LIBS`, Type: `PATH`, Value: *see examples above*). yquake2-QUAKE2_8_40/doc/030_configuration.md000066400000000000000000000217361465112212000204420ustar00rootroot00000000000000# Configuration Guide Yamagi Quake II provides a lot of configuration options. This guides shows how to configure Yamagi Quake II to match you needs. This guide is for advanced users, if you just want to play you're likely happy with the defaults and the options that can be set through the menu. ## Choosing a Renderer Yamagi Quake II ships with 4 renderers: * The **OpenGL 3.2** renderer: This renderer was developed for the needs of modern graphics hardware and is usually the best choice for OpenGL 3.2 capable graphics cards. It provides a very detailed look and feel, matching the dark and dirty atmosphere on Stroggos. The texture rendering looks mostly the same on all GPU drivers. Depending on the display, the default lighting may be too bright or too dark, it can be adjusted through the menu or through the *vid_gamma* cvar. * The **OpenGL ES3** renderer: This is pretty much the same as the OpenGL 3.2 renderer (and uses the same cvars for configuration), but uses OpenGL ES 3.0 instead of "desktop" OpenGL, so it also works on the Raspberry Pi 4, for example. Reportedly it also has slightly better performance on Wayland, at least with the open source AMD drivers. * The **OpenGL 1.4** renderer: This is a slightly enhanced version of the original OpenGL renderer shipped in 1997 with the retail release. It's provided for older graphics cards, not able to run the OpenGL 3.2 renderer. The OpenGL 1.4 renderer has some limitations. The look and feel is highly dependent on the GPU driver and the platforms OpenGL implementation, especially the texture rendering may vary to a wide margin. The global lighting may not be rendered correctly, especially liquids may be too dark or too bright. * The **Software** renderer: Shipped for historical reasons only. Setting the OpenGL 3.2 renderer to match the software renderers look and feel is often the better choice, since it's faster and provides colored lighting. The software renderer may show some rendering errors on widescreen resolutions. ## Choosing a Sound System Yamagi Quake II ships with 2 sound system: * The **OpenAL** sound system: This is the default and highly recommended. It provides full surround sound support and even HRTF for headphones. But also the plain stereo playback is much better than in the original sound system. The setup is done mostly through OpenAL, have a look at the documentation of your OpenAL library. * The **SDL** sound system: This is the classic sound system, providing an experience like the original client. Set `s_openal` to `0` and execute an `snd_restart` to activate it. The classic sound system may be somewhat problematic on modern systems like Windows 10 or Linux with Pulseaudio. ## Tuning for Precise Timings Yamagi Quake II comes with a highly evolved asynchronous client. While the default settings are usually good, some players may want to tune for more precise timing or better vertical synchronization accuracy. Quake II was never meant to run on todays hardware. Modern hardware is hundred times faster than the hardware of 1997. Faster hardware brings higher framerates, which make inaccuracies scattered all over the code visible and a problem. We're unable to fix all those inaccuracies, because the game data, the network protocol and the whole look and feel depends on them. We can just try work around them. If your computer is **fast enough** to reach at least 60fps in Yamagi Quake II (this should be the case for most machines these days, including Laptops with integrated GPUs; a notable exception are Raspberry PIs), the following settings should give you the best results: * Make sure that `busywait` is set to `1`. That's the default. Setting it to `0` saves some CPU time (and thus increase battery life and reduce heat), but can mess up the timings, especially on Windows, which can lead to tearing- and / or micro stuttering. * Set `cl_async` to `1` (the default) to avoid glitches especially in physics/movement when rendering at high framerates (>90fps). * `cl_maxfps` should usually be set to `-1` (the **new** default) so the engine can choose a packet framerate that should be ideal. * If your display's refreshrate isn't detected correctly, set `vid_displayrefreshrate`. * If your display has a vertical refreshrate of 60Hz, just enable vsync (`r_vsync 1`) and make sure `vid_maxfps` is at least `60`, which by default is the case. * If you have a display that runs at more than 60Hz, enabling vsync should still make the game run well, but you get best results by running it at a render framerate that's a multiple of 60fps, for example by setting `vid_maxfps` to `120` (for 144Hz displays). * If you have vsync disabled, you should set `vid_maxfps` to a framerate that your computer can reach. For **slower machines** (that can't consistently reach 60fps) it makes sense to disable the asynchronous client all together by setting `cl_async` to `0` and limiting the framerate to a value <= 90, to avoid the aforementioned glitches in parts of the game where your machine reaches more than 90fps after all. This is done by setting `vid_maxfps` (to `80`, for example). `cl_maxfps` is ignored with `cl_async 0`, and every render frame is also a client/server frame. The [cl_async entry in the cvar documentation](040_cvarlist.md#general) has some more information on the asynchronous client. ## Getting a classic look and feel Yamagi Quake II has some features to provide a better experience on modern hardware. For example widescreen support, HUD scaling or FOV alterations. Not all users may like these changes. ### HUD scaling All levels of scaling can be switched off in the *Video* menu. It's also possible to switch only parts of the scaling off, for example the menu and the console can be scaled, but the HUD not. The cvars are: * `r_consolescale` for the console. * `r_hudscale` for the HUD. * `r_menuscale` for the menu. * `crosshair_scale` for the crosshair. Please note that's not always clear which GUI elements are part of what subsystem. The loading plaque is part of the menu and not of the HUD, for example. ### Field of View Yamagi Quake II has a different FOV calculation then the original client. Yamagi Quake II determines the optimal FOV (horizontal and vertical) with the *Horplus* algorithm and the gun is rendered always with a static FOV of 80, the original client only had a vertical FOV applied to everything. * The FOV itself can be altered through the *Video* menu or the `fov` cvar. That gives a smaller or wider FOV, but not the classic Quake II look because the Horplus algorithm is still active. * The Horplus algorithm can be disabled by setting `horplus` to `0`. * The gun can be rendered with the global FOV by setting `r_gunfov` to the same value as `fov`. ### 4/3 Cinematics While the original Quake II client stretched cinematics over the whole window, Yamagi Quake II always renders them with 4/3 aspect. The old behavior can be enforced by setting `cin_force43` to `0`. ### Sound system As already said above, Yamagi Quake II has two sound systems. The old one and OpenAL. Additionally some new sound effects were added. We recommend to stay with OpenAL, even if the stereo rendering is somewhat different to the old sound system. OpenAL is much more distortion free and more reliable, especially on modern platforms like Windows 10 or Linux with PulseAudio / Pipewire. The new sound effects can be disabled with: * `s_doppler` set to `0` disables the doppler effect. * `s_underwater` set to `0` disables the underwater effect. ### Renderer While Yamagi Quake II still supports the Software renderer, configuring one of the OpenGL renderers to give a classic look and feel is often the better choice. The OpenGL renderers are much faster, more reliable and support colored lighting. General cvars: * `cl_lights`: Set to `0` to disable the dynamic lighting. Both OpenGL renderers: * `gl_texturemode`: Set to `GL_NEAREST` to disable the texture filtering, giving a classic pixel look. Additionally disabling anisostropic filtering makes it look even more authentic. The OpenGL 1.4 renderer: * `gl1_pointparameters`: When set to `0` the particles are rendered as blurry octagon. May be already the case if the GPU driver doesn't support point parameters. The OpenGL 3.2 renderer: * `gl3_particle_square`: When set to `1` the particles are rendered as squares. * `gl3_colorlight`: When set to `0`, the lights and lightmaps are colorless (greyscale-only), like in the original soft renderer. ## Retexturing Packs Yamagi Quake II has full support for retexturing packs. They just need to be installed and should be picked up automatically. To disable the retexturing pack at a later time set `r_retexturing` to `0`. * The most comprehensive build retexturing pack can be found here: https://deponie.yamagi.org/quake2/assets/texturepack/ * And there's an AI upscale of the original textures: https://github.com/Calinou/quake2-neural-upscale/releases Retexturing packs can be installed by placing the pak or zip files in the *baseq2* directory. yquake2-QUAKE2_8_40/doc/040_cvarlist.md000066400000000000000000000747361465112212000174330ustar00rootroot00000000000000# Yamagi 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 due to architectural changes some of them have been renamed. The prefixes are: * No prefix: General stuff. * `cl_`: Client. * `gl_`: Common to all OpenGL renderers. * `gl1_`: OpenGL 1.4 renderer. * `gl3_`: OpenGL 3.2 and OpenGL ES3 renderers. * `ogg_`: Ogg/Vorbis music playback. * `r_`: Common to all renderers. * `s_`: Sound system. * `sw_`: Software renderer. * `vid_`: Video backend. All cvars may be given at command line through `+set cvar value` or typed into the console. The console can be opended with *Left Shift + Esc*. Keep in mind that some cvars need quotation marks around the arguments. When giving such cvars at the command line the argument string must be surrounded by ticks. For example `+set sv_maplist '"q2dm1 q2dm2"'`. ## Command line arguments These are not console variables, they cannot be entered into the console, only be given through the command line at startup. While cvars are prefixed with a `+`, arguments are starting with a `-`. For example it's `+set busywait 0` (setting the `busywait` cvar) and `-portable` (setting the `portable` argument). * **cfgdir**: The name (not the path) of the configuration directory. * **datadir**: 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. * **portable**: Makes Quake II portable, all runtime data like the the config, savegames and so on is stored next to the executable and not in the users home directory. ## General * **aimfix**: Fix aiming. When set to to `0` (the default) aiming is slightly inaccurate, bullets and the like have a little drift. When set to `1` they hit exactly were the crosshair is. * **busywait**: By default this is set to `1`, causing Quake II to spin in a very tight loop until it's time to process the next frame. This is a very accurate way to determine the internal timing, but comes with a relatively high CPU usage. If set to `0` Quake II lays itself to sleep and tells the operating system to send a wakeup signal when it's time for the next frame. The latter is more CPU friendly but can be rather inaccurate, especially on Windows. Use with care. * **cl_maxfps**: The approximate framerate for client/server ("packet") frames if *cl_async* is `1`. If set to `-1` (the default), the engine will choose a packet framerate appropriate for the render framerate. See `cl_async` for more information. * **cl_async**: Run render frames independently of client/server frames. If set to `0`, client, server (gamecode) and the renderer run synchronous, (like Quake2 originally did) which means that for every rendered frame a client- and server-frame is executed, which includes the gamecode and physics/movement-simulation etc. At higher framerates (above 95 or so) this leads to movement bugs, like being able to jump higher than expected (kind of like the infamous Quake 3 125Hz bug). For `cl_async 0`, *vid_maxfps* (or, if vsync is enabled, the display refresh rate) is used and *cl_maxfps* is ignored. If *cl_async* is set to `1` (the default) the client is asynchronous, which means that there can be multiple render frames between client- and server-frames. This makes it possible to renderer as many frames as desired without physics and movement problems. The client framerate is controlled by *cl_maxfps*, the renderer framerate is controlled by *vid_maxfps*. As client/server frames ("packet frames") are only run together with a render frame, the *real* client/server framerate is always rounded to a fraction of the renderframerate that's closest to *cl_maxfps*. So if for example *vid_maxfps* is `60` and *cl_maxfps* is `50`, it will be rounded to `60` and every renderframe is also a packet frame. If *vid_maxfps* is `60` and *cl_maxfps* is `40`, it will be rounded to `30` and every second render frame is also a packet frame. It seems like the best working packet framerate is `60` (which means that the render framerate should be a multiple of that), otherwise values between `45` and `90` seem to work ok, lower and higher values can lead to buggy movement, jittering and other issues. Setting *cl_maxfps* to `-1` (the default since 8.02) will automatically choose a packet framerate that's *both* a fraction of *vid_maxfps* (or display refreshrate if vsync is on) *and* between 45 and 90. * **cl_http_downloads**: Allow HTTP download. Set to `1` by default, set to `0` to disable. * **cl_http_filelists**: Allow downloading and processing of filelists. A filelist can contain an arbitrary number of files which are downloaded as soon asthe filelist is found on the server. Set to `1` by default, set to `0` to disable. * **cl_http_max_connections**: Maximum number of parallel downloads. Set to `4` by default. A higher number may help with slow servers. * **cl_http_proxy**: Proxy to use, empty by default. * **cl_http_show_dw_progress**: Show a HTTP download progress bar. * **cl_http_bw_limit_rate**: Average speed transfer threshold for `cl_http_bw_limit_tmout` variable. Set `0` by default. * **cl_http_bw_limit_tmout**: Seconds before the download is aborted when the speed transfer is below the var set by `cl_http_bw_limit_rate`. Set `0` by default. * **cl_kickangles**: If set to `0` angle kicks (weapon recoil, damage hits and the like) are ignored. Cheat-protected. Defaults to `1`. * **cl_laseralpha**: Controls how see-through laserbeams are. The value ranges from 0.0 to 1.0, from completely invisible to completely opaque. So higher value means better visibility. Defaults to `0.3`. * **cl_limitsparksounds**: If set to `1` the number of sound generated when shooting into power screen and power shields is limited to 16. This works around global volume drops in some OpenAL implementations if too many sounds are played at the same time. This was the default behavior between Yamagi Quake II 7.10 and 7.45. Defaults to `0`. * **cl_loadpaused**: If set to `1` (the default) the client is put into pause mode during single player savegame load. This prevents monsters and the environment from hurting the player while the client is still connecting. If set to `2` the client stays in pause mode after loading. If set to `0` pause mode is never entered, this is the Vanilla Quake II behaviour. * **cl_model_preview_start**: Start frame value in multiplayer model preview. `-1` - don't show animation. Defaults to `84` for show salute animation. * **cl_model_preview_end**: End frame value in multiplayer model preview. `-1` - don't show animation. Defaults to `94` for show salute animation. * **cl_nodownload_list**: Whitespace separated list of substrings, files having one these strings in their name are never downloaded. Empty by default. Note that some substrings are always forbidden, for security reasons these cannot be overridden: '.dll', '.dylib' and '.so' to prevent downloading of libraries which could be injected into the Yamagi Quake II process. '..' or ':' inside filenames and '/' or '.' at the beginning of filenames to prevent downloading files into arbitrary directories. * **cl_r1q2_lightstyle**: Since the first release Yamagi Quake II used the R1Q2 colors for the dynamic lights of rockets. Set to `0` to get the Vanilla Quake II colors. Defaults to `1`. * **cl_showfps**: Shows the framecounter. Set to `2` for more and to `3` for even more informations. * **cl_showspeed**: Shows the players speed. Set to `1` to display both overall speed and (horizontal speed) in Quake Units (QU) respectfully at the top right corner of the screen. Set to `2` to show only the horizontal speed under the crosshair. * **cl_unpaused_scvis**: If set to `1` (the default) the client unpause when the screen becomes visible. * **in_grab**: Defines how the mouse is grabbed by Yamagi 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, videos, console or if game is paused). * **singleplayer**: Only available in the dedicated server. Vanilla Quake II enforced that either `coop` or `deathmatch` is set to `1` when running the dedicated server. That made it impossible to play single player campaigns over the dedicated server. When set to `1`, both `coop` and `deathmatch` are forced to `0` and `maxclients` is forced to `1`. This can be used to run a dedicated server with an old single player mod, where the source code isn't available, inside a Windows 98 or XP VM and connect over network from an non Windows system. * **coop_pickup_weapons**: In coop a weapon can be picked up only once. For example, if the player already has the shotgun they cannot pickup a second shotgun found at a later time, thus not getting the ammo that comes with it. This breaks the balancing. If set to `1` a weapon can be picked up if a) the player doesn't have it or b) it wasn't already picked up by another player. Defaults to `1`. * **coop_elevator_delay**: In coop it's often hard to get on the same elevator together, because they're immediately triggered once the first player steps on it. This cvar sets a delay for the elevator to wait before moving, so other players have some time to get on it. Defaults to `1.0` (seconds). * **coop_baseq2 (Ground Zero only)**: In Ground Zero, entity spawnflags (which difficulty modes / game modes level entities spawn in) are interpreted a bit differently. In original Quake 2, if an entity is set to not spawn on any difficulty, it is treated as deathmatch-only, however, in Ground Zero this same condition is treated as coop-only. This causes maps made for original Quake 2, including the entire Quake 2 campaign, to not work correctly when played in Ground Zero in co-op mode. This cvar, when set to 1, restores the original interpretation and enables you to play original Quake 2 maps in Ground Zero co-op. Though keep in mind that Ground Zero maps will not work correctly when this cvar is enabled so remember to disable it again before playing Ground Zero maps in co-op. By default this cvar is disabled (set to 0). * **g_commanderbody_nogod**: If set to `1` the tank commanders body entity can be destroyed. If the to `0` (the default) it is indestructible. * **g_footsteps**: If set to `1` (the default) footstep sounds are generated when the player is on ground and faster than 255. This is the behaviour of Vanilla Quake II. If set to `2` footestep sound always generated as long as the player is on ground. If set to `3` footsteps are always generated. If set to `0` footstep sounds are never generated. Cheat protected to `1`. Note that there isn't a reliable way to figure out if the player is on ground. Footsteps may not be generated in all circumstances, especially when the player is moving over stairs and slopes. * **g_monsterfootsteps**: If set to `1` monster footstep are generated. By default this cvar is disabled (set to 0). Additional footstep sounds are required. See the installation guide for details. * **g_fix_triggered**: This cvar, when set to `1`, forces monsters to spawn in normally if they are set to a triggered spawn but do not have a targetname. There are a few cases of this in Ground Zero and The Reckoning. This cvar is disabled by default to maintain the original gameplay experience. * **g_machinegun_norecoil**: Disable machine gun recoil in single player. By default this is set to `0`, this keeps the original machine gun recoil in single player. When set to `1` the recoil is disabled in single player, the same way as in multiplayer. This cvar only works if the game.dll implements this behaviour. * **g_quick_weap**: If set to `1`, both *weapprev* and *weapnext* commands will "count" how many times they have been called, making possible to skip weapons by quickly tapping one of these keys. By default this cvar is set to `1`, and will only work if the game.dll implements this behaviour. * **g_swap_speed**: Sets the speed of the "changing weapon" animation. Default is `1`. If set to `2`, it will be double the speed, `3` is the triple... up until the max of `8`, since there are at least 2 frames of animation that will be played compulsorily, on every weapon. Cheat-protected, has to be a positive integer. As with the last one, will only work if the game.dll implements this behaviour. * **g_disruptor (Ground Zero only)**: This boolean cvar controls the availability of the Disruptor weapon to players. The Disruptor is a weapon that was cut from Ground Zero during development but all of its code and assets were still present in the source code and the released game. This is basically a player-held version of the 2nd Widow boss' tracker weapon - a black-ish ball of energy. When this cvar is set to 1 you can use the "give Disruptor" and "give rounds X" commands to give yourself the weapon and its ammo, and its items, weapon\_disintegrator and ammo\_disruptor, can be spawned in maps (in fact, some official Ground Zero maps contain these entities). This cvar is set to 0 by default. * **nextdemo**: Defines the next command to run after maps from the `nextserver` list. By default this is set to the empty string. * **nextserver**: Used for looping the introduction demos. ## Audio * **al_device**: OpenAL device to use. In most cases there's no need to change this, since the default device is normally the correct choice. * **al_driver**: OpenAL library to use. This is useful 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 disables 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` 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 for headphones. OpenAL is much more reliable than the classic sound system, especially on modern systems like Windows 10 or Linux with PulseAudio. * **s_underwater**: Dampen sounds if submerged. Enabled by default. * **s_occlusion_strength**: If set bigger than `0` sound occlusion effects are enabled. This is only supported by the OpenAL sound backend. By default this cvar is disabled (set to 0). * **s_reverb_preset**: Enable reverb effect. By default this cvar is disabled (set to `-1`). Possible values: `-2`: Auto reverb effect select, `-1`: Disable reverb effect, `>=0`: select predefined effect. * **cl_audiopaused**: If set to `1` the sounds pause when the game does. ## Graphics (all renderers) * **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. * **r_gunfov**: The weapons are rendered with a custom field of view, independently of the global **fov**, so they are not distorted at high FOVs. A value of `75` should look identical to the old code at `fov 90`, it defaults to `80` because that looks a bit better. Set to `-1` for the same value as `fov`. * **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. * **r_consolescale** / **r_hudscale** / **r_menuscale** and **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` (default) sets the optimal scaling factor for the current resolution. All cvars are set through the scaling slider in the video menu. * **r_customheight** / **r_customwidth**: Specifies a custom resolution, the windows will be *r_customheight* pixels high and *r_customwidth* pixels wide. Set *r_mode* to `-1` to use the custom resolution. * **r_farsee**: Normally Quake II renders only up to 4096 units. If set to `1` the limit is increased to 8192 units. This helps with some custom maps and is problematic with other custom maps. * **r_fixsurfsky**: Some maps misuse sky surfaces for interior lighting. The original renderer had a bug that made such surfaces mess up the lighting of entities near them. If set to `0` (the default) the bug is there and maps look like their developers intended. If set to `1` the bug is fixed and the lighting correct. * **r_vsync**: Enables the vsync: frames are synchronized with display refresh rate, should (but doesn't always) prevent tearing. Set to `1` for normal vsync and `2` for adaptive vsync. * **r_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. * **r_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 the framerate is too low. * **r_videos_unfiltered**: If set to `1`, don't use bilinear texture filtering on videos (defaults to `0`). * **r_2D_unfiltered**: If set to `1`, don't filter textures of 2D elements like menus and the HUD (defaults to `0`). * **r_lerp_list**: List separated by spaces of 2D textures that *should* be filtered bilinearly, even if `r_2D_unfiltered` is set to `1`. * **r_nolerp_list**: List separated by spaces of textures omitted from bilinear filtering (mostly relevant if `r_2D_unfiltered` is `0`). Used by default to exclude the console and HUD font and crosshairs. Make sure to include the default values when extending the list. * **r_retexturing**: If set to `1` (the default) and a retexturing pack is installed, the high resolution textures are used. * **r_scale8bittextures**: If set to `1`, scale up all 8bit textures. * **r_shadows**: Enables rendering of shadows. Quake IIs shadows are very simple and are prone to render errors. * **vid_displayrefreshrate**: Sets the displays refresh rate. The default `-1` let the game determine the refresh rate automatically. Often the default setting is okay, but some graphics drivers report wrong refresh rates. For example 59hz are reported while the display has 59.95hz. * **vid_gamma**: The value used for gamma correction. Higher values look brighter. The OpenGL 3.2 OpenGL ES3 and Vulkan renderers apply this to the window in realtime via shaders (on all platforms). When the game is build against SDL2, the OpenGL 1.4 renderer uses "hardware gamma" when available, increasing the brightness of the whole screen. On MacOS the gamma is applied only at renderer start, so a `vid_restart` is required. When the game is build against SDL3, the OpenGL 1.4 renderer doesn't support gamma. Have a look at `gl1_overbrightbits` instead. This is also set by the brightness slider in the video menu. * **vid_fullscreen**: Sets the fullscreen mode. When set to `0` (the default) the game runs in window mode. When set to `1` the games switches the display to the requested resolution. That resolution must be supported by the display, otherwise the game tries several steps to recover. When set to `2` a fullscreen window is created. It's recommended to use the displays native resolution with the fullscreen window, use `r_mode -2` to switch to it. * **vid_highdpiaware**: When set to `1` the client is high DPI aware and scales the window (and thus the requested resolution) by the scaling factor of the underlying display. Example: The displays scaling factor is 1.25 and the user requests 1920x1080. The client will render at 1920\*1.25x1080\*1.25=2400x1350. When set to `0` the client leaves the decision if the window should be scaled to the underlying compositor. Scaling applied by the compositor may introduce blur and sluggishness. Currently high dpi awareness is only supported under Wayland. Defaults to `0` when build against SDL2 and to `1` when build against SDL3. * **vid_maxfps**: The maximum framerate. *Note* that vsync (`r_vsync`) also restricts the framerate to the monitor refresh rate, so if vsync is enabled, the game won't render more than frame than the display can show. Defaults to `300`. Related to this: *cl_maxfps* and *cl_async*. * **vid_pauseonfocuslost**: When set to `1` the game is paused as soon as it's window looses focus. It will work only in situation were the game can be paused, e.g. not in multiplayer games. Defaults to `0`. * **vid_renderer**: Selects the renderer library. Possible options are `gl3` (the default) for the OpenGL 3.2 renderer, `gles3` for the OpenGL ES3 renderer, gl1 for the original OpenGL 1.4 renderer and `soft` for the software renderer. ## Graphics (GL renderers only) * **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 make things better or worse, depending on the map. * **gl_texturemode**: How textures are filtered. - `GL_NEAREST`: No filtering (using value of *nearest* source pixel), mipmaps not used - `GL_LINEAR`: Bilinear filtering, mipmaps not used - `GL_LINEAR_MIPMAP_NEAREST`: The default - Bilinear filtering when scaling up, using mipmaps with nearest/no filtering when scaling down Other supported values: `GL_NEAREST_MIPMAP_NEAREST`, `GL_NEAREST_MIPMAP_LINEAR`, `GL_LINEAR_MIPMAP_LINEAR` ## Graphics (OpenGL 1.4 only) * **gl1_intensity**: Sets the color intensity. 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`. * **gl1_multitexture**: Enables (`1`, default) the blending of color and light textures on a single drawing pass; disabling this (`0`) does one pass for color and another for light. Requires a `vid_restart` when changed. * **gl1_overbrightbits**: Enables overbright bits, brightness scaling of lightmaps and models. Higher values make shadows less dark. Possible values are `0` (no overbright bits), `1` (more correct lighting for liquids), `2` (scale lighting by factor 2), and `4` (scale by factor 4). Applied in realtime, does not need `vid_restart`. * **gl1_particle_square**: If set to `1` particles are rendered as squares. * **gl1_stencilshadow**: If `gl_shadows` is set to `1`, this makes them look a bit better (no flickering) by using the stencil buffer. ## Graphics (OpenGL 3.2 and OpenGL ES3 only) * **gl3_debugcontext**: Enables the OpenGL 3.2 renderers debug context, e.g. prints warnings and errors emitted by the GPU driver. Not supported on macOS. This is a pure debug cvar and slows down rendering. * **gl3_intensity**: Sets the color intensity used for 3D rendering. Similar to OpenGL 1.4 `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 OpenGL 1.4 `gl1_overbrightbits`, but allows any floating point number. Default is `1.3`. In the OpenGL 3.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 OpenGL 1.4 particles. * **gl3_particle_square**: If set to `1`, particles are rendered as squares, like in the old software renderer or Quake 1. Default is `0`. * **gl3_colorlight**: When set to `0`, the lights and lightmaps are colorless (greyscale-only), like in the original soft renderer. Default is `1`. * **gl3_usefbo**: When set to `1` (the default), an OpenGL Framebuffer Object is used to implement a warping underwater-effect (like the software renderer has). Set to `0` to disable this, in case you don't like the effect or it's too slow on your machine. ## Graphics (Software only) * **sw_gunzposition**: Z offset for the gun. In the original code this was always `0`, which will draw the gun too near to the player if a custom gun field of view is used. Defaults to `8`, which is more or less optimal for the default gun field of view of 80. * **sw_colorlight**: enable experimental color lighting. ## Game Controller * **in_initjoy**: Toggles initialization of game controller. Default is `1`, which enables gamepad usage; `0` disables its detection at startup. Can only be set from command line. * **in_sdlbackbutton**: Defines which button is used in the gamepad or joystick as the `Esc` key, to access the main menu and 'cancel' / 'go back' on its options. Default is `0`, which corresponds to the Back/Select/Minus button. Set to `1` to use Start/Menu/Plus, and to `2` to use the Guide/Home/PS button. Requires a game restart (or controller replug) when changed. * **joy_layout**: Allows to select the stick layout of the gamepad. - `0`: *Default*, left stick moves, right aims - `1`: *Southpaw*, same as previous one with inverted sticks - `2`: *Legacy*, left moves forward/backward and turns, right strafes and looks up/down - `3`: *Legacy Southpaw*, inverted sticks version of previous one - `4`: *Flick Stick*, left stick moves, right checks your surroundings in 360º, gyro required for looking up/down - `5`: *Flick Stick Southpaw*, swapped sticks version of last one * **joy_left_deadzone** / **joy_right_deadzone**: Inner, circular deadzone for each stick, where inputs below this radius will be ignored. Default is `0.16` (16% of possible stick travel). * **joy_left_snapaxis** / **joy_right_snapaxis**: Ratio on the value of one axis (X or Y) to snap you to the other. It creates an axial deadzone with the shape of a "bowtie", which will help you to do perfectly horizontal or vertical movements the more you mark a direction with the stick. Increasing this too much will reduce speed for the diagonals, but will help you to mark 90º/180º turns with Flick Stick. Default `0.15`. * **joy_left_expo** / **joy_right_expo**: Exponents on the response curve on each stick. Increasing this will make small movements to represent much smaller inputs, which helps precision with the sticks. `1.0` is linear. Default `2.0` (quadratic curve). * **joy_flick_threshold**: Used only with Flick Stick, specifies the distance from the center of the stick that will make the player flick or rotate. Default `0.65` (65%). * **joy_flick_smoothed**: Flick Stick only, rotations below this angle (in degrees) will be smoothed. Reducing this will increase responsiveness at the cost of jittery movement. Most gamepads will work nicely with a value between 4.0 and 8.0. Default `8.0`. * **gyro_mode**: Operation mode for the gyroscope sensor of the game controller. Options are `0` = always off, `1` = off with the `+gyroaction` bind to enable, `2` = on with `+gyroaction` to disable (default), `3` = always on. * **gyro_turning_axis**: Sets which gyro axis will be used for turning. The default `0` is "yaw" (turn), for people who prefer to hold their controller flat, like using a pointing device. `1` is "roll" (lean), for people who hold the controller upright, or use a device with the controller attached to the screen, e.g. Steam Deck. * **gyro_tightening**: Threshold of rotation in degrees per second, where gyro inputs below it will be dampened. Meant to counter a noisy gyro and involuntary hand movements. Default `3.5`. * **gyro_calibration_(x/y/z)**: Offset values on each axis of the gyro which helps it reach true "zero movement", complete stillness. These values are wrong if you see your in-game view "drift" when leaving the controller alone. As these vary by device, it's better to use 'calibrate' in the 'gamepad' -> 'gyro' menu to set them. * **joy_haptic_magnitude**: Haptic magnitude value, By default this cvar is `0.0` or disabled. Valid values are positive, e.g. 0..2.0. * **joy_haptic_filter**: List of sound file names produced haptic feedback separated by space. `*` could be used for replace part of file name as regular expression. `!` at the beginning of value could be used for skip file name equal to value. * **joy_haptic_distance**: Haptic maximum effect distance value, By default this cvar is `100.0`. Any positive value is valid. E.g. effect of shoot to a barrel has 58 points when player stay near the barrel. * **s_feedback_kind**: Select kind of controller feedback to use. By default this cvar is `0`. Possible values: `0`: Rumble feedback, `1`: Haptic feedback. ## cvar operations cvar operations are special commands that allow the programmatic manipulation of cvar values. They can be used for scripting and the like. * **dec [val]**: Decrements the given cvar by `1` or the optional value `val`. * **inc [val]**: Increments the given cvar by `1` or the optional value `val`. * **reset **: Reset the given cvar to it's default value. * **resetall**: Reset all known cvar to their default values. * **toggle [val0] [val1]**: Toggle the given cvar between `0` and `1`. If the optional arguments `val0` and `val1` are given the given cvar is toggled between them. yquake2-QUAKE2_8_40/doc/050_commands.md000066400000000000000000000036311465112212000173700ustar00rootroot00000000000000# Yamagi Quake II Commands This list explains all commands added by Yamagi Quake II. All of the original clients (Vanilla Quake II) commands are still in place. * **cycleweap **: Cycles through the given weapons. Can be used to bind several weapons on one key. The list is provided as a list of weapon classnames separated by whitespaces. A weapon in the list is skipped if it is not a valid weapon classname, you do not own it in your inventory or you do not have enough ammo to use it. By quickly tapping the bound key, you can navigate the list faster. * **prefweap **: Similar to the previous command, this will select the first weapon available in the priority list given. Useful to set a "panic button". E.g. the following will select your best shotgun: `prefweap weapon_supershotgun weapon_shotgun`. * **gamemode **: Provides a convenient way to switch the game mode between `coop`, `dm` and `sp` without having to set three cvars the correct way. `?` prints the current mode. * **listentities **: Lists the coordinates of all entities of a given class. Possible classes are `ammo`, `items`, `keys`, `monsters` and `weapons`. Multiple classes can be given, they're separated by whitespaces. The special class `all` lists the coordinates of all entities. * **viewpos**: Show player position. * **teleport **: Teleports the player to the given coordinates. * **spawnentity classname x y z **: Spawn new entity of `classname` at `x y z` coordinates. * **spawnonstart classname**: Spawn new entity of `classname` at start point. * **listmaps**: Lists available maps for the player to load. Maps from loaded pak files will be listed first followed by maps placed in the current game's maps folder. * **vstr**: Inserts the current value of a variable as command text. * **playermodels**: Lists available multiplayer models. yquake2-QUAKE2_8_40/doc/060_multiplayer.md000066400000000000000000000100411465112212000201300ustar00rootroot00000000000000# Multiplayer Servers In general running a Yamagi Quake II server is the same as running a Vanilla Quake2 server, so the old guides should still apply. One thing to keep in mind is that the server must be restarted at least every 49 days, because the Quake II network protocol represents the interal time as a 32 bit integer and after 49 days that integer overflows, leading to all kinds of trouble. This problem has always existed in Quake II and is not fixable (at least not without breaking compatibility with the existing network protocol), but back in Win9x days this was less of a problem because Windows crashed frequently anyways and Win9x had the same bug and crashed after 49 days or so... Apart from this, we'll only document changes/additions here. ## Security considerations Quake II was released in 1997 into a world where security didn't matter. The most important thing when connecting to random servers is: **Quake II allows the server to do anything on the client!** The server may execute any console command, it may overwrite any cvar, download any file to your computer and given the rather fragile state of the command parsers chances are high that there are remote code execution vulnerabilities. Only connect to trustworthy servers! ## HTTP Downloads Like r1q2 and some other Quake II source ports, we allow downloading game data for multiplayer via HTTP. This is a lot faster than the Quake II internal protocol that was used in the original client and Yamagi Quake II up to version 7.30. As a **client** you don't have to do anything, just use a Yamagi Quake II version newer than 7.30 and if you build it yourself don't disable cURL support. For **servers** the following must be done: ### Put the game data on a http server The directory structure on the server must be the same as in the game, so if you want to provide `maps/foo.bsp` and your server base path is `http://example.com/q2data/`, then you must put that map into `http://example.com/q2data/maps/foo.bsp`. You can either just put the raw .bsp (and files the bsp needs, textures, modes and so on) on your HTTP server, or you can upload a whole `.pak` or `.pk3` that contains the needed data. If you're using a `.pak` or `.pk3` you need a **file list** that's hosted on your http server. #### Map specific file lists One way is to have one file list for each map that's running on your server. If your server is rotating between `maps/foo.bsp` and `maps/bar.bsp`, you'd have `http://example.com/q2data/maps/foo.filelist` and `http://example.com/q2data/maps/bar.filelist`. A file list is a plain text file that lists one file path per line that's to be downloaded. Those paths are relative to the server base path and **must not** begin with a slash! So if `maps/bar.bsp` needs `bar.pak` and `textures.pak`, your `http://example.com/q2data/maps/bar.filelist` would look like: ``` bar.pak textures.pak ``` #### Global File List Instead of map-specific file lists, you could have one global file list. All those files are downloaded when someone connects to your server, regardless of the currently running map. This global file list must be at your server base path and must be called `.filelist`. So in our example `http://example.com/q2data/.filelist` could look like: ``` bar.pak foo.pak textures.pak ``` or ``` maps/bar.bsp maps/foo.bsp textures/my_tex.wal ``` ### Configure the Quake II server to tell clients about the HTTP server All you have to do is to set the `sv_downloadserver` CVar to your server base path, so in our example you could start your dedicated server with `q2ded +set sv_downloadserver http://example.com/q2data/` (+ your other options). This CVar will be set to connecting Multiplayer Clients and if they support HTTP downloading they will try to load missing game data from that server. ## Map rotation Map rotations are configured through the `sv_maplist` CVar added with Quake II patch 3.15. The map list must be enclosed in quotation marks and all maps must exist. Start the game with the first map. For example: `q2ded +set sv_maplist '"q2dm1 q2dm2 q2dm3"' +map q2dm1` yquake2-QUAKE2_8_40/doc/070_packaging.md000066400000000000000000000046301465112212000175150ustar00rootroot00000000000000# Notes for Package Maintainers This guide shows how Yamagi Quake II should be packaged and what assumptions the games makes regarding binary and game data locations. This guide uses the notation for unixoid platforms. Executables have no file extensions and libraries are given with the *.so* extension. MacOS and Windows behave exactly the same way, but with the platform specific file extensions. ## The Executables Yamagi Quake II expects all binaries (executables and libraries) to be in the same directory or, in the case of *game.so*, in the mod-specific subdirectory. So the binary directory should look somehow like this: * /path/to/yamagi-quake2/ * quake2 * q2ded * ref_gl1.so * ref_gl3.so * baseq2/ * game.so * xatrix/ * game.so * ... (the same for other addons) Yamagi Quake2 will get the directory, the `quake2` executable is in, from the system and then look in that directory (and nowhere else!) for the `ref_*.so` renderer libraries. 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. This is for better compatibility with mods that might ship their own game.so. You can **just symlink the executables to a directory in the $PATH**, like */usr/bin/*. There's one exception to this rule: OpenBSD does not provide a way to get the executable path, so a wrapper script is needed. 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 installation, so they'd end up with a new quake2 executable and old renderer libraries (`ref_*.so`) and report weird bugs. ## The SYSTEMWIDE and SYSTEMDIR options The Makefile allows to enable the *SYSTEMWIDE* feature to force Yamagi Quake II to search the game data in an system specific directory. That directory can be given in the Makefile. If no directory is given the game defaults to */usr/share/games/quake2/* which should be correct for most Linux distributions. The `SYSTEMDIR` is meant to contain only the game data and *not* the binaries. It allows several Quake2 source ports to share the same game data. ## Alternative startup config Yamagi Quake II has support for an alternative startup config. That config overrides some global values with sane defaults. It may be a good idea to install it. Copy yq2.cfg to the baseq2/ subdirectory in the gamedata directory. yquake2-QUAKE2_8_40/doc/080_contributing.md000066400000000000000000000031261465112212000203000ustar00rootroot00000000000000# Contributing Code You want to contribute code to Yamagi Quake II? That's good! We're always interested in contributions, as long as they're in the scope of our little project. It doesn't matter if you're sending new features, bugfixes or even documentation updates. As a general note: **Ask before writing code!** Nobody was ever hurt for asking if his or her idea is in scope for Yamagi Quake II. And an early "no" is always better then rejections after having put tens of hours into the idea. Some rules to follow: * Use GitHub! Sign up for an account, fork our repository and send a pull request after you're satisfied with your work. We won't accept patches sent by email or - even worse - as pastebin links. * Create a history of small, distinct commits. Several small commits are always better then one big commit. They make your changes more understandable and ease debugging. * Never ever add new dependencies. We won't accept code that add new dependencies. If in doubt, because you really need that nice little library, ask. * Make sure that your code is compiling without warnings with at least current versions of GCC and Clang. Also make sure that it's working on both unixoid platforms and Windows. * Don't do unnecessary cleanups. Yes, your linter or sanity checker may complain. But that's your problem and not ours. Cleanups often bring next to no advantage, Quake II has always been a mess and it'll stay a mess until the sun collapses. And cleanups are hard to test, often introduce new bugs and make debugging harder. * Stick to the code style of the file you're editing. yquake2-QUAKE2_8_40/doc/090_filelists.md000066400000000000000000000267641465112212000176050ustar00rootroot00000000000000# File Listings These are listings of all assets of a correct Yamagi Quake II installation. These may be used to verify if the installation is complete. ## Game Data The game data. See inline comments (lines starting with #) for details. The gama data tree is the same on all platform. The executable may be placed in the tree or in an own directory. See the [02_installation.md](Installation Guide) for details. We're using the Windows notation in this list. It's the same for other platforms, but executables and libraries may have other or no file extensions. The three Addons - Three Wave Capture the Flag, The Reckoning and Ground Zero - are optional, the main game will run fine without them. All releases have the music under the mod that it belongs to, **only** the GOG.com release has it at top level. ``` # Top level directory # ------------------- # Shared libaries (Windows only): /curl.dll /openal32.dll /SDL2.DLL # Renderer libraries: /ref_gl1.dll /ref_gl3.dll /ref_gles3.dll /ref_soft.dll # The Executables: /quake2.exe /yquake2.exe # baseq2/ (Main game) # ------------------- # Optional monster footstep sounds: /baseq2/footsteps.pkg # Game library: /baseq2/game.dll # maps.lst is used in the Multiplayer menus: /baseq2/maps.lst # The *.pak files contain the game assets: /baseq2/pak0.pak /baseq2/pak1.pak /baseq2/pak2.pak # yq2.cfg is an optional alternative default config: /baseq2/yq2.cfg # Player models, needed for multiplayer. /baseq2/players/crakhor /baseq2/players/crakhor/a_grenades.md2 /baseq2/players/crakhor/w_bfg.md2 /baseq2/players/crakhor/w_blaster.md2 /baseq2/players/crakhor/w_chainfist.md2 /baseq2/players/crakhor/w_chaingun.md2 /baseq2/players/crakhor/w_disrupt.md2 /baseq2/players/crakhor/w_etfrifle.md2 /baseq2/players/crakhor/w_glauncher.md2 /baseq2/players/crakhor/w_grapple.md2 /baseq2/players/crakhor/w_hyperblaster.md2 /baseq2/players/crakhor/w_machinegun.md2 /baseq2/players/crakhor/w_phalanx.md2 /baseq2/players/crakhor/w_plasma.md2 /baseq2/players/crakhor/w_plauncher.md2 /baseq2/players/crakhor/w_railgun.md2 /baseq2/players/crakhor/w_ripper.md2 /baseq2/players/crakhor/w_rlauncher.md2 /baseq2/players/crakhor/w_shotgun.md2 /baseq2/players/crakhor/w_sshotgun.md2 /baseq2/players/cyborg/a_grenades.md2 /baseq2/players/cyborg/bump1.wav /baseq2/players/cyborg/death1.wav /baseq2/players/cyborg/death2.wav /baseq2/players/cyborg/death3.wav /baseq2/players/cyborg/death4.wav /baseq2/players/cyborg/drown1.wav /baseq2/players/cyborg/fall1.wav /baseq2/players/cyborg/fall2.wav /baseq2/players/cyborg/gurp1.wav /baseq2/players/cyborg/gurp2.wav /baseq2/players/cyborg/jump1.wav /baseq2/players/cyborg/oni911_i.pcx /baseq2/players/cyborg/oni911.pcx /baseq2/players/cyborg/pain100_1.wav /baseq2/players/cyborg/pain100_2.wav /baseq2/players/cyborg/pain25_1.wav /baseq2/players/cyborg/pain25_2.wav /baseq2/players/cyborg/pain50_1.wav /baseq2/players/cyborg/pain50_2.wav /baseq2/players/cyborg/pain75_1.wav /baseq2/players/cyborg/pain75_2.wav /baseq2/players/cyborg/ps9000_i.pcx /baseq2/players/cyborg/ps9000.pcx /baseq2/players/cyborg/tris.md2 /baseq2/players/cyborg/tyr574_i.pcx /baseq2/players/cyborg/tyr574.pcx /baseq2/players/cyborg/w_bfg.md2 /baseq2/players/cyborg/w_blaster.md2 /baseq2/players/cyborg/w_chainfist.md2 /baseq2/players/cyborg/w_chaingun.md2 /baseq2/players/cyborg/w_disrupt.md2 /baseq2/players/cyborg/w_etfrifle.md2 /baseq2/players/cyborg/w_glauncher.md2 /baseq2/players/cyborg/w_grapple.md2 /baseq2/players/cyborg/w_hyperblaster.md2 /baseq2/players/cyborg/w_machinegun.md2 /baseq2/players/cyborg/w_phalanx.md2 /baseq2/players/cyborg/w_plasma.md2 /baseq2/players/cyborg/w_plauncher.md2 /baseq2/players/cyborg/w_railgun.md2 /baseq2/players/cyborg/w_ripper.md2 /baseq2/players/cyborg/w_rlauncher.md2 /baseq2/players/cyborg/w_shotgun.md2 /baseq2/players/cyborg/w_sshotgun.md2 /baseq2/players/cyborg/weapon.md2 /baseq2/players/cyborg/weapon.pcx /baseq2/players/cyborg/weapon.pcx.pcx /baseq2/players/female/a_grenades.md2 /baseq2/players/female/athena_i.pcx /baseq2/players/female/athena.pcx /baseq2/players/female/brianna_i.pcx /baseq2/players/female/brianna.pcx /baseq2/players/female/cobalt_i.pcx /baseq2/players/female/cobalt.pcx /baseq2/players/female/death1.wav /baseq2/players/female/death2.wav /baseq2/players/female/death3.wav /baseq2/players/female/death4.wav /baseq2/players/female/drown.wav /baseq2/players/female/ensign_i.pcx /baseq2/players/female/ensign.pcx /baseq2/players/female/fall1.wav /baseq2/players/female/fall2.wav /baseq2/players/female/gurp1.wav /baseq2/players/female/gurp2.wav /baseq2/players/female/jezebel_i.pcx /baseq2/players/female/jezebel.pcx /baseq2/players/female/jump1.wav /baseq2/players/female/jungle_i.pcx /baseq2/players/female/jungle.pcx /baseq2/players/female/lotus_i.pcx /baseq2/players/female/lotus.pcx /baseq2/players/female/pain100_1.wav /baseq2/players/female/pain100_2.wav /baseq2/players/female/pain25_1.wav /baseq2/players/female/pain25_2.wav /baseq2/players/female/pain50_1.wav /baseq2/players/female/pain50_2.wav /baseq2/players/female/pain75_1.wav /baseq2/players/female/pain75_2.wav /baseq2/players/female/stiletto_i.pcx /baseq2/players/female/stiletto.pcx /baseq2/players/female/tris.md2 /baseq2/players/female/venus_i.pcx /baseq2/players/female/venus.pcx /baseq2/players/female/voodoo_i.pcx /baseq2/players/female/voodoo.pcx /baseq2/players/female/w_bfg.md2 /baseq2/players/female/w_blaster.md2 /baseq2/players/female/w_chainfist.md2 /baseq2/players/female/w_chaingun.md2 /baseq2/players/female/w_disrupt.md2 /baseq2/players/female/w_etfrifle.md2 /baseq2/players/female/w_glauncher.md2 /baseq2/players/female/w_grapple.md2 /baseq2/players/female/w_hyperblaster.md2 /baseq2/players/female/w_machinegun.md2 /baseq2/players/female/w_phalanx.md2 /baseq2/players/female/w_plasma.md2 /baseq2/players/female/w_plauncher.md2 /baseq2/players/female/w_railgun.md2 /baseq2/players/female/w_ripper.md2 /baseq2/players/female/w_rlauncher.md2 /baseq2/players/female/w_shotgun.md2 /baseq2/players/female/w_sshotgun.md2 /baseq2/players/female/weapon.md2 /baseq2/players/female/weapon.pcx /baseq2/players/male/a_grenades.md2 /baseq2/players/male/bump1.wav /baseq2/players/male/cipher_i.pcx /baseq2/players/male/cipher.pcx /baseq2/players/male/claymore_i.pcx /baseq2/players/male/claymore.pcx /baseq2/players/male/death1.wav /baseq2/players/male/death2.wav /baseq2/players/male/death3.wav /baseq2/players/male/death4.wav /baseq2/players/male/drown1.wav /baseq2/players/male/fall1.wav /baseq2/players/male/fall2.wav /baseq2/players/male/flak_i.pcx /baseq2/players/male/flak.pcx /baseq2/players/male/grunt_i.pcx /baseq2/players/male/grunt.pcx /baseq2/players/male/gurp1.wav /baseq2/players/male/gurp2.wav /baseq2/players/male/howitzer_i.pcx /baseq2/players/male/howitzer.pcx /baseq2/players/male/jump1.wav /baseq2/players/male/major_i.pcx /baseq2/players/male/major.pcx /baseq2/players/male/nightops_i.pcx /baseq2/players/male/nightops.pcx /baseq2/players/male/pain100_1.wav /baseq2/players/male/pain100_2.wav /baseq2/players/male/pain25_1.wav /baseq2/players/male/pain25_2.wav /baseq2/players/male/pain50_1.wav /baseq2/players/male/pain50_2.wav /baseq2/players/male/pain75_1.wav /baseq2/players/male/pain75_2.wav /baseq2/players/male/pointman_i.pcx /baseq2/players/male/pointman.pcx /baseq2/players/male/psycho_i.pcx /baseq2/players/male/psycho.pcx /baseq2/players/male/rampage_i.pcx /baseq2/players/male/rampage.pcx /baseq2/players/male/razor_i.pcx /baseq2/players/male/razor.pcx /baseq2/players/male/recon_i.pcx /baseq2/players/male/recon.pcx /baseq2/players/male/scout_i.pcx /baseq2/players/male/scout.pcx /baseq2/players/male/sniper_i.pcx /baseq2/players/male/sniper.pcx /baseq2/players/male/tris.md2 /baseq2/players/male/viper_i.pcx /baseq2/players/male/viper.pcx /baseq2/players/male/w_bfg.md2 /baseq2/players/male/w_blaster.md2 /baseq2/players/male/w_chainfist.md2 /baseq2/players/male/w_chaingun.md2 /baseq2/players/male/w_disrupt.md2 /baseq2/players/male/w_etfrifle.md2 /baseq2/players/male/w_glauncher.md2 /baseq2/players/male/w_grapple.md2 /baseq2/players/male/w_hyperblaster.md2 /baseq2/players/male/w_machinegun.md2 /baseq2/players/male/w_phalanx.md2 /baseq2/players/male/w_plasma.md2 /baseq2/players/male/w_plauncher.md2 /baseq2/players/male/w_railgun.md2 /baseq2/players/male/w_ripper.md2 /baseq2/players/male/w_rlauncher.md2 /baseq2/players/male/w_shotgun.md2 /baseq2/players/male/w_sshotgun.md2 /baseq2/players/male/weapon.md2 /baseq2/players/male/weapon.pcx # Cinematics, videos played between units. /baseq2/video/end.cin /baseq2/video/eou1_.cin /baseq2/video/eou2_.cin /baseq2/video/eou3_.cin /baseq2/video/eou4_.cin /baseq2/video/eou5_.cin /baseq2/video/eou6_.cin /baseq2/video/eou7_.cin /baseq2/video/eou8_.cin /baseq2/video/idlog.cin /baseq2/video/ntro.cin # OGG files. For standard releases, NOT for the GOG.com release: /baseq2/music/02.ogg /baseq2/music/03.ogg /baseq2/music/04.ogg /baseq2/music/05.ogg /baseq2/music/06.ogg /baseq2/music/07.ogg /baseq2/music/08.ogg /baseq2/music/09.ogg /baseq2/music/10.ogg /baseq2/music/11.ogg # ctf/ (Three Wave Capture the Flag) # ---------------------------------- # Game library: /ctf/game.dll # The *.pak files contain the game assets: /ctf/pak0.pak # rogue/ (Ground Zero) # -------------------- # Game library: /rogue/game.dll # The *.pak files contain the game assets: /rogue/pak0.pak # Cinematics, videos played between units. /rogue/video/logo.cin /rogue/video/rend.cin /rogue/video/reu1_.cin /rogue/video/reu2_.cin /rogue/video/reu3_.cin /rogue/video/reu4_.cin /rogue/video/rintro.cin # OGG files. For standard releases, NOT for the GOG.com release: /rogue/music/02.ogg /rogue/music/03.ogg /rogue/music/04.ogg /rogue/music/05.ogg /rogue/music/06.ogg /rogue/music/07.ogg /rogue/music/08.ogg /rogue/music/09.ogg /rogue/music/10.ogg /rogue/music/11.ogg # xatrix/ (The Reckoning) # ----------------------- # Game library: /xatrix/game.dll # The *.pak files contain the game assets: /xatrix/pak0.pak # Cinematics, videos played between units. /xatrix/video/idlog.cin /xatrix/video/logo.cin /xatrix/video/xin.cin /xatrix/video/xout.cin /xatrix/video/xu1.cin /xatrix/video/xu2.cin /xatrix/video/xu3.cin # OGG files. For standard releases, NOT for the GOG.com release: /xatrix/music/02.ogg /xatrix/music/03.ogg /xatrix/music/04.ogg /xatrix/music/05.ogg /xatrix/music/06.ogg /xatrix/music/07.ogg /xatrix/music/08.ogg /xatrix/music/09.ogg /xatrix/music/10.ogg /xatrix/music/11.ogg # music/ (GOG.com music files) # ---------------------------- # These are for the GOG.com release only. /music/Track02.ogg /music/Track03.ogg /music/Track04.ogg /music/Track05.ogg /music/Track06.ogg /music/Track07.ogg /music/Track08.ogg /music/Track09.ogg /music/Track10.ogg /music/Track11.ogg /music/Track12.ogg /music/Track13.ogg /music/Track14.ogg /music/Track15.ogg /music/Track16.ogg /music/Track17.ogg /music/Track18.ogg /music/Track19.ogg /music/Track20.ogg /music/Track21.ogg ``` ## Checksums SHA1 and md5 checksums of all pak files. ``` SHA1 (baseq2/pak0.pak) = 1dd586a3230d1ac5bfd34e57cc796000d4c252c2 SHA1 (baseq2/pak1.pak) = 588ef09965dee539521b4eb6da4337ce78a2ea21 SHA1 (baseq2/pak2.pak) = 67e76a7f3234646507ae59ec1bf755637c1dad03 SHA1 (ctf/pak0.pak) = 11b2362521cfe27473d79f72172531d206d07e14 SHA1 (rogue/pak0.pak) = 8d3979e90f2ffd97692c5d1461b6ddaa5bf84ce6 SHA1 (xatrix/pak0.pak) = 05070e2567ac3f726b4ef8eb62f0c645cdc24547 ``` ``` MD5 (baseq2/pak0.pak) = 1ec55a724dc3109fd50dde71ab581d70 MD5 (baseq2/pak1.pak) = 42663ea709b7cd3eb9b634b36cfecb1a MD5 (baseq2/pak2.pak) = c8217cc5557b672a87fc210c2347d98d MD5 (ctf/pak0.pak) = 1f6bd3d4c08f7ed8c037b12fcffd2eb5 MD5 (rogue/pak0.pak) = 5e2ecbe9287152a1e6e0d77b3f47dcb2 MD5 (xatrix/pak0.pak) = f5e7b04f7d6b9530c59c5e1daa873b51 ``` yquake2-QUAKE2_8_40/src/000077500000000000000000000000001465112212000147005ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/backends/000077500000000000000000000000001465112212000164525ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/backends/generic/000077500000000000000000000000001465112212000200665ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/backends/generic/misc.c000066400000000000000000000136021465112212000211670ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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__) || defined(__sun) #include // readlink(), amongst others #endif #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) #include // for sysctl() to get path to executable #endif #ifdef _WIN32 #include // GetModuleFileNameA() #endif #ifdef __APPLE__ #include // _NSGetExecutablePath #endif #ifdef __HAIKU__ #include #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 WCHAR wexePath[PATH_MAX]; DWORD len; GetModuleFileNameW(NULL, wexePath, PATH_MAX); len = WideCharToMultiByte(CP_UTF8, 0, wexePath, -1, exePath, PATH_MAX, NULL, NULL); if(len <= 0 || len == PATH_MAX) { // an error occured, clear exe path exePath[0] = '\0'; } #elif defined(__linux) || defined(__sun) // 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}; #if defined(__linux) snprintf(buf, sizeof(buf), "/proc/%d/exe", getpid()); #else snprintf(buf, sizeof(buf), "/proc/%ld/path/a.out", 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__) || defined(__NetBSD__) || defined(__DragonFly__) // the sysctl should also work when /proc/ is not mounted (which seems to // be common on FreeBSD), so use it.. #if defined(__FreeBSD__) || defined(__DragonFly__) int name[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; #else int name[4] = {CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME}; #endif 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 #elif defined(__HAIKU__) if (find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, exePath, PATH_MAX) != B_OK) { exePath[0] = '\0'; } #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) { #if defined(__arm__) && defined(__ARM_PCS_VFP) // Enable RunFast mode if not enabled already static const unsigned int bit = 0x04086060; static const unsigned int fpscr = 0x03000000; int ret; asm volatile("fmrx %0, fpscr" : "=r"(ret)); if (ret != fpscr) { asm volatile("fmrx %0, fpscr\n\t" "and %0, %0, %1\n\t" "orr %0, %0, %2\n\t" "fmxr fpscr, %0" : "=r"(ret) : "r"(bit), "r"(fpscr)); } #endif } #endif yquake2-QUAKE2_8_40/src/backends/unix/000077500000000000000000000000001465112212000174355ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/backends/unix/main.c000066400000000000000000000071761465112212000205400ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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. It does some platform * specific initialization stuff and calls the common initialization code. * * ======================================================================= */ #include #include #include #include #include #ifndef FNDELAY #define FNDELAY O_NDELAY #endif #include "../../common/header/common.h" void registerHandler(void); int main(int argc, char **argv) { // register signal handler registerHandler(); // Setup FPU if necessary Sys_SetupFPU(); // Implement command line options that the rather // crappy argument parser can't parse. for (int i = 0; i < argc; i++) { // Are we portable? if (strcmp(argv[i], "-portable") == 0) { is_portable = true; } // Inject a custom data dir. if (strcmp(argv[i], "-datadir") == 0) { // Mkay, did the user give us an argument? if (i != (argc - 1)) { // Check if it exists. struct stat sb; if (stat(argv[i + 1], &sb) == 0) { if (!S_ISDIR(sb.st_mode)) { printf("-datadir %s is not a directory\n", argv[i + 1]); return 1; } if(realpath(argv[i + 1], datadir) == NULL) { printf("realpath(datadir) failed: %s\n", strerror(errno)); datadir[0] = '\0'; } } else { printf("-datadir %s could not be found\n", argv[i + 1]); return 1; } } else { printf("-datadir needs an argument\n"); return 1; } } // Inject a custom config dir. if (strcmp(argv[i], "-cfgdir") == 0) { // We need an argument. if (i != (argc - 1)) { Q_strlcpy(cfgdir, argv[i + 1], sizeof(cfgdir)); } else { printf("-cfgdir needs an argument\n"); return 1; } } } #ifndef __HAIKU__ /* 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; } #endif // 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); /// Do not delay reads on stdin fcntl(fileno(stdin), F_SETFL, fcntl(fileno(stdin), F_GETFL, NULL) | FNDELAY); // Initialize the game. // Never returns. Qcommon_Init(argc, argv); return 0; } yquake2-QUAKE2_8_40/src/backends/unix/network.c000066400000000000000000000470221465112212000212770ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * ======================================================================= * * Low level network code, based upon the BSD socket api. * * ======================================================================= */ #include "../../common/header/common.h" #include #include #include #include #include #include #include #include #if defined(__sun) #include #if !defined(MAX) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif #endif #include #include #include netadr_t net_local_adr; #define LOOPBACK 0x7f000001 #define MAX_LOOPBACK 4 #define QUAKE2MCAST "ff12::666" typedef struct { byte data[MAX_MSGLEN]; int datalen; } loopmsg_t; typedef struct { loopmsg_t msgs[MAX_LOOPBACK]; int get, send; } loopback_t; loopback_t loopbacks[2]; int ip_sockets[2]; int ip6_sockets[2]; int ipx_sockets[2]; char *multicast_interface = NULL; int NET_Socket(char *net_interface, int port, netsrc_t type, int family); char *NET_ErrorString(void); void NetadrToSockadr(netadr_t *a, struct sockaddr_storage *s) { struct sockaddr_in6 *s6; memset(s, 0, sizeof(*s)); switch (a->type) { case NA_BROADCAST: ((struct sockaddr_in *)s)->sin_family = AF_INET; ((struct sockaddr_in *)s)->sin_port = a->port; ((struct sockaddr_in *)s)->sin_addr.s_addr = (in_addr_t)INADDR_BROADCAST; break; case NA_IP: ((struct sockaddr_in *)s)->sin_family = AF_INET; *(int *)&((struct sockaddr_in *)s)->sin_addr = *(int *)&a->ip; ((struct sockaddr_in *)s)->sin_port = a->port; break; case NA_MULTICAST6: s6 = (struct sockaddr_in6 *)s; if (inet_pton(AF_INET6, QUAKE2MCAST, &s6->sin6_addr.s6_addr) != 1) { Com_Printf("NET_NetadrToSockadr: inet_pton: %s\n", strerror(errno)); return; } s6->sin6_family = AF_INET6; s6->sin6_port = a->port; #ifdef SIN6_LEN s6->sin6_len = sizeof(struct sockaddr_in6); #endif /* scope_id is important for * link-local destination.*/ s6->sin6_scope_id = a->scope_id; break; case NA_IP6: if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)a->ip)) { #ifdef SIN6_LEN s->ss_len = sizeof(struct sockaddr_in); #endif s->ss_family = AF_INET; memcpy(&((struct sockaddr_in *)s)->sin_addr, &((struct in6_addr *)a->ip)->s6_addr[12], sizeof(struct in_addr)); ((struct sockaddr_in *)s)->sin_port = a->port; } else { s6 = (struct sockaddr_in6 *)s; s6->sin6_family = AF_INET6; memcpy(&s6->sin6_addr, a->ip, sizeof(s6->sin6_addr)); s6->sin6_port = a->port; #ifdef SIN6_LEN s6->sin6_len = sizeof(struct sockaddr_in6); #endif /* scope_id is important for * link-local destination. */ s6->sin6_scope_id = a->scope_id; } break; case NA_LOOPBACK: case NA_IPX: case NA_BROADCAST_IPX: break; } } void SockadrToNetadr(struct sockaddr_storage *s, netadr_t *a) { struct sockaddr_in6 *s6; if (s->ss_family == AF_INET) { *(int *) &a->ip = *(int *)&((struct sockaddr_in *)s)->sin_addr; a->port = ((struct sockaddr_in *)s)->sin_port; a->type = NA_IP; } else if (s->ss_family == AF_INET6) { s6 = (struct sockaddr_in6 *)s; if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)&s6->sin6_addr)) { memcpy(a->ip, (struct in_addr *)&s6->sin6_addr.s6_addr[12], sizeof(struct in_addr)); a->port = ((struct sockaddr_in *)s)->sin_port; a->type = NA_IP; } else { memcpy(a->ip, &s6->sin6_addr, sizeof(a->ip)); a->port = s6->sin6_port; a->type = NA_IP6; a->scope_id = s6->sin6_scope_id; } } } void NET_Init() { } qboolean NET_CompareAdr(netadr_t a, netadr_t b) { if (a.type != b.type) { return false; } if (a.type == NA_LOOPBACK) { return true; } if (a.type == NA_IP) { if ((a.ip[0] == b.ip[0]) && (a.ip[1] == b.ip[1]) && (a.ip[2] == b.ip[2]) && (a.ip[3] == b.ip[3]) && (a.port == b.port)) { return true; } } if (a.type == NA_IP6) { if ((memcmp(a.ip, b.ip, 16) == 0) && (a.port == b.port)) { return true; } } return false; } /* * Compares without the port */ qboolean NET_CompareBaseAdr(netadr_t a, netadr_t b) { if (a.type != b.type) { return false; } if (a.type == NA_LOOPBACK) { return true; } if (a.type == NA_IP) { if ((a.ip[0] == b.ip[0]) && (a.ip[1] == b.ip[1]) && (a.ip[2] == b.ip[2]) && (a.ip[3] == b.ip[3])) { return true; } return false; } if (a.type == NA_IP6) { if ((memcmp(a.ip, b.ip, 16) == 0)) { return true; } return false; } if (a.type == NA_IPX) { if ((memcmp(a.ipx, b.ipx, 10) == 0)) { return true; } return false; } return false; } char * NET_BaseAdrToString(netadr_t a) { static char s[64]; struct sockaddr_storage ss; struct sockaddr_in6 *s6; switch (a.type) { case NA_IP: case NA_LOOPBACK: Com_sprintf(s, sizeof(s), "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3]); break; case NA_BROADCAST: Com_sprintf(s, sizeof(s), "255.255.255.255"); break; case NA_IP6: case NA_MULTICAST6: memset(&ss, 0, sizeof(ss)); s6 = (struct sockaddr_in6 *)&ss; if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)a.ip)) { #ifdef SIN6_LEN ss.ss_len = sizeof(struct sockaddr_in); #endif ss.ss_family = AF_INET; memcpy(&((struct sockaddr_in *)&ss)->sin_addr, &((struct in6_addr *)a.ip)->s6_addr[12], sizeof(struct in_addr)); } else { #ifdef SIN6_LEN s6->sin6_len = sizeof(struct sockaddr_in6); #endif s6->sin6_scope_id = a.scope_id; s6->sin6_family = AF_INET6; memcpy(&s6->sin6_addr, a.ip, sizeof(struct in6_addr)); } #ifdef SIN6_LEN socklen_t const salen = ss.ss_len; #else socklen_t const salen = sizeof(ss); #endif if (getnameinfo((struct sockaddr *)&ss, salen, s, sizeof(s), NULL, 0, NI_NUMERICHOST)) { Com_sprintf(s, sizeof(s), ""); } else { if ((a.type == NA_MULTICAST6) || IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&ss)-> sin6_addr)) { char tmp[64]; /* If the address is multicast (link) or a link-local, need to carry the scope. The string format of the IPv6 address is used by the client to extablish the connect to the server. */ Com_sprintf(tmp, sizeof(tmp), "%s%%%d", s, s6->sin6_scope_id); memcpy(s, tmp, sizeof(s)); } } break; default: Com_sprintf(s, sizeof(s), "invalid IP address family type"); break; } return s; } char * NET_AdrToString(netadr_t a) { static char s[64]; const char *base; base = NET_BaseAdrToString(a); Com_sprintf(s, sizeof(s), "[%s]:%d", base, ntohs(a.port)); return s; } qboolean NET_StringToSockaddr(const char *s, struct sockaddr_storage *sadr) { char copy[128]; char *addrs, *space; char *ports = NULL; int err; struct addrinfo hints; struct addrinfo *resultp; memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_DGRAM; hints.ai_family = PF_UNSPEC; strcpy(copy, s); addrs = space = copy; if (*addrs == '[') { addrs++; for ( ; *space && *space != ']'; space++) { } if (!*space) { Com_Printf("NET_StringToSockaddr: invalid IPv6 address %s\n", s); return 0; } *space++ = '\0'; } for ( ; *space; space++) { if (*space == ':') { *space = '\0'; ports = space + 1; } } if ((err = getaddrinfo(addrs, ports, &hints, &resultp))) { /* Error */ Com_Printf("NET_StringToSockaddr: string %s:\n%s\n", s, gai_strerror(err)); return 0; } switch (resultp->ai_family) { case AF_INET: case AF_INET6: /* convert to ipv4/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; } freeaddrinfo(resultp); return true; } qboolean NET_StringToAdr(const 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]; if (multicast_interface != NULL) { int error; char mcast_addr[128], mcast_port[10]; /* 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); freeaddrinfo(res); } 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) { if (!multiplayer) { int i; /* 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 = 0; int Error = 0; 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))) { Com_Printf("ERROR: NET_Socket: getaddrinfo:%s\n", gai_strerror(Error)); 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)); close(newsocket); 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()); freeaddrinfo(res); close(newsocket); 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()); freeaddrinfo(res); close(newsocket); return 0; } if (bind(newsocket, ai->ai_addr, ai->ai_addrlen) < 0) { Com_Printf("NET_Socket: bind: %s\n", strerror(errno)); close(newsocket); } else { memcpy(&ss, ai->ai_addr, ai->ai_addrlen); break; } } if (res != NULL) { freeaddrinfo(res); } if (ai == NULL) { close(newsocket); 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_8_40/src/backends/unix/shared/000077500000000000000000000000001465112212000207035ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/backends/unix/shared/hunk.c000066400000000000000000000075741465112212000220310ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; size_t maxhunksize; size_t curhunksize; void * Hunk_Begin(int maxsize) { /* reserve a huge chunk of memory, but don't commit any yet */ /* plus 32 bytes for cacheline */ maxhunksize = maxsize + sizeof(size_t) + 32; curhunksize = 0; int flags = MAP_PRIVATE | MAP_ANONYMOUS; int prot = PROT_READ | PROT_WRITE; #if defined(MAP_ALIGNED_SUPER) const size_t hgpagesize = 1UL<<21; size_t page_size = sysconf(_SC_PAGESIZE); /* Archs supported has 2MB for super pages size */ if (maxhunksize >= hgpagesize) { maxhunksize = (maxhunksize & ~(page_size-1)) + page_size; flags |= MAP_ALIGNED_SUPER; } #endif #if defined(PROT_MAX) /* For now it is FreeBSD exclusif but could possibly be extended to other like DFBSD for example */ prot |= PROT_MAX(prot); #endif membase = mmap(0, maxhunksize, prot, flags, -1, 0); if ((membase == NULL) || (membase == (byte *)-1)) { Sys_Error("unable to virtual allocate %d bytes", maxsize); } *((size_t *)membase) = curhunksize; return membase + sizeof(size_t); } 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(size_t) + curhunksize; curhunksize += size; return buf; } int Hunk_End(void) { byte *n = NULL; #if defined(__linux__) n = (byte *)mremap(membase, maxhunksize, curhunksize + sizeof(size_t), 0); #elif defined(__NetBSD__) n = (byte *)mremap(membase, maxhunksize, NULL, curhunksize + sizeof(size_t), 0); #else #ifndef round_page size_t page_size = sysconf(_SC_PAGESIZE); #define round_page(x) ((((size_t)(x)) + page_size-1) & ~(page_size-1)) #endif size_t old_size = round_page(maxhunksize); size_t new_size = round_page(curhunksize + sizeof(size_t)); if (new_size > old_size) { /* Can never happen. If it happens something's very wrong. */ n = 0; } else if (new_size < old_size) { /* Hunk is to big, we need to shrink it. */ n = munmap(membase + new_size, old_size - new_size) + membase; } else { /* No change necessary. */ n = membase; } #endif if (n != membase) { Sys_Error("Hunk_End: Could not remap virtual block (%d)", errno); } *((size_t *)membase) = curhunksize + sizeof(size_t); return curhunksize; } void Hunk_Free(void *base) { if (base) { byte *m; m = ((byte *)base) - sizeof(size_t); if (munmap(m, *((size_t *)m))) { Sys_Error("Hunk_Free: munmap failed (%d)", errno); } } } yquake2-QUAKE2_8_40/src/backends/unix/signalhandler.c000066400000000000000000000075411465112212000224230ustar00rootroot00000000000000/* * 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 is supplied. * * ======================================================================= */ #include #include "../../common/header/common.h" #if defined(HAVE_EXECINFO) #include 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 platform.\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); signal(SIGBUS, SIG_DFL); /* pass signal to the os */ raise(sig); } extern qboolean quitnextframe; void terminate(int sig) { quitnextframe = true; } void registerHandler(void) { /* Crash */ signal(SIGSEGV, signalhandler); signal(SIGILL, signalhandler); signal(SIGFPE, signalhandler); signal(SIGABRT, signalhandler); signal(SIGBUS, signalhandler); /* User abort */ signal(SIGINT, terminate); signal(SIGTERM, terminate); } yquake2-QUAKE2_8_40/src/backends/unix/system.c000066400000000000000000000276551465112212000211440ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 dependent generic functions. * * ======================================================================= */ #include #include #include #include #include #include #include #include #include #include #include /* for fd_set */ #ifndef FNDELAY #define FNDELAY O_NDELAY #endif #ifdef __APPLE__ #include #include #endif #include "../../common/header/common.h" #include "../../common/header/glob.h" // Pointer to game library static void *game_library; // Evil hack to determine if stdin is available qboolean stdin_active = true; // Terminal supports colors static qboolean color_active = false; // Config dir char cfgdir[MAX_OSPATH] = CFGDIR; // Console logfile extern FILE *logfile; /* ================================================================ */ void Sys_Error(const 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); } 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_Init(void) { char *envvar; envvar = getenv("TERM"); if (envvar && strstr(envvar, "color")) { char buf[256]; color_active = true; snprintf(buf, sizeof(buf), "\2Terminal supports colors: TERM='%s'\n", envvar); Sys_ConsoleOutput(buf); return; } envvar = getenv("COLORTERM"); if (envvar && strlen(envvar)) { char buf[256]; color_active = true; snprintf(buf, sizeof(buf), "\2Terminal supports colors: COLORTERM='%s'\n", envvar); Sys_ConsoleOutput(buf); return; } Sys_ConsoleOutput("Terminal has no colors support.\n"); } /* ================================================================ */ 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_ConsoleOutput(char *string) { if ((string[0] == 0x01) || (string[0] == 0x02)) { if (color_active) { if (string[0] == 0x01) { /* red */ fputs("\033[31;1m", stdout); } else { /* green */ fputs("\033[32;1m", stdout); } fputs(string + 1, stdout); /* reset to default terminal settings */ fputs("\033[0m", stdout); return; } } fputs(string, stdout); } /* ================================================================ */ 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_Nanosleep(int nanosec) { struct timespec t = {0, nanosec}; nanosleep(&t, NULL); } /* ================================================================ */ /* The musthave and canhave arguments are unused in YQ2. We can't remove them since Sys_FindFirst() and Sys_FindNext() are defined in shared.h and may be used in custom game DLLs. */ static char findbase[MAX_OSPATH]; static char findpath[MAX_OSPATH]; static char findpattern[MAX_OSPATH]; static DIR *fdir; 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 ((strcmp(d->d_name, ".") != 0) || (strcmp(d->d_name, "..") != 0)) { snprintf(findpath, sizeof(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 ((strcmp(d->d_name, ".") != 0) || (strcmp(d->d_name, "..") != 0)) { snprintf(findpath, sizeof(findpath), "%s/%s", findbase, d->d_name); return findpath; } } } return NULL; } void Sys_FindClose(void) { if (fdir != NULL) { closedir(fdir); } fdir = NULL; } /* ================================================================ */ void Sys_UnloadGame(void) { if (game_library) { dlclose(game_library); } game_library = NULL; } void * Sys_GetGameAPI(void *parms) { typedef void *(*fnAPI)(void *); fnAPI GetGameAPI; char name[MAX_OSPATH]; char *path; char *str_p; #ifdef __APPLE__ const char *gamename = "game.dylib"; #else const char *gamename = "game.so"; #endif if (game_library) { Com_Error(ERR_FATAL, "Sys_GetGameAPI without Sys_UnloadingGame"); } Com_Printf("Loading library: %s\n", gamename); /* now run through the search paths */ path = NULL; while (1) { FILE *fp; 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); #ifdef USE_SANITIZER game_library = dlopen(name, RTLD_NOW | RTLD_NODELETE); #else game_library = dlopen(name, RTLD_NOW); #endif if (game_library) { Com_MDPrintf("Loading library: %s\n", name); break; } else { Com_Printf("Loading library: %s\n: ", 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 = (fnAPI)dlsym(game_library, "GetGameAPI"); if (!GetGameAPI) { Sys_UnloadGame(); return NULL; } return GetGameAPI(parms); } /* ================================================================ */ void Sys_Mkdir(const char *path) { if (!Sys_IsDir(path)) { if (mkdir(path, 0755) != 0) { Com_Error(ERR_FATAL, "Couldn't create dir %s\n", path); } } } qboolean Sys_IsDir(const char *path) { struct stat sb; if (stat(path, &sb) != -1) { if (S_ISDIR(sb.st_mode)) { return true; } } return false; } qboolean Sys_IsFile(const char *path) { struct stat sb; if (stat(path, &sb) != -1) { if (S_ISREG(sb.st_mode)) { return true; } } return false; } char * Sys_GetHomeDir(void) { static char gdir[MAX_OSPATH]; char *home; home = getenv("HOME"); if (!home) { return NULL; } #ifndef __HAIKU__ snprintf(gdir, sizeof(gdir), "%s/%s/", home, cfgdir); #else snprintf(gdir, sizeof(gdir), "%s/config/settings/%s", home, cfgdir); #endif Sys_Mkdir(gdir); return gdir; } void Sys_Remove(const char *path) { remove(path); } int Sys_Rename(const char *from, const char *to) { return rename(from, to); } void Sys_RemoveDir(const char *path) { char filepath[MAX_OSPATH]; struct dirent *file; DIR *directory; if (Sys_IsDir(path)) { directory = opendir(path); if (directory) { while ((file = readdir(directory)) != NULL) { snprintf(filepath, MAX_OSPATH, "%s/%s", path, file->d_name); Sys_Remove(filepath); } closedir(directory); Sys_Remove(path); } } } qboolean Sys_Realpath(const char *in, char *out, size_t size) { char *converted = realpath(in, NULL); if (converted == NULL) { Com_Printf("Couldn't get realpath for %s\n", in); return false; } Q_strlcpy(out, converted, size); free(converted); return true; } /* ================================================================ */ 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_FreeLibrary(void *handle) { if (handle && dlclose(handle)) { Com_Error(ERR_FATAL, "dlclose failed on %p: %s", handle, dlerror()); } } void * Sys_LoadLibrary(const char *path, const char *sym, void **handle) { void *module, *entry; *handle = NULL; #ifdef USE_SANITIZER module = dlopen(path, RTLD_LAZY | RTLD_NODELETE); #else module = dlopen(path, RTLD_LAZY); #endif 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_GetWorkDir(char *buffer, size_t len) { if (getcwd(buffer, len) != 0) { return; } buffer[0] = '\0'; } qboolean Sys_SetWorkDir(char *path) { if (chdir(path) == 0) { return true; } return false; } yquake2-QUAKE2_8_40/src/backends/windows/000077500000000000000000000000001465112212000201445ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/backends/windows/header/000077500000000000000000000000001465112212000213745ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/backends/windows/header/resource.h000066400000000000000000000005371465112212000234010ustar00rootroot00000000000000#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_8_40/src/backends/windows/icon.rc000066400000000000000000000000531465112212000214200ustar00rootroot00000000000000icon ICON "../../../stuff/icon/Quake2.ico" yquake2-QUAKE2_8_40/src/backends/windows/main.c000066400000000000000000000055321465112212000212410ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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. It does some platform * specific initialization stuff and calls the common initialization code. * * ======================================================================= */ #include #ifndef DEDICATED_ONLY #ifdef USE_SDL3 #include #include #else #include #include #endif #endif #include "../../common/header/common.h" /* * Windows main function. Containts the * initialization code and the main loop */ int main(int argc, char **argv) { // Setup FPU if necessary. Sys_SetupFPU(); // Force DPI awareness. Sys_SetHighDPIMode(); // crappy argument parser can't parse. for (int i = 0; i < argc; i++) { // Are we portable? if (strcmp(argv[i], "-portable") == 0) { is_portable = true; } // Inject a custom data dir. if (strcmp(argv[i], "-datadir") == 0) { // Mkay, did the user give us an argument? if (i != (argc - 1)) { DWORD attrib; WCHAR wpath[MAX_OSPATH]; MultiByteToWideChar(CP_UTF8, 0, argv[i + 1], -1, wpath, MAX_OSPATH); attrib = GetFileAttributesW(wpath); if (attrib != INVALID_FILE_ATTRIBUTES) { if (!(attrib & FILE_ATTRIBUTE_DIRECTORY)) { printf("-datadir %s is not a directory\n", argv[i + 1]); return 1; } Q_strlcpy(datadir, argv[i + 1], MAX_OSPATH); } else { printf("-datadir %s could not be found\n", argv[i + 1]); return 1; } } else { printf("-datadir needs an argument\n"); return 1; } } // Inject a custom config dir. if (strcmp(argv[i], "-cfgdir") == 0) { // We need an argument. if (i != (argc - 1)) { Q_strlcpy(cfgdir, argv[i + 1], sizeof(cfgdir)); } else { printf("-cfgdir needs an argument\n"); return 1; } } } // Need to redirect stdout before anything happens. #ifndef DEDICATED_ONLY Sys_RedirectStdout(); #endif // Call the initialization code. // Never returns. Qcommon_Init(argc, argv); return 0; } yquake2-QUAKE2_8_40/src/backends/windows/network.c000066400000000000000000000657121465112212000220140ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; } } } 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]; 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) { char tmp[64]; 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(const 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(const 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]; 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) { char mcast_addr[128]; char mcast_port[10]; 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) ? "::/128" : "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; if ((newsocket = socket(PF_IPX, SOCK_DGRAM, NSPROTO_IPX)) == -1) { int err; 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) { static qboolean old_config; if (old_config == multiplayer) { return; } old_config = multiplayer; if (!multiplayer) { int i; /* 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 = Q_max(ip_sockets[NS_SERVER], ip6_sockets[NS_SERVER]); i = Q_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_8_40/src/backends/windows/shared/000077500000000000000000000000001465112212000214125ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/backends/windows/shared/hunk.c000066400000000000000000000037401465112212000225270ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 #include "../../../common/header/common.h" byte *membase; int hunkcount; size_t hunkmaxsize; size_t cursize; void * Hunk_Begin(int maxsize) { /* reserve a huge chunk of memory, but don't commit any yet */ /* plus 32 bytes for cacheline */ hunkmaxsize = maxsize + sizeof(size_t) + 32; cursize = 0; membase = VirtualAlloc(NULL, hunkmaxsize, 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) { Sys_Error("VirtualAlloc commit failed.\n"); } 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_8_40/src/backends/windows/system.c000066400000000000000000000400231465112212000216330ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 dependent generic functions. * * ======================================================================= */ #include #include #include #include #include #include #include #include #include #include #include "../../common/header/common.h" #include "header/resource.h" // stdin and stdout handles static HANDLE hinput, houtput; // Game library handle static HINSTANCE game_library; // Config dir char cfgdir[MAX_OSPATH] = CFGDIR; // Buffer for the dedicated server console static char console_text[256]; static size_t console_textlen; /* ================================================================ */ void Sys_Error(const char *error, ...) { va_list argptr; char text[1024]; #ifndef DEDICATED_ONLY CL_Shutdown(); #endif Qcommon_Shutdown(); va_start(argptr, error); vsnprintf(text, sizeof(text), error, argptr); va_end(argptr); fprintf(stderr, "Error: %s\n", text); fprintf(stdout, "Error: %s\n", text); MessageBox(NULL, text, "Error", 0 /* MB_OK */); /* Close stdout and stderr */ #ifndef DEDICATED_ONLY fclose(stdout); fclose(stderr); #endif exit(1); } void Sys_Quit(void) { const qboolean free_console = (dedicated && dedicated->value); timeEndPeriod(1); #ifndef DEDICATED_ONLY CL_Shutdown(); #endif Qcommon_Shutdown(); if (free_console) { FreeConsole(); } printf( "------------------------------------\n" ); /* Close stdout and stderr */ #ifndef DEDICATED_ONLY fclose(stdout); fclose(stderr); #endif exit(0); } 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 ((string[0] == 0x01) || (string[0] == 0x02)) { // remove color marker string[0] = ' '; } 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); } } } /* ================================================================ */ long long Sys_Microseconds(void) { static LARGE_INTEGER freq = { 0 }; static LARGE_INTEGER base = { 0 }; if (!freq.QuadPart) { QueryPerformanceFrequency(&freq); } if (!base.QuadPart) { QueryPerformanceCounter(&base); base.QuadPart -= 1001; } LARGE_INTEGER cur; QueryPerformanceCounter(&cur); return (cur.QuadPart - base.QuadPart) * 1000000 / freq.QuadPart; } int Sys_Milliseconds(void) { return (int)(Sys_Microseconds()/1000ll); } 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); } /* ================================================================ */ /* The musthave and canhave arguments are unused in YQ2. We can't remove them since Sys_FindFirst() and Sys_FindNext() are defined in shared.h and may be used in custom game DLLs. */ // File searching static char findbase[MAX_OSPATH]; static char findpath[MAX_OSPATH]; static HANDLE findhandle; char * Sys_FindFirst(char *path, unsigned musthave, unsigned canthave) { if (findhandle) { Sys_Error("Sys_BeginFind without close"); } COM_FilePath(path, findbase); WCHAR wpath[MAX_OSPATH] = {0}; MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_OSPATH); WIN32_FIND_DATAW findinfo; findhandle = FindFirstFileW(wpath, &findinfo); if (findhandle == INVALID_HANDLE_VALUE) { return NULL; } CHAR cFileName[MAX_OSPATH]; WideCharToMultiByte(CP_UTF8, 0, findinfo.cFileName, -1, cFileName, MAX_OSPATH, NULL, NULL); Com_sprintf(findpath, sizeof(findpath), "%s/%s", findbase, cFileName); return findpath; } char * Sys_FindNext(unsigned musthave, unsigned canthave) { WIN32_FIND_DATAW findinfo; if (findhandle == INVALID_HANDLE_VALUE) { return NULL; } if (!FindNextFileW(findhandle, &findinfo)) { return NULL; } CHAR cFileName[MAX_OSPATH]; WideCharToMultiByte(CP_UTF8, 0, findinfo.cFileName, -1, cFileName, MAX_OSPATH, NULL, NULL); Com_sprintf(findpath, sizeof(findpath), "%s/%s", findbase, cFileName); return findpath; } void Sys_FindClose(void) { if (findhandle != INVALID_HANDLE_VALUE) { FindClose(findhandle); } findhandle = 0; } /* ================================================================ */ 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]; WCHAR wname[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"); MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, MAX_OSPATH); game_library = LoadLibraryW(wname); if (game_library) { Com_DPrintf("Loading library: %s\n", name); break; } /* Try gamex86.dll as fallback */ Com_sprintf(name, sizeof(name), "%s/%s", path, "gamex86.dll"); MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, MAX_OSPATH); game_library = LoadLibraryW(wname); if (game_library) { Com_DPrintf("Loading library: %s\n", name); break; } } GetGameAPI = (void *)GetProcAddress(game_library, "GetGameAPI"); if (!GetGameAPI) { Sys_UnloadGame(); return NULL; } return GetGameAPI(parms); } /* ======================================================================= */ void Sys_Mkdir(const char *path) { if (!Sys_IsDir(path)) { WCHAR wpath[MAX_OSPATH] = {0}; MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_OSPATH); if (CreateDirectoryW(wpath, NULL) == 0) { Com_Error(ERR_FATAL, "Couldn't create dir %s\n", path); } } } qboolean Sys_IsDir(const char *path) { WCHAR wpath[MAX_OSPATH] = {0}; MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_OSPATH); DWORD fileAttributes = GetFileAttributesW(wpath); if (fileAttributes == INVALID_FILE_ATTRIBUTES) { return false; } return (fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; } qboolean Sys_IsFile(const char *path) { WCHAR wpath[MAX_OSPATH] = {0}; MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_OSPATH); DWORD fileAttributes = GetFileAttributesW(wpath); if (fileAttributes == INVALID_FILE_ATTRIBUTES) { return false; } // I guess the assumption that if it's not a file or device // then it's a directory is good enough for us? return (fileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE)) == 0; } char * Sys_GetHomeDir(void) { char *cur; char *old; char profile[MAX_PATH]; static char gdir[MAX_OSPATH]; WCHAR uprofile[MAX_PATH]; /* Get the path to "My Documents" directory */ SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, 0, uprofile); if (WideCharToMultiByte(CP_UTF8, 0, uprofile, -1, profile, sizeof(profile), NULL, NULL) == 0) { 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); Sys_Mkdir(gdir); return gdir; } void Sys_Remove(const char *path) { WCHAR wpath[MAX_OSPATH] = {0}; MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_OSPATH); _wremove(wpath); } int Sys_Rename(const char *from, const char *to) { WCHAR wfrom[MAX_OSPATH] = {0}; MultiByteToWideChar(CP_UTF8, 0, from, -1, wfrom, MAX_OSPATH); WCHAR wto[MAX_OSPATH] = {0}; MultiByteToWideChar(CP_UTF8, 0, to, -1, wto, MAX_OSPATH); return _wrename(wfrom, wto); } void Sys_RemoveDir(const char *path) { WCHAR wpath[MAX_OSPATH] = {0}; WCHAR wpathwithwildcard[MAX_OSPATH] = {0}; WCHAR wpathwithfilename[MAX_OSPATH] = {0}; WIN32_FIND_DATAW fd; if (MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_OSPATH) >= MAX_QPATH) { /* This is hopefully never reached, because in a good world path has a maximum length of MAX_QPATH. Since we can't use wcscat_s() below because it would break compat with Win XP, nevertheless do this check. */ return; } wcsncpy(wpathwithwildcard, wpath, MAX_OSPATH); wcscat(wpathwithwildcard, L"\\*.*"); HANDLE hFind = FindFirstFileW(wpathwithwildcard, &fd); if (hFind != INVALID_HANDLE_VALUE) { do { if (wcslen(wpath) + wcslen(fd.cFileName) >= MAX_QPATH) { // Same as above. continue; } wmemset(wpathwithfilename, 0, MAX_OSPATH); wcscat(wpathwithfilename, wpath); wcscat(wpathwithfilename, fd.cFileName); DeleteFileW(wpathwithfilename); } while (FindNextFileW(hFind, &fd)); FindClose(hFind); } RemoveDirectoryW(wpath); } qboolean Sys_Realpath(const char *in, char *out, size_t size) { WCHAR win[MAX_OSPATH] = {0}; WCHAR wconverted[MAX_OSPATH] = {0}; MultiByteToWideChar(CP_UTF8, 0, in, -1, win, sizeof(win)/sizeof(win[0])); if (_wfullpath(wconverted, win, size) == NULL) { Com_Printf("Couldn't get realpath for %s\n", in); return false; } WideCharToMultiByte(CP_UTF8, 0, wconverted, -1, out, size, NULL, NULL); return true; } /* ======================================================================= */ void * Sys_GetProcAddress(void *handle, const char *sym) { return GetProcAddress(handle, sym); } 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; WCHAR wpath[MAX_OSPATH]; *handle = NULL; MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_OSPATH); module = LoadLibraryW(wpath); 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_GetWorkDir(char *buffer, size_t len) { WCHAR wbuffer[MAX_OSPATH]; if (GetCurrentDirectoryW(sizeof(wbuffer)/sizeof(wbuffer[0]), wbuffer) != 0) { WideCharToMultiByte(CP_UTF8, 0, wbuffer, -1, buffer, len, NULL, NULL); return; } buffer[0] = '\0'; } qboolean Sys_SetWorkDir(char *path) { WCHAR wpath[MAX_OSPATH]; MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, sizeof(wpath)/sizeof(wpath[0])); if (SetCurrentDirectoryW(wpath) != 0) { return true; } return false; } /* ======================================================================= */ // This one is Windows specific. void Sys_RedirectStdout(void) { char dir[MAX_OSPATH]; char path_stdout[MAX_OSPATH]; char path_stderr[MAX_OSPATH]; WCHAR wpath_stdout[MAX_OSPATH]; WCHAR wpath_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[0] == '\0') { return; } snprintf(path_stdout, sizeof(path_stdout), "%s/%s", dir, "stdout.txt"); snprintf(path_stderr, sizeof(path_stderr), "%s/%s", dir, "stderr.txt"); MultiByteToWideChar(CP_UTF8, 0, path_stdout, -1, wpath_stdout, sizeof(wpath_stdout)/sizeof(wpath_stdout[0])); MultiByteToWideChar(CP_UTF8, 0, path_stderr, -1, wpath_stderr, sizeof(wpath_stderr)/sizeof( wpath_stderr[0] ) ); _wfreopen(wpath_stdout, L"w", stdout); _wfreopen(wpath_stderr, L"w", stderr); setvbuf(stdout, (char *)NULL, _IOLBF, BUFSIZ); setvbuf(stderr, (char *)NULL, _IOLBF, BUFSIZ); } /* ======================================================================= */ // This one is windows specific. 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(); } } yquake2-QUAKE2_8_40/src/client/000077500000000000000000000000001465112212000161565ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/cl_cin.c000066400000000000000000000341461465112212000175610ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 #include "header/client.h" #include "input/header/input.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) // Switch of the thread local stuff. Breaks mingw under Windows. #define STBI_NO_THREAD_LOCALS // include implementation part of stb_image into this file #define STB_IMAGE_IMPLEMENTATION #include "refresh/files/stb_image.h" extern cvar_t *vid_renderer; cvar_t *cin_force43; int abort_cinematic; 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; int color_bits; 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, bpl; 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; } bpl = LittleShort(pcx->bytes_per_line); if (bpl <= pcx->xmax) { bpl = pcx->xmax + 1; } 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 < bpl; ) { 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; // the samples array is used as bytes or shorts, depending on bitrate (cin.s_width) // so we need to make sure to align it correctly YQ2_ALIGNAS_TYPE(short) 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 (((size_t)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; } } static int SCR_MinimalColor(void) { int i, min_color, min_index; min_color = 255 * 3; min_index = 0; for(i=0; i<255; i++) { int current_color = (cl.cinematicpalette[i*3+0] + cl.cinematicpalette[i*3+1] + cl.cinematicpalette[i*3+2]); if (min_color > current_color) { min_color = current_color; min_index = i; } } return min_index; } /* * Returns true if a cinematic is active, meaning the * view rendering should be skipped */ qboolean SCR_DrawCinematic(void) { int x, y, w, h, color; 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 (!vid_renderer) { vid_renderer = Cvar_Get("vid_renderer", "gl1", CVAR_ARCHIVE); } if (Q_stricmp(vid_renderer->string, "soft") == 0) { color = SCR_MinimalColor(); /* Soft render requires to reset palette before show RGBA image */ if (cin.color_bits == 32) { R_SetPalette(NULL); } } else { color = 0; } if (x > 0) { Draw_Fill(0, 0, x, viddef.height, color); } if (x + w < viddef.width) { Draw_Fill(x + w, 0, viddef.width - (x + w), viddef.height, color); } if (y > 0) { Draw_Fill(x, 0, w, y, color); } if (y + h < viddef.height) { Draw_Fill(x, y + h, w, viddef.height - (y + h), color); } Draw_StretchRaw(x, y, w, h, cin.width, cin.height, cin.pic, cin.color_bits); return true; } byte * SCR_LoadHiColor(const char* namewe, const char *ext, int *width, int *height) { char filename[256]; int bytesPerPixel; byte *pic, *data = NULL; void *rawdata; size_t len; Q_strlcpy(filename, namewe, sizeof(filename)); Q_strlcat(filename, ".", sizeof(filename)); Q_strlcat(filename, ext, sizeof(filename)); len = FS_LoadFile(filename, &rawdata); if (!rawdata || len <=0) { return NULL; } data = stbi_load_from_memory(rawdata, len, width, height, &bytesPerPixel, STBI_rgb_alpha); if (data == NULL) { FS_FreeFile(rawdata); return NULL; } pic = Z_Malloc(cin.height * cin.width * 4); memcpy(pic, data, cin.height * cin.width * 4); free(data); return pic; } void SCR_PlayCinematic(char *arg) { int width, height; byte *palette = NULL; char name[MAX_OSPATH], *dot; In_FlushQueue(); abort_cinematic = INT_MAX; /* make sure background music is not playing */ OGG_Stop(); cl.cinematicframe = 0; dot = strstr(arg, "."); /* static pcx image */ if (dot && !strcmp(dot, ".pcx")) { cvar_t *r_retexturing; Com_sprintf(name, sizeof(name), "pics/%s", arg); r_retexturing = Cvar_Get("r_retexturing", "1", CVAR_ARCHIVE); if (r_retexturing->value) { char namewe[256]; cin.color_bits = 32; /* Remove the extension */ memset(namewe, 0, 256); memcpy(namewe, name, strlen(name) - strlen(dot)); cin.pic = SCR_LoadHiColor(namewe, "tga", &cin.width, &cin.height); if (!cin.pic) { cin.pic = SCR_LoadHiColor(namewe, "png", &cin.width, &cin.height); } if (!cin.pic) { cin.pic = SCR_LoadHiColor(namewe, "jpg", &cin.width, &cin.height); } } if (!cin.pic) { SCR_LoadPCX(name, &cin.pic, &palette, &cin.width, &cin.height); cin.color_bits = 8; } 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 if (palette) { 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(); cin.color_bits = 8; 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_8_40/src/client/cl_console.c000066400000000000000000000322671465112212000204540ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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"); /* play music */ if (Cvar_VariableValue("ogg_pausewithgame") == 1 && OGG_Status() == PAUSE) { Cbuf_AddText("ogg toggle\n"); } } else { M_ForceMenuOff(); cls.key_dest = key_console; if ((Cvar_VariableValue("maxclients") == 1) && Com_ServerState()) { Cvar_Set("paused", "1"); /* pause music */ if (Cvar_VariableValue("ogg_pausewithgame") == 1 && OGG_Status() == PLAY) { Cbuf_AddText("ogg toggle\n"); } } } } 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 = Q_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(); /* We need to clamp the line width to MAXCMDLINE - 2, otherwise we may overflow the text buffer if the vertical resultion / 8 (one char == 8 pixels) is bigger then MAXCMDLINE. MAXCMDLINE - 2 because 1 for the prompt and 1 for the terminating \0. */ width = ((int)(viddef.width / scale) / 8) - 2; width = width > MAXCMDLINE - 2 ? MAXCMDLINE - 2 : width; 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 */ #ifdef USE_CURL if (cls.downloadname[0] && (cls.download || cls.downloadposition)) #else if (cls.download) #endif { 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_8_40/src/client/cl_download.c000066400000000000000000000446121465112212000206160ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; // Forces all downloads to UDP. static qboolean forceudp; // Gives HTTP downloads a second chance after // we've fallen trough to UDP downloads. static qboolean httpSecondChance = true; /* This - and some more code down below - is the 'Crazy Fallback Magic'. First we're trying to download all files over HTTP with r1q2-style URLs. If we encountered errors we reset the complete precacher state and retry with HTTP and q2pro-style URLs. If we still got errors we're falling back to UDP. So: - 0: Virgin state, r1q2-style URLs. - 1: Second iteration, q2pro-style URL. - 3: Third iteration, UDP downloads. */ static unsigned int precacherIteration; /* Another quirk: Don't restart texture downloading from the beginning, instead continue after the last requested texture. This is used to skip over a texture missing on the server. */ static qboolean dont_restart_texture_stage; // r1q2 searches the global filelist at /, q2pro at /gamedir... static qboolean gamedirForFilelist; 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 (precacherIteration == 0) { #if USE_CURL // r1q2-style URLs. Q_strlcpy(dlquirks.gamedir, cl.gamedir, sizeof(dlquirks.gamedir)); #endif } else if (precacherIteration == 1) { #if USE_CURL // q2pro-style URLs. if (cl.gamedir[0] == '\0') { Q_strlcpy(dlquirks.gamedir, BASEDIRNAME, sizeof(dlquirks.gamedir)); } else { Q_strlcpy(dlquirks.gamedir, cl.gamedir, sizeof(dlquirks.gamedir)); } // Force another try with the filelist. dlquirks.filelist = true; gamedirForFilelist = true; #endif } else if (precacherIteration == 2) { // UDP Fallback. forceudp = true; } else { // Cannot get here. assert(1 && "Recursed from UDP fallback case"); } 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; } #ifdef USE_CURL /* Wait for the models to download before checking * skins. */ if (CL_PendingHTTPDownloads()) { return; } #endif /* 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; } } } } #ifdef USE_CURL /* Wait for pending downloads. */ if (CL_PendingHTTPDownloads()) { return; } if (dlquirks.error) { dlquirks.error = false; /* Mkay, there were download errors. Let's start over. */ precacherIteration++; CL_ResetPrecacheCheck(); CL_RequestNextDownload(); return; } #endif /* precache phase completed */ if (!dont_restart_texture_stage) { 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; } #ifdef USE_CURL /* Wait for pending downloads. */ if (CL_PendingHTTPDownloads()) { return; } #endif /* This map is done, start over for next map. */ forceudp = false; precacherIteration = 0; gamedirForFilelist = false; httpSecondChance = true; dont_restart_texture_stage = false; #ifdef USE_CURL dlquirks.filelist = true; #endif 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 a file is filtered and * should not be downloaded, false otherwise. */ static qboolean CL_DownloadFilter(const char *filename) { if (FS_LoadFile((char *) filename, NULL) != -1) { /* it exists, no need to download */ return true; } if (strstr(filename, "..") || strchr(filename, ':') || (*filename == '.') || (*filename == '/')) { Com_Printf("Refusing to download a path containing '..' or ':' or starting with '.' or '/': %s\n", filename); return true; } if (strstr(filename, ".dll") || strstr(filename, ".dylib") || strstr(filename, ".so")) { Com_Printf("Refusing to download a path containing '.dll', '.dylib' or '.so': %s\n", filename); return true; } char *nodownload = strdup(cl_nodownload_list->string); char *nodownload_token = strtok(nodownload, " "); while (nodownload_token != NULL) { Com_Printf("Token: %s\n", nodownload_token); if (Q_strcasestr(filename, nodownload_token)) { Com_Printf("Filename is filtered by cl_nodownload_list: %s\n", filename); free(nodownload); return true; } nodownload_token = strtok(NULL, " "); } free(nodownload); return false; } /* * 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 (CL_DownloadFilter(filename)) { return true; } #ifdef USE_CURL if (!forceudp) { if (CL_QueueHTTPDownload(filename, gamedirForFilelist)) { /* We return true so that the precache check keeps feeding us more files. Since we have multiple HTTP connections we want to minimize latency and be constantly sending requests, not one at a time. */ return true; } } else { /* There're 2 cases: - forceudp was set after a 404. In this case we want to retry that single file over UDP and all later files over HTTP. - forceudp was set after another error code. In that case the HTTP code aborts all HTTP downloads and CL_QueueHTTPDownload() returns false. */ forceudp = false; /* This is one of the nasty special cases. A r1q2 server might miss only one file. This missing file may lead to a fallthrough to q2pro URLs, since it isn't a q2pro server all files would yield error 404 and we're falling back to UDP downloads. To work around this we need to start over with the r1q2 case and see what happens. But we can't do that unconditionally, because we would run in endless loops r1q2 -> q2pro -> UDP -> r1q2. So hack in a variable that allows for one and only one second chance. If the r1q2 server is missing more than file we've lost and we're doing unnecessary UDP downloads. */ if (httpSecondChance) { precacherIteration = 0; httpSecondChance = false; } } #endif 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 = Q_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 (CL_DownloadFilter(filename)) { 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) { char name[MAX_OSPATH]; int r, percent, size; static qboolean second_try; /* 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; } if (second_try) { precache_check++; dont_restart_texture_stage = true; second_try = false; } else { second_try = true; } CL_RequestNextDownload(); return; } second_try = false; /* open the file if not opened yet */ if (!cls.download) { CL_DownloadFileName(name, sizeof(name), cls.downloadtempname); FS_CreatePath(name); cls.download = Q_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 = Sys_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_8_40/src/client/cl_effects.c000066400000000000000000001622431465112212000204270ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/client/cl_entities.c000066400000000000000000000454261465112212000206370ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; 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) { ent.alpha = cl_laseralpha->value; if (ent.alpha < 0.0f) { ent.alpha = 0.0f; } else if (ent.alpha > 1.0f) { ent.alpha = 1.0f; } /* the four beam colors are encoded in 32 bits of skinnum (hack) */ 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); if (cl_r1q2_lightstyle->value) { V_AddLight(ent.origin, 200, 1, 0.23f, 0); } else { V_AddLight(ent.origin, 200, 1, 1, 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); } } if (cl_kickangles->value) { 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_8_40/src/client/cl_input.c000066400000000000000000000352601465112212000201450ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 "input/header/input.h" cvar_t *cl_nodelta; 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 ((cls.realtime > abort_cinematic) && (cl.cinematictime > 0) && !cl.attractloop && (cls.realtime - cl.cinematictime > 1000) && (cls.key_dest == key_game)) { /* 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_8_40/src/client/cl_inventory.c000066400000000000000000000062021465112212000210350ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; char string[1024]; int x, y; char binding[1024]; const char *bind; int selected; int top; int index[MAX_ITEMS] = {0}; 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 < K_LAST; j++) { if (keybindings[j] && !Q_stricmp(keybindings[j], binding)) { bind = Key_KeynumToString(j); break; } } Com_sprintf(string, sizeof(string), "%6.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_8_40/src/client/cl_keyboard.c000066400000000000000000000674171465112212000206170ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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}, {"SEMICOLON", ';'}, /* because a raw semicolon separates commands */ {"DOUBLEQUOTE", '"'}, /* because "" has special meaning in configs */ {"QUOTE", '\'' }, /* just to be sure */ {"DOLLAR", '$'}, /* $ is used in macros => can occur in configs */ {"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}, {"INS", K_INS}, {"DEL", K_DEL}, {"PGDN", K_PGDN}, {"PGUP", K_PGUP}, {"HOME", K_HOME}, {"END", K_END}, {"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}, {"F13", K_F13}, {"F14", K_F14}, {"F15", K_F15}, {"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}, {"KP_NUMLOCK", K_KP_NUMLOCK}, {"KP_STAR", K_KP_STAR}, {"KP_EQUALS", K_KP_EQUALS}, {"MOUSE1", K_MOUSE1}, {"MOUSE2", K_MOUSE2}, {"MOUSE3", K_MOUSE3}, {"MOUSE4", K_MOUSE4}, {"MOUSE5", K_MOUSE5}, {"MWHEELUP", K_MWHEELUP}, {"MWHEELDOWN", K_MWHEELDOWN}, {"BTN_A", K_BTN_A}, {"BTN_B", K_BTN_B}, {"BTN_X", K_BTN_X}, {"BTN_Y", K_BTN_Y}, {"STICK_LEFT", K_STICK_LEFT}, {"STICK_RIGHT", K_STICK_RIGHT}, {"SHOULDR_LEFT", K_SHOULDER_LEFT}, {"SHOULDR_RIGHT", K_SHOULDER_RIGHT}, {"TRIG_LEFT", K_TRIG_LEFT}, {"TRIG_RIGHT", K_TRIG_RIGHT}, {"DP_UP", K_DPAD_UP}, {"DP_DOWN", K_DPAD_DOWN}, {"DP_LEFT", K_DPAD_LEFT}, {"DP_RIGHT", K_DPAD_RIGHT}, {"PADDLE_1", K_PADDLE_1}, {"PADDLE_2", K_PADDLE_2}, {"PADDLE_3", K_PADDLE_3}, {"PADDLE_4", K_PADDLE_4}, {"BTN_MISC1", K_BTN_MISC1}, {"TOUCHPAD", K_TOUCHPAD}, {"BTN_BACK", K_BTN_BACK}, {"BTN_GUIDE", K_BTN_GUIDE}, {"BTN_START", K_BTN_START}, // virtual keys you get by pressing the corresponding normal joy key // and the altselector key {"BTN_A_ALT", K_BTN_A_ALT}, {"BTN_B_ALT", K_BTN_B_ALT}, {"BTN_X_ALT", K_BTN_X_ALT}, {"BTN_Y_ALT", K_BTN_Y_ALT}, {"STICK_LEFT_ALT", K_STICK_LEFT_ALT}, {"STICK_RIGHT_ALT", K_STICK_RIGHT_ALT}, {"SHOULDR_LEFT_ALT", K_SHOULDER_LEFT_ALT}, {"SHOULDR_RIGHT_ALT", K_SHOULDER_RIGHT_ALT}, {"TRIG_LEFT_ALT", K_TRIG_LEFT_ALT}, {"TRIG_RIGHT_ALT", K_TRIG_RIGHT_ALT}, {"DP_UP_ALT", K_DPAD_UP_ALT}, {"DP_DOWN_ALT", K_DPAD_DOWN_ALT}, {"DP_LEFT_ALT", K_DPAD_LEFT_ALT}, {"DP_RIGHT_ALT", K_DPAD_RIGHT_ALT}, {"PADDLE_1_ALT", K_PADDLE_1_ALT}, {"PADDLE_2_ALT", K_PADDLE_2_ALT}, {"PADDLE_3_ALT", K_PADDLE_3_ALT}, {"PADDLE_4_ALT", K_PADDLE_4_ALT}, {"BTN_MISC1_ALT", K_BTN_MISC1_ALT}, {"TOUCHPAD_ALT", K_TOUCHPAD_ALT}, {"BTN_BACK_ALT", K_BTN_BACK_ALT}, {"BTN_GUIDE_ALT", K_BTN_GUIDE_ALT}, {"BTN_START_ALT", K_BTN_START_ALT}, {"JOY_BACK", K_JOY_BACK}, {"SUPER", K_SUPER}, {"COMPOSE", K_COMPOSE}, {"MODE", K_MODE}, {"HELP", K_HELP}, {"PRINT", K_PRINT}, {"SYSREQ", K_SYSREQ}, {"SCROLLOCK", K_SCROLLOCK}, {"MENU", K_MENU}, {"UNDO", K_UNDO}, // entries for the mapped scancodes, see comment above K_SC_A in keyboard.h #define MY_SC_ENTRY(X) { #X , K_ ## X } // { "SC_A", K_SC_A }, MY_SC_ENTRY(SC_A), MY_SC_ENTRY(SC_B), MY_SC_ENTRY(SC_C), MY_SC_ENTRY(SC_D), MY_SC_ENTRY(SC_E), MY_SC_ENTRY(SC_F), MY_SC_ENTRY(SC_G), MY_SC_ENTRY(SC_H), MY_SC_ENTRY(SC_I), MY_SC_ENTRY(SC_J), MY_SC_ENTRY(SC_K), MY_SC_ENTRY(SC_L), MY_SC_ENTRY(SC_M), MY_SC_ENTRY(SC_N), MY_SC_ENTRY(SC_O), MY_SC_ENTRY(SC_P), MY_SC_ENTRY(SC_Q), MY_SC_ENTRY(SC_R), MY_SC_ENTRY(SC_S), MY_SC_ENTRY(SC_T), MY_SC_ENTRY(SC_U), MY_SC_ENTRY(SC_V), MY_SC_ENTRY(SC_W), MY_SC_ENTRY(SC_X), MY_SC_ENTRY(SC_Y), MY_SC_ENTRY(SC_Z), MY_SC_ENTRY(SC_MINUS), MY_SC_ENTRY(SC_EQUALS), MY_SC_ENTRY(SC_LEFTBRACKET), MY_SC_ENTRY(SC_RIGHTBRACKET), MY_SC_ENTRY(SC_BACKSLASH), MY_SC_ENTRY(SC_NONUSHASH), MY_SC_ENTRY(SC_SEMICOLON), MY_SC_ENTRY(SC_APOSTROPHE), MY_SC_ENTRY(SC_GRAVE), // console key MY_SC_ENTRY(SC_COMMA), MY_SC_ENTRY(SC_PERIOD), MY_SC_ENTRY(SC_SLASH), MY_SC_ENTRY(SC_NONUSBACKSLASH), MY_SC_ENTRY(SC_INTERNATIONAL1), MY_SC_ENTRY(SC_INTERNATIONAL2), MY_SC_ENTRY(SC_INTERNATIONAL3), MY_SC_ENTRY(SC_INTERNATIONAL4), MY_SC_ENTRY(SC_INTERNATIONAL5), MY_SC_ENTRY(SC_INTERNATIONAL6), MY_SC_ENTRY(SC_INTERNATIONAL7), MY_SC_ENTRY(SC_INTERNATIONAL8), MY_SC_ENTRY(SC_INTERNATIONAL9), MY_SC_ENTRY(SC_THOUSANDSSEPARATOR), MY_SC_ENTRY(SC_DECIMALSEPARATOR), MY_SC_ENTRY(SC_CURRENCYUNIT), MY_SC_ENTRY(SC_CURRENCYSUBUNIT), #undef MY_SC_ENTRY {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; } void CompleteMapNameCommand(void) { int i; char *s, *t, *cmdArg; const char *mapCmdString = "map "; s = key_lines[edit_line] + 1; if ((*s == '\\') || (*s == '/')) { s++; } t = s; for (i = 0; i < strlen(mapCmdString); i++) { if (t[i] == mapCmdString[i]) { s++; } else { return; } } cmdArg = Cmd_CompleteMapCommand(s); if (cmdArg) { key_lines[edit_line][1] = '/'; strcpy(key_lines[edit_line] + 2, mapCmdString); key_linepos = strlen(key_lines[edit_line]); strcpy(key_lines[edit_line] + key_linepos, cmdArg); key_linepos = key_linepos + strlen(cmdArg); } } /* * 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_lines[edit_line][1] = '\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(); CompleteMapNameCommand(); 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); } memmove(key_lines[edit_line], key_lines[history_line], sizeof(key_lines[edit_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 { memmove(key_lines[edit_line], key_lines[history_line], sizeof(key_lines[edit_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) && keynum != ';' && keynum != '"' && keynum != '\'' && keynum != '$') { /* printable ASCII, except for special cases that have special meanings in configs like quotes or ; (separates commands) or $ (used to expand cvars to their values in macros/commands) and thus need escaping */ 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 = Q_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_STAR] = true; consolekeys[K_KP_PLUS] = true; consolekeys[K_KP_MINUS] = true; consolekeys[K_KP_5] = true; consolekeys[K_MWHEELUP] = true; consolekeys[K_MWHEELDOWN] = true; consolekeys[K_MOUSE4] = true; consolekeys[K_MOUSE5] = 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); } void Key_Shutdown(void) { int i; for (i = 0; i < K_LAST; ++i) { if (keybindings[i]) { Z_Free(keybindings[i]); keybindings[i] = NULL; } } } /* * 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) { 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(); // evil hack for the joystick key altselector, which turns K_BTN_x into K_BTN_x_ALT if(joy_altselector_pressed && key >= K_JOY_FIRST_REGULAR && key <= K_JOY_LAST_REGULAR) { // make sure key is not the altselector itself (which we won't turn into *_ALT) if(keybindings[key] == NULL || strcmp(keybindings[key], "+joyaltselector") != 0) { int altkey = key + (K_JOY_FIRST_REGULAR_ALT - K_JOY_FIRST_REGULAR); // allow fallback to binding with non-alt key if(keybindings[altkey] != NULL || keybindings[key] == NULL) key = altkey; } } /* Track if key is down */ keydown[key] = down; /* Evil hack against spurious cinematic aborts. */ if (down && (key != K_ESCAPE) && !keydown[K_SHIFT]) { abort_cinematic = cls.realtime; } /* 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"); Cbuf_AddText("vid_restart\n"); } else { Cvar_Set("vid_fullscreen", "0"); Cbuf_AddText("vid_restart\n"); } return; } /* Toogle console through Shift + Escape or special K_CONSOLE key */ if (key == K_CONSOLE || (keydown[K_SHIFT] && key == K_ESCAPE)) { if (down) { Con_ToggleConsole_f(); } return; } /* Key is unbound */ if ((key >= K_MOUSE1 && key != K_JOY_BACK) && !keybindings[key] && (cls.key_dest != key_console) && (cls.state == ca_active)) { Com_Printf("%s (%d) is unbound, hit F4 to set.\n", Key_KeynumToString(key), 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) returns above). */ /* 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_8_40/src/client/cl_lights.c000066400000000000000000000075011465112212000202750ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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) { 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; } /* Vanilla Quake II had just cl.time. This worked in 1997 when computers were slow and the game reached ~30 FPS on beefy hardware. Nowadays with 1000 FPS the dlights are often rendered just a fraction of a frame. Work around that by adding 32 ms, e.g. each dlight is shown for at least 32 ms. */ if (dl->die < cl.time - 32) { dl->radius = 0; continue; } 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_8_40/src/client/cl_main.c000066400000000000000000000500141465112212000177240ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 "input/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 *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_showfps; cvar_t *cl_showspeed; 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_kickangles; cvar_t *cl_laseralpha; cvar_t *cl_nodownload_list; cvar_t *cl_shownet; cvar_t *cl_showmiss; cvar_t *cl_showclamp; cvar_t *cl_paused; cvar_t *cl_loadpaused; cvar_t *cl_audiopaused; cvar_t *cl_unpaused_scvis; cvar_t *cl_lightlevel; cvar_t *cl_r1q2_lightstyle; cvar_t *cl_limitsparksounds; /* userinfo */ cvar_t *name; cvar_t *skin; cvar_t *rate; cvar_t *fov; cvar_t *horplus; cvar_t *windowed_mouse; cvar_t *hand; cvar_t *gender; cvar_t *gender_auto; cvar_t *gl1_stereo; cvar_t *gl1_stereo_separation; cvar_t *gl1_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; /* Used to communicate if we entered paused mode during server connect or if we were already in it. */ qboolean paused_at_load; 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 = Q_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; Q_strlcpy(buffer, Cmd_Argv(1), sizeof(buffer)); Q_strlcat(buffer, "=", sizeof(buffer)); for (i = 2; i < argc; i++) { Q_strlcat(buffer, Cmd_Argv(i), sizeof(buffer)); Q_strlcat(buffer, " ", sizeof(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); if (Cvar_VariableValue("ogg_pausewithgame") == 1 && OGG_Status() == PAUSE && cl_paused->value == 0) /* play music */ { Cbuf_AddText("ogg toggle\n"); } else if (Cvar_VariableValue("ogg_pausewithgame") == 1 && OGG_Status() == PLAY && cl_paused->value == 1) /* pause music */ { Cbuf_AddText("ogg toggle\n"); } } 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(); IN_Update(); /* 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) { OGG_SaveState(); S_Shutdown(); S_Init(); CL_RegisterSounds(); OGG_InitTrackList(); OGG_RecoverState(); } int precache_check; int precache_spawncount; int precache_tex; int precache_model_skin; byte *precache_model; void CL_ResetPrecacheCheck (void) { precache_check = CS_MODELS; precache_model = 0; precache_model_skin = 0; } /* * 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_CurrentMap_f(void) { Com_Printf("%s\n", cl.configstrings[CS_MODELS + 1]); } 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_kickangles = Cvar_Get("cl_kickangles", "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_showfps = Cvar_Get("cl_showfps", "0", CVAR_ARCHIVE); cl_showspeed = Cvar_Get("cl_showspeed", "0", CVAR_ARCHIVE); cl_laseralpha = Cvar_Get("cl_laseralpha", "0.3", 0); cl_nodownload_list = Cvar_Get("cl_nodownload_list", "", 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); cl_shownet = Cvar_Get("cl_shownet", "0", 0); cl_showmiss = Cvar_Get("cl_showmiss", "0", 0); cl_showclamp = Cvar_Get("showclamp", "0", 0); cl_timeout = Cvar_Get("cl_timeout", "120", 0); cl_paused = Cvar_Get("paused", "0", 0); cl_loadpaused = Cvar_Get("cl_loadpaused", "1", CVAR_ARCHIVE); cl_audiopaused = Cvar_Get("cl_audiopaused", "1", CVAR_ARCHIVE); cl_unpaused_scvis = Cvar_Get("cl_unpaused_scvis", "1", CVAR_ARCHIVE); gl1_stereo = Cvar_Get( "gl1_stereo", "0", CVAR_ARCHIVE ); gl1_stereo_separation = Cvar_Get( "gl1_stereo_separation", "1", CVAR_ARCHIVE ); gl1_stereo_convergence = Cvar_Get( "gl1_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("r_lightlevel", "0", 0); cl_r1q2_lightstyle = Cvar_Get("cl_r1q2_lightstyle", "1", CVAR_ARCHIVE); cl_limitsparksounds = Cvar_Get("cl_limitsparksounds", "0", CVAR_ARCHIVE); /* 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); 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); #ifdef USE_CURL cl_http_proxy = Cvar_Get("cl_http_proxy", "", 0); cl_http_filelists = Cvar_Get("cl_http_filelists", "1", 0); cl_http_downloads = Cvar_Get("cl_http_downloads", "1", CVAR_ARCHIVE); cl_http_max_connections = Cvar_Get("cl_http_max_connections", "4", 0); cl_http_show_dw_progress = Cvar_Get("cl_http_show_dw_progress", "0", 0); cl_http_bw_limit_rate = Cvar_Get("cl_http_bw_limit_rate", "0", 0); cl_http_bw_limit_tmout = Cvar_Get("cl_http_bw_limit_tmout", "0", 0); #endif /* 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); Cmd_AddCommand("currentmap", CL_CurrentMap_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); Cmd_AddCommand("listentities", NULL); Cmd_AddCommand("teleport", NULL); Cmd_AddCommand("spawnentity", NULL); Cmd_AddCommand("spawnonstart", NULL); Cmd_AddCommand("cycleweap", 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 = Q_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"}, {"r_drawworld", "1"}, {"cl_testlights", "0"}, {"r_fullbright", "0"}, {"gl_drawflat", "0"}, {"paused", "0"}, {"fixedtime", "0"}, {"sw_draworder", "0"}, {"r_lightmap", "0"}, {"gl_saturatelighting", "0"}, {"cl_kickangles", "1"}, {"g_footsteps", "1"}, {"g_machinegun_norecoil", "0"}, {"g_swap_speed", "1"}, {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; } } // Run HTTP downloads more often while connecting. #ifdef USE_CURL if (cls.state == ca_connected) { CL_RunHTTPDownloads(); } #endif // Update input stuff. if (packetframe || renderframe) { CL_ReadPackets(); CL_UpdateWindowedMouse(); IN_Update(); 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(); // Run HTTP downloads during game. #ifdef USE_CURL CL_RunHTTPDownloads(); #endif } 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); /* 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(); #ifdef USE_CURL CL_InitHTTPDownloads(); #endif cls.disable_screen = true; /* don't draw yet */ CL_InitLocal(); Cbuf_Execute(); Key_ReadConsoleHistory(); } void CL_Shutdown(void) { static qboolean isdown = false; if (isdown) { printf("recursive shutdown\n"); return; } isdown = true; #ifdef USE_CURL CL_HTTP_Cleanup(true); #endif CL_WriteConfiguration(); Key_WriteConsoleHistory(); OGG_Stop(); S_Shutdown(); IN_Shutdown(); VID_Shutdown(); } yquake2-QUAKE2_8_40/src/client/cl_network.c000066400000000000000000000365551465112212000205070ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 *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[256]; if (Cmd_Argc() != 2) { Com_Printf("usage: connect \n"); return; } Q_strlcpy(server, Cmd_Argv(1), sizeof(server)); 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); } 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); } void CL_WriteConfiguration(void); /* * 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(); OGG_Stop(); 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; } #ifdef USE_CURL CL_CancelHTTPDownloads(true); cls.downloadReferer[0] = 0; cls.downloadname[0] = 0; cls.downloadposition = 0; #endif cls.state = ca_disconnected; snd_is_underwater = false; // save config for old game/mod CL_WriteConfiguration(); // we disconnected, so revert to default game/mod (might have been different mod on MP server) Cvar_Set("game", userGivenGame); } 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"); #ifdef USE_CURL if (cls.downloadServerRetry[0] != 0) { CL_SetHTTPServer(cls.downloadServerRetry); } #endif } /* * 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]; const char *adrstring; cvar_t *noudp; cvar_t *noipx; memset(&adr, 0, sizeof(adr)); 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 = 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); char *buff = NET_AdrToString(cls.netchan.remote_address); for(int i = 1; i < Cmd_Argc(); i++) { char *p = Cmd_Argv(i); if(!strncmp(p, "dlserver=", 9)) { #ifdef USE_CURL p += 9; Com_sprintf(cls.downloadReferer, sizeof(cls.downloadReferer), "quake2://%s", buff); CL_SetHTTPServer (p); if (cls.downloadServer[0]) { Com_Printf("HTTP downloading enabled, URL: %s\n", cls.downloadServer); } #else Com_Printf("HTTP downloading supported by server but not the client.\n"); #endif } } /* Put client into pause mode when connecting to a local server. This prevents the world from being forwarded while the client is connecting, loading assets, etc. It's not 100%, there're still 4 world frames (for baseq2) processed in the game and 100 frames by the server if the player enters a level that he or she already visited. In practise both shouldn't be a big problem. 4 frames are hardly enough for monsters staring to attack and in most levels the starting area in unreachable by monsters and free from environmental effects. Com_Serverstate() returns 2 if the server is local and we're running a real game and no timedemo, cinematic, etc. The 2 is taken from the server_state_t enum value 'ss_game'. If it's a local server, maxclients aus either 0 (for single player), or 2 to 8 (coop and deathmatch) if we're reaching this code. For remote servers it's always 1. So this should trigger only if it's a local single player server. Since the player can load savegames from a paused state (e.g. through the console) we'll need to communicate if we entered paused mode (and it should left as soon as the player joined the server) or if it was already there. Last but not least this can be disabled by cl_loadpaused 0. */ if (Com_ServerState() == 2 && (Cvar_VariableValue("maxclients") <= 1)) { if (cl_loadpaused->value) { if (!cl_paused->value) { paused_at_load = true; Cvar_Set("paused", "1"); } } } 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_8_40/src/client/cl_parse.c000066400000000000000000000700101465112212000201100ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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" #include "input/header/input.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]); IN_Update(); } 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 & (1u << 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 & (1u << 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; if (paused_at_load) { if (cl_loadpaused->value == 1) { Cvar_Set("paused", "0"); } paused_at_load = false; } } /* 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; /* Clear all key states */ In_FlushQueue(); 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) { int track = (int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10); OGG_PlayTrack(track, true, true); } } 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_8_40/src/client/cl_particles.c000066400000000000000000000122351465112212000207710ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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) { if (cl_numparticles == 0) return; 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_8_40/src/client/cl_prediction.c000066400000000000000000000147171465112212000211520ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; } } } } 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 is used for movement prediction on stairs // (so moving up/down stairs is smooth) step = pm.s.origin[2] - (int)(cl.predicted_origin[2] * 8); VectorCopy(pm.s.velocity, tmp); if (((step > 62 && step < 66) || (step > 94 && step < 98) || (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_8_40/src/client/cl_screen.c000066400000000000000000001042341465112212000202630ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 *r_hudscale; /* named for consistency with R1Q2 */ cvar_t *r_consolescale; cvar_t *r_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_showfps; extern cvar_t *crosshair_scale; extern cvar_t *cl_showspeed; extern float GetPlayerSpeed(float *, float *); 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); r_hudscale = Cvar_Get("r_hudscale", "-1", CVAR_ARCHIVE); r_consolescale = Cvar_Get("r_consolescale", "-1", CVAR_ARCHIVE); r_menuscale = Cvar_Get("r_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) { /* src_conspeed must be a positiv integer, otherwise things go wrong. Clamp it. */ if (scr_conspeed->value < 0.1f) { Cvar_Set("scr_conspeed", "0.1"); } /* 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 */ OGG_Stop(); if (cls.disable_screen) { return; } if (developer->value) { /* Hack: When we are returning here (not drawing the loading plaque) we don't reset the palette later on. We might end up with the cinematic palette applied to the world. Enforce the world palette. */ if (cl.cinematictime > 0) { R_SetPalette(NULL); } 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 */ } /* This highly complicated pseudo damage tracking is very effective with the soft render, it gives about 10% speedup. On the GL renderers the speedup is negligible, but in introduce a bug: OpenGL requires to redraw the borders after every glClear(), which the damage tracking doesn't take into account. Fix this by not using the damage tracking when running somethinge else than the soft renderer. Just redraw the borders every frame. */ if (strcmp(vid_renderer->string, "soft") != 0) { // Top Draw_TileClear(scr_vrect.x, 0, scr_vrect.width, scr_vrect.y, "backtile"); // Bottom Draw_TileClear(scr_vrect.x, scr_vrect.y + scr_vrect.height, scr_vrect.width, viddef.height, "backtile"); // Left Draw_TileClear(0, 0, scr_vrect.x, viddef.height, "backtile"); // Right Draw_TileClear(scr_vrect.x + scr_vrect.width, 0, viddef.width, viddef.height, "backtile"); } else { /* 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 >= MAX_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][0] != '\0') { 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 { DrawStringScaled(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_STATS)) { 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_DrawSpeed(void) { if (cl_showspeed->value < 1) //Disabled, do nothing return; char spd_str[32]; float speed, speedxy; float scale = SCR_GetConsoleScale(); int str_len, xPos, yPos = 0; GetPlayerSpeed(&speed, &speedxy); snprintf(spd_str, sizeof(spd_str), "%6.2f (%6.2f) QU/s", speed, speedxy); str_len = scale * (strlen(spd_str) * 8 + 2); if (cl_showspeed->value == 1) //Draw speed and xy speed at top right { xPos = viddef.width - str_len; if (cl_showfps->value == 1 || cl_showfps->value == 2) // If showfps is enabled, draw it underneath { yPos = scale * 10; } else if (cl_showfps->value > 2) { yPos = scale * 20; } DrawStringScaled(xPos, yPos, spd_str, scale); SCR_AddDirtyPoint(xPos, yPos); SCR_AddDirtyPoint(viddef.width, yPos); } else if (cl_showspeed->value > 1) //Draw only xy speed under the crosshair { if (scale != 1) // Check if low resolution { scale -= 1; } snprintf(spd_str, sizeof(spd_str), "%6.2f", speedxy); str_len = scale * (strlen(spd_str) * 8 + 2); yPos = scr_vrect.y + (scr_vrect.height / 2) + (scale * 10); xPos = scr_vrect.x + (scr_vrect.width / 2) - (str_len / 2); DrawStringScaled(xPos, yPos, spd_str, scale); SCR_AddDirtyPoint(xPos, yPos); SCR_AddDirtyPoint(xPos + str_len, yPos); } } void SCR_Framecounter(void) { long long newtime; static int frame; static int frametimes[60] = {0}; static long long oldtime; /* skip statistics without show fps */ if (cl_showfps->value < 1) return; newtime = Sys_Microseconds(); frametimes[frame] = (int)(newtime - oldtime); oldtime = newtime; frame++; if (frame > 59) { frame = 0; } float scale = SCR_GetConsoleScale(); if (cl_showfps->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); SCR_AddDirtyPoint(viddef.width - scale*(strlen(str)*8 + 2), 0); SCR_AddDirtyPoint(viddef.width, 0); } else if (cl_showfps->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); SCR_AddDirtyPoint(viddef.width - scale*(strlen(str)*8 + 2), 0); SCR_AddDirtyPoint(viddef.width, 0); if (cl_showfps->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); SCR_AddDirtyPoint(viddef.width - scale*(strlen(str)*8 + 2), scale*10); SCR_AddDirtyPoint(viddef.width, scale+10); } } } // ---- /* * 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 ( gl1_stereo->value ) { numframes = 2; separation[0] = -gl1_stereo_separation->value / 2; separation[1] = +gl1_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; R_EndWorldRenderpass(); 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; } R_EndWorldRenderpass(); M_Draw(); } else if (cls.key_dest == key_console) { if (cl.cinematicpalette_active) { R_SetPalette(NULL); cl.cinematicpalette_active = false; } R_EndWorldRenderpass(); SCR_DrawConsole(); } else { R_EndWorldRenderpass(); 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(); SCR_DrawSpeed(); 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 (r_hudscale->value < 0) { scale = SCR_GetDefaultScale(); } else if (r_hudscale->value == 0) /* HACK: allow scale 0 to hide the HUD */ { scale = 0; } else { scale = SCR_ClampScale(r_hudscale->value); } return scale; } float SCR_GetConsoleScale(void) { float scale; if (!scr_initialized) { scale = 1; } else if (r_consolescale->value < 0) { scale = SCR_GetDefaultScale(); } else { scale = SCR_ClampScale(r_consolescale->value); } return scale; } float SCR_GetMenuScale(void) { float scale; if (!scr_initialized) { scale = 1; } else if (r_menuscale->value < 0) { scale = SCR_GetDefaultScale(); } else { scale = SCR_ClampScale(r_menuscale->value); } return scale; } yquake2-QUAKE2_8_40/src/client/cl_tempentities.c000066400000000000000000001140451465112212000215170ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * ======================================================================= * * This file implements all temporary (dynamic created) entities * * ======================================================================= */ #include "header/client.h" #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) { float alpha = cl_laseralpha->value; if (alpha < 0.0f) { alpha = 0.0f; } else if (alpha > 1.0f) { alpha = 1.0f; } l->ent.flags = RF_TRANSLUCENT | RF_BEAM; VectorCopy(start, l->ent.origin); VectorCopy(end, l->ent.oldorigin); l->ent.alpha = alpha; 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); } if (cl_limitsparksounds->value) { 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. * * This was fixed in openal-soft 0.19.0. We're keeping * the work around hidden behind a cvar and no longer * limited to OpenAL because a) some Linux distros may * still ship older openal-soft versions and b) some * player may like the changed behavior. */ if (num_power_sounds < 16) { S_StartSound(pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0); } } else { 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; 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|RF_WEAPONMODEL; // DG: fix rogue heatbeam high FOV rendering 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_8_40/src/client/cl_view.c000066400000000000000000000344661465112212000177670ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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" #include "input/header/input.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.0f * ((float)(i % 4) - 1.5f); f = (float)(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[MAX_QPATH]; 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(); IN_Update(); 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]); IN_Update(); } 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(); IN_Update(); 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 */ /* start the cd track */ int track = (int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10); OGG_PlayTrack(track, true, true); } 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) ? 1 : -1; } } void V_RenderView(float stereo_separation) { if (cls.state != ca_active) { R_EndWorldRenderpass(); return; } if (!cl.refresh_prepped) { R_EndWorldRenderpass(); return; // still loading } 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 && gl1_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("position: %i %i %i, angles: %i %i %i\n", (int)cl.refdef.vieworg[0], (int)cl.refdef.vieworg[1], (int)cl.refdef.vieworg[2], (int)cl.refdef.viewangles[PITCH], (int)cl.refdef.viewangles[YAW], (int)cl.refdef.viewangles[ROLL]); } 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_8_40/src/client/curl/000077500000000000000000000000001465112212000171235ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/curl/download.c000066400000000000000000000662561465112212000211150ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2018 Yamagi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * ======================================================================= * * Asset downloads over HTTP with CURL. * * ======================================================================= */ #include #include "../header/client.h" #include "header/qcurl.h" #ifdef USE_CURL cvar_t *cl_http_downloads; cvar_t *cl_http_filelists; cvar_t *cl_http_proxy; cvar_t *cl_http_max_connections; cvar_t *cl_http_show_dw_progress; cvar_t *cl_http_bw_limit_rate; cvar_t *cl_http_bw_limit_tmout; dlquirks_t dlquirks = { .error = false, .filelist = true, .gamedir = {'\0'} }; typedef enum { HTTPDL_ABORT_NONE, HTTPDL_ABORT_SOFT, HTTPDL_ABORT_HARD } http_abort_t; static CURLM *multi = NULL; static int handleCount = 0; static int pendingCount = 0; static int abortDownloads = HTTPDL_ABORT_NONE; static qboolean downloadingPak = false; static qboolean httpDown = false; #if defined(CURLOPT_XFERINFODATA) typedef curl_off_t CL_Progresstype; #define PROGRESSDATA CURLOPT_XFERINFODATA #define PROGRESSFUNCTION CURLOPT_XFERINFOFUNCTION #else typedef double CL_Progresstype; #define PROGRESSDATA CURLOPT_PROGRESSDATA #define PROGRESSFUNCTION CURLOPT_XFERINFOFUNCTION #endif // -------- // CURL callback functions // ----------------------- /* * In memory buffer for receiving files. Used * as special CURL callback for filelists. */ static size_t CL_HTTP_Recv(void *ptr, size_t size, size_t nmemb, void *stream) { dlhandle_t *dl = (dlhandle_t *)stream; size_t bytes = size * nmemb; if (!dl->fileSize) { double length = 0; qcurl_easy_getinfo(dl->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &length); // Mkay, the remote file should be at least one byte long. // Since this is used for paclists only we assume that the // file cannot be longer then 256k. Everythings else is // considered malicious or a broken server. if (length < 1 || length > 262144) { length = 262144.0f; } dl->fileSize = ceil(length) + 1; dl->tempBuffer = malloc(dl->fileSize); } else if (dl->position + bytes >= dl->fileSize) { dl->fileSize *= 2; char *tempBuffer = realloc(dl->tempBuffer, dl->fileSize); if (!tempBuffer) { free(dl->tempBuffer); dl->tempBuffer = 0; return 0; } dl->tempBuffer = tempBuffer; } memcpy (dl->tempBuffer + dl->position, ptr, bytes); dl->position += bytes; dl->tempBuffer[dl->position] = 0; return bytes; } static size_t CL_HTTP_CurlWriteCB(char* data, size_t size, size_t nmemb, void* userdata) { dlhandle_t *dl = (dlhandle_t *)userdata; return fwrite(data, size, nmemb, dl->file); } static int CL_HTTP_CurlProgressCB(void* ptr, CL_Progresstype total /* unused */, CL_Progresstype now, CL_Progresstype uptotal /* unused */, CL_Progresstype upnow /* unused */) { dlhandle_t *dl = (dlhandle_t *)ptr; if (now) { dl->fileDownloadedSize = (size_t)now; Com_DPrintf("CL_HTTP_CurlProgressCB: Downloaded " YQ2_COM_PRIdS "/" YQ2_COM_PRIdS "\n", dl->fileDownloadedSize, dl->fileSize); } return 0; } // -------- // Helper functions // ---------------- /* * Escapes a path with HTTP encoding. We can't use the * function provided by CURL because it encodes to many * characters and filter some characters needed by Quake * out. Oh Yeah. */ static void CL_EscapeHTTPPath(const char *filePath, char *escaped) { char *p = escaped; size_t len = strlen (filePath); for (int i = 0; i < len; i++) { if (!isalnum((unsigned char)filePath[i]) && filePath[i] != ';' && filePath[i] != '/' && filePath[i] != '?' && filePath[i] != ':' && filePath[i] != '@' && filePath[i] != '&' && filePath[i] != '=' && filePath[i] != '+' && filePath[i] != '$' && filePath[i] != ',' && filePath[i] != '[' && filePath[i] != ']' && filePath[i] != '-' && filePath[i] != '_' && filePath[i] != '.' && filePath[i] != '!' && filePath[i] != '~' && filePath[i] != '*' && filePath[i] != '\'' && filePath[i] != '(' && filePath[i] != ')') { sprintf(p, "%%%02x", filePath[i]); p += 3; } else { *p = filePath[i]; p++; } } p[0] = 0; // Using ./ in a url is legal, but all browsers condense // the path and some IDS / request filtering systems act // a bit funky if http requests come in with uncondensed // paths. len = strlen(escaped); p = escaped; while ((p = strstr (p, "./"))) { memmove (p, p+2, len - (p - escaped) - 1); len -= 2; } } /* * Removes an entry from the download queue. */ static qboolean CL_RemoveFromQueue(dlqueue_t *entry) { dlqueue_t *last = &cls.downloadQueue; dlqueue_t *cur = last->next; while (cur) { if (cur == entry) { last->next = cur->next; free(cur); cur = NULL; return true; } last = cur; cur = cur->next; } return false; } /* * Starts a download. Generates an URL, brings the * download handle into defined state and adds it * to the CURL multihandle. */ static void CL_StartHTTPDownload (dlqueue_t *entry, dlhandle_t *dl) { char tempFile[MAX_OSPATH]; char escapedFilePath[MAX_QPATH*4] = {0}; size_t len = strlen(entry->quakePath); if (len > 9 && !strncmp(entry->quakePath + len - 9, ".filelist", len)) { // Special case for filelists. The code identifies // filelist by the special handle NULL... dl->file = NULL; CL_EscapeHTTPPath(entry->quakePath, escapedFilePath); } else { // Full path to the local file. Com_sprintf (dl->filePath, sizeof(dl->filePath), "%s/%s", FS_Gamedir(), entry->quakePath); // Full path to the remote file. if (dlquirks.gamedir[0] == '\0') { Com_sprintf (tempFile, sizeof(tempFile), "/%s", entry->quakePath); } else { Com_sprintf (tempFile, sizeof(tempFile), "/%s/%s", dlquirks.gamedir, entry->quakePath); } CL_EscapeHTTPPath (tempFile, escapedFilePath); // Create a temporary file where the downloaded data is stored... Q_strlcat(dl->filePath, ".tmp", sizeof(dl->filePath)); FS_CreatePath (dl->filePath); // ...and put it into the download handle. dl->file = Q_fopen(dl->filePath, "wb"); if (!dl->file) { Com_Printf("HTTP download: Couldn't open %s for writing\n", dl->filePath); CL_RemoveFromQueue(entry); pendingCount--; return; } } // Make sure that the download handle is in empty state. dl->tempBuffer = NULL; dl->fileSize = 0; dl->position = 0; dl->fileDownloadedSize = 0; dl->queueEntry = entry; // Setup and configure the CURL part of our download handle. if (!dl->curl) { dl->curl = qcurl_easy_init(); } Com_sprintf(dl->URL, sizeof(dl->URL), "%s%s", cls.downloadServer, escapedFilePath); qcurl_easy_setopt(dl->curl, CURLOPT_ENCODING, ""); qcurl_easy_setopt(dl->curl, CURLOPT_WRITEDATA, dl); if (dl->file) { qcurl_easy_setopt(dl->curl, CURLOPT_WRITEFUNCTION, CL_HTTP_CurlWriteCB); } else { qcurl_easy_setopt(dl->curl, CURLOPT_WRITEFUNCTION, CL_HTTP_Recv); } qcurl_easy_setopt(dl->curl, CURLOPT_PROXY, cl_http_proxy->string); qcurl_easy_setopt(dl->curl, CURLOPT_LOW_SPEED_TIME, (long)cl_http_bw_limit_tmout->value); qcurl_easy_setopt(dl->curl, CURLOPT_LOW_SPEED_LIMIT, (long)cl_http_bw_limit_rate->value); qcurl_easy_setopt(dl->curl, CURLOPT_CONNECTTIMEOUT, 30); qcurl_easy_setopt(dl->curl, CURLOPT_FOLLOWLOCATION, 1); qcurl_easy_setopt(dl->curl, CURLOPT_MAXREDIRS, 5); qcurl_easy_setopt(dl->curl, CURLOPT_NOPROGRESS, (cl_http_show_dw_progress->value != 1.0)); qcurl_easy_setopt(dl->curl, PROGRESSDATA, dl); qcurl_easy_setopt(dl->curl, PROGRESSFUNCTION, CL_HTTP_CurlProgressCB); qcurl_easy_setopt(dl->curl, CURLOPT_USERAGENT, Cvar_VariableString ("version")); qcurl_easy_setopt(dl->curl, CURLOPT_REFERER, cls.downloadReferer); qcurl_easy_setopt(dl->curl, CURLOPT_URL, dl->URL); size_t ret; if ((ret = qcurl_multi_add_handle(multi, dl->curl)) != CURLM_OK) { Com_Printf("HTTP download: cURL error - %s\n", qcurl_easy_strerror(ret)); CL_RemoveFromQueue(entry); return; } handleCount++; Com_DPrintf("CL_StartHTTPDownload: Fetching %s...\n", dl->URL); dl->queueEntry->state = DLQ_STATE_RUNNING; } /* * Checks if a line given in a filelist is a * valid file and puts it into the download * queue. */ static void CL_CheckAndQueueDownload(char *path) { // NOTE: The original r1q2 download code in r1q2 allowed // only // paths made of plain ASCII chars. YQ2 is more // or less UTF-8 clean, so we're allowing all characters. // Malicious filelists may have very long lines. size_t length = strlen(path); if (length >= MAX_QPATH) { return; } // The good, old problem with Windows being case sensitive // and Unix platforms are not. We're asuming that the // the server admin did the right things(tm). That should // be more or less save since r1q2, q2pro and q2dos are // doing the same. But for the sake of readbility and the // general state of my brain we're doing the security // checks on an lower case copy of the path. char lowerPath[MAX_QPATH]; Q_strlcpy(lowerPath, path, sizeof(lowerPath)); Q_strlwr(lowerPath); // All files must have an extension, extensionsless // files just make no sense for Quake II. char *ext = strrchr(lowerPath, '.'); if (!ext) { return; } ext++; if (!ext[0]) { return; } // These file extensions must be in sync with // fs_packtypes in filesystem.c! qboolean pak = false; if (!strcmp (ext, "pak") || !strcmp (ext, "pk2") || !strcmp(ext, "pk3") || !strcmp(ext, "zip")) { pak = true; } // Filter out all filetypes not understood by Quake II. if (!pak && strcmp(ext, "pcx") && strcmp(ext, "wal") && strcmp(ext, "wav") && strcmp(ext, "md2") && strcmp(ext, "sp2") && strcmp(ext, "tga") && strcmp(ext, "png") && strcmp(ext, "jpg") && strcmp(ext, "bsp") && strcmp(ext, "ent") && strcmp(ext, "txt") && strcmp(ext, "dm2") && strcmp(ext, "loc")) { Com_Printf ("HTTP download: Illegal file type '%s' in filelist\n", ext); return; } // Files starting with @ are game local. For example the code // wouldn't download conchars.pcx because it already exists in // the global scope. If it's prefixed with @ it's downloaded // anyways if it doesn't exists in the current game dir. qboolean gameLocal = false; if (path[0] == '@') { if (pak) { Com_Printf("HTTP download: @ prefix used on a pak file '%s' in filelist\n", path); return; } gameLocal = true; path++; length--; } // Make sure that there're no evil paths in the filelist. Most // of these should be pretty okay with YQ2 since we've got a much // better filesystem as other clients but let's stay consistent. // // .. -> Don't change to upper dirs. // // -> Should be okay // \\ -> May fuck some string functions and CURL up. They // aren't valid URL characters anyways. // // !pak && !strchr (path, '/') -> Plain files are always loaded // from and into subdirs. // (pak && strchr(path, '/') -> Paks are always loaded from and // into the toplevel dir. if (strstr (path, "..") || strstr(path, "//") || strchr (path, '\\') || (!pak && !strchr(path, '/')) || (pak && strchr(path, '/'))) { Com_Printf("HTTP download: Illegal path '%s' in filelist\n", path); return; } // Let's see if we've already got that file. qboolean exists = false; if (gameLocal || pak) { if (pak) { // We need to check paks ourself. char gamePath[MAX_OSPATH]; Com_sprintf(gamePath, sizeof(gamePath),"%s/%s",FS_Gamedir(), path); FILE *f = Q_fopen(gamePath, "rb"); if (f) { exists = true; fclose(f); } } else { // All other files are checked by the filesystem. exists = FS_FileInGamedir(path); } if (!exists) { // Queue the file for download. CL_QueueHTTPDownload(path, false); } } else { // Check if the file is already there and download it if not. CL_CheckOrDownloadFile(path); } } /* * Parses a filelist referenced in the memory * buffer in the given dlhandle_t. */ static void CL_ParseFileList(dlhandle_t *dl) { if (!cl_http_filelists->value) { return; } char *list = dl->tempBuffer; for (;;) { char *p = strchr(list, '\n'); if (p) { p[0] = 0; if (list[0]) { CL_CheckAndQueueDownload(list); } list = p + 1; } else { if (list[0]) { CL_CheckAndQueueDownload(list); } break; } } free(dl->tempBuffer); dl->tempBuffer = NULL; } /* * A pak file was just downloaded. Go through the * download queue and check if we can cancel some * downloads. */ static void CL_ReVerifyHTTPQueue (void) { dlqueue_t *q = cls.downloadQueue.next; pendingCount = 0; while (q) { dlqueue_t *next = q->next; if (q->state == DLQ_STATE_NOT_STARTED) { if (FS_LoadFile (q->quakePath, NULL) != -1) { CL_RemoveFromQueue(q); } else { pendingCount++; } } q = next; } } /* * Processesall finished downloads. React on * errors, if there're none process the file. */ static void CL_FinishHTTPDownload(void) { CURL *curl; char tempName[MAX_OSPATH]; dlhandle_t *dl = NULL; int msgs_in_queue; qboolean isFile; size_t i; do { // Get a message from CURL. CURLMsg *msg = qcurl_multi_info_read(multi, &msgs_in_queue); if (!msg) { return; } if (msg->msg != CURLMSG_DONE) { continue; } // Find the download handle for the message. curl = msg->easy_handle; for (i = 0; i < MAX_HTTP_HANDLES; i++) { if (cls.HTTPHandles[i].curl == curl) { dl = &cls.HTTPHandles[i]; break; } } if (i == MAX_HTTP_HANDLES) { Com_Error(ERR_DROP, "CL_FinishHTTPDownload: Handle not found"); } // Some files aren't saved but read // into memory buffers. This is used // for filelists only. if (dl->file) { isFile = true; // Mkay, it's a file. Let's // close it's handle. fclose(dl->file); dl->file = NULL; } else { isFile = false; } // All downloads might have been aborted. // This is the case if the backend (or the // whole client) shuts down. if (pendingCount) { pendingCount--; } // The file finished downloading, it's // handle it's now empty and ready for // reuse. handleCount--; // Get the download result (success, some // error, etc.) from CURL and process it. CURLcode result = msg->data.result; long responseCode = 0; switch (result) { case CURLE_HTTP_RETURNED_ERROR: case CURLE_OK: qcurl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode); if (responseCode == 404) { Com_Printf("HTTP download: %s - File Not Found\n", dl->queueEntry->quakePath); // We got a 404, reset pak downloading state... size_t len = strlen(dl->queueEntry->quakePath); if (!strcmp(dl->queueEntry->quakePath + len - 4, ".pak") || !strcmp(dl->queueEntry->quakePath + len - 4, ".pk2") || !strcmp(dl->queueEntry->quakePath + len - 4, ".pk3") || !strcmp(dl->queueEntry->quakePath + len - 4, ".zip")) { downloadingPak = false; } // ...remove the target file... if (isFile) { Sys_Remove(dl->filePath); } // ...remove it from the CURL multihandle... qcurl_multi_remove_handle(multi, dl->curl); CL_RemoveFromQueue(dl->queueEntry); dl->queueEntry = NULL; // ...and communicate the error. if (isFile) { dlquirks.error = true; isFile = false; } break; } else if (responseCode == 200) { Com_Printf("HTTP download: %s - OK\n", dl->queueEntry->quakePath); // This wasn't a file, so it must be a filelist. if (!isFile && !abortDownloads) { CL_ParseFileList(dl); CL_RemoveFromQueue(dl->queueEntry); dl->queueEntry = NULL; } break; } // Everything that's not 200 and 404 is fatal, fall through. case CURLE_COULDNT_RESOLVE_HOST: case CURLE_COULDNT_CONNECT: case CURLE_COULDNT_RESOLVE_PROXY: Com_Printf("HTTP download: %s - Server broken, aborting\n", dl->queueEntry->quakePath); // The download failed. Reset pak downloading state... size_t len = strlen(dl->queueEntry->quakePath); if (!strcmp(dl->queueEntry->quakePath + len - 4, ".pak") || !strcmp(dl->queueEntry->quakePath + len - 4, ".pk2") || !strcmp(dl->queueEntry->quakePath + len - 4, ".pk3") || !strcmp(dl->queueEntry->quakePath + len - 4, ".zip")) { downloadingPak = false; } // remove the temporary file... if (isFile) { Sys_Remove(dl->filePath); isFile = false; } // ...and the handle from CURLs mutihandle. qcurl_multi_remove_handle(multi, dl->curl); // Special case: We're already aborting HTTP downloading, // so we can't just kill everything. Otherwise we'll get // stuck. if (abortDownloads) { CL_RemoveFromQueue(dl->queueEntry); dl->queueEntry = NULL; } // Abort all HTTP downloads. CL_CancelHTTPDownloads (true); CL_RemoveFromQueue(dl->queueEntry); dl->queueEntry = NULL; break; default: Com_Printf ("HTTP download: cURL error - %s\n", qcurl_easy_strerror(result)); // The download failed. Clear the Remove the temporary file... if (isFile) { Sys_Remove(dl->filePath); isFile = false; } // ...and the handle from CURLs mutihandle. qcurl_multi_remove_handle (multi, dl->curl); CL_RemoveFromQueue(dl->queueEntry); dl->queueEntry = NULL; break; } if (isFile) { // Rename the temporary file to it's final location Com_sprintf(tempName, sizeof(tempName), "%s/%s", FS_Gamedir(), dl->queueEntry->quakePath); Sys_Rename(dl->filePath, tempName); // Pak files are special because they contain // other files that we may be downloading... i = strlen(tempName); // The list of file types must be consistent with fs_packtypes in filesystem.c. if ( !strcmp (tempName + i - 4, ".pak") || !strcmp (tempName + i - 4, ".pk2") || !strcmp (tempName + i - 4, ".pk3") || !strcmp (tempName + i - 4, ".zip") ) { FS_AddPAKFromGamedir(dl->queueEntry->quakePath); CL_ReVerifyHTTPQueue (); downloadingPak = false; } CL_RemoveFromQueue(dl->queueEntry); dl->queueEntry = NULL; } // Remove the file fo CURLs multihandle. qcurl_multi_remove_handle (multi, dl->curl); } while (msgs_in_queue > 0); // No more downloads are in in flight, so... if (handleCount == 0) { if (abortDownloads == HTTPDL_ABORT_SOFT) { // ...if we're soft aborting we're done. abortDownloads = HTTPDL_ABORT_NONE; } else if (abortDownloads == HTTPDL_ABORT_HARD) { // ...if we're hard aborting we need to prevent future downloads. Q_strlcpy(cls.downloadServerRetry, cls.downloadServer, sizeof(cls.downloadServerRetry)); cls.downloadServer[0] = 0; } } // All downloads done. Let's check if we've got more files to // request. This can be the case if we've processed a filelist // or downloaded a BSP that references other assets. if (cls.state == ca_connected && !CL_PendingHTTPDownloads()) { CL_RequestNextDownload(); } } /* * Returns a free download handle. */ static dlhandle_t *CL_GetFreeDLHandle(void) { for (int i = 0; i < MAX_HTTP_HANDLES; i++) { dlhandle_t *dl = &cls.HTTPHandles[i]; if (!dl->queueEntry) { return dl; } } return NULL; } /* * Starts the next download. */ static void CL_StartNextHTTPDownload(void) { dlqueue_t *q = &cls.downloadQueue; while (q->next) { q = q->next; if (q->state == DLQ_STATE_NOT_STARTED) { dlhandle_t *dl = CL_GetFreeDLHandle(); if (!dl) { return; } CL_StartHTTPDownload(q, dl); size_t len = strlen(q->quakePath); if (!strcmp(q->quakePath + len - 4, ".pak") || !strcmp(q->quakePath + len - 4, ".pk2") || !strcmp(q->quakePath + len - 4, ".pk3") || !strcmp(q->quakePath + len - 4, ".zip")) { downloadingPak = true; } break; } } } // -------- // Startup and shutdown // -------------------- /* * Initializes CURL. */ void CL_InitHTTPDownloads (void) { // We're initializing the cURL backend here because // currently we're the only user. As soon as there // are other users this must be moved up into the // global client intialization. qcurlInit(); } /* * Resets the internal state and - in case * of full shutdown - shuts CURL down. */ void CL_HTTP_Cleanup(qboolean fullShutdown) { if (fullShutdown && httpDown) { return; } // Cleanup all internal handles. for (int i = 0; i < MAX_HTTP_HANDLES; i++) { dlhandle_t *dl = &cls.HTTPHandles[i]; if (dl->file) { fclose (dl->file); Sys_Remove (dl->filePath); dl->file = NULL; } if (dl->tempBuffer) { free(dl->tempBuffer); dl->tempBuffer = NULL; } if (dl->curl) { if (multi) { qcurl_multi_remove_handle(multi, dl->curl); } qcurl_easy_cleanup(dl->curl); dl->curl = NULL; } dl->queueEntry = NULL; } // Cleanup download queue. dlqueue_t *q = &cls.downloadQueue; dlqueue_t *last = NULL; while (q->next) { q = q->next; if (last) { free(last); last = NULL; } last = q; } if (last) { free(last); last = NULL; } // Cleanup CURL multihandle. if (multi) { qcurl_multi_cleanup(multi); multi = NULL; } // Shutdown CURL. if (fullShutdown) { // This must be moved up into the generic // client shutdown as soon as there're // other users of the cURL backend. qcurlShutdown(); httpDown = true; } } // -------- // Public functions // ---------------- /* * The server hand us a new HTTP URL. Nuke * the internal state to start over. */ void CL_SetHTTPServer (const char *URL) { dlqueue_t *last = NULL; dlqueue_t *q = &cls.downloadQueue; // This code abuses the download server setting to // determine if HTTP downloads are possible. So if // we don't set a download server, no downloads are // queued and run. :) if (!qcurlInitialized) { cls.downloadServer[0] = 0; return; } CL_HTTP_Cleanup(false); // Cleanup download queues. while (q->next) { q = q->next; if (last) { free(last); last = NULL; } last = q; } if (last) { free(last); last = NULL; } memset (&cls.downloadQueue, 0, sizeof(cls.downloadQueue)); // Cleanup internal state. abortDownloads = HTTPDL_ABORT_NONE; handleCount = pendingCount = 0; cls.downloadServerRetry[0] = 0; dlquirks.error = false; // Remove trailing / from URL if any. size_t urllen = strlen(URL); char *cleanURL = strdup(URL); YQ2_COM_CHECK_OOM(cleanURL, "strdup(URL)", strlen(URL)) if (cleanURL[urllen - 1] == '/') { cleanURL[urllen - 1] = '\0'; } Q_strlcpy(cls.downloadServer, cleanURL, sizeof(cls.downloadServer) - 1); free(cleanURL); // Initializes a new multihandle. if (multi) { Com_Error(ERR_DROP, "HTTP download: Still have old handle?!"); } multi = qcurl_multi_init(); } /* * Cancels all downloads and clears the queue. */ void CL_CancelHTTPDownloads(qboolean permKill) { if (permKill) { CL_ResetPrecacheCheck(); abortDownloads = HTTPDL_ABORT_HARD; } else { abortDownloads = HTTPDL_ABORT_SOFT; } dlqueue_t *q = cls.downloadQueue.next; while (q) { dlqueue_t *next = q->next; if (q->state == DLQ_STATE_NOT_STARTED) { CL_RemoveFromQueue(q); } q = next; } if (!pendingCount && !handleCount && abortDownloads == HTTPDL_ABORT_HARD) { cls.downloadServer[0] = 0; } pendingCount = 0; } /* * This function should be called from old UDP download code * during the precache phase to determine if HTTP downloads * for the requested files are possible. Queues the download * and returns true if yes, returns fales if not. */ qboolean CL_QueueHTTPDownload(const char *quakePath, qboolean gamedirForFilelist) { // Not HTTP servers were send by the server, HTTP is disabled // or the client is shutting down and we're wrapping up. if (!cls.downloadServer[0] || abortDownloads || !cl_http_downloads->value) { return false; } // Mkay, now that the first download is queued we want // the generic(!) filelist. qboolean needList = false; if (dlquirks.filelist && cl_http_filelists->value) { needList = true; dlquirks.filelist = false; } // Queue the download. dlqueue_t *q = &cls.downloadQueue; while (q->next) { q = q->next; // The generic code may request the same // file more than one time. *sigh* if (!strcmp(quakePath, q->quakePath)) { return true; } } q->next = malloc(sizeof(*q)); YQ2_COM_CHECK_OOM(q->next, "malloc(sizeof(*q))", sizeof(*q)) q = q->next; q->next = NULL; q->state = DLQ_STATE_NOT_STARTED; Q_strlcpy(q->quakePath, quakePath, sizeof(q->quakePath) - 1); // Let's download the generic filelist if necessary. if (needList) { if (gamedirForFilelist) { char fileList[MAX_OSPATH]; Com_sprintf(fileList, sizeof(fileList), "/%s%s", dlquirks.gamedir, ".filelist"); CL_QueueHTTPDownload(fileList, false); } else { CL_QueueHTTPDownload("/.filelist", false); } } // If we just queued a .bsp file ask for it's map // filelist. I don't think that this is good idea. // Cause a filelist can specifiy a .bsp that again // specifies a filelist... But r1q2 chose this way, // others followed and we have no choice but doing // the same. size_t len = strlen (quakePath); if (cl_http_filelists->value && len > 4 && !Q_stricmp((char *)(quakePath + len - 4), ".bsp")) { char listPath[MAX_OSPATH]; char filePath[MAX_OSPATH]; if (dlquirks.gamedir[0] == '\0') { Com_sprintf (filePath, sizeof(filePath), "/%s", quakePath); } else { Com_sprintf (filePath, sizeof(filePath), "/%s/%s", dlquirks.gamedir, quakePath); } COM_StripExtension (filePath, listPath); Q_strlcat(listPath, ".filelist", sizeof(listPath)); CL_QueueHTTPDownload(listPath, false); } // If we're here CL_FinishHTTPDownload() is guaranteed to be called. pendingCount++; Com_Printf("HTTP download: %s - Queued\n", q->quakePath); return true; } /* * Returns true if still downloads pending and false * if not. Used by the old UDP code during precache * phase to determine if it's necessary to wait for * outstanding download. */ qboolean CL_PendingHTTPDownloads(void) { if (!cls.downloadServer[0]) { return false; } return pendingCount + handleCount; } /* * Calls CURL to perform the actual downloads. * Must be called every frame, otherwise CURL * will starve. */ void CL_RunHTTPDownloads(void) { int newHandleCount; CURLMcode ret; // No HTTP server given or not initialized. if (!cls.downloadServer[0]) { return; } // Kick CURL into action. do { ret = qcurl_multi_perform(multi, &newHandleCount); if (newHandleCount < handleCount) { CL_FinishHTTPDownload(); handleCount = newHandleCount; } } while (ret == CURLM_CALL_MULTI_PERFORM); // Somethings gone very wrong. if (ret != CURLM_OK) { Com_Printf("HTTP download: cURL error - %s\n", qcurl_multi_strerror(ret)); CL_CancelHTTPDownloads(true); } // Not enough downloads running, start some more. if (pendingCount && abortDownloads == HTTPDL_ABORT_NONE && handleCount < cl_http_max_connections->value && !downloadingPak) { CL_StartNextHTTPDownload(); } } #endif /* USE_CURL */ yquake2-QUAKE2_8_40/src/client/curl/header/000077500000000000000000000000001465112212000203535ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/curl/header/download.h000066400000000000000000000044271465112212000223420ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 HTTP / cURL download stuff. * * ======================================================================= */ #ifdef USE_CURL #ifndef DOWNLOAD_H #define DOWNLOAD_H // Number of max. parallel downloads. #define MAX_HTTP_HANDLES 4 #include #include "../../../common/header/common.h" typedef enum { DLQ_STATE_NOT_STARTED, DLQ_STATE_RUNNING } dlq_state; typedef struct dlqueue_s { struct dlqueue_s *next; char quakePath[MAX_QPATH]; dlq_state state; } dlqueue_t; typedef struct dlhandle_s { CURL *curl; char filePath[MAX_OSPATH]; FILE *file; dlqueue_t *queueEntry; size_t fileSize; size_t position; size_t fileDownloadedSize; char URL[576]; char *tempBuffer; } dlhandle_t; typedef struct dlquirks_s { qboolean error; qboolean filelist; char gamedir[MAX_QPATH]; } dlquirks_t; extern dlquirks_t dlquirks; extern cvar_t *cl_http_downloads; extern cvar_t *cl_http_filelists; extern cvar_t *cl_http_proxy; extern cvar_t *cl_http_max_connections; extern cvar_t *cl_http_show_dw_progress; extern cvar_t *cl_http_bw_limit_rate; extern cvar_t *cl_http_bw_limit_tmout; void CL_CancelHTTPDownloads(qboolean permKill); void CL_InitHTTPDownloads(void); qboolean CL_QueueHTTPDownload(const char *quakePath, qboolean gamedirForFilelist); void CL_RunHTTPDownloads(void); qboolean CL_PendingHTTPDownloads(void); void CL_SetHTTPServer(const char *URL); void CL_HTTP_Cleanup(qboolean fullShutdown); #endif // DOWNLOAD_H #endif // USE_CURL yquake2-QUAKE2_8_40/src/client/curl/header/qcurl.h000066400000000000000000000043071465112212000216560ustar00rootroot00000000000000/* * Copyright (C) 2018 Yamagi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * ======================================================================= * * CURL backend for Quake II. * * ======================================================================= */ #ifdef USE_CURL #ifndef QCURL_H #define QCURL_H // -------- #include #include "../../../common/header/common.h" // -------- // True if cURL is initialized. extern qboolean qcurlInitialized; // Function pointers to cURL. extern void (*qcurl_easy_cleanup)(CURL *curl); extern CURL *(*qcurl_easy_init)(void); extern CURLcode (*qcurl_easy_getinfo)(CURL *curl, CURLINFO info, ...); extern CURLcode (*qcurl_easy_setopt)(CURL *curl, CURLoption option, ...); extern const char *(*qcurl_easy_strerror)(CURLcode); extern void (*qcurl_global_cleanup)(void); extern CURLcode (*qcurl_global_init)(long flags); extern CURLMcode (*qcurl_multi_add_handle)(CURLM *multi_handle, CURL *curl_handle); extern CURLMcode (*qcurl_multi_cleanup)(CURLM *multi_handle); extern CURLMsg *(*qcurl_multi_info_read)(CURLM *multi_handle, int *msgs_in_queue); extern CURLM *(*qcurl_multi_init)(void); extern CURLMcode (*qcurl_multi_perform)(CURLM *multi_handle, int *running_handles); extern CURLMcode (*qcurl_multi_remove_handle)(CURLM *multi_handle, CURL *curl_handle); extern const char *(*qcurl_multi_strerror)(CURLMcode); // -------- // Loads and initialized cURL. qboolean qcurlInit(void); // Shuts cURL down and unloads it. void qcurlShutdown(void); // -------- #endif // QCURL_H #endif // USE_CURL yquake2-QUAKE2_8_40/src/client/curl/qcurl.c000066400000000000000000000143411465112212000204200ustar00rootroot00000000000000/* * Copyright (C) 2018 Yamagi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * ======================================================================= * * CURL backend for Quake II. * * ======================================================================= */ #ifdef USE_CURL // -------- #include #include "header/qcurl.h" #include "../../common/header/common.h" // -------- // True if cURL is initialized. qboolean qcurlInitialized; // Pointer to the dynamic library. static void *curlhandle; // CVars static cvar_t *cl_libcurl; // Function pointers to cURL. void (*qcurl_easy_cleanup)(CURL *curl); CURL *(*qcurl_easy_init)(void); CURLcode (*qcurl_easy_getinfo)(CURL *curl, CURLINFO info, ...); CURLcode (*qcurl_easy_setopt)(CURL *curl, CURLoption option, ...); const char *(*qcurl_easy_strerror)(CURLcode); void (*qcurl_global_cleanup)(void); CURLcode (*qcurl_global_init)(long flags); CURLMcode (*qcurl_multi_add_handle)(CURLM *multi_handle, CURL *curl_handle); CURLMcode (*qcurl_multi_cleanup)(CURLM *multi_handle); CURLMsg *(*qcurl_multi_info_read)(CURLM *multi_handle, int *msgs_in_queue); CURLM *(*qcurl_multi_init)(void); CURLMcode (*qcurl_multi_perform)(CURLM *multi_handle, int *running_handles); CURLMcode (*qcurl_multi_remove_handle)(CURLM *multi_handle, CURL *curl_handle); const char *(*qcurl_multi_strerror)(CURLMcode); // -------- /* * Load libcurl, connect the function pointer * and call cURLs global init function. */ qboolean qcurlInit(void) { Com_Printf("------- curl initialization -------\n"); assert(!qcurlInitialized && "cURL already initialized?!"); // Most systems have only one distinct name for the // libcurl, loading that one will be successfull in // at least 99% of all cases. But Linux, the mother // of chaos, is more complicated. Debian / Ubuntu // alone has 6 possible names... So: // - Let's try whatever the user has written into // the cl_libcurl cvar first. // - If that fails try all possible names until we // find a working one. // - If we found one put it into cl_libcurl, so we // don't need to iterate trought the whole list // the next time the game is started. // - If we found none error out. #ifdef __APPLE__ const char *libcurl[] = { "libcurl.dylib", NULL }; #elif __linux__ const char *libcurl[] = { "libcurl.so.3", "libcurl.so.4", "libcurl-gnutls.so.3", "libcurl-gnutls.so.4", "libcurl-nss.so.3", "libcurl-nss.so.4", "libcurl.so", NULL }; #elif _WIN32 const char *libcurl[] = { "curl.dll", "libcurl.dll", NULL }; #else const char *libcurl[] = { "libcurl.so", NULL }; #endif // Mkay, let's try to find a working libcurl. cl_libcurl = Cvar_Get("cl_libcurl", (char *)libcurl[0], CVAR_ARCHIVE); if (strstr(cl_libcurl->string, "..") || strstr(cl_libcurl->string, ":") || strstr(cl_libcurl->string, "/") || strstr(cl_libcurl->string, "\\")) { Com_Printf("cl_libcurl must not contain '..', ':', '/' or '\': %s\n", cl_libcurl->string); goto error; } Com_Printf("Loading library: %s\n", cl_libcurl->string); Sys_LoadLibrary(cl_libcurl->string, NULL, &curlhandle); if (!curlhandle) { Com_Printf("Loading %s failed!\n", cl_libcurl->string); for (int i = 0; libcurl[i] != NULL; i++) { // Tried that one already. if (!strcmp(cl_libcurl->string, libcurl[i])) { continue; } Com_Printf("Loading library: %s\n", libcurl[i]); Sys_LoadLibrary(libcurl[i], NULL, &curlhandle); if (!curlhandle) { // No luck with this one. Com_Printf("Loading %s failed!\n", libcurl[i]); continue; } else { // Got one! Cvar_Set("cl_libcurl", (char *)libcurl[i]); break; } } if (!curlhandle) { goto error; } } // libcurl loaded sucessfully, connect the pointers. #define CONCURL(var, sym) do { \ var = Sys_GetProcAddress(curlhandle, sym); \ if (!var) goto error; \ } while(0) CONCURL(qcurl_easy_cleanup, "curl_easy_cleanup"); CONCURL(qcurl_easy_init, "curl_easy_init"); CONCURL(qcurl_easy_getinfo, "curl_easy_getinfo"); CONCURL(qcurl_easy_setopt, "curl_easy_setopt"); CONCURL(qcurl_easy_strerror, "curl_easy_strerror"); CONCURL(qcurl_global_cleanup, "curl_global_cleanup"); CONCURL(qcurl_global_init, "curl_global_init"); CONCURL(qcurl_multi_add_handle, "curl_multi_add_handle"); CONCURL(qcurl_multi_cleanup, "curl_multi_cleanup"); CONCURL(qcurl_multi_info_read, "curl_multi_info_read"); CONCURL(qcurl_multi_init, "curl_multi_init"); CONCURL(qcurl_multi_perform, "curl_multi_perform"); CONCURL(qcurl_multi_remove_handle, "curl_multi_remove_handle"); CONCURL(qcurl_multi_strerror, "curl_multi_strerror"); #undef CONCURL // And finally the global cURL initialization. qcurl_global_init(CURL_GLOBAL_NOTHING); qcurlInitialized = true; Com_Printf("------------------------------------\n\n"); return true; error: qcurlShutdown(); Com_Printf("------------------------------------\n\n"); return false; } /* * Calls cURLs global shutdown funtion, * unloads libcurl and set the function * pointers to NULL. */ void qcurlShutdown(void) { if (qcurlInitialized) { Com_Printf("Shutting down curl.\n"); qcurl_global_cleanup(); qcurlInitialized = false; } qcurl_easy_cleanup = NULL; qcurl_easy_init = NULL; qcurl_easy_getinfo = NULL; qcurl_easy_setopt = NULL; qcurl_easy_strerror = NULL; qcurl_global_cleanup = NULL; qcurl_global_init = NULL; qcurl_multi_add_handle = NULL; qcurl_multi_cleanup = NULL; qcurl_multi_info_read = NULL; qcurl_multi_init = NULL; qcurl_multi_perform = NULL; qcurl_multi_remove_handle = NULL; if (curlhandle) { Sys_FreeLibrary(curlhandle); curlhandle = NULL; } } // -------- #endif // USE_CURL yquake2-QUAKE2_8_40/src/client/header/000077500000000000000000000000001465112212000174065ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/header/client.h000066400000000000000000000377561465112212000210570ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 "../../common/header/common.h" #include "../curl/header/download.h" #include "../sound/header/sound.h" #include "../sound/header/vorbis.h" #include "../vid/header/ref.h" #include "../vid/header/vid.h" #include "screen.h" #include "keyboard.h" #include "console.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, in MS */ 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; size_t downloadposition; 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; #ifdef USE_CURL /* http downloading */ dlqueue_t downloadQueue; /* queues with files to download. */ dlhandle_t HTTPHandles[MAX_HTTP_HANDLES]; /* download handles. */ char downloadServer[512]; /* URL prefix to dowload from .*/ char downloadServerRetry[512]; /* retry count. */ char downloadReferer[32]; /* referer string. */ #endif } 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; /* Even more evil hack against spurious cinematic aborts caused by an unspeakable evil hack right out of the deeps of hell... Aeh... KeyEvent(). */ extern int abort_cinematic; /* cvars */ extern cvar_t *gl1_stereo_separation; extern cvar_t *gl1_stereo_convergence; extern cvar_t *gl1_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 *joy_layout; extern cvar_t *gyro_mode; extern cvar_t *gyro_turning_axis; 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_loadpaused; extern cvar_t *cl_audiopaused; extern cvar_t *cl_unpaused_scvis; extern cvar_t *cl_timedemo; extern cvar_t *cl_vwep; extern cvar_t *horplus; extern cvar_t *cin_force43; extern cvar_t *vid_fullscreen; extern cvar_t *vid_renderer; extern cvar_t *cl_kickangles; extern cvar_t *cl_r1q2_lightstyle; extern cvar_t *cl_limitsparksounds; extern cvar_t *cl_laseralpha; extern cvar_t *cl_nodownload_list; 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; extern qboolean paused_at_load; 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); void CL_ResetPrecacheCheck (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_8_40/src/client/header/console.h000066400000000000000000000036441465112212000212300ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/client/header/keyboard.h000066400000000000000000000156041465112212000213650ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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. 1024 * chars allow for a vertical resolution of * 8192 pixel which should be enough for the * years to come. */ #define MAXCMDLINE 1024 /* 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 /* * the joystick altselector key is pressed * => K_BTN_x turns into K_BTN_x_ALT */ extern qboolean joy_altselector_pressed; /* these are the key numbers that should be passed to Key_Event they must be matched by the low level key event processing! */ enum QKEYS { K_TAB = 9, K_ENTER = 13, K_ESCAPE = 27, // Note: ASCII keys are generally valid but don't get constants here, // just use 'a' (yes, lowercase) or '2' or whatever, however there are // some special cases when writing/parsing configs (space or quotes or // also ; and $ have a special meaning there so we use e.g. "SPACE" instead), // see keynames[] in cl_keyboard.c K_SPACE = 32, K_BACKSPACE = 127, K_COMMAND = 128, // "Windows Key" 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_SUPER, // TODO: what is this? SDL doesn't seem to know it.. K_COMPOSE, K_MODE, K_HELP, K_PRINT, K_SYSREQ, K_SCROLLOCK, K_MENU, K_UNDO, // The following are mapped from SDL_Scancodes, used as a *fallback* for keys // whose SDL_KeyCode we don't have a K_ constant for, like German Umlaut keys. // The scancode name corresponds to the key at that position on US-QWERTY keyboards // *not* the one in the local layout (e.g. German 'Ö' key is K_SC_SEMICOLON) // !!! NOTE: if you add a scancode here, make sure to also add it to: // 1. keynames[] in cl_keyboard.c // 2. IN_TranslateScancodeToQ2Key() in input/sdl.c K_SC_A, K_SC_B, K_SC_C, K_SC_D, K_SC_E, K_SC_F, K_SC_G, K_SC_H, K_SC_I, K_SC_J, K_SC_K, K_SC_L, K_SC_M, K_SC_N, K_SC_O, K_SC_P, K_SC_Q, K_SC_R, K_SC_S, K_SC_T, K_SC_U, K_SC_V, K_SC_W, K_SC_X, K_SC_Y, K_SC_Z, // leaving out SDL_SCANCODE_1 ... _0, we handle them separately already // also return, escape, backspace, tab, space, already handled as keycodes K_SC_MINUS, K_SC_EQUALS, K_SC_LEFTBRACKET, K_SC_RIGHTBRACKET, K_SC_BACKSLASH, K_SC_NONUSHASH, K_SC_SEMICOLON, K_SC_APOSTROPHE, K_SC_GRAVE, K_SC_COMMA, K_SC_PERIOD, K_SC_SLASH, // leaving out lots of key incl. from keypad, we already handle them as normal keys K_SC_NONUSBACKSLASH, K_SC_INTERNATIONAL1, /**< used on Asian keyboards, see footnotes in USB doc */ K_SC_INTERNATIONAL2, K_SC_INTERNATIONAL3, /**< Yen */ K_SC_INTERNATIONAL4, K_SC_INTERNATIONAL5, K_SC_INTERNATIONAL6, K_SC_INTERNATIONAL7, K_SC_INTERNATIONAL8, K_SC_INTERNATIONAL9, K_SC_THOUSANDSSEPARATOR, K_SC_DECIMALSEPARATOR, K_SC_CURRENCYUNIT, K_SC_CURRENCYSUBUNIT, // hardcoded pseudo-key to open the console, emitted when pressing the "console key" // (SDL_SCANCODE_GRAVE, the one between Esc, 1 and Tab) on layouts that don't // have a relevant char there (unlike Brazilian which has quotes there which you // want to be able to type in the console) - the user can't bind this key. K_CONSOLE, // Keyboard keys / codes end here. Any new ones should go before this. // From here on, only gamepad controls must be allowed. // Otherwise, separate bindings (keyboard / controller) menu options will not work. K_BTN_A, K_JOY_FIRST_REGULAR = K_BTN_A, K_BTN_B, K_BTN_X, K_BTN_Y, K_BTN_BACK, K_BTN_GUIDE, K_BTN_START, K_STICK_LEFT, K_STICK_RIGHT, K_SHOULDER_LEFT, K_SHOULDER_RIGHT, K_DPAD_UP, K_DPAD_DOWN, K_DPAD_LEFT, K_DPAD_RIGHT, K_BTN_MISC1, K_PADDLE_1, K_PADDLE_2, K_PADDLE_3, K_PADDLE_4, K_TOUCHPAD, // SDL_CONTROLLER_BUTTON_MAX - 1 K_TRIG_LEFT, // buttons for triggers (axes) K_TRIG_RIGHT, // add other joystick/controller keys before this one // and adjust it accordingly, also remember to add corresponding _ALT key below! K_JOY_LAST_REGULAR = K_TRIG_RIGHT, /* Can't be mapped to any action (=> not regular) */ K_JOY_BACK, K_BTN_A_ALT, K_JOY_FIRST_REGULAR_ALT = K_BTN_A_ALT, K_BTN_B_ALT, K_BTN_X_ALT, K_BTN_Y_ALT, K_BTN_BACK_ALT, K_BTN_GUIDE_ALT, K_BTN_START_ALT, K_STICK_LEFT_ALT, K_STICK_RIGHT_ALT, K_SHOULDER_LEFT_ALT, K_SHOULDER_RIGHT_ALT, K_DPAD_UP_ALT, K_DPAD_DOWN_ALT, K_DPAD_LEFT_ALT, K_DPAD_RIGHT_ALT, K_BTN_MISC1_ALT, K_PADDLE_1_ALT, K_PADDLE_2_ALT, K_PADDLE_3_ALT, K_PADDLE_4_ALT, K_TOUCHPAD_ALT, K_TRIG_LEFT_ALT, K_TRIG_RIGHT_ALT, 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; qboolean IN_NumpadIsOn(); void Char_Event(int key); void Key_Event(int key, qboolean down, qboolean special); void Key_Init(void); void Key_Shutdown(void); void Key_WriteBindings(FILE *f); void Key_ReadConsoleHistory(); void Key_WriteConsoleHistory(); void Key_SetBinding(int keynum, char *binding); void Key_MarkAllUp(void); void Controller_Rumble(const char *name, vec3_t source, qboolean from_player, unsigned int duration, unsigned short int volume); void Haptic_Feedback(const char *name, int effect_volume, int effect_duration, int effect_delay, int effect_attack, int effect_fade, int effect_x, int effect_y, int effect_z, float effect_distance); int Key_GetMenuKey(int key); #endif yquake2-QUAKE2_8_40/src/client/header/screen.h000066400000000000000000000037461465112212000210500ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/client/input/000077500000000000000000000000001465112212000173155ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/input/header/000077500000000000000000000000001465112212000205455ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/input/header/input.h000066400000000000000000000027031465112212000220570ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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" /* * Saves the time of the last input event. */ extern int sys_frame_time; /* * 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_8_40/src/client/input/sdl2.c000066400000000000000000001666361465112212000203470ustar00rootroot00000000000000/* * 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. * * Joystick reading and deadzone handling is based on: * http://joshsutphin.com/2013/04/12/doing-thumbstick-dead-zones-right.html * ...and implementation is partially based on code from: * - http://quakespasm.sourceforge.net * - https://github.com/Minimuino/thumbstick-deadzones * * Flick Stick handling is based on: * http://gyrowiki.jibbsmart.com/blog:good-gyro-controls-part-2:the-flick-stick * * ======================================================================= * * This is the Quake II input system backend, implemented with SDL. * * ======================================================================= */ #include #include "header/input.h" #include "../header/keyboard.h" #include "../header/client.h" // ---- // Maximal mouse move per frame #define MOUSE_MAX 3000 // Minimal mouse move per frame #define MOUSE_MIN 40 // ---- enum { LAYOUT_DEFAULT = 0, LAYOUT_SOUTHPAW, LAYOUT_LEGACY, LAYOUT_LEGACY_SOUTHPAW, LAYOUT_FLICK_STICK, LAYOUT_FLICK_STICK_SOUTHPAW }; typedef struct { float x; float y; } thumbstick_t; typedef enum { REASON_NONE, REASON_CONTROLLERINIT, REASON_GYROCALIBRATION } updates_countdown_reasons; // ---- // These are used to communicate the events collected by // IN_Update() called at the beginning of a frame to the // actual movement functions called at a later time. static float mouse_x, mouse_y; static unsigned char sdl_back_button = SDL_CONTROLLER_BUTTON_BACK; static int joystick_left_x, joystick_left_y, joystick_right_x, joystick_right_y; static float gyro_yaw, gyro_pitch; static qboolean mlooking; // The last time input events were processed. // Used throughout the client. int sys_frame_time; // the joystick altselector that turns K_BTN_X into K_BTN_X_ALT // is pressed qboolean joy_altselector_pressed = false; // Console Variables cvar_t *freelook; cvar_t *lookstrafe; cvar_t *m_forward; cvar_t *m_pitch; cvar_t *m_side; cvar_t *m_up; cvar_t *m_yaw; static cvar_t *sensitivity; static cvar_t *exponential_speedup; static cvar_t *in_grab; static cvar_t *m_filter; static cvar_t *windowed_pauseonfocuslost; static cvar_t *windowed_mouse; static cvar_t *haptic_feedback_filter; // ---- typedef struct haptic_effects_cache { int effect_volume; int effect_duration; int effect_delay; int effect_attack; int effect_fade; int effect_id; int effect_x; int effect_y; int effect_z; } haptic_effects_cache_t; qboolean show_gamepad = false, show_haptic = false, show_gyro = false; static SDL_Haptic *joystick_haptic = NULL; static SDL_GameController *controller = NULL; #define HAPTIC_EFFECT_LIST_SIZE 16 static int last_haptic_volume = 0; static int last_haptic_effect_size = HAPTIC_EFFECT_LIST_SIZE; static int last_haptic_effect_pos = 0; static haptic_effects_cache_t last_haptic_effect[HAPTIC_EFFECT_LIST_SIZE]; // Joystick sensitivity static cvar_t *joy_yawsensitivity; static cvar_t *joy_pitchsensitivity; static cvar_t *joy_forwardsensitivity; static cvar_t *joy_sidesensitivity; // Joystick's analog sticks configuration cvar_t *joy_layout; static cvar_t *joy_left_expo; static cvar_t *joy_left_snapaxis; static cvar_t *joy_left_deadzone; static cvar_t *joy_right_expo; static cvar_t *joy_right_snapaxis; static cvar_t *joy_right_deadzone; static cvar_t *joy_flick_threshold; static cvar_t *joy_flick_smoothed; // Joystick haptic static cvar_t *joy_haptic_magnitude; static cvar_t *joy_haptic_distance; // Gyro mode (0=off, 3=on, 1-2=uses button to enable/disable) cvar_t *gyro_mode; cvar_t *gyro_turning_axis; // yaw or roll // Gyro sensitivity static cvar_t *gyro_yawsensitivity; static cvar_t *gyro_pitchsensitivity; static cvar_t *gyro_tightening; // Gyro is being used in this very moment static qboolean gyro_active = false; // Gyro calibration static float gyro_accum[3]; static cvar_t *gyro_calibration_x; static cvar_t *gyro_calibration_y; static cvar_t *gyro_calibration_z; #if SDL_VERSION_ATLEAST(2, 0, 14) // support for controller sensors (gyro, accelerometer) static unsigned int num_samples; #define NATIVE_SDL_GYRO // uses SDL_CONTROLLERSENSORUPDATE to read gyro #else // for SDL < 2.0.14, gyro can be read as a "secondary joystick" exposed by dkms-hid-nintendo static unsigned int num_samples[3]; static SDL_Joystick *imu_joystick = NULL; // gyro "joystick" #define IMU_JOY_AXIS_GYRO_ROLL 3 #define IMU_JOY_AXIS_GYRO_PITCH 4 #define IMU_JOY_AXIS_GYRO_YAW 5 #endif // To ignore SDL_JOYDEVICEADDED at game init. Allows for hot plugging of game controller afterwards. static qboolean first_init = true; // Countdown of calls to IN_Update(), needed for controller init and gyro calibration static unsigned short int updates_countdown = 30; // Reason for the countdown static updates_countdown_reasons countdown_reason = REASON_CONTROLLERINIT; // Flick Stick #define FLICK_TIME 6 // number of frames it takes for a flick to execute static float target_angle; // angle to end up facing at the end of a flick static unsigned short int flick_progress = FLICK_TIME; // Flick Stick's rotation input samples to smooth out #define MAX_SMOOTH_SAMPLES 8 static float flick_samples[MAX_SMOOTH_SAMPLES]; static unsigned short int front_sample = 0; extern void CalibrationFinishedCallback(void); /* ------------------------------------------------------------------ */ /* * 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_TAB: key = K_TAB; break; case SDLK_RETURN: key = K_ENTER; break; case SDLK_ESCAPE: key = K_ESCAPE; break; case SDLK_BACKSPACE: key = K_BACKSPACE; break; case SDLK_LGUI: case SDLK_RGUI: key = K_COMMAND; // Win key break; case SDLK_CAPSLOCK: key = K_CAPSLOCK; break; case SDLK_POWER: key = K_POWER; break; case SDLK_PAUSE: key = K_PAUSE; break; case SDLK_UP: key = K_UPARROW; break; case SDLK_DOWN: key = K_DOWNARROW; break; case SDLK_LEFT: key = K_LEFTARROW; break; case SDLK_RIGHT: key = K_RIGHTARROW; break; case SDLK_RALT: case SDLK_LALT: key = K_ALT; break; case SDLK_LCTRL: case SDLK_RCTRL: key = K_CTRL; break; case SDLK_LSHIFT: case SDLK_RSHIFT: key = K_SHIFT; break; case SDLK_INSERT: key = K_INS; break; case SDLK_DELETE: key = K_DEL; break; case SDLK_PAGEDOWN: key = K_PGDN; break; case SDLK_PAGEUP: key = K_PGUP; break; case SDLK_HOME: key = K_HOME; break; case SDLK_END: key = K_END; 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_KP_7: key = K_KP_HOME; break; case SDLK_KP_8: key = K_KP_UPARROW; break; case SDLK_KP_9: key = K_KP_PGUP; break; case SDLK_KP_4: key = K_KP_LEFTARROW; break; case SDLK_KP_5: key = K_KP_5; break; case SDLK_KP_6: key = K_KP_RIGHTARROW; break; case SDLK_KP_1: key = K_KP_END; break; case SDLK_KP_2: key = K_KP_DOWNARROW; break; case SDLK_KP_3: key = K_KP_PGDN; break; case SDLK_KP_ENTER: key = K_KP_ENTER; break; case SDLK_KP_0: key = K_KP_INS; break; case SDLK_KP_PERIOD: key = K_KP_DEL; break; case SDLK_KP_DIVIDE: key = K_KP_SLASH; break; case SDLK_KP_MINUS: key = K_KP_MINUS; break; case SDLK_KP_PLUS: key = K_KP_PLUS; break; case SDLK_NUMLOCKCLEAR: key = K_KP_NUMLOCK; break; case SDLK_KP_MULTIPLY: key = K_KP_STAR; break; case SDLK_KP_EQUALS: key = K_KP_EQUALS; break; // TODO: K_SUPER ? Win Key is already K_COMMAND case SDLK_APPLICATION: key = K_COMPOSE; break; case SDLK_MODE: key = K_MODE; break; case SDLK_HELP: key = K_HELP; break; case SDLK_PRINTSCREEN: key = K_PRINT; break; case SDLK_SYSREQ: key = K_SYSREQ; break; case SDLK_SCROLLLOCK: key = K_SCROLLOCK; break; case SDLK_MENU: key = K_MENU; break; case SDLK_UNDO: key = K_UNDO; break; default: break; } return key; } static int IN_TranslateScancodeToQ2Key(SDL_Scancode sc) { #define MY_SC_CASE(X) case SDL_SCANCODE_ ## X : return K_SC_ ## X; switch( (int)sc ) // cast to int to shut -Wswitch up { // case SDL_SCANCODE_A : return K_SC_A; MY_SC_CASE(A) MY_SC_CASE(B) MY_SC_CASE(C) MY_SC_CASE(D) MY_SC_CASE(E) MY_SC_CASE(F) MY_SC_CASE(G) MY_SC_CASE(H) MY_SC_CASE(I) MY_SC_CASE(J) MY_SC_CASE(K) MY_SC_CASE(L) MY_SC_CASE(M) MY_SC_CASE(N) MY_SC_CASE(O) MY_SC_CASE(P) MY_SC_CASE(Q) MY_SC_CASE(R) MY_SC_CASE(S) MY_SC_CASE(T) MY_SC_CASE(U) MY_SC_CASE(V) MY_SC_CASE(W) MY_SC_CASE(X) MY_SC_CASE(Y) MY_SC_CASE(Z) MY_SC_CASE(MINUS) MY_SC_CASE(EQUALS) MY_SC_CASE(LEFTBRACKET) MY_SC_CASE(RIGHTBRACKET) MY_SC_CASE(BACKSLASH) MY_SC_CASE(NONUSHASH) MY_SC_CASE(SEMICOLON) MY_SC_CASE(APOSTROPHE) MY_SC_CASE(GRAVE) MY_SC_CASE(COMMA) MY_SC_CASE(PERIOD) MY_SC_CASE(SLASH) MY_SC_CASE(NONUSBACKSLASH) MY_SC_CASE(INTERNATIONAL1) MY_SC_CASE(INTERNATIONAL2) MY_SC_CASE(INTERNATIONAL3) MY_SC_CASE(INTERNATIONAL4) MY_SC_CASE(INTERNATIONAL5) MY_SC_CASE(INTERNATIONAL6) MY_SC_CASE(INTERNATIONAL7) MY_SC_CASE(INTERNATIONAL8) MY_SC_CASE(INTERNATIONAL9) MY_SC_CASE(THOUSANDSSEPARATOR) MY_SC_CASE(DECIMALSEPARATOR) MY_SC_CASE(CURRENCYUNIT) MY_SC_CASE(CURRENCYSUBUNIT) } #undef MY_SC_CASE return 0; } static void IN_Controller_Init(qboolean notify_user); static void IN_Controller_Shutdown(qboolean notify_user); qboolean IN_NumpadIsOn() { SDL_Keymod mod = SDL_GetModState(); if ((mod & KMOD_NUM) == KMOD_NUM) { return true; } return false; } /* ------------------------------------------------------------------ */ /* * 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; static qboolean left_trigger = false; static qboolean right_trigger = false; static int consoleKeyCode = 0; /* Get and process an event */ while (SDL_PollEvent(&event)) { switch (event.type) { 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; case SDL_MOUSEBUTTONDOWN: 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; case SDL_TEXTINPUT: { int c = event.text.text[0]; // also make sure we don't get the char that corresponds to the // "console key" (like "^" or "`") as text input if ((c >= ' ') && (c <= '~') && c != consoleKeyCode) { Char_Event(c); } } break; case SDL_KEYDOWN: case SDL_KEYUP: { qboolean down = (event.type == SDL_KEYDOWN); /* 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 { SDL_Keycode kc = event.key.keysym.sym; if(sc == SDL_SCANCODE_GRAVE && kc != '\'' && kc != '"') { // special case/hack: open the console with the "console key" // (beneath Esc, left of 1, above Tab) // but not if the keycode for this is a quote (like on Brazilian // keyboards) - otherwise you couldn't type them in the console if((event.key.keysym.mod & (KMOD_CAPS|KMOD_SHIFT|KMOD_ALT|KMOD_CTRL|KMOD_GUI)) == 0) { // also, only do this if no modifiers like shift or AltGr or whatever are pressed // so kc will most likely be the ascii char generated by this and can be ignored // in case SDL_TEXTINPUT above (so we don't get ^ or whatever as text in console) // (can't just check for mod == 0 because numlock is a KMOD too) Key_Event(K_CONSOLE, down, true); consoleKeyCode = kc; } } else if ((kc >= SDLK_SPACE) && (kc < SDLK_DELETE)) { Key_Event(kc, down, false); } else { int key = IN_TranslateSDLtoQ2Key(kc); if(key == 0) { // fallback to scancodes if we don't know the keycode key = IN_TranslateScancodeToQ2Key(sc); } if(key != 0) { Key_Event(key, down, true); } else { Com_DPrintf("Pressed unknown key with SDL_Keycode %d, SDL_Scancode %d.\n", kc, (int)sc); } } } break; } case SDL_WINDOWEVENT: if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST || event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { Key_MarkAllUp(); if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) { S_Activate(false); if (windowed_pauseonfocuslost->value != 1) { Cvar_SetValue("paused", 1); } /* pause music */ if (Cvar_VariableValue("ogg_pausewithgame") == 1 && OGG_Status() == PLAY && cl.attractloop == false) { Cbuf_AddText("ogg toggle\n"); } } if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { S_Activate(true); if (windowed_pauseonfocuslost->value == 2) { Cvar_SetValue("paused", 0); } /* play music */ if (Cvar_VariableValue("ogg_pausewithgame") == 1 && OGG_Status() == PAUSE && cl.attractloop == false && cl_paused->value == 0) { Cbuf_AddText("ogg toggle\n"); } } } 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.0f; } else if (event.window.event == SDL_WINDOWEVENT_SHOWN) { if (cl_unpaused_scvis->value > 0) { Cvar_SetValue("paused", 0); } /* play music */ if (Cvar_VariableValue("ogg_pausewithgame") == 1 && OGG_Status() == PAUSE && cl.attractloop == false && cl_paused->value == 0) { Cbuf_AddText("ogg toggle\n"); } } break; case SDL_CONTROLLERBUTTONUP: case SDL_CONTROLLERBUTTONDOWN: { qboolean down = (event.type == SDL_CONTROLLERBUTTONDOWN); unsigned char btn = event.cbutton.button; // Handle Back Button, to override its original key Key_Event( (btn == sdl_back_button)? K_JOY_BACK : K_BTN_A + btn, down, true ); break; } case SDL_CONTROLLERAXISMOTION: /* Handle Controller Motion */ { int axis_value = event.caxis.value; switch (event.caxis.axis) { case SDL_CONTROLLER_AXIS_TRIGGERLEFT: { qboolean new_left_trigger = axis_value > 8192; if (new_left_trigger != left_trigger) { left_trigger = new_left_trigger; Key_Event(K_TRIG_LEFT, left_trigger, true); } break; } case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: { qboolean new_right_trigger = axis_value > 8192; if (new_right_trigger != right_trigger) { right_trigger = new_right_trigger; Key_Event(K_TRIG_RIGHT, right_trigger, true); } break; } } if (!cl_paused->value && cls.key_dest == key_game) { switch (event.caxis.axis) { case SDL_CONTROLLER_AXIS_LEFTX: joystick_left_x = axis_value; break; case SDL_CONTROLLER_AXIS_LEFTY: joystick_left_y = axis_value; break; case SDL_CONTROLLER_AXIS_RIGHTX: joystick_right_x = axis_value; break; case SDL_CONTROLLER_AXIS_RIGHTY: joystick_right_y = axis_value; break; } } break; } #ifdef NATIVE_SDL_GYRO // controller sensors' reading supported (gyro, accelerometer) case SDL_CONTROLLERSENSORUPDATE: if (event.csensor.sensor != SDL_SENSOR_GYRO) { break; } if (countdown_reason == REASON_GYROCALIBRATION && updates_countdown) { gyro_accum[0] += event.csensor.data[0]; gyro_accum[1] += event.csensor.data[1]; gyro_accum[2] += event.csensor.data[2]; num_samples++; break; } #else // gyro read as "secondary joystick" case SDL_JOYAXISMOTION: if ( !imu_joystick || event.cdevice.which != SDL_JoystickInstanceID(imu_joystick) ) { break; // controller axes handled by SDL_CONTROLLERAXISMOTION } int axis_value = event.caxis.value; if (countdown_reason == REASON_GYROCALIBRATION && updates_countdown) { switch (event.caxis.axis) { case IMU_JOY_AXIS_GYRO_PITCH: gyro_accum[0] += axis_value; num_samples[0]++; break; case IMU_JOY_AXIS_GYRO_YAW: gyro_accum[1] += axis_value; num_samples[1]++; break; case IMU_JOY_AXIS_GYRO_ROLL: gyro_accum[2] += axis_value; num_samples[2]++; } break; } #endif // NATIVE_SDL_GYRO if (gyro_active && gyro_mode->value && !cl_paused->value && cls.key_dest == key_game) { #ifdef NATIVE_SDL_GYRO if (!gyro_turning_axis->value) { gyro_yaw = event.csensor.data[1] - gyro_calibration_y->value; // yaw } else { gyro_yaw = -(event.csensor.data[2] - gyro_calibration_z->value); // roll } gyro_pitch = event.csensor.data[0] - gyro_calibration_x->value; #else // old "joystick" gyro switch (event.caxis.axis) // inside "case SDL_JOYAXISMOTION" here { case IMU_JOY_AXIS_GYRO_PITCH: gyro_pitch = -(axis_value - gyro_calibration_x->value); break; case IMU_JOY_AXIS_GYRO_YAW: if (!gyro_turning_axis->value) { gyro_yaw = axis_value - gyro_calibration_y->value; } break; case IMU_JOY_AXIS_GYRO_ROLL: if (gyro_turning_axis->value) { gyro_yaw = axis_value - gyro_calibration_z->value; } } #endif // NATIVE_SDL_GYRO } else { gyro_yaw = gyro_pitch = 0; } break; case SDL_CONTROLLERDEVICEREMOVED: if (!controller) { break; } if (event.cdevice.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller))) { Cvar_SetValue("paused", 1); IN_Controller_Shutdown(true); IN_Controller_Init(false); } break; case SDL_JOYDEVICEADDED: if (!controller) { // This should be lower, but some controllers just don't want to get detected by the OS updates_countdown = 100; countdown_reason = REASON_CONTROLLERINIT; } break; #if SDL_VERSION_ATLEAST(2, 24, 0) // support for battery status changes case SDL_JOYBATTERYUPDATED: if (!controller || event.jbattery.which != SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller))) { break; } if (event.jbattery.level == SDL_JOYSTICK_POWER_LOW) { Com_Printf("WARNING: Gamepad battery Low, it is recommended to connect it by cable.\n"); } else if (event.jbattery.level == SDL_JOYSTICK_POWER_EMPTY) { SCR_CenterPrint("ALERT: Gamepad battery almost Empty.\n"); } break; #endif // SDL_VERSION_ATLEAST(2, 24, 0) case SDL_QUIT: Com_Quit(); break; } } /* Grab and ungrab the mouse if the console or the menu is opened */ if (in_grab->value == 3) { want_grab = windowed_mouse->value; } else { want_grab = (vid_fullscreen->value || in_grab->value == 1 || (in_grab->value == 2 && windowed_mouse->value)); } // calling GLimp_GrabInput() each frame is a bit ugly but simple and should work. // The called SDL functions return after a cheap check, if there's nothing to do. GLimp_GrabInput(want_grab); // We need to save the frame time so other subsystems // know the exact time of the last input events. sys_frame_time = Sys_Milliseconds(); // Hot plugging delay handling, to not be "overwhelmed" because some controllers // present themselves as two different devices, triggering SDL_JOYDEVICEADDED // too many times. They could trigger it even at game initialization. // Also used to keep time of the 'controller gyro calibration' pause. if (updates_countdown) { updates_countdown--; if (!updates_countdown) // Countdown finished, apply needed action by reason { switch (countdown_reason) { case REASON_CONTROLLERINIT: if (!first_init) { IN_Controller_Shutdown(false); IN_Controller_Init(true); } else { first_init = false; } break; case REASON_GYROCALIBRATION: // finish and save calibration { #ifdef NATIVE_SDL_GYRO const float inverseSamples = 1.f / num_samples; Cvar_SetValue("gyro_calibration_x", gyro_accum[0] * inverseSamples); Cvar_SetValue("gyro_calibration_y", gyro_accum[1] * inverseSamples); Cvar_SetValue("gyro_calibration_z", gyro_accum[2] * inverseSamples); #else if (!num_samples[0] || !num_samples[1] || !num_samples[2]) { Com_Printf("Calibration failed, please retry inside a level after having moved your controller a little.\n"); } else { Cvar_SetValue("gyro_calibration_x", gyro_accum[0] / num_samples[0]); Cvar_SetValue("gyro_calibration_y", gyro_accum[1] / num_samples[1]); Cvar_SetValue("gyro_calibration_z", gyro_accum[2] / num_samples[2]); } #endif Com_Printf("Calibration results:\n X=%f Y=%f Z=%f\n", gyro_calibration_x->value, gyro_calibration_y->value, gyro_calibration_z->value); CalibrationFinishedCallback(); break; } default: break; // avoiding compiler warning } countdown_reason = REASON_NONE; } } } /* * Joystick vector magnitude */ static float IN_StickMagnitude(thumbstick_t stick) { return sqrtf((stick.x * stick.x) + (stick.y * stick.y)); } /* * Scales "v" from [deadzone, 1] range to [0, 1] range, then inherits sign */ static float IN_MapRange(float v, float deadzone, float sign) { return ((v - deadzone) / (1 - deadzone)) * sign; } /* * Radial deadzone based on github.com/jeremiah-sypult/Quakespasm-Rift */ static thumbstick_t IN_RadialDeadzone(thumbstick_t stick, float deadzone) { thumbstick_t result = {0}; float magnitude = Q_min(IN_StickMagnitude(stick), 1.0f); deadzone = Q_min( Q_max(deadzone, 0.0f), 0.9f); // clamp to [0.0, 0.9] if ( magnitude > deadzone ) { const float scale = ((magnitude - deadzone) / (1.0 - deadzone)) / magnitude; result.x = stick.x * scale; result.y = stick.y * scale; } return result; } /* * Sloped axial deadzone based on github.com/Minimuino/thumbstick-deadzones * Provides a "snap-to-axis" feeling, without losing precision near the center of the stick */ static thumbstick_t IN_SlopedAxialDeadzone(thumbstick_t stick, float deadzone) { thumbstick_t result = {0}; float abs_x = fabsf(stick.x); float abs_y = fabsf(stick.y); float sign_x = copysignf(1.0f, stick.x); float sign_y = copysignf(1.0f, stick.y); deadzone = Q_min(deadzone, 0.5f); float deadzone_x = deadzone * abs_y; // deadzone of one axis depends... float deadzone_y = deadzone * abs_x; // ...on the value of the other axis if (abs_x > deadzone_x) { result.x = IN_MapRange(abs_x, deadzone_x, sign_x); } if (abs_y > deadzone_y) { result.y = IN_MapRange(abs_y, deadzone_y, sign_y); } return result; } /* * Exponent applied on stick magnitude */ static thumbstick_t IN_ApplyExpo(thumbstick_t stick, float exponent) { thumbstick_t result = {0}; float magnitude = IN_StickMagnitude(stick); if (magnitude == 0) { return result; } const float eased = powf(magnitude, exponent) / magnitude; result.x = stick.x * eased; result.y = stick.y * eased; return result; } /* * Minimize gyro movement when under a small threshold. * http://gyrowiki.jibbsmart.com/blog:good-gyro-controls-part-1:the-gyro-is-a-mouse#toc9 */ static thumbstick_t IN_TightenInput(float yaw, float pitch) { thumbstick_t input = { yaw, pitch }; const float magnitude = IN_StickMagnitude(input); #ifdef NATIVE_SDL_GYRO const float threshold = (M_PI / 180.0f) * gyro_tightening->value; #else const float threshold = (2560.0f / 180.0f) * gyro_tightening->value; #endif if (magnitude < threshold) { const float scale = magnitude / threshold; input.x *= scale; input.y *= scale; } return input; } /* * Delete flick stick's buffer of angle samples for smoothing */ static void IN_ResetSmoothSamples() { front_sample = 0; for (int i = 0; i < MAX_SMOOTH_SAMPLES; i++) { flick_samples[i] = 0.0f; } } /* * Soft tiered smoothing for angle rotations with Flick Stick * http://gyrowiki.jibbsmart.com/blog:tight-and-smooth:soft-tiered-smoothing */ static float IN_SmoothedStickRotation(float value) { float top_threshold = joy_flick_smoothed->value; float bottom_threshold = top_threshold / 2.0f; if (top_threshold == 0) { return value; } // sample in the circular smoothing buffer we want to write over front_sample = (front_sample + 1) % MAX_SMOOTH_SAMPLES; // if input > top threshold, it'll all be consumed immediately // 0 gets put into the smoothing buffer // if input < bottom threshold, it'll all be put in the smoothing buffer // 0 for immediate consumption float immediate_weight = (fabsf(value) - bottom_threshold) / (top_threshold - bottom_threshold); immediate_weight = Q_min( Q_max(immediate_weight, 0.0f), 1.0f ); // clamp to [0, 1] range // now we can push the smooth sample float smooth_weight = 1.0f - immediate_weight; flick_samples[front_sample] = value * smooth_weight; // calculate smoothed result float average = 0; for (int i = 0; i < MAX_SMOOTH_SAMPLES; i++) { average += flick_samples[i]; } average /= MAX_SMOOTH_SAMPLES; // finally, add immediate portion (original input) return average + value * immediate_weight; } /* * Flick Stick handling: detect if the player just started one, or return the * player rotation if stick was already flicked */ static float IN_FlickStick(thumbstick_t stick, float axial_deadzone) { static qboolean is_flicking; static float last_stick_angle; thumbstick_t processed = stick; float angle_change = 0; if (IN_StickMagnitude(stick) > Q_min(joy_flick_threshold->value, 1.0f)) // flick! { // Make snap-to-axis only if player wasn't already flicking if (!is_flicking || flick_progress < FLICK_TIME) { processed = IN_SlopedAxialDeadzone(stick, axial_deadzone); } const float stick_angle = (180 / M_PI) * atan2f(-processed.x, -processed.y); if (!is_flicking) { // Flicking begins now, with a new target is_flicking = true; flick_progress = 0; target_angle = stick_angle; IN_ResetSmoothSamples(); } else { // Was already flicking, just turning now angle_change = stick_angle - last_stick_angle; // angle wrap: https://stackoverflow.com/a/11498248/1130520 angle_change = fmod(angle_change + 180.0f, 360.0f); if (angle_change < 0) { angle_change += 360.0f; } angle_change -= 180.0f; angle_change = IN_SmoothedStickRotation(angle_change); } last_stick_angle = stick_angle; } else { is_flicking = false; } return angle_change; } /* * Move handling */ void IN_Move(usercmd_t *cmd) { // Factor used to transform from SDL joystick input ([-32768, 32767]) to [-1, 1] range static const float normalize_sdl_axis = 1.0f / 32768.0f; // Flick Stick's factors to change to the target angle with a feeling of "ease out" static const float rotation_factor[FLICK_TIME] = { 0.305555556f, 0.249999999f, 0.194444445f, 0.138888889f, 0.083333333f, 0.027777778f }; static float old_mouse_x; static float old_mouse_y; static float joystick_yaw, joystick_pitch; static float joystick_forwardmove, joystick_sidemove; static thumbstick_t left_stick = {0}, right_stick = {0}; thumbstick_t gyro_in = {0}; 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; } // Joystick reading and processing left_stick.x = joystick_left_x * normalize_sdl_axis; left_stick.y = joystick_left_y * normalize_sdl_axis; right_stick.x = joystick_right_x * normalize_sdl_axis; right_stick.y = joystick_right_y * normalize_sdl_axis; if (left_stick.x || left_stick.y) { left_stick = IN_RadialDeadzone(left_stick, joy_left_deadzone->value); if ((int)joy_layout->value == LAYOUT_FLICK_STICK_SOUTHPAW) { cl.viewangles[YAW] += IN_FlickStick(left_stick, joy_left_snapaxis->value); } else { left_stick = IN_SlopedAxialDeadzone(left_stick, joy_left_snapaxis->value); left_stick = IN_ApplyExpo(left_stick, joy_left_expo->value); } } if (right_stick.x || right_stick.y) { right_stick = IN_RadialDeadzone(right_stick, joy_right_deadzone->value); if ((int)joy_layout->value == LAYOUT_FLICK_STICK) { cl.viewangles[YAW] += IN_FlickStick(right_stick, joy_right_snapaxis->value); } else { right_stick = IN_SlopedAxialDeadzone(right_stick, joy_right_snapaxis->value); right_stick = IN_ApplyExpo(right_stick, joy_right_expo->value); } } switch((int)joy_layout->value) { case LAYOUT_SOUTHPAW: joystick_forwardmove = right_stick.y; joystick_sidemove = right_stick.x; joystick_yaw = left_stick.x; joystick_pitch = left_stick.y; break; case LAYOUT_LEGACY: joystick_forwardmove = left_stick.y; joystick_sidemove = right_stick.x; joystick_yaw = left_stick.x; joystick_pitch = right_stick.y; break; case LAYOUT_LEGACY_SOUTHPAW: joystick_forwardmove = right_stick.y; joystick_sidemove = left_stick.x; joystick_yaw = right_stick.x; joystick_pitch = left_stick.y; break; case LAYOUT_FLICK_STICK: // yaw already set by now joystick_forwardmove = left_stick.y; joystick_sidemove = left_stick.x; break; case LAYOUT_FLICK_STICK_SOUTHPAW: joystick_forwardmove = right_stick.y; joystick_sidemove = right_stick.x; break; default: // LAYOUT_DEFAULT joystick_forwardmove = left_stick.y; joystick_sidemove = left_stick.x; joystick_yaw = right_stick.x; joystick_pitch = right_stick.y; } // To make the the viewangles changes independent of framerate we need to scale // with frametime (assuming the configured values are for 60hz) // // For movement this is not needed, as those are absolute values independent of framerate float joyViewFactor = cls.rframetime/0.01666f; #ifdef NATIVE_SDL_GYRO float gyroViewFactor = (1.0f / M_PI) * joyViewFactor; #else float gyroViewFactor = (1.0f / 2560.0f) * joyViewFactor; // normalized for Switch gyro #endif if (joystick_yaw) { cl.viewangles[YAW] -= (m_yaw->value * joy_yawsensitivity->value * cl_yawspeed->value * joystick_yaw) * joyViewFactor; } if(joystick_pitch) { cl.viewangles[PITCH] += (m_pitch->value * joy_pitchsensitivity->value * cl_pitchspeed->value * joystick_pitch) * joyViewFactor; } if (joystick_forwardmove) { // We need to be twice as fast because with joystick we run... cmd->forwardmove -= m_forward->value * joy_forwardsensitivity->value * cl_forwardspeed->value * 2.0f * joystick_forwardmove; } if (joystick_sidemove) { // We need to be twice as fast because with joystick we run... cmd->sidemove += m_side->value * joy_sidesensitivity->value * cl_sidespeed->value * 2.0f * joystick_sidemove; } if (gyro_yaw || gyro_pitch) { gyro_in = IN_TightenInput(gyro_yaw, gyro_pitch); } if (gyro_in.x) { cl.viewangles[YAW] += m_yaw->value * gyro_yawsensitivity->value * cl_yawspeed->value * gyro_in.x * gyroViewFactor; } if (gyro_in.y) { cl.viewangles[PITCH] -= m_pitch->value * gyro_pitchsensitivity->value * cl_pitchspeed->value * gyro_in.y * gyroViewFactor; } // Flick Stick: flick in progress, changing the yaw angle to the target progressively if (flick_progress < FLICK_TIME) { cl.viewangles[YAW] += target_angle * rotation_factor[flick_progress]; flick_progress++; } } /* ------------------------------------------------------------------ */ /* * Look down */ static void IN_MLookDown(void) { mlooking = true; } /* * Look up */ static void IN_MLookUp(void) { mlooking = false; IN_CenterView(); } static void IN_JoyAltSelectorDown(void) { joy_altselector_pressed = true; } static void IN_JoyAltSelectorUp(void) { joy_altselector_pressed = false; } static void IN_GyroActionDown(void) { switch ((int)gyro_mode->value) { case 1: gyro_active = true; return; case 2: gyro_active = false; } } static void IN_GyroActionUp(void) { switch ((int)gyro_mode->value) { case 1: gyro_active = false; return; case 2: gyro_active = true; } } /* * Removes all pending events from SDLs queue. */ void In_FlushQueue(void) { SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); Key_MarkAllUp(); IN_JoyAltSelectorUp(); } /* ------------------------------------------------------------------ */ static void IN_Haptic_Shutdown(void); /* * Init haptic effects */ static int IN_Haptic_Effect_Init(int effect_x, int effect_y, int effect_z, int period, int magnitude, int delay, int attack, int fade) { static SDL_HapticEffect haptic_effect; /* limit magnitude */ if (magnitude > SHRT_MAX) { magnitude = SHRT_MAX; } else if (magnitude < 0) { magnitude = 0; } 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_CARTESIAN; // Cartesian/3d coordinates haptic_effect.periodic.direction.dir[0] = effect_x; haptic_effect.periodic.direction.dir[1] = effect_y; haptic_effect.periodic.direction.dir[2] = effect_z; haptic_effect.periodic.period = period; haptic_effect.periodic.magnitude = magnitude; haptic_effect.periodic.length = period; haptic_effect.periodic.delay = delay; haptic_effect.periodic.attack_length = attack; haptic_effect.periodic.fade_length = fade; return SDL_HapticNewEffect(joystick_haptic, &haptic_effect); } static void IN_Haptic_Effects_Info(void) { Com_Printf ("Joystick/Mouse haptic:\n"); Com_Printf (" * %d effects\n", SDL_HapticNumEffects(joystick_haptic)); Com_Printf (" * %d haptic effects at the 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_effect_size = SDL_HapticNumEffectsPlaying(joystick_haptic); if (last_haptic_effect_size > HAPTIC_EFFECT_LIST_SIZE) { last_haptic_effect_size = HAPTIC_EFFECT_LIST_SIZE; } memset(&last_haptic_effect, 0, sizeof(last_haptic_effect)); 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 * effect_volume; // 32767 max strength; /* Com_Printf("%d: volume %d: %d ms %d:%d:%d ms speed: %.2f\n", last_haptic_effect_pos, effect_volume, effect_duration, effect_delay, effect_attack, effect_fade, (float)effect_volume / effect_fade); */ // FIFO for effects last_haptic_effect_pos = (last_haptic_effect_pos + 1) % last_haptic_effect_size; IN_Haptic_Effect_Shutdown(&last_haptic_effect[last_haptic_effect_pos].effect_id); last_haptic_effect[last_haptic_effect_pos].effect_volume = effect_volume; last_haptic_effect[last_haptic_effect_pos].effect_duration = effect_duration; last_haptic_effect[last_haptic_effect_pos].effect_delay = effect_delay; last_haptic_effect[last_haptic_effect_pos].effect_attack = effect_attack; last_haptic_effect[last_haptic_effect_pos].effect_fade = effect_fade; last_haptic_effect[last_haptic_effect_pos].effect_x = effect_x; last_haptic_effect[last_haptic_effect_pos].effect_y = effect_y; last_haptic_effect[last_haptic_effect_pos].effect_z = effect_z; last_haptic_effect[last_haptic_effect_pos].effect_id = IN_Haptic_Effect_Init( effect_x, effect_y, effect_z, effect_duration, haptic_volume, effect_delay, effect_attack, effect_fade); return last_haptic_effect[last_haptic_effect_pos].effect_id; } // Keep it same with rumble rules, look for descriptions to rumble // filtering in Controller_Rumble static char *default_haptic_filter = ( // skipped files should be before wider rule "!weapons/*grenlb " // bouncing grenades don't have feedback "!weapons/*hgrenb " // bouncing grenades don't have feedback "!weapons/*open " // rogue's items don't have feedback "!weapons/*warn " // rogue's items don't have feedback // any weapons that are not in previous list "weapons/ " // player{,s} effects "player/*land " // fall without injury "player/*burn " "player/*pain " "player/*fall " "player/*death " "players/*burn " "players/*pain " "players/*fall " "players/*death " // environment effects "doors/ " "plats/ " "world/*dish " "world/*drill2a " "world/*dr_ " "world/*explod1 " "world/*rocks " "world/*rumble " "world/*quake " "world/*train2 " ); /* * name: sound name * filter: sound name rule with '*' * return false for empty filter */ static qboolean Haptic_Feedback_Filtered_Line(const char *name, const char *filter) { const char *current_filter = filter; // skip empty filter if (!*current_filter) { return false; } while (*current_filter) { char part_filter[MAX_QPATH]; const char *name_part; const char *str_end; str_end = strchr(current_filter, '*'); if (!str_end) { if (!strstr(name, current_filter)) { // no such part in string return false; } // have such part break; } // copy filter line if ((str_end - current_filter) >= MAX_QPATH) { return false; } memcpy(part_filter, current_filter, str_end - current_filter); part_filter[str_end - current_filter] = 0; // place part in name name_part = strstr(name, part_filter); if (!name_part) { // no such part in string return false; } // have such part name = name_part + strlen(part_filter); // move to next filter current_filter = str_end + 1; } return true; } /* * name: sound name * filter: sound names separated by space, and '!' for skip file */ static qboolean Haptic_Feedback_Filtered(const char *name, const char *filter) { const char *current_filter = filter; while (*current_filter) { char line_filter[MAX_QPATH]; const char *str_end; str_end = strchr(current_filter, ' '); // its end of filter if (!str_end) { // check rules inside line if (Haptic_Feedback_Filtered_Line(name, current_filter)) { return true; } return false; } // copy filter line if ((str_end - current_filter) >= MAX_QPATH) { return false; } memcpy(line_filter, current_filter, str_end - current_filter); line_filter[str_end - current_filter] = 0; // check rules inside line if (*line_filter == '!') { // has invert rule if (Haptic_Feedback_Filtered_Line(name, line_filter + 1)) { return false; } } else { if (Haptic_Feedback_Filtered_Line(name, line_filter)) { return true; } } // move to next filter current_filter = str_end + 1; } return false; } /* * Haptic Feedback: * effect_volume=0..SHRT_MAX * effect{x,y,z} - effect direction * effect{delay,attack,fade} - effect durations * effect_distance - distance to sound source * name - sound file name */ void Haptic_Feedback(const char *name, int effect_volume, int effect_duration, int effect_delay, int effect_attack, int effect_fade, int effect_x, int effect_y, int effect_z, float effect_distance) { float max_distance = joy_haptic_distance->value; if (!joystick_haptic || joy_haptic_magnitude->value <= 0 || max_distance <= 0 || /* skip haptic if distance is negative */ effect_distance > max_distance || effect_volume <= 0 || effect_duration <= 0 || last_haptic_effect_size <= 0) /* haptic but without slots? */ { return; } /* combine distance and volume */ effect_volume *= (max_distance - effect_distance) / max_distance; if (last_haptic_volume != (int)(joy_haptic_magnitude->value * 16)) { IN_Haptic_Effects_Shutdown(); IN_Haptic_Effects_Init(); } last_haptic_volume = joy_haptic_magnitude->value * 16; if (Haptic_Feedback_Filtered(name, haptic_feedback_filter->string)) { int effect_id; effect_id = IN_Haptic_GetEffectId(effect_volume, effect_duration, effect_delay, effect_attack, effect_fade, effect_x, effect_y, effect_z); if (effect_id == -1) { /* have rumble used some slots in haptic effect list?, * ok, use little bit less haptic effects at the same time*/ IN_Haptic_Effects_Shutdown(); last_haptic_effect_size --; Com_Printf("%d haptic effects at the same time\n", last_haptic_effect_size); return; } SDL_HapticRunEffect(joystick_haptic, effect_id, 1); } } /* * Controller_Rumble: * name = sound file name * effect_volume = 0..USHRT_MAX * effect_duration is in ms * source = origin of audio * from_player = if source is the client (player) */ void Controller_Rumble(const char *name, vec3_t source, qboolean from_player, unsigned int duration, unsigned short int volume) { vec_t intens = 0.0f, low_freq = 1.0f, hi_freq = 1.0f, dist_prop; unsigned short int max_distance = 4; unsigned int effect_volume; if (!show_haptic || !controller || joy_haptic_magnitude->value <= 0 || volume == 0 || duration == 0) { return; } if (strstr(name, "weapons/")) { intens = 1.75f; if (strstr(name, "/blastf") || strstr(name, "/hyprbf") || strstr(name, "/nail")) { intens = 0.125f; // dampen blasters and nailgun's fire low_freq = 0.7f; hi_freq = 1.2f; } else if (strstr(name, "/shotgf") || strstr(name, "/rocklf")) { low_freq = 1.1f; // shotgun & RL shouldn't feel so weak duration *= 0.7; } else if (strstr(name, "/sshotf")) { duration *= 0.6; // the opposite for super shotgun } else if (strstr(name, "/machgf") || strstr(name, "/disint")) { intens = 1.125f; // machine gun & disruptor fire } else if (strstr(name, "/grenlb") || strstr(name, "/hgrenb") // bouncing grenades || strstr(name, "open") || strstr(name, "warn")) // rogue's items { return; // ... don't have feedback } else if (strstr(name, "/plasshot")) // phalanx cannon { intens = 1.0f; hi_freq = 0.3f; duration *= 0.5; } else if (strstr(name, "x")) // explosions... { low_freq = 1.1f; hi_freq = 0.9f; max_distance = 550; // can be felt far away } else if (strstr(name, "r")) // reloads & ion ripper fire { low_freq = 0.1f; hi_freq = 0.6f; } } else if (strstr(name, "player/land")) { intens = 2.2f; // fall without injury low_freq = 1.1f; } else if (strstr(name, "player/") || strstr(name, "players/")) { low_freq = 1.2f; // exaggerate player damage if (strstr(name, "/burn") || strstr(name, "/pain100") || strstr(name, "/pain75")) { intens = 2.4f; } else if (strstr(name, "/fall") || strstr(name, "/pain50") || strstr(name, "/pain25")) { intens = 2.7f; } else if (strstr(name, "/death")) { intens = 2.9f; } } else if (strstr(name, "doors/")) { intens = 0.125f; low_freq = 0.4f; max_distance = 280; } else if (strstr(name, "plats/")) { intens = 1.0f; // platforms rumble... max_distance = 200; // when player near them } else if (strstr(name, "world/")) { max_distance = 3500; // ambient events if (strstr(name, "/dish") || strstr(name, "/drill2a") || strstr(name, "/dr_") || strstr(name, "/explod1") || strstr(name, "/rocks") || strstr(name, "/rumble")) { intens = 0.28f; low_freq = 0.7f; } else if (strstr(name, "/quake")) { intens = 0.67f; // (earth)quakes are more evident low_freq = 1.2f; } else if (strstr(name, "/train2")) { intens = 0.28f; max_distance = 290; // just machinery } } if (intens == 0.0f) { return; } if (from_player) { dist_prop = 1.0f; } else { dist_prop = VectorLength(source); if (dist_prop > max_distance) { return; } dist_prop = (max_distance - dist_prop) / max_distance; } effect_volume = joy_haptic_magnitude->value * intens * dist_prop * volume; low_freq = Q_min(effect_volume * low_freq, USHRT_MAX); hi_freq = Q_min(effect_volume * hi_freq, USHRT_MAX); // Com_Printf("%-29s: vol %5u - %4u ms - dp %.3f l %5.0f h %5.0f\n", // name, effect_volume, duration, dist_prop, low_freq, hi_freq); #if SDL_VERSION_ATLEAST(2, 0, 9) if (SDL_GameControllerRumble(controller, low_freq, hi_freq, duration) == -1) { if (!joystick_haptic) { /* no haptic, some other reason of error */ return; } /* All haptic/force feedback slots are busy, try to clean up little bit. */ IN_Haptic_Effects_Shutdown(); } #endif } /* * Gyro calibration functions, called from menu */ void StartCalibration(void) { #ifdef NATIVE_SDL_GYRO num_samples = 0; #else num_samples[0] = num_samples[1] = num_samples[2] = 0; #endif gyro_accum[0] = 0.0; gyro_accum[1] = 0.0; gyro_accum[2] = 0.0; updates_countdown = 300; countdown_reason = REASON_GYROCALIBRATION; } qboolean IsCalibrationZero(void) { return (!gyro_calibration_x->value && !gyro_calibration_y->value && !gyro_calibration_z->value); } /* * Game Controller */ static void IN_Controller_Init(qboolean notify_user) { cvar_t *cvar; int nummappings; char controllerdb[MAX_OSPATH] = {0}; SDL_Joystick *joystick = NULL; SDL_bool is_controller = SDL_FALSE; cvar = Cvar_Get("in_sdlbackbutton", "0", CVAR_ARCHIVE); if (cvar) { switch ((int)cvar->value) { case 1: sdl_back_button = SDL_CONTROLLER_BUTTON_START; break; case 2: sdl_back_button = SDL_CONTROLLER_BUTTON_GUIDE; break; default: sdl_back_button = SDL_CONTROLLER_BUTTON_BACK; } } cvar = Cvar_Get("in_initjoy", "1", CVAR_NOSET); if (!cvar->value) { return; } if (notify_user) { Com_Printf("- Game Controller init attempt -\n"); } if (!SDL_WasInit(SDL_INIT_GAMECONTROLLER)) { #ifdef SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE // extended input reports on PS controllers (enables gyro thru bluetooth) SDL_SetHint( SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1" ); #endif #ifdef SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE SDL_SetHint( SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1" ); #endif if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) == -1) { Com_Printf ("Couldn't init SDL Game Controller: %s.\n", SDL_GetError()); return; } } Com_Printf ("%i joysticks were found.\n", SDL_NumJoysticks()); if (!SDL_NumJoysticks()) { joystick_haptic = SDL_HapticOpenFromMouse(); if (joystick_haptic && (SDL_HapticQuery(joystick_haptic) & SDL_HAPTIC_SINE) == 0) { /* Disable haptic for joysticks without SINE */ SDL_HapticClose(joystick_haptic); joystick_haptic = NULL; } if (joystick_haptic) { IN_Haptic_Effects_Info(); show_haptic = true; } return; } for (const char* rawPath = FS_GetNextRawPath(NULL); rawPath != NULL; rawPath = FS_GetNextRawPath(rawPath)) { snprintf(controllerdb, MAX_OSPATH, "%s/gamecontrollerdb.txt", rawPath); nummappings = SDL_GameControllerAddMappingsFromFile(controllerdb); if (nummappings > 0) Com_Printf ("%d mappings loaded from gamecontrollerdb.txt\n", nummappings); } for (int i = 0; i < SDL_NumJoysticks(); i++) { joystick = SDL_JoystickOpen(i); if (!joystick) { Com_Printf ("Couldn't open joystick %d: %s.\n", i+1, SDL_GetError()); continue; // try next joystick } const char* joystick_name = SDL_JoystickName(joystick); const int name_len = strlen(joystick_name); Com_Printf ("Trying joystick %d, '%s'\n", i+1, joystick_name); // Ugly hack to detect IMU-only devices - works for Switch controllers at least if (name_len > 4 && !strncmp(joystick_name + name_len - 4, " IMU", 4)) { SDL_JoystickClose(joystick); joystick = NULL; #ifdef NATIVE_SDL_GYRO Com_Printf ("Skipping IMU device.\n"); #else // if it's not a Left JoyCon, use it as Gyro Com_Printf ("IMU device found.\n"); if ( !imu_joystick && name_len > 16 && strncmp(joystick_name + name_len - 16, "Left Joy-Con IMU", 16) != 0 ) { imu_joystick = SDL_JoystickOpen(i); if (imu_joystick) { show_gyro = true; Com_Printf ("Using this device as Gyro sensor.\n"); } else { Com_Printf ("Couldn't open IMU: %s.\n", SDL_GetError()); } } #endif continue; } Com_Printf ("Buttons = %d, Axes = %d, Hats = %d\n", SDL_JoystickNumButtons(joystick), SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick)); is_controller = SDL_IsGameController(i); if (!is_controller) { char joystick_guid[65] = {0}; SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(i); SDL_JoystickGetGUIDString(guid, joystick_guid, 64); Com_Printf ("To use joystick as game controller, provide its config by either:\n" " * Putting 'gamecontrollerdb.txt' file in your game directory.\n" " * Or setting SDL_GAMECONTROLLERCONFIG environment variable. E.g.:\n"); Com_Printf ("SDL_GAMECONTROLLERCONFIG='%s,%s,leftx:a0,lefty:a1,rightx:a2,righty:a3,back:b1,...'\n", joystick_guid, joystick_name); } SDL_JoystickClose(joystick); joystick = NULL; if (is_controller && !controller) { controller = SDL_GameControllerOpen(i); if (!controller) { Com_Printf("SDL Controller error: %s.\n", SDL_GetError()); continue; // try next joystick } show_gamepad = true; Com_Printf("Enabled as Game Controller, settings:\n%s\n", SDL_GameControllerMapping(controller)); #ifdef NATIVE_SDL_GYRO if ( SDL_GameControllerHasSensor(controller, SDL_SENSOR_GYRO) && !SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE) ) { show_gyro = true; #if SDL_VERSION_ATLEAST(2, 0, 16) Com_Printf( "Gyro sensor enabled at %.2f Hz\n", SDL_GameControllerGetSensorDataRate(controller, SDL_SENSOR_GYRO) ); #else Com_Printf( "Gyro sensor enabled.\n" ); #endif // #if SDL_VERSION_ATLEAST(2, 0, 16) } else { Com_Printf("Gyro sensor not found.\n"); } if ( SDL_GameControllerHasLED(controller) ) { SDL_GameControllerSetLED(controller, 0, 80, 0); // green light } #endif // NATIVE_SDL_GYRO joystick_haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(controller)); if (joystick_haptic && (SDL_HapticQuery(joystick_haptic) & SDL_HAPTIC_SINE) == 0) { /* Disable haptic for joysticks without SINE */ SDL_HapticClose(joystick_haptic); joystick_haptic = NULL; } if (joystick_haptic) { IN_Haptic_Effects_Info(); show_haptic = true; } #if SDL_VERSION_ATLEAST(2, 0, 18) // support for query on features from controller if (SDL_GameControllerHasRumble(controller)) #elif SDL_VERSION_ATLEAST(2, 0, 9) // support for rumble if (SDL_GameControllerRumble(controller, 1, 1, 1) == 0) #else // no rumble support on SDL < 2.0.9 if (false) #endif { show_haptic = true; Com_Printf("Rumble support available.\n"); } else { Com_Printf("Controller doesn't support rumble.\n"); } #ifdef NATIVE_SDL_GYRO // "native" exits when finding a single working controller break; #endif } } } /* * Initializes the backend */ void IN_Init(void) { Com_Printf("------- input initialization -------\n"); mouse_x = mouse_y = 0; joystick_left_x = joystick_left_y = joystick_right_x = joystick_right_y = 0; gyro_yaw = gyro_pitch = 0; exponential_speedup = Cvar_Get("exponential_speedup", "0", CVAR_ARCHIVE); freelook = Cvar_Get("freelook", "1", CVAR_ARCHIVE); in_grab = Cvar_Get("in_grab", "2", CVAR_ARCHIVE); lookstrafe = Cvar_Get("lookstrafe", "0", CVAR_ARCHIVE); m_filter = Cvar_Get("m_filter", "0", CVAR_ARCHIVE); m_up = Cvar_Get("m_up", "1", CVAR_ARCHIVE); m_forward = Cvar_Get("m_forward", "1", CVAR_ARCHIVE); m_pitch = Cvar_Get("m_pitch", "0.022", CVAR_ARCHIVE); m_side = Cvar_Get("m_side", "0.8", CVAR_ARCHIVE); m_yaw = Cvar_Get("m_yaw", "0.022", CVAR_ARCHIVE); sensitivity = Cvar_Get("sensitivity", "3", CVAR_ARCHIVE); joy_haptic_magnitude = Cvar_Get("joy_haptic_magnitude", "0.0", CVAR_ARCHIVE); joy_haptic_distance = Cvar_Get("joy_haptic_distance", "100.0", CVAR_ARCHIVE); haptic_feedback_filter = Cvar_Get("joy_haptic_filter", default_haptic_filter, 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_layout = Cvar_Get("joy_layout", "0", CVAR_ARCHIVE); joy_left_expo = Cvar_Get("joy_left_expo", "2.0", CVAR_ARCHIVE); joy_left_snapaxis = Cvar_Get("joy_left_snapaxis", "0.15", CVAR_ARCHIVE); joy_left_deadzone = Cvar_Get("joy_left_deadzone", "0.16", CVAR_ARCHIVE); joy_right_expo = Cvar_Get("joy_right_expo", "2.0", CVAR_ARCHIVE); joy_right_snapaxis = Cvar_Get("joy_right_snapaxis", "0.15", CVAR_ARCHIVE); joy_right_deadzone = Cvar_Get("joy_right_deadzone", "0.16", CVAR_ARCHIVE); joy_flick_threshold = Cvar_Get("joy_flick_threshold", "0.65", CVAR_ARCHIVE); joy_flick_smoothed = Cvar_Get("joy_flick_smoothed", "8.0", CVAR_ARCHIVE); gyro_calibration_x = Cvar_Get("gyro_calibration_x", "0.0", CVAR_ARCHIVE); gyro_calibration_y = Cvar_Get("gyro_calibration_y", "0.0", CVAR_ARCHIVE); gyro_calibration_z = Cvar_Get("gyro_calibration_z", "0.0", CVAR_ARCHIVE); gyro_yawsensitivity = Cvar_Get("gyro_yawsensitivity", "1.0", CVAR_ARCHIVE); gyro_pitchsensitivity = Cvar_Get("gyro_pitchsensitivity", "1.0", CVAR_ARCHIVE); gyro_tightening = Cvar_Get("gyro_tightening", "3.5", CVAR_ARCHIVE); gyro_turning_axis = Cvar_Get("gyro_turning_axis", "0", CVAR_ARCHIVE); gyro_mode = Cvar_Get("gyro_mode", "2", CVAR_ARCHIVE); if ((int)gyro_mode->value == 2) { gyro_active = true; } windowed_pauseonfocuslost = Cvar_Get("vid_pauseonfocuslost", "0", CVAR_USERINFO | CVAR_ARCHIVE); windowed_mouse = Cvar_Get("windowed_mouse", "1", CVAR_USERINFO | CVAR_ARCHIVE); Cmd_AddCommand("+mlook", IN_MLookDown); Cmd_AddCommand("-mlook", IN_MLookUp); Cmd_AddCommand("+joyaltselector", IN_JoyAltSelectorDown); Cmd_AddCommand("-joyaltselector", IN_JoyAltSelectorUp); Cmd_AddCommand("+gyroaction", IN_GyroActionDown); Cmd_AddCommand("-gyroaction", IN_GyroActionUp); SDL_StartTextInput(); IN_Controller_Init(false); Com_Printf("------------------------------------\n\n"); } /* * Shuts the backend down */ static void IN_Haptic_Shutdown(void) { if (joystick_haptic) { IN_Haptic_Effects_Shutdown(); SDL_HapticClose(joystick_haptic); joystick_haptic = NULL; } } static void IN_Controller_Shutdown(qboolean notify_user) { if (notify_user) { Com_Printf("- Game Controller disconnected -\n"); } IN_Haptic_Shutdown(); if (controller) { SDL_GameControllerClose(controller); controller = NULL; } show_gamepad = show_gyro = show_haptic = false; joystick_left_x = joystick_left_y = joystick_right_x = joystick_right_y = 0; gyro_yaw = gyro_pitch = 0; #ifndef NATIVE_SDL_GYRO if (imu_joystick) { SDL_JoystickClose(imu_joystick); imu_joystick = NULL; } #endif } /* * Shuts the backend down */ void IN_Shutdown(void) { Cmd_RemoveCommand("force_centerview"); Cmd_RemoveCommand("+mlook"); Cmd_RemoveCommand("-mlook"); Cmd_RemoveCommand("+joyaltselector"); Cmd_RemoveCommand("-joyaltselector"); Cmd_RemoveCommand("+gyroaction"); Cmd_RemoveCommand("-gyroaction"); Com_Printf("Shutting down input.\n"); IN_Controller_Shutdown(false); const Uint32 subsystems = SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC; if (SDL_WasInit(subsystems) == subsystems) SDL_QuitSubSystem(subsystems); } /* ------------------------------------------------------------------ */ yquake2-QUAKE2_8_40/src/client/input/sdl3.c000066400000000000000000001655771465112212000203530ustar00rootroot00000000000000/* * 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. * * Joystick reading and deadzone handling is based on: * http://joshsutphin.com/2013/04/12/doing-thumbstick-dead-zones-right.html * ...and implementation is partially based on code from: * - http://quakespasm.sourceforge.net * - https://github.com/Minimuino/thumbstick-deadzones * * Flick Stick handling is based on: * http://gyrowiki.jibbsmart.com/blog:good-gyro-controls-part-2:the-flick-stick * * ======================================================================= * * This is the Quake II input system backend, implemented with SDL. * * ======================================================================= */ #include #include "SDL3/SDL_gamepad.h" #include "SDL3/SDL_properties.h" #include "SDL3/SDL_stdinc.h" #include "header/input.h" #include "../header/keyboard.h" #include "../header/client.h" // ---- // Maximal mouse move per frame #define MOUSE_MAX 3000 // Minimal mouse move per frame #define MOUSE_MIN 40 // ---- enum { LAYOUT_DEFAULT = 0, LAYOUT_SOUTHPAW, LAYOUT_LEGACY, LAYOUT_LEGACY_SOUTHPAW, LAYOUT_FLICK_STICK, LAYOUT_FLICK_STICK_SOUTHPAW }; typedef struct { float x; float y; } thumbstick_t; typedef enum { REASON_NONE, REASON_CONTROLLERINIT, REASON_GYROCALIBRATION } updates_countdown_reasons; // ---- // These are used to communicate the events collected by // IN_Update() called at the beginning of a frame to the // actual movement functions called at a later time. static float mouse_x, mouse_y; static unsigned char sdl_back_button = SDL_GAMEPAD_BUTTON_BACK; static int joystick_left_x, joystick_left_y, joystick_right_x, joystick_right_y; static float gyro_yaw, gyro_pitch; static qboolean mlooking; // The last time input events were processed. // Used throughout the client. int sys_frame_time; // the joystick altselector that turns K_BTN_X into K_BTN_X_ALT // is pressed qboolean joy_altselector_pressed = false; // Console Variables cvar_t *freelook; cvar_t *lookstrafe; cvar_t *m_forward; cvar_t *m_pitch; cvar_t *m_side; cvar_t *m_up; cvar_t *m_yaw; static cvar_t *sensitivity; static cvar_t *exponential_speedup; static cvar_t *in_grab; static cvar_t *m_filter; static cvar_t *windowed_pauseonfocuslost; static cvar_t *windowed_mouse; static cvar_t *haptic_feedback_filter; // ---- typedef struct haptic_effects_cache { int effect_volume; int effect_duration; int effect_delay; int effect_attack; int effect_fade; int effect_id; int effect_x; int effect_y; int effect_z; } haptic_effects_cache_t; qboolean show_gamepad = false, show_haptic = false, show_gyro = false; static SDL_Haptic *joystick_haptic = NULL; static SDL_Gamepad *controller = NULL; #define HAPTIC_EFFECT_LIST_SIZE 16 static int last_haptic_volume = 0; static int last_haptic_effect_size = HAPTIC_EFFECT_LIST_SIZE; static int last_haptic_effect_pos = 0; static haptic_effects_cache_t last_haptic_effect[HAPTIC_EFFECT_LIST_SIZE]; // Joystick sensitivity static cvar_t *joy_yawsensitivity; static cvar_t *joy_pitchsensitivity; static cvar_t *joy_forwardsensitivity; static cvar_t *joy_sidesensitivity; // Joystick's analog sticks configuration cvar_t *joy_layout; static cvar_t *joy_left_expo; static cvar_t *joy_left_snapaxis; static cvar_t *joy_left_deadzone; static cvar_t *joy_right_expo; static cvar_t *joy_right_snapaxis; static cvar_t *joy_right_deadzone; static cvar_t *joy_flick_threshold; static cvar_t *joy_flick_smoothed; // Joystick haptic static cvar_t *joy_haptic_magnitude; static cvar_t *joy_haptic_distance; // Gyro mode (0=off, 3=on, 1-2=uses button to enable/disable) cvar_t *gyro_mode; cvar_t *gyro_turning_axis; // yaw or roll // Gyro sensitivity static cvar_t *gyro_yawsensitivity; static cvar_t *gyro_pitchsensitivity; static cvar_t *gyro_tightening; // Gyro is being used in this very moment static qboolean gyro_active = false; // Gyro calibration static float gyro_accum[3]; static cvar_t *gyro_calibration_x; static cvar_t *gyro_calibration_y; static cvar_t *gyro_calibration_z; static unsigned int num_samples; #define NATIVE_SDL_GYRO // uses SDL_CONTROLLERSENSORUPDATE to read gyro // To ignore SDL_JOYDEVICEADDED at game init. Allows for hot plugging of game controller afterwards. static qboolean first_init = true; // Countdown of calls to IN_Update(), needed for controller init and gyro calibration static unsigned short int updates_countdown = 30; // Reason for the countdown static updates_countdown_reasons countdown_reason = REASON_CONTROLLERINIT; // Flick Stick #define FLICK_TIME 6 // number of frames it takes for a flick to execute static float target_angle; // angle to end up facing at the end of a flick static unsigned short int flick_progress = FLICK_TIME; // Flick Stick's rotation input samples to smooth out #define MAX_SMOOTH_SAMPLES 8 static float flick_samples[MAX_SMOOTH_SAMPLES]; static unsigned short int front_sample = 0; extern void CalibrationFinishedCallback(void); /* ------------------------------------------------------------------ */ /* * 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_TAB: key = K_TAB; break; case SDLK_RETURN: key = K_ENTER; break; case SDLK_ESCAPE: key = K_ESCAPE; break; case SDLK_BACKSPACE: key = K_BACKSPACE; break; case SDLK_LGUI: case SDLK_RGUI: key = K_COMMAND; // Win key break; case SDLK_CAPSLOCK: key = K_CAPSLOCK; break; case SDLK_POWER: key = K_POWER; break; case SDLK_PAUSE: key = K_PAUSE; break; case SDLK_UP: key = K_UPARROW; break; case SDLK_DOWN: key = K_DOWNARROW; break; case SDLK_LEFT: key = K_LEFTARROW; break; case SDLK_RIGHT: key = K_RIGHTARROW; break; case SDLK_RALT: case SDLK_LALT: key = K_ALT; break; case SDLK_LCTRL: case SDLK_RCTRL: key = K_CTRL; break; case SDLK_LSHIFT: case SDLK_RSHIFT: key = K_SHIFT; break; case SDLK_INSERT: key = K_INS; break; case SDLK_DELETE: key = K_DEL; break; case SDLK_PAGEDOWN: key = K_PGDN; break; case SDLK_PAGEUP: key = K_PGUP; break; case SDLK_HOME: key = K_HOME; break; case SDLK_END: key = K_END; 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_KP_7: key = K_KP_HOME; break; case SDLK_KP_8: key = K_KP_UPARROW; break; case SDLK_KP_9: key = K_KP_PGUP; break; case SDLK_KP_4: key = K_KP_LEFTARROW; break; case SDLK_KP_5: key = K_KP_5; break; case SDLK_KP_6: key = K_KP_RIGHTARROW; break; case SDLK_KP_1: key = K_KP_END; break; case SDLK_KP_2: key = K_KP_DOWNARROW; break; case SDLK_KP_3: key = K_KP_PGDN; break; case SDLK_KP_ENTER: key = K_KP_ENTER; break; case SDLK_KP_0: key = K_KP_INS; break; case SDLK_KP_PERIOD: key = K_KP_DEL; break; case SDLK_KP_DIVIDE: key = K_KP_SLASH; break; case SDLK_KP_MINUS: key = K_KP_MINUS; break; case SDLK_KP_PLUS: key = K_KP_PLUS; break; case SDLK_NUMLOCKCLEAR: key = K_KP_NUMLOCK; break; case SDLK_KP_MULTIPLY: key = K_KP_STAR; break; case SDLK_KP_EQUALS: key = K_KP_EQUALS; break; // TODO: K_SUPER ? Win Key is already K_COMMAND case SDLK_APPLICATION: key = K_COMPOSE; break; case SDLK_MODE: key = K_MODE; break; case SDLK_HELP: key = K_HELP; break; case SDLK_PRINTSCREEN: key = K_PRINT; break; case SDLK_SYSREQ: key = K_SYSREQ; break; case SDLK_SCROLLLOCK: key = K_SCROLLOCK; break; case SDLK_MENU: key = K_MENU; break; case SDLK_UNDO: key = K_UNDO; break; default: break; } return key; } static int IN_TranslateScancodeToQ2Key(SDL_Scancode sc) { #define MY_SC_CASE(X) case SDL_SCANCODE_ ## X : return K_SC_ ## X; switch( (int)sc ) // cast to int to shut -Wswitch up { // case SDL_SCANCODE_A : return K_SC_A; MY_SC_CASE(A) MY_SC_CASE(B) MY_SC_CASE(C) MY_SC_CASE(D) MY_SC_CASE(E) MY_SC_CASE(F) MY_SC_CASE(G) MY_SC_CASE(H) MY_SC_CASE(I) MY_SC_CASE(J) MY_SC_CASE(K) MY_SC_CASE(L) MY_SC_CASE(M) MY_SC_CASE(N) MY_SC_CASE(O) MY_SC_CASE(P) MY_SC_CASE(Q) MY_SC_CASE(R) MY_SC_CASE(S) MY_SC_CASE(T) MY_SC_CASE(U) MY_SC_CASE(V) MY_SC_CASE(W) MY_SC_CASE(X) MY_SC_CASE(Y) MY_SC_CASE(Z) MY_SC_CASE(MINUS) MY_SC_CASE(EQUALS) MY_SC_CASE(LEFTBRACKET) MY_SC_CASE(RIGHTBRACKET) MY_SC_CASE(BACKSLASH) MY_SC_CASE(NONUSHASH) MY_SC_CASE(SEMICOLON) MY_SC_CASE(APOSTROPHE) MY_SC_CASE(GRAVE) MY_SC_CASE(COMMA) MY_SC_CASE(PERIOD) MY_SC_CASE(SLASH) MY_SC_CASE(NONUSBACKSLASH) MY_SC_CASE(INTERNATIONAL1) MY_SC_CASE(INTERNATIONAL2) MY_SC_CASE(INTERNATIONAL3) MY_SC_CASE(INTERNATIONAL4) MY_SC_CASE(INTERNATIONAL5) MY_SC_CASE(INTERNATIONAL6) MY_SC_CASE(INTERNATIONAL7) MY_SC_CASE(INTERNATIONAL8) MY_SC_CASE(INTERNATIONAL9) MY_SC_CASE(THOUSANDSSEPARATOR) MY_SC_CASE(DECIMALSEPARATOR) MY_SC_CASE(CURRENCYUNIT) MY_SC_CASE(CURRENCYSUBUNIT) } #undef MY_SC_CASE return 0; } static void IN_Controller_Init(qboolean notify_user); static void IN_Controller_Shutdown(qboolean notify_user); qboolean IN_NumpadIsOn() { SDL_Keymod mod = SDL_GetModState(); if ((mod & SDL_KMOD_NUM) == SDL_KMOD_NUM) { return true; } return false; } /* ------------------------------------------------------------------ */ /* * 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; static qboolean left_trigger = false; static qboolean right_trigger = false; static int consoleKeyCode = 0; /* Get and process an event */ while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_EVENT_MOUSE_WHEEL : 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; case SDL_EVENT_MOUSE_BUTTON_DOWN : case SDL_EVENT_MOUSE_BUTTON_UP : 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_EVENT_MOUSE_BUTTON_DOWN), true); break; case SDL_EVENT_MOUSE_MOTION : if (cls.key_dest == key_game && (int) cl_paused->value == 0) { mouse_x += event.motion.xrel; mouse_y += event.motion.yrel; } break; case SDL_EVENT_TEXT_INPUT : { int c = event.text.text[0]; // also make sure we don't get the char that corresponds to the // "console key" (like "^" or "`") as text input if ((c >= ' ') && (c <= '~') && c != consoleKeyCode) { Char_Event(c); } } break; case SDL_EVENT_KEY_DOWN : case SDL_EVENT_KEY_UP : { qboolean down = (event.type == SDL_EVENT_KEY_DOWN); /* 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 { SDL_Keycode kc = event.key.keysym.sym; if(sc == SDL_SCANCODE_GRAVE && kc != '\'' && kc != '"') { // special case/hack: open the console with the "console key" // (beneath Esc, left of 1, above Tab) // but not if the keycode for this is a quote (like on Brazilian // keyboards) - otherwise you couldn't type them in the console if((event.key.keysym.mod & (SDL_KMOD_CAPS|SDL_KMOD_SHIFT|SDL_KMOD_ALT|SDL_KMOD_CTRL|SDL_KMOD_GUI)) == 0) { // also, only do this if no modifiers like shift or AltGr or whatever are pressed // so kc will most likely be the ascii char generated by this and can be ignored // in case SDL_TEXTINPUT above (so we don't get ^ or whatever as text in console) // (can't just check for mod == 0 because numlock is a KMOD too) Key_Event(K_CONSOLE, down, true); consoleKeyCode = kc; } } else if ((kc >= SDLK_SPACE) && (kc < SDLK_DELETE)) { Key_Event(kc, down, false); } else { int key = IN_TranslateSDLtoQ2Key(kc); if(key == 0) { // fallback to scancodes if we don't know the keycode key = IN_TranslateScancodeToQ2Key(sc); } if(key != 0) { Key_Event(key, down, true); } else { Com_DPrintf("Pressed unknown key with SDL_Keycode %d, SDL_Scancode %d.\n", kc, (int)sc); } } } break; } case SDL_EVENT_WINDOW_FOCUS_LOST: { Key_MarkAllUp(); S_Activate(false); if (windowed_pauseonfocuslost->value != 1) { Cvar_SetValue("paused", 1); } /* pause music */ if (Cvar_VariableValue("ogg_pausewithgame") == 1 && OGG_Status() == PLAY && cl.attractloop == false) { Cbuf_AddText("ogg toggle\n"); } break; } case SDL_EVENT_WINDOW_FOCUS_GAINED: { S_Activate(true); if (windowed_pauseonfocuslost->value == 2) { Cvar_SetValue("paused", 0); } /* play music */ if (Cvar_VariableValue("ogg_pausewithgame") == 1 && OGG_Status() == PAUSE && cl.attractloop == false && cl_paused->value == 0) { Cbuf_AddText("ogg toggle\n"); } break; } case SDL_EVENT_WINDOW_MOVED: { // make sure GLimp_GetRefreshRate() will query from SDL again - the window might // be on another display now! glimp_refreshRate = -1.0; break; } case SDL_EVENT_WINDOW_SHOWN: { if (cl_unpaused_scvis->value > 0) { Cvar_SetValue("paused", 0); } /* play music */ if (Cvar_VariableValue("ogg_pausewithgame") == 1 && OGG_Status() == PAUSE && cl.attractloop == false && cl_paused->value == 0) { Cbuf_AddText("ogg toggle\n"); } break; } case SDL_EVENT_GAMEPAD_BUTTON_UP : case SDL_EVENT_GAMEPAD_BUTTON_DOWN : { qboolean down = (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN); unsigned char btn = event.gbutton.button; // Handle Back Button, to override its original key Key_Event( (btn == sdl_back_button)? K_JOY_BACK : K_BTN_A + btn, down, true ); break; } case SDL_EVENT_GAMEPAD_AXIS_MOTION : /* Handle Controller Motion */ { int axis_value = event.gaxis.value; switch (event.gaxis.axis) { case SDL_GAMEPAD_AXIS_LEFT_TRIGGER : { qboolean new_left_trigger = axis_value > 8192; if (new_left_trigger != left_trigger) { left_trigger = new_left_trigger; Key_Event(K_TRIG_LEFT, left_trigger, true); } break; } case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER : { qboolean new_right_trigger = axis_value > 8192; if (new_right_trigger != right_trigger) { right_trigger = new_right_trigger; Key_Event(K_TRIG_RIGHT, right_trigger, true); } break; } } if (!cl_paused->value && cls.key_dest == key_game) { switch (event.gaxis.axis) { case SDL_GAMEPAD_AXIS_LEFTX : joystick_left_x = axis_value; break; case SDL_GAMEPAD_AXIS_LEFTY : joystick_left_y = axis_value; break; case SDL_GAMEPAD_AXIS_RIGHTX : joystick_right_x = axis_value; break; case SDL_GAMEPAD_AXIS_RIGHTY : joystick_right_y = axis_value; break; } } break; } #ifdef NATIVE_SDL_GYRO // controller sensors' reading supported (gyro, accelerometer) case SDL_EVENT_GAMEPAD_SENSOR_UPDATE : if (event.gsensor.sensor != SDL_SENSOR_GYRO) { break; } if (countdown_reason == REASON_GYROCALIBRATION && updates_countdown) { gyro_accum[0] += event.gsensor.data[0]; gyro_accum[1] += event.gsensor.data[1]; gyro_accum[2] += event.gsensor.data[2]; num_samples++; break; } #else // gyro read as "secondary joystick" case SDL_EVENT_JOYSTICK_AXIS_MOTION : if ( !imu_joystick || event.gdevice.which != SDL_GetJoystickInstanceID(imu_joystick) ) { break; // controller axes handled by SDL_CONTROLLERAXISMOTION } int axis_value = event.gaxis.value; if (countdown_reason == REASON_GYROCALIBRATION && updates_countdown) { switch (event.gaxis.axis) { case IMU_JOY_AXIS_GYRO_PITCH: gyro_accum[0] += axis_value; num_samples[0]++; break; case IMU_JOY_AXIS_GYRO_YAW: gyro_accum[1] += axis_value; num_samples[1]++; break; case IMU_JOY_AXIS_GYRO_ROLL: gyro_accum[2] += axis_value; num_samples[2]++; } break; } #endif // NATIVE_SDL_GYRO if (gyro_active && gyro_mode->value && !cl_paused->value && cls.key_dest == key_game) { #ifdef NATIVE_SDL_GYRO if (!gyro_turning_axis->value) { gyro_yaw = event.gsensor.data[1] - gyro_calibration_y->value; // yaw } else { gyro_yaw = -(event.gsensor.data[2] - gyro_calibration_z->value); // roll } gyro_pitch = event.gsensor.data[0] - gyro_calibration_x->value; #else // old "joystick" gyro switch (event.gaxis.axis) // inside "case SDL_JOYAXISMOTION" here { case IMU_JOY_AXIS_GYRO_PITCH: gyro_pitch = -(axis_value - gyro_calibration_x->value); break; case IMU_JOY_AXIS_GYRO_YAW: if (!gyro_turning_axis->value) { gyro_yaw = axis_value - gyro_calibration_y->value; } break; case IMU_JOY_AXIS_GYRO_ROLL: if (gyro_turning_axis->value) { gyro_yaw = axis_value - gyro_calibration_z->value; } } #endif // NATIVE_SDL_GYRO } else { gyro_yaw = gyro_pitch = 0; } break; case SDL_EVENT_GAMEPAD_REMOVED : if (!controller) { break; } if (event.gdevice.which == SDL_GetJoystickInstanceID(SDL_GetGamepadJoystick(controller))) { Cvar_SetValue("paused", 1); IN_Controller_Shutdown(true); IN_Controller_Init(false); } break; case SDL_EVENT_JOYSTICK_ADDED : if (!controller) { // This should be lower, but some controllers just don't want to get detected by the OS updates_countdown = 100; countdown_reason = REASON_CONTROLLERINIT; } break; case SDL_EVENT_JOYSTICK_BATTERY_UPDATED : if (!controller || event.jbattery.which != SDL_GetJoystickInstanceID(SDL_GetGamepadJoystick(controller))) { break; } if (event.jbattery.percent <= 20) { Com_Printf("WARNING: Gamepad battery Low, it is recommended to connect it by cable.\n"); } else if (event.jbattery.percent <= 1) { SCR_CenterPrint("ALERT: Gamepad battery almost Empty.\n"); } break; case SDL_EVENT_QUIT : Com_Quit(); break; } } /* Grab and ungrab the mouse if the console or the menu is opened */ if (in_grab->value == 3) { want_grab = windowed_mouse->value; } else { want_grab = (vid_fullscreen->value || in_grab->value == 1 || (in_grab->value == 2 && windowed_mouse->value)); } // calling GLimp_GrabInput() each frame is a bit ugly but simple and should work. // The called SDL functions return after a cheap check, if there's nothing to do. GLimp_GrabInput(want_grab); // We need to save the frame time so other subsystems // know the exact time of the last input events. sys_frame_time = Sys_Milliseconds(); // Hot plugging delay handling, to not be "overwhelmed" because some controllers // present themselves as two different devices, triggering SDL_JOYDEVICEADDED // too many times. They could trigger it even at game initialization. // Also used to keep time of the 'controller gyro calibration' pause. if (updates_countdown) { updates_countdown--; if (!updates_countdown) // Countdown finished, apply needed action by reason { switch (countdown_reason) { case REASON_CONTROLLERINIT: if (!first_init) { IN_Controller_Shutdown(false); IN_Controller_Init(true); } else { first_init = false; } break; case REASON_GYROCALIBRATION: // finish and save calibration { #ifdef NATIVE_SDL_GYRO const float inverseSamples = 1.f / num_samples; Cvar_SetValue("gyro_calibration_x", gyro_accum[0] * inverseSamples); Cvar_SetValue("gyro_calibration_y", gyro_accum[1] * inverseSamples); Cvar_SetValue("gyro_calibration_z", gyro_accum[2] * inverseSamples); #else if (!num_samples[0] || !num_samples[1] || !num_samples[2]) { Com_Printf("Calibration failed, please retry inside a level after having moved your controller a little.\n"); } else { Cvar_SetValue("gyro_calibration_x", gyro_accum[0] / num_samples[0]); Cvar_SetValue("gyro_calibration_y", gyro_accum[1] / num_samples[1]); Cvar_SetValue("gyro_calibration_z", gyro_accum[2] / num_samples[2]); } #endif Com_Printf("Calibration results:\n X=%f Y=%f Z=%f\n", gyro_calibration_x->value, gyro_calibration_y->value, gyro_calibration_z->value); CalibrationFinishedCallback(); break; } default: break; // avoiding compiler warning } countdown_reason = REASON_NONE; } } } /* * Joystick vector magnitude */ static float IN_StickMagnitude(thumbstick_t stick) { return sqrtf((stick.x * stick.x) + (stick.y * stick.y)); } /* * Scales "v" from [deadzone, 1] range to [0, 1] range, then inherits sign */ static float IN_MapRange(float v, float deadzone, float sign) { return ((v - deadzone) / (1 - deadzone)) * sign; } /* * Radial deadzone based on github.com/jeremiah-sypult/Quakespasm-Rift */ static thumbstick_t IN_RadialDeadzone(thumbstick_t stick, float deadzone) { thumbstick_t result = {0}; float magnitude = Q_min(IN_StickMagnitude(stick), 1.0f); deadzone = Q_min( Q_max(deadzone, 0.0f), 0.9f); // clamp to [0.0, 0.9] if ( magnitude > deadzone ) { const float scale = ((magnitude - deadzone) / (1.0 - deadzone)) / magnitude; result.x = stick.x * scale; result.y = stick.y * scale; } return result; } /* * Sloped axial deadzone based on github.com/Minimuino/thumbstick-deadzones * Provides a "snap-to-axis" feeling, without losing precision near the center of the stick */ static thumbstick_t IN_SlopedAxialDeadzone(thumbstick_t stick, float deadzone) { thumbstick_t result = {0}; float abs_x = fabsf(stick.x); float abs_y = fabsf(stick.y); float sign_x = copysignf(1.0f, stick.x); float sign_y = copysignf(1.0f, stick.y); deadzone = Q_min(deadzone, 0.5f); float deadzone_x = deadzone * abs_y; // deadzone of one axis depends... float deadzone_y = deadzone * abs_x; // ...on the value of the other axis if (abs_x > deadzone_x) { result.x = IN_MapRange(abs_x, deadzone_x, sign_x); } if (abs_y > deadzone_y) { result.y = IN_MapRange(abs_y, deadzone_y, sign_y); } return result; } /* * Exponent applied on stick magnitude */ static thumbstick_t IN_ApplyExpo(thumbstick_t stick, float exponent) { thumbstick_t result = {0}; float magnitude = IN_StickMagnitude(stick); if (magnitude == 0) { return result; } const float eased = powf(magnitude, exponent) / magnitude; result.x = stick.x * eased; result.y = stick.y * eased; return result; } /* * Minimize gyro movement when under a small threshold. * http://gyrowiki.jibbsmart.com/blog:good-gyro-controls-part-1:the-gyro-is-a-mouse#toc9 */ static thumbstick_t IN_TightenInput(float yaw, float pitch) { thumbstick_t input = { yaw, pitch }; const float magnitude = IN_StickMagnitude(input); const float threshold = (M_PI / 180.0f) * gyro_tightening->value; if (magnitude < threshold) { const float scale = magnitude / threshold; input.x *= scale; input.y *= scale; } return input; } /* * Delete flick stick's buffer of angle samples for smoothing */ static void IN_ResetSmoothSamples() { front_sample = 0; for (int i = 0; i < MAX_SMOOTH_SAMPLES; i++) { flick_samples[i] = 0.0f; } } /* * Soft tiered smoothing for angle rotations with Flick Stick * http://gyrowiki.jibbsmart.com/blog:tight-and-smooth:soft-tiered-smoothing */ static float IN_SmoothedStickRotation(float value) { float top_threshold = joy_flick_smoothed->value; float bottom_threshold = top_threshold / 2.0f; if (top_threshold == 0) { return value; } // sample in the circular smoothing buffer we want to write over front_sample = (front_sample + 1) % MAX_SMOOTH_SAMPLES; // if input > top threshold, it'll all be consumed immediately // 0 gets put into the smoothing buffer // if input < bottom threshold, it'll all be put in the smoothing buffer // 0 for immediate consumption float immediate_weight = (fabsf(value) - bottom_threshold) / (top_threshold - bottom_threshold); immediate_weight = Q_min( Q_max(immediate_weight, 0.0f), 1.0f ); // clamp to [0, 1] range // now we can push the smooth sample float smooth_weight = 1.0f - immediate_weight; flick_samples[front_sample] = value * smooth_weight; // calculate smoothed result float average = 0; for (int i = 0; i < MAX_SMOOTH_SAMPLES; i++) { average += flick_samples[i]; } average /= MAX_SMOOTH_SAMPLES; // finally, add immediate portion (original input) return average + value * immediate_weight; } /* * Flick Stick handling: detect if the player just started one, or return the * player rotation if stick was already flicked */ static float IN_FlickStick(thumbstick_t stick, float axial_deadzone) { static qboolean is_flicking; static float last_stick_angle; thumbstick_t processed = stick; float angle_change = 0; if (IN_StickMagnitude(stick) > Q_min(joy_flick_threshold->value, 1.0f)) // flick! { // Make snap-to-axis only if player wasn't already flicking if (!is_flicking || flick_progress < FLICK_TIME) { processed = IN_SlopedAxialDeadzone(stick, axial_deadzone); } const float stick_angle = (180 / M_PI) * atan2f(-processed.x, -processed.y); if (!is_flicking) { // Flicking begins now, with a new target is_flicking = true; flick_progress = 0; target_angle = stick_angle; IN_ResetSmoothSamples(); } else { // Was already flicking, just turning now angle_change = stick_angle - last_stick_angle; // angle wrap: https://stackoverflow.com/a/11498248/1130520 angle_change = fmod(angle_change + 180.0f, 360.0f); if (angle_change < 0) { angle_change += 360.0f; } angle_change -= 180.0f; angle_change = IN_SmoothedStickRotation(angle_change); } last_stick_angle = stick_angle; } else { is_flicking = false; } return angle_change; } /* * Move handling */ void IN_Move(usercmd_t *cmd) { // Factor used to transform from SDL joystick input ([-32768, 32767]) to [-1, 1] range static const float normalize_sdl_axis = 1.0f / 32768.0f; // Flick Stick's factors to change to the target angle with a feeling of "ease out" static const float rotation_factor[FLICK_TIME] = { 0.305555556f, 0.249999999f, 0.194444445f, 0.138888889f, 0.083333333f, 0.027777778f }; static float old_mouse_x; static float old_mouse_y; static float joystick_yaw, joystick_pitch; static float joystick_forwardmove, joystick_sidemove; static thumbstick_t left_stick = {0}, right_stick = {0}; thumbstick_t gyro_in = {0}; 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; } // Joystick reading and processing left_stick.x = joystick_left_x * normalize_sdl_axis; left_stick.y = joystick_left_y * normalize_sdl_axis; right_stick.x = joystick_right_x * normalize_sdl_axis; right_stick.y = joystick_right_y * normalize_sdl_axis; if (left_stick.x || left_stick.y) { left_stick = IN_RadialDeadzone(left_stick, joy_left_deadzone->value); if ((int)joy_layout->value == LAYOUT_FLICK_STICK_SOUTHPAW) { cl.viewangles[YAW] += IN_FlickStick(left_stick, joy_left_snapaxis->value); } else { left_stick = IN_SlopedAxialDeadzone(left_stick, joy_left_snapaxis->value); left_stick = IN_ApplyExpo(left_stick, joy_left_expo->value); } } if (right_stick.x || right_stick.y) { right_stick = IN_RadialDeadzone(right_stick, joy_right_deadzone->value); if ((int)joy_layout->value == LAYOUT_FLICK_STICK) { cl.viewangles[YAW] += IN_FlickStick(right_stick, joy_right_snapaxis->value); } else { right_stick = IN_SlopedAxialDeadzone(right_stick, joy_right_snapaxis->value); right_stick = IN_ApplyExpo(right_stick, joy_right_expo->value); } } switch((int)joy_layout->value) { case LAYOUT_SOUTHPAW: joystick_forwardmove = right_stick.y; joystick_sidemove = right_stick.x; joystick_yaw = left_stick.x; joystick_pitch = left_stick.y; break; case LAYOUT_LEGACY: joystick_forwardmove = left_stick.y; joystick_sidemove = right_stick.x; joystick_yaw = left_stick.x; joystick_pitch = right_stick.y; break; case LAYOUT_LEGACY_SOUTHPAW: joystick_forwardmove = right_stick.y; joystick_sidemove = left_stick.x; joystick_yaw = right_stick.x; joystick_pitch = left_stick.y; break; case LAYOUT_FLICK_STICK: // yaw already set by now joystick_forwardmove = left_stick.y; joystick_sidemove = left_stick.x; break; case LAYOUT_FLICK_STICK_SOUTHPAW: joystick_forwardmove = right_stick.y; joystick_sidemove = right_stick.x; break; default: // LAYOUT_DEFAULT joystick_forwardmove = left_stick.y; joystick_sidemove = left_stick.x; joystick_yaw = right_stick.x; joystick_pitch = right_stick.y; } // To make the the viewangles changes independent of framerate we need to scale // with frametime (assuming the configured values are for 60hz) // // For movement this is not needed, as those are absolute values independent of framerate float joyViewFactor = cls.rframetime/0.01666f; #ifdef NATIVE_SDL_GYRO float gyroViewFactor = (1.0f / M_PI) * joyViewFactor; #else float gyroViewFactor = (1.0f / 2560.0f) * joyViewFactor; // normalized for Switch gyro #endif if (joystick_yaw) { cl.viewangles[YAW] -= (m_yaw->value * joy_yawsensitivity->value * cl_yawspeed->value * joystick_yaw) * joyViewFactor; } if(joystick_pitch) { cl.viewangles[PITCH] += (m_pitch->value * joy_pitchsensitivity->value * cl_pitchspeed->value * joystick_pitch) * joyViewFactor; } if (joystick_forwardmove) { // We need to be twice as fast because with joystick we run... cmd->forwardmove -= m_forward->value * joy_forwardsensitivity->value * cl_forwardspeed->value * 2.0f * joystick_forwardmove; } if (joystick_sidemove) { // We need to be twice as fast because with joystick we run... cmd->sidemove += m_side->value * joy_sidesensitivity->value * cl_sidespeed->value * 2.0f * joystick_sidemove; } if (gyro_yaw || gyro_pitch) { gyro_in = IN_TightenInput(gyro_yaw, gyro_pitch); } if (gyro_in.x) { cl.viewangles[YAW] += m_yaw->value * gyro_yawsensitivity->value * cl_yawspeed->value * gyro_in.x * gyroViewFactor; } if (gyro_in.y) { cl.viewangles[PITCH] -= m_pitch->value * gyro_pitchsensitivity->value * cl_pitchspeed->value * gyro_in.y * gyroViewFactor; } // Flick Stick: flick in progress, changing the yaw angle to the target progressively if (flick_progress < FLICK_TIME) { cl.viewangles[YAW] += target_angle * rotation_factor[flick_progress]; flick_progress++; } } /* ------------------------------------------------------------------ */ /* * Look down */ static void IN_MLookDown(void) { mlooking = true; } /* * Look up */ static void IN_MLookUp(void) { mlooking = false; IN_CenterView(); } static void IN_JoyAltSelectorDown(void) { joy_altselector_pressed = true; } static void IN_JoyAltSelectorUp(void) { joy_altselector_pressed = false; } static void IN_GyroActionDown(void) { switch ((int)gyro_mode->value) { case 1: gyro_active = true; return; case 2: gyro_active = false; } } static void IN_GyroActionUp(void) { switch ((int)gyro_mode->value) { case 1: gyro_active = false; return; case 2: gyro_active = true; } } /* * Removes all pending events from SDLs queue. */ void In_FlushQueue(void) { SDL_FlushEvents(SDL_EVENT_FIRST, SDL_EVENT_LAST); Key_MarkAllUp(); IN_JoyAltSelectorUp(); } /* ------------------------------------------------------------------ */ static void IN_Haptic_Shutdown(void); /* * Init haptic effects */ static int IN_Haptic_Effect_Init(int effect_x, int effect_y, int effect_z, int period, int magnitude, int delay, int attack, int fade) { static SDL_HapticEffect haptic_effect; /* limit magnitude */ if (magnitude > SHRT_MAX) { magnitude = SHRT_MAX; } else if (magnitude < 0) { magnitude = 0; } 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_CARTESIAN; // Cartesian/3d coordinates haptic_effect.periodic.direction.dir[0] = effect_x; haptic_effect.periodic.direction.dir[1] = effect_y; haptic_effect.periodic.direction.dir[2] = effect_z; haptic_effect.periodic.period = period; haptic_effect.periodic.magnitude = magnitude; haptic_effect.periodic.length = period; haptic_effect.periodic.delay = delay; haptic_effect.periodic.attack_length = attack; haptic_effect.periodic.fade_length = fade; return SDL_CreateHapticEffect(joystick_haptic, &haptic_effect); } static void IN_Haptic_Effects_Info(void) { Com_Printf ("Joystick/Mouse haptic:\n"); Com_Printf (" * %d effects\n", SDL_GetMaxHapticEffects(joystick_haptic)); Com_Printf (" * %d haptic effects at the same time\n", SDL_GetMaxHapticEffectsPlaying(joystick_haptic)); Com_Printf (" * %d haptic axis\n", SDL_GetNumHapticAxes(joystick_haptic)); } static void IN_Haptic_Effects_Init(void) { last_haptic_effect_size = SDL_GetMaxHapticEffectsPlaying(joystick_haptic); if (last_haptic_effect_size > HAPTIC_EFFECT_LIST_SIZE) { last_haptic_effect_size = HAPTIC_EFFECT_LIST_SIZE; } memset(&last_haptic_effect, 0, sizeof(last_haptic_effect)); for (int i=0; i= 0) { SDL_DestroyHapticEffect(joystick_haptic, *effect_id); } *effect_id = -1; } static void IN_Haptic_Effects_Shutdown(void) { for (int i=0; ivalue * effect_volume; // 32767 max strength; /* Com_Printf("%d: volume %d: %d ms %d:%d:%d ms speed: %.2f\n", last_haptic_effect_pos, effect_volume, effect_duration, effect_delay, effect_attack, effect_fade, (float)effect_volume / effect_fade); */ // FIFO for effects last_haptic_effect_pos = (last_haptic_effect_pos + 1) % last_haptic_effect_size; IN_Haptic_Effect_Shutdown(&last_haptic_effect[last_haptic_effect_pos].effect_id); last_haptic_effect[last_haptic_effect_pos].effect_volume = effect_volume; last_haptic_effect[last_haptic_effect_pos].effect_duration = effect_duration; last_haptic_effect[last_haptic_effect_pos].effect_delay = effect_delay; last_haptic_effect[last_haptic_effect_pos].effect_attack = effect_attack; last_haptic_effect[last_haptic_effect_pos].effect_fade = effect_fade; last_haptic_effect[last_haptic_effect_pos].effect_x = effect_x; last_haptic_effect[last_haptic_effect_pos].effect_y = effect_y; last_haptic_effect[last_haptic_effect_pos].effect_z = effect_z; last_haptic_effect[last_haptic_effect_pos].effect_id = IN_Haptic_Effect_Init( effect_x, effect_y, effect_z, effect_duration, haptic_volume, effect_delay, effect_attack, effect_fade); return last_haptic_effect[last_haptic_effect_pos].effect_id; } // Keep it same with rumble rules, look for descriptions to rumble // filtering in Controller_Rumble static char *default_haptic_filter = ( // skipped files should be before wider rule "!weapons/*grenlb " // bouncing grenades don't have feedback "!weapons/*hgrenb " // bouncing grenades don't have feedback "!weapons/*open " // rogue's items don't have feedback "!weapons/*warn " // rogue's items don't have feedback // any weapons that are not in previous list "weapons/ " // player{,s} effects "player/*land " // fall without injury "player/*burn " "player/*pain " "player/*fall " "player/*death " "players/*burn " "players/*pain " "players/*fall " "players/*death " // environment effects "doors/ " "plats/ " "world/*dish " "world/*drill2a " "world/*dr_ " "world/*explod1 " "world/*rocks " "world/*rumble " "world/*quake " "world/*train2 " ); /* * name: sound name * filter: sound name rule with '*' * return false for empty filter */ static qboolean Haptic_Feedback_Filtered_Line(const char *name, const char *filter) { const char *current_filter = filter; // skip empty filter if (!*current_filter) { return false; } while (*current_filter) { char part_filter[MAX_QPATH]; const char *name_part; const char *str_end; str_end = strchr(current_filter, '*'); if (!str_end) { if (!strstr(name, current_filter)) { // no such part in string return false; } // have such part break; } // copy filter line if ((str_end - current_filter) >= MAX_QPATH) { return false; } memcpy(part_filter, current_filter, str_end - current_filter); part_filter[str_end - current_filter] = 0; // place part in name name_part = strstr(name, part_filter); if (!name_part) { // no such part in string return false; } // have such part name = name_part + strlen(part_filter); // move to next filter current_filter = str_end + 1; } return true; } /* * name: sound name * filter: sound names separated by space, and '!' for skip file */ static qboolean Haptic_Feedback_Filtered(const char *name, const char *filter) { const char *current_filter = filter; while (*current_filter) { char line_filter[MAX_QPATH]; const char *str_end; str_end = strchr(current_filter, ' '); // its end of filter if (!str_end) { // check rules inside line if (Haptic_Feedback_Filtered_Line(name, current_filter)) { return true; } return false; } // copy filter line if ((str_end - current_filter) >= MAX_QPATH) { return false; } memcpy(line_filter, current_filter, str_end - current_filter); line_filter[str_end - current_filter] = 0; // check rules inside line if (*line_filter == '!') { // has invert rule if (Haptic_Feedback_Filtered_Line(name, line_filter + 1)) { return false; } } else { if (Haptic_Feedback_Filtered_Line(name, line_filter)) { return true; } } // move to next filter current_filter = str_end + 1; } return false; } /* * Haptic Feedback: * effect_volume=0..SHRT_MAX * effect{x,y,z} - effect direction * effect{delay,attack,fade} - effect durations * effect_distance - distance to sound source * name - sound file name */ void Haptic_Feedback(const char *name, int effect_volume, int effect_duration, int effect_delay, int effect_attack, int effect_fade, int effect_x, int effect_y, int effect_z, float effect_distance) { float max_distance = joy_haptic_distance->value; if (!joystick_haptic || joy_haptic_magnitude->value <= 0 || max_distance <= 0 || /* skip haptic if distance is negative */ effect_distance > max_distance || effect_volume <= 0 || effect_duration <= 0 || last_haptic_effect_size <= 0) /* haptic but without slots? */ { return; } /* combine distance and volume */ effect_volume *= (max_distance - effect_distance) / max_distance; if (last_haptic_volume != (int)(joy_haptic_magnitude->value * 16)) { IN_Haptic_Effects_Shutdown(); IN_Haptic_Effects_Init(); } last_haptic_volume = joy_haptic_magnitude->value * 16; if (Haptic_Feedback_Filtered(name, haptic_feedback_filter->string)) { int effect_id; effect_id = IN_Haptic_GetEffectId(effect_volume, effect_duration, effect_delay, effect_attack, effect_fade, effect_x, effect_y, effect_z); if (effect_id == -1) { /* have rumble used some slots in haptic effect list?, * ok, use little bit less haptic effects at the same time*/ IN_Haptic_Effects_Shutdown(); last_haptic_effect_size --; Com_Printf("%d haptic effects at the same time\n", last_haptic_effect_size); return; } SDL_RunHapticEffect(joystick_haptic, effect_id, 1); } } /* * Controller_Rumble: * name = sound file name * effect_volume = 0..USHRT_MAX * effect_duration is in ms * source = origin of audio * from_player = if source is the client (player) */ void Controller_Rumble(const char *name, vec3_t source, qboolean from_player, unsigned int duration, unsigned short int volume) { vec_t intens = 0.0f, low_freq = 1.0f, hi_freq = 1.0f, dist_prop; unsigned short int max_distance = 4; unsigned int effect_volume; if (!show_haptic || !controller || joy_haptic_magnitude->value <= 0 || volume == 0 || duration == 0) { return; } if (strstr(name, "weapons/")) { intens = 1.75f; if (strstr(name, "/blastf") || strstr(name, "/hyprbf") || strstr(name, "/nail")) { intens = 0.125f; // dampen blasters and nailgun's fire low_freq = 0.7f; hi_freq = 1.2f; } else if (strstr(name, "/shotgf") || strstr(name, "/rocklf")) { low_freq = 1.1f; // shotgun & RL shouldn't feel so weak duration *= 0.7; } else if (strstr(name, "/sshotf")) { duration *= 0.6; // the opposite for super shotgun } else if (strstr(name, "/machgf") || strstr(name, "/disint")) { intens = 1.125f; // machine gun & disruptor fire } else if (strstr(name, "/grenlb") || strstr(name, "/hgrenb") // bouncing grenades || strstr(name, "open") || strstr(name, "warn")) // rogue's items { return; // ... don't have feedback } else if (strstr(name, "/plasshot")) // phalanx cannon { intens = 1.0f; hi_freq = 0.3f; duration *= 0.5; } else if (strstr(name, "x")) // explosions... { low_freq = 1.1f; hi_freq = 0.9f; max_distance = 550; // can be felt far away } else if (strstr(name, "r")) // reloads & ion ripper fire { low_freq = 0.1f; hi_freq = 0.6f; } } else if (strstr(name, "player/land")) { intens = 2.2f; // fall without injury low_freq = 1.1f; } else if (strstr(name, "player/") || strstr(name, "players/")) { low_freq = 1.2f; // exaggerate player damage if (strstr(name, "/burn") || strstr(name, "/pain100") || strstr(name, "/pain75")) { intens = 2.4f; } else if (strstr(name, "/fall") || strstr(name, "/pain50") || strstr(name, "/pain25")) { intens = 2.7f; } else if (strstr(name, "/death")) { intens = 2.9f; } } else if (strstr(name, "doors/")) { intens = 0.125f; low_freq = 0.4f; max_distance = 280; } else if (strstr(name, "plats/")) { intens = 1.0f; // platforms rumble... max_distance = 200; // when player near them } else if (strstr(name, "world/")) { max_distance = 3500; // ambient events if (strstr(name, "/dish") || strstr(name, "/drill2a") || strstr(name, "/dr_") || strstr(name, "/explod1") || strstr(name, "/rocks") || strstr(name, "/rumble")) { intens = 0.28f; low_freq = 0.7f; } else if (strstr(name, "/quake")) { intens = 0.67f; // (earth)quakes are more evident low_freq = 1.2f; } else if (strstr(name, "/train2")) { intens = 0.28f; max_distance = 290; // just machinery } } if (intens == 0.0f) { return; } if (from_player) { dist_prop = 1.0f; } else { dist_prop = VectorLength(source); if (dist_prop > max_distance) { return; } dist_prop = (max_distance - dist_prop) / max_distance; } effect_volume = joy_haptic_magnitude->value * intens * dist_prop * volume; low_freq = Q_min(effect_volume * low_freq, USHRT_MAX); hi_freq = Q_min(effect_volume * hi_freq, USHRT_MAX); // Com_Printf("%-29s: vol %5u - %4u ms - dp %.3f l %5.0f h %5.0f\n", // name, effect_volume, duration, dist_prop, low_freq, hi_freq); if (SDL_RumbleGamepad(controller, low_freq, hi_freq, duration) == -1) { if (!joystick_haptic) { /* no haptic, some other reason of error */ return; } /* All haptic/force feedback slots are busy, try to clean up little bit. */ IN_Haptic_Effects_Shutdown(); } } /* * Gyro calibration functions, called from menu */ void StartCalibration(void) { #ifdef NATIVE_SDL_GYRO num_samples = 0; #else num_samples[0] = num_samples[1] = num_samples[2] = 0; #endif gyro_accum[0] = 0.0; gyro_accum[1] = 0.0; gyro_accum[2] = 0.0; updates_countdown = 300; countdown_reason = REASON_GYROCALIBRATION; } qboolean IsCalibrationZero(void) { return (!gyro_calibration_x->value && !gyro_calibration_y->value && !gyro_calibration_z->value); } /* * Game Controller */ static void IN_Controller_Init(qboolean notify_user) { cvar_t *cvar; int nummappings; char controllerdb[MAX_OSPATH] = {0}; SDL_Joystick *joystick = NULL; SDL_bool is_controller = SDL_FALSE; cvar = Cvar_Get("in_sdlbackbutton", "0", CVAR_ARCHIVE); if (cvar) { switch ((int)cvar->value) { case 1: sdl_back_button = SDL_GAMEPAD_BUTTON_START; break; case 2: sdl_back_button = SDL_GAMEPAD_BUTTON_GUIDE; break; default: sdl_back_button = SDL_GAMEPAD_BUTTON_BACK; } } cvar = Cvar_Get("in_initjoy", "1", CVAR_NOSET); if (!cvar->value) { return; } if (notify_user) { Com_Printf("- Game Controller init attempt -\n"); } if (!SDL_WasInit(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC)) { #ifdef SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE // extended input reports on PS controllers (enables gyro thru bluetooth) SDL_SetHint( SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1" ); #endif #ifdef SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE SDL_SetHint( SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1" ); #endif if (SDL_Init(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC) == -1) { Com_Printf ("Couldn't init SDL Game Controller: %s.\n", SDL_GetError()); return; } } int numjoysticks; const SDL_JoystickID *joysticks = SDL_GetJoysticks(&numjoysticks); if (joysticks != NULL) { Com_Printf ("%i joysticks were found.\n", numjoysticks); } if (numjoysticks == 0) { joystick_haptic = SDL_OpenHapticFromMouse(); if (joystick_haptic && (SDL_GetHapticFeatures(joystick_haptic) & SDL_HAPTIC_SINE) == 0) { /* Disable haptic for joysticks without SINE */ SDL_CloseHaptic(joystick_haptic); joystick_haptic = NULL; } if (joystick_haptic) { IN_Haptic_Effects_Info(); show_haptic = true; } return; } for (const char* rawPath = FS_GetNextRawPath(NULL); rawPath != NULL; rawPath = FS_GetNextRawPath(rawPath)) { snprintf(controllerdb, MAX_OSPATH, "%s/gamecontrollerdb.txt", rawPath); nummappings = SDL_AddGamepadMappingsFromFile(controllerdb); if (nummappings > 0) Com_Printf ("%d mappings loaded from gamecontrollerdb.txt\n", nummappings); } for (int i = 0; i < numjoysticks; i++) { joystick = SDL_OpenJoystick(joysticks[i]); if (!joystick) { Com_Printf ("Couldn't open joystick %d: %s.\n", i+1, SDL_GetError()); continue; // try next joystick } const char* joystick_name = SDL_GetJoystickName(joystick); const int name_len = strlen(joystick_name); Com_Printf ("Trying joystick %d, '%s'\n", i+1, joystick_name); // Ugly hack to detect IMU-only devices - works for Switch controllers at least if (name_len > 4 && !strncmp(joystick_name + name_len - 4, " IMU", 4)) { SDL_CloseJoystick(joystick); joystick = NULL; #ifdef NATIVE_SDL_GYRO Com_Printf ("Skipping IMU device.\n"); #else // if it's not a Left JoyCon, use it as Gyro Com_Printf ("IMU device found.\n"); if ( !imu_joystick && name_len > 16 && strncmp(joystick_name + name_len - 16, "Left Joy-Con IMU", 16) != 0 ) { imu_joystick = SDL_OpenJoystick(joysticks[i]); if (imu_joystick) { show_gyro = true; Com_Printf ("Using this device as Gyro sensor.\n"); } else { Com_Printf ("Couldn't open IMU: %s.\n", SDL_GetError()); } } #endif continue; } Com_Printf ("Buttons = %d, Axes = %d, Hats = %d\n", SDL_GetNumJoystickButtons(joystick), SDL_GetNumJoystickAxes(joystick), SDL_GetNumJoystickHats(joystick)); is_controller = SDL_IsGamepad(joysticks[i]); if (!is_controller) { char joystick_guid[65] = {0}; SDL_JoystickGUID guid = SDL_GetJoystickInstanceGUID(joysticks[i]); SDL_GetJoystickGUIDString(guid, joystick_guid, 64); Com_Printf ("To use joystick as game controller, provide its config by either:\n" " * Putting 'gamecontrollerdb.txt' file in your game directory.\n" " * Or setting SDL_GAMECONTROLLERCONFIG environment variable. E.g.:\n"); Com_Printf ("SDL_GAMECONTROLLERCONFIG='%s,%s,leftx:a0,lefty:a1,rightx:a2,righty:a3,back:b1,...'\n", joystick_guid, joystick_name); } SDL_CloseJoystick(joystick); joystick = NULL; if (is_controller && !controller) { controller = SDL_OpenGamepad(joysticks[i]); if (!controller) { Com_Printf("SDL Controller error: %s.\n", SDL_GetError()); continue; // try next joystick } show_gamepad = true; Com_Printf("Enabled as Game Controller, settings:\n%s\n", SDL_GetGamepadMapping(controller)); #ifdef NATIVE_SDL_GYRO if (SDL_GamepadHasSensor(controller, SDL_SENSOR_GYRO) && !SDL_SetGamepadSensorEnabled(controller, SDL_SENSOR_GYRO, SDL_TRUE) ) { show_gyro = true; Com_Printf( "Gyro sensor enabled at %.2f Hz\n", SDL_GetGamepadSensorDataRate(controller, SDL_SENSOR_GYRO) ); } else { Com_Printf("Gyro sensor not found.\n"); } SDL_bool hasLED = SDL_GetBooleanProperty(SDL_GetGamepadProperties(controller), SDL_PROP_JOYSTICK_CAP_RGB_LED_BOOLEAN, SDL_FALSE); if (hasLED) { SDL_SetGamepadLED(controller, 0, 80, 0); // green light } #endif // NATIVE_SDL_GYRO joystick_haptic = SDL_OpenHapticFromJoystick(SDL_GetGamepadJoystick(controller)); if (joystick_haptic && (SDL_GetHapticFeatures(joystick_haptic) & SDL_HAPTIC_SINE) == 0) { /* Disable haptic for joysticks without SINE */ SDL_CloseHaptic(joystick_haptic); joystick_haptic = NULL; } if (joystick_haptic) { IN_Haptic_Effects_Info(); show_haptic = true; } SDL_bool hasRumble = SDL_GetBooleanProperty(SDL_GetGamepadProperties(controller), SDL_PROP_GAMEPAD_CAP_RUMBLE_BOOLEAN, SDL_FALSE); if (hasRumble) { show_haptic = true; Com_Printf("Rumble support available.\n"); } else { Com_Printf("Controller doesn't support rumble.\n"); } #ifdef NATIVE_SDL_GYRO // "native" exits when finding a single working controller break; #endif } } SDL_free((void *)joysticks); } /* * Initializes the backend */ void IN_Init(void) { Com_Printf("------- input initialization -------\n"); mouse_x = mouse_y = 0; joystick_left_x = joystick_left_y = joystick_right_x = joystick_right_y = 0; gyro_yaw = gyro_pitch = 0; exponential_speedup = Cvar_Get("exponential_speedup", "0", CVAR_ARCHIVE); freelook = Cvar_Get("freelook", "1", CVAR_ARCHIVE); in_grab = Cvar_Get("in_grab", "2", CVAR_ARCHIVE); lookstrafe = Cvar_Get("lookstrafe", "0", CVAR_ARCHIVE); m_filter = Cvar_Get("m_filter", "0", CVAR_ARCHIVE); m_up = Cvar_Get("m_up", "1", CVAR_ARCHIVE); m_forward = Cvar_Get("m_forward", "1", CVAR_ARCHIVE); m_pitch = Cvar_Get("m_pitch", "0.022", CVAR_ARCHIVE); m_side = Cvar_Get("m_side", "0.8", CVAR_ARCHIVE); m_yaw = Cvar_Get("m_yaw", "0.022", CVAR_ARCHIVE); sensitivity = Cvar_Get("sensitivity", "3", CVAR_ARCHIVE); joy_haptic_magnitude = Cvar_Get("joy_haptic_magnitude", "0.0", CVAR_ARCHIVE); joy_haptic_distance = Cvar_Get("joy_haptic_distance", "100.0", CVAR_ARCHIVE); haptic_feedback_filter = Cvar_Get("joy_haptic_filter", default_haptic_filter, 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_layout = Cvar_Get("joy_layout", "0", CVAR_ARCHIVE); joy_left_expo = Cvar_Get("joy_left_expo", "2.0", CVAR_ARCHIVE); joy_left_snapaxis = Cvar_Get("joy_left_snapaxis", "0.15", CVAR_ARCHIVE); joy_left_deadzone = Cvar_Get("joy_left_deadzone", "0.16", CVAR_ARCHIVE); joy_right_expo = Cvar_Get("joy_right_expo", "2.0", CVAR_ARCHIVE); joy_right_snapaxis = Cvar_Get("joy_right_snapaxis", "0.15", CVAR_ARCHIVE); joy_right_deadzone = Cvar_Get("joy_right_deadzone", "0.16", CVAR_ARCHIVE); joy_flick_threshold = Cvar_Get("joy_flick_threshold", "0.65", CVAR_ARCHIVE); joy_flick_smoothed = Cvar_Get("joy_flick_smoothed", "8.0", CVAR_ARCHIVE); gyro_calibration_x = Cvar_Get("gyro_calibration_x", "0.0", CVAR_ARCHIVE); gyro_calibration_y = Cvar_Get("gyro_calibration_y", "0.0", CVAR_ARCHIVE); gyro_calibration_z = Cvar_Get("gyro_calibration_z", "0.0", CVAR_ARCHIVE); gyro_yawsensitivity = Cvar_Get("gyro_yawsensitivity", "1.0", CVAR_ARCHIVE); gyro_pitchsensitivity = Cvar_Get("gyro_pitchsensitivity", "1.0", CVAR_ARCHIVE); gyro_tightening = Cvar_Get("gyro_tightening", "3.5", CVAR_ARCHIVE); gyro_turning_axis = Cvar_Get("gyro_turning_axis", "0", CVAR_ARCHIVE); gyro_mode = Cvar_Get("gyro_mode", "2", CVAR_ARCHIVE); if ((int)gyro_mode->value == 2) { gyro_active = true; } windowed_pauseonfocuslost = Cvar_Get("vid_pauseonfocuslost", "0", CVAR_USERINFO | CVAR_ARCHIVE); windowed_mouse = Cvar_Get("windowed_mouse", "1", CVAR_USERINFO | CVAR_ARCHIVE); Cmd_AddCommand("+mlook", IN_MLookDown); Cmd_AddCommand("-mlook", IN_MLookUp); Cmd_AddCommand("+joyaltselector", IN_JoyAltSelectorDown); Cmd_AddCommand("-joyaltselector", IN_JoyAltSelectorUp); Cmd_AddCommand("+gyroaction", IN_GyroActionDown); Cmd_AddCommand("-gyroaction", IN_GyroActionUp); if (!SDL_WasInit(SDL_INIT_EVENTS)) { if ((SDL_InitSubSystem(SDL_INIT_EVENTS)) != 0) { Com_Error(ERR_FATAL, "Couldn't initialize SDL event subsystem:%s\n", SDL_GetError()); } } SDL_StartTextInput(); IN_Controller_Init(false); Com_Printf("------------------------------------\n\n"); } /* * Shuts the backend down */ static void IN_Haptic_Shutdown(void) { if (joystick_haptic) { IN_Haptic_Effects_Shutdown(); SDL_CloseHaptic(joystick_haptic); joystick_haptic = NULL; } } static void IN_Controller_Shutdown(qboolean notify_user) { if (notify_user) { Com_Printf("- Game Controller disconnected -\n"); } IN_Haptic_Shutdown(); if (controller) { SDL_CloseGamepad(controller); controller = NULL; } show_gamepad = show_gyro = show_haptic = false; joystick_left_x = joystick_left_y = joystick_right_x = joystick_right_y = 0; gyro_yaw = gyro_pitch = 0; #ifndef NATIVE_SDL_GYRO if (imu_joystick) { SDL_CloseJoystick(imu_joystick); imu_joystick = NULL; } #endif } /* * Shuts the backend down */ void IN_Shutdown(void) { Cmd_RemoveCommand("force_centerview"); Cmd_RemoveCommand("+mlook"); Cmd_RemoveCommand("-mlook"); Cmd_RemoveCommand("+joyaltselector"); Cmd_RemoveCommand("-joyaltselector"); Cmd_RemoveCommand("+gyroaction"); Cmd_RemoveCommand("-gyroaction"); Com_Printf("Shutting down input.\n"); IN_Controller_Shutdown(false); const Uint32 subsystems = SDL_INIT_GAMEPAD | SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_EVENTS; if (SDL_WasInit(subsystems) == subsystems) { SDL_QuitSubSystem(subsystems); } } /* ------------------------------------------------------------------ */ yquake2-QUAKE2_8_40/src/client/menu/000077500000000000000000000000001465112212000171225ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/menu/header/000077500000000000000000000000001465112212000203525ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/menu/header/qmenu.h000066400000000000000000000070441465112212000216550ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 RCOLUMN_OFFSET 16 #define LCOLUMN_OFFSET -16 #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 MTYPE_BITMAP 6 #define QMF_LEFT_JUSTIFY 0x00000001 #define QMF_GRAYED 0x00000002 #define QMF_NUMBERSONLY 0x00000004 #define QMF_HIGHLIGHT_IF_FOCUS 0x00000008 #define QMF_INACTIVE 0x00000010 enum { KEYS_ALL = 0, KEYS_KEYBOARD_MOUSE, KEYS_CONTROLLER, KEYS_CONTROLLER_ALT }; typedef struct _tag_menuframework { int x, y; int cursor; int nitems; void *items[64]; const char *statusbar; void (*draw)(void); const char *(*key)(int k); 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 * focuspic; char * errorpic; int width; int height; } menubitmap_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; char * cvar; float minvalue; float maxvalue; float slidestep; char * printformat; qboolean abs; } 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; void M_PushMenu(menuframework_s* menu); 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); 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 *); float ClampCvar(float min, float max, float value); #endif yquake2-QUAKE2_8_40/src/client/menu/menu.c000066400000000000000000005055461465112212000202510ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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" /* Number of the frames of the spinning quake logo */ #define NUM_CURSOR_FRAMES 15 static int m_cursor_width = 0; /* Signals the file system to start the demo loop. */ qboolean menu_startdemoloop; 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_Mods_f(void); static void M_Menu_Multiplayer_f(void); static void M_Menu_Multiplayer_Keys_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); void M_Menu_Video_f(void); static void M_Menu_Options_f(void); static void M_Menu_Keys_f(void); static void M_Menu_Joy_f(void); static void M_Menu_ControllerButtons_f(void); static void M_Menu_ControllerAltButtons_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 */ /* 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]; menulayer_t m_active; /* active menu layer */ 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 || (strcmp(gamename, BASEDIRNAME) == 0 && strcmp(game->string, "") == 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) { cls.key_dest = key_game; m_active.draw = NULL; m_active.key = NULL; 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_active.draw = m_layers[m_menudepth].draw; m_active.key = m_layers[m_menudepth].key; if (!m_menudepth) { M_ForceMenuOff(); /* play music */ if (Cvar_VariableValue("ogg_pausewithgame") == 1 && OGG_Status() == PAUSE && cl.attractloop == false) { Cbuf_AddText("ogg toggle\n"); } } } /* * 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. */ void M_PushMenu(menuframework_s* menu) { int i; int alreadyPresent = 0; if (menu == NULL) { return; } 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 /* pause music */ if (Cvar_VariableValue("ogg_pausewithgame") == 1 && OGG_Status() == PLAY && cl.attractloop == false) { Cbuf_AddText("ogg toggle\n"); } /* if this menu is already open (and on top), close it => toggling behaviour */ if ((m_active.draw == menu->draw) && (m_active.key == menu->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 == menu->draw) && (m_layers[i].key == menu->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_active.draw; m_layers[m_menudepth].key = m_active.key; m_menudepth++; m_active.draw = menu->draw; m_active.key = menu->key; m_entersound = true; cls.key_dest = key_menu; } int Key_GetMenuKey(int key) { switch (key) { case K_KP_UPARROW: if (IN_NumpadIsOn() == true) { break; } case K_UPARROW: case K_DPAD_UP: return K_UPARROW; case K_TAB: case K_KP_DOWNARROW: if (IN_NumpadIsOn() == true) { break; } case K_DOWNARROW: case K_DPAD_DOWN: return K_DOWNARROW; case K_KP_LEFTARROW: if (IN_NumpadIsOn() == true) { break; } case K_LEFTARROW: case K_DPAD_LEFT: case K_SHOULDER_LEFT: return K_LEFTARROW; case K_KP_RIGHTARROW: if (IN_NumpadIsOn() == true) { break; } case K_RIGHTARROW: case K_DPAD_RIGHT: case K_SHOULDER_RIGHT: return K_RIGHTARROW; case K_MOUSE1: case K_MOUSE2: case K_MOUSE3: case K_MOUSE4: case K_MOUSE5: case K_KP_ENTER: case K_ENTER: case K_BTN_A: return K_ENTER; case K_ESCAPE: case K_JOY_BACK: case K_BTN_B: return K_ESCAPE; case K_BACKSPACE: case K_DEL: case K_KP_DEL: if (IN_NumpadIsOn() == true) { break; } case K_BTN_Y: return K_BACKSPACE; case K_KP_INS: if (IN_NumpadIsOn() == true) { break; } case K_INS: return K_INS; } return key; } const char * Default_MenuKey(menuframework_s *m, int key) { const char *sound = NULL; int menu_key = Key_GetMenuKey(key); if (m) { menucommon_s *item; 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++; } } /* Unsused, left for backward compability */ 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 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; } if (!R_EndWorldRenderpass()) { 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) { int x, y; 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 */ static menuframework_s s_main; static menubitmap_s s_plaque; static menubitmap_s s_logo; static menubitmap_s s_game; static menubitmap_s s_multiplayer; static menubitmap_s s_options; static menubitmap_s s_video; static menubitmap_s s_quit; static void GameFunc(void *unused) { M_Menu_Game_f(); } static void MultiplayerFunc(void *unused) { M_Menu_Multiplayer_f(); } static void OptionsFunc(void *unused) { M_Menu_Options_f(); } static void VideoFunc(void *unused) { M_Menu_Video_f(); } static void QuitFunc(void *unused) { M_Menu_Quit_f(); } static void InitMainMenu(void) { float scale = SCR_GetMenuScale(); int i = 0; int x = 0; int y = 0; int w = 0; int h = 0; int widest = -1; 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; } } x = (viddef.width / scale - widest + 70) / 2; y = (viddef.height / (2 * scale) - 110); memset(&s_main, 0, sizeof( menuframework_s )); Draw_GetPicSize(&w, &h, "m_main_plaque"); s_plaque.generic.type = MTYPE_BITMAP; s_plaque.generic.flags = QMF_LEFT_JUSTIFY | QMF_INACTIVE; s_plaque.generic.x = (x - (m_cursor_width + 5) - w); s_plaque.generic.y = y; s_plaque.generic.name = "m_main_plaque"; s_plaque.generic.callback = 0; s_plaque.focuspic = 0; s_logo.generic.type = MTYPE_BITMAP; s_logo.generic.flags = QMF_LEFT_JUSTIFY | QMF_INACTIVE; s_logo.generic.x = (x - (m_cursor_width + 5) - w); s_logo.generic.y = y + h + 5; s_logo.generic.name = "m_main_logo"; s_logo.generic.callback = 0; s_logo.focuspic = 0; y += 10; s_game.generic.type = MTYPE_BITMAP; s_game.generic.flags = QMF_LEFT_JUSTIFY | QMF_HIGHLIGHT_IF_FOCUS; s_game.generic.x = x; s_game.generic.y = y; s_game.generic.name = "m_main_game"; s_game.generic.callback = GameFunc; s_game.focuspic = "m_main_game_sel"; Draw_GetPicSize(&w, &h, ( char * )s_game.generic.name); y += h + 8; s_multiplayer.generic.type = MTYPE_BITMAP; s_multiplayer.generic.flags = QMF_LEFT_JUSTIFY | QMF_HIGHLIGHT_IF_FOCUS; s_multiplayer.generic.x = x; s_multiplayer.generic.y = y; s_multiplayer.generic.name = "m_main_multiplayer"; s_multiplayer.generic.callback = MultiplayerFunc; s_multiplayer.focuspic = "m_main_multiplayer_sel"; Draw_GetPicSize(&w, &h, ( char * )s_multiplayer.generic.name); y += h + 8; s_options.generic.type = MTYPE_BITMAP; s_options.generic.flags = QMF_LEFT_JUSTIFY | QMF_HIGHLIGHT_IF_FOCUS; s_options.generic.x = x; s_options.generic.y = y; s_options.generic.name = "m_main_options"; s_options.generic.callback = OptionsFunc; s_options.focuspic = "m_main_options_sel"; Draw_GetPicSize(&w, &h, ( char * )s_options.generic.name); y += h + 8; s_video.generic.type = MTYPE_BITMAP; s_video.generic.flags = QMF_LEFT_JUSTIFY | QMF_HIGHLIGHT_IF_FOCUS; s_video.generic.x = x; s_video.generic.y = y; s_video.generic.name = "m_main_video"; s_video.generic.callback = VideoFunc; s_video.focuspic = "m_main_video_sel"; Draw_GetPicSize(&w, &h, ( char * )s_video.generic.name); y += h + 8; s_quit.generic.type = MTYPE_BITMAP; s_quit.generic.flags = QMF_LEFT_JUSTIFY | QMF_HIGHLIGHT_IF_FOCUS; s_quit.generic.x = x; s_quit.generic.y = y; s_quit.generic.name = "m_main_quit"; s_quit.generic.callback = QuitFunc; s_quit.focuspic = "m_main_quit_sel"; Menu_AddItem(&s_main, (void *)&s_plaque); Menu_AddItem(&s_main, (void *)&s_logo); Menu_AddItem(&s_main, (void *)&s_game); Menu_AddItem(&s_main, (void *)&s_multiplayer); Menu_AddItem(&s_main, (void *)&s_options); Menu_AddItem(&s_main, (void *)&s_video); Menu_AddItem(&s_main, (void *)&s_quit); Menu_Center(&s_main); } static void M_Main_Draw(void) { menucommon_s * item = NULL; int x = 0; int y = 0; item = ( menucommon_s * )s_main.items[s_main.cursor]; if (item) { x = item->x; y = item->y; } Menu_Draw(&s_main); M_DrawCursor(x - m_cursor_width, y, ( int )(cls.realtime / 100) % NUM_CURSOR_FRAMES); } const char * M_Main_Key(int key) { return Default_MenuKey(&s_main, key); } void M_Menu_Main_f(void) { menucommon_s * item = NULL; InitMainMenu(); // force first available item to have focus while (s_main.cursor >= 0 && s_main.cursor < s_main.nitems) { item = ( menucommon_s * )s_main.items[s_main.cursor]; if ((item->flags & (QMF_INACTIVE))) { s_main.cursor++; } else { break; } } s_main.draw = M_Main_Draw; s_main.key = M_Main_Key; M_PushMenu(&s_main); } /* * 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 menuaction_s s_customize_options_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 MultplayerCustomizeControlsFunc(void *unused) { M_Menu_Multiplayer_Keys_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; s_customize_options_action.generic.type = MTYPE_ACTION; s_customize_options_action.generic.flags = QMF_LEFT_JUSTIFY; s_customize_options_action.generic.x = 0; s_customize_options_action.generic.y = 30; s_customize_options_action.generic.name = " customize controls"; s_customize_options_action.generic.callback = MultplayerCustomizeControlsFunc; 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_AddItem(&s_multiplayer_menu, (void *)&s_customize_options_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(); s_multiplayer_menu.draw = Multiplayer_MenuDraw; s_multiplayer_menu.key = Multiplayer_MenuKey; M_PushMenu(&s_multiplayer_menu); } /* * 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 menukeyitem_bind; static menuframework_s s_keys_menu; static menuframework_s s_joy_menu; static menuaction_s s_keys_actions[NUM_BINDNAMES]; static void M_UnbindCommand(char *command, int scope) { int j; int begin = 0, end = K_LAST; switch (scope) { case KEYS_KEYBOARD_MOUSE: end = K_JOY_FIRST_REGULAR; break; case KEYS_CONTROLLER: begin = K_JOY_FIRST_REGULAR; end = K_JOY_LAST_REGULAR + 1; break; case KEYS_CONTROLLER_ALT: begin = K_JOY_FIRST_REGULAR_ALT; } for (j = begin; j < end; j++) { char *b; b = keybindings[j]; if (!b) { continue; } if (!strcmp(b, command)) { Key_SetBinding(j, ""); } } } static void M_FindKeysForCommand(char *command, int *twokeys, int scope) { int count; int j; int begin = 0, end = K_LAST; switch (scope) { case KEYS_KEYBOARD_MOUSE: end = K_JOY_FIRST_REGULAR; break; case KEYS_CONTROLLER: begin = K_JOY_FIRST_REGULAR; end = K_JOY_LAST_REGULAR + 1; break; case KEYS_CONTROLLER_ALT: begin = K_JOY_FIRST_REGULAR_ALT; } twokeys[0] = twokeys[1] = -1; count = 0; for (j = begin; j < end; j++) { char *b; b = keybindings[j]; if (!b) { continue; } if (!strcmp(b, command)) { twokeys[count] = j; count++; if (count == 2) { break; } } } } static void KeyCursorDrawFunc(menuframework_s *menu) { float scale = SCR_GetMenuScale(); if (menukeyitem_bind) { 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, KEYS_KEYBOARD_MOUSE); if (keys[0] == -1) { Menu_DrawString(a->generic.x + a->generic.parent->x + RCOLUMN_OFFSET * 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 + RCOLUMN_OFFSET * 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, KEYS_KEYBOARD_MOUSE); if (keys[1] != -1) { M_UnbindCommand(bindnames[a->generic.localdata[0]][0], KEYS_KEYBOARD_MOUSE); } menukeyitem_bind = 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 (menukeyitem_bind) { // Any key/button except from the game controller and escape keys if ((key != K_ESCAPE) && (key != '`') && (key < K_JOY_FIRST_REGULAR)) { 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"); menukeyitem_bind = false; return menu_out_sound; } key = Key_GetMenuKey(key); switch (key) { case K_ENTER: KeyBindingFunc(item); return menu_in_sound; case K_BACKSPACE: /* delete bindings */ M_UnbindCommand(bindnames[item->generic.localdata[0]][0], KEYS_KEYBOARD_MOUSE); return menu_out_sound; default: return Default_MenuKey(&s_keys_menu, key); } } static void M_Menu_Keys_f(void) { Keys_MenuInit(); s_keys_menu.draw = Keys_MenuDraw; s_keys_menu.key = Keys_MenuKey; M_PushMenu(&s_keys_menu); } /* * MULTIPLAYER KEYS MENU */ char *multiplayer_key_bindnames[][2] = { {"score", "score"}, {"messagemode", "chat"}, {"messagemode2", "team chat"}, {"wave 1", "wave 1"}, {"wave 2", "wave 2"}, {"wave 3", "wave 3"}, {"wave 4", "wave 4"}, }; #define NUM_MULTIPLAYER_KEY_BINDNAMES (sizeof multiplayer_key_bindnames / sizeof multiplayer_key_bindnames[0]) static menuframework_s s_multiplayer_keys_menu; static menuaction_s s_multiplayer_keys_actions[NUM_MULTIPLAYER_KEY_BINDNAMES]; static void MultiplayerDrawKeyBindingFunc(void *self) { int keys[2]; menuaction_s *a = (menuaction_s *)self; float scale = SCR_GetMenuScale(); M_FindKeysForCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0], keys, KEYS_ALL); if (keys[0] == -1) { Menu_DrawString(a->generic.x + a->generic.parent->x + RCOLUMN_OFFSET * 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 + RCOLUMN_OFFSET * 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 MultiplayerKeyBindingFunc(void *self) { menuaction_s *a = (menuaction_s *)self; int keys[2]; M_FindKeysForCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0], keys, KEYS_ALL); if (keys[1] != -1) { M_UnbindCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0], KEYS_ALL); } menukeyitem_bind = true; Menu_SetStatusBar(&s_multiplayer_keys_menu, "press a key or button for this action"); } static void MultiplayerKeys_MenuInit(void) { int i; s_multiplayer_keys_menu.x = (int)(viddef.width * 0.50f); s_multiplayer_keys_menu.nitems = 0; s_multiplayer_keys_menu.cursordraw = KeyCursorDrawFunc; for (i = 0; i < NUM_MULTIPLAYER_KEY_BINDNAMES; i++) { s_multiplayer_keys_actions[i].generic.type = MTYPE_ACTION; s_multiplayer_keys_actions[i].generic.flags = QMF_GRAYED; s_multiplayer_keys_actions[i].generic.x = 0; s_multiplayer_keys_actions[i].generic.y = (i * 9); s_multiplayer_keys_actions[i].generic.ownerdraw = MultiplayerDrawKeyBindingFunc; s_multiplayer_keys_actions[i].generic.localdata[0] = i; s_multiplayer_keys_actions[i].generic.name = multiplayer_key_bindnames[s_multiplayer_keys_actions[i].generic.localdata[0]][1]; Menu_AddItem(&s_multiplayer_keys_menu, (void *)&s_multiplayer_keys_actions[i]); } Menu_SetStatusBar(&s_multiplayer_keys_menu, "ENTER to change, BACKSPACE to clear"); Menu_Center(&s_multiplayer_keys_menu); } static void MultiplayerKeys_MenuDraw(void) { Menu_AdjustCursor(&s_multiplayer_keys_menu, 1); Menu_Draw(&s_multiplayer_keys_menu); } static const char * MultiplayerKeys_MenuKey(int key) { menuaction_s *item = (menuaction_s *)Menu_ItemAtCursor(&s_multiplayer_keys_menu); if (menukeyitem_bind) { // Any key/button but the escape ones if ((key != K_ESCAPE) && (key != '`') && (key != K_JOY_BACK)) { char cmd[1024]; Com_sprintf(cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n", Key_KeynumToString(key), multiplayer_key_bindnames[item->generic.localdata[0]][0]); Cbuf_InsertText(cmd); } Menu_SetStatusBar(&s_multiplayer_keys_menu, "ENTER to change, BACKSPACE to clear"); menukeyitem_bind = false; return menu_out_sound; } key = Key_GetMenuKey(key); switch (key) { case K_ENTER: MultiplayerKeyBindingFunc(item); return menu_in_sound; case K_BACKSPACE: /* delete bindings */ M_UnbindCommand(multiplayer_key_bindnames[item->generic.localdata[0]][0], KEYS_ALL); return menu_out_sound; default: return Default_MenuKey(&s_multiplayer_keys_menu, key); } } static void M_Menu_Multiplayer_Keys_f(void) { MultiplayerKeys_MenuInit(); s_multiplayer_keys_menu.draw = MultiplayerKeys_MenuDraw; s_multiplayer_keys_menu.key = MultiplayerKeys_MenuKey; M_PushMenu(&s_multiplayer_keys_menu); } /* * GAME CONTROLLER ( GAMEPAD / JOYSTICK ) BUTTONS MENU */ char *controller_bindnames[][2] = { {"+attack", "attack"}, {"+moveup", "up / jump"}, {"+movedown", "down / crouch"}, {"weapnext", "next weapon"}, {"weapprev", "previous weapon"}, {"cycleweap weapon_chaingun weapon_machinegun weapon_blaster", "long range: quickswitch 1"}, {"cycleweap weapon_supershotgun weapon_shotgun", "close range: quickswitch 2"}, {"cycleweap weapon_rocketlauncher weapon_grenadelauncher ammo_grenades", "explosives: quickswitch 3"}, {"cycleweap weapon_bfg weapon_railgun weapon_hyperblaster", "special: quickswitch 4"}, {"prefweap weapon_railgun weapon_hyperblaster weapon_chaingun weapon_supershotgun weapon_machinegun weapon_shotgun weapon_blaster", "best safe weapon"}, {"prefweap weapon_bfg weapon_railgun weapon_rocketlauncher weapon_hyperblaster weapon_grenadelauncher weapon_chaingun ammo_grenades weapon_supershotgun", "best unsafe weapon"}, {"centerview", "center view"}, {"inven", "inventory"}, {"invuse", "use item"}, {"invdrop", "drop item"}, {"invprev", "prev item"}, {"invnext", "next item"}, {"cmd help", "help computer"}, {"+gyroaction", "gyro off / on"}, {"+joyaltselector", "alt buttons modifier"} }; #define NUM_CONTROLLER_BINDNAMES (sizeof controller_bindnames / sizeof controller_bindnames[0]) static menuframework_s s_controller_buttons_menu; static menuaction_s s_controller_buttons_actions[NUM_CONTROLLER_BINDNAMES]; static void DrawControllerButtonBindingFunc(void *self) { int keys[2]; menuaction_s *a = (menuaction_s *)self; float scale = SCR_GetMenuScale(); M_FindKeysForCommand(controller_bindnames[a->generic.localdata[0]][0], keys, KEYS_CONTROLLER); if (keys[0] == -1) { Menu_DrawString(a->generic.x + a->generic.parent->x + RCOLUMN_OFFSET * 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 + RCOLUMN_OFFSET * 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 ControllerButtonBindingFunc(void *self) { menuaction_s *a = (menuaction_s *)self; int keys[2]; M_FindKeysForCommand(controller_bindnames[a->generic.localdata[0]][0], keys, KEYS_CONTROLLER); if (keys[1] != -1) { M_UnbindCommand(controller_bindnames[a->generic.localdata[0]][0], KEYS_CONTROLLER); } menukeyitem_bind = true; Menu_SetStatusBar(&s_controller_buttons_menu, "press a button for this action"); } static void ControllerButtons_MenuInit(void) { int i; s_controller_buttons_menu.x = (int)(viddef.width * 0.50f); s_controller_buttons_menu.nitems = 0; s_controller_buttons_menu.cursordraw = KeyCursorDrawFunc; for (i = 0; i < NUM_CONTROLLER_BINDNAMES; i++) { s_controller_buttons_actions[i].generic.type = MTYPE_ACTION; s_controller_buttons_actions[i].generic.flags = QMF_GRAYED; s_controller_buttons_actions[i].generic.x = 0; s_controller_buttons_actions[i].generic.y = (i * 9); s_controller_buttons_actions[i].generic.ownerdraw = DrawControllerButtonBindingFunc; s_controller_buttons_actions[i].generic.localdata[0] = i; s_controller_buttons_actions[i].generic.name = controller_bindnames[s_controller_buttons_actions[i].generic.localdata[0]][1]; Menu_AddItem(&s_controller_buttons_menu, (void *)&s_controller_buttons_actions[i]); } Menu_SetStatusBar(&s_controller_buttons_menu, "BTN_A assigns, BTN_Y clears, BTN_B exits"); Menu_Center(&s_controller_buttons_menu); } static void ControllerButtons_MenuDraw(void) { Menu_AdjustCursor(&s_controller_buttons_menu, 1); Menu_Draw(&s_controller_buttons_menu); } static const char * ControllerButtons_MenuKey(int key) { menuaction_s *item = (menuaction_s *)Menu_ItemAtCursor(&s_controller_buttons_menu); if (menukeyitem_bind) { // Only controller buttons allowed if (key >= K_JOY_FIRST_REGULAR && key != K_JOY_BACK) { char cmd[1024]; Com_sprintf(cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n", Key_KeynumToString(key), controller_bindnames[item->generic.localdata[0]][0]); Cbuf_InsertText(cmd); } Menu_SetStatusBar(&s_controller_buttons_menu, "BTN_A assigns, BTN_Y clears, BTN_B exits"); menukeyitem_bind = false; return menu_out_sound; } key = Key_GetMenuKey(key); switch (key) { case K_ENTER: ControllerButtonBindingFunc(item); return menu_in_sound; case K_BACKSPACE: M_UnbindCommand(controller_bindnames[item->generic.localdata[0]][0], KEYS_CONTROLLER); return menu_out_sound; default: return Default_MenuKey(&s_controller_buttons_menu, key); } } static void M_Menu_ControllerButtons_f(void) { ControllerButtons_MenuInit(); s_controller_buttons_menu.draw = ControllerButtons_MenuDraw; s_controller_buttons_menu.key = ControllerButtons_MenuKey; M_PushMenu(&s_controller_buttons_menu); } /* * GAME CONTROLLER ALTERNATE BUTTONS MENU */ char *controller_alt_bindnames[][2] = { {"weapnext", "next weapon"}, {"weapprev", "previous weapon"}, {"cycleweap weapon_chaingun weapon_machinegun weapon_blaster", "long range: quickswitch 1"}, {"cycleweap weapon_supershotgun weapon_shotgun", "close range: quickswitch 2"}, {"cycleweap weapon_rocketlauncher weapon_grenadelauncher ammo_grenades", "explosives: quickswitch 3"}, {"cycleweap weapon_bfg weapon_railgun weapon_hyperblaster", "special: quickswitch 4"}, {"prefweap weapon_railgun weapon_hyperblaster weapon_chaingun weapon_supershotgun weapon_machinegun weapon_shotgun weapon_blaster", "best safe weapon"}, {"prefweap weapon_bfg weapon_railgun weapon_rocketlauncher weapon_hyperblaster weapon_grenadelauncher weapon_chaingun ammo_grenades weapon_supershotgun", "best unsafe weapon"}, {"centerview", "center view"}, {"inven", "inventory"}, {"invuse", "use item"}, {"invdrop", "drop item"}, {"invprev", "prev item"}, {"invnext", "next item"}, {"use invulnerability", "use invulnerability"}, {"use rebreather", "use rebreather"}, {"use environment suit", "use environment suit"}, {"use power shield", "use power shield"}, {"use quad damage", "use quad damage"}, {"cmd help", "help computer"} }; #define NUM_CONTROLLER_ALT_BINDNAMES (sizeof controller_alt_bindnames / sizeof controller_alt_bindnames[0]) static menuframework_s s_controller_alt_buttons_menu; static menuaction_s s_controller_alt_buttons_actions[NUM_CONTROLLER_ALT_BINDNAMES]; static void DrawControllerAltButtonBindingFunc(void *self) { int keys[2]; menuaction_s *a = (menuaction_s *)self; float scale = SCR_GetMenuScale(); M_FindKeysForCommand(controller_alt_bindnames[a->generic.localdata[0]][0], keys, KEYS_CONTROLLER_ALT); if (keys[0] == -1) { Menu_DrawString(a->generic.x + a->generic.parent->x + RCOLUMN_OFFSET * 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 + RCOLUMN_OFFSET * 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 ControllerAltButtonBindingFunc(void *self) { menuaction_s *a = (menuaction_s *)self; int keys[2]; M_FindKeysForCommand(controller_alt_bindnames[a->generic.localdata[0]][0], keys, KEYS_CONTROLLER_ALT); if (keys[1] != -1) { M_UnbindCommand(controller_alt_bindnames[a->generic.localdata[0]][0], KEYS_CONTROLLER_ALT); } menukeyitem_bind = true; Menu_SetStatusBar(&s_controller_alt_buttons_menu, "press a button for this action"); } static void ControllerAltButtons_MenuInit(void) { int i; s_controller_alt_buttons_menu.x = (int)(viddef.width * 0.50f); s_controller_alt_buttons_menu.nitems = 0; s_controller_alt_buttons_menu.cursordraw = KeyCursorDrawFunc; for (i = 0; i < NUM_CONTROLLER_ALT_BINDNAMES; i++) { s_controller_alt_buttons_actions[i].generic.type = MTYPE_ACTION; s_controller_alt_buttons_actions[i].generic.flags = QMF_GRAYED; s_controller_alt_buttons_actions[i].generic.x = 0; s_controller_alt_buttons_actions[i].generic.y = (i * 9); s_controller_alt_buttons_actions[i].generic.ownerdraw = DrawControllerAltButtonBindingFunc; s_controller_alt_buttons_actions[i].generic.localdata[0] = i; s_controller_alt_buttons_actions[i].generic.name = controller_alt_bindnames[s_controller_alt_buttons_actions[i].generic.localdata[0]][1]; Menu_AddItem(&s_controller_alt_buttons_menu, (void *)&s_controller_alt_buttons_actions[i]); } Menu_SetStatusBar(&s_controller_alt_buttons_menu, "BTN_A assigns, BTN_Y clears, BTN_B exits"); Menu_Center(&s_controller_alt_buttons_menu); } static void ControllerAltButtons_MenuDraw(void) { Menu_AdjustCursor(&s_controller_alt_buttons_menu, 1); Menu_Draw(&s_controller_alt_buttons_menu); } static const char * ControllerAltButtons_MenuKey(int key) { menuaction_s *item = (menuaction_s *)Menu_ItemAtCursor(&s_controller_alt_buttons_menu); if (menukeyitem_bind) { // Only controller buttons allowed, different from the alt buttons modifier if (key >= K_JOY_FIRST_REGULAR && key != K_JOY_BACK && (keybindings[key] == NULL || strcmp(keybindings[key], "+joyaltselector") != 0)) { char cmd[1024]; key = key + (K_JOY_FIRST_REGULAR_ALT - K_JOY_FIRST_REGULAR); // change input to its ALT mode Com_sprintf(cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n", Key_KeynumToString(key), controller_alt_bindnames[item->generic.localdata[0]][0]); Cbuf_InsertText(cmd); } Menu_SetStatusBar(&s_controller_alt_buttons_menu, "BTN_A assigns, BTN_Y clears, BTN_B exits"); menukeyitem_bind = false; return menu_out_sound; } key = Key_GetMenuKey(key); switch (key) { case K_ENTER: ControllerAltButtonBindingFunc(item); return menu_in_sound; case K_BACKSPACE: M_UnbindCommand(controller_alt_bindnames[item->generic.localdata[0]][0], KEYS_CONTROLLER_ALT); return menu_out_sound; default: return Default_MenuKey(&s_controller_alt_buttons_menu, key); } } static void M_Menu_ControllerAltButtons_f(void) { ControllerAltButtons_MenuInit(); s_controller_alt_buttons_menu.draw = ControllerAltButtons_MenuDraw; s_controller_alt_buttons_menu.key = ControllerAltButtons_MenuKey; M_PushMenu(&s_controller_alt_buttons_menu); } /* * STICKS CONFIGURATION MENU */ static menuframework_s s_sticks_config_menu; static menulist_s s_stk_layout_box; static menuseparator_s s_stk_title_text[2]; static menuslider_s s_stk_expo_slider[2]; static menuslider_s s_stk_deadzone_slider[4]; extern qboolean show_gyro; static void StickLayoutFunc(void *unused) { Cvar_SetValue("joy_layout", (int)s_stk_layout_box.curvalue); } static void Stick_MenuInit(void) { static const char *stick_layouts[] = { "default", "southpaw", "legacy", "legacy southpaw", 0 }; static const char *stick_layouts_fs[] = { "default", "southpaw", "legacy", "legacy southpaw", "flick stick", "flick stick spaw", 0 }; unsigned short int y = 0, i; float scale = SCR_GetMenuScale(); s_sticks_config_menu.x = (int)(viddef.width * 0.50f); s_sticks_config_menu.nitems = 0; s_stk_layout_box.generic.type = MTYPE_SPINCONTROL; s_stk_layout_box.generic.x = 0; s_stk_layout_box.generic.y = y; s_stk_layout_box.generic.name = "layout"; s_stk_layout_box.generic.callback = StickLayoutFunc; if (show_gyro || joy_layout->value > 3) { s_stk_layout_box.itemnames = stick_layouts_fs; s_stk_layout_box.curvalue = ClampCvar(0, 5, joy_layout->value); } else { s_stk_layout_box.itemnames = stick_layouts; s_stk_layout_box.curvalue = ClampCvar(0, 3, joy_layout->value); } s_stk_title_text[0].generic.name = "left stick"; s_stk_title_text[0].generic.y = (y += 22); s_stk_expo_slider[0].generic.name = "expo"; s_stk_expo_slider[0].generic.y = (y += 14); s_stk_expo_slider[0].cvar = "joy_left_expo"; s_stk_deadzone_slider[0].generic.name = "snap to axis"; s_stk_deadzone_slider[0].generic.y = (y += 10); s_stk_deadzone_slider[0].cvar = "joy_left_snapaxis"; s_stk_deadzone_slider[1].generic.name = "deadzone"; s_stk_deadzone_slider[1].generic.y = (y += 10); s_stk_deadzone_slider[1].cvar = "joy_left_deadzone"; s_stk_title_text[1].generic.name = "right stick"; s_stk_title_text[1].generic.y = (y += 22); s_stk_expo_slider[1].generic.name = "expo"; s_stk_expo_slider[1].generic.y = (y += 14); s_stk_expo_slider[1].cvar = "joy_right_expo"; s_stk_deadzone_slider[2].generic.name = "snap to axis"; s_stk_deadzone_slider[2].generic.y = (y += 10); s_stk_deadzone_slider[2].cvar = "joy_right_snapaxis"; s_stk_deadzone_slider[3].generic.name = "deadzone"; s_stk_deadzone_slider[3].generic.y = (y += 10); s_stk_deadzone_slider[3].cvar = "joy_right_deadzone"; for (i = 0; i < 2; i++) { s_stk_title_text[i].generic.type = MTYPE_SEPARATOR; s_stk_title_text[i].generic.x = 48 * scale; s_stk_expo_slider[i].generic.type = MTYPE_SLIDER; s_stk_expo_slider[i].generic.x = 0; s_stk_expo_slider[i].minvalue = 1; s_stk_expo_slider[i].maxvalue = 5; } for (i = 0; i < 4; i++) { s_stk_deadzone_slider[i].generic.type = MTYPE_SLIDER; s_stk_deadzone_slider[i].generic.x = 0; s_stk_deadzone_slider[i].minvalue = 0.0f; s_stk_deadzone_slider[i].maxvalue = 0.30f; s_stk_deadzone_slider[i].slidestep = 0.01f; s_stk_deadzone_slider[i].printformat = "%.2f"; } Menu_AddItem(&s_sticks_config_menu, (void *)&s_stk_layout_box); Menu_AddItem(&s_sticks_config_menu, (void *)&s_stk_title_text[0]); Menu_AddItem(&s_sticks_config_menu, (void *)&s_stk_expo_slider[0]); Menu_AddItem(&s_sticks_config_menu, (void *)&s_stk_deadzone_slider[0]); Menu_AddItem(&s_sticks_config_menu, (void *)&s_stk_deadzone_slider[1]); Menu_AddItem(&s_sticks_config_menu, (void *)&s_stk_title_text[1]); Menu_AddItem(&s_sticks_config_menu, (void *)&s_stk_expo_slider[1]); Menu_AddItem(&s_sticks_config_menu, (void *)&s_stk_deadzone_slider[2]); Menu_AddItem(&s_sticks_config_menu, (void *)&s_stk_deadzone_slider[3]); Menu_Center(&s_sticks_config_menu); } static void Stick_MenuDraw(void) { Menu_AdjustCursor(&s_sticks_config_menu, 1); Menu_Draw(&s_sticks_config_menu); } static const char * Stick_MenuKey(int key) { return Default_MenuKey(&s_sticks_config_menu, key); } static void M_Menu_Stick_f(void) { Stick_MenuInit(); s_sticks_config_menu.draw = Stick_MenuDraw; s_sticks_config_menu.key = Stick_MenuKey; M_PushMenu(&s_sticks_config_menu); } /* * GYRO OPTIONS MENU */ static menuframework_s s_gyro_menu; static menulist_s s_gyro_mode_box; static menulist_s s_turning_axis_box; static menuslider_s s_gyro_yawsensitivity_slider; static menuslider_s s_gyro_pitchsensitivity_slider; static menulist_s s_gyro_invertyaw_box; static menulist_s s_gyro_invertpitch_box; static menuslider_s s_gyro_tightening_slider; static menuseparator_s s_calibrating_text[2]; static menuaction_s s_calibrate_gyro; extern void StartCalibration(void); extern qboolean IsCalibrationZero(void); static void CalibrateGyroFunc(void *unused) { if (!show_gyro) { return; } m_popup_string = "Calibrating, please wait..."; m_popup_endtime = cls.realtime + 4650; M_Popup(); R_EndFrame(); StartCalibration(); } void CalibrationFinishedCallback(void) { Menu_SetStatusBar(&s_gyro_menu, NULL); m_popup_string = "Calibration complete!"; m_popup_endtime = cls.realtime + 1900; M_Popup(); R_EndFrame(); } static void GyroModeFunc(void *unused) { Cvar_SetValue("gyro_mode", (int)s_gyro_mode_box.curvalue); } static void TurningAxisFunc(void *unused) { Cvar_SetValue("gyro_turning_axis", (int)s_turning_axis_box.curvalue); } static void InvertGyroYawFunc(void *unused) { Cvar_SetValue("gyro_yawsensitivity", -Cvar_VariableValue("gyro_yawsensitivity")); } static void InvertGyroPitchFunc(void *unused) { Cvar_SetValue("gyro_pitchsensitivity", -Cvar_VariableValue("gyro_pitchsensitivity")); } static void Gyro_MenuInit(void) { static const char *gyro_modes[] = { "always off", "off, button enables", "on, button disables", "always on", 0 }; static const char *axis_choices[] = { "yaw (turn)", "roll (lean)", 0 }; static const char *yesno_names[] = { "no", "yes", 0 }; unsigned short int y = 0; float scale = SCR_GetMenuScale(); s_gyro_menu.x = (int)(viddef.width * 0.50f); s_gyro_menu.nitems = 0; s_gyro_mode_box.generic.type = MTYPE_SPINCONTROL; s_gyro_mode_box.generic.x = 0; s_gyro_mode_box.generic.y = y; s_gyro_mode_box.generic.name = "gyro mode"; s_gyro_mode_box.generic.callback = GyroModeFunc; s_gyro_mode_box.itemnames = gyro_modes; s_gyro_mode_box.curvalue = ClampCvar(0, 3, gyro_mode->value); s_turning_axis_box.generic.type = MTYPE_SPINCONTROL; s_turning_axis_box.generic.x = 0; s_turning_axis_box.generic.y = (y += 10); s_turning_axis_box.generic.name = "turning axis"; s_turning_axis_box.generic.callback = TurningAxisFunc; s_turning_axis_box.itemnames = axis_choices; s_turning_axis_box.curvalue = ClampCvar(0, 1, gyro_turning_axis->value); s_gyro_yawsensitivity_slider.generic.type = MTYPE_SLIDER; s_gyro_yawsensitivity_slider.generic.x = 0; s_gyro_yawsensitivity_slider.generic.y = (y += 20); s_gyro_yawsensitivity_slider.generic.name = "yaw sensitivity"; s_gyro_yawsensitivity_slider.cvar = "gyro_yawsensitivity"; s_gyro_yawsensitivity_slider.minvalue = 0.1f; s_gyro_yawsensitivity_slider.maxvalue = 8.0f; s_gyro_yawsensitivity_slider.abs = true; s_gyro_pitchsensitivity_slider.generic.type = MTYPE_SLIDER; s_gyro_pitchsensitivity_slider.generic.x = 0; s_gyro_pitchsensitivity_slider.generic.y = (y += 10); s_gyro_pitchsensitivity_slider.generic.name = "pitch sensitivity"; s_gyro_pitchsensitivity_slider.cvar = "gyro_pitchsensitivity"; s_gyro_pitchsensitivity_slider.minvalue = 0.1f; s_gyro_pitchsensitivity_slider.maxvalue = 8.0f; s_gyro_pitchsensitivity_slider.abs = true; s_gyro_invertyaw_box.generic.type = MTYPE_SPINCONTROL; s_gyro_invertyaw_box.generic.x = 0; s_gyro_invertyaw_box.generic.y = (y += 10); s_gyro_invertyaw_box.generic.name = "invert yaw"; s_gyro_invertyaw_box.generic.callback = InvertGyroYawFunc; s_gyro_invertyaw_box.itemnames = yesno_names; s_gyro_invertyaw_box.curvalue = (Cvar_VariableValue("gyro_yawsensitivity") < 0); s_gyro_invertpitch_box.generic.type = MTYPE_SPINCONTROL; s_gyro_invertpitch_box.generic.x = 0; s_gyro_invertpitch_box.generic.y = (y += 10); s_gyro_invertpitch_box.generic.name = "invert pitch"; s_gyro_invertpitch_box.generic.callback = InvertGyroPitchFunc; s_gyro_invertpitch_box.itemnames = yesno_names; s_gyro_invertpitch_box.curvalue = (Cvar_VariableValue("gyro_pitchsensitivity") < 0); s_gyro_tightening_slider.generic.type = MTYPE_SLIDER; s_gyro_tightening_slider.generic.x = 0; s_gyro_tightening_slider.generic.y = (y += 20); s_gyro_tightening_slider.generic.name = "tightening thresh"; s_gyro_tightening_slider.cvar = "gyro_tightening"; s_gyro_tightening_slider.minvalue = 0.0f; s_gyro_tightening_slider.maxvalue = 12.0f; s_gyro_tightening_slider.slidestep = 0.5f; s_calibrating_text[0].generic.type = MTYPE_SEPARATOR; s_calibrating_text[0].generic.x = 48 * scale + 32; s_calibrating_text[0].generic.y = (y += 20); s_calibrating_text[0].generic.name = "place the controller on a flat,"; s_calibrating_text[1].generic.type = MTYPE_SEPARATOR; s_calibrating_text[1].generic.x = 48 * scale + 32; s_calibrating_text[1].generic.y = (y += 10); s_calibrating_text[1].generic.name = "stable surface to..."; s_calibrate_gyro.generic.type = MTYPE_ACTION; s_calibrate_gyro.generic.x = 0; s_calibrate_gyro.generic.y = (y += 15); s_calibrate_gyro.generic.name = "calibrate"; s_calibrate_gyro.generic.callback = CalibrateGyroFunc; Menu_AddItem(&s_gyro_menu, (void *)&s_gyro_mode_box); Menu_AddItem(&s_gyro_menu, (void *)&s_turning_axis_box); Menu_AddItem(&s_gyro_menu, (void *)&s_gyro_yawsensitivity_slider); Menu_AddItem(&s_gyro_menu, (void *)&s_gyro_pitchsensitivity_slider); Menu_AddItem(&s_gyro_menu, (void *)&s_gyro_invertyaw_box); Menu_AddItem(&s_gyro_menu, (void *)&s_gyro_invertpitch_box); Menu_AddItem(&s_gyro_menu, (void *)&s_gyro_tightening_slider); Menu_AddItem(&s_gyro_menu, (void *)&s_calibrating_text[0]); Menu_AddItem(&s_gyro_menu, (void *)&s_calibrating_text[1]); Menu_AddItem(&s_gyro_menu, (void *)&s_calibrate_gyro); if (IsCalibrationZero()) { Menu_SetStatusBar(&s_gyro_menu, "Calibration required"); } Menu_Center(&s_gyro_menu); } static void Gyro_MenuDraw(void) { Menu_AdjustCursor(&s_gyro_menu, 1); Menu_Draw(&s_gyro_menu); M_Popup(); } static const char * Gyro_MenuKey(int key) { return Default_MenuKey(&s_gyro_menu, key); } static void M_Menu_Gyro_f(void) { Gyro_MenuInit(); s_gyro_menu.draw = Gyro_MenuDraw; s_gyro_menu.key = Gyro_MenuKey; M_PushMenu(&s_gyro_menu); } /* * JOY MENU */ static menulist_s s_joy_invertpitch_box; static menuslider_s s_joy_yawsensitivity_slider; static menuslider_s s_joy_pitchsensitivity_slider; static menuslider_s s_joy_forwardsensitivity_slider; static menuslider_s s_joy_sidesensitivity_slider; static menuslider_s s_joy_haptic_slider; static menuaction_s s_joy_stickcfg_action; static menuaction_s s_joy_gyro_action; static menuaction_s s_joy_customize_buttons_action; static menuaction_s s_joy_customize_alt_buttons_action; static void CustomizeControllerButtonsFunc(void *unused) { M_Menu_ControllerButtons_f(); } static void CustomizeControllerAltButtonsFunc(void *unused) { M_Menu_ControllerAltButtons_f(); } static void ConfigStickFunc(void *unused) { M_Menu_Stick_f(); } static void ConfigGyroFunc(void *unused) { M_Menu_Gyro_f(); } static void InvertJoyPitchFunc(void *unused) { Cvar_SetValue("joy_pitchsensitivity", -Cvar_VariableValue("joy_pitchsensitivity")); } static void Joy_MenuInit(void) { static const char *yesno_names[] = { "no", "yes", 0 }; extern qboolean show_haptic; unsigned short int y = 0; s_joy_menu.x = (int)(viddef.width * 0.50f); s_joy_menu.nitems = 0; s_joy_yawsensitivity_slider.generic.type = MTYPE_SLIDER; s_joy_yawsensitivity_slider.generic.x = 0; s_joy_yawsensitivity_slider.generic.y = y; s_joy_yawsensitivity_slider.generic.name = "yaw sensitivity"; s_joy_yawsensitivity_slider.cvar = "joy_yawsensitivity"; s_joy_yawsensitivity_slider.minvalue = 0.0f; s_joy_yawsensitivity_slider.maxvalue = 7.0f; Menu_AddItem(&s_joy_menu, (void *)&s_joy_yawsensitivity_slider); s_joy_pitchsensitivity_slider.generic.type = MTYPE_SLIDER; s_joy_pitchsensitivity_slider.generic.x = 0; s_joy_pitchsensitivity_slider.generic.y = (y += 10); s_joy_pitchsensitivity_slider.generic.name = "pitch sensitivity"; s_joy_pitchsensitivity_slider.cvar = "joy_pitchsensitivity"; s_joy_pitchsensitivity_slider.minvalue = 0.0f; s_joy_pitchsensitivity_slider.maxvalue = 7.0f; s_joy_pitchsensitivity_slider.abs = true; Menu_AddItem(&s_joy_menu, (void *)&s_joy_pitchsensitivity_slider); s_joy_invertpitch_box.generic.type = MTYPE_SPINCONTROL; s_joy_invertpitch_box.generic.x = 0; s_joy_invertpitch_box.generic.y = (y += 10); s_joy_invertpitch_box.generic.name = "invert pitch"; s_joy_invertpitch_box.generic.callback = InvertJoyPitchFunc; s_joy_invertpitch_box.itemnames = yesno_names; s_joy_invertpitch_box.curvalue = (Cvar_VariableValue("joy_pitchsensitivity") < 0); Menu_AddItem(&s_joy_menu, (void *)&s_joy_invertpitch_box); s_joy_forwardsensitivity_slider.generic.type = MTYPE_SLIDER; s_joy_forwardsensitivity_slider.generic.x = 0; s_joy_forwardsensitivity_slider.generic.y = (y += 20); s_joy_forwardsensitivity_slider.generic.name = "forward sensitivity"; s_joy_forwardsensitivity_slider.cvar = "joy_forwardsensitivity"; s_joy_forwardsensitivity_slider.minvalue = 0.0f; s_joy_forwardsensitivity_slider.maxvalue = 2.0f; Menu_AddItem(&s_joy_menu, (void *)&s_joy_forwardsensitivity_slider); s_joy_sidesensitivity_slider.generic.type = MTYPE_SLIDER; s_joy_sidesensitivity_slider.generic.x = 0; s_joy_sidesensitivity_slider.generic.y = (y += 10); s_joy_sidesensitivity_slider.generic.name = "side sensitivity"; s_joy_sidesensitivity_slider.cvar = "joy_sidesensitivity"; s_joy_sidesensitivity_slider.minvalue = 0.0f; s_joy_sidesensitivity_slider.maxvalue = 2.0f; Menu_AddItem(&s_joy_menu, (void *)&s_joy_sidesensitivity_slider); if (show_haptic) { s_joy_haptic_slider.generic.type = MTYPE_SLIDER; s_joy_haptic_slider.generic.x = 0; s_joy_haptic_slider.generic.y = (y += 20); s_joy_haptic_slider.generic.name = "rumble intensity"; s_joy_haptic_slider.cvar = "joy_haptic_magnitude"; s_joy_haptic_slider.minvalue = 0.0f; s_joy_haptic_slider.maxvalue = 2.0f; Menu_AddItem(&s_joy_menu, (void *)&s_joy_haptic_slider); } s_joy_stickcfg_action.generic.type = MTYPE_ACTION; s_joy_stickcfg_action.generic.x = 0; s_joy_stickcfg_action.generic.y = (y += 20); s_joy_stickcfg_action.generic.name = "sticks config"; s_joy_stickcfg_action.generic.callback = ConfigStickFunc; Menu_AddItem(&s_joy_menu, (void *)&s_joy_stickcfg_action); if (show_gyro) { s_joy_gyro_action.generic.type = MTYPE_ACTION; s_joy_gyro_action.generic.x = 0; s_joy_gyro_action.generic.y = (y += 10); s_joy_gyro_action.generic.name = "gyro options"; s_joy_gyro_action.generic.callback = ConfigGyroFunc; Menu_AddItem(&s_joy_menu, (void *)&s_joy_gyro_action); } s_joy_customize_buttons_action.generic.type = MTYPE_ACTION; s_joy_customize_buttons_action.generic.x = 0; s_joy_customize_buttons_action.generic.y = (y += 20); s_joy_customize_buttons_action.generic.name = "customize buttons"; s_joy_customize_buttons_action.generic.callback = CustomizeControllerButtonsFunc; Menu_AddItem(&s_joy_menu, (void *)&s_joy_customize_buttons_action); s_joy_customize_alt_buttons_action.generic.type = MTYPE_ACTION; s_joy_customize_alt_buttons_action.generic.x = 0; s_joy_customize_alt_buttons_action.generic.y = (y += 10); s_joy_customize_alt_buttons_action.generic.name = "custom. alt buttons"; s_joy_customize_alt_buttons_action.generic.callback = CustomizeControllerAltButtonsFunc; Menu_AddItem(&s_joy_menu, (void *)&s_joy_customize_alt_buttons_action); Menu_Center(&s_joy_menu); } static void Joy_MenuDraw(void) { Menu_AdjustCursor(&s_joy_menu, 1); Menu_Draw(&s_joy_menu); } static const char * Joy_MenuKey(int key) { return Default_MenuKey(&s_joy_menu, key); } static void M_Menu_Joy_f(void) { Joy_MenuInit(); s_joy_menu.draw = Joy_MenuDraw; s_joy_menu.key = Joy_MenuKey; M_PushMenu(&s_joy_menu); } /* * CONTROLS MENU */ static menuframework_s s_options_menu; static menuaction_s s_options_defaults_action; static menuaction_s s_options_customize_options_action; static menuaction_s s_options_customize_joy_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; static menulist_s s_options_oggshuffle_box; static menuslider_s s_options_oggvolume_slider; static menulist_s s_options_oggenable_box; static menulist_s s_options_quality_list; static menulist_s s_options_console_action; static menulist_s s_options_pauseonfocus_box; static void CrosshairFunc(void *unused) { Cvar_SetValue("crosshair", (float)s_options_crosshair_box.curvalue); } static void PauseFocusFunc() { Cvar_SetValue("vid_pauseonfocuslost", (float)s_options_pauseonfocus_box.curvalue); } static void CustomizeControlsFunc(void *unused) { M_Menu_Keys_f(); } static void CustomizeJoyFunc(void *unused) { M_Menu_Joy_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 ControlsSetMenuItemValues(void) { s_options_oggshuffle_box.curvalue = Cvar_VariableValue("ogg_shuffle"); s_options_oggenable_box.curvalue = (Cvar_VariableValue("ogg_enable") != 0); s_options_quality_list.curvalue = (Cvar_VariableValue("s_loadas8bit") == 0); 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); s_options_pauseonfocus_box.curvalue = ClampCvar(0, 2, Cvar_VariableValue("vid_pauseonfocuslost")); } static void ControlsResetDefaultsFunc(void *unused) { Cbuf_AddText("exec default.cfg\n"); Cbuf_AddText("exec yq2.cfg\n"); Cbuf_Execute(); ControlsSetMenuItemValues(); s_options_oggshuffle_box.curvalue = 0; } 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 OGGShuffleFunc(void *unused) { Cvar_SetValue("ogg_shuffle", s_options_oggshuffle_box.curvalue); } static void EnableOGGMusic(void *unused) { Cvar_SetValue("ogg_enable", (float)s_options_oggenable_box.curvalue); if (s_options_oggenable_box.curvalue) { OGG_Init(); OGG_InitTrackList(); OGG_Stop(); if (cls.state == ca_active) { int track = (int)strtol(cl.configstrings[CS_CDTRACK], (char **)NULL, 10); OGG_PlayTrack(track, true, true); } } else { OGG_Shutdown(); } } 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) { extern qboolean show_gamepad; static const char *ogg_shuffle_items[] = { "default", "play once", "sequential", "random", 0 }; static const char *able_items[] = { "disabled", "enabled", 0 }; static const char *quality_items[] = { "normal", "high", 0 }; static const char *yesno_names[] = { "no", "yes", 0 }; static const char* pause_names[] = { "yes", "no", "unpause on re-focus", 0 }; static const char *crosshair_names[] = { "none", "cross", "dot", "angle", 0 }; float scale = SCR_GetMenuScale(); unsigned short int y = 0; /* 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 = y; s_options_sfxvolume_slider.generic.name = "effects volume"; s_options_sfxvolume_slider.cvar = "s_volume"; s_options_sfxvolume_slider.minvalue = 0.0f; s_options_sfxvolume_slider.maxvalue = 1.0f; s_options_oggvolume_slider.generic.type = MTYPE_SLIDER; s_options_oggvolume_slider.generic.x = 0; s_options_oggvolume_slider.generic.y = (y += 10); s_options_oggvolume_slider.generic.name = "OGG volume"; s_options_oggvolume_slider.cvar = "ogg_volume"; s_options_oggvolume_slider.minvalue = 0.0f; s_options_oggvolume_slider.maxvalue = 1.0f; s_options_oggenable_box.generic.type = MTYPE_SPINCONTROL; s_options_oggenable_box.generic.x = 0; s_options_oggenable_box.generic.y = (y += 10); s_options_oggenable_box.generic.name = "OGG music"; s_options_oggenable_box.generic.callback = EnableOGGMusic; s_options_oggenable_box.itemnames = able_items; s_options_oggshuffle_box.generic.type = MTYPE_SPINCONTROL; s_options_oggshuffle_box.generic.x = 0; s_options_oggshuffle_box.generic.y = (y += 10); s_options_oggshuffle_box.generic.name = "OGG shuffle"; s_options_oggshuffle_box.generic.callback = OGGShuffleFunc; s_options_oggshuffle_box.itemnames = ogg_shuffle_items; s_options_quality_list.generic.type = MTYPE_SPINCONTROL; s_options_quality_list.generic.x = 0; s_options_quality_list.generic.y = (y += 10); 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 = (y += 20); s_options_sensitivity_slider.generic.name = "mouse speed"; s_options_sensitivity_slider.cvar = "sensitivity"; s_options_sensitivity_slider.minvalue = 0; s_options_sensitivity_slider.maxvalue = 11; s_options_sensitivity_slider.slidestep = 0.5f; s_options_alwaysrun_box.generic.type = MTYPE_SPINCONTROL; s_options_alwaysrun_box.generic.x = 0; s_options_alwaysrun_box.generic.y = (y += 10); 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 = (y += 10); 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 = (y += 10); 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 = (y += 10); 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 = (y += 10); s_options_crosshair_box.generic.name = "crosshair"; s_options_crosshair_box.generic.callback = CrosshairFunc; s_options_crosshair_box.itemnames = crosshair_names; s_options_pauseonfocus_box.generic.type = MTYPE_SPINCONTROL; s_options_pauseonfocus_box.generic.x = 0; s_options_pauseonfocus_box.generic.y = (y += 10); s_options_pauseonfocus_box.generic.name = "pause on minimized"; s_options_pauseonfocus_box.generic.callback = PauseFocusFunc; s_options_pauseonfocus_box.itemnames = pause_names; y += 10; if (show_gamepad) { s_options_customize_joy_action.generic.type = MTYPE_ACTION; s_options_customize_joy_action.generic.x = 0; s_options_customize_joy_action.generic.y = (y += 10); s_options_customize_joy_action.generic.name = "customize gamepad"; s_options_customize_joy_action.generic.callback = CustomizeJoyFunc; } s_options_customize_options_action.generic.type = MTYPE_ACTION; s_options_customize_options_action.generic.x = 0; s_options_customize_options_action.generic.y = (y += 10); 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 = (y += 10); 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 = (y += 10); 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); Menu_AddItem(&s_options_menu, (void *)&s_options_oggvolume_slider); Menu_AddItem(&s_options_menu, (void *)&s_options_oggenable_box); Menu_AddItem(&s_options_menu, (void *)&s_options_oggshuffle_box); 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); Menu_AddItem(&s_options_menu, (void*)&s_options_pauseonfocus_box); if (show_gamepad) { Menu_AddItem(&s_options_menu, (void *)&s_options_customize_joy_action); } 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(); s_options_menu.draw = Options_MenuDraw; s_options_menu.key = Options_MenuKey; M_PushMenu(&s_options_menu); } /* * END GAME MENU */ menuframework_s s_credits; #define CREDITS_SIZE 256 static int credits_start_time; static const char **credits; static char *creditsIndex[CREDITS_SIZE]; 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", "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", "", "+PATCHES AUTHORS", "eliasm", "", "+YAMAGI QUAKE II BY", "Yamagi Burmeister", "Daniel Gibson", "Sander van Dijk", "Denis Pauk", "Bjorn Alfredsson", "Jaime Moreira", "", "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", "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", "Zoid Kirsch", "", "", "", "+THANKS TO ACTIVISION", "+IN PARTICULAR:", "", "Marty Stratton", "Henk Hartong", "Mitch Lasky", "Steve Rosenthal", "Steve Elwell", "", "+AND THE GAME TESTERS", "", "The Ranger Clan", "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_Draw(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; } } static const char * M_Credits_Key(int key) { key = Key_GetMenuKey(key); if (key == K_ESCAPE) { if (creditsBuffer) { FS_FreeFile(creditsBuffer); } M_PopMenu(); } return menu_out_sound; } static void M_Menu_Credits_f(void) { int count; char *p; creditsBuffer = NULL; count = FS_LoadFile("credits", (void **)&creditsBuffer); if (count != -1) { int n; p = creditsBuffer; // CREDITS_SIZE - 1 - last pointer should be NULL for (n = 0; n < CREDITS_SIZE - 1; 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) { // no credits any more // move one step futher for set NULL n ++; 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; s_credits.draw = M_Credits_Draw; s_credits.key = M_Credits_Key; M_PushMenu(&s_credits); } /* * MODS MENU */ static menuframework_s s_mods_menu; static menulist_s s_mods_list; static menuaction_s s_mods_apply_action; static char mods_statusbar[64]; static char **modnames = NULL; static int nummods; static void Mods_NamesInit(void) { /* initialize list of mods once, reuse it afterwards (=> it isn't freed) */ if (modnames == NULL) { modnames = FS_ListMods(&nummods); } } static void ModsListFunc(void *unused) { if (strcmp(BASEDIRNAME, modnames[s_mods_list.curvalue]) == 0) { strcpy(mods_statusbar, "Quake II"); } else if (strcmp("ctf", modnames[s_mods_list.curvalue]) == 0) { strcpy(mods_statusbar, "Quake II Capture The Flag"); } else if (strcmp("rogue", modnames[s_mods_list.curvalue]) == 0) { strcpy(mods_statusbar, "Quake II Mission Pack: Ground Zero"); } else if (strcmp("xatrix", modnames[s_mods_list.curvalue]) == 0) { strcpy(mods_statusbar, "Quake II Mission Pack: The Reckoning"); } else { strcpy(mods_statusbar, "\0"); } } static void ModsApplyActionFunc(void *unused) { if (!M_IsGame(modnames[s_mods_list.curvalue])) { if(Com_ServerState()) { // equivalent to "killserver" cmd, but avoids cvar latching below SV_Shutdown("Server is changing games.\n", false); NET_Config(false); } // called via command buffer so that any running server has time to shutdown Cbuf_AddText(va("game %s\n", modnames[s_mods_list.curvalue])); // start the demo cycle in the new game directory menu_startdemoloop = true; M_ForceMenuOff(); } } static void Mods_MenuInit(void) { int currentmod; int x = 0; int y = 0; char modname[MAX_QPATH]; //TG626 Mods_NamesInit(); // create array of bracketed display names from folder names - TG626 char **displaynames=malloc(sizeof(*displaynames) * (nummods+1)); for (int i=0; i < nummods; i++) { strcpy(modname, "["); if (strlen(modnames[i]) < 16) { strcat(modname, modnames[i]); for (int j=0; j < 15 - strlen(modnames[i]); j++) { strcat(modname, " "); } } else { strncat(modname, modnames[i], 12); strcat(modname, "..."); } strcat(modname, "]"); displaynames[i] = malloc(strlen(modname) + 1); strcpy(displaynames[i], modname); } displaynames[nummods] = NULL; //end TG626 // pre-select the current mod for display in the list for (currentmod = 0; currentmod < nummods; currentmod++) { if (M_IsGame(modnames[currentmod])) { break; } } s_mods_menu.x = viddef.width * 0.50; s_mods_menu.nitems = 0; s_mods_list.generic.type = MTYPE_SPINCONTROL; s_mods_list.generic.name = "mod"; s_mods_list.generic.x = x; s_mods_list.generic.y = y; s_mods_list.generic.callback = ModsListFunc; s_mods_list.itemnames = (const char **)displaynames; s_mods_list.curvalue = currentmod < nummods ? currentmod : 0; y += 20; s_mods_apply_action.generic.type = MTYPE_ACTION; s_mods_apply_action.generic.name = "apply"; s_mods_apply_action.generic.x = x; s_mods_apply_action.generic.y = y; s_mods_apply_action.generic.callback = ModsApplyActionFunc; Menu_AddItem(&s_mods_menu, (void *)&s_mods_list); Menu_AddItem(&s_mods_menu, (void *)&s_mods_apply_action); Menu_Center(&s_mods_menu); /* set the original mods statusbar */ ModsListFunc(0); Menu_SetStatusBar(&s_mods_menu, mods_statusbar); } static void Mods_MenuDraw(void) { Menu_AdjustCursor(&s_mods_menu, 1); Menu_Draw(&s_mods_menu); M_Popup(); } static const char * Mods_MenuKey(int key) { return Default_MenuKey(&s_mods_menu, key); } static void M_Menu_Mods_f(void) { Mods_MenuInit(); s_mods_menu.draw = Mods_MenuDraw; s_mods_menu.key = Mods_MenuKey; M_PushMenu(&s_mods_menu); } /* * 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 menuaction_s s_mods_action; static menuseparator_s s_blankline; static void StartGame(void) { if (cls.state != ca_disconnected && cls.state != ca_uninitialized) { CL_Disconnect(); } /* 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(); } static void ModsFunc(void *unused) { M_Menu_Mods_f(); } void Game_MenuInit(void) { Mods_NamesInit(); 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 = "hard+"; 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_credits_action); if(nummods > 1) { s_mods_action.generic.type = MTYPE_ACTION; s_mods_action.generic.flags = QMF_LEFT_JUSTIFY; s_mods_action.generic.x = 0; s_mods_action.generic.y = 90; s_mods_action.generic.name = "mods"; s_mods_action.generic.callback = ModsFunc; Menu_AddItem(&s_game_menu, (void *)&s_blankline); Menu_AddItem(&s_game_menu, (void *)&s_mods_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(); s_game_menu.draw = Game_MenuDraw; s_game_menu.key = Game_MenuKey; M_PushMenu(&s_game_menu); m_game_cursor = 1; } /* * LOADGAME MENU */ #define MAX_SAVESLOTS 14 #define MAX_SAVEPAGES 4 // The magic comment string length of 32 is the same as // the comment string length set in SV_WriteServerFile()! static char m_quicksavestring[32]; static qboolean m_quicksavevalid; 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 + 1]; // One for quick static menuframework_s s_savegame_menu; static menuaction_s s_savegame_actions[MAX_SAVESLOTS + 1]; // One for quick /* DELETE SAVEGAME */ static qboolean menukeyitem_delete = false; static void PromptDeleteSaveFunc(menuframework_s *m) { menucommon_s *item = Menu_ItemAtCursor(m); if (item == NULL || item->type != MTYPE_ACTION) { return; } if (item->localdata[0] == -1) { if (m_quicksavevalid) { menukeyitem_delete = true; } } else { if (m_savevalid[item->localdata[0] - m_loadsave_page * MAX_SAVESLOTS]) { menukeyitem_delete = true; } } if (menukeyitem_delete) { Menu_SetStatusBar( m, "are you sure you want to delete? y\\n" ); } } static qboolean ExecDeleteSaveFunc(menuframework_s *m, int menu_key) { menucommon_s *item = Menu_ItemAtCursor(m); menukeyitem_delete = false; if (menu_key == K_ENTER || menu_key == 'y' || menu_key == 'Y') { char name[MAX_OSPATH] = {0}; if (item->localdata[0] == -1) // quicksave { Com_sprintf(name, sizeof(name), "%s/save/quick/", FS_Gamedir()); } else { Com_sprintf(name, sizeof(name), "%s/save/save%d/", FS_Gamedir(), item->localdata[0]); } Sys_RemoveDir(name); return true; } Menu_SetStatusBar( m, m_loadsave_statusbar ); return false; } static void Create_Savestrings(void) { int i; fileHandle_t f; char name[MAX_OSPATH]; char tmp[32]; // Same length as m_quicksavestring- // The quicksave slot... FS_FOpenFile("save/quick/server.ssv", &f, true); if (!f) { strcpy(m_quicksavestring, "QUICKSAVE "); m_quicksavevalid = false; } else { FS_Read(tmp, sizeof(tmp), f); FS_FCloseFile(f); if (strlen(tmp) > 12) { /* Horrible hack to construct a nice looking 'QUICKSAVE Level Name' comment string matching the 'ENTERING Level Name' comment string of autosaves. The comment field is in format 'HH:MM mm:dd Level Name'. Remove the date (the first 13) characters and replace it with 'QUICKSAVE'. */ Com_sprintf(m_quicksavestring, sizeof(m_quicksavestring), "QUICKSAVE %s", tmp + 13); } else { Q_strlcpy(m_quicksavestring, tmp, sizeof(m_quicksavestring)); } m_quicksavevalid = true; } // ... and everything else. 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; 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++) { char *str; 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; if (a->generic.localdata[0] == -1) { Cbuf_AddText("load quick\n"); } else { 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(); // The quicksave slot... s_loadgame_actions[0].generic.type = MTYPE_ACTION; s_loadgame_actions[0].generic.name = m_quicksavestring; s_loadgame_actions[0].generic.x = 0; s_loadgame_actions[0].generic.y = 0; s_loadgame_actions[0].generic.localdata[0] = -1; s_loadgame_actions[0].generic.flags = QMF_LEFT_JUSTIFY; if (!m_quicksavevalid) { s_loadgame_actions[0].generic.callback = NULL; } else { s_loadgame_actions[0].generic.callback = LoadGameCallback; } Menu_AddItem(&s_loadgame_menu, &s_loadgame_actions[0]); // ...and everything else. for (i = 0; i < MAX_SAVESLOTS; i++) { s_loadgame_actions[i + 1].generic.type = MTYPE_ACTION; s_loadgame_actions[i + 1].generic.name = m_savestrings[i]; s_loadgame_actions[i + 1].generic.x = 0; s_loadgame_actions[i + 1].generic.y = i * 10 + 20; s_loadgame_actions[i + 1].generic.localdata[0] = i + m_loadsave_page * MAX_SAVESLOTS; s_loadgame_actions[i + 1].generic.flags = QMF_LEFT_JUSTIFY; if (!m_savevalid[i]) { s_loadgame_actions[i + 1].generic.callback = NULL; } else { s_loadgame_actions[i + 1].generic.callback = LoadGameCallback; } Menu_AddItem(&s_loadgame_menu, &s_loadgame_actions[i + 1]); } 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); if (menukeyitem_delete) { if (ExecDeleteSaveFunc(m, menu_key)) { LoadGame_MenuInit(); } return menu_move_sound; } 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; case K_BACKSPACE: PromptDeleteSaveFunc(m); 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(); s_loadgame_menu.draw = LoadGame_MenuDraw; s_loadgame_menu.key = LoadGame_MenuKey; M_PushMenu(&s_loadgame_menu); } /* * SAVEGAME MENU */ static void SaveGameCallback(void *self) { menuaction_s *a = (menuaction_s *)self; if (a->generic.localdata[0] == -1) { m_popup_string = "This slot is reserved for\n" "quicksaving, so please select\n" "another one."; m_popup_endtime = cls.realtime + 2000; M_Popup(); return; } else 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(); // The quicksave slot... s_savegame_actions[0].generic.type = MTYPE_ACTION; s_savegame_actions[0].generic.name = m_quicksavestring; s_savegame_actions[0].generic.x = 0; s_savegame_actions[0].generic.y = 0; s_savegame_actions[0].generic.localdata[0] = -1; s_savegame_actions[0].generic.flags = QMF_LEFT_JUSTIFY; s_savegame_actions[0].generic.callback = SaveGameCallback; Menu_AddItem(&s_savegame_menu, &s_savegame_actions[0]); // ...and everything else. for (i = 0; i < MAX_SAVESLOTS; i++) { s_savegame_actions[i + 1].generic.type = MTYPE_ACTION; s_savegame_actions[i + 1].generic.name = m_savestrings[i]; s_savegame_actions[i + 1].generic.x = 0; s_savegame_actions[i + 1].generic.y = i * 10 + 20; s_savegame_actions[i + 1].generic.localdata[0] = i + m_loadsave_page * MAX_SAVESLOTS; s_savegame_actions[i + 1].generic.flags = QMF_LEFT_JUSTIFY; s_savegame_actions[i + 1].generic.callback = SaveGameCallback; Menu_AddItem(&s_savegame_menu, &s_savegame_actions[i + 1]); } 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; } if (menukeyitem_delete) { if (ExecDeleteSaveFunc(m, menu_key)) { SaveGame_MenuInit(); } return menu_move_sound; } 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; case K_BACKSPACE: PromptDeleteSaveFunc(m); 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(); s_savegame_menu.draw = SaveGame_MenuDraw; s_savegame_menu.key = SaveGame_MenuKey; M_PushMenu(&s_savegame_menu); } /* * 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(); s_joinserver_menu.draw = JoinServer_MenuDraw; s_joinserver_menu.key = JoinServer_MenuKey; M_PushMenu(&s_joinserver_menu); } /* * START SERVER MENU */ static menuframework_s s_startserver_menu; char **mapnames = NULL; int nummaps; 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_capturelimit_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 capturelimit; 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); if (M_IsGame("ctf")) { capturelimit = (float)strtod(s_capturelimit_field.buffer, (char **)NULL); Cvar_SetValue("capturelimit", ClampCvar(0, capturelimit, capturelimit)); } 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); Cvar_SetValue("singleplayer", 0); 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; float scale = SCR_GetMenuScale(); /* initialize list of maps once, reuse it afterwards (=> it isn't freed unless the game dir is changed) */ if (mapnames == NULL) { int i, length; size_t nummapslen; nummaps = 0; s_startmap_list.curvalue = 0; /* 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] == '\n') { nummaps++; } i++; } if (nummaps == 0) { Com_Error(ERR_DROP, "no maps in maps.lst\n"); } nummapslen = sizeof(char *) * (nummaps + 1); mapnames = malloc(nummapslen); YQ2_COM_CHECK_OOM(mapnames, "malloc(sizeof(char *) * (nummaps + 1))", nummapslen) memset(mapnames, 0, nummapslen); 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((unsigned char)shortname[j]); } strcpy(longname, COM_Parse(&s)); Com_sprintf(scratch, sizeof(scratch), "%s\n%s", longname, shortname); mapnames[i] = strdup(scratch); YQ2_COM_CHECK_OOM(mapnames[i], "strdup(scratch)", strlen(scratch)+1) } 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; if (M_IsGame("ctf")) s_startmap_list.generic.y = -8; else s_startmap_list.generic.y = 0; s_startmap_list.generic.name = "initial map"; s_startmap_list.itemnames = (const char **)mapnames; if (M_IsGame("ctf")) { s_capturelimit_field.generic.type = MTYPE_FIELD; s_capturelimit_field.generic.name = "capture limit"; s_capturelimit_field.generic.flags = QMF_NUMBERSONLY; s_capturelimit_field.generic.x = 0; s_capturelimit_field.generic.y = 18; s_capturelimit_field.generic.statusbar = "0 = no limit"; s_capturelimit_field.length = 3; s_capturelimit_field.visible_length = 3; strcpy(s_capturelimit_field.buffer, Cvar_VariableString("capturelimit")); } else { 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_hostname_field.cursor = strlen(s_hostname_field.buffer); 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); if (M_IsGame("ctf")) Menu_AddItem(&s_startserver_menu, &s_capturelimit_field); else 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(); s_startserver_menu.draw = StartServer_MenuDraw; s_startserver_menu.key = StartServer_MenuKey; M_PushMenu(&s_startserver_menu); } /* * 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; } } else if (M_IsGame("ctf")) { if (f == &s_no_mines_box) { bit = DF_NO_MINES; /* Equivalent to DF_CTF_FORCEJOIN in CTF */ } else if (f == &s_no_nukes_box) { bit = DF_NO_NUKES; /* Equivalent to DF_CTF_NO_TECH in CTF */ } else if (f == &s_stack_double_box) { bit = DF_NO_STACK_DOUBLE; /* Equivalent to DF_ARMOR_PROTECT in CTF */ } } 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"); unsigned short 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; if (!M_IsGame("ctf")) { 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; if (!M_IsGame("ctf")) { 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; } else if (M_IsGame("ctf")) { 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 = "force join"; 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_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 = "armor protect"; 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_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 = "techs off"; 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; } 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); if (!M_IsGame("ctf")) 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); if (!M_IsGame("ctf")) Menu_AddItem(&s_dmoptions_menu, &s_friendlyfire_box); if (M_IsGame("rogue")) { 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); } else if (M_IsGame("ctf")) { Menu_AddItem(&s_dmoptions_menu, &s_no_mines_box); Menu_AddItem(&s_dmoptions_menu, &s_stack_double_box); Menu_AddItem(&s_dmoptions_menu, &s_no_nukes_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(); s_dmoptions_menu.draw = DMOptions_MenuDraw; s_dmoptions_menu.key = DMOptions_MenuKey; M_PushMenu(&s_dmoptions_menu); } /* * DOWNLOADOPTIONS BOOK MENU */ static menuframework_s s_downloadoptions_menu; static menuseparator_s s_download_title; static menulist_s s_allow_download_box; #ifdef USE_CURL static menulist_s s_allow_download_http_box; #endif 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); } #ifdef USE_CURL else if (f == &s_allow_download_http_box) { Cvar_SetValue("cl_http_downloads", f->curvalue); } #endif 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 }; unsigned short 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); #ifdef USE_CURL s_allow_download_http_box.generic.type = MTYPE_SPINCONTROL; s_allow_download_http_box.generic.x = 0; s_allow_download_http_box.generic.y = y += 20; s_allow_download_http_box.generic.name = "http downloading"; s_allow_download_http_box.generic.callback = DownloadCallback; s_allow_download_http_box.itemnames = yes_no_names; s_allow_download_http_box.curvalue = (Cvar_VariableValue("cl_http_downloads") != 0); #else y += 10; #endif 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 += 10; 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); #ifdef USE_CURL Menu_AddItem(&s_downloadoptions_menu, &s_allow_download_http_box); #endif 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(); s_downloadoptions_menu.draw = DownloadOptions_MenuDraw; s_downloadoptions_menu.key = DownloadOptions_MenuKey; M_PushMenu(&s_downloadoptions_menu); } /* * 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(); s_addressbook_menu.draw = AddressBook_MenuDraw; s_addressbook_menu.key = AddressBook_MenuKey; M_PushMenu(&s_addressbook_menu); } /* * PLAYER CONFIG MENU */ static menuframework_s s_player_config_menu; static menufield_s s_player_name_field; static menubitmap_s s_player_icon_bitmap; 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_PLAYERMODELS 1024 typedef struct _stringlist { char** data; int num; } stringlist_t; // player model info static stringlist_t s_skinnames[MAX_PLAYERMODELS]; static stringlist_t s_modelname; static stringlist_t s_directory; 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_skinnames[s_player_model_box.curvalue].data; s_player_skin_box.curvalue = 0; } // returns true if icon .pcx exists for skin .pcx 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; } // strip file extension static void StripExtension(char* path) { int length; length = strlen(path) - 1; while (length > 0 && path[length] != '.') { length--; if (path[length] == '/') { return; // no extension } } if (length) { path[length] = 0; } } // returns true if file is in path static qboolean ContainsFile(char* path, char* file) { char pathname[MAX_QPATH]; int handle = 0; int length = 0; qboolean result = false; if (path != 0 && file != 0) { Com_sprintf(pathname, MAX_QPATH, "%s/%s", path, file); length = FS_FOpenFile(pathname, &handle, false); // verify the existence of file if (handle != 0 && length != 0) { FS_FCloseFile(handle); result = true; } } return result; } // replace characters in string static void ReplaceCharacters(char* s, char r, char c) { char* p = s; if (p == 0) { return; } while (*p != 0) { if (*p == r) { *p = c; } p++; } } // qsort directory name compare function static int dircmp_func(const void* _a, const void* _b) { const char* a = strrchr(*(const char**)_a, '/') + 1; const char* b = strrchr(*(const char**)_b, '/') + 1; // sort by male, female, then alphabetical if (strcmp(a, "male") == 0) { return -1; } else if (strcmp(b, "male") == 0) { return 1; } if (strcmp(a, "female") == 0) { return -1; } else if (strcmp(b, "female") == 0) { return 1; } return strcmp(a, b); } // frees all player model info static void PlayerModelFree() { char* s = NULL; // there should be no valid skin names if there is no valid model if (s_modelname.num != 0) { while (s_modelname.num-- > 0) { // skins while (s_skinnames[s_modelname.num].num-- > 0) { s = s_skinnames[s_modelname.num].data[s_skinnames[s_modelname.num].num]; if (s != 0) { free(s); s = 0; } } s = (char*)s_skinnames[s_modelname.num].data; if (s != 0) { free(s); s = 0; } s_skinnames[s_modelname.num].data = 0; s_skinnames[s_modelname.num].num = 0; // models s = s_modelname.data[s_modelname.num]; if (s != 0) { free(s); s = 0; } } } s = (char*)s_modelname.data; if (s != 0) { free(s); s = 0; } s_modelname.data = 0; s_modelname.num = 0; // directories while (s_directory.num-- > 0) { s = s_directory.data[s_directory.num]; if (s != 0) { free(s); s = 0; } } s = (char*)s_directory.data; if (s != 0) { free(s); s = 0; } s_directory.data = 0; s_directory.num = 0; } // list all player model directories. // directory names are stored players/. // directory number never exceeds MAX_PLAYERMODELS static qboolean PlayerDirectoryList(void) { char* findname = "players/*"; char** list = NULL; int num = 0; // get a list of "players" subdirectories if ((list = FS_ListFiles2(findname, &num, 0, 0)) == NULL) { return false; } if (num > MAX_PLAYERMODELS) { Com_Printf("Too many player models (%d)!\n", num); num = MAX_PLAYERMODELS - 1; } // malloc directories char** data = (char**)calloc(num, sizeof(char*)); YQ2_COM_CHECK_OOM(data, "calloc()", num * sizeof(char*)) s_directory.data = data; s_directory.num = num; for (int i = 0; i < num; ++i) { // last element of FS_FileList maybe null if (list[i] == 0) { break; } ReplaceCharacters(list[i], '\\', '/'); char* s = (char*)malloc(MAX_QPATH); char* t = list[i]; YQ2_COM_CHECK_OOM(s, "malloc()", MAX_QPATH * sizeof(char)) Q_strlcpy(s, t, MAX_QPATH); data[i] = s; } // free file list FS_FreeList(list, num); // sort them male, female, alphabetical qsort(s_directory.data, s_directory.num - 1, sizeof(char**), dircmp_func); return true; } // list all valid player models. // call PlayerDirectoryList first. // model names is always allocated MAX_PLAYERMODELS static qboolean PlayerModelList(void) { char findname[MAX_QPATH]; char** list = NULL; char** data = NULL; int num = 0; int mdl = 0; qboolean result = true; // malloc models data = (char**)calloc(MAX_PLAYERMODELS, sizeof(char*)); YQ2_COM_CHECK_OOM(data, "calloc()", MAX_PLAYERMODELS * sizeof(char*)) s_modelname.data = data; s_modelname.num = 0; // verify the existence of at least one pcx skin for (int i = 0; i < s_directory.num; ++i) { char* s = NULL; char* t = NULL; int l; if (s_directory.data[i] == 0) { continue; } strcpy(findname, s_directory.data[i]); strcat(findname, "/*.pcx"); // get a list of pcx files if ((list = FS_ListFiles2(findname, &num, 0, 0)) == NULL) { continue; } // contains triangle .md2 model s = s_directory.data[i]; if (ContainsFile(s, "tris.md2") == false) { continue; // invalid player model } // count valid skins, which consist of a skin with a matching "_i" icon s_skinnames[mdl].num = 0; for (int j = 0; j < num; j++) { // last element of FS_FileList maybe null if (list[j] == 0) { break; } if (!strstr(list[j], "_i.pcx")) { if (IconOfSkinExists(list[j], list, num - 1)) { s_skinnames[mdl].num++; } } } if (s_skinnames[mdl].num == 0) { FS_FreeList(list, num); continue; } // malloc skinnames data = (char**)malloc((s_skinnames[mdl].num + 1) * sizeof(char*)); YQ2_COM_CHECK_OOM(data, "malloc()", (s_skinnames[mdl].num + 1) * sizeof(char*)) memset(data, 0, (s_skinnames[mdl].num + 1) * sizeof(char*)); s_skinnames[mdl].data = data; s_skinnames[mdl].num = 0; // duplicate strings for (int k = 0; k < num; ++k) { // last element of FS_FileList maybe null if (list[k] == 0) { break; } if (!strstr(list[k], "_i.pcx")) { if (IconOfSkinExists(list[k], list, num - 1)) { ReplaceCharacters(list[k], '\\', '/'); t = strrchr(list[k], '/'); l = strlen(t) + 1; s = (char*)malloc(l); YQ2_COM_CHECK_OOM(s, "malloc()", l * sizeof(char)) StripExtension(t); Q_strlcpy(s, t + 1, l); data[s_skinnames[mdl].num++] = s; } } } // sort skin names alphabetically qsort(s_skinnames[mdl].data, s_skinnames[mdl].num, sizeof(char**), Q_sort_stricmp); // at this point we have a valid player model t = strrchr(s_directory.data[i], '/'); l = strlen(t) + 1; s = (char*)malloc(l); YQ2_COM_CHECK_OOM(s, "malloc()", l * sizeof(char)) Q_strlcpy(s, t + 1, l); s_modelname.data[s_modelname.num++] = s; mdl = s_modelname.num; // free file list FS_FreeList(list, num); } if (s_modelname.num == 0) { PlayerModelFree(); result = false; } return result; } static qboolean PlayerConfig_ScanDirectories(void) { qboolean result = false; // directory names result = PlayerDirectoryList(); if (result == false) { Com_Printf("No valid player directories found.\n"); } // valid models result = PlayerModelList(); if (result == false) { Com_Printf("No valid player models found.\n"); } return result; } void ListModels_f(void) { PlayerConfig_ScanDirectories(); for (size_t i = 0; i < s_modelname.num; i++) { for (size_t j = 0; j < s_skinnames[i].num; j++) { Com_Printf("%s/%s\n", s_modelname.data[i], s_skinnames[i].data[j]); } } PlayerModelFree(); } static qboolean PlayerConfig_MenuInit(void) { extern cvar_t *name; extern cvar_t *skin; cvar_t *hand = Cvar_Get( "hand", "0", CVAR_USERINFO | CVAR_ARCHIVE ); static const char *handedness[] = { "right", "left", "center", 0 }; char mdlname[MAX_QPATH]; char imgname[MAX_QPATH]; int mdlindex = 0; int imgindex = 0; int i = 0; float scale = SCR_GetMenuScale(); if (PlayerConfig_ScanDirectories() == false) { return false; } Q_strlcpy(mdlname, skin->string, sizeof(mdlname)); ReplaceCharacters(mdlname, '\\', '/' ); if (strchr(mdlname, '/')) { Q_strlcpy(imgname, strchr(mdlname, '/') + 1, sizeof(imgname)); *strchr(mdlname, '/') = 0; } else { strcpy(mdlname, "male\0"); strcpy(imgname, "grunt\0"); } for (i = 0; i < s_modelname.num; i++) { if (Q_stricmp(s_modelname.data[i], mdlname) == 0) { mdlindex = i; break; } } for (i = 0; i < s_skinnames[mdlindex].num; i++) { char* names = s_skinnames[mdlindex].data[i]; if (Q_stricmp(names, imgname) == 0) { imgindex = i; break; } } if (hand->value < 0 || hand->value > 2) { Cvar_SetValue("hand", 0); } 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_icon_bitmap.generic.type = MTYPE_BITMAP; s_player_icon_bitmap.generic.flags = QMF_INACTIVE; s_player_icon_bitmap.generic.x = ((viddef.width / scale - 95) / 2) - 87; s_player_icon_bitmap.generic.y = ((viddef.height / (2 * scale))) - 72; s_player_icon_bitmap.generic.name = 0; s_player_icon_bitmap.generic.callback = 0; s_player_icon_bitmap.focuspic = 0; 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 = mdlindex; s_player_model_box.itemnames = (const char**)s_modelname.data; 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 = imgindex; s_player_skin_box.itemnames = (const char **)s_skinnames[mdlindex].data; 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_icon_bitmap); 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); /* * Model animation */ static void PlayerConfig_AnimateModel(entity_t *entity, int count, int curTime) { const cvar_t *cl_start_frame, *cl_end_frame; int startFrame, endFrame; cl_start_frame = Cvar_Get("cl_model_preview_start", "84", CVAR_ARCHIVE); cl_end_frame = Cvar_Get("cl_model_preview_end", "94", CVAR_ARCHIVE); startFrame = cl_start_frame->value; endFrame = cl_end_frame->value; if (startFrame >= 0 && endFrame > startFrame) { int i; for (i = 0; i < count; i ++) { /* salute male 84..94 frame */ entity[i].frame = (curTime / 100) % (endFrame - startFrame) + startFrame; } } } static void PlayerConfig_MenuDraw(void) { refdef_t refdef; 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; // could remove this, there should be a valid set of models if ((s_player_model_box.curvalue >= 0 && s_player_model_box.curvalue < s_modelname.num) && (s_player_skin_box.curvalue >= 0 && s_player_skin_box.curvalue < s_skinnames[s_player_model_box.curvalue].num)) { entity_t entities[2]; char scratch[MAX_QPATH]; char* mdlname = s_modelname.data[s_player_model_box.curvalue]; char* imgname = s_skinnames[s_player_model_box.curvalue].data[s_player_skin_box.curvalue]; int i, curTime; memset(&entities, 0, sizeof(entities)); Com_sprintf(scratch, sizeof(scratch), "players/%s/tris.md2", mdlname); entities[0].model = R_RegisterModel(scratch); Com_sprintf(scratch, sizeof(scratch), "players/%s/%s.pcx", mdlname, imgname); entities[0].skin = R_RegisterSkin(scratch); curTime = Sys_Milliseconds(); /* multiplayer weapons loaded */ if (num_cl_weaponmodels) { int weapon_id; /* change weapon every 3 rounds */ weapon_id = curTime / 9000; weapon_id = weapon_id % num_cl_weaponmodels; /* show weapon also */ Com_sprintf(scratch, sizeof(scratch), "players/%s/%s", mdlname, cl_weaponmodels[weapon_id]); entities[1].model = R_RegisterModel(scratch); } /* no such weapon model */ if (!entities[1].model) { /* show weapon also */ Com_sprintf(scratch, sizeof(scratch), "players/%s/weapon.md2", mdlname); entities[1].model = R_RegisterModel(scratch); } curTime = curTime % 3000; for (i = 0; i < 2; i++) { entities[i].flags = RF_FULLBRIGHT; entities[i].origin[0] = 80; entities[i].origin[1] = 0; entities[i].origin[2] = 0; VectorCopy(entities[i].origin, entities[i].oldorigin); entities[i].frame = 0; entities[i].oldframe = 0; entities[i].backlerp = 0.0; // one full turn is 3s = 3000ms => 3000/360 deg per millisecond entities[i].angles[1] = (float)curTime/(3000.0f/360.0f); } PlayerConfig_AnimateModel(entities, 2, curTime); refdef.areabits = 0; refdef.num_entities = (entities[1].model) ? 2 : 1; refdef.entities = entities; refdef.lightstyles = 0; refdef.rdflags = RDF_NOWORLDMODEL; Com_sprintf(scratch, sizeof(scratch), "/players/%s/%s_i.pcx", mdlname, imgname); // icon bitmap to draw s_player_icon_bitmap.generic.name = scratch; 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); } } static const char * PlayerConfig_MenuKey(int key) { key = Key_GetMenuKey(key); if (key == K_ESCAPE) { char skin[MAX_QPATH]; char* name = NULL; char* mdl = NULL; char* img = NULL; name = s_player_name_field.buffer; mdl = s_modelname.data[s_player_model_box.curvalue]; img = s_skinnames[s_player_model_box.curvalue].data[s_player_skin_box.curvalue]; Com_sprintf(skin, MAX_QPATH, "%s/%s", mdl, img); // set and / Cvar_Set("name", name); Cvar_Set("skin", skin); PlayerModelFree(); // free player skins, models and directories } 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); s_player_config_menu.draw = PlayerConfig_MenuDraw; s_player_config_menu.key = PlayerConfig_MenuKey; M_PushMenu(&s_player_config_menu); } /* * QUIT MENU */ menuframework_s s_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) { s_quit_menu.draw = M_Quit_Draw; s_quit_menu.key = M_Quit_Key; M_PushMenu(&s_quit_menu); } void M_Init(void) { char cursorname[MAX_QPATH]; int w = 0; int h = 0; Cmd_AddCommand("playermodels", ListModels_f); 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_mods", M_Menu_Mods_f); Cmd_AddCommand("menu_multiplayer", M_Menu_Multiplayer_f); Cmd_AddCommand("menu_multiplayer_keys", M_Menu_Multiplayer_Keys_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_joy", M_Menu_Joy_f); Cmd_AddCommand("menu_gyro", M_Menu_Gyro_f); Cmd_AddCommand("menu_buttons", M_Menu_ControllerButtons_f); Cmd_AddCommand("menu_altbuttons", M_Menu_ControllerAltButtons_f); Cmd_AddCommand("menu_quit", M_Menu_Quit_f); /* initialize the server address book cvars (adr0, adr1, ...) * so the entries are not lost if you don't open the address book */ for (int index = 0; index < NUM_ADDRESSBOOK_ENTRIES; index++) { char buffer[20]; Com_sprintf(buffer, sizeof(buffer), "adr%d", index); Cvar_Get(buffer, "", CVAR_ARCHIVE); } // cache the cursor frames for (int i = 0; i < NUM_CURSOR_FRAMES; i++) { Com_sprintf(cursorname, sizeof(cursorname), "m_cursor%d", i); Draw_FindPic(cursorname); Draw_GetPicSize(&w, &h, cursorname); if (w > m_cursor_width) { m_cursor_width = w; } } } 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(); } if (m_active.draw) { m_active.draw(); } /* 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) { if (m_active.key) { const char *s; if ((s = m_active.key(key)) != 0) { S_StartLocalSound((char *)s); } } } yquake2-QUAKE2_8_40/src/client/menu/qmenu.c000066400000000000000000000333761465112212000204270ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_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); extern viddef_t viddef; #define VID_WIDTH viddef.width #define VID_HEIGHT viddef.height float ClampCvar(float min, float max, float value) { if (value < min) { return min; } if (value > max) { return max; } return value; } /* ================= Bitmap_Draw ================= */ void Bitmap_Draw(menubitmap_s * item) { float scale = SCR_GetMenuScale(); int x = 0; int y = 0; x = item->generic.x; y = item->generic.y; if (((item->generic.flags & QMF_HIGHLIGHT_IF_FOCUS) && (Menu_ItemAtCursor(item->generic.parent) == item))) { Draw_PicScaled(x * scale, y * scale, item->focuspic, scale); } else if (item->generic.name) { Draw_PicScaled(x * scale, y * scale, ( char * )item->generic.name, scale); } } void Action_Draw(menuaction_s *a) { float scale = SCR_GetMenuScale(); int x = 0; int y = 0; x = a->generic.parent->x + a->generic.x; y = a->generic.parent->y + a->generic.y; if (a->generic.flags & QMF_LEFT_JUSTIFY) { if (a->generic.flags & QMF_GRAYED) { Menu_DrawStringDark(x + (LCOLUMN_OFFSET * scale), y, a->generic.name); } else { Menu_DrawString(x + (LCOLUMN_OFFSET * scale), y, a->generic.name); } } else { if (a->generic.flags & QMF_GRAYED) { Menu_DrawStringR2LDark(x + (LCOLUMN_OFFSET * scale), y, a->generic.name); } else { Menu_DrawStringR2L(x + (LCOLUMN_OFFSET * scale), 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(); int x = 0; int y = 0; x = f->generic.parent->x + f->generic.x; y = f->generic.parent->y + f->generic.y; if (f->generic.name) { Menu_DrawStringR2LDark(x + LCOLUMN_OFFSET * scale, 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(x + (16 * scale), (y - 4) * scale, 18, scale); Draw_CharScaled(x + 16 * scale, (y + 4) * scale, 24, scale); Draw_CharScaled(x + (24 * scale) + (f->visible_length * (8 * scale)), (y - 4) * scale, 20, scale); Draw_CharScaled(x + (24 * scale) + (f->visible_length * (8 * scale)), (y + 4) * scale, 26, scale); for (i = 0; i < f->visible_length; i++) { Draw_CharScaled(x + (24 * scale) + (i * 8 * scale), (y - 4) * scale, 19, scale); Draw_CharScaled(x + (24 * scale) + (i * 8 * scale), (y + 4) * scale, 25, scale); } Menu_DrawString(x + (24 * scale), 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( x + (24 * scale) + (offset * (8 * scale)), y * scale, 11, scale); } else { Draw_CharScaled( x + (24 * scale) + (offset * (8 * scale)), y * scale, ' ', scale); } } } extern int keydown[]; qboolean Field_Key(menufield_s *f, int key) { 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 < MAXMENUITEMS) { menu->items[menu->nitems] = item; ((menucommon_s *)menu->items[menu->nitems])->parent = menu; menu->nitems++; } } /* * 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 = NULL; /* 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 && (citem->flags & QMF_INACTIVE) != QMF_INACTIVE) { return; } } } /* it's not in a valid spot, so crawl in the direction indicated until we find a valid spot */ int cursor = m->nitems; while (cursor-- > 0) { citem = Menu_ItemAtCursor(m); if (citem) { if (citem->type != MTYPE_SEPARATOR && (citem->flags & QMF_INACTIVE) != QMF_INACTIVE) { break; } } m->cursor += dir; if (m->cursor >= m->nitems) { m->cursor = 0; } 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_BITMAP: Bitmap_Draw(( menubitmap_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) && item->type != MTYPE_BITMAP) { 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 { // FIXME:: menu->x + (item->x / scale + 24 + item->cursor_offset) * scale 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); int 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->callback) { item->callback(item); return true; } 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; } } } void MenuList_Draw(menulist_s *l) { const char **n; float scale = SCR_GetMenuScale(); int x = 0; int y = 0; x = l->generic.parent->x + l->generic.x; y = l->generic.parent->y + l->generic.y; Menu_DrawStringR2LDark(x + (LCOLUMN_OFFSET * scale), y, l->generic.name); n = l->itemnames; Draw_Fill(x - 112, y + l->curvalue * 10 * scale + 10 * scale, 128, 10, 16); while (*n) { Menu_DrawStringR2LDark(x + (LCOLUMN_OFFSET * scale), y, *n); n++; y += 10 * scale; } } void Separator_Draw(menuseparator_s *s) { int x = 0; int y = 0; x = s->generic.parent->x + s->generic.x; y = s->generic.parent->y + s->generic.y; if (s->generic.name) { Menu_DrawStringR2LDark(x, y, s->generic.name); } } void Slider_DoSlide(menuslider_s *s, int dir) { const float step = (s->slidestep)? s->slidestep : 0.1f; float value = Cvar_VariableValue(s->cvar); float sign = 1.0f; if (s->abs && value < 0) // absolute value treatment { value = -value; sign = -1.0f; } value += dir * step; Cvar_SetValue(s->cvar, ClampCvar(s->minvalue, s->maxvalue, value) * sign); if (s->generic.callback) { s->generic.callback(s); } } #define SLIDER_RANGE 10 void Slider_Draw(menuslider_s *s) { const float scale = SCR_GetMenuScale(); const int x = s->generic.parent->x + s->generic.x; const int y = s->generic.parent->y + s->generic.y; const int x_rcol = x + (RCOLUMN_OFFSET * scale); int i; char buffer[5]; float value = Cvar_VariableValue(s->cvar); if (s->abs && value < 0) // absolute value { value = -value; } const float range = (ClampCvar(s->minvalue, s->maxvalue, value) - s->minvalue) / (s->maxvalue - s->minvalue); Menu_DrawStringR2LDark(x + (LCOLUMN_OFFSET * scale), y, s->generic.name); Draw_CharScaled(x_rcol, y * scale, 128, scale); for (i = 0; i < SLIDER_RANGE * scale; i++) { Draw_CharScaled(x_rcol + (i * 8) + 8, y * scale, 129, scale); } Draw_CharScaled(x_rcol + (i * 8) + 8, y * scale, 130, scale); Draw_CharScaled(x_rcol + (int)((SLIDER_RANGE * scale - 1) * 8 * range) + 8, y * scale, 131, scale); snprintf(buffer, 5, (s->printformat)? s->printformat : "%.1f", value); Menu_DrawString(x_rcol + ((SLIDER_RANGE + 2) * scale * 8), y, buffer); } void SpinControl_DoSlide(menulist_s *s, int dir) { s->curvalue += dir; if (s->curvalue < 0) { s->curvalue = 0; return; } else if (s->itemnames[s->curvalue] == 0) { s->curvalue--; return; } if (s->generic.callback) { s->generic.callback(s); } } void SpinControl_Draw(menulist_s *s) { char buffer[100]; float scale = SCR_GetMenuScale(); int x = 0; int y = 0; x = s->generic.parent->x + s->generic.x; y = s->generic.parent->y + s->generic.y; if (s->generic.name) { Menu_DrawStringR2LDark(x + (LCOLUMN_OFFSET * scale), y, s->generic.name); } if (!strchr(s->itemnames[s->curvalue], '\n')) { Menu_DrawString(x + (RCOLUMN_OFFSET * scale), y, s->itemnames[s->curvalue]); } else { strcpy(buffer, s->itemnames[s->curvalue]); *strchr(buffer, '\n') = 0; Menu_DrawString(x + (RCOLUMN_OFFSET * scale), y, buffer); strcpy(buffer, strchr(s->itemnames[s->curvalue], '\n') + 1); Menu_DrawString(x + (RCOLUMN_OFFSET * scale), y + 10, buffer); } } yquake2-QUAKE2_8_40/src/client/menu/videomenu.c000066400000000000000000000537501465112212000212730ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 *r_mode; static cvar_t *vid_displayindex; static cvar_t *r_hudscale; static cvar_t *r_consolescale; static cvar_t *r_menuscale; static cvar_t *crosshair_scale; extern cvar_t *scr_viewsize; extern cvar_t *vid_fullscreen; extern cvar_t *vid_renderer; static cvar_t *r_vsync; static cvar_t *gl_anisotropic; static cvar_t *gl_msaa_samples; static cvar_t *gl1_colorlight; static cvar_t *gl3_colorlight; static cvar_t *vk_dynamic; static menuframework_s s_opengl_menu; static menulist_s s_renderer_list; static menulist_s s_mode_list; static menulist_s s_display_list; static menulist_s s_uiscale_list; static menuslider_s s_brightness_slider; static menuslider_s s_fov_slider; static menuslider_s s_gl1_intensity_slider; static menuslider_s s_gl3_intensity_slider; static menuslider_s s_vk_intensity_slider; static menuslider_s s_gl1_overbrightbits_slider; static menuslider_s s_gl3_overbrightbits_slider; static menuslider_s s_vk_overbrightbits_slider; static menulist_s s_gl1_colorlight_list; static menulist_s s_gl3_colorlight_list; static menulist_s s_vk_dynamic_list; 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 menulist_s s_filter_list; static menuaction_s s_defaults_action; static menuaction_s s_apply_action; // -------- // gl1, gl3, gles3, gl4, vk, soft #define MAXRENDERERS 6 typedef struct { const char *boxstr; const char *cvarstr; } renderer; renderer rendererlist[MAXRENDERERS]; int numrenderer; static void Renderer_FillRenderdef(void) { numrenderer = -1; if (VID_HasRenderer("gl1")) { numrenderer++; rendererlist[numrenderer].boxstr = "[OpenGL 1.4]"; rendererlist[numrenderer].cvarstr = "gl1"; } if (VID_HasRenderer("gl3")) { numrenderer++; rendererlist[numrenderer].boxstr = "[OpenGL 3.2]"; rendererlist[numrenderer].cvarstr = "gl3"; } if (VID_HasRenderer("gles3")) { numrenderer++; rendererlist[numrenderer].boxstr = "[OpenGL ES3]"; rendererlist[numrenderer].cvarstr = "gles3"; } if (VID_HasRenderer("gl4")) { numrenderer++; rendererlist[numrenderer].boxstr = "[OpenGL 4.6]"; rendererlist[numrenderer].cvarstr = "gl4"; } if (VID_HasRenderer("vk")) { numrenderer++; rendererlist[numrenderer].boxstr = "[Vulkan ]"; rendererlist[numrenderer].cvarstr = "vk"; } if (VID_HasRenderer("soft")) { numrenderer++; rendererlist[numrenderer].boxstr = "[Software ]"; rendererlist[numrenderer].cvarstr = "soft"; } // The custom renderer. Must be known to the menu, // but nothing more. The display string is hard // coded below, the cvar is unknown. numrenderer++; } static int Renderer_GetRenderer(void) { for (int i = 0; i < numrenderer; i++) { if (strcmp(vid_renderer->string, rendererlist[i].cvarstr) == 0) { return i; } } // Unknown renderer. return numrenderer; } // -------- 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 ResetDefaults(void *unused) { VID_MenuInit(); } #define CUSTOM_MODE_NAME "[Custom ]" #define AUTO_MODE_NAME "[Auto ]" static void ApplyFilter(void* unused) { if (Q_stricmp(vid_renderer->string, "gl3") == 0 || Q_stricmp(vid_renderer->string, "gles3") == 0 || Q_stricmp(vid_renderer->string, "gl1") == 0) { if (s_filter_list.curvalue == 0) { Cvar_Set("gl_texturemode", "GL_NEAREST"); } else if (s_filter_list.curvalue == 1) { Cvar_Set("gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST"); } else if (s_filter_list.curvalue == 2) { Cvar_Set("gl_texturemode", "GL_LINEAR_MIPMAP_LINEAR"); } } if (Q_stricmp(vid_renderer->string, "soft") == 0) { if (s_filter_list.curvalue == 0) { Cvar_Set("sw_texture_filtering", "0"); } else if (s_filter_list.curvalue == 1) { Cvar_Set("sw_texture_filtering", "1"); } } } static void ApplyChanges(void *unused) { qboolean restart = false; /* Renderer */ if (s_renderer_list.curvalue != Renderer_GetRenderer()) { // The custom renderer (the last known renderer) cannot be // set, because the menu doesn't know it's cvar value. TODO: // Hack something that it cannot be selected. if (s_renderer_list.curvalue != numrenderer) { Cvar_Set("vid_renderer", (char *)rendererlist[s_renderer_list.curvalue].cvarstr); restart = true; } } /* auto mode */ if (!strcmp(s_mode_list.itemnames[s_mode_list.curvalue], AUTO_MODE_NAME)) { Cvar_SetValue("r_mode", -2); restart = true; } else if (!strcmp(s_mode_list.itemnames[s_mode_list.curvalue], CUSTOM_MODE_NAME)) { Cvar_SetValue("r_mode", -1); restart = true; } else { Cvar_SetValue("r_mode", s_mode_list.curvalue); restart = true; } if (s_display_list.curvalue != GLimp_GetWindowDisplayIndex() ) { Cvar_SetValue( "vid_displayindex", s_display_list.curvalue ); restart = true; } /* UI scaling */ if (s_uiscale_list.curvalue == 0) { Cvar_SetValue("r_hudscale", -1); } else if (s_uiscale_list.curvalue < GetCustomValue(&s_uiscale_list)) { Cvar_SetValue("r_hudscale", s_uiscale_list.curvalue); } if (s_uiscale_list.curvalue != GetCustomValue(&s_uiscale_list)) { Cvar_SetValue("r_consolescale", r_hudscale->value); Cvar_SetValue("r_menuscale", r_hudscale->value); Cvar_SetValue("crosshair_scale", r_hudscale->value); } /* Restarts automatically */ if (vid_fullscreen->value != s_fs_box.curvalue) { Cvar_SetValue("vid_fullscreen", s_fs_box.curvalue); restart = true; } if (s_fs_box.curvalue == 2) { Cvar_SetValue("r_mode", -2.0f); } /* vertical sync */ if (r_vsync->value != s_vsync_list.curvalue) { Cvar_SetValue("r_vsync", s_vsync_list.curvalue); restart = true; } if (gl3_colorlight && gl3_colorlight->value != s_gl3_colorlight_list.curvalue) { Cvar_SetValue("gl3_colorlight", s_gl3_colorlight_list.curvalue); } if (gl1_colorlight && gl1_colorlight->value != s_gl1_colorlight_list.curvalue) { Cvar_SetValue("gl1_colorlight", s_gl1_colorlight_list.curvalue); } /* anisotropic filtering */ if (s_af_list.curvalue == 0) { if (gl_anisotropic->value != 0) { Cvar_SetValue("r_anisotropic", 0); restart = true; } } else { if (gl_anisotropic->value != pow(2, s_af_list.curvalue)) { Cvar_SetValue("r_anisotropic", pow(2, s_af_list.curvalue)); restart = true; } } /* multisample anti-aliasing */ if (s_msaa_list.curvalue == 0) { if (gl_msaa_samples->value != 0) { Cvar_SetValue("r_msaa_samples", 0); restart = true; } } else { if (gl_msaa_samples->value != pow(2, s_msaa_list.curvalue)) { Cvar_SetValue("r_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 = 30; // Renderer selection box. // MAXRENDERERS + Custom + NULL. static const char *renderers[MAXRENDERERS + 2] = { NULL }; Renderer_FillRenderdef(); for (int i = 0; i < numrenderer; i++) { renderers[i] = rendererlist[i].boxstr; } renderers[numrenderer] = CUSTOM_MODE_NAME; // must be kept in sync with vid_modes[] in vid.c 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 ]", "[2560 1080 ]", "[2560 1440 ]", "[2560 1600 ]", "[3440 1440 ]", "[3840 1600 ]", "[3840 2160 ]", "[4096 2160 ]", "[5120 2880 ]", AUTO_MODE_NAME, CUSTOM_MODE_NAME, 0 }; static const char *uiscale_names[] = { "auto", "1x", "2x", "3x", "4x", "5x", "6x", "custom", 0 }; static const char *onoff_names[] = { "off", "on", 0 }; static const char *yesno_names[] = { "no", "yes", 0 }; static const char *fullscreen_names[] = { "no", "native fullscreen", "fullscreen window", 0 }; static const char *pow2_names[] = { "off", "2x", "4x", "8x", "16x", 0 }; static const char *filter_names[] = { "pixelated", "standard", "trilinear", "custom", 0 }; if (!r_mode) { r_mode = Cvar_Get("r_mode", "4", 0); } if (!vid_displayindex) { vid_displayindex = Cvar_Get("vid_displayindex", "0", CVAR_ARCHIVE); } if (!r_hudscale) { r_hudscale = Cvar_Get("r_hudscale", "-1", CVAR_ARCHIVE); } if (!r_consolescale) { r_consolescale = Cvar_Get("r_consolescale", "-1", CVAR_ARCHIVE); } if (!r_menuscale) { r_menuscale = Cvar_Get("r_menuscale", "-1", CVAR_ARCHIVE); } if (!crosshair_scale) { crosshair_scale = Cvar_Get("crosshair_scale", "-1", CVAR_ARCHIVE); } if (!vid_renderer) { vid_renderer = Cvar_Get("vid_renderer", "gl1", CVAR_ARCHIVE); } if (!r_vsync) { r_vsync = Cvar_Get("r_vsync", "1", CVAR_ARCHIVE); } if (!gl_anisotropic) { gl_anisotropic = Cvar_Get("r_anisotropic", "0", CVAR_ARCHIVE); } if (!gl_msaa_samples) { gl_msaa_samples = Cvar_Get("r_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; s_renderer_list.itemnames = renderers; s_renderer_list.curvalue = Renderer_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 (r_mode->value >= 0) { s_mode_list.curvalue = r_mode->value; } else if (r_mode->value == -2) { // 'auto' is before 'custom' s_mode_list.curvalue = GetCustomValue(&s_mode_list) - 1; } else { // 'custom' s_mode_list.curvalue = GetCustomValue(&s_mode_list); } if (GLimp_GetNumVideoDisplays() > 1) { s_display_list.generic.type = MTYPE_SPINCONTROL; s_display_list.generic.name = "display index"; s_display_list.generic.x = 0; s_display_list.generic.y = (y += 10); s_display_list.itemnames = GLimp_GetDisplayIndices(); s_display_list.curvalue = GLimp_GetWindowDisplayIndex(); } s_brightness_slider.generic.type = MTYPE_SLIDER; s_brightness_slider.generic.name = "brightness"; s_brightness_slider.generic.x = 0; s_brightness_slider.generic.y = (y += 10); s_brightness_slider.cvar = "vid_gamma"; s_brightness_slider.minvalue = 0.1f; s_brightness_slider.maxvalue = 2.0f; s_fov_slider.generic.type = MTYPE_SLIDER; s_fov_slider.generic.name = "field of view"; s_fov_slider.generic.x = 0; s_fov_slider.generic.y = (y += 10); s_fov_slider.cvar = "fov"; s_fov_slider.minvalue = 60; s_fov_slider.maxvalue = 120; s_fov_slider.slidestep = 1; s_fov_slider.printformat = "%.0f"; if (strcmp(vid_renderer->string, "gl3") == 0 || strcmp(vid_renderer->string, "gles3") == 0) { gl1_colorlight = NULL; s_gl3_intensity_slider.generic.type = MTYPE_SLIDER; s_gl3_intensity_slider.generic.name = "color intensity"; s_gl3_intensity_slider.generic.x = 0; s_gl3_intensity_slider.generic.y = (y += 10); s_gl3_intensity_slider.cvar = "gl3_intensity"; s_gl3_intensity_slider.minvalue = 0.1f; s_gl3_intensity_slider.maxvalue = 5.0f; s_gl3_overbrightbits_slider.generic.type = MTYPE_SLIDER; s_gl3_overbrightbits_slider.generic.name = "overbrights"; s_gl3_overbrightbits_slider.generic.x = 0; s_gl3_overbrightbits_slider.generic.y = (y += 10); s_gl3_overbrightbits_slider.cvar = "gl3_overbrightbits"; s_gl3_overbrightbits_slider.minvalue = 0.1f; s_gl3_overbrightbits_slider.maxvalue = 5.0f; gl3_colorlight = Cvar_Get("gl3_colorlight", "1", CVAR_ARCHIVE); s_gl3_colorlight_list.generic.type = MTYPE_SPINCONTROL; s_gl3_colorlight_list.generic.name = "color light"; s_gl3_colorlight_list.generic.x = 0; s_gl3_colorlight_list.generic.y = (y += 10); s_gl3_colorlight_list.itemnames = yesno_names; s_gl3_colorlight_list.curvalue = (gl3_colorlight->value != 0); } else if (strcmp(vid_renderer->string, "vk") == 0) { s_vk_intensity_slider.generic.type = MTYPE_SLIDER; s_vk_intensity_slider.generic.name = "color intensity"; s_vk_intensity_slider.generic.x = 0; s_vk_intensity_slider.generic.y = (y += 10); s_vk_intensity_slider.cvar = "vk_intensity"; s_vk_intensity_slider.minvalue = 0; s_vk_intensity_slider.maxvalue = 5; s_vk_intensity_slider.slidestep = 1; s_vk_intensity_slider.printformat = "%.0f"; s_vk_overbrightbits_slider.generic.type = MTYPE_SLIDER; s_vk_overbrightbits_slider.generic.name = "overbrights"; s_vk_overbrightbits_slider.generic.x = 0; s_vk_overbrightbits_slider.generic.y = (y += 10); s_vk_overbrightbits_slider.cvar = "vk_overbrightbits"; s_vk_overbrightbits_slider.minvalue = 0.1f; s_vk_overbrightbits_slider.maxvalue = 5.0f; vk_dynamic = Cvar_Get("vk_dynamic", "1", CVAR_ARCHIVE); s_vk_dynamic_list.generic.type = MTYPE_SPINCONTROL; s_vk_dynamic_list.generic.name = "dynamic light"; s_vk_dynamic_list.generic.x = 0; s_vk_dynamic_list.generic.y = (y += 10); s_vk_dynamic_list.itemnames = yesno_names; s_vk_dynamic_list.curvalue = (vk_dynamic->value != 0); } else { gl3_colorlight = NULL; s_gl1_intensity_slider.generic.type = MTYPE_SLIDER; s_gl1_intensity_slider.generic.name = "color intensity"; s_gl1_intensity_slider.generic.x = 0; s_gl1_intensity_slider.generic.y = (y += 10); s_gl1_intensity_slider.cvar = "gl1_intensity"; s_gl1_intensity_slider.minvalue = 1.0f; s_gl1_intensity_slider.maxvalue = 10.0f; s_gl1_overbrightbits_slider.generic.type = MTYPE_SLIDER; s_gl1_overbrightbits_slider.generic.name = "overbrights"; s_gl1_overbrightbits_slider.generic.x = 0; s_gl1_overbrightbits_slider.generic.y = (y += 10); s_gl1_overbrightbits_slider.cvar = "gl1_overbrightbits"; s_gl1_overbrightbits_slider.minvalue = 0; s_gl1_overbrightbits_slider.maxvalue = 2; s_gl1_overbrightbits_slider.slidestep = 1; s_gl1_overbrightbits_slider.printformat = "%.0f"; gl1_colorlight = Cvar_Get("gl1_colorlight", "1", CVAR_ARCHIVE); s_gl1_colorlight_list.generic.type = MTYPE_SPINCONTROL; s_gl1_colorlight_list.generic.name = "color light"; s_gl1_colorlight_list.generic.x = 0; s_gl1_colorlight_list.generic.y = (y += 10); s_gl1_colorlight_list.itemnames = yesno_names; s_gl1_colorlight_list.curvalue = (gl1_colorlight->value != 0); } 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 (r_hudscale->value != r_consolescale->value || r_hudscale->value != r_menuscale->value || r_hudscale->value != crosshair_scale->value) { s_uiscale_list.curvalue = GetCustomValue(&s_uiscale_list); } else if (r_hudscale->value < 0) { s_uiscale_list.curvalue = 0; } else if (r_hudscale->value > 0 && r_hudscale->value < GetCustomValue(&s_uiscale_list) && r_hudscale->value == (int)r_hudscale->value) { s_uiscale_list.curvalue = r_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 = (r_vsync->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.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_filter_list.generic.type = MTYPE_SPINCONTROL; s_filter_list.generic.name = "texture filter"; s_filter_list.curvalue = 0; s_filter_list.generic.callback = ApplyFilter; const char* filter = NULL; int mode = 0; if (Q_stricmp(vid_renderer->string, "gl3") == 0 || Q_stricmp(vid_renderer->string, "gles3") == 0 || Q_stricmp(vid_renderer->string, "gl1") == 0) { s_filter_list.generic.x = 0; s_filter_list.generic.y = (y += 10); s_filter_list.itemnames = filter_names; filter = Cvar_VariableString("gl_texturemode"); mode = 3; if (Q_stricmp(filter, "GL_NEAREST") == 0) { mode = 0; } else if (Q_stricmp(filter, "GL_LINEAR_MIPMAP_NEAREST") == 0) { mode = 1; } else if (Q_stricmp(filter, "GL_LINEAR_MIPMAP_LINEAR") == 0) { mode = 2; } } else if (Q_stricmp(vid_renderer->string, "soft") == 0) { s_filter_list.generic.x = 0; s_filter_list.generic.y = (y += 10); s_filter_list.itemnames = onoff_names; filter = Cvar_VariableString("sw_texture_filtering"); mode = 0; if (Q_stricmp(filter, "1") == 0) { mode = 1; } } s_filter_list.curvalue = mode; 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); // only show this option if we have multiple displays if (GLimp_GetNumVideoDisplays() > 1) { Menu_AddItem(&s_opengl_menu, (void *)&s_display_list); } Menu_AddItem(&s_opengl_menu, (void *)&s_brightness_slider); Menu_AddItem(&s_opengl_menu, (void *)&s_fov_slider); if (strcmp(vid_renderer->string, "gl3") == 0 || strcmp(vid_renderer->string, "gles3") == 0) { Menu_AddItem(&s_opengl_menu, (void *)&s_gl3_intensity_slider); Menu_AddItem(&s_opengl_menu, (void *)&s_gl3_overbrightbits_slider); Menu_AddItem(&s_opengl_menu, (void *)&s_gl3_colorlight_list); } else if (strcmp(vid_renderer->string, "vk") == 0) { Menu_AddItem(&s_opengl_menu, (void *)&s_vk_intensity_slider); Menu_AddItem(&s_opengl_menu, (void *)&s_vk_overbrightbits_slider); Menu_AddItem(&s_opengl_menu, (void *)&s_vk_dynamic_list); } else if (strcmp(vid_renderer->string, "gl1") == 0) { Menu_AddItem(&s_opengl_menu, (void *)&s_gl1_intensity_slider); Menu_AddItem(&s_opengl_menu, (void *)&s_gl1_overbrightbits_slider); Menu_AddItem(&s_opengl_menu, (void *)&s_gl1_colorlight_list); } 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); if (Q_stricmp(vid_renderer->string, "gl3") == 0 || Q_stricmp(vid_renderer->string, "gles3") == 0 || Q_stricmp(vid_renderer->string, "gl1") == 0 || Q_stricmp(vid_renderer->string, "soft") == 0) { Menu_AddItem(&s_opengl_menu, (void *)&s_filter_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; } /* * VIDEO MENU */ void M_Menu_Video_f(void) { VID_MenuInit(); s_opengl_menu.draw = VID_MenuDraw; s_opengl_menu.key = VID_MenuKey; M_PushMenu(&s_opengl_menu); } yquake2-QUAKE2_8_40/src/client/refresh/000077500000000000000000000000001465112212000176145ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/constants/000077500000000000000000000000001465112212000216305ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/constants/anorms.h000066400000000000000000000150451465112212000233050ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/client/refresh/constants/anormtab.h000066400000000000000000000631341465112212000236130ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/client/refresh/constants/warpsin.h000066400000000000000000000076501465112212000234740ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/client/refresh/files/000077500000000000000000000000001465112212000207165ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/files/models.c000066400000000000000000000417641465112212000223610ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 models file format * * ======================================================================= */ #include "../ref_shared.h" /* ================= Mod_LoadAliasModel/Mod_LoadMD2 ================= */ void * Mod_LoadMD2 (const char *mod_name, const void *buffer, int modfilelen, vec3_t mins, vec3_t maxs, struct image_s **skins, findimage_t find_image, modtype_t *type) { dmdl_t *pinmodel, *pheader; dtriangle_t *pintri, *pouttri; dstvert_t *pinst, *poutst; int *pincmd, *poutcmd; void *extradata; int version; int ofs_end; int i, j; pinmodel = (dmdl_t *)buffer; version = LittleLong (pinmodel->version); if (version != ALIAS_VERSION) { R_Printf(PRINT_ALL, "%s: %s has wrong version number (%i should be %i)", __func__, mod_name, version, ALIAS_VERSION); return NULL; } ofs_end = LittleLong(pinmodel->ofs_end); if (ofs_end < 0 || ofs_end > modfilelen) { R_Printf(PRINT_ALL, "%s: model %s file size(%d) too small, should be %d", __func__, mod_name, modfilelen, ofs_end); return NULL; } extradata = Hunk_Begin(modfilelen); pheader = Hunk_Alloc(ofs_end); // byte swap the header fields and sanity check for (i=0 ; iskinheight > MAX_LBM_HEIGHT) { R_Printf(PRINT_ALL, "%s: model %s has a skin taller than %d", __func__, mod_name, MAX_LBM_HEIGHT); return NULL; } if (pheader->num_xyz <= 0) { R_Printf(PRINT_ALL, "%s: model %s has no vertices", __func__, mod_name); return NULL; } if (pheader->num_xyz > MAX_VERTS) { R_Printf(PRINT_ALL, "%s: model %s has too many vertices", __func__, mod_name); return NULL; } if (pheader->num_st <= 0) { R_Printf(PRINT_ALL, "%s: model %s has no st vertices", __func__, mod_name); return NULL; } if (pheader->num_tris <= 0) { R_Printf(PRINT_ALL, "%s: model %s has no triangles", __func__, mod_name); return NULL; } if (pheader->num_frames <= 0) { R_Printf(PRINT_ALL, "%s: model %s has no frames", __func__, mod_name); return NULL; } if (pheader->num_skins > MAX_MD2SKINS) { R_Printf(PRINT_ALL, "%s has too many skins (%i > %i), " "extra sprites will be ignored\n", mod_name, pheader->num_skins, MAX_MD2SKINS); pheader->num_skins = MAX_MD2SKINS; } // // 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 ; inum_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 ; inum_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 ; inum_frames ; i++) { daliasframe_t *pinframe, *poutframe; 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)); } // // 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]); } if (poutcmd[pheader->num_glcmds-1] != 0) { R_Printf(PRINT_ALL, "%s: Entity %s has possible last element issues with %d verts.\n", __func__, mod_name, poutcmd[pheader->num_glcmds-1]); } // register all skins memcpy ((char *)pheader + pheader->ofs_skins, (char *)pinmodel + pheader->ofs_skins, pheader->num_skins*MAX_SKINNAME); for (i=0 ; inum_skins ; i++) { skins[i] = find_image((char *)pheader + pheader->ofs_skins + i*MAX_SKINNAME, it_skin); } *type = mod_alias; mins[0] = -32; mins[1] = -32; mins[2] = -32; maxs[0] = 32; maxs[1] = 32; maxs[2] = 32; return extradata; } /* ============================================================================== SPRITE MODELS ============================================================================== */ /* ================= Mod_LoadSP2 support for .sp2 sprites ================= */ void * Mod_LoadSP2 (const char *mod_name, const void *buffer, int modfilelen, struct image_s **skins, findimage_t find_image, modtype_t *type) { dsprite_t *sprin, *sprout; void *extradata; int i; sprin = (dsprite_t *)buffer; extradata = Hunk_Begin(modfilelen); sprout = Hunk_Alloc(modfilelen); sprout->ident = LittleLong(sprin->ident); sprout->version = LittleLong(sprin->version); sprout->numframes = LittleLong(sprin->numframes); if (sprout->version != SPRITE_VERSION) { R_Printf(PRINT_ALL, "%s has wrong version number (%i should be %i)", mod_name, sprout->version, SPRITE_VERSION); return NULL; } if (sprout->numframes > MAX_MD2SKINS) { R_Printf(PRINT_ALL, "%s has too many frames (%i > %i), " "extra frames will be ignored\n", mod_name, sprout->numframes, MAX_MD2SKINS); 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); skins[i] = find_image((char *)sprout->frames[i].name, it_sprite); } *type = mod_sprite; return extradata; } /* ================= Mod_ReLoad Reload images in SP2/MD2 (mark registration_sequence) ================= */ int Mod_ReLoadSkins(struct image_s **skins, findimage_t find_image, void *extradata, modtype_t type) { if (type == mod_sprite) { dsprite_t *sprout; int i; sprout = (dsprite_t *)extradata; for (i=0; i < sprout->numframes; i++) { skins[i] = find_image(sprout->frames[i].name, it_sprite); } return sprout->numframes; } else if (type == mod_alias) { dmdl_t *pheader; int i; pheader = (dmdl_t *)extradata; for (i=0; i < pheader->num_skins; i++) { skins[i] = find_image ((char *)pheader + pheader->ofs_skins + i*MAX_SKINNAME, it_skin); } return pheader->num_frames; } /* Unknow format, no images associated with it */ return 0; } /* ================= Mod_SetParent ================= */ static void Mod_SetParent(mnode_t *node, mnode_t *parent) { node->parent = parent; if (node->contents != CONTENTS_NODE) { return; } Mod_SetParent (node->children[0], node); Mod_SetParent (node->children[1], node); } /* ================= Mod_NumberLeafs ================= */ static void Mod_NumberLeafs(mleaf_t *leafs, mnode_t *node, int *r_leaftovis, int *r_vistoleaf, int *numvisleafs) { if (node->contents != CONTENTS_NODE) { mleaf_t *leaf; int leafnum; leaf = (mleaf_t *)node; leafnum = leaf - leafs; if (leaf->contents & CONTENTS_SOLID) { return; } r_leaftovis[leafnum] = *numvisleafs; r_vistoleaf[*numvisleafs] = leafnum; (*numvisleafs) ++; return; } Mod_NumberLeafs(leafs, node->children[0], r_leaftovis, r_vistoleaf, numvisleafs); Mod_NumberLeafs(leafs, node->children[1], r_leaftovis, r_vistoleaf, numvisleafs); } /* ================= Mod_LoadNodes ================= */ void Mod_LoadNodes(const char *name, cplane_t *planes, int numplanes, mleaf_t *leafs, int numleafs, mnode_t **nodes, int *numnodes, const byte *mod_base, const lump_t *l) { int r_leaftovis[MAX_MAP_LEAFS], r_vistoleaf[MAX_MAP_LEAFS]; int i, count, numvisleafs; dnode_t *in; mnode_t *out; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s", __func__, name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count * sizeof(*out)); *nodes = out; *numnodes = count; for (i = 0; i < count; i++, in++, out++) { int j, planenum; for (j = 0; j < 3; j++) { out->minmaxs[j] = LittleShort(in->mins[j]); out->minmaxs[3 + j] = LittleShort(in->maxs[j]); } planenum = LittleLong(in->planenum); if (planenum < 0 || planenum >= numplanes) { ri.Sys_Error(ERR_DROP, "%s: Incorrect %d < %d planenum.", __func__, planenum, numplanes); } out->plane = planes + planenum; out->firstsurface = LittleShort(in->firstface); out->numsurfaces = LittleShort(in->numfaces); out->contents = CONTENTS_NODE; /* differentiate from leafs */ for (j = 0; j < 2; j++) { int leafnum; leafnum = LittleLong(in->children[j]); if (leafnum >= 0) { if (leafnum < 0 || leafnum >= *numnodes) { ri.Sys_Error(ERR_DROP, "%s: Incorrect %d nodenum as leaf.", __func__, leafnum); } out->children[j] = *nodes + leafnum; } else { leafnum = -1 - leafnum; if (leafnum < 0 || leafnum >= numleafs) { ri.Sys_Error(ERR_DROP, "%s: Incorrect %d leafnum.", __func__, leafnum); } out->children[j] = (mnode_t *)(leafs + leafnum); } } } Mod_SetParent(*nodes, NULL); /* sets nodes and leafs */ numvisleafs = 0; Mod_NumberLeafs (leafs, *nodes, r_leaftovis, r_vistoleaf, &numvisleafs); } /* ================= Mod_LoadVisibility ================= */ void Mod_LoadVisibility (dvis_t **vis, const byte *mod_base, const lump_t *l) { dvis_t *out; int i; if (!l->filelen) { *vis = NULL; return; } out = Hunk_Alloc(l->filelen); *vis = out; memcpy(out, mod_base + l->fileofs, l->filelen); out->numclusters = LittleLong(out->numclusters); for (i = 0; i < out->numclusters; i++) { out->bitofs[i][0] = LittleLong(out->bitofs[i][0]); out->bitofs[i][1] = LittleLong(out->bitofs[i][1]); } } /* ================= Mod_LoadVertexes extra for skybox ================= */ void Mod_LoadVertexes(const char *name, mvertex_t **vertexes, int *numvertexes, const byte *mod_base, const lump_t *l, int extra) { 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, "%s: funny lump size in %s", __func__, name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc((count + extra)*sizeof(*out)); /* * FIXME: Recheck with soft render * Fix for the problem where the games dumped core * when changing levels. */ memset(out, 0, (count + extra) * sizeof(*out)); *vertexes = out; *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]); } } /* ================= Mod_LoadLighting ================= */ void Mod_LoadLighting(byte **lightdata, const byte *mod_base, const lump_t *l) { int size; if (!l->filelen) { *lightdata = NULL; return; } size = l->filelen; *lightdata = Hunk_Alloc(size); memcpy(*lightdata, mod_base + l->fileofs, size); } /* ================= Mod_LoadTexinfo extra for skybox in soft render ================= */ void Mod_LoadTexinfo(const char *name, mtexinfo_t **texinfo, int *numtexinfo, const byte *mod_base, const lump_t *l, findimage_t find_image, struct image_s *notexture, int extra) { texinfo_t *in; mtexinfo_t *out, *step; int i, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s", __func__, name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc((count + extra)*sizeof(*out)); *texinfo = out; *numtexinfo = count; for ( i=0 ; ivecs[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 = *texinfo + next; } else { /* * Fix for the problem where the game * domed core when loading a new level. */ out->next = NULL; } image = GetTexImage(in->texture, find_image); if (!image) { R_Printf(PRINT_ALL, "%s: Couldn't load %s\n", __func__, in->texture); image = notexture; } out->image = image; } // count animation frames for (i=0 ; inumframes = 1; for (step = out->next ; step && step != out ; step=step->next) { out->numframes++; } } } /* ================= Mod_LoadEdges extra is used for skybox, which adds 6 surfaces ================= */ void Mod_LoadEdges(const char *name, medge_t **edges, int *numedges, const byte *mod_base, const lump_t *l, int extra) { 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, "%s: funny lump size in %s", __func__, name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc((count + extra) * sizeof(*out)); *edges = out; *numedges = count; for ( i=0 ; iv[0] = (unsigned short)LittleShort(in->v[0]); out->v[1] = (unsigned short)LittleShort(in->v[1]); } } /* ================= Mod_LoadPlanes extra is used for skybox, which adds 6 surfaces ================= */ void Mod_LoadPlanes(const char *name, cplane_t **planes, int *numplanes, const byte *mod_base, const lump_t *l, int extra) { int i; cplane_t *out; dplane_t *in; int count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s", __func__, name); } count = l->filelen / sizeof(*in); // FIXME: why double of count out = Hunk_Alloc((count * 2 + extra) * sizeof(*out)); *planes = out; *numplanes = count; for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); if (out->normal[j] < 0) bits |= 1<dist = LittleFloat (in->dist); out->type = LittleLong (in->type); out->signbits = bits; } } /* ================= Mod_LoadSurfedges ================= */ void Mod_LoadSurfedges(const char *name, int **surfedges, int *numsurfedges, const byte *mod_base, const lump_t *l, int extra) { int i, count; int *in, *out; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s", __func__, name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc((count + extra)*sizeof(*out)); // extra for skybox *surfedges = out; *numsurfedges = count; for ( i=0 ; i includes its padding), so we'll know how big the hunk needs to be extra is used for skybox, which adds 6 surfaces ================= */ int Mod_CalcLumpHunkSize(const lump_t *l, int inSize, int outSize, int extra) { if (l->filelen % inSize) { // Mod_Load*() will error out on this because of "funny size" // don't error out here because in Mod_Load*() it can print the functionname // (=> tells us what kind of lump) before shutting down the game return 0; } int count = l->filelen / inSize + extra; int size = count * outSize; // round to cacheline, like Hunk_Alloc() does size = (size + 31) & ~31; return size; } /* =============== Mod_PointInLeaf =============== */ mleaf_t * Mod_PointInLeaf(const vec3_t p, mnode_t *node) { if (!node) { ri.Sys_Error(ERR_DROP, "%s: bad node.", __func__); return NULL; } while (1) { float d; cplane_t *plane; if (node->contents != CONTENTS_NODE) { 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 */ } yquake2-QUAKE2_8_40/src/client/refresh/files/pcx.c000066400000000000000000000210651465112212000216600ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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(const 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]; FixFileExt(origname, "pcx", filename, 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); ri.FS_FreeFile(pcx); return; } if (pcx->bytes_per_line <= pcx_width) { pcx->bytes_per_line = pcx_width + 1; image_issues = true; } full_size = (pcx_height + 1) * (pcx_width + 1); out = malloc(full_size); if (!out) { R_Printf(PRINT_ALL, "Can't allocate\n"); ri.FS_FreeFile(pcx); return; } *pic = out; pix = out; if (palette) { *palette = malloc(768); if (!(*palette)) { R_Printf(PRINT_ALL, "Can't allocate\n"); free(out); ri.FS_FreeFile(pcx); return; } 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->bytes_per_line; ) { 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(filename, "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(const char *origname, int *width, int *height) { pcx_t *pcx; byte *raw; char filename[256]; FixFileExt(origname, "pcx", filename, sizeof(filename)); 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; } /* =============== GetPCXPalette =============== */ void GetPCXPalette (byte **colormap, unsigned *d_8to24table) { byte *pal; int i; /* get the palette and colormap */ LoadPCX ("pics/colormap.pcx", colormap, &pal, NULL, NULL); if (!*colormap || !pal) { ri.Sys_Error (ERR_FATAL, "%s: Couldn't load pics/colormap.pcx", __func__); } for (i=0 ; i<256 ; i++) { unsigned v; int r, g, b; r = pal[i*3+0]; g = pal[i*3+1]; b = pal[i*3+2]; v = (255U<<24) + (r<<0) + (g<<8) + (b<<16); d_8to24table[i] = LittleLong(v); } d_8to24table[255] &= LittleLong(0xffffff); // 255 is transparent free (pal); } yquake2-QUAKE2_8_40/src/client/refresh/files/pvs.c000066400000000000000000000033561465112212000217010ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 PVS Decompress * * ======================================================================= */ #include "../ref_shared.h" /* =================== Mod_DecompressVis =================== */ const byte * Mod_DecompressVis(const byte *in, int row) { YQ2_ALIGNAS_TYPE(int) static byte decompressed[MAX_MAP_LEAFS / 8]; int c; byte *out; 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; } float Mod_RadiusFromBounds(const vec3_t mins, const 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); } yquake2-QUAKE2_8_40/src/client/refresh/files/stb.c000066400000000000000000000361611465112212000216610ustar00rootroot00000000000000/* * 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 "../ref_shared.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) // Switch of the thread local stuff. Breaks mingw under Windows. #define STBI_NO_THREAD_LOCALS // include implementation part of stb_image into this file #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" // include resize implementation #define STB_IMAGE_RESIZE_IMPLEMENTATION #include "stb_image_resize.h" /* * Add extension to file name */ void FixFileExt(const char *origname, const char *ext, char *filename, size_t size) { Q_strlcpy(filename, origname, size); /* Add the extension */ if (strcmp(COM_FileExtension(filename), ext)) { Q_strlcat(filename, ".", size); Q_strlcat(filename, ext, size); } } /* * 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 */ static qboolean LoadSTB(const char *origname, const char* type, byte **pic, int *width, int *height) { char filename[256]; FixFileExt(origname, type, filename, 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, "%s couldn't load data from %s: %s!\n", __func__, filename, stbi_failure_reason()); ri.FS_FreeFile(rawdata); return false; } ri.FS_FreeFile(rawdata); R_Printf(PRINT_DEVELOPER, "%s() loaded: %s\n", __func__, filename); *pic = data; *width = w; *height = h; return true; } qboolean ResizeSTB(const byte *input_pixels, int input_width, int input_height, byte *output_pixels, int output_width, int output_height) { if (stbir_resize_uint8(input_pixels, input_width, input_height, 0, output_pixels, output_width, output_height, 0, 4)) return true; return false; } // We have 16 color palette, 256 / 16 should be enough #define COLOR_DISTANCE 16 void SmoothColorImage(unsigned *dst, size_t size, size_t rstep) { unsigned *full_size; unsigned last_color; unsigned *last_diff; // maximum step for apply if (rstep < 2) { return; } // step one pixel back as with check one pixel more full_size = dst + size - rstep - 1; last_diff = dst; last_color = *dst; // skip current point dst ++; while (dst < full_size) { if (last_color != *dst) { int step = dst - last_diff; if (step > 1) { int a_beg, b_beg, c_beg, d_beg; int a_end, b_end, c_end, d_end; int a_step, b_step, c_step, d_step; int k; // minimize effect size to rstep if (step > rstep) { // change place for start effect last_diff += (step - rstep); step = rstep; } // compare next pixels for(k = 1; k <= step; k ++) { if (dst[k] != dst[0]) { break; } } // step back as pixel different after previous step k --; // mirror steps if (k < step) { // R_Printf(PRINT_ALL, "%s %d -> %d\n", __func__, k, step); // change place for start effect last_diff += (step - k); step = k; } // update step to correct value step += k; dst += k; // get colors a_beg = (last_color >> 0 ) & 0xff; b_beg = (last_color >> 8 ) & 0xff; c_beg = (last_color >> 16) & 0xff; d_beg = (last_color >> 24) & 0xff; a_end = (*dst >> 0 ) & 0xff; b_end = (*dst >> 8 ) & 0xff; c_end = (*dst >> 16) & 0xff; d_end = (*dst >> 24) & 0xff; a_step = a_end - a_beg; b_step = b_end - b_beg; c_step = c_end - c_beg; d_step = d_end - d_beg; if ((abs(a_step) <= COLOR_DISTANCE) && (abs(b_step) <= COLOR_DISTANCE) && (abs(c_step) <= COLOR_DISTANCE) && (abs(d_step) <= COLOR_DISTANCE) && step > 0) { // generate color change steps a_step = (a_step << 16) / step; b_step = (b_step << 16) / step; c_step = (c_step << 16) / step; d_step = (d_step << 16) / step; // apply color changes for (k=0; k < step; k++) { *last_diff = (((a_beg + ((a_step * k) >> 16)) << 0) & 0x000000ff) | (((b_beg + ((b_step * k) >> 16)) << 8) & 0x0000ff00) | (((c_beg + ((c_step * k) >> 16)) << 16) & 0x00ff0000) | (((d_beg + ((d_step * k) >> 16)) << 24) & 0xff000000); last_diff++; } } } last_color = *dst; last_diff = dst; } dst ++; } } /* https://en.wikipedia.org/wiki/Pixel-art_scaling_algorithms */ void scale2x(const byte *src, byte *dst, int width, int height) { /* EPX/Scale2×/AdvMAME2× x A x C P B -> 1 2 x D x 3 4 1=P; 2=P; 3=P; 4=P; IF C==A AND C!=D AND A!=B => 1=A IF A==B AND A!=C AND B!=D => 2=B IF D==C AND D!=B AND C!=A => 3=C IF B==D AND B!=A AND D!=C => 4=D */ { const byte *in_buff = src; byte *out_buff = dst; byte *out_buff_full = dst + ((width * height) << 2); while (out_buff < out_buff_full) { int x; for (x = 0; x < width; x ++) { // copy one source byte to two destinatuion bytes *out_buff = *in_buff; out_buff ++; *out_buff = *in_buff; out_buff ++; // next source pixel in_buff ++; } // copy last line one more time memcpy(out_buff, out_buff - (width << 1), width << 1); out_buff += width << 1; } } { int y, h, w; h = height - 1; w = width - 1; for (y = 0; y < height; y ++) { int x; for (x = 0; x < width; x ++) { byte a, b, c, d, p; p = src[(width * (y )) + (x )]; a = (y > 0) ? src[(width * (y - 1)) + (x )] : p; b = (x < w) ? src[(width * (y )) + (x + 1)] : p; c = (x > 0) ? src[(width * (y )) + (x - 1)] : p; d = (y < h) ? src[(width * (y + 1)) + (x )] : p; if ((c == a) && (c != d) && (a != b)) { dst[(2 * width * ((y * 2) )) + ((x * 2) )] = a; } if ((a == b) && (a != c) && (b != d)) { dst[(2 * width * ((y * 2) )) + ((x * 2) + 1)] = b; } if ((d == c) && (d != b) && (c != a)) { dst[(2 * width * ((y * 2) + 1)) + ((x * 2) )] = c; } if ((b == d) && (b != a) && (d != c)) { dst[(2 * width * ((y * 2) + 1)) + ((x * 2) + 1)] = d; } } } } } void scale3x(const byte *src, byte *dst, int width, int height) { /* Scale3×/AdvMAME3× and ScaleFX A B C 1 2 3 D E F -> 4 5 6 G H I 7 8 9 1=E; 2=E; 3=E; 4=E; 5=E; 6=E; 7=E; 8=E; 9=E; IF D==B AND D!=H AND B!=F => 1=D IF (D==B AND D!=H AND B!=F AND E!=C) OR (B==F AND B!=D AND F!=H AND E!=A) => 2=B IF B==F AND B!=D AND F!=H => 3=F IF (H==D AND H!=F AND D!=B AND E!=A) OR (D==B AND D!=H AND B!=F AND E!=G) => 4=D 5=E IF (B==F AND B!=D AND F!=H AND E!=I) OR (F==H AND F!=B AND H!=D AND E!=C) => 6=F IF H==D AND H!=F AND D!=B => 7=D IF (F==H AND F!=B AND H!=D AND E!=G) OR (H==D AND H!=F AND D!=B AND E!=I) => 8=H IF F==H AND F!=B AND H!=D => 9=F */ { const byte *in_buff = src; byte *out_buff = dst; byte *out_buff_full = dst + ((width * height) * 9); while (out_buff < out_buff_full) { int x; for (x = 0; x < width; x ++) { // copy one source byte to two destinatuion bytes *out_buff = *in_buff; out_buff ++; *out_buff = *in_buff; out_buff ++; *out_buff = *in_buff; out_buff ++; // next source pixel in_buff ++; } // copy last line one more time memcpy(out_buff, out_buff - (width * 3), width * 3); out_buff += width * 3; // copy last line one more time memcpy(out_buff, out_buff - (width * 3), width * 3); out_buff += width * 3; } } { int y, z, w; z = height - 1; w = width - 1; for (y = 0; y < height; y ++) { int x; for (x = 0; x < width; x ++) { byte a, b, c, d, e, f, g, h, i; e = src[(width * y) + x]; a = ((y > 0) && (x > 0)) ? src[(width * (y - 1)) + (x - 1)] : e; b = ((y > 0) && (x )) ? src[(width * (y - 1)) + (x )] : e; c = ((y > 0) && (x < w)) ? src[(width * (y - 1)) + (x + 1)] : e; d = ( (x > 0)) ? src[(width * (y )) + (x - 1)] : e; f = ( (x < w)) ? src[(width * (y )) + (x + 1)] : e; g = ((y < z) && (x > 0)) ? src[(width * (y + 1)) + (x - 1)] : e; h = ((y < z) && (x )) ? src[(width * (y + 1)) + (x )] : e; i = ((y < z) && (x < w)) ? src[(width * (y + 1)) + (x + 1)] : e; if ((d == b) && (d != h) && (b != f)) { dst[(3 * width * ((y * 3) )) + ((x * 3) )] = d; } if (((d == b) && (d != h) && (b != f) && (e != c)) || ((b == f) && (b != d) && (f != h) && (e != a))) { dst[(3 * width * ((y * 3) )) + ((x * 3) + 1)] = b; } if ((b == f) && (b != d) && (f != h)) { dst[(3 * width * ((y * 3) )) + ((x * 3) + 2)] = f; } if (((h == d) && (h != f) && (d != b) && (e != a)) || ((d == b) && (d != h) && (b != f) && (e != g))) { dst[(3 * width * ((y * 3) + 1)) + ((x * 3) )] = d; } if (((b == f) && (b != d) && (f != h) && (e != i)) || ((f == h) && (f != b) && (h != d) && (e != c))) { dst[(3 * width * ((y * 3) + 1)) + ((x * 3) + 2)] = f; } if ((h == d) && (h != f) && (d != b)) { dst[(3 * width * ((y * 3) + 2)) + ((x * 3) )] = d; } if (((f == h) && (f != b) && (h != d) && (e != g)) || ((h == d) && (h != f) && (d != b) && (e != i))) { dst[(3 * width * ((y * 3) + 2)) + ((x * 3) + 1)] = h; } if ((f == h) && (f != b) && (h != d)) { dst[(3 * width * ((y * 3) + 2)) + ((x * 3) + 2)] = f; } } } } } static struct image_s * LoadHiColorImage(const char *name, const char* namewe, const char *ext, imagetype_t type, loadimage_t load_image) { int realwidth = 0, realheight = 0; int width = 0, height = 0; struct image_s *image = NULL; byte *pic = NULL; /* Get size of the original texture */ if (strcmp(ext, "pcx") == 0) { GetPCXInfo(name, &realwidth, &realheight); } else if (strcmp(ext, "wal") == 0) { GetWalInfo(name, &realwidth, &realheight); } else if (strcmp(ext, "m8") == 0) { GetM8Info(name, &realwidth, &realheight); } else if (strcmp(ext, "m32") == 0) { GetM32Info(name, &realwidth, &realheight); } /* 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) ) { if (width >= realwidth && height >= realheight) { if (realheight == 0 || realwidth == 0) { realheight = height; realwidth = width; } image = load_image(name, pic, width, realwidth, height, realheight, width * height, type, 32); } } if (pic) { free(pic); } return image; } struct image_s * R_LoadImage(const char *name, const char* namewe, const char *ext, imagetype_t type, qboolean r_retexturing, loadimage_t load_image) { struct image_s *image = NULL; // with retexturing and not skin if (r_retexturing) { image = LoadHiColorImage(name, namewe, ext, type, load_image); } if (!image) { if (!strcmp(ext, "pcx")) { byte *pic = NULL; byte *palette = NULL; int width = 0, height = 0; LoadPCX (namewe, &pic, &palette, &width, &height); if (!pic) return NULL; image = load_image(name, pic, width, width, height, height, width * height, type, 8); if (palette) { free(palette); } free(pic); } else if (!strcmp(ext, "wal")) { image = LoadWal(namewe, type, load_image); } else if (!strcmp(ext, "m8")) { image = LoadM8(namewe, type, load_image); } else if (!strcmp(ext, "m32")) { image = LoadM32(namewe, type, load_image); } else if (!strcmp(ext, "tga") || !strcmp(ext, "png") || !strcmp(ext, "jpg")) { byte *pic = NULL; int width = 0, height = 0; if (LoadSTB (namewe, ext, &pic, &width, &height) && pic) { image = load_image(name, pic, width, width, height, height, width * height, type, 32); free(pic); } } } return image; } struct image_s* GetSkyImage(const char *skyname, const char* surfname, qboolean palettedtexture, findimage_t find_image) { struct image_s *image = NULL; char pathname[MAX_QPATH]; /* Quake 2 */ if (palettedtexture) { Com_sprintf(pathname, sizeof(pathname), "env/%s%s.pcx", skyname, surfname); image = find_image(pathname, it_sky); } if (!image) { Com_sprintf(pathname, sizeof(pathname), "env/%s%s.tga", skyname, surfname); image = find_image(pathname, it_sky); } /* Heretic 2 */ if (!image) { Com_sprintf(pathname, sizeof(pathname), "pics/Skies/%s%s.m32", skyname, surfname); image = find_image(pathname, it_sky); } if (!image) { Com_sprintf(pathname, sizeof(pathname), "pics/Skies/%s%s.m8", skyname, surfname); image = find_image(pathname, it_sky); } return image; } struct image_s * GetTexImage(const char *name, findimage_t find_image) { struct image_s *image = NULL; char pathname[MAX_QPATH]; /* Quake 2 */ Com_sprintf(pathname, sizeof(pathname), "textures/%s.wal", name); image = find_image(pathname, it_wall); /* Heretic 2 */ if (!image) { Com_sprintf(pathname, sizeof(pathname), "textures/%s.m32", name); image = find_image(pathname, it_wall); } if (!image) { Com_sprintf(pathname, sizeof(pathname), "textures/%s.m8", name); image = find_image(pathname, it_wall); } return image; } struct image_s * R_FindPic(const char *name, findimage_t find_image) { struct image_s *image = NULL; if ((name[0] != '/') && (name[0] != '\\')) { char pathname[MAX_QPATH]; /* Quake 2 */ Com_sprintf(pathname, sizeof(pathname), "pics/%s.pcx", name); image = find_image(pathname, it_pic); /* Heretic 2 */ if (!image) { Com_sprintf(pathname, sizeof(pathname), "pics/misc/%s.m32", name); image = find_image(pathname, it_pic); } if (!image) { Com_sprintf(pathname, sizeof(pathname), "pics/misc/%s.m8", name); image = find_image(pathname, it_pic); } } else { image = find_image(name + 1, it_pic); } return image; } yquake2-QUAKE2_8_40/src/client/refresh/files/stb_image.h000066400000000000000000010540751465112212000230350ustar00rootroot00000000000000/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb 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.28 (2023-01-29) many error fixes, security errors, just tons of stuff 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes 2.26 (2020-07-13) many minor fixes 2.25 (2020-02-02) fix warnings 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically 2.23 (2019-08-11) fix clang static analysis warning 2.22 (2019-03-04) gif fixes, fix warnings 2.21 (2019-02-25) fix typo in comment 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings 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) Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) socks-the-fox (16-bit PNG) Jeremy Sawicki (handle all ImageNet JPGs) Optimizations & bugfixes Mikhail Morozov (1-bit BMP) Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) Arseny Kapoulkine Simon Breuss (16-bit PNM) John-Mark Allen Carmelo J Fdez-Aguera Bug & warning fixes Marc LeBlanc David Woo Guillaume George Martins Mozeiko Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski 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 github:rlyeh Janez Zemva John Bartholomew Michal Cichon github:romigrou Jonathan Blow Ken Hamada Tero Hanninen github:svdijk Eugene Golushkov Laurent Gomila Cort Stratton github:snagar Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex Cass Everitt Ryamond Barbiero github:grim210 Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo Julian Raschke Gregory Mullen Christian Floisand github:darealshinji Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 Brad Weinberger Matvey Cherevko github:mosra Luca Sas Alexander Veselov Zack Middleton [reserved] Ryan C. Gordon [reserved] [reserved] DO NOT ADD YOUR NAME HERE Jacko Dirks To add your name to the credits, pick a random blank space in the middle and fill it. 80% of merge conflicts on stb PRs are due to people adding their name at the end of the credits. */ #ifndef STBI_INCLUDE_STB_IMAGE_H #define STBI_INCLUDE_STB_IMAGE_H // DOCUMENTATION // // Limitations: // - no 12-bit-per-channel JPEG // - no JPEGs with arithmetic coding // - 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. // // To query the width, height and component count of an image without having to // decode the full file, you can use the stbi_info family of functions: // // int x,y,n,ok; // ok = stbi_info(filename, &x, &y, &n); // // returns ok=1 and sets x, y, n if image is a supported format, // // 0 otherwise. // // Note that stb_image pervasively uses ints in its public API for sizes, // including sizes of memory buffers. This is now part of the API and thus // hard to change without causing breakage. As a result, the various image // loaders all have certain limits on image size; these differ somewhat // by format but generally boil down to either just under 2GB or just under // 1GB. When the decoded image would be larger than this, stb_image decoding // will fail. // // Additionally, stb_image will reject image files that have any of their // dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, // which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, // the only way to have an image with such dimensions load correctly // is for it to have a rather extreme aspect ratio. Either way, the // assumption here is that such larger images are likely to be malformed // or malicious. If you do need to load an image with individual dimensions // larger than that, and it still fits in the overall size limit, you can // #define STBI_MAX_DIMENSIONS on your own to be something larger. // // =========================================================================== // // UNICODE: // // If compiling for Windows and you wish to use Unicode filenames, compile // with // #define STBI_WINDOWS_UTF8 // and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert // Windows wchar_t filenames to utf8. // // =========================================================================== // // 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 // provide 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 supports loading HDR images in general, and currently the Radiance // .HDR file format specifically. 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: // // We optionally support converting iPhone-formatted PNGs (which store // premultiplied BGRA) back to RGB, even though they're internally encoded // differently. To enable this conversion, call // stbi_convert_iphone_png_to_rgb(1). // // 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 // // - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater // than that size (in either width or height) without further processing. // This is to let programs in the wild set an upper bound to prevent // denial-of-service attacks on untrusted data, as one could generate a // valid image of gigantic dimensions and force stb_image to allocate a // huge block of memory and spend disproportionate time decoding it. By // default this is set to (1 << 24), which is 16777216, but that's still // very big. #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 }; #include typedef unsigned char stbi_uc; typedef unsigned short stbi_us; #ifdef __cplusplus extern "C" { #endif #ifndef STBIDEF #ifdef STB_IMAGE_STATIC #define STBIDEF static #else #define STBIDEF extern #endif #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 #ifndef STBI_NO_GIF STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); #endif #ifdef STBI_WINDOWS_UTF8 STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); #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 // on most compilers (and ALL modern mainstream compilers) this is 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); STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); #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); STBIDEF int stbi_is_16_bit (char const *filename); STBIDEF int stbi_is_16_bit_from_file(FILE *f); #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); // as above, but only applies to images loaded on the thread that calls the function // this function is only available if your compiler supports thread-local variables; // calling it will fail to link if your compiler doesn't STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); STBIDEF void stbi_set_flip_vertically_on_load_thread(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, pow #endif #ifndef STBI_NO_STDIO #include #endif #ifndef STBI_ASSERT #include #define STBI_ASSERT(x) assert(x) #endif #ifdef __cplusplus #define STBI_EXTERN extern "C" #else #define STBI_EXTERN extern #endif #ifndef _MSC_VER #ifdef __cplusplus #define stbi_inline inline #else #define stbi_inline #endif #else #define stbi_inline __forceinline #endif #ifndef STBI_NO_THREAD_LOCALS #if defined(__cplusplus) && __cplusplus >= 201103L #define STBI_THREAD_LOCAL thread_local #elif defined(__GNUC__) && __GNUC__ < 5 #define STBI_THREAD_LOCAL __thread #elif defined(_MSC_VER) #define STBI_THREAD_LOCAL __declspec(thread) #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) #define STBI_THREAD_LOCAL _Thread_local #endif #ifndef STBI_THREAD_LOCAL #if defined(__GNUC__) #define STBI_THREAD_LOCAL __thread #endif #endif #endif #if defined(_MSC_VER) || defined(__SYMBIAN32__) 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) >> (-(y) & 31))) #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 #if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { int info3 = stbi__cpuid3(); return ((info3 >> 26) & 1) != 0; } #endif #else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) #if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) 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 #endif // ARM NEON #if defined(STBI_NO_SIMD) && defined(STBI_NEON) #undef STBI_NEON #endif #ifdef STBI_NEON #include #ifdef _MSC_VER #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name #else #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) #endif #endif #ifndef STBI_SIMD_ALIGN #define STBI_SIMD_ALIGN(type, name) type name #endif #ifndef STBI_MAX_DIMENSIONS #define STBI_MAX_DIMENSIONS (1 << 24) #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]; int callback_already_read; 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->callback_already_read = 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->callback_already_read = 0; s->img_buffer = 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) { int ch; fseek((FILE*) user, n, SEEK_CUR); ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ if (ch != EOF) { ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ } } static int stbi__stdio_eof(void *user) { return feof((FILE*) user) || ferror((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); static int stbi__png_is16(stbi__context *s); #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); static int stbi__psd_is16(stbi__context *s); #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 void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNM static int stbi__pnm_test(stbi__context *s); static 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); static int stbi__pnm_is16(stbi__context *s); #endif static #ifdef STBI_THREAD_LOCAL STBI_THREAD_LOCAL #endif const char *stbi__g_failure_reason; STBIDEF const char *stbi_failure_reason(void) { return stbi__g_failure_reason; } #ifndef STBI_NO_FAILURE_STRINGS static int stbi__err(const char *str) { stbi__g_failure_reason = str; return 0; } #endif 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; } #if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // 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); } #endif // 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 #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) 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); } #endif #if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // 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); } #endif 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); } #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) 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); } #endif // returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. static int stbi__addints_valid(int a, int b) { if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. return a <= INT_MAX - b; } // returns 1 if the product of two signed shorts is valid, 0 on overflow. static int stbi__mul2shorts_valid(short a, short b) { if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN return a >= SHRT_MIN / b; } // 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_global = 0; STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { stbi__vertically_flip_on_load_global = flag_true_if_should_flip; } #ifndef STBI_THREAD_LOCAL #define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global #else static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) { stbi__vertically_flip_on_load_local = flag_true_if_should_flip; stbi__vertically_flip_on_load_set = 1; } #define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ ? stbi__vertically_flip_on_load_local \ : stbi__vertically_flip_on_load_global) #endif // STBI_THREAD_LOCAL 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; // test the formats with a very explicit header first (at least a FOURCC // or distinctive magic number first) #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); #else STBI_NOTUSED(bpc); #endif #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); #endif // then the formats that can end up attempting to load with just 1 or 2 // bytes matching expectations; these are prone to false positives, so // try them later #ifndef STBI_NO_JPEG if (stbi__jpeg_test(s)) return stbi__jpeg_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; } } } #ifndef STBI_NO_GIF static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) { int slice; int slice_size = w * h * bytes_per_pixel; stbi_uc *bytes = (stbi_uc *)image; for (slice = 0; slice < z; ++slice) { stbi__vertical_flip(bytes, w, h, bytes_per_pixel); bytes += slice_size; } } #endif 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; // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); if (ri.bits_per_channel != 8) { 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; // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); if (ri.bits_per_channel != 16) { 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; } #if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) 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 #if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); #endif #if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) { return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); } #endif static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; #if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) wchar_t wMode[64]; wchar_t wFilename[1024]; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) return 0; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) return 0; #if defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != _wfopen_s(&f, wFilename, wMode)) f = 0; #else f = _wfopen(wFilename, wMode); #endif #elif 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_GIF STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { unsigned char *result; stbi__context s; stbi__start_mem(&s,buffer,len); result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); if (stbi__vertically_flip_on_load) { stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); } return result; } #endif #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 long pos = ftell(f); int res; stbi__context s; stbi__start_file(&s,f); res = stbi__hdr_test(&s); fseek(f, pos, SEEK_SET); return res; #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); s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); 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; } #if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing #else 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; } #endif #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) // nothing #else static void stbi__skip(stbi__context *s, int n) { if (n == 0) return; // already there! 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; } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) // nothing #else 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; } #endif #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) // nothing #else static int stbi__get16be(stbi__context *s) { int z = stbi__get8(s); return (z << 8) + stbi__get8(s); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) // nothing #else static stbi__uint32 stbi__get32be(stbi__context *s) { stbi__uint32 z = stbi__get16be(s); return (z << 16) + stbi__get16be(s); } #endif #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); z += (stbi__uint32)stbi__get16le(s) << 16; return z; } #endif #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing #else ////////////////////////////////////////////////////////////////////////////// // // 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); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing #else 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); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } STBI_FREE(data); return good; } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) // nothing #else static stbi__uint16 stbi__compute_y_16(int r, int g, int b) { return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) // nothing #else 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); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } STBI_FREE(data); return good; } #endif #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 (n < comp) { for (i=0; i < x*y; ++i) { output[i*comp + n] = data[i*comp + n]/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; unsigned int 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); if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); } } 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 >= (1u << 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 * 256) + (run * 16) + (len + magbits)); } } } } static void stbi__grow_buffer_unsafe(stbi__jpeg *j) { do { unsigned 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 const 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]; if(c < 0 || c >= 256) // symbol id out of bounds! return -1; 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); if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) k = stbi_lrot(j->code_buffer, n); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; return k + (stbi__jbias[n] & (sgn - 1)); } // 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); if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing 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); if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing 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 const 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 || t > 15) 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; if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); 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 if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); 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); if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); diff = t ? stbi__extend_receive(j, t) : 0; if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); data[0] = (short) (dc * (1 << 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 if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); j->code_buffer <<= s; j->code_bits -= s; zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) ((r >> 8) * (1 << 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) * (1 << 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) * 4096) // 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]*4; 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]; } if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! 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 if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); 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 const 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; } // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios // and I've never seen a non-corrupted JPEG file actually use them for (i=0; i < s->img_n; ++i) { if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); } // 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; } static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) { // some JPEGs have junk at end, skip over it but if we find what looks // like a valid marker, resume there while (!stbi__at_eof(j->s)) { int x = stbi__get8(j->s); while (x == 255) { // might be a marker if (stbi__at_eof(j->s)) return STBI__MARKER_none; x = stbi__get8(j->s); if (x != 0x00 && x != 0xff) { // not a stuffed zero or lead-in to another marker, looks // like an actual marker, return it return x; } // stuffed zero has x=0 now which ends the loop, meaning we go // back to regular scan loop. // repeated 0xff keeps trying to read the next byte of the marker. } } return STBI__MARKER_none; } // 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 ) { j->marker = stbi__skip_jpeg_junk_at_end(j); // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } m = stbi__get_marker(j); if (STBI__RESTART(m)) m = stbi__get_marker(j); } else if (stbi__DNL(m)) { int Ld = stbi__get16be(j->s); stbi__uint32 NL = stbi__get16be(j->s); if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); m = stbi__get_marker(j); } else { if (!stbi__process_marker(j, m)) return 1; 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; // nothing to do if no components requested; check this now to avoid // accessing uninitialized coutput[0] later if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } // resample and color-convert { int k; unsigned int i,j; stbi_uc *output; stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; 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)); if (!j) return stbi__errpuc("outofmem", "Out of memory"); memset(j, 0, 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)); if (!j) return stbi__err("outofmem", "Out of memory"); memset(j, 0, 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))); if (!j) return stbi__err("outofmem", "Out of memory"); memset(j, 0, 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) #define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet // 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[STBI__ZNSYMS]; stbi__uint16 value[STBI__ZNSYMS]; } 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 int stbi__zeof(stbi__zbuf *z) { return (z->zbuffer >= z->zbuffer_end); } stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) { return stbi__zeof(z) ? 0 : *z->zbuffer++; } static void stbi__fill_bits(stbi__zbuf *z) { do { if (z->code_buffer >= (1U << z->num_bits)) { z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ return; } 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]; if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. 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) { if (stbi__zeof(a)) { return -1; /* report error for unexpected end of data. */ } 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; unsigned int cur, limit, old_limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); cur = (unsigned int) (z->zout - z->zout_start); limit = old_limit = (unsigned) (z->zout_end - z->zout_start); if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); while (cur + n > limit) { if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); 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 const 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 const 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 const 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 const 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; } if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data 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 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data 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 const 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 if (c == 18) { c = stbi__zreceive(a,7)+11; } else { return stbi__err("bad codelengths", "Corrupt PNG"); } 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; } if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); // 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 (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec 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[STBI__ZNSYMS] = { 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 , STBI__ZNSYMS)) 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 const 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 const 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"); if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); 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) { if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); 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); if (!final) return stbi__err("outofmem", "Out of memory"); 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_global = 0; static int stbi__de_iphone_flag_global = 0; STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) { stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; } STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) { stbi__de_iphone_flag_global = flag_true_if_should_convert; } #ifndef STBI_THREAD_LOCAL #define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global #define stbi__de_iphone_flag stbi__de_iphone_flag_global #else static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) { stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; stbi__unpremultiply_on_load_set = 1; } STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) { stbi__de_iphone_flag_local = flag_true_if_should_convert; stbi__de_iphone_flag_set = 1; } #define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ ? stbi__unpremultiply_on_load_local \ : stbi__unpremultiply_on_load_global) #define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ ? stbi__de_iphone_flag_local \ : stbi__de_iphone_flag_global) #endif // STBI_THREAD_LOCAL 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) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (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]={0}; 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); s->img_y = stbi__get32be(s); if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); if (s->img_x > STBI_MAX_DIMENSIONS) 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"); } 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"); } // even with 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; // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. if (scan == STBI__SCAN_header) { ++s->img_n; return 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) { // header scan definitely stops at first IDAT if (pal_img_n) s->img_n = pal_img_n; return 1; } if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); 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; // end of PNG chunk, read and skip CRC stbi__get32be(s); 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 if (p->depth == 16) ri->bits_per_channel = 16; else return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color 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); } static int stbi__png_is16(stbi__context *s) { stbi__png p; p.s = s; if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) return 0; if (p.depth != 16) { stbi__rewind(p.s); return 0; } return 1; } #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;/* >>= 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; } // extract an arbitrarily-aligned N-bit value (N=bits) // from v, and then make it 8-bits long and fractionally // extend it to full full range. static int stbi__shiftsigned(unsigned int v, int shift, int bits) { static unsigned int mul_table[9] = { 0, 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, }; static unsigned int shift_table[9] = { 0, 0,0,1,0,2,4,6,0, }; if (shift < 0) v <<= -shift; else v >>= shift; STBI_ASSERT(v < 256); v >>= (8-bits); STBI_ASSERT(bits >= 0 && bits <= 8); return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; } typedef struct { int bpp, offset, hsz; unsigned int mr,mg,mb,ma, all_a; int extra_read; } stbi__bmp_data; static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) { // BI_BITFIELDS specifies masks explicitly, don't override if (compress == 3) return 1; if (compress == 0) { if (info->bpp == 16) { info->mr = 31u << 10; info->mg = 31u << 5; info->mb = 31u << 0; } else 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 { // otherwise, use defaults, which is all-0 info->mr = info->mg = info->mb = info->ma = 0; } return 1; } return 0; // error } 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; info->extra_read = 14; if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); 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 (hsz != 12) { int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel 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) { stbi__bmp_set_mask_defaults(info, compress); } else if (compress == 3) { info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); info->extra_read += 12; // 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 { // V4/V5 header 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); if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs stbi__bmp_set_mask_defaults(info, compress); 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); if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); 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 - info.extra_read - 24) / 3; } else { if (info.bpp < 16) psize = (info.offset - info.extra_read - info.hsz) >> 2; } if (psize == 0) { // accept some number of extra bytes after the header, but if the offset points either to before // the header ends or implies a large amount of extra data, reject the file as malformed int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); int header_limit = 1024; // max we actually read is below 256 bytes currently. int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { return stbi__errpuc("bad header", "Corrupt BMP"); } // we established that bytes_read_so_far is positive and sensible. // the first half of this test rejects offsets that are either too small positives, or // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn // ensures the number computed in the second half of the test can't overflow. if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { return stbi__errpuc("bad offset", "Corrupt BMP"); } else { stbi__skip(s, info.offset - bytes_read_so_far); } } if (info.bpp == 24 && ma == 0xff000000) s->img_n = 3; else 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 - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); if (info.bpp == 1) width = (s->img_x + 7) >> 3; else 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; if (info.bpp == 1) { for (j=0; j < (int) s->img_y; ++j) { int bit_offset = 7, v = stbi__get8(s); for (i=0; i < (int) s->img_x; ++i) { int color = (v>>bit_offset)&0x1; out[z++] = pal[color][0]; out[z++] = pal[color][1]; out[z++] = pal[color][2]; if (target == 4) out[z++] = 255; if (i+1 == (int) s->img_x) break; if((--bit_offset) < 0) { bit_offset = 7; v = stbi__get8(s); } } stbi__skip(s, pad); } } else { 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 - info.extra_read - 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); if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } } 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)); unsigned 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; // fallthrough case 15: if(is_rgb16) *is_rgb16 = 1; return STBI_rgb; case 24: // fallthrough 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); STBI_NOTUSED(tga_x_origin); // @TODO STBI_NOTUSED(tga_y_origin); // @TODO if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); // 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) { if (tga_palette_len == 0) { /* you have to have at least one entry! */ STBI_FREE(tga_data); return stbi__errpuc("bad palette", "Corrupt TGA"); } // 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; STBI_NOTUSED(tga_palette_start); // 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); if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); // 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 preceded 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 (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); 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); if (!result) return stbi__errpuc("outofmem", "Out of memory"); memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { STBI_FREE(result); result=0; } *px = x; *py = y; if (req_comp == 0) req_comp = *comp; result=stbi__convert_format(result,4,req_comp,x,y); return result; } static int stbi__pic_test(stbi__context *s) { int r = stbi__pic_test_core(s); stbi__rewind(s); return r; } #endif // ************************************************************************************************* // GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb #ifndef STBI_NO_GIF typedef struct { stbi__int16 prefix; stbi_uc first; stbi_uc suffix; } stbi__gif_lzw; typedef struct { int w,h; stbi_uc *out; // output buffer (always 4 components) stbi_uc *background; // The current "background" as far as a gif is concerned stbi_uc *history; int flags, bgindex, ratio, transparent, eflags; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; stbi__gif_lzw codes[8192]; 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; int delay; } 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 (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); 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 (!g) return stbi__err("outofmem", "Out of memory"); 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; int idx; // 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; idx = g->cur_x + g->cur_y; p = &g->out[idx]; g->history[idx / 4] = 1; c = &g->color_table[g->codes[code].suffix * 4]; if (c[3] > 128) { // don't render transparent pixels; 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 > 8192) { 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"); } } } } // this function is designed to support animated gifs, although stb_image doesn't support it // two back is the image from two frames ago, used for a very specific disposal format static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) { int dispose; int first_frame; int pi; int pcount; STBI_NOTUSED(req_comp); // on first frame, any non-written pixels get the background colour (non-transparent) first_frame = 0; if (g->out == 0) { if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) return stbi__errpuc("too large", "GIF image is too large"); pcount = g->w * g->h; g->out = (stbi_uc *) stbi__malloc(4 * pcount); g->background = (stbi_uc *) stbi__malloc(4 * pcount); g->history = (stbi_uc *) stbi__malloc(pcount); if (!g->out || !g->background || !g->history) return stbi__errpuc("outofmem", "Out of memory"); // image is treated as "transparent" at the start - ie, nothing overwrites the current background; // background colour is only used for pixels that are not rendered first frame, after that "background" // color refers to the color that was there the previous frame. memset(g->out, 0x00, 4 * pcount); memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) memset(g->history, 0x00, pcount); // pixels that were affected previous frame first_frame = 1; } else { // second frame - how do we dispose of the previous one? dispose = (g->eflags & 0x1C) >> 2; pcount = g->w * g->h; if ((dispose == 3) && (two_back == 0)) { dispose = 2; // if I don't have an image to revert back to, default to the old background } if (dispose == 3) { // use previous graphic for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); } } } else if (dispose == 2) { // restore what was changed last frame to background before that frame; for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); } } } else { // This is a non-disposal case eithe way, so just // leave the pixels as is, and they will become the new background // 1: do not dispose // 0: not specified. } // background is what out is after the undoing of the previou frame; memcpy( g->background, g->out, 4 * g->w * g->h ); } // clear my history; memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame for (;;) { int tag = stbi__get8(s); switch (tag) { case 0x2C: /* Image Descriptor */ { stbi__int32 x, y, w, h; stbi_uc *o; x = stbi__get16le(s); y = stbi__get16le(s); w = stbi__get16le(s); h = stbi__get16le(s); if (((x + w) > (g->w)) || ((y + h) > (g->h))) return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); g->line_size = g->w * 4; g->start_x = x * 4; g->start_y = y * g->line_size; g->max_x = g->start_x + w * 4; g->max_y = g->start_y + h * g->line_size; g->cur_x = g->start_x; g->cur_y = g->start_y; // if the width of the specified rectangle is 0, that means // we may not see *any* pixels or the image is malformed; // to make sure this is caught, move the current y down to // max_y (which is what out_gif_code checks). if (w == 0) g->cur_y = g->max_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) { 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) return NULL; // if this was the first frame, pcount = g->w * g->h; if (first_frame && (g->bgindex > 0)) { // if first frame, any pixel not drawn to gets the background color for (pi = 0; pi < pcount; ++pi) { if (g->history[pi] == 0) { g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); } } } return o; } case 0x21: // Comment Extension. { int len; int ext = stbi__get8(s); if (ext == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { g->eflags = stbi__get8(s); g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. // unset old transparent if (g->transparent >= 0) { g->pal[g->transparent][3] = 255; } if (g->eflags & 0x01) { g->transparent = stbi__get8(s); if (g->transparent >= 0) { g->pal[g->transparent][3] = 0; } } else { // don't need transparent stbi__skip(s, 1); g->transparent = -1; } } else { stbi__skip(s, len); break; } } while ((len = stbi__get8(s)) != 0) { stbi__skip(s, len); } break; } case 0x3B: // gif stream termination code return (stbi_uc *) s; // using '1' causes warning on some compilers default: return stbi__errpuc("unknown code", "Corrupt GIF"); } } } static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) { STBI_FREE(g->out); STBI_FREE(g->history); STBI_FREE(g->background); if (out) STBI_FREE(out); if (delays && *delays) STBI_FREE(*delays); return stbi__errpuc("outofmem", "Out of memory"); } static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { if (stbi__gif_test(s)) { int layers = 0; stbi_uc *u = 0; stbi_uc *out = 0; stbi_uc *two_back = 0; stbi__gif g; int stride; int out_size = 0; int delays_size = 0; STBI_NOTUSED(out_size); STBI_NOTUSED(delays_size); memset(&g, 0, sizeof(g)); if (delays) { *delays = 0; } do { u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker if (u) { *x = g.w; *y = g.h; ++layers; stride = g.w * g.h * 4; if (out) { void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); if (!tmp) return stbi__load_gif_main_outofmem(&g, out, delays); else { out = (stbi_uc*) tmp; out_size = layers * stride; } if (delays) { int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); if (!new_delays) return stbi__load_gif_main_outofmem(&g, out, delays); *delays = new_delays; delays_size = layers * sizeof(int); } } else { out = (stbi_uc*)stbi__malloc( layers * stride ); if (!out) return stbi__load_gif_main_outofmem(&g, out, delays); out_size = layers * stride; if (delays) { *delays = (int*) stbi__malloc( layers * sizeof(int) ); if (!*delays) return stbi__load_gif_main_outofmem(&g, out, delays); delays_size = layers * sizeof(int); } } memcpy( out + ((layers - 1) * stride), u, stride ); if (layers >= 2) { two_back = out - 2 * stride; } if (delays) { (*delays)[layers - 1U] = g.delay; } } } while (u != 0); // free temp buffer; STBI_FREE(g.out); STBI_FREE(g.history); STBI_FREE(g.background); // do the final conversion after loading everything; if (req_comp && req_comp != 4) out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); *z = layers; return out; } else { return stbi__errpuc("not GIF", "Image was not as a gif type."); } } 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; memset(&g, 0, sizeof(g)); STBI_NOTUSED(ri); u = stbi__gif_load_next(s, &g, comp, req_comp, 0); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker if (u) { *x = g.w; *y = g.h; // moved conversion to after successful load so that the same // can be done for multiple frames. if (req_comp && req_comp != 4) u = stbi__convert_format(u, 4, req_comp, g.w, g.h); } else if (g.out) { // if there was an error and we allocated an image buffer, free it! STBI_FREE(g.out); } // free buffers needed for multiple frame loading; STBI_FREE(g.history); STBI_FREE(g.background); 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); if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); *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 == 0) || (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 == 0) || (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); if (p == NULL) { stbi__rewind( s ); return 0; } if (x) *x = s->img_x; if (y) *y = s->img_y; if (comp) { if (info.bpp == 24 && info.ma == 0xff000000) *comp = 3; else *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, depth; 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); depth = stbi__get16be(s); if (depth != 8 && depth != 16) { stbi__rewind( s ); return 0; } if (stbi__get16be(s) != 3) { stbi__rewind( s ); return 0; } *comp = 4; return 1; } static int stbi__psd_is16(stbi__context *s) { int channelCount, depth; 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; } STBI_NOTUSED(stbi__get32be(s)); STBI_NOTUSED(stbi__get32be(s)); depth = stbi__get16be(s); if (depth != 16) { stbi__rewind( s ); return 0; } 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) #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); ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); if (ri->bits_per_channel == 0) return 0; if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); *x = s->img_x; *y = s->img_y; if (comp) *comp = s->img_n; if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) return stbi__errpuc("too large", "PNM too large"); out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { STBI_FREE(out); return stbi__errpuc("bad PNM", "PNM file truncated"); } if (req_comp && req_comp != s->img_n) { if (ri->bits_per_channel == 16) { out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); } else { 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); if((value > 214748364) || (value == 214748364 && *c > '7')) return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); } 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 if(*x == 0) return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); *y = stbi__pnm_getinteger(s, &c); // read height if (*y == 0) return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); maxv = stbi__pnm_getinteger(s, &c); // read max value if (maxv > 65535) return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); else if (maxv > 255) return 16; else return 8; } static int stbi__pnm_is16(stbi__context *s) { if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) return 1; return 0; } #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"); } static int stbi__is_16_main(stbi__context *s) { #ifndef STBI_NO_PNG if (stbi__png_is16(s)) return 1; #endif #ifndef STBI_NO_PSD if (stbi__psd_is16(s)) return 1; #endif #ifndef STBI_NO_PNM if (stbi__pnm_is16(s)) return 1; #endif return 0; } #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; } STBIDEF int stbi_is_16_bit(char const *filename) { FILE *f = stbi__fopen(filename, "rb"); int result; if (!f) return stbi__err("can't fopen", "Unable to open file"); result = stbi_is_16_bit_from_file(f); fclose(f); return result; } STBIDEF int stbi_is_16_bit_from_file(FILE *f) { int r; stbi__context s; long pos = ftell(f); stbi__start_file(&s, f); r = stbi__is_16_main(&s); 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); } STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__is_16_main(&s); } STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); return stbi__is_16_main(&s); } #endif // STB_IMAGE_IMPLEMENTATION /* revision history: 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug 1-bit BMP *_is_16_bit api avoid warnings 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_8_40/src/client/refresh/files/stb_image_resize.h000066400000000000000000003434441465112212000244160ustar00rootroot00000000000000/* stb_image_resize - v0.97 - public domain image resizing by Jorge L Rodriguez (@VinoBS) - 2014 http://github.com/nothings/stb Written with emphasis on usability, portability, and efficiency. (No SIMD or threads, so it be easily outperformed by libs that use those.) Only scaling and translation is supported, no rotations or shears. Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation. COMPILING & LINKING In one C/C++ file that #includes this file, do this: #define STB_IMAGE_RESIZE_IMPLEMENTATION before the #include. That will create the implementation in that file. QUICKSTART stbir_resize_uint8( input_pixels , in_w , in_h , 0, output_pixels, out_w, out_h, 0, num_channels) stbir_resize_float(...) stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0, output_pixels, out_w, out_h, 0, num_channels , alpha_chan , 0) stbir_resize_uint8_srgb_edgemode( input_pixels , in_w , in_h , 0, output_pixels, out_w, out_h, 0, num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) // WRAP/REFLECT/ZERO FULL API See the "header file" section of the source for API documentation. ADDITIONAL DOCUMENTATION SRGB & FLOATING POINT REPRESENTATION The sRGB functions presume IEEE floating point. If you do not have IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use a slower implementation. MEMORY ALLOCATION The resize functions here perform a single memory allocation using malloc. To control the memory allocation, before the #include that triggers the implementation, do: #define STBIR_MALLOC(size,context) ... #define STBIR_FREE(ptr,context) ... Each resize function makes exactly one call to malloc/free, so to use temp memory, store the temp memory in the context and return that. ASSERT Define STBIR_ASSERT(boolval) to override assert() and not use assert.h OPTIMIZATION Define STBIR_SATURATE_INT to compute clamp values in-range using integer operations instead of float operations. This may be faster on some platforms. DEFAULT FILTERS For functions which don't provide explicit control over what filters to use, you can change the compile-time defaults with #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something See stbir_filter in the header-file section for the list of filters. NEW FILTERS A number of 1D filter kernels are used. For a list of supported filters see the stbir_filter enum. To add a new filter, write a filter function and add it to stbir__filter_info_table. PROGRESS For interactive use with slow resize operations, you can install a progress-report callback: #define STBIR_PROGRESS_REPORT(val) some_func(val) The parameter val is a float which goes from 0 to 1 as progress is made. For example: static void my_progress_report(float progress); #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) #define STB_IMAGE_RESIZE_IMPLEMENTATION #include "stb_image_resize.h" static void my_progress_report(float progress) { printf("Progress: %f%%\n", progress*100); } MAX CHANNELS If your image has more than 64 channels, define STBIR_MAX_CHANNELS to the max you'll have. ALPHA CHANNEL Most of the resizing functions provide the ability to control how the alpha channel of an image is processed. The important things to know about this: 1. The best mathematically-behaved version of alpha to use is called "premultiplied alpha", in which the other color channels have had the alpha value multiplied in. If you use premultiplied alpha, linear filtering (such as image resampling done by this library, or performed in texture units on GPUs) does the "right thing". While premultiplied alpha is standard in the movie CGI industry, it is still uncommon in the videogame/real-time world. If you linearly filter non-premultiplied alpha, strange effects occur. (For example, the 50/50 average of 99% transparent bright green and 1% transparent black produces 50% transparent dark green when non-premultiplied, whereas premultiplied it produces 50% transparent near-black. The former introduces green energy that doesn't exist in the source image.) 2. Artists should not edit premultiplied-alpha images; artists want non-premultiplied alpha images. Thus, art tools generally output non-premultiplied alpha images. 3. You will get best results in most cases by converting images to premultiplied alpha before processing them mathematically. 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the resizer does not do anything special for the alpha channel; it is resampled identically to other channels. This produces the correct results for premultiplied-alpha images, but produces less-than-ideal results for non-premultiplied-alpha images. 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, then the resizer weights the contribution of input pixels based on their alpha values, or, equivalently, it multiplies the alpha value into the color channels, resamples, then divides by the resultant alpha value. Input pixels which have alpha=0 do not contribute at all to output pixels unless _all_ of the input pixels affecting that output pixel have alpha=0, in which case the result for that pixel is the same as it would be without STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for input images in integer formats. For input images in float format, input pixels with alpha=0 have no effect, and output pixels which have alpha=0 will be 0 in all channels. (For float images, you can manually achieve the same result by adding a tiny epsilon value to the alpha channel of every image, and then subtracting or clamping it at the end.) 6. You can suppress the behavior described in #5 and make all-0-alpha pixels have 0 in all channels by #defining STBIR_NO_ALPHA_EPSILON. 7. You can separately control whether the alpha channel is interpreted as linear or affected by the colorspace. By default it is linear; you almost never want to apply the colorspace. (For example, graphics hardware does not apply sRGB conversion to the alpha channel.) CONTRIBUTORS Jorge L Rodriguez: Implementation Sean Barrett: API design, optimizations Aras Pranckevicius: bugfix Nathan Reed: warning fixes REVISIONS 0.97 (2020-02-02) fixed warning 0.96 (2019-03-04) fixed warnings 0.95 (2017-07-23) fixed warnings 0.94 (2017-03-18) fixed warnings 0.93 (2017-03-03) fixed bug with certain combinations of heights 0.92 (2017-01-02) fix integer overflow on large (>2GB) images 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions 0.90 (2014-09-17) first released version LICENSE See end of file for license information. TODO Don't decode all of the image data when only processing a partial tile Don't use full-width decode buffers when only processing a partial tile When processing wide images, break processing into tiles so data fits in L1 cache Installable filters? Resize that respects alpha test coverage (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp ) */ #ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H #define STBIR_INCLUDE_STB_IMAGE_RESIZE_H #ifdef _MSC_VER typedef unsigned char stbir_uint8; typedef unsigned short stbir_uint16; typedef unsigned int stbir_uint32; #else #include typedef uint8_t stbir_uint8; typedef uint16_t stbir_uint16; typedef uint32_t stbir_uint32; #endif #ifndef STBIRDEF #ifdef STB_IMAGE_RESIZE_STATIC #define STBIRDEF static #else #ifdef __cplusplus #define STBIRDEF extern "C" #else #define STBIRDEF extern #endif #endif #endif ////////////////////////////////////////////////////////////////////////////// // // Easy-to-use API: // // * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4) // * input_w is input image width (x-axis), input_h is input image height (y-axis) // * stride is the offset between successive rows of image data in memory, in bytes. you can // specify 0 to mean packed continuously in memory // * alpha channel is treated identically to other channels. // * colorspace is linear or sRGB as specified by function name // * returned result is 1 for success or 0 in case of an error. // #define STBIR_ASSERT() to trigger an assert on parameter validation errors. // * Memory required grows approximately linearly with input and output size, but with // discontinuities at input_w == output_w and input_h == output_h. // * These functions use a "default" resampling filter defined at compile time. To change the filter, // you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE // and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API. STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels); STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels); // The following functions interpret image data as gamma-corrected sRGB. // Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel, // or otherwise provide the index of the alpha channel. Flags value // of 0 will probably do the right thing if you're not sure what // the flags mean. #define STBIR_ALPHA_CHANNEL_NONE -1 // Set this flag if your texture has premultiplied alpha. Otherwise, stbir will // use alpha-weighted resampling (effectively premultiplying, resampling, // then unpremultiplying). #define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0) // The specified alpha channel should be handled as gamma-corrected value even // when doing sRGB operations. #define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags); typedef enum { STBIR_EDGE_CLAMP = 1, STBIR_EDGE_REFLECT = 2, STBIR_EDGE_WRAP = 3, STBIR_EDGE_ZERO = 4, } stbir_edge; // This function adds the ability to specify how requests to sample off the edge of the image are handled. STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode); ////////////////////////////////////////////////////////////////////////////// // // Medium-complexity API // // This extends the easy-to-use API as follows: // // * Alpha-channel can be processed separately // * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE // * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT) // * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED) // * Filter can be selected explicitly // * uint16 image type // * sRGB colorspace available for all types // * context parameter for passing to STBIR_MALLOC typedef enum { STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 } stbir_filter; typedef enum { STBIR_COLORSPACE_LINEAR, STBIR_COLORSPACE_SRGB, STBIR_MAX_COLORSPACES, } stbir_colorspace; // The following functions are all identical except for the type of the image data STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context); STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context); STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context); ////////////////////////////////////////////////////////////////////////////// // // Full-complexity API // // This extends the medium API as follows: // // * uint32 image type // * not typesafe // * separate filter types for each axis // * separate edge modes for each axis // * can specify scale explicitly for subpixel correctness // * can specify image source tile using texture coordinates typedef enum { STBIR_TYPE_UINT8 , STBIR_TYPE_UINT16, STBIR_TYPE_UINT32, STBIR_TYPE_FLOAT , STBIR_MAX_TYPES } stbir_datatype; STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context); STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context, float x_scale, float y_scale, float x_offset, float y_offset); STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context, float s0, float t0, float s1, float t1); // (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use. // // //// end header file ///////////////////////////////////////////////////// #endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H #ifdef STB_IMAGE_RESIZE_IMPLEMENTATION #ifndef STBIR_ASSERT #include #define STBIR_ASSERT(x) assert(x) #endif // For memset #include #include #ifndef STBIR_MALLOC #include // use comma operator to evaluate c, to avoid "unused parameter" warnings #define STBIR_MALLOC(size,c) ((void)(c), malloc(size)) #define STBIR_FREE(ptr,c) ((void)(c), free(ptr)) #endif #ifndef _MSC_VER #ifdef __cplusplus #define stbir__inline inline #else #define stbir__inline #endif #else #define stbir__inline __forceinline #endif // should produce compiler error if size is wrong typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]; #ifdef _MSC_VER #define STBIR__NOTUSED(v) (void)(v) #else #define STBIR__NOTUSED(v) (void)sizeof(v) #endif #define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) #ifndef STBIR_DEFAULT_FILTER_UPSAMPLE #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM #endif #ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL #endif #ifndef STBIR_PROGRESS_REPORT #define STBIR_PROGRESS_REPORT(float_0_to_1) #endif #ifndef STBIR_MAX_CHANNELS #define STBIR_MAX_CHANNELS 64 #endif #if STBIR_MAX_CHANNELS > 65536 #error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536." // because we store the indices in 16-bit variables #endif // This value is added to alpha just before premultiplication to avoid // zeroing out color values. It is equivalent to 2^-80. If you don't want // that behavior (it may interfere if you have floating point images with // very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to // disable it. #ifndef STBIR_ALPHA_EPSILON #define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) #endif #ifdef _MSC_VER #define STBIR__UNUSED_PARAM(v) (void)(v) #else #define STBIR__UNUSED_PARAM(v) (void)sizeof(v) #endif // must match stbir_datatype static unsigned char stbir__type_size[] = { 1, // STBIR_TYPE_UINT8 2, // STBIR_TYPE_UINT16 4, // STBIR_TYPE_UINT32 4, // STBIR_TYPE_FLOAT }; // Kernel function centered at 0 typedef float (stbir__kernel_fn)(float x, float scale); typedef float (stbir__support_fn)(float scale); typedef struct { stbir__kernel_fn* kernel; stbir__support_fn* support; } stbir__filter_info; // When upsampling, the contributors are which source pixels contribute. // When downsampling, the contributors are which destination pixels are contributed to. typedef struct { int n0; // First contributing pixel int n1; // Last contributing pixel } stbir__contributors; typedef struct { const void* input_data; int input_w; int input_h; int input_stride_bytes; void* output_data; int output_w; int output_h; int output_stride_bytes; float s0, t0, s1, t1; float horizontal_shift; // Units: output pixels float vertical_shift; // Units: output pixels float horizontal_scale; float vertical_scale; int channels; int alpha_channel; stbir_uint32 flags; stbir_datatype type; stbir_filter horizontal_filter; stbir_filter vertical_filter; stbir_edge edge_horizontal; stbir_edge edge_vertical; stbir_colorspace colorspace; stbir__contributors* horizontal_contributors; float* horizontal_coefficients; stbir__contributors* vertical_contributors; float* vertical_coefficients; int decode_buffer_pixels; float* decode_buffer; float* horizontal_buffer; // cache these because ceil/floor are inexplicably showing up in profile int horizontal_coefficient_width; int vertical_coefficient_width; int horizontal_filter_pixel_width; int vertical_filter_pixel_width; int horizontal_filter_pixel_margin; int vertical_filter_pixel_margin; int horizontal_num_contributors; int vertical_num_contributors; int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) int ring_buffer_num_entries; // Total number of entries in the ring buffer. int ring_buffer_first_scanline; int ring_buffer_last_scanline; int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer float* ring_buffer; float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. int horizontal_contributors_size; int horizontal_coefficients_size; int vertical_contributors_size; int vertical_coefficients_size; int decode_buffer_size; int horizontal_buffer_size; int ring_buffer_size; int encode_buffer_size; } stbir__info; static const float stbir__max_uint8_as_float = 255.0f; static const float stbir__max_uint16_as_float = 65535.0f; static const double stbir__max_uint32_as_float = 4294967295.0; static stbir__inline int stbir__min(int a, int b) { return a < b ? a : b; } static stbir__inline float stbir__saturate(float x) { if (x < 0) return 0; if (x > 1) return 1; return x; } #ifdef STBIR_SATURATE_INT static stbir__inline stbir_uint8 stbir__saturate8(int x) { if ((unsigned int) x <= 255) return x; if (x < 0) return 0; return 255; } static stbir__inline stbir_uint16 stbir__saturate16(int x) { if ((unsigned int) x <= 65535) return x; if (x < 0) return 0; return 65535; } #endif static float stbir__srgb_uchar_to_linear_float[256] = { 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, 0.982251f, 0.991102f, 1.0f }; static float stbir__srgb_to_linear(float f) { if (f <= 0.04045f) return f / 12.92f; else return (float)pow((f + 0.055f) / 1.055f, 2.4f); } static float stbir__linear_to_srgb(float f) { if (f <= 0.0031308f) return f * 12.92f; else return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; } #ifndef STBIR_NON_IEEE_FLOAT // From https://gist.github.com/rygorous/2203834 typedef union { stbir_uint32 u; float f; } stbir__FP32; static const stbir_uint32 fp32_to_srgb8_tab4[104] = { 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, }; static stbir_uint8 stbir__linear_to_srgb_uchar(float in) { static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps static const stbir__FP32 minval = { (127-13) << 23 }; stbir_uint32 tab,bias,scale,t; stbir__FP32 f; // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. // The tests are carefully written so that NaNs map to 0, same as in the reference // implementation. if (!(in > minval.f)) // written this way to catch NaNs in = minval.f; if (in > almostone.f) in = almostone.f; // Do the table lookup and unpack bias, scale f.f = in; tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; bias = (tab >> 16) << 9; scale = tab & 0xffff; // Grab next-highest mantissa bits and perform linear interpolation t = (f.u >> 12) & 0xff; return (unsigned char) ((bias + scale*t) >> 16); } #else // sRGB transition values, scaled by 1<<28 static int stbir__srgb_offset_to_linear_scaled[256] = { 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926, 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148, 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856, 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731, 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369, 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021, 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073, 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389, 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552, 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066, 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490, 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568, 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316, 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096, 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700, 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376, 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912, 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648, 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512, 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072, 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544, 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832, 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528, 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968, 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184, 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992, 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968, 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480, 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656, 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464, 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664, }; static stbir_uint8 stbir__linear_to_srgb_uchar(float f) { int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp int v = 0; int i; // Refine the guess with a short binary search. i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; return (stbir_uint8) v; } #endif static float stbir__filter_trapezoid(float x, float scale) { float halfscale = scale / 2; float t = 0.5f + halfscale; STBIR_ASSERT(scale <= 1); x = (float)fabs(x); if (x >= t) return 0; else { float r = 0.5f - halfscale; if (x <= r) return 1; else return (t - x) / scale; } } static float stbir__support_trapezoid(float scale) { STBIR_ASSERT(scale <= 1); return 0.5f + scale / 2; } static float stbir__filter_triangle(float x, float s) { STBIR__UNUSED_PARAM(s); x = (float)fabs(x); if (x <= 1.0f) return 1 - x; else return 0; } static float stbir__filter_cubic(float x, float s) { STBIR__UNUSED_PARAM(s); x = (float)fabs(x); if (x < 1.0f) return (4 + x*x*(3*x - 6))/6; else if (x < 2.0f) return (8 + x*(-12 + x*(6 - x)))/6; return (0.0f); } static float stbir__filter_catmullrom(float x, float s) { STBIR__UNUSED_PARAM(s); x = (float)fabs(x); if (x < 1.0f) return 1 - x*x*(2.5f - 1.5f*x); else if (x < 2.0f) return 2 - x*(4 + x*(0.5f*x - 2.5f)); return (0.0f); } static float stbir__filter_mitchell(float x, float s) { STBIR__UNUSED_PARAM(s); x = (float)fabs(x); if (x < 1.0f) return (16 + x*x*(21 * x - 36))/18; else if (x < 2.0f) return (32 + x*(-60 + x*(36 - 7*x)))/18; return (0.0f); } static float stbir__support_zero(float s) { STBIR__UNUSED_PARAM(s); return 0; } static float stbir__support_one(float s) { STBIR__UNUSED_PARAM(s); return 1; } static float stbir__support_two(float s) { STBIR__UNUSED_PARAM(s); return 2; } static stbir__filter_info stbir__filter_info_table[] = { { NULL, stbir__support_zero }, { stbir__filter_trapezoid, stbir__support_trapezoid }, { stbir__filter_triangle, stbir__support_one }, { stbir__filter_cubic, stbir__support_two }, { stbir__filter_catmullrom, stbir__support_two }, { stbir__filter_mitchell, stbir__support_two }, }; stbir__inline static int stbir__use_upsampling(float ratio) { return ratio > 1; } stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info) { return stbir__use_upsampling(stbir_info->horizontal_scale); } stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) { return stbir__use_upsampling(stbir_info->vertical_scale); } // This is the maximum number of input samples that can affect an output sample // with the given filter static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) { STBIR_ASSERT(filter != 0); STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); if (stbir__use_upsampling(scale)) return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); else return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); } // This is how much to expand buffers to account for filters seeking outside // the image boundaries. static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) { return stbir__get_filter_pixel_width(filter, scale) / 2; } static int stbir__get_coefficient_width(stbir_filter filter, float scale) { if (stbir__use_upsampling(scale)) return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); else return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); } static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) { if (stbir__use_upsampling(scale)) return output_size; else return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); } static int stbir__get_total_horizontal_coefficients(stbir__info* info) { return info->horizontal_num_contributors * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); } static int stbir__get_total_vertical_coefficients(stbir__info* info) { return info->vertical_num_contributors * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); } static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) { return &contributors[n]; } // For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, // if you change it here change it there too. static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) { int width = stbir__get_coefficient_width(filter, scale); return &coefficients[width*n + c]; } static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) { switch (edge) { case STBIR_EDGE_ZERO: return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later case STBIR_EDGE_CLAMP: if (n < 0) return 0; if (n >= max) return max - 1; return n; // NOTREACHED case STBIR_EDGE_REFLECT: { if (n < 0) { if (n < max) return -n; else return max - 1; } if (n >= max) { int max2 = max * 2; if (n >= max2) return 0; else return max2 - n - 1; } return n; // NOTREACHED } case STBIR_EDGE_WRAP: if (n >= 0) return (n % max); else { int m = (-n) % max; if (m != 0) m = max - m; return (m); } // NOTREACHED default: STBIR_ASSERT(!"Unimplemented edge type"); return 0; } } stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) { // avoid per-pixel switch if (n >= 0 && n < max) return n; return stbir__edge_wrap_slow(edge, n, max); } // What input pixels contribute to this output pixel? static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) { float out_pixel_center = (float)n + 0.5f; float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); } // What output pixels does this input pixel contribute to? static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) { float in_pixel_center = (float)n + 0.5f; float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; *out_center_of_in = in_pixel_center * scale_ratio - out_shift; *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); } static void stbir__calculate_coefficients_upsample(stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) { int i; float total_filter = 0; float filter_scale; STBIR_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. contributor->n0 = in_first_pixel; contributor->n1 = in_last_pixel; STBIR_ASSERT(contributor->n1 >= contributor->n0); for (i = 0; i <= in_last_pixel - in_first_pixel; i++) { float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) if (i == 0 && !coefficient_group[i]) { contributor->n0 = ++in_first_pixel; i--; continue; } total_filter += coefficient_group[i]; } // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be. // It would be true in exact math but is at best approximately true in floating-point math, // and it would not make sense to try and put actual bounds on this here because it depends // on the image aspect ratio which can get pretty extreme. //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); STBIR_ASSERT(total_filter > 0.9); STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. // Make sure the sum of all coefficients is 1. filter_scale = 1 / total_filter; for (i = 0; i <= in_last_pixel - in_first_pixel; i++) coefficient_group[i] *= filter_scale; for (i = in_last_pixel - in_first_pixel; i >= 0; i--) { if (coefficient_group[i]) break; // This line has no weight. We can skip it. contributor->n1 = contributor->n0 + i - 1; } } static void stbir__calculate_coefficients_downsample(stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) { int i; STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. contributor->n0 = out_first_pixel; contributor->n1 = out_last_pixel; STBIR_ASSERT(contributor->n1 >= contributor->n0); for (i = 0; i <= out_last_pixel - out_first_pixel; i++) { float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; float x = out_pixel_center - out_center_of_in; coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; } // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be. // It would be true in exact math but is at best approximately true in floating-point math, // and it would not make sense to try and put actual bounds on this here because it depends // on the image aspect ratio which can get pretty extreme. //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); for (i = out_last_pixel - out_first_pixel; i >= 0; i--) { if (coefficient_group[i]) break; // This line has no weight. We can skip it. contributor->n1 = contributor->n0 + i - 1; } } static void stbir__normalize_downsample_coefficients(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, int input_size, int output_size) { int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); int i, j; int skip; for (i = 0; i < output_size; i++) { float scale; float total = 0; for (j = 0; j < num_contributors; j++) { if (i >= contributors[j].n0 && i <= contributors[j].n1) { float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); total += coefficient; } else if (i < contributors[j].n0) break; } STBIR_ASSERT(total > 0.9f); STBIR_ASSERT(total < 1.1f); scale = 1 / total; for (j = 0; j < num_contributors; j++) { if (i >= contributors[j].n0 && i <= contributors[j].n1) *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; else if (i < contributors[j].n0) break; } } // Optimize: Skip zero coefficients and contributions outside of image bounds. // Do this after normalizing because normalization depends on the n0/n1 values. for (j = 0; j < num_contributors; j++) { int range, max, width; skip = 0; while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) skip++; contributors[j].n0 += skip; while (contributors[j].n0 < 0) { contributors[j].n0++; skip++; } range = contributors[j].n1 - contributors[j].n0 + 1; max = stbir__min(num_coefficients, range); width = stbir__get_coefficient_width(filter, scale_ratio); for (i = 0; i < max; i++) { if (i + skip >= width) break; *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); } continue; } // Using min to avoid writing into invalid pixels. for (i = 0; i < num_contributors; i++) contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); } // Each scan line uses the same kernel values so we should calculate the kernel // values once and then we can use them for every scan line. static void stbir__calculate_filters(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) { int n; int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); if (stbir__use_upsampling(scale_ratio)) { float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; // Looping through out pixels for (n = 0; n < total_contributors; n++) { float in_center_of_out; // Center of the current out pixel in the in pixel space int in_first_pixel, in_last_pixel; stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); stbir__calculate_coefficients_upsample(filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); } } else { float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; // Looping through in pixels for (n = 0; n < total_contributors; n++) { float out_center_of_in; // Center of the current out pixel in the in pixel space int out_first_pixel, out_last_pixel; int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); stbir__calculate_coefficients_downsample(filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); } stbir__normalize_downsample_coefficients(contributors, coefficients, filter, scale_ratio, input_size, output_size); } } static float* stbir__get_decode_buffer(stbir__info* stbir_info) { // The 0 index of the decode buffer starts after the margin. This makes // it okay to use negative indexes on the decode buffer. return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels]; } #define STBIR__DECODE(type, colorspace) ((int)(type) * (STBIR_MAX_COLORSPACES) + (int)(colorspace)) static void stbir__decode_scanline(stbir__info* stbir_info, int n) { int c; int channels = stbir_info->channels; int alpha_channel = stbir_info->alpha_channel; int type = stbir_info->type; int colorspace = stbir_info->colorspace; int input_w = stbir_info->input_w; size_t input_stride_bytes = stbir_info->input_stride_bytes; float* decode_buffer = stbir__get_decode_buffer(stbir_info); stbir_edge edge_horizontal = stbir_info->edge_horizontal; stbir_edge edge_vertical = stbir_info->edge_vertical; size_t in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; int decode = STBIR__DECODE(type, colorspace); int x = -stbir_info->horizontal_filter_pixel_margin; // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) { for (; x < max_x; x++) for (c = 0; c < channels; c++) decode_buffer[x*channels + c] = 0; return; } switch (decode) { case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): for (; x < max_x; x++) { int decode_pixel_index = x * channels; int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / stbir__max_uint8_as_float; } break; case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): for (; x < max_x; x++) { int decode_pixel_index = x * channels; int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint8_as_float; } break; case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): for (; x < max_x; x++) { int decode_pixel_index = x * channels; int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float; } break; case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): for (; x < max_x; x++) { int decode_pixel_index = x * channels; int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float); if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint16_as_float; } break; case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): for (; x < max_x; x++) { int decode_pixel_index = x * channels; int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float); } break; case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): for (; x < max_x; x++) { int decode_pixel_index = x * channels; int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float)); if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint32_as_float); } break; case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): for (; x < max_x; x++) { int decode_pixel_index = x * channels; int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; } break; case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): for (; x < max_x; x++) { int decode_pixel_index = x * channels; int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; for (c = 0; c < channels; c++) decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; } break; default: STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); break; } if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) { for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++) { int decode_pixel_index = x * channels; // If the alpha value is 0 it will clobber the color values. Make sure it's not. float alpha = decode_buffer[decode_pixel_index + alpha_channel]; #ifndef STBIR_NO_ALPHA_EPSILON if (stbir_info->type != STBIR_TYPE_FLOAT) { alpha += STBIR_ALPHA_EPSILON; decode_buffer[decode_pixel_index + alpha_channel] = alpha; } #endif for (c = 0; c < channels; c++) { if (c == alpha_channel) continue; decode_buffer[decode_pixel_index + c] *= alpha; } } } if (edge_horizontal == STBIR_EDGE_ZERO) { for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++) { for (c = 0; c < channels; c++) decode_buffer[x*channels + c] = 0; } for (x = input_w; x < max_x; x++) { for (c = 0; c < channels; c++) decode_buffer[x*channels + c] = 0; } } } static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) { return &ring_buffer[index * ring_buffer_length]; } static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) { int ring_buffer_index; float* ring_buffer; stbir_info->ring_buffer_last_scanline = n; if (stbir_info->ring_buffer_begin_index < 0) { ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; stbir_info->ring_buffer_first_scanline = n; } else { ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); } ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); return ring_buffer; } static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, float* output_buffer) { int x, k; int output_w = stbir_info->output_w; int channels = stbir_info->channels; float* decode_buffer = stbir__get_decode_buffer(stbir_info); stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; float* horizontal_coefficients = stbir_info->horizontal_coefficients; int coefficient_width = stbir_info->horizontal_coefficient_width; for (x = 0; x < output_w; x++) { int n0 = horizontal_contributors[x].n0; int n1 = horizontal_contributors[x].n1; int out_pixel_index = x * channels; int coefficient_group = coefficient_width * x; int coefficient_counter = 0; STBIR_ASSERT(n1 >= n0); STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin); STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin); STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); switch (channels) { case 1: for (k = n0; k <= n1; k++) { int in_pixel_index = k * 1; float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; } break; case 2: for (k = n0; k <= n1; k++) { int in_pixel_index = k * 2; float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; } break; case 3: for (k = n0; k <= n1; k++) { int in_pixel_index = k * 3; float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; } break; case 4: for (k = n0; k <= n1; k++) { int in_pixel_index = k * 4; float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; } break; default: for (k = n0; k <= n1; k++) { int in_pixel_index = k * channels; float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; int c; STBIR_ASSERT(coefficient != 0); for (c = 0; c < channels; c++) output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; } break; } } } static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float* output_buffer) { int x, k; int input_w = stbir_info->input_w; int channels = stbir_info->channels; float* decode_buffer = stbir__get_decode_buffer(stbir_info); stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; float* horizontal_coefficients = stbir_info->horizontal_coefficients; int coefficient_width = stbir_info->horizontal_coefficient_width; int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin; int max_x = input_w + filter_pixel_margin * 2; STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info)); switch (channels) { case 1: for (x = 0; x < max_x; x++) { int n0 = horizontal_contributors[x].n0; int n1 = horizontal_contributors[x].n1; int in_x = x - filter_pixel_margin; int in_pixel_index = in_x * 1; int max_n = n1; int coefficient_group = coefficient_width * x; for (k = n0; k <= max_n; k++) { int out_pixel_index = k * 1; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; } } break; case 2: for (x = 0; x < max_x; x++) { int n0 = horizontal_contributors[x].n0; int n1 = horizontal_contributors[x].n1; int in_x = x - filter_pixel_margin; int in_pixel_index = in_x * 2; int max_n = n1; int coefficient_group = coefficient_width * x; for (k = n0; k <= max_n; k++) { int out_pixel_index = k * 2; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; } } break; case 3: for (x = 0; x < max_x; x++) { int n0 = horizontal_contributors[x].n0; int n1 = horizontal_contributors[x].n1; int in_x = x - filter_pixel_margin; int in_pixel_index = in_x * 3; int max_n = n1; int coefficient_group = coefficient_width * x; for (k = n0; k <= max_n; k++) { int out_pixel_index = k * 3; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; } } break; case 4: for (x = 0; x < max_x; x++) { int n0 = horizontal_contributors[x].n0; int n1 = horizontal_contributors[x].n1; int in_x = x - filter_pixel_margin; int in_pixel_index = in_x * 4; int max_n = n1; int coefficient_group = coefficient_width * x; for (k = n0; k <= max_n; k++) { int out_pixel_index = k * 4; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; } } break; default: for (x = 0; x < max_x; x++) { int n0 = horizontal_contributors[x].n0; int n1 = horizontal_contributors[x].n1; int in_x = x - filter_pixel_margin; int in_pixel_index = in_x * channels; int max_n = n1; int coefficient_group = coefficient_width * x; for (k = n0; k <= max_n; k++) { int c; int out_pixel_index = k * channels; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; for (c = 0; c < channels; c++) output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; } } break; } } static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n) { // Decode the nth scanline from the source image into the decode buffer. stbir__decode_scanline(stbir_info, n); // Now resample it into the ring buffer. if (stbir__use_width_upsampling(stbir_info)) stbir__resample_horizontal_upsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); else stbir__resample_horizontal_downsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. } static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n) { // Decode the nth scanline from the source image into the decode buffer. stbir__decode_scanline(stbir_info, n); memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); // Now resample it into the horizontal buffer. if (stbir__use_width_upsampling(stbir_info)) stbir__resample_horizontal_upsample(stbir_info, stbir_info->horizontal_buffer); else stbir__resample_horizontal_downsample(stbir_info, stbir_info->horizontal_buffer); // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. } // Get the specified scan line from the ring buffer. static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_num_entries, int ring_buffer_length) { int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_num_entries; return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); } static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode) { int x; int n; int num_nonalpha; stbir_uint16 nonalpha[STBIR_MAX_CHANNELS]; if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) { for (x=0; x < num_pixels; ++x) { int pixel_index = x*channels; float alpha = encode_buffer[pixel_index + alpha_channel]; float reciprocal_alpha = alpha ? 1.0f / alpha : 0; // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb for (n = 0; n < channels; n++) if (n != alpha_channel) encode_buffer[pixel_index + n] *= reciprocal_alpha; // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. // Because we only add it for integer types, it will automatically be discarded on integer // conversion, so we don't need to subtract it back out (which would be problematic for // numeric precision reasons). } } // build a table of all channels that need colorspace correction, so // we don't perform colorspace correction on channels that don't need it. for (x = 0, num_nonalpha = 0; x < channels; ++x) { if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) { nonalpha[num_nonalpha++] = (stbir_uint16)x; } } #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5)) #ifdef STBIR__SATURATE_INT #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * stbir__max_uint8_as_float )) #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * stbir__max_uint16_as_float)) #else #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint8_as_float ) #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint16_as_float) #endif switch (decode) { case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): for (x=0; x < num_pixels; ++x) { int pixel_index = x*channels; for (n = 0; n < channels; n++) { int index = pixel_index + n; ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]); } } break; case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): for (x=0; x < num_pixels; ++x) { int pixel_index = x*channels; for (n = 0; n < num_nonalpha; n++) { int index = pixel_index + nonalpha[n]; ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); } if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]); } break; case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): for (x=0; x < num_pixels; ++x) { int pixel_index = x*channels; for (n = 0; n < channels; n++) { int index = pixel_index + n; ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]); } } break; case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): for (x=0; x < num_pixels; ++x) { int pixel_index = x*channels; for (n = 0; n < num_nonalpha; n++) { int index = pixel_index + nonalpha[n]; ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * stbir__max_uint16_as_float); } if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]); } break; case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): for (x=0; x < num_pixels; ++x) { int pixel_index = x*channels; for (n = 0; n < channels; n++) { int index = pixel_index + n; ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * stbir__max_uint32_as_float); } } break; case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): for (x=0; x < num_pixels; ++x) { int pixel_index = x*channels; for (n = 0; n < num_nonalpha; n++) { int index = pixel_index + nonalpha[n]; ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * stbir__max_uint32_as_float); } if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * stbir__max_uint32_as_float); } break; case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): for (x=0; x < num_pixels; ++x) { int pixel_index = x*channels; for (n = 0; n < channels; n++) { int index = pixel_index + n; ((float*)output_buffer)[index] = encode_buffer[index]; } } break; case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): for (x=0; x < num_pixels; ++x) { int pixel_index = x*channels; for (n = 0; n < num_nonalpha; n++) { int index = pixel_index + nonalpha[n]; ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); } if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; } break; default: STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); break; } } static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n) { int x, k; int output_w = stbir_info->output_w; stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; float* vertical_coefficients = stbir_info->vertical_coefficients; int channels = stbir_info->channels; int alpha_channel = stbir_info->alpha_channel; int type = stbir_info->type; int colorspace = stbir_info->colorspace; int ring_buffer_entries = stbir_info->ring_buffer_num_entries; void* output_data = stbir_info->output_data; float* encode_buffer = stbir_info->encode_buffer; int decode = STBIR__DECODE(type, colorspace); int coefficient_width = stbir_info->vertical_coefficient_width; int coefficient_counter; int contributor = n; float* ring_buffer = stbir_info->ring_buffer; int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); int n0,n1, output_row_start; int coefficient_group = coefficient_width * contributor; n0 = vertical_contributors[contributor].n0; n1 = vertical_contributors[contributor].n1; output_row_start = n * stbir_info->output_stride_bytes; STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); memset(encode_buffer, 0, output_w * sizeof(float) * channels); // I tried reblocking this for better cache usage of encode_buffer // (using x_outer, k, x_inner), but it lost speed. -- stb coefficient_counter = 0; switch (channels) { case 1: for (k = n0; k <= n1; k++) { int coefficient_index = coefficient_counter++; float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; for (x = 0; x < output_w; ++x) { int in_pixel_index = x * 1; encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; } } break; case 2: for (k = n0; k <= n1; k++) { int coefficient_index = coefficient_counter++; float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; for (x = 0; x < output_w; ++x) { int in_pixel_index = x * 2; encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; } } break; case 3: for (k = n0; k <= n1; k++) { int coefficient_index = coefficient_counter++; float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; for (x = 0; x < output_w; ++x) { int in_pixel_index = x * 3; encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; } } break; case 4: for (k = n0; k <= n1; k++) { int coefficient_index = coefficient_counter++; float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; for (x = 0; x < output_w; ++x) { int in_pixel_index = x * 4; encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient; } } break; default: for (k = n0; k <= n1; k++) { int coefficient_index = coefficient_counter++; float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; for (x = 0; x < output_w; ++x) { int in_pixel_index = x * channels; int c; for (c = 0; c < channels; c++) encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; } } break; } stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); } static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n) { int x, k; int output_w = stbir_info->output_w; stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; float* vertical_coefficients = stbir_info->vertical_coefficients; int channels = stbir_info->channels; int ring_buffer_entries = stbir_info->ring_buffer_num_entries; float* horizontal_buffer = stbir_info->horizontal_buffer; int coefficient_width = stbir_info->vertical_coefficient_width; int contributor = n + stbir_info->vertical_filter_pixel_margin; float* ring_buffer = stbir_info->ring_buffer; int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); int n0,n1; n0 = vertical_contributors[contributor].n0; n1 = vertical_contributors[contributor].n1; STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); for (k = n0; k <= n1; k++) { int coefficient_index = k - n0; int coefficient_group = coefficient_width * contributor; float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); switch (channels) { case 1: for (x = 0; x < output_w; x++) { int in_pixel_index = x * 1; ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; } break; case 2: for (x = 0; x < output_w; x++) { int in_pixel_index = x * 2; ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; } break; case 3: for (x = 0; x < output_w; x++) { int in_pixel_index = x * 3; ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; } break; case 4: for (x = 0; x < output_w; x++) { int in_pixel_index = x * 4; ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient; } break; default: for (x = 0; x < output_w; x++) { int in_pixel_index = x * channels; int c; for (c = 0; c < channels; c++) ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; } break; } } } static void stbir__buffer_loop_upsample(stbir__info* stbir_info) { int y; float scale_ratio = stbir_info->vertical_scale; float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); for (y = 0; y < stbir_info->output_h; y++) { float in_center_of_out = 0; // Center of the current out scanline in the in scanline space int in_first_scanline = 0, in_last_scanline = 0; stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); STBIR_ASSERT(in_last_scanline - in_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); if (stbir_info->ring_buffer_begin_index >= 0) { // Get rid of whatever we don't need anymore. while (in_first_scanline > stbir_info->ring_buffer_first_scanline) { if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) { // We just popped the last scanline off the ring buffer. // Reset it to the empty state. stbir_info->ring_buffer_begin_index = -1; stbir_info->ring_buffer_first_scanline = 0; stbir_info->ring_buffer_last_scanline = 0; break; } else { stbir_info->ring_buffer_first_scanline++; stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; } } } // Load in new ones. if (stbir_info->ring_buffer_begin_index < 0) stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); while (in_last_scanline > stbir_info->ring_buffer_last_scanline) stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); // Now all buffers should be ready to write a row of vertical sampling. stbir__resample_vertical_upsample(stbir_info, y); STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); } } static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) { int output_stride_bytes = stbir_info->output_stride_bytes; int channels = stbir_info->channels; int alpha_channel = stbir_info->alpha_channel; int type = stbir_info->type; int colorspace = stbir_info->colorspace; int output_w = stbir_info->output_w; void* output_data = stbir_info->output_data; int decode = STBIR__DECODE(type, colorspace); float* ring_buffer = stbir_info->ring_buffer; int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); if (stbir_info->ring_buffer_begin_index >= 0) { // Get rid of whatever we don't need anymore. while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) { if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) { int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); } if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) { // We just popped the last scanline off the ring buffer. // Reset it to the empty state. stbir_info->ring_buffer_begin_index = -1; stbir_info->ring_buffer_first_scanline = 0; stbir_info->ring_buffer_last_scanline = 0; break; } else { stbir_info->ring_buffer_first_scanline++; stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; } } } } static void stbir__buffer_loop_downsample(stbir__info* stbir_info) { int y; float scale_ratio = stbir_info->vertical_scale; int output_h = stbir_info->output_h; float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; int pixel_margin = stbir_info->vertical_filter_pixel_margin; int max_y = stbir_info->input_h + pixel_margin; STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); for (y = -pixel_margin; y < max_y; y++) { float out_center_of_in; // Center of the current out scanline in the in scanline space int out_first_scanline, out_last_scanline; stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); if (out_last_scanline < 0 || out_first_scanline >= output_h) continue; stbir__empty_ring_buffer(stbir_info, out_first_scanline); stbir__decode_and_resample_downsample(stbir_info, y); // Load in new ones. if (stbir_info->ring_buffer_begin_index < 0) stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); while (out_last_scanline > stbir_info->ring_buffer_last_scanline) stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); // Now the horizontal buffer is ready to write to all ring buffer rows. stbir__resample_vertical_downsample(stbir_info, y); } stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); } static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels) { info->input_w = input_w; info->input_h = input_h; info->output_w = output_w; info->output_h = output_h; info->channels = channels; } static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform) { info->s0 = s0; info->t0 = t0; info->s1 = s1; info->t1 = t1; if (transform) { info->horizontal_scale = transform[0]; info->vertical_scale = transform[1]; info->horizontal_shift = transform[2]; info->vertical_shift = transform[3]; } else { info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); info->horizontal_shift = s0 * info->output_w / (s1 - s0); info->vertical_shift = t0 * info->output_h / (t1 - t0); } } static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter) { if (h_filter == 0) h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; if (v_filter == 0) v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; info->horizontal_filter = h_filter; info->vertical_filter = v_filter; } static stbir_uint32 stbir__calculate_memory(stbir__info *info) { int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h); // One extra entry because floating point precision problems sometimes cause an extra to be necessary. info->ring_buffer_num_entries = filter_height + 1; info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors); info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors); info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); info->ring_buffer_size = info->output_w * info->channels * info->ring_buffer_num_entries * sizeof(float); info->encode_buffer_size = info->output_w * info->channels * sizeof(float); STBIR_ASSERT(info->horizontal_filter != 0); STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late STBIR_ASSERT(info->vertical_filter != 0); STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late if (stbir__use_height_upsampling(info)) // The horizontal buffer is for when we're downsampling the height and we // can't output the result of sampling the decode buffer directly into the // ring buffers. info->horizontal_buffer_size = 0; else // The encode buffer is to retain precision in the height upsampling method // and isn't used when height downsampling. info->encode_buffer_size = 0; return info->horizontal_contributors_size + info->horizontal_coefficients_size + info->vertical_contributors_size + info->vertical_coefficients_size + info->decode_buffer_size + info->horizontal_buffer_size + info->ring_buffer_size + info->encode_buffer_size; } static int stbir__resize_allocated(stbir__info *info, const void* input_data, int input_stride_in_bytes, void* output_data, int output_stride_in_bytes, int alpha_channel, stbir_uint32 flags, stbir_datatype type, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, void* tempmem, size_t tempmem_size_in_bytes) { size_t memory_required = stbir__calculate_memory(info); int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; #ifdef STBIR_DEBUG_OVERWRITE_TEST #define OVERWRITE_ARRAY_SIZE 8 unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); #endif STBIR_ASSERT(info->channels >= 0); STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS) return 0; STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) return 0; if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) return 0; if (alpha_channel < 0) flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) { STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); } if (alpha_channel >= info->channels) return 0; STBIR_ASSERT(tempmem); if (!tempmem) return 0; STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); if (tempmem_size_in_bytes < memory_required) return 0; memset(tempmem, 0, tempmem_size_in_bytes); info->input_data = input_data; info->input_stride_bytes = width_stride_input; info->output_data = output_data; info->output_stride_bytes = width_stride_output; info->alpha_channel = alpha_channel; info->flags = flags; info->type = type; info->edge_horizontal = edge_horizontal; info->edge_vertical = edge_vertical; info->colorspace = colorspace; info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale ); info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale ); info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale ); info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2; #define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) info->horizontal_contributors = (stbir__contributors *) tempmem; info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); if (stbir__use_height_upsampling(info)) { info->horizontal_buffer = NULL; info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } else { info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); info->encode_buffer = NULL; STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); } #undef STBIR__NEXT_MEMPTR // This signals that the ring buffer is empty info->ring_buffer_begin_index = -1; stbir__calculate_filters(info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); stbir__calculate_filters(info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); STBIR_PROGRESS_REPORT(0); if (stbir__use_height_upsampling(info)) stbir__buffer_loop_upsample(info); else stbir__buffer_loop_downsample(info); STBIR_PROGRESS_REPORT(1); #ifdef STBIR_DEBUG_OVERWRITE_TEST STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); #endif return 1; } static int stbir__resize_arbitrary( void *alloc_context, const void* input_data, int input_w, int input_h, int input_stride_in_bytes, void* output_data, int output_w, int output_h, int output_stride_in_bytes, float s0, float t0, float s1, float t1, float *transform, int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, stbir_filter h_filter, stbir_filter v_filter, stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) { stbir__info info; int result; size_t memory_required; void* extra_memory; stbir__setup(&info, input_w, input_h, output_w, output_h, channels); stbir__calculate_transform(&info, s0,t0,s1,t1,transform); stbir__choose_filter(&info, h_filter, v_filter); memory_required = stbir__calculate_memory(&info); extra_memory = STBIR_MALLOC(memory_required, alloc_context); if (!extra_memory) return 0; result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, output_data, output_stride_in_bytes, alpha_channel, flags, type, edge_horizontal, edge_vertical, colorspace, extra_memory, memory_required); STBIR_FREE(extra_memory, alloc_context); return result; } STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels) { return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); } STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels) { return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); } STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags) { return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); } STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode) { return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); } STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, edge_wrap_mode, edge_wrap_mode, space); } STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, edge_wrap_mode, edge_wrap_mode, space); } STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, void *alloc_context) { return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, edge_wrap_mode, edge_wrap_mode, space); } STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context) { return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, edge_mode_horizontal, edge_mode_vertical, space); } STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context, float x_scale, float y_scale, float x_offset, float y_offset) { float transform[4]; transform[0] = x_scale; transform[1] = y_scale; transform[2] = x_offset; transform[3] = y_offset; return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, edge_mode_horizontal, edge_mode_vertical, space); } STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, stbir_datatype datatype, int num_channels, int alpha_channel, int flags, stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, stbir_filter filter_horizontal, stbir_filter filter_vertical, stbir_colorspace space, void *alloc_context, float s0, float t0, float s1, float t1) { return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, output_h, output_stride_in_bytes, s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, edge_mode_horizontal, edge_mode_vertical, space); } #endif // STB_IMAGE_RESIZE_IMPLEMENTATION /* ------------------------------------------------------------------------------ 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_8_40/src/client/refresh/files/surf.c000066400000000000000000000100011465112212000220310ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 logic * * ======================================================================= */ #include "../ref_shared.h" /* =============== R_TextureAnimation Returns the proper texture for a given time and base texture =============== */ struct image_s * R_TextureAnimation(const entity_t *currententity, const mtexinfo_t *tex) { int c; if (!tex->next) return tex->image; if (!currententity) return tex->image; c = currententity->frame % tex->numframes; while (c && tex) { tex = tex->next; c--; } return tex->image; } qboolean R_AreaVisible(const byte *areabits, mleaf_t *pleaf) { int area; // check for door connected areas if (!areabits) return true; area = pleaf->area; if ((areabits[area >> 3] & (1 << (area & 7)))) return true; return false; // not visible } /* ============= R_MarkLights bit: 1 << i for light number i, will be or'ed into msurface_t::dlightbits if surface is affected by this light ============= */ void R_MarkLights(dlight_t *light, int bit, mnode_t *node, int lightframecount, marksurfacelights_t mark_surface_lights) { cplane_t *splitplane; float dist; int intensity; if (node->contents != CONTENTS_NODE) return; splitplane = node->plane; dist = DotProduct(light->origin, splitplane->normal) - splitplane->dist; intensity = light->intensity; if (dist > intensity - DLIGHT_CUTOFF) // (dist > light->intensity) { R_MarkLights (light, bit, node->children[0], lightframecount, mark_surface_lights); return; } if (dist < -intensity + DLIGHT_CUTOFF) // (dist < -light->intensity) { R_MarkLights(light, bit, node->children[1], lightframecount, mark_surface_lights); return; } mark_surface_lights(light, bit, node, lightframecount); R_MarkLights(light, bit, node->children[0], lightframecount, mark_surface_lights); R_MarkLights(light, bit, node->children[1], lightframecount, mark_surface_lights); } /* * Returns true if the box is completely outside the frustom */ qboolean R_CullBox(vec3_t mins, vec3_t maxs, cplane_t *frustum) { int i; for (i = 0; i < 4; i++) { if (BOX_ON_PLANE_SIDE(mins, maxs, frustum + i) == 2) { return true; } } return false; } static 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(vec3_t vup, vec3_t vpn, vec3_t vright, vec3_t r_origin, float fov_x, float fov_y, cplane_t *frustum) { int i; /* rotate VPN right by FOV_X/2 degrees */ RotatePointAroundVector(frustum[0].normal, vup, vpn, -(90 - fov_x / 2)); /* rotate VPN left by FOV_X/2 degrees */ RotatePointAroundVector(frustum[1].normal, vup, vpn, 90 - fov_x / 2); /* rotate VPN up by FOV_X/2 degrees */ RotatePointAroundVector(frustum[2].normal, vright, vpn, 90 - fov_y / 2); /* rotate VPN down by FOV_X/2 degrees */ RotatePointAroundVector(frustum[3].normal, vright, vpn, -(90 - fov_y / 2)); #if defined(__GNUC__) # pragma GCC unroll 4 #endif 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]); } } yquake2-QUAKE2_8_40/src/client/refresh/files/wal.c000066400000000000000000000134731465112212000216550ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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" struct image_s * LoadWal(const char *origname, imagetype_t type, loadimage_t load_image) { int width, height, ofs, size; struct image_s *image; char name[256]; miptex_t *mt; FixFileExt(origname, "wal", name, sizeof(name)); size = ri.FS_LoadFile(name, (void **)&mt); if (!mt) { return NULL; } if (size < sizeof(miptex_t)) { R_Printf(PRINT_ALL, "%s: can't load %s, small header\n", __func__, name); ri.FS_FreeFile((void *)mt); return NULL; } width = LittleLong(mt->width); height = LittleLong(mt->height); ofs = LittleLong(mt->offsets[0]); if ((ofs <= 0) || (width <= 0) || (height <= 0) || (((size - ofs) / height) < width)) { R_Printf(PRINT_ALL, "%s: can't load %s, small body\n", __func__, name); ri.FS_FreeFile((void *)mt); return NULL; } image = load_image(name, (byte *)mt + ofs, width, 0, height, 0, (size - ofs), type, 8); ri.FS_FreeFile((void *)mt); return image; } struct image_s * LoadM8(const char *origname, imagetype_t type, loadimage_t load_image) { m8tex_t *mt; int width, height, ofs, size, i; struct image_s *image; char name[256]; unsigned char *image_buffer = NULL; FixFileExt(origname, "m8", name, sizeof(name)); size = ri.FS_LoadFile(name, (void **)&mt); if (!mt) { return NULL; } if (size < sizeof(m8tex_t)) { R_Printf(PRINT_ALL, "%s: can't load %s, small header\n", __func__, name); ri.FS_FreeFile((void *)mt); return NULL; } if (LittleLong (mt->version) != M8_VERSION) { R_Printf(PRINT_ALL, "%s: can't load %s, wrong magic value.\n", __func__, name); ri.FS_FreeFile ((void *)mt); return NULL; } width = LittleLong(mt->width[0]); height = LittleLong(mt->height[0]); ofs = LittleLong(mt->offsets[0]); if ((ofs <= 0) || (width <= 0) || (height <= 0) || (((size - ofs) / height) < width)) { R_Printf(PRINT_ALL, "%s: can't load %s, small body\n", __func__, name); ri.FS_FreeFile((void *)mt); return NULL; } image_buffer = malloc ((size - ofs) * 4); for(i=0; i<(size - ofs); i++) { unsigned char value = *((byte *)mt + ofs + i); image_buffer[i * 4 + 0] = mt->palette[value].r; image_buffer[i * 4 + 1] = mt->palette[value].g; image_buffer[i * 4 + 2] = mt->palette[value].b; image_buffer[i * 4 + 3] = value == 255 ? 0 : 255; } image = load_image(name, image_buffer, width, 0, height, 0, (size - ofs), type, 32); free(image_buffer); ri.FS_FreeFile((void *)mt); return image; } struct image_s * LoadM32(const char *origname, imagetype_t type, loadimage_t load_image) { m32tex_t *mt; int width, height, ofs, size; struct image_s *image; char name[256]; FixFileExt(origname, "m32", name, sizeof(name)); size = ri.FS_LoadFile(name, (void **)&mt); if (!mt) { return NULL; } if (size < sizeof(m32tex_t)) { R_Printf(PRINT_ALL, "%s: can't load %s, small header\n", __func__, name); ri.FS_FreeFile((void *)mt); return NULL; } if (LittleLong (mt->version) != M32_VERSION) { R_Printf(PRINT_ALL, "%s: can't load %s, wrong magic value.\n", __func__, name); ri.FS_FreeFile ((void *)mt); return NULL; } width = LittleLong (mt->width[0]); height = LittleLong (mt->height[0]); ofs = LittleLong (mt->offsets[0]); if ((ofs <= 0) || (width <= 0) || (height <= 0) || (((size - ofs) / height) < (width * 4))) { R_Printf(PRINT_ALL, "%s: can't load %s, small body\n", __func__, name); ri.FS_FreeFile((void *)mt); return NULL; } image = load_image(name, (byte *)mt + ofs, width, 0, height, 0, (size - ofs) / 4, type, 32); ri.FS_FreeFile ((void *)mt); return image; } void GetWalInfo(const char *origname, int *width, int *height) { miptex_t *mt; int size; char filename[256]; FixFileExt(origname, "wal", filename, sizeof(filename)); size = ri.FS_LoadFile(filename, (void **)&mt); if (!mt) { return; } if (size < sizeof(miptex_t)) { ri.FS_FreeFile((void *)mt); return; } *width = LittleLong(mt->width); *height = LittleLong(mt->height); ri.FS_FreeFile((void *)mt); return; } void GetM8Info(const char *origname, int *width, int *height) { m8tex_t *mt; int size; char filename[256]; FixFileExt(origname, "m8", filename, sizeof(filename)); size = ri.FS_LoadFile(filename, (void **)&mt); if (!mt) { return; } if (size < sizeof(m8tex_t) || LittleLong (mt->version) != M8_VERSION) { ri.FS_FreeFile((void *)mt); return; } *width = LittleLong(mt->width[0]); *height = LittleLong(mt->height[0]); ri.FS_FreeFile((void *)mt); return; } void GetM32Info(const char *origname, int *width, int *height) { m32tex_t *mt; int size; char filename[256]; FixFileExt(origname, "m32", filename, sizeof(filename)); size = ri.FS_LoadFile(filename, (void **)&mt); if (!mt) { return; } if (size < sizeof(m32tex_t) || LittleLong (mt->version) != M32_VERSION) { ri.FS_FreeFile((void *)mt); return; } *width = LittleLong(mt->width[0]); *height = LittleLong(mt->height[0]); ri.FS_FreeFile((void *)mt); return; } yquake2-QUAKE2_8_40/src/client/refresh/gl1/000077500000000000000000000000001465112212000202775ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/gl1/gl1_buffer.c000066400000000000000000000250341465112212000224630ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * Copyright (C) 2024 Jaime Moreira * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 buffer: sort of a "Q3A shader" handler, allows to join multiple * draw calls into one, by grouping those which share the same * characteristics (mostly the same texture). * * ======================================================================= */ #include "header/local.h" #define MAX_VERTICES 16384 #define MAX_INDICES (MAX_VERTICES * 4) typedef struct // 832k aprox. { buffered_draw_t type; GLfloat vtx[MAX_VERTICES * 3], // vertexes tex[MAX_TEXTURE_UNITS][MAX_VERTICES * 2], // texture coords clr[MAX_VERTICES * 4]; // color components GLushort idx[MAX_INDICES], // indices vtx_ptr, idx_ptr; // pointers for array positions int texture[MAX_TEXTURE_UNITS]; int flags; // entity flags float alpha; } glbuffer_t; glbuffer_t gl_buf; GLuint vt, tx, cl; // indices for arrays in gl_buf extern void R_MYgluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar); void R_ApplyGLBuffer(void) { // Properties of batched draws here GLint vtx_size; qboolean texture, mtex, alpha, color, alias, texenv_set; float fovy, dist; if (gl_buf.vtx_ptr == 0 || gl_buf.idx_ptr == 0) { return; } // defaults for drawing (mostly buf_singletex features) vtx_size = 3; texture = true; mtex = alpha = color = alias = texenv_set = false; // choosing features by type switch (gl_buf.type) { case buf_2d: vtx_size = 2; break; case buf_mtex: mtex = true; break; case buf_alpha: alpha = true; break; case buf_alias: alias = color = true; break; case buf_flash: color = true; case buf_shadow: texture = false; break; default: break; } R_EnableMultitexture(mtex); if (alias) { if (gl_buf.flags & RF_DEPTHHACK) { // hack the depth range to prevent view model from poking into walls glDepthRange(gldepthmin, gldepthmin + 0.3 * (gldepthmax - gldepthmin)); } if (gl_buf.flags & RF_WEAPONMODEL) { glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); if (gl_lefthand->value == 1.0f) { glScalef(-1, 1, 1); } fovy = (r_gunfov->value < 0) ? r_newrefdef.fov_y : r_gunfov->value; dist = (r_farsee->value == 0) ? 4096.0f : 8192.0f; R_MYgluPerspective(fovy, (float)r_newrefdef.width / r_newrefdef.height, 4, dist); glMatrixMode(GL_MODELVIEW); if (gl_lefthand->value == 1.0f) { glCullFace(GL_BACK); } } glShadeModel(GL_SMOOTH); R_TexEnv(GL_MODULATE); if (gl_buf.flags & RF_TRANSLUCENT) { glEnable(GL_BLEND); } if (gl_buf.flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) { texture = false; glDisable(GL_TEXTURE_2D); } } if (alpha) { // the textures are prescaled up for a better // lighting range, so scale it back down glColor4f(gl_state.inverse_intensity, gl_state.inverse_intensity, gl_state.inverse_intensity, gl_buf.alpha); } else if (gl_buf.flags & SURF_DRAWTURB) { texenv_set = true; // 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 // gl1_overbrightbits the result is different: // 0: Old behaviour. // 1: No overbright bits on the global scene but correct lighting on // warping surfaces. // 2,4: Overbright bits on the global scene but not on warping surfaces. // They oversaturate otherwise. if (gl1_overbrightbits->value) { R_TexEnv(GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, 1); } else { R_TexEnv(GL_MODULATE); glColor4f(gl_state.inverse_intensity, gl_state.inverse_intensity, gl_state.inverse_intensity, 1.0f); } } glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer (vtx_size, GL_FLOAT, 0, gl_buf.vtx); if (texture) { if (mtex) { // TMU 1: Lightmap texture R_MBind(GL_TEXTURE1, gl_state.lightmap_textures + gl_buf.texture[1]); if (gl1_overbrightbits->value) { R_TexEnv(GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, gl1_overbrightbits->value); } glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2, GL_FLOAT, 0, gl_buf.tex[1]); // TMU 0: Color texture R_MBind(GL_TEXTURE0, gl_buf.texture[0]); } else { R_Bind(gl_buf.texture[0]); } glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2, GL_FLOAT, 0, gl_buf.tex[0]); } if (color) { glEnableClientState(GL_COLOR_ARRAY); glColorPointer(4, GL_FLOAT, 0, gl_buf.clr); } // All set, we can finally draw glDrawElements(GL_TRIANGLES, gl_buf.idx_ptr, GL_UNSIGNED_SHORT, gl_buf.idx); // ... and now, turn back everything as it was if (color) { glDisableClientState(GL_COLOR_ARRAY); } if (texture) { glDisableClientState( GL_TEXTURE_COORD_ARRAY ); } glDisableClientState( GL_VERTEX_ARRAY ); if (texenv_set) { R_TexEnv(GL_REPLACE); } if (alias) { if (gl_buf.flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM)) { glEnable(GL_TEXTURE_2D); } if (gl_buf.flags & RF_TRANSLUCENT) { glDisable(GL_BLEND); } R_TexEnv(GL_REPLACE); glShadeModel(GL_FLAT); if (gl_buf.flags & RF_WEAPONMODEL) { glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); if (gl_lefthand->value == 1.0F) { glCullFace(GL_FRONT); } } if (gl_buf.flags & RF_DEPTHHACK) { glDepthRange(gldepthmin, gldepthmax); } } gl_buf.vtx_ptr = gl_buf.idx_ptr = 0; } void R_UpdateGLBuffer(buffered_draw_t type, int colortex, int lighttex, int flags, float alpha) { if ( gl_buf.type != type || gl_buf.texture[0] != colortex || (gl_config.multitexture && type == buf_mtex && gl_buf.texture[1] != lighttex) || ((type == buf_singletex || type == buf_alias) && gl_buf.flags != flags) || (type == buf_alpha && gl_buf.alpha != alpha)) { R_ApplyGLBuffer(); gl_buf.type = type; gl_buf.texture[0] = colortex; gl_buf.texture[1] = lighttex; gl_buf.flags = flags; gl_buf.alpha = alpha; } } void R_Buffer2DQuad(GLfloat ul_vx, GLfloat ul_vy, GLfloat dr_vx, GLfloat dr_vy, GLfloat ul_tx, GLfloat ul_ty, GLfloat dr_tx, GLfloat dr_ty) { static const GLushort idx_max = MAX_INDICES - 7; static const GLushort vtx_max = MAX_VERTICES - 5; unsigned int i; if (gl_buf.idx_ptr > idx_max || gl_buf.vtx_ptr > vtx_max) { R_ApplyGLBuffer(); } i = gl_buf.vtx_ptr * 2; // vertex index // "Quad" = 2-triangle GL_TRIANGLE_FAN gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr; gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+1; gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+2; gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr; gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+2; gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+3; // up left corner coords gl_buf.vtx[i] = ul_vx; gl_buf.vtx[i+1] = ul_vy; // up right gl_buf.vtx[i+2] = dr_vx; gl_buf.vtx[i+3] = ul_vy; // down right gl_buf.vtx[i+4] = dr_vx; gl_buf.vtx[i+5] = dr_vy; // and finally, down left gl_buf.vtx[i+6] = ul_vx; gl_buf.vtx[i+7] = dr_vy; gl_buf.tex[0][i] = ul_tx; gl_buf.tex[0][i+1] = ul_ty; gl_buf.tex[0][i+2] = dr_tx; gl_buf.tex[0][i+3] = ul_ty; gl_buf.tex[0][i+4] = dr_tx; gl_buf.tex[0][i+5] = dr_ty; gl_buf.tex[0][i+6] = ul_tx; gl_buf.tex[0][i+7] = dr_ty; gl_buf.vtx_ptr += 4; } /* * Set up indices with the proper shape for the next buffered vertices */ void R_SetBufferIndices(GLenum type, GLuint vertices_num) { int i; if ( gl_buf.vtx_ptr + vertices_num >= MAX_VERTICES || gl_buf.idx_ptr + ( (vertices_num - 2) * 3 ) >= MAX_INDICES ) { R_ApplyGLBuffer(); } switch (type) { case GL_TRIANGLE_FAN: for (i = 0; i < vertices_num-2; i++) { gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr; gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i+1; gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i+2; } break; case GL_TRIANGLE_STRIP: for (i = 0; i < vertices_num-2; i++) { if (i % 2 == 0) { gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i; gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i+1; gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i+2; } else // backwards order { gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i+2; gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i+1; gl_buf.idx[gl_buf.idx_ptr++] = gl_buf.vtx_ptr+i; } } break; default: R_Printf(PRINT_DEVELOPER, "R_SetBufferIndices: no such type %d\n", type); return; } // These affect the functions that follow in this file vt = gl_buf.vtx_ptr * 3; // vertex index tx = gl_buf.vtx_ptr * 2; // texcoord index cl = gl_buf.vtx_ptr * 4; // color index // R_BufferVertex() must be called as many times as vertices_num gl_buf.vtx_ptr += vertices_num; } /* * Adds a single vertex to buffer */ void R_BufferVertex(GLfloat x, GLfloat y, GLfloat z) { gl_buf.vtx[vt++] = x; gl_buf.vtx[vt++] = y; gl_buf.vtx[vt++] = z; } /* * Adds texture coordinates for color texture (no lightmap coords) */ void R_BufferSingleTex(GLfloat s, GLfloat t) { // tx should be set before this is called, by R_SetBufferIndices gl_buf.tex[0][tx++] = s; gl_buf.tex[0][tx++] = t; } /* * Adds texture coordinates for color and lightmap */ void R_BufferMultiTex(GLfloat cs, GLfloat ct, GLfloat ls, GLfloat lt) { gl_buf.tex[0][tx] = cs; gl_buf.tex[0][tx+1] = ct; gl_buf.tex[1][tx] = ls; gl_buf.tex[1][tx+1] = lt; tx += 2; } /* * Adds color components of vertex */ void R_BufferColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { gl_buf.clr[cl++] = r; gl_buf.clr[cl++] = g; gl_buf.clr[cl++] = b; gl_buf.clr[cl++] = a; } yquake2-QUAKE2_8_40/src/client/refresh/gl1/gl1_draw.c000066400000000000000000000225041465112212000221460ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_FindPic("conchars", (findimage_t)R_FindImage); if (!draw_chars) { ri.Sys_Error(ERR_FATAL, "%s: Couldn't load pics/conchars.pcx", __func__); } } /* * 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_UpdateGLBuffer(buf_2d, draw_chars->texnum, 0, 0, 1); R_Buffer2DQuad(x, y, x + scaledSize, y + scaledSize, fcol, frow, fcol + size, frow + size); } image_t * RDraw_FindPic(char *name) { return R_FindPic(name, (findimage_t)R_FindImage); } void RDraw_GetPicSize(int *w, int *h, char *pic) { image_t *gl; gl = R_FindPic(pic, (findimage_t)R_FindImage); 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 = R_FindPic(pic, (findimage_t)R_FindImage); 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 = R_FindPic(pic, (findimage_t)R_FindImage); if (!gl) { R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); return; } if (scrap_dirty) { Scrap_Upload(); } if (gl->texnum == TEXNUM_SCRAPS) { R_UpdateGLBuffer(buf_2d, TEXNUM_SCRAPS, 0, 0, 1); R_Buffer2DQuad(x, y, x + gl->width * factor, y + gl->height * factor, gl->sl, gl->tl, gl->sh, gl->th); return; } 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 = R_FindPic(pic, (findimage_t)R_FindImage); if (!image) { R_Printf(PRINT_ALL, "Can't find pic: %s\n", pic); return; } R_UpdateGLBuffer(buf_2d, image->texnum, 0, 0, 1); R_Buffer2DQuad(x, y, x + w, y + h, x / 64.0, y / 64.0, ( x + w ) / 64.0, ( y + h ) / 64.0); } /* * 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) { R_ApplyGLBuffer(); // draw what needs to be hidden 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, const byte *data, int bits) { GLfloat tex[8]; float hscale = 1.0f; int frac, fracstep; int i, j, trows; int row; R_Bind(0); if(gl_config.npottextures || rows <= 256 || bits == 32) { // 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 || bits == 32) { 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 (bits == 32) { glTexImage2D(GL_TEXTURE_2D, 0, gl_tex_solid_format, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); } else 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++) { const byte *source; 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); } // Note: gl_filter_min could be GL_*_MIPMAP_* so we can't use it for min filter here (=> no mipmaps) // but gl_filter_max (either GL_LINEAR or GL_NEAREST) should do the trick. GLint filter = (r_videos_unfiltered->value == 0) ? gl_filter_max : GL_NEAREST; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); 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 ); } yquake2-QUAKE2_8_40/src/client/refresh/gl1/gl1_image.c000066400000000000000000000650141465112212000222760ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; static int image_max = 0; int base_textureid; /* gltextures[i] = base_textureid+i */ extern qboolean scrap_dirty; extern byte *scrap_texels[MAX_SCRAPS]; 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; 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_SelectTexture(GLenum texture) { if (!gl_config.multitexture || gl_state.currenttarget == texture) { return; } gl_state.currenttmu = texture - GL_TEXTURE0; gl_state.currenttarget = texture; qglActiveTexture(texture); qglClientActiveTexture(texture); } void R_TexEnv(GLenum mode) { static int lastmodes[2] = {-1, -1}; if (mode != lastmodes[gl_state.currenttmu]) { glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode); lastmodes[gl_state.currenttmu] = mode; } } qboolean 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 false; } gl_state.currenttextures[gl_state.currenttmu] = texnum; glBindTexture(GL_TEXTURE_2D, texnum); return true; } void R_MBind(GLenum target, int texnum) { const int tmu = target - GL_TEXTURE0; if (target != gl_state.currenttarget) { R_SelectTexture(target); } if (gl_state.currenttextures[tmu] == texnum) { return; } R_Bind(texnum); } void R_EnableMultitexture(qboolean enable) { static qboolean active; if (!gl_config.multitexture || enable == active) { return; // current state is the right one } active = enable; R_SelectTexture(GL_TEXTURE1); if (active && !r_fullbright->value) { glEnable(GL_TEXTURE_2D); if (gl_lightmap->value) { R_TexEnv(GL_REPLACE); } else { R_TexEnv(GL_MODULATE); } } else // disable multitexturing { glDisable(GL_TEXTURE_2D); R_TexEnv(GL_REPLACE); } R_SelectTexture(GL_TEXTURE0); R_TexEnv(GL_REPLACE); } 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("r_anisotropic", gl_config.max_anisotropy); } } else { ri.Cvar_SetValue("r_anisotropic", 0.0); } const char* nolerplist = gl_nolerp_list->string; const char* lerplist = r_lerp_list->string; qboolean unfiltered2D = r_2D_unfiltered->value != 0; /* change all the existing mipmap texture objects */ for (i = 0, glt = gltextures; i < numgltextures; i++, glt++) { qboolean nolerp = false; /* r_2D_unfiltered and r_nolerp_list allow rendering stuff unfiltered even if gl_filter_* is filtered */ if (unfiltered2D && glt->type == it_pic) { // exception to that exception: stuff on the r_lerp_list nolerp = (lerplist== NULL) || (strstr(lerplist, glt->name) == NULL); } else if(nolerplist != NULL && strstr(nolerplist, glt->name) != NULL) { nolerp = true; } if ( !R_Bind(glt->texnum) ) { continue; // don't bother changing anything if texture was already set } if ((glt->type != it_pic) && (glt->type != it_sky)) /* mipmapped texture */ { 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, Q_max(gl_anisotropic->value, 1.f)); } } else /* texture has no mipmaps */ { if (nolerp) { // this texture shouldn't be filtered at all (no gl_nolerp_list or r_2D_unfiltered case) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } else { // we can't use gl_filter_min which might be GL_*_MIPMAP_* // also, there's no anisotropic filtering for textures w/o mipmaps glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } } } } 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, used, texels; image_t *image; qboolean freeup; const char *palstrings[2] = { "RGB", "PAL" }; R_Printf(PRINT_ALL, "------------------\n"); texels = 0; used = 0; for (i = 0, image = gltextures; i < numgltextures; i++, image++) { char *in_use = ""; if (image->texnum <= 0) { continue; } if (image->registration_sequence == registration_sequence) { in_use = "*"; used++; } 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 (%dx%d) %s\n", image->upload_width, image->upload_height, palstrings[image->paletted], image->name, image->width, image->height, in_use); } R_Printf(PRINT_ALL, "Total texel count (not counting mipmaps): %i\n", texels); freeup = R_ImageHasFreeSpace(); R_Printf(PRINT_ALL, "Used %d of %d images%s.\n", used, image_max, freeup ? ", has free space" : ""); } /* * 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 = 0; int i; // NOTE: there was a if(filledcolor == -1) which didn't make sense b/c filledcolor used to be initialized to -1 /* 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; } } /* * 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; } } 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 (gl1_round_down->value && (scaled_width > width) && mipmap) { scaled_width >>= 1; } for (scaled_height = 1; scaled_height < height; scaled_height <<= 1) { } if (gl1_round_down->value && (scaled_height > height) && mipmap) { scaled_height >>= 1; } /* let people sample down the world textures for speed */ if (mipmap) { scaled_width >>= (int)gl1_picmip->value; scaled_height >>= (int)gl1_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) { // this can't really happen (because they're clamped to 256 above), but whatever 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 && gl1_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 { ResizeSTB((byte *)data, width, height, (byte *)scaled, scaled_width, scaled_height); } R_LightScaleTexture(scaled, scaled_width, scaled_height, !mipmap); if (qglColorTableEXT && gl1_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 && gl1_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, Q_max(gl_anisotropic->value, 1.f)); } return res; } /* * Returns has_alpha */ qboolean R_Upload8(byte *data, int width, int height, qboolean mipmap, qboolean is_sky) { int s = width * height; 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 { unsigned *trans = malloc(s * sizeof(unsigned)); for (int i = 0; i < s; i++) { int 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]; } } qboolean ret = R_Upload32(trans, width, height, mipmap); free(trans); return ret; } } /* * This is also used as an entry point for the generated r_notexture */ image_t * R_LoadPic(const char *name, byte *pic, int width, int realwidth, int height, int realheight, size_t data_size, imagetype_t type, int bits) { image_t *image; int i; qboolean nolerp = false; if (r_2D_unfiltered->value && type == it_pic) { // if r_2D_unfiltered is true(ish), nolerp should usually be true, // *unless* the texture is on the r_lerp_list nolerp = (r_lerp_list->string == NULL) || (strstr(r_lerp_list->string, name) == NULL); } else 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, "%s: \"%s\" is too long", __func__, 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) * gl_state.scrap_width + x + j] = pic[k]; } } image->texnum = TEXNUM_SCRAPS + texnum; image->scrap = true; image->has_alpha = true; image->sl = (float)x / gl_state.scrap_width; image->sh = (float)(x + image->width) / gl_state.scrap_width; image->tl = (float)y / gl_state.scrap_height; image->th = (float)(y + image->height) / gl_state.scrap_height; } else { nonscrap: image->scrap = false; image->texnum = TEXNUM_IMAGES + (image - gltextures); R_Bind(image->texnum); if (bits == 8) { // resize 8bit images only when we forced such logic if (r_scale8bittextures->value) { byte *image_converted; int scale = 2; // scale 3 times if lerp image if (!nolerp && (vid.height >= 240 * 3)) scale = 3; image_converted = malloc(width * height * scale * scale); if (!image_converted) return NULL; if (scale == 3) { scale3x(pic, image_converted, width, height); } else { scale2x(pic, image_converted, width, height); } image->has_alpha = R_Upload8(image_converted, width * scale, height * scale, (image->type != it_pic && image->type != it_sky), image->type == it_sky); free(image_converted); } else { 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; } /* * Finds or loads the given image or null */ image_t * R_FindImage(const char *name, imagetype_t type) { image_t *image; int i, len; char *ptr; char namewe[256]; 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 - (strlen(ext) + 1)); 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 // image = (image_t *)R_LoadImage(name, namewe, ext, type, r_retexturing->value, (loadimage_t)R_LoadPic); if (!image && r_validation->value) { R_Printf(PRINT_ALL, "%s: can't load %s\n", __func__, name); } 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)); } } qboolean R_ImageHasFreeSpace(void) { int i, used; image_t *image; used = 0; for (i = 0, image = gltextures; i < numgltextures; i++, image++) { if (!image->name[0]) continue; if (image->registration_sequence == registration_sequence) { used ++; } } if (image_max < used) { image_max = used; } // should same size of free slots as currently used return (numgltextures + used) < MAX_GLTEXTURES; } void R_InitImages(void) { byte *colormap; int i, j; registration_sequence = 1; image_max = 0; /* init intensity conversions */ intensity = ri.Cvar_Get("gl1_intensity", "2", CVAR_ARCHIVE); if (intensity->value <= 1) { ri.Cvar_Set("gl1_intensity", "1"); } gl_state.inverse_intensity = 1 / intensity->value; // FIXME: I think this is redundant - RI_Init() already calls that! GetPCXPalette (&colormap, d_8to24table); free(colormap); 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, "%s: Couldn't load pics/16to8.pcx", __func__); } } for (i = 0; i < 256; i++) { gammatable[i] = i; } 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_8_40/src/client/refresh/gl1/gl1_light.c000066400000000000000000000301411465112212000223140ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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" 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) { const float rad = light->intensity * 0.35; int i, j; float vtx[3], a; R_SetBufferIndices(GL_TRIANGLE_FAN, 18); for ( i = 0; i < 3; i++ ) { vtx [ i ] = light->origin [ i ] - vpn [ i ] * rad; } R_BufferVertex( vtx[0], vtx[1], vtx[2] ); R_BufferColor( light->color[0] * 0.2, light->color[1] * 0.2, light->color[2] * 0.2, 1 ); for ( i = 16; i >= 0; i-- ) { a = i / 16.0 * M_PI * 2; for ( j = 0; j < 3; j++ ) { vtx[ j ] = light->origin [ j ] + vright [ j ] * cos( a ) * rad + vup [ j ] * sin( a ) * rad; } R_BufferVertex( vtx[0], vtx[1], vtx[2] ); R_BufferColor( 0, 0, 0, 1 ); } } void R_RenderDlights(void) { int i; dlight_t *l; if (!gl1_flashblend->value) { return; } R_UpdateGLBuffer(buf_flash, 0, 0, 0, 1); /* because the count hasn't advanced yet for this frame */ r_dlightframecount = r_framecount + 1; glDepthMask(GL_FALSE); 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); } R_ApplyGLBuffer(); glColor4f(1, 1, 1, 1); glDisable(GL_BLEND); glEnable(GL_TEXTURE_2D); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(GL_TRUE); } void R_MarkSurfaceLights(dlight_t *light, int bit, mnode_t *node, int lightframecount) { msurface_t *surf; int i; /* mark the polygons */ surf = r_worldmodel->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { int sidebit; float dist; 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 != lightframecount) { surf->dlightbits = 0; surf->dlightframe = lightframecount; } surf->dlightbits |= bit; } } void R_PushDlights(void) { int i; dlight_t *l; if (gl1_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, r_dlightframecount, R_MarkSurfaceLights); } } 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 != CONTENTS_NODE) { 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); lightmap += 3 * (dt * ((surf->extents[0] >> 4) + 1) + ds); for (maps = 0; maps < MAXLIGHTMAPS && surf->styles[maps] != 255; maps++) { const float *rgb; int j; rgb = r_newrefdef.lightstyles[surf->styles[maps]].rgb; /* Apply light level to models */ for (j = 0; j < 3; j++) { float scale; scale = rgb[j] * r_modulate->value; pointcolor[j] += lightmap[j] * scale * (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(entity_t *currententity, 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 || !currententity) { 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, r_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] = r_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] = r_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_8_40/src/client/refresh/gl1/gl1_lightmap.c000066400000000000000000000174211465112212000230200ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_FreeLightmapBuffers(void) { for (int i=0; i height) { height = gl_lms.allocated[i]; } } glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, gl_state.block_width, height, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer[buffer]); } else { gl_lms.internal_format = GL_LIGHTMAP_FORMAT; glTexImage2D(GL_TEXTURE_2D, 0, gl_lms.internal_format, gl_state.block_width, gl_state.block_height, 0, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer[buffer]); if (++gl_lms.current_lightmap_texture == gl_state.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 = gl_state.block_height; for (i = 0; i < gl_state.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 > gl_state.block_height) { return false; } for (i = 0; i < w; i++) { gl_lms.allocated[*x + i] = best + h; } return true; } void LM_BuildPolygonFromSurface(model_t *currentmodel, 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 /= gl_state.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 /= gl_state.block_height * 16; /* fa->texinfo->texture->height; */ poly->verts[i][5] = s; poly->verts[i][6] = t; } } void LM_CreateSurfaceLightmap(msurface_t *surf) { int smax, tmax, buffer; 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; buffer = (gl_config.multitexture)? surf->lightmaptexturenum : 0; base = gl_lms.lightmap_buffer[buffer]; base += (surf->light_t * gl_state.block_width + surf->light_s) * LIGHTMAP_BYTES; R_SetCacheState(surf); R_BuildLightMap(surf, base, gl_state.block_width * LIGHTMAP_BYTES); } void LM_BeginBuildingLightmaps(model_t *m) { static lightstyle_t lightstyles[MAX_LIGHTSTYLES]; int i; LM_FreeLightmapBuffers(); gl_lms.allocated = (int*)malloc(gl_state.block_width * sizeof(int)); if (!gl_lms.allocated) { ri.Sys_Error(ERR_FATAL, "Could not create lightmap allocator\n"); } memset(gl_lms.allocated, 0, gl_state.block_width * sizeof(int)); 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; if (gl_config.multitexture) { LM_AllocLightmapBuffer(gl_lms.current_lightmap_texture, false); return; } // dynamic lightmap for classic rendering path (no multitexture) LM_AllocLightmapBuffer(0, true); /* 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, gl_state.block_width, gl_state.block_height, 0, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer[0]); } void LM_EndBuildingLightmaps(void) { LM_UploadBlock(false); } yquake2-QUAKE2_8_40/src/client/refresh/gl1/gl1_main.c000066400000000000000000001450501465112212000221370ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 */ 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; unsigned r_rawpalette[256]; cvar_t *r_norefresh; cvar_t *r_drawentities; cvar_t *r_drawworld; cvar_t *r_speeds; cvar_t *r_fullbright; cvar_t *r_novis; cvar_t *r_lerpmodels; cvar_t *gl_lefthand; cvar_t *r_gunfov; cvar_t *r_farsee; cvar_t *r_validation; cvar_t *r_lightlevel; cvar_t *gl1_overbrightbits; cvar_t *gl1_particle_min_size; cvar_t *gl1_particle_max_size; cvar_t *gl1_particle_size; cvar_t *gl1_particle_att_a; cvar_t *gl1_particle_att_b; cvar_t *gl1_particle_att_c; cvar_t *gl1_particle_square; cvar_t *gl1_palettedtexture; cvar_t *gl1_pointparameters; cvar_t *gl1_multitexture; cvar_t *gl_drawbuffer; cvar_t *gl_lightmap; cvar_t *gl_shadows; cvar_t *gl1_stencilshadow; cvar_t *r_mode; cvar_t *r_fixsurfsky; cvar_t *r_customwidth; cvar_t *r_customheight; cvar_t *r_retexturing; cvar_t *r_scale8bittextures; cvar_t *gl_nolerp_list; cvar_t *r_lerp_list; cvar_t *r_2D_unfiltered; cvar_t *r_videos_unfiltered; cvar_t *gl1_dynamic; cvar_t *r_modulate; cvar_t *gl_nobind; cvar_t *gl1_round_down; cvar_t *gl1_picmip; cvar_t *gl_showtris; cvar_t *gl_showbbox; cvar_t *gl1_ztrick; cvar_t *gl_zfix; cvar_t *gl_finish; cvar_t *r_clear; cvar_t *r_cull; cvar_t *gl_polyblend; cvar_t *gl1_flashblend; cvar_t *gl1_saturatelighting; cvar_t *r_vsync; cvar_t *gl_texturemode; cvar_t *gl1_texturealphamode; cvar_t *gl1_texturesolidmode; cvar_t *gl_anisotropic; cvar_t *r_lockpvs; cvar_t *gl_msaa_samples; cvar_t *vid_fullscreen; cvar_t *vid_gamma; cvar_t *gl1_stereo; cvar_t *gl1_stereo_separation; cvar_t *gl1_stereo_anaglyph_colors; cvar_t *gl1_stereo_convergence; refimport_t ri; void LM_FreeLightmapBuffers(void); void Scrap_Free(void); void Scrap_Init(void); 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 *currententity, const model_t *currentmodel) { float alpha = 1.0F; vec3_t point[4]; dsprframe_t *frame; float *up, *right; dsprite_t *psprite; image_t *skin; R_EnableMultitexture(false); /* don't even bother culling, because it's just a single polygon without a surface cache */ psprite = (dsprite_t *)currentmodel->extradata; currententity->frame %= psprite->numframes; frame = &psprite->frames[currententity->frame]; /* normal sprite */ up = vup; right = vright; if (currententity->flags & RF_TRANSLUCENT) { alpha = currententity->alpha; } if (alpha != 1.0F) { glEnable(GL_BLEND); } glColor4f(1, 1, 1, alpha); skin = currentmodel->skins[currententity->frame]; if (!skin) { skin = r_notexture; /* fallback... */ } R_Bind(skin->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( currententity->origin, -frame->origin_y, up, point[0] ); VectorMA( point[0], -frame->origin_x, right, point[0] ); VectorMA( currententity->origin, frame->height - frame->origin_y, up, point[1] ); VectorMA( point[1], -frame->origin_x, right, point[1] ); VectorMA( currententity->origin, frame->height - frame->origin_y, up, point[2] ); VectorMA( point[2], frame->width - frame->origin_x, right, point[2] ); VectorMA( currententity->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(entity_t *currententity) { vec3_t shadelight; if (currententity->flags & RF_FULLBRIGHT) { shadelight[0] = shadelight[1] = shadelight[2] = 1.0F; } else { R_LightPoint(currententity, currententity->origin, shadelight); } R_EnableMultitexture(false); 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 (!r_drawentities->value) { return; } /* draw non-transparent first */ for (i = 0; i < r_newrefdef.num_entities; i++) { entity_t *currententity = &r_newrefdef.entities[i]; if (currententity->flags & RF_TRANSLUCENT) { continue; /* solid */ } if (currententity->flags & RF_BEAM) { R_DrawBeam(currententity); } else { const model_t *currentmodel = currententity->model; if (!currentmodel) { R_DrawNullModel(currententity); continue; } switch (currentmodel->type) { case mod_alias: R_DrawAliasModel(currententity, currentmodel); break; case mod_brush: R_DrawBrushModel(currententity, currentmodel); break; case mod_sprite: R_DrawSpriteModel(currententity, currentmodel); break; default: ri.Sys_Error(ERR_DROP, "Bad modeltype"); break; } } } /* draw transparent entities we could sort these if it ever becomes a problem... */ glDepthMask(GL_FALSE); for (i = 0; i < r_newrefdef.num_entities; i++) { entity_t *currententity = &r_newrefdef.entities[i]; if (!(currententity->flags & RF_TRANSLUCENT)) { continue; /* solid */ } if (currententity->flags & RF_BEAM) { R_DrawBeam(currententity); } else { const model_t *currentmodel = currententity->model; if (!currentmodel) { R_DrawNullModel(currententity); continue; } switch (currentmodel->type) { case mod_alias: R_DrawAliasModel(currententity, currentmodel); break; case mod_brush: R_DrawBrushModel(currententity, currentmodel); break; case mod_sprite: R_DrawSpriteModel(currententity, currentmodel); break; default: ri.Sys_Error(ERR_DROP, "Bad modeltype"); break; } } } glDepthMask(GL_TRUE); /* back to writing */ R_EnableMultitexture(false); } void R_DrawParticles2(int num_particles, const particle_t particles[], const unsigned *colortable) { const particle_t *p; int i; vec3_t up, right; float scale; YQ2_ALIGNAS_TYPE(unsigned) byte color[4]; YQ2_VLA(GLfloat, vtx, 3 * num_particles * 3); YQ2_VLA(GLfloat, tex, 2 * num_particles * 3); YQ2_VLA(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; } *(unsigned *) 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(GL_TRUE); /* back to normal Z buffering */ R_TexEnv(GL_REPLACE); YQ2_VLAFREE(vtx); YQ2_VLAFREE(tex); YQ2_VLAFREE(clr); } 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 (r_newrefdef.num_particles <= 0) /* avoiding VLA with no size and vertexes built on it */ { return; } if (gl_config.pointparameters && !(stereo_split_tb || stereo_split_lr)) { int i; YQ2_ALIGNAS_TYPE(unsigned) byte color[4]; const particle_t *p; YQ2_VLA(GLfloat, vtx, 3 * r_newrefdef.num_particles); YQ2_VLA(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(gl1_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); YQ2_VLAFREE(vtx); YQ2_VLAFREE(clr); } 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); } 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)) { if (!r_worldmodel) { ri.Sys_Error(ERR_DROP, "%s: bad world model", __func__); return; } r_oldviewcluster = r_viewcluster; r_oldviewcluster2 = r_viewcluster2; leaf = Mod_PointInLeaf(r_origin, r_worldmodel->nodes); 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->nodes); 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->nodes); 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 += - gl1_stereo_convergence->value * (2 * gl_state.camera_separation) / zNear; xmax += - gl1_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 (r_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 (r_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(GL_FALSE); stencilFlags |= GL_STENCIL_BUFFER_BIT; } if (gl1_ztrick->value) { static int trickframe; if (r_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 (r_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 && gl_state.stencil && gl1_stencilshadow->value) { glClearStencil(GL_TRUE); 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 */ static 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(gl1_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((unsigned char)gl1_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 (r_norefresh->value) { return; } r_newrefdef = *fd; if (!r_worldmodel && !(r_newrefdef.rdflags & RDF_NOWORLDMODEL)) { ri.Sys_Error(ERR_DROP, "%s: NULL worldmodel", __func__); } if (r_speeds->value) { c_brush_polys = 0; c_alias_polys = 0; } R_PushDlights(); if (gl_finish->value) { glFinish(); } R_SetupFrame(); R_SetFrustum(vup, vpn, vright, r_origin, r_newrefdef.fov_x, r_newrefdef.fov_y, frustum); 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 (r_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; } static void R_SetLightLevel(entity_t *currententity) { vec3_t shadelight; if (r_newrefdef.rdflags & RDF_NOWORLDMODEL) { return; } /* save off light value for server to look at */ R_LightPoint(currententity, 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]) { r_lightlevel->value = 150 * shadelight[0]; } else { r_lightlevel->value = 150 * shadelight[2]; } } else { if (shadelight[1] > shadelight[2]) { r_lightlevel->value = 150 * shadelight[1]; } else { r_lightlevel->value = 150 * shadelight[2]; } } } static void RI_RenderFrame(refdef_t *fd) { R_ApplyGLBuffer(); // menu rendering when needed R_RenderView(fd); R_SetLightLevel (NULL); R_SetGL2D(); } void R_Register(void) { gl_lefthand = ri.Cvar_Get("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE); r_gunfov = ri.Cvar_Get("r_gunfov", "80", CVAR_ARCHIVE); r_farsee = ri.Cvar_Get("r_farsee", "0", CVAR_LATCH | CVAR_ARCHIVE); r_norefresh = ri.Cvar_Get("r_norefresh", "0", 0); r_fullbright = ri.Cvar_Get("r_fullbright", "0", 0); r_drawentities = ri.Cvar_Get("r_drawentities", "1", 0); r_drawworld = ri.Cvar_Get("r_drawworld", "1", 0); r_novis = ri.Cvar_Get("r_novis", "0", 0); r_lerpmodels = ri.Cvar_Get("r_lerpmodels", "1", 0); r_speeds = ri.Cvar_Get("r_speeds", "0", 0); r_lightlevel = ri.Cvar_Get("r_lightlevel", "0", 0); gl1_overbrightbits = ri.Cvar_Get("gl1_overbrightbits", "0", CVAR_ARCHIVE); gl1_particle_min_size = ri.Cvar_Get("gl1_particle_min_size", "2", CVAR_ARCHIVE); gl1_particle_max_size = ri.Cvar_Get("gl1_particle_max_size", "40", CVAR_ARCHIVE); gl1_particle_size = ri.Cvar_Get("gl1_particle_size", "40", CVAR_ARCHIVE); gl1_particle_att_a = ri.Cvar_Get("gl1_particle_att_a", "0.01", CVAR_ARCHIVE); gl1_particle_att_b = ri.Cvar_Get("gl1_particle_att_b", "0.0", CVAR_ARCHIVE); gl1_particle_att_c = ri.Cvar_Get("gl1_particle_att_c", "0.01", CVAR_ARCHIVE); gl1_particle_square = ri.Cvar_Get("gl1_particle_square", "0", CVAR_ARCHIVE); r_modulate = ri.Cvar_Get("r_modulate", "1", CVAR_ARCHIVE); r_mode = ri.Cvar_Get("r_mode", "4", CVAR_ARCHIVE); gl_lightmap = ri.Cvar_Get("r_lightmap", "0", 0); gl_shadows = ri.Cvar_Get("r_shadows", "0", CVAR_ARCHIVE); gl1_stencilshadow = ri.Cvar_Get("gl1_stencilshadow", "0", CVAR_ARCHIVE); gl1_dynamic = ri.Cvar_Get("gl1_dynamic", "1", 0); gl_nobind = ri.Cvar_Get("gl_nobind", "0", 0); gl1_round_down = ri.Cvar_Get("gl1_round_down", "1", 0); gl1_picmip = ri.Cvar_Get("gl1_picmip", "0", 0); gl_showtris = ri.Cvar_Get("gl_showtris", "0", 0); gl_showbbox = ri.Cvar_Get("gl_showbbox", "0", 0); gl1_ztrick = ri.Cvar_Get("gl1_ztrick", "0", 0); gl_zfix = ri.Cvar_Get("gl_zfix", "0", 0); gl_finish = ri.Cvar_Get("gl_finish", "0", CVAR_ARCHIVE); r_clear = ri.Cvar_Get("r_clear", "0", 0); r_cull = ri.Cvar_Get("r_cull", "1", 0); gl_polyblend = ri.Cvar_Get("gl_polyblend", "1", 0); gl1_flashblend = ri.Cvar_Get("gl1_flashblend", "0", 0); r_fixsurfsky = ri.Cvar_Get("r_fixsurfsky", "0", CVAR_ARCHIVE); gl_texturemode = ri.Cvar_Get("gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE); gl1_texturealphamode = ri.Cvar_Get("gl1_texturealphamode", "default", CVAR_ARCHIVE); gl1_texturesolidmode = ri.Cvar_Get("gl1_texturesolidmode", "default", CVAR_ARCHIVE); gl_anisotropic = ri.Cvar_Get("r_anisotropic", "0", CVAR_ARCHIVE); r_lockpvs = ri.Cvar_Get("r_lockpvs", "0", 0); gl1_palettedtexture = ri.Cvar_Get("r_palettedtextures", "0", CVAR_ARCHIVE); gl1_pointparameters = ri.Cvar_Get("gl1_pointparameters", "1", CVAR_ARCHIVE); gl1_multitexture = ri.Cvar_Get("gl1_multitexture", "1", CVAR_ARCHIVE); gl_drawbuffer = ri.Cvar_Get("gl_drawbuffer", "GL_BACK", 0); r_vsync = ri.Cvar_Get("r_vsync", "1", CVAR_ARCHIVE); gl1_saturatelighting = ri.Cvar_Get("gl1_saturatelighting", "0", 0); vid_fullscreen = ri.Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); vid_gamma = ri.Cvar_Get("vid_gamma", "1.2", CVAR_ARCHIVE); r_customwidth = ri.Cvar_Get("r_customwidth", "1024", CVAR_ARCHIVE); r_customheight = ri.Cvar_Get("r_customheight", "768", CVAR_ARCHIVE); gl_msaa_samples = ri.Cvar_Get ( "r_msaa_samples", "0", CVAR_ARCHIVE ); r_retexturing = ri.Cvar_Get("r_retexturing", "1", CVAR_ARCHIVE); r_validation = ri.Cvar_Get("r_validation", "0", CVAR_ARCHIVE); r_scale8bittextures = ri.Cvar_Get("r_scale8bittextures", "0", CVAR_ARCHIVE); /* don't bilerp characters and crosshairs */ gl_nolerp_list = ri.Cvar_Get("r_nolerp_list", "pics/conchars.pcx pics/ch1.pcx pics/ch2.pcx pics/ch3.pcx", CVAR_ARCHIVE); /* textures that should always be filtered, even if r_2D_unfiltered or an unfiltered gl mode is used */ r_lerp_list = ri.Cvar_Get("r_lerp_list", "", CVAR_ARCHIVE); /* don't bilerp any 2D elements */ r_2D_unfiltered = ri.Cvar_Get("r_2D_unfiltered", "0", CVAR_ARCHIVE); /* don't bilerp videos */ r_videos_unfiltered = ri.Cvar_Get("r_videos_unfiltered", "0", CVAR_ARCHIVE); gl1_stereo = ri.Cvar_Get( "gl1_stereo", "0", CVAR_ARCHIVE ); gl1_stereo_separation = ri.Cvar_Get( "gl1_stereo_separation", "-0.4", CVAR_ARCHIVE ); gl1_stereo_anaglyph_colors = ri.Cvar_Get( "gl1_stereo_anaglyph_colors", "rc", CVAR_ARCHIVE ); gl1_stereo_convergence = ri.Cvar_Get( "gl1_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 >= 0) && !ri.Vid_GetModeInfo(pwidth, pheight, mode)) { R_Printf(PRINT_ALL, " invalid mode\n"); return rserr_invalid_mode; } /* We trying to get resolution from desktop */ if (mode == -2) { if(!ri.GLimp_GetDesktopMode(pwidth, pheight)) { R_Printf( PRINT_ALL, " can't detect mode\n" ); return rserr_invalid_mode; } } R_Printf(PRINT_ALL, " %dx%d (vid_fullscreen %i)\n", *pwidth, *pheight, fullscreen); if (!ri.GLimp_InitGraphics(fullscreen, pwidth, pheight)) { return rserr_invalid_mode; } /* This is totaly obscure: For some strange reasons the renderer maintains two(!) repesentations of the resolution. One comes from the client and is saved in r_newrefdef. The other one is determined here and saved in vid. Several calculations take both representations into account. The values will always be the same. The GLimp_InitGraphics() call above communicates the requested resolution to the client where it ends up in the vid subsystem and the vid system writes it into r_newrefdef. We can't avoid the client roundtrip, because we can get the real size of the drawable (which can differ from the resolution due to high dpi awareness) only after the render context was created by GLimp_InitGraphics() and need to communicate it somehow to the client. So we just overwrite the values saved in vid with a call to RI_GetDrawableSize(), just like the client does. This makes sure that both values are the same and everything is okay. We also need to take the special case fullscreen window into account. With the fullscreen windows we cannot use the drawable size, it would scale all cases to the size of the window. Instead use the drawable size when the user wants native resolution (the fullscreen window fills the screen) and use the requested resolution in all other cases. */ if (IsHighDPIaware) { if (vid_fullscreen->value != 2) { RI_GetDrawableSize(pwidth, pheight); } else { if (r_mode->value == -2) { /* User requested native resolution. */ RI_GetDrawableSize(pwidth, pheight); } } } return rserr_ok; } qboolean R_SetMode(void) { rserr_t err; int fullscreen; fullscreen = (int)vid_fullscreen->value; /* a bit hackish approach to enable custom resolutions: Glimp_SetMode needs these values set for mode -1 */ vid.width = r_customwidth->value; vid.height = r_customheight->value; if ((err = SetMode_impl(&vid.width, &vid.height, r_mode->value, fullscreen)) == rserr_ok) { if (r_mode->value == -1) { gl_state.prev_mode = 4; /* safe default for custom mode */ } else { gl_state.prev_mode = r_mode->value; } } 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("r_msaa_samples", 0.0f); gl_msaa_samples->modified = false; if ((err = SetMode_impl(&vid.width, &vid.height, r_mode->value, 0)) == rserr_ok) { return true; } } if(r_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("r_mode", gl_state.prev_mode); r_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(void) { int j; byte *colormap; 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"); #ifdef DEBUG R_Printf(PRINT_ALL, "ref_gl1::R_Init() - DEBUG mode enabled\n"); #endif GetPCXPalette (&colormap, d_8to24table); free(colormap); R_Register(); /* initialize our QGL dynamic bindings */ QGL_Init(); /* set our "safe" mode */ gl_state.prev_mode = 4; gl_state.stereo_mode = gl1_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") || strstr(gl_config.extensions_string, "GL_EXT_point_parameters") ) // should exist for all OGL 1.4 hw... { qglPointParameterf = (void (APIENTRY *)(GLenum, GLfloat))RI_GetProcAddress ( "glPointParameterf" ); qglPointParameterfv = (void (APIENTRY *)(GLenum, const GLfloat *))RI_GetProcAddress ( "glPointParameterfv" ); if (!qglPointParameterf || !qglPointParameterfv) { qglPointParameterf = (void (APIENTRY *)(GLenum, GLfloat))RI_GetProcAddress ( "glPointParameterfARB" ); qglPointParameterfv = (void (APIENTRY *)(GLenum, const GLfloat *))RI_GetProcAddress ( "glPointParameterfvARB" ); } if (!qglPointParameterf || !qglPointParameterfv) { qglPointParameterf = (void (APIENTRY *)(GLenum, GLfloat))RI_GetProcAddress ( "glPointParameterfEXT" ); qglPointParameterfv = (void (APIENTRY *)(GLenum, const GLfloat *))RI_GetProcAddress ( "glPointParameterfvEXT" ); } } gl_config.pointparameters = false; if (gl1_pointparameters->value) { if (qglPointParameterf && qglPointParameterfv) { 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 * )) RI_GetProcAddress ("glColorTableEXT"); } gl_config.palettedtexture = false; if (gl1_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"); } // ---- /* Multitexturing */ gl_config.multitexture = false; R_Printf(PRINT_ALL, " - Multitexturing: "); if (strstr(gl_config.extensions_string, "GL_ARB_multitexture")) { qglActiveTexture = (void (APIENTRY *)(GLenum))RI_GetProcAddress ("glActiveTexture"); qglClientActiveTexture = (void (APIENTRY *)(GLenum))RI_GetProcAddress ("glClientActiveTexture"); if (!qglActiveTexture || !qglClientActiveTexture) { qglActiveTexture = (void (APIENTRY *)(GLenum))RI_GetProcAddress ("glActiveTextureARB"); qglClientActiveTexture = (void (APIENTRY *)(GLenum))RI_GetProcAddress ("glClientActiveTextureARB"); } } if (gl1_multitexture->value) { if (qglActiveTexture && qglClientActiveTexture) { gl_config.multitexture = true; R_Printf(PRINT_ALL, "Okay\n"); } else { R_Printf(PRINT_ALL, "Failed\n"); } } else { R_Printf(PRINT_ALL, "Disabled\n"); } // ---- /* Big lightmaps: this used to be fast, but after the implementation of the "GL Buffer", it * became too evident that the bigger the texture, the slower the call to glTexSubImage2D() is. * Original logic remains, but it's preferable not to make it visible to the user. * Let's see if something changes in the future. */ gl_state.block_width = BLOCK_WIDTH; gl_state.block_height = BLOCK_HEIGHT; gl_state.max_lightmaps = MAX_LIGHTMAPS; gl_state.scrap_width = BLOCK_WIDTH * 2; gl_state.scrap_height = BLOCK_HEIGHT * 2; // ---- R_SetDefaultState(); Scrap_Init(); 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"); LM_FreeLightmapBuffers(); Scrap_Free(); Mod_FreeAll(); R_ShutdownImages(); /* shutdown OS specific OpenGL stuff like contexts, etc. */ RI_ShutdownContext(); /* shutdown our QGL subsystem */ QGL_Shutdown(); } void RI_BeginFrame(float camera_separation) { gl_state.camera_separation = camera_separation; // force a vid_restart if gl1_stereo has been modified. if ( gl_state.stereo_mode != gl1_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( gl1_stereo->value ) ) { gl_state.stereo_mode = gl1_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; RI_UpdateGamma(); } // Clamp overbrightbits if (gl1_overbrightbits->modified) { int obb_val = (int)gl1_overbrightbits->value; if (obb_val < 0) { obb_val = 0; } else if (obb_val == 3) { obb_val = 2; } else if (obb_val > 4) { obb_val = 4; } ri.Cvar_SetValue("gl1_overbrightbits", obb_val); gl1_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); if (gl1_particle_square->modified) { if (gl_config.pointparameters) { /* 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! */ if (gl1_particle_square->value) { glDisable(GL_POINT_SMOOTH); } else { glEnable(GL_POINT_SMOOTH); } } else { // particles aren't drawn as GL_POINTS, but as textured triangles // => update particle texture to look square - or circle-ish R_InitParticleTexture(); } gl1_particle_square->modified = false; } /* 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) || gl_nolerp_list->modified || r_lerp_list->modified || r_2D_unfiltered->modified || r_videos_unfiltered->modified) { R_TextureMode(gl_texturemode->string); gl_texturemode->modified = false; gl_anisotropic->modified = false; gl_nolerp_list->modified = false; r_lerp_list->modified = false; r_2D_unfiltered->modified = false; r_videos_unfiltered->modified = false; } if (gl1_texturealphamode->modified) { R_TextureAlphaMode(gl1_texturealphamode->string); gl1_texturealphamode->modified = false; } if (gl1_texturesolidmode->modified) { R_TextureSolidMode(gl1_texturesolidmode->string); gl1_texturesolidmode->modified = false; } if (r_vsync->modified) { r_vsync->modified = false; RI_SetVsync(); } /* 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]); } R_EnableMultitexture(false); 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 void RI_SetPalette(const unsigned char *palette); extern qboolean RI_IsVSyncActive(void); extern void RI_EndFrame(void); /* ===================== RI_EndWorldRenderpass ===================== */ static qboolean RI_EndWorldRenderpass( void ) { return true; } Q2_DLL_EXPORTED refexport_t GetRefAPI(refimport_t imp) { refexport_t re = {0}; ri = imp; re.api_version = API_VERSION; re.framework_version = RI_GetSDLVersion(); re.Init = RI_Init; re.Shutdown = RI_Shutdown; re.PrepareForWindow = RI_PrepareForWindow; re.InitContext = RI_InitContext; re.GetDrawableSize = RI_GetDrawableSize; re.ShutdownContext = RI_ShutdownContext; 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.EndWorldRenderpass = RI_EndWorldRenderpass; re.EndFrame = RI_EndFrame; // Tell the client that we're unsing the // new renderer restart API. ri.Vid_RequestRestart(RESTART_NO); 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(const char *error, ...) { va_list argptr; char text[4096]; // MAXPRINTMSG == 4096 va_start(argptr, error); vsnprintf(text, sizeof(text), error, argptr); va_end(argptr); ri.Sys_Error(ERR_FATAL, "%s", text); } void Com_Printf(const char *msg, ...) { va_list argptr; va_start(argptr, msg); ri.Com_VPrintf(PRINT_ALL, msg, argptr); va_end(argptr); } #ifdef DEBUG void glCheckError_(const char *file, const char *function, int line) { GLenum errorCode; const char * msg; #define MY_ERROR_CASE(X) case X : msg = #X; break; while ((errorCode = glGetError()) != GL_NO_ERROR) { switch(errorCode) { MY_ERROR_CASE(GL_INVALID_ENUM); MY_ERROR_CASE(GL_INVALID_VALUE); MY_ERROR_CASE(GL_INVALID_OPERATION); MY_ERROR_CASE(GL_STACK_OVERFLOW); MY_ERROR_CASE(GL_STACK_UNDERFLOW); MY_ERROR_CASE(GL_OUT_OF_MEMORY); default: msg = "UNKNOWN"; } R_Printf(PRINT_ALL, "glError: %s in %s (%s, %d)\n", msg, function, file, line); } #undef MY_ERROR_CASE } #endif yquake2-QUAKE2_8_40/src/client/refresh/gl1/gl1_mesh.c000066400000000000000000000350241465112212000221460ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; static void R_LerpVerts(entity_t *currententity, 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 */ static void R_DrawAliasFrameLerp(entity_t *currententity, dmdl_t *paliashdr, float backlerp) { daliasframe_t *frame, *oldframe; dtrivertx_t *v, *ov, *verts; int *order; int count, i, index_xyz; float tex[2], frontlerp, l, alpha; vec3_t move, delta, vectors[3]; vec3_t frontv, backv; 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; } 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(currententity, 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; R_SetBufferIndices(GL_TRIANGLE_FAN, count); } else { R_SetBufferIndices(GL_TRIANGLE_STRIP, count); } if (currententity->flags & (RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE)) { do { index_xyz = order[2]; order += 3; R_BufferVertex(s_lerped[index_xyz][0], s_lerped[index_xyz][1], s_lerped[index_xyz][2]); R_BufferColor(shadelight[0], shadelight[1], shadelight[2], alpha); } while (--count); } else { do { /* texture coordinates come from the draw list */ tex[0] = ((float *)order)[0]; tex[1] = ((float *)order)[1]; index_xyz = order[2]; order += 3; /* normals and vertexes come from the frame list */ l = shadedots[verts[index_xyz].lightnormalindex]; R_BufferVertex(s_lerped[index_xyz][0], s_lerped[index_xyz][1], s_lerped[index_xyz][2]); R_BufferSingleTex(tex[0], tex[1]); R_BufferColor(l * shadelight[0], l * shadelight[1], l * shadelight[2], alpha); } while (--count); } } } static void R_DrawAliasShadow(entity_t *currententity, dmdl_t *paliashdr, int posenum) { int *order; vec3_t point; float height = 0, lheight; int count; lheight = currententity->origin[2] - lightspot[2]; order = (int *)((byte *)paliashdr + paliashdr->ofs_glcmds); height = -lheight + 0.1f; R_UpdateGLBuffer(buf_shadow, 0, 0, 0, 1); /* stencilbuffer shadows */ if (gl_state.stencil && gl1_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; R_SetBufferIndices(GL_TRIANGLE_FAN, count); } else { R_SetBufferIndices(GL_TRIANGLE_STRIP, count); } 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; R_BufferVertex( point[0], point[1], point[2] ); order += 3; } while (--count); } R_ApplyGLBuffer(); /* stencilbuffer shadows */ if (gl_state.stencil && gl1_stencilshadow->value) { glDisable(GL_STENCIL_TEST); } } static qboolean R_CullAliasModel(const model_t *currentmodel, 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 *currententity, const model_t *currentmodel) { int i; dmdl_t *paliashdr; float an; vec3_t bbox[8]; image_t *skin; if (!(currententity->flags & RF_WEAPONMODEL)) { if (R_CullAliasModel(currentmodel, bbox, currententity)) { return; } } if (currententity->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, 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]) { r_lightlevel->value = 150 * shadelight[0]; } else { r_lightlevel->value = 150 * shadelight[2]; } } else { if (shadelight[1] > shadelight[2]) { r_lightlevel->value = 150 * shadelight[1]; } else { r_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 gl1_overbrightbits to the mesh. If we don't do this they will appear slightly dimmer relative to walls. if (gl1_overbrightbits->value) { for (i = 0; i < 3; ++i) { shadelight[i] *= gl1_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; glPushMatrix(); currententity->angles[PITCH] = -currententity->angles[PITCH]; R_RotateForEntity(currententity); currententity->angles[PITCH] = -currententity->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... */ } 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 (!r_lerpmodels->value) { currententity->backlerp = 0; } R_UpdateGLBuffer(buf_alias, skin->texnum, 0, currententity->flags, 1); R_DrawAliasFrameLerp(currententity, paliashdr, currententity->backlerp); R_ApplyGLBuffer(); 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); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, bbox); glDrawArrays(GL_TRIANGLE_STRIP, 0, 8); glDisableClientState(GL_VERTEX_ARRAY); glEnable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glEnable(GL_CULL_FACE); } if (gl_shadows->value && !(currententity->flags & (RF_TRANSLUCENT | RF_WEAPONMODEL | RF_NOSHADOW))) { glPushMatrix(); /* don't rotate shadows on ungodly axes */ glTranslatef(currententity->origin[0], currententity->origin[1], currententity->origin[2]); glRotatef(currententity->angles[1], 0, 0, 1); glDisable(GL_TEXTURE_2D); glEnable(GL_BLEND); glColor4f(0, 0, 0, 0.5f); R_DrawAliasShadow(currententity, paliashdr, currententity->frame); glEnable(GL_TEXTURE_2D); glDisable(GL_BLEND); glPopMatrix(); } glColor4f(1, 1, 1, 1); } yquake2-QUAKE2_8_40/src/client/refresh/gl1/gl1_misc.c000066400000000000000000000153111465112212000221420ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 squaretexture[16][16] = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 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} }; 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; if (!gl1_particle_square->value) { partData[y][x][3] = dottexture[x][y] * 85; } else { partData[y][x][3] = squaretexture[x][y] * 85; } } } r_particletexture = R_LoadPic("***particle***", (byte *)partData, 16, 0, 16, 0, 16 * 16, 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, 8 * 8, 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; YQ2_VLA(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; } YQ2_VLAFREE(rowBuffer); } 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(gl1_texturealphamode->string); R_TextureSolidMode(gl1_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] = gl1_particle_att_a->value; attenuations[1] = gl1_particle_att_b->value; attenuations[2] = gl1_particle_att_c->value; qglPointParameterf(GL_POINT_SIZE_MIN, gl1_particle_min_size->value); qglPointParameterf(GL_POINT_SIZE_MAX, gl1_particle_max_size->value); qglPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, attenuations); /* 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! */ if (gl1_particle_square->value) { glDisable(GL_POINT_SMOOTH); } else { glEnable(GL_POINT_SMOOTH); } } 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_8_40/src/client/refresh/gl1/gl1_model.c000066400000000000000000000455051465112212000223170ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 int modfilelen; YQ2_ALIGNAS_TYPE(int) byte mod_novis[MAX_MAP_LEAFS / 8]; static model_t mod_known[MAX_MOD_KNOWN]; static int mod_numknown; static int mod_max = 0; int registration_sequence; static void Mod_LoadBrushModel(model_t *mod, void *buffer, int modfilelen); void LM_BuildPolygonFromSurface(model_t *currentmodel, msurface_t *fa); void LM_CreateSurfaceLightmap(msurface_t *surf); void LM_EndBuildingLightmaps(void); void LM_BeginBuildingLightmaps(model_t *m); //=============================================================================== static qboolean Mod_HasFreeSpace(void) { int i, used; model_t *mod; used = 0; for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) { if (!mod->name[0]) continue; if (mod->registration_sequence == registration_sequence) { used ++; } } if (mod_max < used) { mod_max = used; } // should same size of free slots as currently used return (mod_numknown + mod_max) < MAX_MOD_KNOWN; } const byte * Mod_ClusterPVS(int cluster, const model_t *model) { if ((cluster == -1) || !model->vis) { return mod_novis; } return Mod_DecompressVis((byte *)model->vis + model->vis->bitofs[cluster][DVIS_PVS], (model->vis->numclusters + 7) >> 3); } void Mod_Modellist_f(void) { int i, total, used; model_t *mod; qboolean freeup; total = 0; used = 0; R_Printf(PRINT_ALL, "Loaded models:\n"); for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { char *in_use = ""; if (mod->registration_sequence == registration_sequence) { in_use = "*"; used ++; } if (!mod->name[0]) { continue; } R_Printf(PRINT_ALL, "%8i : %s %s\n", mod->extradatasize, mod->name, in_use); total += mod->extradatasize; } R_Printf(PRINT_ALL, "Total resident: %i\n", total); // update statistics freeup = Mod_HasFreeSpace(); R_Printf(PRINT_ALL, "Used %d of %d models%s.\n", used, mod_max, freeup ? ", has free space" : ""); } void Mod_Init(void) { mod_max = 0; memset(mod_novis, 0xff, sizeof(mod_novis)); } /* * Loads in a model for the given name */ static model_t * Mod_ForName (char *name, model_t *parent_model, qboolean crash) { model_t *mod; void *buf; int i; if (!name[0]) { ri.Sys_Error(ERR_DROP, "%s: NULL name", __func__); } /* inline models are grabbed only from worldmodel */ if (name[0] == '*' && parent_model) { i = (int)strtol(name + 1, (char **)NULL, 10); if (i < 1 || i >= parent_model->numsubmodels) { ri.Sys_Error(ERR_DROP, "%s: bad inline model number", __func__); } return &parent_model->submodels[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, "%s: %s not found", __func__, mod->name); } memset(mod->name, 0, sizeof(mod->name)); return NULL; } /* call the apropriate loader */ switch (LittleLong(*(unsigned *)buf)) { case IDALIASHEADER: { mod->extradata = Mod_LoadMD2(mod->name, buf, modfilelen, mod->mins, mod->maxs, (struct image_s **)mod->skins, (findimage_t)R_FindImage, &(mod->type)); if (!mod->extradata) { ri.Sys_Error(ERR_DROP, "%s: Failed to load %s", __func__, mod->name); } }; break; case IDSPRITEHEADER: { mod->extradata = Mod_LoadSP2(mod->name, buf, modfilelen, (struct image_s **)mod->skins, (findimage_t)R_FindImage, &(mod->type)); if (!mod->extradata) { ri.Sys_Error(ERR_DROP, "%s: Failed to load %s", __func__, mod->name); } } break; case IDBSPHEADER: Mod_LoadBrushModel(mod, buf, modfilelen); break; default: ri.Sys_Error(ERR_DROP, "%s: unknown fileid for %s", __func__, mod->name); break; } mod->extradatasize = Hunk_End(); ri.FS_FreeFile(buf); return mod; } static void Mod_LoadSubmodels (model_t *loadmodel, byte *mod_base, lump_t *l) { dmodel_t *in; model_t *out; int i, j, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s", __func__, 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++) { if (i == 0) { // copy parent as template for first model memcpy(out, loadmodel, sizeof(*out)); } else { // copy first as template for model memcpy(out, loadmodel->submodels, sizeof(*out)); } Com_sprintf (out->name, sizeof(out->name), "*%d", 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->radius = Mod_RadiusFromBounds(out->mins, out->maxs); out->firstnode = LittleLong (in->headnode); out->firstmodelsurface = LittleLong (in->firstface); out->nummodelsurfaces = LittleLong (in->numfaces); // visleafs out->numleafs = 0; // check limits if (out->firstnode >= loadmodel->numnodes) { ri.Sys_Error(ERR_DROP, "%s: Inline model %i has bad firstnode", __func__, i); } } } /* * Fills in s->texturemins[] and s->extents[] */ static void Mod_CalcSurfaceExtents(model_t *loadmodel, 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; } } static int calcTexinfoAndFacesSize(byte *mod_base, const lump_t *fl, const lump_t *tl) { dface_t* face_in = (void *)(mod_base + fl->fileofs); texinfo_t* texinfo_in = (void *)(mod_base + tl->fileofs); if (fl->filelen % sizeof(*face_in) || tl->filelen % sizeof(*texinfo_in)) { // will error out when actually loading it return 0; } int ret = 0; int face_count = fl->filelen / sizeof(*face_in); int texinfo_count = tl->filelen / sizeof(*texinfo_in); { // out = Hunk_Alloc(count * sizeof(*out)); int baseSize = face_count * sizeof(msurface_t); baseSize = (baseSize + 31) & ~31; ret += baseSize; int ti_size = texinfo_count * sizeof(mtexinfo_t); ti_size = (ti_size + 31) & ~31; ret += ti_size; } int numWarpFaces = 0; for (int surfnum = 0; surfnum < face_count; surfnum++, face_in++) { int numverts = LittleShort(face_in->numedges); int ti = LittleShort(face_in->texinfo); if ((ti < 0) || (ti >= texinfo_count)) { return 0; // will error out } int texFlags = LittleLong(texinfo_in[ti].flags); /* set the drawing flags */ if (texFlags & SURF_WARP) { if (numverts > 60) return 0; // will error out in R_SubdividePolygon() // GL3_SubdivideSurface(out, loadmodel); /* cut up polygon for warps */ // for each (pot. recursive) call to R_SubdividePolygon(): // sizeof(glpoly_t) + ((numverts - 4) + 2) * sizeof(gl3_3D_vtx_t) // this is tricky, how much is allocated depends on the size of the surface // which we don't know (we'd need the vertices etc to know, but we can't load // those without allocating...) // so we just count warped faces and use a generous estimate below ++numWarpFaces; } else { // LM_BuildPolygonFromSurface(out); // => poly = Hunk_Alloc(sizeof(glpoly_t) + (numverts - 4) * VERTEXSIZE * sizeof(float)); int polySize = sizeof(glpoly_t) + (numverts - 4) * VERTEXSIZE * sizeof(float); polySize = (polySize + 31) & ~31; ret += polySize; } } // yeah, this is a bit hacky, but it looks like for each warped face // 256-55000 bytes are allocated (usually on the lower end), // so just assume 48k per face to be safe ret += numWarpFaces * 49152; ret += 5000000; // and 5MB extra just in case return ret; } static void Mod_LoadFaces(model_t *loadmodel, byte *mod_base, 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, "%s: funny lump size in %s", __func__, loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count * sizeof(*out)); loadmodel->surfaces = out; loadmodel->numsurfaces = count; 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; } if (planenum < 0 || planenum >= loadmodel->numplanes) { ri.Sys_Error(ERR_DROP, "%s: Incorrect %d planenum.", __func__, planenum); } out->plane = loadmodel->planes + planenum; ti = LittleShort(in->texinfo); if ((ti < 0) || (ti >= loadmodel->numtexinfo)) { ri.Sys_Error(ERR_DROP, "%s: bad texinfo number", __func__); } out->texinfo = loadmodel->texinfo + ti; Mod_CalcSurfaceExtents(loadmodel, 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(loadmodel, out); /* cut up polygon for warps */ } if (r_fixsurfsky->value) { if (out->texinfo->flags & SURF_SKY) { out->flags |= SURF_DRAWSKY; } } /* 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(loadmodel, out); } } LM_EndBuildingLightmaps(); } static void Mod_LoadLeafs(model_t *loadmodel, byte *mod_base, 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, "%s: funny lump size in %s", __func__, 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++) { unsigned firstleafface; 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); // make unsigned long from signed short firstleafface = LittleShort(in->firstleafface) & 0xFFFF; out->nummarksurfaces = LittleShort(in->numleaffaces) & 0xFFFF; out->firstmarksurface = loadmodel->marksurfaces + firstleafface; if ((firstleafface + out->nummarksurfaces) > loadmodel->nummarksurfaces) { ri.Sys_Error(ERR_DROP, "%s: wrong marksurfaces position in %s", __func__, loadmodel->name); } } } static void Mod_LoadMarksurfaces(model_t *loadmodel, byte *mod_base, 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, "%s: funny lump size in %s", __func__, 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, "%s: bad surface number", __func__); } out[i] = loadmodel->surfaces + j; } } static void Mod_LoadBrushModel(model_t *mod, void *buffer, int modfilelen) { int i; dheader_t *header; byte *mod_base; if (mod != 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, "%s: %s has wrong version number (%i should be %i)", __func__, 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]); } // calculate the needed hunksize from the lumps int hunkSize = 0; hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_VERTEXES], sizeof(dvertex_t), sizeof(mvertex_t), 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_EDGES], sizeof(dedge_t), sizeof(medge_t), 0); hunkSize += sizeof(medge_t) + 31; // for count+1 in Mod_LoadEdges() int surfEdgeCount = (header->lumps[LUMP_SURFEDGES].filelen+sizeof(int)-1)/sizeof(int); if(surfEdgeCount < MAX_MAP_SURFEDGES) // else it errors out later anyway hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_SURFEDGES], sizeof(int), sizeof(int), 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_LIGHTING], 1, 1, 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_PLANES], sizeof(dplane_t), sizeof(cplane_t)*2, 0); hunkSize += calcTexinfoAndFacesSize(mod_base, &header->lumps[LUMP_FACES], &header->lumps[LUMP_TEXINFO]); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_LEAFFACES], sizeof(short), sizeof(msurface_t *), 0); // yes, out is indeed a pointer! hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_VISIBILITY], 1, 1, 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_LEAFS], sizeof(dleaf_t), sizeof(mleaf_t), 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_NODES], sizeof(dnode_t), sizeof(mnode_t), 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_MODELS], sizeof(dmodel_t), sizeof(model_t), 0); mod->extradata = Hunk_Begin(hunkSize); mod->type = mod_brush; /* load into heap */ Mod_LoadVertexes(mod->name, &mod->vertexes, &mod->numvertexes, mod_base, &header->lumps[LUMP_VERTEXES], 0); Mod_LoadEdges(mod->name, &mod->edges, &mod->numedges, mod_base, &header->lumps[LUMP_EDGES], 1); Mod_LoadSurfedges(mod->name, &mod->surfedges, &mod->numsurfedges, mod_base, &header->lumps[LUMP_SURFEDGES], 0); Mod_LoadLighting(&mod->lightdata, mod_base, &header->lumps[LUMP_LIGHTING]); Mod_LoadPlanes (mod->name, &mod->planes, &mod->numplanes, mod_base, &header->lumps[LUMP_PLANES], 0); Mod_LoadTexinfo(mod->name, &mod->texinfo, &mod->numtexinfo, mod_base, &header->lumps[LUMP_TEXINFO], (findimage_t)R_FindImage, r_notexture, 0); Mod_LoadFaces(mod, mod_base, &header->lumps[LUMP_FACES]); Mod_LoadMarksurfaces(mod, mod_base, &header->lumps[LUMP_LEAFFACES]); Mod_LoadVisibility (&mod->vis, mod_base, &header->lumps[LUMP_VISIBILITY]); Mod_LoadLeafs(mod, mod_base, &header->lumps[LUMP_LEAFS]); Mod_LoadNodes(mod->name, mod->planes, mod->numplanes, mod->leafs, mod->numleafs, &mod->nodes, &mod->numnodes, mod_base, &header->lumps[LUMP_NODES]); Mod_LoadSubmodels (mod, mod_base, &header->lumps[LUMP_MODELS]); mod->numframes = 2; /* regular and alternate animation */ } 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, NULL, true); r_viewcluster = -1; } struct model_s * RI_RegisterModel(char *name) { model_t *mod; mod = Mod_ForName(name, r_worldmodel, false); if (mod) { mod->registration_sequence = registration_sequence; /* register any images used by the models */ if (mod->type == mod_brush) { int i; for (i = 0; i < mod->numtexinfo; i++) { mod->texinfo[i].image->registration_sequence = registration_sequence; } } else { /* numframes is unused for SP2 but lets set it also */ mod->numframes = Mod_ReLoadSkins((struct image_s **)mod->skins, (findimage_t)R_FindImage, mod->extradata, mod->type); } } return mod; } void RI_EndRegistration(void) { int i; model_t *mod; if (Mod_HasFreeSpace() && R_ImageHasFreeSpace()) { // should be enough space for load next maps return; } 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_8_40/src/client/refresh/gl1/gl1_scrap.c000066400000000000000000000062301465112212000223170ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 objects into a single texture * to crutch up inefficient hardware / drivers. * * ======================================================================= */ #include "header/local.h" int *scrap_allocated[MAX_SCRAPS]; byte *scrap_texels[MAX_SCRAPS]; qboolean scrap_dirty; 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; w += 2; // add an empty border to all sides h += 2; for (texnum = 0; texnum < MAX_SCRAPS; texnum++) { best = gl_state.scrap_height; for (i = 0; i < gl_state.scrap_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 > gl_state.scrap_height) { continue; } for (i = 0; i < w; i++) { scrap_allocated[texnum][*x + i] = best + h; } (*x)++; // jump the border (*y)++; return texnum; } return -1; } void Scrap_Upload(void) { R_Bind(TEXNUM_SCRAPS); R_Upload8(scrap_texels[0], gl_state.scrap_width, gl_state.scrap_height, false, false); scrap_dirty = false; } void Scrap_Free(void) { for (int i = 0; i < MAX_SCRAPS; i++) { if (scrap_allocated[i]) { free(scrap_allocated[i]); } scrap_allocated[i] = NULL; if (scrap_texels[i]) { free(scrap_texels[i]); } scrap_texels[i] = NULL; } } void Scrap_Init(void) { const unsigned int allocd_size = gl_state.scrap_width * sizeof(int); const unsigned int texels_size = gl_state.scrap_width * gl_state.scrap_height * sizeof(byte); int i; Scrap_Free(); for (i = 0; i < MAX_SCRAPS; i++) { if (!scrap_allocated[i]) { scrap_allocated[i] = malloc (allocd_size) ; } if (!scrap_texels[i]) { scrap_texels[i] = malloc (texels_size) ; } if (!scrap_allocated[i] || !scrap_texels[i]) { ri.Sys_Error(ERR_FATAL, "Could not allocate scrap memory.\n"); } memset (scrap_allocated[i], 0, allocd_size); // empty memset (scrap_texels[i], 255, texels_size); // transparent } } yquake2-QUAKE2_8_40/src/client/refresh/gl1/gl1_sdl.c000066400000000000000000000160401465112212000217710ustar00rootroot00000000000000/* * 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 backend for the GL1 renderer. * * ======================================================================= */ #include "header/local.h" #ifdef USE_SDL3 #include #else #include #endif static SDL_Window* window = NULL; static SDL_GLContext context = NULL; qboolean IsHighDPIaware = false; static qboolean vsyncActive = false; // ---- /* * Swaps the buffers and shows the next frame. */ void RI_EndFrame(void) { R_ApplyGLBuffer(); // to draw buffered 2D text SDL_GL_SwapWindow(window); } /* * Returns the adress of a GL function */ void * RI_GetProcAddress(const char* proc) { return SDL_GL_GetProcAddress(proc); } /* * Returns whether the vsync is enabled. */ qboolean RI_IsVSyncActive(void) { return vsyncActive; } /* * This function returns the flags used at the SDL window * creation by GLimp_InitGraphics(). In case of error -1 * is returned. */ int RI_PrepareForWindow(void) { // Set GL context attributs bound to the window. 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); if (SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8) == 0) { gl_state.stencil = true; } else { gl_state.stencil = false; } // Let's see if the driver supports MSAA. int msaa_samples = 0; 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 ("r_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("r_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); } return SDL_WINDOW_OPENGL; } /* * Enables or disables the vsync. */ void RI_SetVsync(void) { // Make sure that the user given // value is SDL compatible... int vsync = 0; if (r_vsync->value == 1) { vsync = 1; } else if (r_vsync->value == 2) { vsync = -1; } if (SDL_GL_SetSwapInterval(vsync) == -1) { if (vsync == -1) { // Not every system supports adaptive // vsync, fallback to normal vsync. R_Printf(PRINT_ALL, "Failed to set adaptive vsync, reverting to normal vsync.\n"); SDL_GL_SetSwapInterval(1); } } #ifdef USE_SDL3 int vsyncState; if (SDL_GL_GetSwapInterval(&vsyncState) != 0) { R_Printf(PRINT_ALL, "Failed to get vsync state, assuming vsync inactive.\n"); vsyncActive = false; } else { vsyncActive = vsyncState ? true : false; } #else vsyncActive = SDL_GL_GetSwapInterval() != 0; #endif } /* * Updates the gamma ramp. */ void RI_UpdateGamma(void) { // TODO SDL3: Hardware gamma / gamma ramps are no longer supported with // SDL3. There's no replacement and sdl2-compat won't support it either. // See https://github.com/libsdl-org/SDL/pull/6617 for the rational. #ifndef USE_SDL3 float gamma = (vid_gamma->value); Uint16 ramp[256]; SDL_CalculateGammaRamp(gamma, ramp); if (SDL_SetWindowGammaRamp(window, ramp, ramp, ramp) != 0) { R_Printf(PRINT_ALL, "Setting gamma failed: %s\n", SDL_GetError()); } #endif } /* * Initializes the OpenGL context. Returns true at * success and false at failure. */ int RI_InitContext(void* win) { // Coders are stupid. if (win == NULL) { ri.Sys_Error(ERR_FATAL, "R_InitContext() must not be called with NULL argument!"); return false; } window = (SDL_Window*)win; // Initialize GL context. 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; } // Check if it's really OpenGL 1.4. 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; } // Check if we've got the requested MSAA. int msaa_samples = 0; if (gl_msaa_samples->value) { if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &msaa_samples) == 0) { ri.Cvar_SetValue("r_msaa_samples", msaa_samples); } } // Enable vsync if requested. RI_SetVsync(); // Check if we've got 8 stencil bits. int stencil_bits = 0; if (gl_state.stencil) { if (SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil_bits) < 0 || stencil_bits < 8) { gl_state.stencil = false; } } // Initialize gamma. vid_gamma->modified = true; // Window title - set here so we can display renderer name in it. char title[40] = {0}; snprintf(title, sizeof(title), "Yamagi Quake II %s - OpenGL 1.4", YQ2VERSION); SDL_SetWindowTitle(window, title); #if SDL_VERSION_ATLEAST(2, 26, 0) // Figure out if we are high dpi aware. int flags = SDL_GetWindowFlags(win); #ifdef USE_SDL3 IsHighDPIaware = (flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) ? true : false; #else IsHighDPIaware = (flags & SDL_WINDOW_ALLOW_HIGHDPI) ? true : false; #endif #endif return true; } /* * Fills the actual size of the drawable into width and height. */ void RI_GetDrawableSize(int* width, int* height) { #ifdef USE_SDL3 SDL_GetWindowSizeInPixels(window, width, height); #else SDL_GL_GetDrawableSize(window, width, height); #endif } /* * Shuts the GL context down. */ void RI_ShutdownContext(void) { if (window) { if(context) { SDL_GL_DeleteContext(context); context = NULL; } } } /* * Returns the SDL major version. Implemented * here to not polute gl1_main.c with the SDL * headers. */ int RI_GetSDLVersion() { #ifdef USE_SDL3 SDL_Version ver; #else SDL_version ver; #endif SDL_VERSION(&ver); return ver.major; } yquake2-QUAKE2_8_40/src/client/refresh/gl1/gl1_surf.c000066400000000000000000000665761465112212000222110ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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); static void R_DrawGLPoly(msurface_t *fa) { int i, nv; float *v, scroll; v = fa->polys->verts[0]; nv = fa->polys->numverts; if (fa->texinfo->flags & SURF_FLOWING) { scroll = -64 * ((r_newrefdef.time / 40.0) - (int)(r_newrefdef.time / 40.0)); if (scroll == 0.0) { scroll = -64.0; } } else { scroll = 0.0; } R_SetBufferIndices(GL_TRIANGLE_FAN, nv); for ( i = 0; i < nv; i++, v += VERTEXSIZE ) { R_BufferVertex(v[0], v[1], v[2]); R_BufferSingleTex(v[3] + scroll, v[4]); } } static 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 < gl_state.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); } static 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]; 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 { // workaround for lack of VLAs (=> our workaround uses alloca() which is bad in loops) #ifdef _MSC_VER int maxNumVerts = 0; for (glpoly_t* tmp = p; tmp; tmp = tmp->chain) { if ( tmp->numverts > maxNumVerts ) maxNumVerts = tmp->numverts; } YQ2_VLA( GLfloat, tex, 2 * maxNumVerts ); #endif for ( ; p != 0; p = p->chain) { float *v; int j; v = p->verts[0]; #ifndef _MSC_VER // we have real VLAs, so it's safe to use one in this loop YQ2_VLA(GLfloat, tex, 2*p->numverts); #endif 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 ); } YQ2_VLAFREE( tex ); } } /* * This routine takes all the given light mapped surfaces * in the world and blends them into the framebuffer. */ static void R_BlendLightmaps(const model_t *currentmodel) { int i; msurface_t *surf, *newdrawsurf = 0; /* don't bother if we're set to fullbright or multitexture is enabled */ if (gl_config.multitexture || r_fullbright->value || !r_worldmodel->lightdata) { return; } /* don't bother writing Z */ glDepthMask(GL_FALSE); /* set the appropriate blending mode unless we're only looking at the lightmaps. */ if (!gl_lightmap->value) { glEnable(GL_BLEND); if (gl1_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 < gl_state.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 (gl1_overbrightbits->value) { R_TexEnv(GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, gl1_overbrightbits->value); } R_DrawGLPolyChain(surf->polys, 0, 0); } } } } /* render dynamic lightmaps */ if (gl1_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[0]; base += (surf->dlight_t * gl_state.block_width + surf->dlight_s) * LIGHTMAP_BYTES; R_BuildLightMap(surf, base, gl_state.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 (gl1_overbrightbits->value) { R_TexEnv(GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, gl1_overbrightbits->value); } R_DrawGLPolyChain(drawsurf->polys, (drawsurf->light_s - drawsurf->dlight_s) * (1.0 / (float)gl_state.block_width), (drawsurf->light_t - drawsurf->dlight_t) * (1.0 / (float)gl_state.block_height)); } } 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[0]; base += (surf->dlight_t * gl_state.block_width + surf->dlight_s) * LIGHTMAP_BYTES; R_BuildLightMap(surf, base, gl_state.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 (gl1_overbrightbits->value) { R_TexEnv(GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, gl1_overbrightbits->value); } R_DrawGLPolyChain(surf->polys, (surf->light_s - surf->dlight_s) * (1.0 / (float)gl_state.block_width), (surf->light_t - surf->dlight_t) * (1.0 / (float)gl_state.block_height)); } } } /* restore state */ glDisable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthMask(GL_TRUE); } static void R_RenderBrushPoly(entity_t *currententity, msurface_t *fa) { int maps; qboolean is_dynamic = false; c_brush_polys++; if (fa->flags & SURF_DRAWTURB) { R_EmitWaterPolys(fa); return; } R_DrawGLPoly(fa); if (gl_config.multitexture) { return; // lighting already done at this point for mtex } /* 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 (gl1_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 alpha; /* go back to the world matrix */ glLoadMatrixf(r_world_matrix); glEnable(GL_BLEND); R_TexEnv(GL_MODULATE); for (s = r_alpha_surfaces; s; s = s->texturechain) { c_brush_polys++; if (s->texinfo->flags & SURF_TRANS33) { alpha = 0.33f; } else if (s->texinfo->flags & SURF_TRANS66) { alpha = 0.66f; } else { alpha = 1.0f; } R_UpdateGLBuffer(buf_alpha, s->texinfo->image->texnum, 0, 0, alpha); if (s->flags & SURF_DRAWTURB) { R_EmitWaterPolys(s); } else { R_DrawGLPoly(s); } } R_ApplyGLBuffer(); // Flush the last batched array R_TexEnv(GL_REPLACE); glColor4f(1, 1, 1, 1); glDisable(GL_BLEND); r_alpha_surfaces = NULL; } static qboolean R_HasDynamicLights(msurface_t *surf, int *mapNum) { int map; qboolean is_dynamic = false; if ( r_fullbright->value || !gl1_dynamic->value || (surf->texinfo->flags & (SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP)) ) { return false; } // Any dynamic lights on this surface? for (map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++) { if (r_newrefdef.lightstyles[surf->styles[map]].white != surf->cached_light[map]) { is_dynamic = true; break; } } // No matter if it is this frame or was in the previous one: has dynamic lights if ( !is_dynamic && (surf->dlightframe == r_framecount || surf->dirty_lightmap) ) { is_dynamic = true; } if (mapNum) { *mapNum = map; } return is_dynamic; } static void R_UpdateSurfCache(msurface_t *surf, int map) { if ( ((surf->styles[map] >= 32) || (surf->styles[map] == 0)) && (surf->dlightframe != r_framecount) ) { R_SetCacheState(surf); } surf->dirty_lightmap = (surf->dlightframe == r_framecount); } static void R_RenderLightmappedPoly(entity_t *currententity, msurface_t *surf) { int i; int nv = surf->polys->numverts; float scroll; float *v; c_brush_polys++; v = surf->polys->verts[0]; if (surf->texinfo->flags & SURF_FLOWING) { scroll = -64 * ((r_newrefdef.time / 40.0) - (int) (r_newrefdef.time / 40.0)); if (scroll == 0.0) { scroll = -64.0; } } else { scroll = 0.0; } R_SetBufferIndices(GL_TRIANGLE_FAN, nv); for (i = 0; i < nv; i++, v += VERTEXSIZE) { R_BufferVertex( v[0], v[1], v[2] ); R_BufferMultiTex( v[3] + scroll, v[4], v[5], v[6] ); } } /* Upload dynamic lights to each lightmap texture (multitexture path only) */ static void R_RegenAllLightmaps() { int i, map, smax, tmax, top, bottom, left, right, bt, bb, bl, br; qboolean affected_lightmap, pixelstore_set = false; msurface_t *surf; byte *base; if ( !gl_config.multitexture ) { return; } for (i = 1; i < gl_state.max_lightmaps; i++) { if (!gl_lms.lightmap_surfaces[i] || !gl_lms.lightmap_buffer[i]) { continue; } affected_lightmap = false; bt = gl_state.block_height; bl = gl_state.block_width; bb = br = 0; for (surf = gl_lms.lightmap_surfaces[i]; surf != 0; surf = surf->lightmapchain) { if ( !R_HasDynamicLights(surf, &map) ) { continue; } affected_lightmap = true; smax = (surf->extents[0] >> 4) + 1; tmax = (surf->extents[1] >> 4) + 1; left = surf->light_s; right = surf->light_s + smax; top = surf->light_t; bottom = surf->light_t + tmax; base = gl_lms.lightmap_buffer[i]; base += (top * gl_state.block_width + left) * LIGHTMAP_BYTES; R_BuildLightMap(surf, base, gl_state.block_width * LIGHTMAP_BYTES); R_UpdateSurfCache(surf, map); if (left < bl) { bl = left; } if (right > br) { br = right; } if (top < bt) { bt = top; } if (bottom > bb) { bb = bottom; } } if (!affected_lightmap) { continue; } if (!pixelstore_set) { glPixelStorei(GL_UNPACK_ROW_LENGTH, gl_state.block_width); pixelstore_set = true; } base = gl_lms.lightmap_buffer[i]; base += (bt * gl_state.block_width + bl) * LIGHTMAP_BYTES; // upload changes R_Bind(gl_state.lightmap_textures + i); glTexSubImage2D(GL_TEXTURE_2D, 0, bl, bt, br - bl, bb - bt, GL_LIGHTMAP_FORMAT, GL_UNSIGNED_BYTE, base); } if (pixelstore_set) { glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); } } static void R_DrawTextureChains(entity_t *currententity) { int i; msurface_t *s; image_t *image; c_visible_textures = 0; if (!gl_config.multitexture) // classic path { 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_UpdateGLBuffer(buf_singletex, image->texnum, 0, s->flags, 1); R_RenderBrushPoly(currententity, s); } image->texturechain = NULL; } R_ApplyGLBuffer(); // Flush the last batched array } else // multitexture { for (i = 0, image = gltextures; i < numgltextures; i++, image++) { if (!image->registration_sequence || !image->texturechain) { continue; } c_visible_textures++; for (s = image->texturechain; s; s = s->texturechain) { if (!(s->flags & SURF_DRAWTURB)) { R_UpdateGLBuffer(buf_mtex, image->texnum, s->lightmaptexturenum, 0, 1); R_RenderLightmappedPoly(currententity, s); } } } R_ApplyGLBuffer(); R_EnableMultitexture(false); // force disabling, SURF_DRAWTURB surfaces may not exist for (i = 0, image = gltextures; i < numgltextures; i++, image++) { if (!image->registration_sequence || !image->texturechain) { continue; } for (s = image->texturechain; s; s = s->texturechain) { if (s->flags & SURF_DRAWTURB) { R_UpdateGLBuffer(buf_singletex, image->texnum, 0, s->flags, 1); R_RenderBrushPoly(currententity, s); } } image->texturechain = NULL; } R_ApplyGLBuffer(); } } static void R_DrawInlineBModel(entity_t *currententity, const model_t *currentmodel) { int i, k; cplane_t *pplane; float dot; msurface_t *psurf; dlight_t *lt; image_t *image; /* calculate dynamic lighting for bmodel */ if (!gl_config.multitexture && !gl1_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, r_dlightframecount, R_MarkSurfaceLights); } } 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 { image = R_TextureAnimation(currententity, psurf->texinfo); if (gl_config.multitexture && !(psurf->flags & SURF_DRAWTURB)) { // Dynamic lighting already generated in R_GetBrushesLighting() R_UpdateGLBuffer(buf_mtex, image->texnum, psurf->lightmaptexturenum, 0, 1); R_RenderLightmappedPoly(currententity, psurf); } else { R_UpdateGLBuffer(buf_singletex, image->texnum, 0, psurf->flags, 1); R_RenderBrushPoly(currententity, psurf); } } } } R_ApplyGLBuffer(); if (!(currententity->flags & RF_TRANSLUCENT)) { R_BlendLightmaps(currentmodel); } else { glDisable(GL_BLEND); glColor4f(1, 1, 1, 1); R_TexEnv(GL_REPLACE); } } void R_DrawBrushModel(entity_t *currententity, const model_t *currentmodel) { vec3_t mins, maxs; int i; qboolean rotated; if (currentmodel->nummodelsurfaces == 0) { return; } gl_state.currenttextures[0] = gl_state.currenttextures[1] = -1; if (currententity->angles[0] || currententity->angles[1] || currententity->angles[2]) { rotated = true; for (i = 0; i < 3; i++) { mins[i] = currententity->origin[i] - currentmodel->radius; maxs[i] = currententity->origin[i] + currentmodel->radius; } } else { rotated = false; VectorAdd(currententity->origin, currentmodel->mins, mins); VectorAdd(currententity->origin, currentmodel->maxs, maxs); } if (r_cull->value && R_CullBox(mins, maxs, frustum)) { 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, currententity->origin, modelorg); if (rotated) { vec3_t temp; vec3_t forward, right, up; VectorCopy(modelorg, temp); AngleVectors(currententity->angles, forward, right, up); modelorg[0] = DotProduct(temp, forward); modelorg[1] = -DotProduct(temp, right); modelorg[2] = DotProduct(temp, up); } glPushMatrix(); currententity->angles[0] = -currententity->angles[0]; currententity->angles[2] = -currententity->angles[2]; R_RotateForEntity(currententity); currententity->angles[0] = -currententity->angles[0]; currententity->angles[2] = -currententity->angles[2]; if (gl_lightmap->value) { R_TexEnv(GL_REPLACE); } else { R_TexEnv(GL_MODULATE); } R_DrawInlineBModel(currententity, currentmodel); glPopMatrix(); if (gl_zfix->value) { glDisable(GL_POLYGON_OFFSET_FILL); } } static void R_RecursiveWorldNode(entity_t *currententity, 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_cull->value && R_CullBox(node->minmaxs, node->minmaxs + 3, frustum)) { return; } /* if a leaf node, draw stuff */ if (node->contents != CONTENTS_NODE) { pleaf = (mleaf_t *)node; /* check for door connected areas */ if (!R_AreaVisible(r_newrefdef.areabits, pleaf)) 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(currententity, 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(currententity, surf->texinfo); } else { /* the polygon is visible, so add it to the texture sorted chain */ image = R_TextureAnimation(currententity, surf->texinfo); surf->texturechain = image->texturechain; image->texturechain = surf; if (gl_config.multitexture && !(surf->texinfo->flags & SURF_WARP)) // needed for R_RegenAllLightmaps() { surf->lightmapchain = gl_lms.lightmap_surfaces[surf->lightmaptexturenum]; gl_lms.lightmap_surfaces[surf->lightmaptexturenum] = surf; } } } /* recurse down the back side */ R_RecursiveWorldNode(currententity, node->children[!side]); } /* * This is for the RegenAllLightmaps() function to be able to regenerate * lighting not only for the world, but also for the brushes in the entity list. * Logic extracted from R_DrawBrushModel() & R_DrawInlineBModel(). */ static void R_GetBrushesLighting(void) { int i, k; vec3_t mins, maxs; msurface_t *surf; cplane_t *pplane; dlight_t *lt; float dot; if (!gl_config.multitexture || !r_drawentities->value || gl1_flashblend->value) { return; } for (i = 0; i < r_newrefdef.num_entities; i++) { entity_t *currententity = &r_newrefdef.entities[i]; if (currententity->flags & RF_BEAM) { continue; } const model_t *currentmodel = currententity->model; if (!currentmodel || currentmodel->type != mod_brush || currentmodel->nummodelsurfaces == 0) { continue; } // from R_DrawBrushModel() if (currententity->angles[0] || currententity->angles[1] || currententity->angles[2]) { for (k = 0; k < 3; k++) { mins[k] = currententity->origin[k] - currentmodel->radius; maxs[k] = currententity->origin[k] + currentmodel->radius; } } else { VectorAdd(currententity->origin, currentmodel->mins, mins); VectorAdd(currententity->origin, currentmodel->maxs, maxs); } if (r_cull->value && R_CullBox(mins, maxs, frustum)) { continue; } // from R_DrawInlineBModel() lt = r_newrefdef.dlights; for (k = 0; k < r_newrefdef.num_dlights; k++, lt++) { R_MarkLights(lt, 1 << k, currentmodel->nodes + currentmodel->firstnode, r_dlightframecount, R_MarkSurfaceLights); } surf = ¤tmodel->surfaces[currentmodel->firstmodelsurface]; for (k = 0; k < currentmodel->nummodelsurfaces; k++, surf++) { if (surf->texinfo->flags & (SURF_TRANS33 | SURF_TRANS66 | SURF_WARP) || surf->flags & SURF_DRAWTURB) { continue; } // find which side of the node we are on pplane = surf->plane; dot = DotProduct(modelorg, pplane->normal) - pplane->dist; if (((surf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(surf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { surf->lightmapchain = gl_lms.lightmap_surfaces[surf->lightmaptexturenum]; gl_lms.lightmap_surfaces[surf->lightmaptexturenum] = surf; } } } } void R_DrawWorld(void) { entity_t ent; if (!r_drawworld->value) { return; } if (r_newrefdef.rdflags & RDF_NOWORLDMODEL) { return; } 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); 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(&ent, r_worldmodel->nodes); R_GetBrushesLighting(); R_RegenAllLightmaps(); R_DrawTextureChains(&ent); R_BlendLightmaps(r_worldmodel); R_DrawSkyBox(); R_DrawTriangleOutlines(); } /* * Mark the leaves and nodes that are * in the PVS for the current cluster */ void R_MarkLeaves(void) { const byte *vis; YQ2_ALIGNAS_TYPE(int) 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) && !r_novis->value && (r_viewcluster != -1)) { return; } /* development aid to let you run around and see exactly where the pvs ends */ if (r_lockpvs->value) { return; } r_visframecount++; r_oldviewcluster = r_viewcluster; r_oldviewcluster2 = r_viewcluster2; if (r_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_8_40/src/client/refresh/gl1/gl1_warp.c000066400000000000000000000304041465112212000221600ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 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(model_t *loadmodel, 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, nv; 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; nv = p->numverts; R_SetBufferIndices(GL_TRIANGLE_FAN, nv); for ( i = 0, v = p->verts [ 0 ]; i < nv; i++, v += VERTEXSIZE ) { os = v [ 3 ]; ot = v [ 4 ]; s = os + r_turbsin [ (int) ( ( ot * 0.125 + rdt ) * TURBSCALE ) & 255 ] + scroll; t = ot + r_turbsin [ (int) ( ( os * 0.125 + rdt ) * TURBSCALE ) & 255 ]; R_BufferVertex( v[0], v[1], v[2] ); R_BufferSingleTex( s * ( 1.0 / 64 ), t * ( 1.0 / 64 ) ); } } } 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 (r_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) { char skyname[MAX_QPATH]; int i; Q_strlcpy(skyname, name, sizeof(skyname)); skyrotate = rotate; VectorCopy(axis, skyaxis); for (i = 0; i < 6; i++) { image_t *image; image = (image_t *)GetSkyImage(skyname, suf[i], gl_config.palettedtexture, (findimage_t)R_FindImage); if (!image) { R_Printf(PRINT_ALL, "%s: can't load %s:%s sky\n", __func__, skyname, suf[i]); image = r_notexture; } sky_images[i] = image; } sky_min = 1.0 / 512; sky_max = 511.0 / 512; } yquake2-QUAKE2_8_40/src/client/refresh/gl1/header/000077500000000000000000000000001465112212000215275ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/gl1/header/local.h000066400000000000000000000357111465112212000230010ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 #define MAX_LIGHTMAPS 128 #define MAX_SCRAPS 1 #define TEXNUM_LIGHTMAPS 1024 #define TEXNUM_SCRAPS (TEXNUM_LIGHTMAPS + MAX_LIGHTMAPS) #define TEXNUM_IMAGES (TEXNUM_SCRAPS + MAX_SCRAPS) #define MAX_GLTEXTURES 1024 #define BLOCK_WIDTH 128 // default values; now defined in glstate_t #define BLOCK_HEIGHT 128 #define REF_VERSION "Yamagi Quake II OpenGL Refresher" #define BACKFACE_EPSILON 0.01 #define LIGHTMAP_BYTES 4 #define MAX_TEXTURE_UNITS 2 #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_mode, rserr_unknown } rserr_t; typedef enum { buf_2d, buf_singletex, buf_mtex, buf_alpha, buf_alias, buf_flash, buf_shadow } buffered_draw_t; #include "model.h" void R_SetDefaultState(void); extern float gldepthmin, gldepthmax; extern image_t gltextures[MAX_GLTEXTURES]; extern int numgltextures; extern image_t *r_notexture; extern image_t *r_particletexture; 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 qboolean IsHighDPIaware; extern cvar_t *r_norefresh; extern cvar_t *gl_lefthand; extern cvar_t *r_gunfov; extern cvar_t *r_farsee; extern cvar_t *r_drawentities; extern cvar_t *r_drawworld; extern cvar_t *r_speeds; extern cvar_t *r_fullbright; extern cvar_t *r_novis; extern cvar_t *r_lerpmodels; extern cvar_t *r_fixsurfsky; extern cvar_t *r_lightlevel; extern cvar_t *gl1_overbrightbits; extern cvar_t *gl1_palettedtexture; extern cvar_t *gl1_pointparameters; extern cvar_t *gl1_multitexture; extern cvar_t *gl1_particle_min_size; extern cvar_t *gl1_particle_max_size; extern cvar_t *gl1_particle_size; extern cvar_t *gl1_particle_att_a; extern cvar_t *gl1_particle_att_b; extern cvar_t *gl1_particle_att_c; extern cvar_t *gl1_particle_square; extern cvar_t *r_mode; extern cvar_t *r_customwidth; extern cvar_t *r_customheight; extern cvar_t *r_retexturing; extern cvar_t *r_scale8bittextures; extern cvar_t *r_validation; extern cvar_t *gl_nolerp_list; extern cvar_t *r_lerp_list; extern cvar_t *r_2D_unfiltered; extern cvar_t *r_videos_unfiltered; extern cvar_t *gl_lightmap; extern cvar_t *gl_shadows; extern cvar_t *gl1_stencilshadow; extern cvar_t *gl1_dynamic; extern cvar_t *gl_nobind; extern cvar_t *gl1_round_down; extern cvar_t *gl1_picmip; extern cvar_t *gl_showtris; extern cvar_t *gl_showbbox; extern cvar_t *gl_finish; extern cvar_t *gl1_ztrick; extern cvar_t *gl_zfix; extern cvar_t *r_clear; extern cvar_t *r_cull; extern cvar_t *gl1_polyblend; extern cvar_t *gl1_flashblend; extern cvar_t *r_modulate; extern cvar_t *gl_drawbuffer; extern cvar_t *r_vsync; extern cvar_t *gl_anisotropic; extern cvar_t *gl_texturemode; extern cvar_t *gl1_texturealphamode; extern cvar_t *gl1_texturesolidmode; extern cvar_t *gl1_saturatelighting; extern cvar_t *r_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); qboolean R_Bind(int texnum); void R_TexEnv(GLenum value); void R_SelectTexture(GLenum); void R_MBind(GLenum target, int texnum); void R_EnableMultitexture(qboolean enable); void R_LightPoint(entity_t *currententity, 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_ScreenShot(void); void R_DrawAliasModel(entity_t *currententity, const model_t *currentmodel); void R_DrawBrushModel(entity_t *currententity, const model_t *currentmodel); void R_DrawSpriteModel(entity_t *currententity, const model_t *currentmodel); void R_DrawBeam(entity_t *e); void R_DrawWorld(void); void R_RenderDlights(void); void R_DrawAlphaSurfaces(void); void R_InitParticleTexture(void); void Draw_InitLocal(void); void R_SubdivideSurface(model_t *loadmodel, msurface_t *fa); void R_RotateForEntity(entity_t *e); void R_MarkLeaves(void); extern int r_dlightframecount; 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_MarkSurfaceLights(dlight_t *light, int bit, mnode_t *node, int lightframecount); void COM_StripExtension(char *in, char *out); void R_SwapBuffers(int); image_t *R_LoadPic(const char *name, byte *pic, int width, int realwidth, int height, int realheight, size_t data_size, imagetype_t type, int bits); image_t *R_FindImage(const 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); qboolean R_ImageHasFreeSpace(void); void R_TextureAlphaMode(char *string); void R_TextureSolidMode(char *string); int Scrap_AllocBlock(int w, int h, int *x, int *y); void R_ApplyGLBuffer(void); void R_UpdateGLBuffer(buffered_draw_t type, int colortex, int lighttex, int flags, float alpha); void R_Buffer2DQuad(GLfloat ul_vx, GLfloat ul_vy, GLfloat dr_vx, GLfloat dr_vy, GLfloat ul_tx, GLfloat ul_ty, GLfloat dr_tx, GLfloat dr_ty); void R_SetBufferIndices(GLenum type, GLuint vertices_num); void R_BufferVertex(GLfloat x, GLfloat y, GLfloat z); void R_BufferSingleTex(GLfloat s, GLfloat t); void R_BufferMultiTex(GLfloat cs, GLfloat ct, GLfloat ls, GLfloat lt); void R_BufferColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a); #ifdef DEBUG void glCheckError_(const char *file, const char *function, int line); // Ideally, the following list should contain all OpenGL calls. // Either way, errors are caught, since error flags are persisted until the next glGetError() call. // So they show, even if the location of the error is inaccurate. #define glDrawArrays(...) glDrawArrays(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glDrawElements(...) glDrawElements(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glTexImage2D(...) glTexImage2D(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glTexSubImage2D(...) glTexSubImage2D(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glTexEnvf(...) glTexEnvf(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glTexEnvi(...) glTexEnvi(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glVertexPointer(...) glVertexPointer(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glTexCoordPointer(...) glTexCoordPointer(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glColorPointer(...) glColorPointer(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glTexParameteri(...) glTexParameteri(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glBindTexture(...) glBindTexture(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glFrustum(...) glFrustum(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glTranslatef(...) glTranslatef(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glRotatef(...) glRotatef(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glScalef(...) glScalef(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glScissor(...) glScissor(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glBlendFunc(...) glBlendFunc(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glDepthFunc(...) glDepthFunc(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glDepthMask(...) glDepthMask(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glDepthRange(...) glDepthRange(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glEnable(...) glEnable(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glDisable(...) glDisable(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glEnableClientState(...) glEnableClientState(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glDisableClientState(...) glDisableClientState(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glPushMatrix(...) glPushMatrix(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glPopMatrix(...) glPopMatrix(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glMatrixMode(...) glMatrixMode(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glOrtho(...) glOrtho(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glColorMask(...) glColorMask(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glStencilOp(...) glStencilOp(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glStencilFunc(...) glStencilFunc(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glDrawBuffer(...) glDrawBuffer(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glReadPixels(...) glReadPixels(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glClear(...) glClear(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glClearColor(...) glClearColor(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glClearStencil(...) glClearStencil(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glDeleteTextures(...) glDeleteTextures(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glFinish() glFinish(); glCheckError_(__FILE__, __func__, __LINE__) #define glAlphaFunc(...) glAlphaFunc(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glHint(...) glHint(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glLoadIdentity() glLoadIdentity(); glCheckError_(__FILE__, __func__, __LINE__) #define glBegin(...) glBegin(__VA_ARGS__); glCheckError_(__FILE__, __func__, __LINE__) #define glEnd() glEnd(); glCheckError_(__FILE__, __func__, __LINE__) #endif /* GL extension emulation functions */ void R_DrawParticles2(int n, const particle_t particles[], const unsigned *colortable); /* * 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; qboolean multitexture; // ---- float max_anisotropy; } glconfig_t; typedef struct { float inverse_intensity; qboolean fullscreen; int prev_mode; unsigned char *d_16to8table; int lightmap_textures; int currenttextures[MAX_TEXTURE_UNITS]; int currenttmu; GLenum currenttarget; float camera_separation; enum stereo_modes stereo_mode; qboolean stencil; int block_width, // replaces BLOCK_WIDTH block_height, // replaces BLOCK_HEIGHT max_lightmaps, // the larger the lightmaps, the fewer the max lightmaps scrap_width, // size for scrap (atlas of 2D elements) scrap_height; } glstate_t; typedef struct { int internal_format; int current_lightmap_texture; msurface_t *lightmap_surfaces[MAX_LIGHTMAPS]; int *allocated; // formerly allocated[BLOCK_WIDTH]; /* the lightmap texture data needs to be kept in main memory so texsubimage can update properly */ byte *lightmap_buffer[MAX_LIGHTMAPS]; } gllightmapstate_t; extern glconfig_t gl_config; extern glstate_t gl_state; /* * Updates the gamma ramp. */ void RI_UpdateGamma(void); /* * Enables or disables the vsync. */ void RI_SetVsync(void); /* * Shuts the GL context down. */ void RI_ShutdownContext(void); /* * Returns the address of the GL function proc, * or NULL if the function is not found. */ void *RI_GetProcAddress (const char* proc); /* * Fills the actual size of the drawable into width and height. */ void RI_GetDrawableSize(int* width, int* height); /* * Returns the SDL major version. Implemented * here to not polute gl1_main.c with the SDL * headers. */ int RI_GetSDLVersion(); /* g11_draw */ 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, const byte *data, int bits); #endif yquake2-QUAKE2_8_40/src/client/refresh/gl1/header/model.h000066400000000000000000000070531465112212000230050ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 VERTEXSIZE 7 /* in memory representation */ 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; qboolean dirty_lightmap; // lightmap has dynamic lights from previous frame (mtex only) int lightmaptexturenum; byte styles[MAXLIGHTMAPS]; float cached_light[MAXLIGHTMAPS]; /* values currently used in lightmap */ byte *samples; /* [numstyles*surfsize] */ } msurface_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; struct model_s *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; // submodules vec3_t origin; // for sounds or lights } model_t; void Mod_Init(void); void Mod_ClearAll(void); const byte *Mod_ClusterPVS(int cluster, const 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_8_40/src/client/refresh/gl1/header/qgl.h000066400000000000000000000055021465112212000224650ustar00rootroot00000000000000/* * 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__) #define GL_SILENCE_DEPRECATION #include #else #include #endif #ifndef APIENTRY #define APIENTRY #endif // Extracted from #ifndef GL_VERSION_1_4 #define GL_POINT_SIZE_MIN 0x8126 #define GL_POINT_SIZE_MAX 0x8127 #define GL_POINT_DISTANCE_ATTENUATION 0x8129 #define GL_GENERATE_MIPMAP 0x8191 #endif #ifndef GL_VERSION_1_3 #define GL_TEXTURE0 0x84C0 #define GL_TEXTURE1 0x84C1 #define GL_MULTISAMPLE 0x809D #define GL_COMBINE 0x8570 #define GL_RGB_SCALE 0x8573 #endif #ifndef GL_EXT_shared_texture_palette #define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB #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_NV_multisample_filter_hint #define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534 #endif // ======================================================================= /* * This is responsible for setting up our QGL extension pointers */ void QGL_Init ( void ); /* * Unloads the specified DLL then nulls out all the proc pointers. */ void QGL_Shutdown ( void ); /* GL extensions */ extern void ( APIENTRY *qglPointParameterf ) ( GLenum param, GLfloat value ); extern void ( APIENTRY *qglPointParameterfv ) ( GLenum param, const GLfloat *value ); extern void ( APIENTRY *qglColorTableEXT ) ( GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid * ); extern void ( APIENTRY *qglActiveTexture ) ( GLenum texture ); extern void ( APIENTRY *qglClientActiveTexture ) ( GLenum texture ); #endif yquake2-QUAKE2_8_40/src/client/refresh/gl1/qgl.c000066400000000000000000000045241465112212000212330ustar00rootroot00000000000000/* * 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 *qglPointParameterf)(GLenum param, GLfloat value); void (APIENTRY *qglPointParameterfv)(GLenum param, const GLfloat *value); void (APIENTRY *qglColorTableEXT)(GLenum, GLenum, GLsizei, GLenum, GLenum, const GLvoid *); void (APIENTRY *qglActiveTexture) (GLenum texture); void (APIENTRY *qglClientActiveTexture) (GLenum texture); /* ========================================================================= */ void QGL_EXT_Reset ( void ) { qglPointParameterf = NULL; qglPointParameterfv = NULL; qglColorTableEXT = NULL; qglActiveTexture = NULL; qglClientActiveTexture = NULL; } /* ========================================================================= */ void QGL_Shutdown ( void ) { // Reset GL extension pointers QGL_EXT_Reset(); } /* ========================================================================= */ void QGL_Init (void) { // Reset GL extension pointers QGL_EXT_Reset(); } yquake2-QUAKE2_8_40/src/client/refresh/gl3/000077500000000000000000000000001465112212000203015ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/gl3/gl3_draw.c000066400000000000000000000224021465112212000221470ustar00rootroot00000000000000/* * 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 = R_FindPic("conchars", (findimage_t)GL3_FindImage); if (!draw_chars) { ri.Sys_Error(ERR_FATAL, "%s: Couldn't load pics/conchars.pcx", __func__); } // 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) { return R_FindPic(name, (findimage_t)GL3_FindImage); } void GL3_Draw_GetPicSize(int *w, int *h, char *pic) { gl3image_t *gl; gl = R_FindPic(pic, (findimage_t)GL3_FindImage); 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 = R_FindPic(pic, (findimage_t)GL3_FindImage); 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 = R_FindPic(pic, (findimage_t)GL3_FindImage); 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 = R_FindPic(pic, (findimage_t)GL3_FindImage); 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); } void GL3_DrawFrameBufferObject(int x, int y, int w, int h, GLuint fboTexture, const float v_blend[4]) { qboolean underwater = (gl3_newrefdef.rdflags & RDF_UNDERWATER) != 0; gl3ShaderInfo_t* shader = underwater ? &gl3state.si2DpostProcessWater : &gl3state.si2DpostProcess; GL3_UseProgram(shader->shaderProgram); GL3_Bind(fboTexture); if(underwater && shader->uniLmScalesOrTime != -1) { glUniform1f(shader->uniLmScalesOrTime, gl3_newrefdef.time); } if(shader->uniVblend != -1) { glUniform4fv(shader->uniVblend, 1, v_blend); } drawTexturedRectangle(x, y, w, h, 0, 1, 1, 0); } /* * 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) { if (gl_polyblend->value == 0) { return; } 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, const byte *data, int bits) { int i, j; GL3_Bind(0); unsigned image32[320*240]; /* was 256 * 256, but we want a bit more space */ unsigned* img = image32; if (bits == 32) { img = (unsigned *)data; } else { 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 no mipmaps) // but gl_filter_max (either GL_LINEAR or GL_NEAREST) should do the trick. GLint filter = (r_videos_unfiltered->value == 0) ? gl_filter_max : GL_NEAREST; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); drawTexturedRectangle(x, y, w, h, 0.0f, 0.0f, 1.0f, 1.0f); glDeleteTextures(1, &glTex); GL3_Bind(0); } yquake2-QUAKE2_8_40/src/client/refresh/gl3/gl3_image.c000066400000000000000000000444351465112212000223060ustar00rootroot00000000000000/* * 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. * * ======================================================================= * * Texture handling for OpenGL3 * * ======================================================================= */ #include "header/local.h" 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} }; int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; int gl_filter_max = GL_LINEAR; gl3image_t gl3textures[MAX_GL3TEXTURES]; int numgl3textures = 0; static int image_max = 0; void GL3_TextureMode(char *string) { const int num_modes = sizeof(modes)/sizeof(modes[0]); int i; for (i = 0; i < num_modes; i++) { if (!Q_stricmp(modes[i].name, string)) { break; } } if (i == num_modes) { R_Printf(PRINT_ALL, "bad filter name '%s' (probably from gl_texturemode)\n", string); return; } gl_filter_min = modes[i].minimize; gl_filter_max = modes[i].maximize; /* clamp selected anisotropy */ if (gl3config.anisotropic) { if (gl_anisotropic->value > gl3config.max_anisotropy) { ri.Cvar_SetValue("r_anisotropic", gl3config.max_anisotropy); } } else { ri.Cvar_SetValue("r_anisotropic", 0.0); } gl3image_t *glt; const char* nolerplist = gl_nolerp_list->string; const char* lerplist = r_lerp_list->string; qboolean unfiltered2D = r_2D_unfiltered->value != 0; /* change all the existing texture objects */ for (i = 0, glt = gl3textures; i < numgl3textures; i++, glt++) { qboolean nolerp = false; /* r_2D_unfiltered and gl_nolerp_list allow rendering stuff unfiltered even if gl_filter_* is filtered */ if (unfiltered2D && glt->type == it_pic) { // exception to that exception: stuff on the r_lerp_list nolerp = (lerplist== NULL) || (strstr(lerplist, glt->name) == NULL); } else if(nolerplist != NULL && strstr(nolerplist, glt->name) != NULL) { nolerp = true; } GL3_SelectTMU(GL_TEXTURE0); GL3_Bind(glt->texnum); if ((glt->type != it_pic) && (glt->type != it_sky)) /* mipmapped texture */ { 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, Q_max(gl_anisotropic->value, 1.f)); } } else /* texture has no mipmaps */ { if (nolerp) { // this texture shouldn't be filtered at all (no gl_nolerp_list or r_2D_unfiltered case) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } else { // we can't use gl_filter_min which might be GL_*_MIPMAP_* // also, there's no anisotropic filtering for textures w/o mipmaps glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); } } } } 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 i; int c = width * height; byte *scan = ((byte *)data) + 3; int comp = gl3_tex_solid_format; int samples = gl3_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 // if the texture has no mipmaps, we can't use gl_filter_min which might be GL_*_MIPMAP_* { 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, Q_max(gl_anisotropic->value, 1.f)); } return res; } /* * Returns has_alpha */ qboolean GL3_Upload8(byte *data, int width, int height, qboolean mipmap, qboolean is_sky) { int s = width * height; unsigned *trans = malloc(s * sizeof(unsigned)); for (int i = 0; i < s; i++) { int 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]; } } qboolean ret = GL3_Upload32(trans, width, height, mipmap); free(trans); return ret; } 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 = 0; int i; /* 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, size_t data_size, imagetype_t type, int bits) { gl3image_t *image = NULL; GLuint texNum=0; int i; qboolean nolerp = false; if (r_2D_unfiltered->value && type == it_pic) { // if r_2D_unfiltered is true(ish), nolerp should usually be true, // *unless* the texture is on the r_lerp_list nolerp = (r_lerp_list->string == NULL) || (strstr(r_lerp_list->string, name) == NULL); } else 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, "%s: \"%s\" is too long", __func__, 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->is_lava = (strstr(name, "lava") != NULL); // 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) { // resize 8bit images only when we forced such logic if (r_scale8bittextures->value) { byte *image_converted; int scale = 2; // scale 3 times if lerp image if (!nolerp && (vid.height >= 240 * 3)) scale = 3; image_converted = malloc(width * height * scale * scale); if (!image_converted) return NULL; if (scale == 3) { scale3x(pic, image_converted, width, height); } else { scale2x(pic, image_converted, width, height); } image->has_alpha = GL3_Upload8(image_converted, width * scale, height * scale, (image->type != it_pic && image->type != it_sky), image->type == it_sky); free(image_converted); } else { 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; } /* * Finds or loads the given image or NULL */ gl3image_t * GL3_FindImage(const char *name, imagetype_t type) { gl3image_t *image; int i, len; char *ptr; char namewe[256]; 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 - (strlen(ext) + 1)); 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 // image = (gl3image_t *)R_LoadImage(name, namewe, ext, type, r_retexturing->value, (loadimage_t)GL3_LoadPic); if (!image && r_validation->value) { R_Printf(PRINT_ALL, "%s: can't load %s\n", __func__, name); } 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)); } } qboolean GL3_ImageHasFreeSpace(void) { int i, used; gl3image_t *image; used = 0; for (i = 0, image = gl3textures; i < numgl3textures; i++, image++) { if (!image->name[0]) continue; if (image->registration_sequence == registration_sequence) { used ++; } } if (image_max < used) { image_max = used; } // should same size of free slots as currently used return (numgl3textures + used) < MAX_GL3TEXTURES; } 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, used, texels; qboolean freeup; gl3image_t *image; const char *formatstrings[2] = { "RGB ", "RGBA" }; const char* potstrings[2] = { " POT", "NPOT" }; R_Printf(PRINT_ALL, "------------------\n"); texels = 0; used = 0; for (i = 0, image = gl3textures; i < numgl3textures; i++, image++) { int w, h; char *in_use = ""; qboolean isNPOT = false; if (image->texnum == 0) { continue; } if (image->registration_sequence == registration_sequence) { in_use = "*"; used++; } w = image->width; h = image->height; isNPOT = IsNPOT(w) || IsNPOT(h); texels += w*h; char imageType = '?'; switch (image->type) { case it_skin: imageType = 'M'; break; case it_sprite: imageType = 'S'; break; case it_wall: imageType = 'W'; break; case it_pic: imageType = 'P'; break; case it_sky: imageType = 'Y'; break; default: imageType = '?'; break; } char isLava = image->is_lava ? 'L' : ' '; R_Printf(PRINT_ALL, "%c%c %3i %3i %s %s: %s %s\n", imageType, isLava, w, h, formatstrings[image->has_alpha], potstrings[isNPOT], image->name, in_use); } R_Printf(PRINT_ALL, "Total texel count (not counting mipmaps): %i\n", texels); freeup = GL3_ImageHasFreeSpace(); R_Printf(PRINT_ALL, "Used %d of %d images%s.\n", used, image_max, freeup ? ", has free space" : ""); } yquake2-QUAKE2_8_40/src/client/refresh/gl3/gl3_light.c000066400000000000000000000217741465112212000223340ustar00rootroot00000000000000/* * 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; int r_dlightframecount; static vec3_t pointcolor; static cplane_t *lightplane; /* used as shadow plane */ vec3_t lightspot; void GL3_MarkSurfaceLights(dlight_t *light, int bit, mnode_t *node, int r_dlightframecount) { msurface_t *surf; int i; /* mark the polygons */ surf = gl3_worldmodel->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { int sidebit; float dist; 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; } } 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]; R_MarkLights(l, 1 << i, gl3_worldmodel->nodes, r_dlightframecount, GL3_MarkSurfaceLights); 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 != CONTENTS_NODE) { 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); lightmap += 3 * (dt * ((surf->extents[0] >> 4) + 1) + ds); for (maps = 0; maps < MAX_LIGHTMAPS_PER_SURFACE && surf->styles[maps] != 255; maps++) { const float *rgb; int j; rgb = gl3_newrefdef.lightstyles[surf->styles[maps]].rgb; /* Apply light level to models */ for (j = 0; j < 3; j++) { float scale; scale = rgb[j] * r_modulate->value; pointcolor[j] += lightmap[j] * scale * (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(entity_t *currententity, 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, r_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_8_40/src/client/refresh/gl3/gl3_lightmap.c000066400000000000000000000143371465112212000230270ustar00rootroot00000000000000/* * 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(gl3model_t *currentmodel, 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; } } 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_8_40/src/client/refresh/gl3/gl3_main.c000066400000000000000000001645621465112212000221540ustar00rootroot00000000000000/* * 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 "../ref_shared.h" #include "header/local.h" #define HANDMADE_MATH_IMPLEMENTATION #include "header/HandmadeMath.h" #define DG_DYNARR_IMPLEMENTATION #include "header/DG_dynarr.h" #ifdef YQ2_GL3_GLES3 #define REF_VERSION "Yamagi Quake II OpenGL ES3 Refresher" #else #define REF_VERSION "Yamagi Quake II OpenGL3 Refresher" #endif 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; 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; 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 *r_vsync; cvar_t *r_retexturing; cvar_t *r_scale8bittextures; cvar_t *vid_fullscreen; cvar_t *r_mode; cvar_t *r_customwidth; cvar_t *r_customheight; cvar_t *vid_gamma; cvar_t *gl_anisotropic; cvar_t *gl_texturemode; cvar_t *gl_drawbuffer; cvar_t *r_clear; cvar_t *gl3_particle_size; cvar_t *gl3_particle_fade_factor; cvar_t *gl3_particle_square; cvar_t *gl3_colorlight; cvar_t *gl_polyblend; cvar_t *gl_lefthand; cvar_t *r_gunfov; cvar_t *r_farsee; cvar_t *gl3_intensity; cvar_t *gl3_intensity_2D; cvar_t *r_lightlevel; cvar_t *gl3_overbrightbits; cvar_t *r_norefresh; cvar_t *r_drawentities; cvar_t *r_drawworld; cvar_t *gl_nolerp_list; cvar_t *r_lerp_list; cvar_t *r_2D_unfiltered; cvar_t *r_videos_unfiltered; cvar_t *gl_nobind; cvar_t *r_lockpvs; cvar_t *r_novis; cvar_t *r_speeds; cvar_t *gl_finish; cvar_t *r_cull; cvar_t *gl_zfix; cvar_t *r_fullbright; cvar_t *r_modulate; cvar_t *gl_lightmap; cvar_t *gl_shadows; cvar_t *gl3_debugcontext; cvar_t *gl3_usebigvbo; cvar_t *r_fixsurfsky; cvar_t *r_palettedtexture; cvar_t *r_validation; cvar_t *gl3_usefbo; // 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); r_gunfov = ri.Cvar_Get("r_gunfov", "80", CVAR_ARCHIVE); r_farsee = ri.Cvar_Get("r_farsee", "0", CVAR_LATCH | CVAR_ARCHIVE); gl_drawbuffer = ri.Cvar_Get("gl_drawbuffer", "GL_BACK", 0); r_vsync = ri.Cvar_Get("r_vsync", "1", CVAR_ARCHIVE); gl_msaa_samples = ri.Cvar_Get ( "r_msaa_samples", "0", CVAR_ARCHIVE ); r_retexturing = ri.Cvar_Get("r_retexturing", "1", CVAR_ARCHIVE); r_scale8bittextures = ri.Cvar_Get("r_scale8bittextures", "0", CVAR_ARCHIVE); gl3_debugcontext = ri.Cvar_Get("gl3_debugcontext", "0", 0); r_mode = ri.Cvar_Get("r_mode", "4", CVAR_ARCHIVE); r_customwidth = ri.Cvar_Get("r_customwidth", "1024", CVAR_ARCHIVE); r_customheight = ri.Cvar_Get("r_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); // if set to 0, lights (from lightmaps, dynamic lights and on models) are white instead of colored gl3_colorlight = ri.Cvar_Get("gl3_colorlight", "1", CVAR_ARCHIVE); gl_polyblend = ri.Cvar_Get("gl_polyblend", "1", CVAR_ARCHIVE); // 0: use lots of calls to glBufferData() // 1: reduce calls to glBufferData() with one big VBO (see GL3_BufferAndDraw3D()) // -1: auto (let yq2 choose to enable/disable this based on detected driver) gl3_usebigvbo = ri.Cvar_Get("gl3_usebigvbo", "-1", CVAR_ARCHIVE); r_norefresh = ri.Cvar_Get("r_norefresh", "0", 0); r_drawentities = ri.Cvar_Get("r_drawentities", "1", 0); r_drawworld = ri.Cvar_Get("r_drawworld", "1", 0); r_fullbright = ri.Cvar_Get("r_fullbright", "0", 0); r_fixsurfsky = ri.Cvar_Get("r_fixsurfsky", "0", CVAR_ARCHIVE); r_palettedtexture = ri.Cvar_Get("r_palettedtexture", "0", 0); r_validation = ri.Cvar_Get("r_validation", "0", CVAR_ARCHIVE); /* don't bilerp characters and crosshairs */ gl_nolerp_list = ri.Cvar_Get("r_nolerp_list", "pics/conchars.pcx pics/ch1.pcx pics/ch2.pcx pics/ch3.pcx", CVAR_ARCHIVE); /* textures that should always be filtered, even if r_2D_unfiltered or an unfiltered gl mode is used */ r_lerp_list = ri.Cvar_Get("r_lerp_list", "", CVAR_ARCHIVE); /* don't bilerp any 2D elements */ r_2D_unfiltered = ri.Cvar_Get("r_2D_unfiltered", "0", CVAR_ARCHIVE); /* don't bilerp videos */ r_videos_unfiltered = ri.Cvar_Get("r_videos_unfiltered", "0", CVAR_ARCHIVE); 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("r_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); r_lightlevel = ri.Cvar_Get("r_lightlevel", "0", 0); gl3_overbrightbits = ri.Cvar_Get("gl3_overbrightbits", "1.3", CVAR_ARCHIVE); gl_lightmap = ri.Cvar_Get("r_lightmap", "0", 0); gl_shadows = ri.Cvar_Get("r_shadows", "0", CVAR_ARCHIVE); r_modulate = ri.Cvar_Get("r_modulate", "1", CVAR_ARCHIVE); gl_zfix = ri.Cvar_Get("gl_zfix", "0", 0); r_clear = ri.Cvar_Get("r_clear", "0", 0); r_cull = ri.Cvar_Get("r_cull", "1", 0); r_lockpvs = ri.Cvar_Get("r_lockpvs", "0", 0); r_novis = ri.Cvar_Get("r_novis", "0", 0); r_speeds = ri.Cvar_Get("r_speeds", "0", 0); gl_finish = ri.Cvar_Get("gl_finish", "0", CVAR_ARCHIVE); gl3_usefbo = ri.Cvar_Get("gl3_usefbo", "1", CVAR_ARCHIVE); // use framebuffer object for postprocess effects (water) #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); //r_norefresh = ri.Cvar_Get("r_norefresh", "0", 0); //r_fullbright = ri.Cvar_Get("r_fullbright", "0", 0); //r_drawentities = ri.Cvar_Get("r_drawentities", "1", 0); //r_drawworld = ri.Cvar_Get("r_drawworld", "1", 0); //r_novis = ri.Cvar_Get("r_novis", "0", 0); //r_lerpmodels = ri.Cvar_Get("r_lerpmodels", "1", 0); NOTE: screw this, it looks horrible without //r_speeds = ri.Cvar_Get("r_speeds", "0", 0); //r_lightlevel = ri.Cvar_Get("r_lightlevel", "0", 0); //gl_overbrightbits = ri.Cvar_Get("gl_overbrightbits", "0", CVAR_ARCHIVE); gl1_particle_min_size = ri.Cvar_Get("gl1_particle_min_size", "2", CVAR_ARCHIVE); gl1_particle_max_size = ri.Cvar_Get("gl1_particle_max_size", "40", CVAR_ARCHIVE); //gl1_particle_size = ri.Cvar_Get("gl1_particle_size", "40", CVAR_ARCHIVE); gl1_particle_att_a = ri.Cvar_Get("gl1_particle_att_a", "0.01", CVAR_ARCHIVE); gl1_particle_att_b = ri.Cvar_Get("gl1_particle_att_b", "0.0", CVAR_ARCHIVE); gl1_particle_att_c = ri.Cvar_Get("gl1_particle_att_c", "0.01", CVAR_ARCHIVE); //gl_modulate = ri.Cvar_Get("gl_modulate", "1", CVAR_ARCHIVE); //r_mode = ri.Cvar_Get("r_mode", "4", CVAR_ARCHIVE); //gl_lightmap = ri.Cvar_Get("r_lightmap", "0", 0); //gl_shadows = ri.Cvar_Get("r_shadows", "0", CVAR_ARCHIVE); //gl_nobind = ri.Cvar_Get("gl_nobind", "0", 0); gl_showtris = ri.Cvar_Get("gl_showtris", "0", 0); gl_showbbox = Cvar_Get("gl_showbbox", "0", 0); //gl1_ztrick = ri.Cvar_Get("gl1_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); r_clear = ri.Cvar_Get("r_clear", "0", 0); //gl1_flashblend = ri.Cvar_Get("gl1_flashblend", "0", 0); //gl_texturemode = ri.Cvar_Get("gl_texturemode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE); gl1_texturealphamode = ri.Cvar_Get("gl1_texturealphamode", "default", CVAR_ARCHIVE); gl1_texturesolidmode = ri.Cvar_Get("gl1_texturesolidmode", "default", CVAR_ARCHIVE); //gl_anisotropic = ri.Cvar_Get("r_anisotropic", "0", CVAR_ARCHIVE); //r_lockpvs = ri.Cvar_Get("r_lockpvs", "0", 0); //gl1_palettedtexture = ri.Cvar_Get("gl1_palettedtexture", "0", CVAR_ARCHIVE); NOPE. gl1_pointparameters = ri.Cvar_Get("gl1_pointparameters", "1", CVAR_ARCHIVE); //gl_drawbuffer = ri.Cvar_Get("gl_drawbuffer", "GL_BACK", 0); //r_vsync = ri.Cvar_Get("r_vsync", "1", CVAR_ARCHIVE); //vid_fullscreen = ri.Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); //vid_gamma = ri.Cvar_Get("vid_gamma", "1.0", CVAR_ARCHIVE); //r_customwidth = ri.Cvar_Get("r_customwidth", "1024", CVAR_ARCHIVE); //r_customheight = ri.Cvar_Get("r_customheight", "768", CVAR_ARCHIVE); //gl_msaa_samples = ri.Cvar_Get ( "r_msaa_samples", "0", CVAR_ARCHIVE ); //r_retexturing = ri.Cvar_Get("r_retexturing", "1", CVAR_ARCHIVE); gl1_stereo = ri.Cvar_Get( "gl1_stereo", "0", CVAR_ARCHIVE ); gl1_stereo_separation = ri.Cvar_Get( "gl1_stereo_separation", "-0.4", CVAR_ARCHIVE ); gl1_stereo_anaglyph_colors = ri.Cvar_Get( "gl1_stereo_anaglyph_colors", "rc", CVAR_ARCHIVE ); gl1_stereo_convergence = ri.Cvar_Get( "gl1_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_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 >= 0) && !ri.Vid_GetModeInfo(pwidth, pheight, mode)) { R_Printf(PRINT_ALL, " invalid mode\n"); return rserr_invalid_mode; } /* We trying to get resolution from desktop */ if (mode == -2) { if(!ri.GLimp_GetDesktopMode(pwidth, pheight)) { R_Printf( PRINT_ALL, " can't detect mode\n" ); return rserr_invalid_mode; } } R_Printf(PRINT_ALL, " %dx%d (vid_fullscreen %i)\n", *pwidth, *pheight, fullscreen); if (!ri.GLimp_InitGraphics(fullscreen, pwidth, pheight)) { return rserr_invalid_mode; } /* This is totaly obscure: For some strange reasons the renderer maintains two(!) repesentations of the resolution. One comes from the client and is saved in gl3_newrefdef. The other one is determined here and saved in vid. Several calculations take both representations into account. The values will always be the same. The GLimp_InitGraphics() call above communicates the requested resolution to the client where it ends up in the vid subsystem and the vid system writes it into gl3_newrefdef. We can't avoid the client roundtrip, because we can get the real size of the drawable (which can differ from the resolution due to high dpi awareness) only after the render context was created by GLimp_InitGraphics() and need to communicate it somehow to the client. So we just overwrite the values saved in vid with a call to GL3_GetDrawableSize(), just like the client does. This makes sure that both values are the same and everything is okay. We also need to take the special case fullscreen window into account. With the fullscreen windows we cannot use the drawable size, it would scale all cases to the size of the window. Instead use the drawable size when the user wants native resolution (the fullscreen window fills the screen) and use the requested resolution in all other cases. */ if (IsHighDPIaware) { if (vid_fullscreen->value != 2) { GL3_GetDrawableSize(pwidth, pheight); } else { if (r_mode->value == -2) { /* User requested native resolution. */ GL3_GetDrawableSize(pwidth, pheight); } } } return rserr_ok; } static qboolean GL3_SetMode(void) { int err; int fullscreen; fullscreen = (int)vid_fullscreen->value; /* a bit hackish approach to enable custom resolutions: Glimp_SetMode needs these values set for mode -1 */ vid.width = r_customwidth->value; vid.height = r_customheight->value; if ((err = SetMode_impl(&vid.width, &vid.height, r_mode->value, fullscreen)) == rserr_ok) { if (r_mode->value == -1) { gl3state.prev_mode = 4; /* safe default for custom mode */ } else { gl3state.prev_mode = r_mode->value; } } 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("r_msaa_samples", 0.0f); gl_msaa_samples->modified = false; if ((err = SetMode_impl(&vid.width, &vid.height, r_mode->value, 0)) == rserr_ok) { return true; } } if(r_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("r_mode", gl3state.prev_mode); r_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) { byte *colormap; 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"); 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; } GetPCXPalette (&colormap, d_8to24table); free(colormap); GL3_Register(); /* set our "safe" mode */ gl3state.prev_mode = 4; //gl_state.stereo_mode = gl1_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"); } 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"); } gl3config.useBigVBO = false; if(gl3_usebigvbo->value == 1.0f) { R_Printf(PRINT_ALL, "Enabling useBigVBO workaround because gl3_usebigvbo = 1\n"); gl3config.useBigVBO = true; } else if(gl3_usebigvbo->value == -1.0f) { // enable for AMDs proprietary Windows and Linux drivers #ifdef _WIN32 if(gl3config.version_string != NULL && gl3config.vendor_string != NULL && strstr(gl3config.vendor_string, "ATI Technologies Inc") != NULL) { int a, b, ver; if(sscanf(gl3config.version_string, " %d.%d.%d ", &a, &b, &ver) >= 3 && ver >= 13431) { // turns out the legacy driver is a lot faster *without* the workaround :-/ // GL_VERSION for legacy 16.2.1 Beta driver: 3.2.13399 Core Profile Forward-Compatible Context 15.200.1062.1004 // (this is the last version that supports the Radeon HD 6950) // GL_VERSION for (non-legacy) 16.3.1 driver on Radeon R9 200: 4.5.13431 Compatibility Profile Context 16.150.2111.0 // GL_VERSION for non-legacy 17.7.2 WHQL driver: 4.5.13491 Compatibility Profile/Debug Context 22.19.662.4 // GL_VERSION for 18.10.1 driver: 4.6.13541 Compatibility Profile/Debug Context 25.20.14003.1010 // GL_VERSION for (current) 19.3.2 driver: 4.6.13547 Compatibility Profile/Debug Context 25.20.15027.5007 // (the 3.2/4.5/4.6 can probably be ignored, might depend on the card and what kind of context was requested // but AFAIK the number behind that can be used to roughly match the driver version) // => let's try matching for x.y.z with z >= 13431 // (no, I don't feel like testing which release since 16.2.1 has introduced the slowdown.) R_Printf(PRINT_ALL, "Detected AMD Windows GPU driver, enabling useBigVBO workaround\n"); gl3config.useBigVBO = true; } } #elif defined(__linux__) if(gl3config.vendor_string != NULL && strstr(gl3config.vendor_string, "Advanced Micro Devices, Inc.") != NULL) { R_Printf(PRINT_ALL, "Detected proprietary AMD GPU driver, enabling useBigVBO workaround\n"); R_Printf(PRINT_ALL, "(consider using the open source RadeonSI drivers, they tend to work better overall)\n"); gl3config.useBigVBO = true; } #endif } // 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(); glGenFramebuffers(1, &gl3state.ppFBO); // the rest for the FBO is done dynamically in GL3_RenderView() so it can // take the viewsize into account (enforce that by setting invalid size) gl3state.ppFBtexWidth = gl3state.ppFBtexHeight = -1; 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(); // free the postprocessing FBO and its renderbuffer and texture if(gl3state.ppFBrbo != 0) glDeleteRenderbuffers(1, &gl3state.ppFBrbo); if(gl3state.ppFBtex != 0) glDeleteTextures(1, &gl3state.ppFBtex); if(gl3state.ppFBO != 0) glDeleteFramebuffers(1, &gl3state.ppFBO); gl3state.ppFBrbo = gl3state.ppFBtex = gl3state.ppFBO = 0; gl3state.ppFBObound = false; gl3state.ppFBtexWidth = gl3state.ppFBtexHeight = -1; } /* shutdown OS specific OpenGL stuff like contexts, etc. */ GL3_ShutdownContext(); } // assumes gl3state.v[ab]o3D are bound // buffers and draws gl3_3D_vtx_t vertices // drawMode is something like GL_TRIANGLE_STRIP or GL_TRIANGLE_FAN or whatever void GL3_BufferAndDraw3D(const gl3_3D_vtx_t* verts, int numVerts, GLenum drawMode) { if(!gl3config.useBigVBO) { glBufferData( GL_ARRAY_BUFFER, sizeof(gl3_3D_vtx_t)*numVerts, verts, GL_STREAM_DRAW ); glDrawArrays( drawMode, 0, numVerts ); } else // gl3config.useBigVBO == true { /* * For some reason, AMD's Windows driver doesn't seem to like lots of * calls to glBufferData() (some of them seem to take very long then). * GL3_BufferAndDraw3D() is called a lot when drawing world geometry * (once for each visible face I think?). * The simple code above caused noticeable slowdowns - even a fast * quadcore CPU and a Radeon RX580 weren't able to maintain 60fps.. * The workaround is to not call glBufferData() with small data all the time, * but to allocate a big buffer and on each call to GL3_BufferAndDraw3D() * to use a different region of that buffer, resulting in a lot less calls * to glBufferData() (=> a lot less buffer allocations in the driver). * Only when the buffer is full and at the end of a frame (=> GL3_EndFrame()) * we get a fresh buffer. * * BTW, we couldn't observe this kind of problem with any other driver: * Neither nvidias driver, nor AMDs or Intels Open Source Linux drivers, * not even Intels Windows driver seem to care that much about the * glBufferData() calls.. However, at least nvidias driver doesn't like * this workaround (with glMapBufferRange()), the framerate dropped * significantly - that's why both methods are available and * selectable at runtime. */ #if 0 // I /think/ doing it with glBufferSubData() didn't really help const int bufSize = gl3state.vbo3Dsize; int neededSize = numVerts*sizeof(gl3_3D_vtx_t); int curOffset = gl3state.vbo3DcurOffset; if(curOffset + neededSize > gl3state.vbo3Dsize) curOffset = 0; int curIdx = curOffset / sizeof(gl3_3D_vtx_t); gl3state.vbo3DcurOffset = curOffset + neededSize; glBufferSubData( GL_ARRAY_BUFFER, curOffset, neededSize, verts ); glDrawArrays( drawMode, curIdx, numVerts ); #else int curOffset = gl3state.vbo3DcurOffset; int neededSize = numVerts*sizeof(gl3_3D_vtx_t); if(curOffset+neededSize > gl3state.vbo3Dsize) { // buffer is full, need to start again from the beginning // => need to sync or get fresh buffer // (getting fresh buffer seems easier) glBufferData(GL_ARRAY_BUFFER, gl3state.vbo3Dsize, NULL, GL_STREAM_DRAW); curOffset = 0; } // as we make sure to use a previously unused part of the buffer, // doing it unsynchronized should be safe.. GLbitfield accessBits = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_UNSYNCHRONIZED_BIT; void* data = glMapBufferRange(GL_ARRAY_BUFFER, curOffset, neededSize, accessBits); memcpy(data, verts, neededSize); glUnmapBuffer(GL_ARRAY_BUFFER); glDrawArrays(drawMode, curOffset/sizeof(gl3_3D_vtx_t), numVerts); gl3state.vbo3DcurOffset = curOffset + neededSize; // TODO: padding or sth needed? #endif } } 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); GL3_BufferAndDraw3D(verts, NUM_BEAM_SEGS*4, GL_TRIANGLE_STRIP); glDisable(GL_BLEND); glDepthMask(GL_TRUE); } static void GL3_DrawSpriteModel(entity_t *e, gl3model_t *currentmodel) { float alpha = 1.0F; gl3_3D_vtx_t verts[4]; dsprframe_t *frame; float *up, *right; dsprite_t *psprite; gl3image_t *skin; /* 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(); } skin = currentmodel->skins[e->frame]; if (!skin) { skin = gl3_notexture; /* fallback... */ } GL3_Bind(skin->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); GL3_BufferAndDraw3D(verts, 4, GL_TRIANGLE_FAN); if (alpha != 1.0F) { glDisable(GL_BLEND); gl3state.uni3DData.alpha = 1.0f; GL3_UpdateUBO3D(); } } static void GL3_DrawNullModel(entity_t *currententity) { vec3_t shadelight; if (currententity->flags & RF_FULLBRIGHT) { shadelight[0] = shadelight[1] = shadelight[2] = 1.0F; } else { GL3_LightPoint(currententity, 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}} }; GL3_BufferAndDraw3D(vtxA, 6, GL_TRIANGLE_FAN); gl3_3D_vtx_t vtxB[6] = { {{0, 0, 16}, {0,0}, {0,0}}, vtxA[5], vtxA[4], vtxA[3], vtxA[2], vtxA[1] }; GL3_BufferAndDraw3D(vtxB, 6, GL_TRIANGLE_FAN); 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; YQ2_ALIGNAS_TYPE(unsigned) byte 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; YQ2_STATIC_ASSERT(sizeof(part_vtx)==9*sizeof(float), "invalid part_vtx size"); // remember to update GL3_SurfInit() if this changes! // Don't try to draw particles if there aren't any. if (numParticles == 0) { return; } YQ2_VLA(part_vtx, buf, numParticles); // TODO: viewOrg could be in UBO vec3_t viewOrg; VectorCopy(gl3_newrefdef.vieworg, viewOrg); glDepthMask(GL_FALSE); glEnable(GL_BLEND); #ifdef YQ2_GL3_GLES // the RPi4 GLES3 implementation doesn't draw particles if culling is // enabled (at least with GL_FRONT which seems to be default in q2?) glDisable(GL_CULL_FACE); #else // GLES doesn't have this, maybe it's always enabled? (https://gamedev.stackexchange.com/a/15528 says it works) // luckily we don't use glPointSize() but set gl_PointSize in shader anyway glEnable(GL_PROGRAM_POINT_SIZE); #endif 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]*(1.0f/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); #ifdef YQ2_GL3_GLES if(r_cull->value != 0.0f) glEnable(GL_CULL_FACE); #else glDisable(GL_PROGRAM_POINT_SIZE); #endif YQ2_VLAFREE(buf); } } static void GL3_DrawEntitiesOnList(void) { int i; if (!r_drawentities->value) { return; } GL3_ResetShadowAliasModels(); /* draw non-transparent first */ for (i = 0; i < gl3_newrefdef.num_entities; i++) { entity_t *currententity = &gl3_newrefdef.entities[i]; if (currententity->flags & RF_TRANSLUCENT) { continue; /* solid */ } if (currententity->flags & RF_BEAM) { GL3_DrawBeam(currententity); } else { gl3model_t *currentmodel = currententity->model; if (!currentmodel) { GL3_DrawNullModel(currententity); continue; } switch (currentmodel->type) { case mod_alias: GL3_DrawAliasModel(currententity); break; case mod_brush: GL3_DrawBrushModel(currententity, currentmodel); break; case mod_sprite: GL3_DrawSpriteModel(currententity, currentmodel); break; default: ri.Sys_Error(ERR_DROP, "Bad modeltype"); break; } } } /* draw transparent entities we could sort these if it ever becomes a problem... */ glDepthMask(GL_FALSE); for (i = 0; i < gl3_newrefdef.num_entities; i++) { entity_t *currententity = &gl3_newrefdef.entities[i]; if (!(currententity->flags & RF_TRANSLUCENT)) { continue; /* solid */ } if (currententity->flags & RF_BEAM) { GL3_DrawBeam(currententity); } else { gl3model_t *currentmodel = currententity->model; if (!currentmodel) { GL3_DrawNullModel(currententity); continue; } switch (currentmodel->type) { case mod_alias: GL3_DrawAliasModel(currententity); break; case mod_brush: GL3_DrawBrushModel(currententity, currentmodel); break; case mod_sprite: GL3_DrawSpriteModel(currententity, currentmodel); break; default: ri.Sys_Error(ERR_DROP, "Bad modeltype"); break; } } } GL3_DrawAliasShadows(); glDepthMask(GL_TRUE); /* back to writing */ } 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)) { if (!gl3_worldmodel) { ri.Sys_Error(ERR_DROP, "%s: bad world model", __func__); return; } gl3_oldviewcluster = gl3_viewcluster; gl3_oldviewcluster2 = gl3_viewcluster2; leaf = Mod_PointInLeaf(gl3_origin, gl3_worldmodel->nodes); 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 = Mod_PointInLeaf(temp, gl3_worldmodel->nodes); 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 = Mod_PointInLeaf(temp, gl3_worldmodel->nodes); 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 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 += - gl1_stereo_convergence->value * (2 * gl_state.camera_separation) / zNear; // right += - gl1_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 // or http://docs.gl/gl2/glFrustum#description (looks better in non-Firefox browsers) 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 GL3_Clear(void); 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 // set up the FBO accordingly, but only if actually rendering the world // (=> don't use FBO when rendering the playermodel in the player menu) // also, only do this when under water, because this has a noticeable overhead on some systems if (gl3_usefbo->value && gl3state.ppFBO != 0 && (gl3_newrefdef.rdflags & (RDF_NOWORLDMODEL|RDF_UNDERWATER)) == RDF_UNDERWATER) { glBindFramebuffer(GL_FRAMEBUFFER, gl3state.ppFBO); gl3state.ppFBObound = true; if(gl3state.ppFBtex == 0) { gl3state.ppFBtexWidth = -1; // make sure we generate the texture storage below glGenTextures(1, &gl3state.ppFBtex); } if(gl3state.ppFBrbo == 0) { gl3state.ppFBtexWidth = -1; // make sure we generate the RBO storage below glGenRenderbuffers(1, &gl3state.ppFBrbo); } // even if the FBO already has a texture and RBO, the viewport size // might have changed so they need to be regenerated with the correct sizes if(gl3state.ppFBtexWidth != w || gl3state.ppFBtexHeight != h) { gl3state.ppFBtexWidth = w; gl3state.ppFBtexHeight = h; GL3_Bind(gl3state.ppFBtex); // create texture for FBO with size of the viewport glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); GL3_Bind(0); // attach it to currently bound FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl3state.ppFBtex, 0); // also create a renderbuffer object so the FBO has a stencil- and depth-buffer glBindRenderbuffer(GL_RENDERBUFFER, gl3state.ppFBrbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h); glBindRenderbuffer(GL_RENDERBUFFER, 0); // attach it to the FBO glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl3state.ppFBrbo); GLenum fbState = glCheckFramebufferStatus(GL_FRAMEBUFFER); if(fbState != GL_FRAMEBUFFER_COMPLETE) { R_Printf(PRINT_ALL, "GL3 SetupGL(): WARNING: FBO is not complete, status = 0x%x\n", fbState); gl3state.ppFBtexWidth = -1; // to try again next frame; TODO: maybe give up? gl3state.ppFBObound = false; glBindFramebuffer(GL_FRAMEBUFFER, 0); } } GL3_Clear(); // clear the FBO that's bound now glViewport(0, 0, w, h); // this will be moved to the center later, so no x/y offset } else // rendering directly (not to FBO for postprocessing) { glViewport(x, y2, w, h); } /* set up projection matrix (eye coordinates -> clip coordinates) */ { float screenaspect = (float)gl3_newrefdef.width / gl3_newrefdef.height; float dist = (r_farsee->value == 0) ? 4096.0f : 8192.0f; gl3state.projMat3D = 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 column-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) ); gl3state.viewMat3D = viewMat; } // just use one projection-view-matrix (premultiplied here) // so we have one less mat4 multiplication in the 3D shaders gl3state.uni3DData.transProjViewMat4 = HMM_MultiplyMat4(gl3state.projMat3D, gl3state.viewMat3D); gl3state.uni3DData.transModelMat4 = gl3_identityMat4; gl3state.uni3DData.time = gl3_newrefdef.time; GL3_UpdateUBO3D(); /* set drawing parms */ if (r_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(gl1_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(gl1_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 (r_norefresh->value) { return; } gl3_newrefdef = *fd; if (!gl3_worldmodel && !(gl3_newrefdef.rdflags & RDF_NOWORLDMODEL)) { ri.Sys_Error(ERR_DROP, "R_RenderView: NULL worldmodel"); } if (r_speeds->value) { c_brush_polys = 0; c_alias_polys = 0; } GL3_PushDlights(); if (gl_finish->value) { glFinish(); } SetupFrame(); R_SetFrustum(vup, vpn, vright, gl3_origin, gl3_newrefdef.fov_x, gl3_newrefdef.fov_y, frustum); SetupGL(); GL3_MarkLeaves(); /* done here so we know if we're in water */ GL3_DrawWorld(); GL3_DrawEntitiesOnList(); // kick the silly gl1_flashblend poly lights // GL3_RenderDlights(); GL3_DrawParticles(); GL3_DrawAlphaSurfaces(); // Note: R_Flash() is now GL3_Draw_Flash() and called from GL3_RenderFrame() if (r_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(entity_t *currententity) { vec3_t shadelight = {0}; if (gl3_newrefdef.rdflags & RDF_NOWORLDMODEL) { return; } /* save off light value for server to look at */ GL3_LightPoint(currententity, 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]) { r_lightlevel->value = 150 * shadelight[0]; } else { r_lightlevel->value = 150 * shadelight[2]; } } else { if (shadelight[1] > shadelight[2]) { r_lightlevel->value = 150 * shadelight[1]; } else { r_lightlevel->value = 150 * shadelight[2]; } } } static void GL3_RenderFrame(refdef_t *fd) { GL3_RenderView(fd); GL3_SetLightLevel(NULL); qboolean usedFBO = gl3state.ppFBObound; // if it was/is used this frame if(usedFBO) { glBindFramebuffer(GL_FRAMEBUFFER, 0); // now render to default framebuffer gl3state.ppFBObound = false; } GL3_SetGL2D(); int x = (vid.width - gl3_newrefdef.width)/2; int y = (vid.height - gl3_newrefdef.height)/2; if (usedFBO) { // if we're actually drawing the world and using an FBO, render the FBO's texture GL3_DrawFrameBufferObject(x, y, gl3_newrefdef.width, gl3_newrefdef.height, gl3state.ppFBtex, v_blend); } else if(v_blend[3] != 0.0f) { 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 (r_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 && gl3config.stencil) { glClearStencil(1); glClear(GL_STENCIL_BUFFER_BIT); } } void GL3_BeginFrame(float camera_separation) { #if 0 // TODO: stereo stuff gl_state.camera_separation = camera_separation; // force a vid_restart if gl1_stereo has been modified. if ( gl_state.stereo_mode != gl1_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( gl1_stereo->value ) ) { gl_state.stereo_mode = gl1_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("gl3_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_colorlight->modified) { gl3_particle_square->modified = false; gl3_colorlight->modified = false; GL3_RecreateShaders(); } /* go into 2D mode */ GL3_SetGL2D(); /* draw buffer stuff */ if (gl_drawbuffer->modified) { gl_drawbuffer->modified = false; #ifdef YQ2_GL3_GLES // OpenGL ES3 only supports GL_NONE, GL_BACK and GL_COLOR_ATTACHMENT* // so this doesn't make sense here, see https://docs.gl/es3/glDrawBuffers R_Printf(PRINT_ALL, "NOTE: gl_drawbuffer not supported by OpenGL ES!\n"); #else // Desktop GL // TODO: stereo stuff //if ((gl3state.camera_separation == 0) || gl3state.stereo_mode != STEREO_MODE_OPENGL) { GLenum drawBuffer = GL_BACK; if (Q_stricmp(gl_drawbuffer->string, "GL_FRONT") == 0) { drawBuffer = GL_FRONT; } glDrawBuffer(drawBuffer); } #endif } /* texturemode stuff */ if (gl_texturemode->modified || (gl3config.anisotropic && gl_anisotropic->modified) || gl_nolerp_list->modified || r_lerp_list->modified || r_2D_unfiltered->modified || r_videos_unfiltered->modified) { GL3_TextureMode(gl_texturemode->string); gl_texturemode->modified = false; gl_anisotropic->modified = false; gl_nolerp_list->modified = false; r_lerp_list->modified = false; r_2D_unfiltered->modified = false; r_videos_unfiltered->modified = false; } if (r_vsync->modified) { r_vsync->modified = false; GL3_SetVsync(); } /* 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); } /* ===================== GL3_EndWorldRenderpass ===================== */ static qboolean GL3_EndWorldRenderpass( void ) { return true; } Q2_DLL_EXPORTED refexport_t GetRefAPI(refimport_t imp) { refexport_t re = {0}; ri = imp; re.api_version = API_VERSION; re.framework_version = GL3_GetSDLVersion(); re.Init = GL3_Init; re.Shutdown = GL3_Shutdown; re.PrepareForWindow = GL3_PrepareForWindow; re.InitContext = GL3_InitContext; re.GetDrawableSize = GL3_GetDrawableSize; re.ShutdownContext = GL3_ShutdownContext; 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.EndWorldRenderpass = GL3_EndWorldRenderpass; re.EndFrame = GL3_EndFrame; // Tell the client that we're unsing the // new renderer restart API. ri.Vid_RequestRestart(RESTART_NO); 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(const char *error, ...) { va_list argptr; char text[4096]; // MAXPRINTMSG == 4096 va_start(argptr, error); vsnprintf(text, sizeof(text), error, argptr); va_end(argptr); ri.Sys_Error(ERR_FATAL, "%s", text); } void Com_Printf(const char *msg, ...) { va_list argptr; va_start(argptr, msg); ri.Com_VPrintf(PRINT_ALL, msg, argptr); va_end(argptr); } yquake2-QUAKE2_8_40/src/client/refresh/gl3/gl3_mesh.c000066400000000000000000000543471465112212000221630ustar00rootroot00000000000000/* * 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; 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); } if(gl3_colorlight->value == 0.0f) { float avg = 0.333333f * (shadelight[0]+shadelight[1]+shadelight[2]); shadelight[0] = shadelight[1] = shadelight[2] = avg; } /* 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); YQ2_STATIC_ASSERT(sizeof(gl3_alias_vtx_t) == 9*sizeof(GLfloat), "invalid gl3_alias_vtx_t size"); // 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 origProjViewMat = {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, 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]) { r_lightlevel->value = 150 * shadelight[0]; } else { r_lightlevel->value = 150 * shadelight[2]; } } else { if (shadelight[1] > shadelight[2]) { r_lightlevel->value = 150 * shadelight[1]; } else { r_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) { extern hmm_mat4 GL3_MYgluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar); origProjViewMat = gl3state.uni3DData.transProjViewMat4; // render weapon with a different FOV (r_gunfov) so it's not distorted at high view FOV float screenaspect = (float)gl3_newrefdef.width / gl3_newrefdef.height; float dist = (r_farsee->value == 0) ? 4096.0f : 8192.0f; hmm_mat4 projMat; if (r_gunfov->value < 0) { projMat = GL3_MYgluPerspective(gl3_newrefdef.fov_y, screenaspect, 4, dist); } else { projMat = GL3_MYgluPerspective(r_gunfov->value, screenaspect, 4, dist); } if(gl_lefthand->value == 1.0F) { // to mirror gun so it's rendered left-handed, just invert X-axis column // of projection matrix for(int i=0; i<4; ++i) { projMat.Elements[0][i] = - projMat.Elements[0][i]; } //GL3_UpdateUBO3D(); Note: GL3_RotateForEntity() will call this,no need to do it twice before drawing glCullFace(GL_BACK); } gl3state.uni3DData.transProjViewMat4 = HMM_MultiplyMat4(projMat, gl3state.viewMat3D); } //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) { gl3state.uni3DData.transProjViewMat4 = origProjViewMat; GL3_UpdateUBO3D(); if(gl_lefthand->value == 1.0F) glCullFace(GL_FRONT); } if (entity->flags & RF_TRANSLUCENT) { glDisable(GL_BLEND); } if (entity->flags & RF_DEPTHHACK) { glDepthRange(gl3depthmin, gl3depthmax); } if (gl_shadows->value && gl3config.stencil && !(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 (gl3config.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 (gl3config.stencil) { glDisable(GL_STENCIL_TEST); } glDisable(GL_BLEND); //glPopMatrix(); gl3state.uni3DData.transModelMat4 = oldMat; GL3_UpdateUBO3D(); } yquake2-QUAKE2_8_40/src/client/refresh/gl3/gl3_misc.c000066400000000000000000000111631465112212000221470ustar00rootroot00000000000000/* * 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); #ifndef YQ2_GL3_GLES // in GLES this is only supported with an extension: // https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_multisample_compatibility.txt // but apparently it's just enabled by default if set in the context? glDisable(GL_MULTISAMPLE); #endif glCullFace(GL_FRONT); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glDisable(GL_BLEND); #ifndef YQ2_GL3_GLES // in GLES GL_FILL is the only supported mode glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); #endif // TODO: gl1_texturealphamode? GL3_TextureMode(gl_texturemode->string); //R_TextureAlphaMode(gl1_texturealphamode->string); //R_TextureSolidMode(gl1_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); #ifndef YQ2_GL3_GLES // see above if (gl_msaa_samples->value) { glEnable(GL_MULTISAMPLE); // glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST); TODO what is this for? } #endif } 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, 8 * 8, 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, 8 * 8, it_wall, 32); } void GL3_ScreenShot(void) { int w=vid.width, h=vid.height; #ifdef YQ2_GL3_GLES // My RPi4's GLES3 doesn't like GL_RGB, so use GL_RGBA with GLES // TODO: we could convert the screenshot to RGB before writing // so the resulting file is smaller static const int comps = 4; #else // Desktop GL static const int comps = 3; #endif byte *buffer = malloc(w*h*comps); 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, (comps == 4) ? GL_RGBA : 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 = comps*w; YQ2_VLA(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; } YQ2_VLAFREE(rowBuffer); } ri.Vid_WriteScreenshot(w, h, comps, buffer); free(buffer); } yquake2-QUAKE2_8_40/src/client/refresh/gl3/gl3_model.c000066400000000000000000000454741465112212000223300ustar00rootroot00000000000000/* * 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 }; YQ2_ALIGNAS_TYPE(int) static byte mod_novis[MAX_MAP_LEAFS / 8]; gl3model_t mod_known[MAX_MOD_KNOWN]; static int mod_numknown; static int mod_max = 0; int registration_sequence; //=============================================================================== static qboolean Mod_HasFreeSpace(void) { int i, used; gl3model_t *mod; used = 0; for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) { if (!mod->name[0]) continue; if (mod->registration_sequence == registration_sequence) { used ++; } } if (mod_max < used) { mod_max = used; } // should same size of free slots as currently used return (mod_numknown + mod_max) < MAX_MOD_KNOWN; } const byte* GL3_Mod_ClusterPVS(int cluster, const gl3model_t *model) { if ((cluster == -1) || !model->vis) { return mod_novis; } return Mod_DecompressVis((byte *)model->vis + model->vis->bitofs[cluster][DVIS_PVS], (model->vis->numclusters + 7) >> 3); } void GL3_Mod_Modellist_f(void) { int i, total, used; gl3model_t *mod; qboolean freeup; total = 0; used = 0; R_Printf(PRINT_ALL, "Loaded models:\n"); for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { char *in_use = ""; if (mod->registration_sequence == registration_sequence) { in_use = "*"; used ++; } if (!mod->name[0]) { continue; } R_Printf(PRINT_ALL, "%8i : %s %s\n", mod->extradatasize, mod->name, in_use); total += mod->extradatasize; } R_Printf(PRINT_ALL, "Total resident: %i\n", total); // update statistics freeup = Mod_HasFreeSpace(); R_Printf(PRINT_ALL, "Used %d of %d models%s.\n", used, mod_max, freeup ? ", has free space" : ""); } void GL3_Mod_Init(void) { mod_max = 0; memset(mod_novis, 0xff, sizeof(mod_novis)); } static void Mod_LoadSubmodels(gl3model_t *loadmodel, byte *mod_base, lump_t *l) { dmodel_t *in; gl3model_t *out; int i, j, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s", __func__, 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++) { if (i == 0) { // copy parent as template for first model memcpy(out, loadmodel, sizeof(*out)); } else { // copy first as template for model memcpy(out, loadmodel->submodels, sizeof(*out)); } Com_sprintf (out->name, sizeof(out->name), "*%d", 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->radius = Mod_RadiusFromBounds(out->mins, out->maxs); out->firstnode = LittleLong(in->headnode); out->firstmodelsurface = LittleLong(in->firstface); out->nummodelsurfaces = LittleLong(in->numfaces); // visleafs out->numleafs = 0; // check limits if (out->firstnode >= loadmodel->numnodes) { ri.Sys_Error(ERR_DROP, "%s: Inline model %i has bad firstnode", __func__, i); } } } /* * Fills in s->texturemins[] and s->extents[] */ static void Mod_CalcSurfaceExtents(gl3model_t *loadmodel, 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 int calcTexinfoAndFacesSize(byte *mod_base, const lump_t *fl, const lump_t *tl) { dface_t* face_in = (void *)(mod_base + fl->fileofs); texinfo_t* texinfo_in = (void *)(mod_base + tl->fileofs); if (fl->filelen % sizeof(*face_in) || tl->filelen % sizeof(*texinfo_in)) { // will error out when actually loading it return 0; } int ret = 0; int face_count = fl->filelen / sizeof(*face_in); int texinfo_count = tl->filelen / sizeof(*texinfo_in); { // out = Hunk_Alloc(count * sizeof(*out)); int baseSize = face_count * sizeof(msurface_t); baseSize = (baseSize + 31) & ~31; ret += baseSize; int ti_size = texinfo_count * sizeof(mtexinfo_t); ti_size = (ti_size + 31) & ~31; ret += ti_size; } int numWarpFaces = 0; for (int surfnum = 0; surfnum < face_count; surfnum++, face_in++) { int numverts = LittleShort(face_in->numedges); int ti = LittleShort(face_in->texinfo); if ((ti < 0) || (ti >= texinfo_count)) { return 0; // will error out } int texFlags = LittleLong(texinfo_in[ti].flags); /* set the drawing flags */ if (texFlags & SURF_WARP) { if (numverts > 60) return 0; // will error out in R_SubdividePolygon() // GL3_SubdivideSurface(out, loadmodel); /* cut up polygon for warps */ // for each (pot. recursive) call to R_SubdividePolygon(): // sizeof(glpoly_t) + ((numverts - 4) + 2) * sizeof(gl3_3D_vtx_t) // this is tricky, how much is allocated depends on the size of the surface // which we don't know (we'd need the vertices etc to know, but we can't load // those without allocating...) // so we just count warped faces and use a generous estimate below ++numWarpFaces; } else { // GL3_LM_BuildPolygonFromSurface(out); // => poly = Hunk_Alloc(sizeof(glpoly_t) + (numverts - 4) * sizeof(gl3_3D_vtx_t)); int polySize = sizeof(glpoly_t) + (numverts - 4) * sizeof(gl3_3D_vtx_t); polySize = (polySize + 31) & ~31; ret += polySize; } } // yeah, this is a bit hacky, but it looks like for each warped face // 256-55000 bytes are allocated (usually on the lower end), // so just assume 48k per face to be safe ret += numWarpFaces * 49152; ret += 5000000; // and 5MB extra just in case return ret; } static void Mod_LoadFaces(gl3model_t *loadmodel, byte *mod_base, 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, "%s: funny lump size in %s", __func__, loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count * sizeof(*out)); loadmodel->surfaces = out; loadmodel->numsurfaces = count; 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; } if (planenum < 0 || planenum >= loadmodel->numplanes) { ri.Sys_Error(ERR_DROP, "%s: Incorrect %d planenum.", __func__, planenum); } out->plane = loadmodel->planes + planenum; ti = LittleShort(in->texinfo); if ((ti < 0) || (ti >= loadmodel->numtexinfo)) { ri.Sys_Error(ERR_DROP, "%s: bad texinfo number", __func__); } out->texinfo = loadmodel->texinfo + ti; Mod_CalcSurfaceExtents(loadmodel, 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 */ } if (r_fixsurfsky->value) { if (out->texinfo->flags & SURF_SKY) { out->flags |= SURF_DRAWSKY; } } /* 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(loadmodel, out); } } GL3_LM_EndBuildingLightmaps(); } static void Mod_LoadLeafs(gl3model_t *loadmodel, byte *mod_base, 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, "%s: funny lump size in %s", __func__, 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++) { unsigned firstleafface; 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); // make unsigned long from signed short firstleafface = LittleShort(in->firstleafface) & 0xFFFF; out->nummarksurfaces = LittleShort(in->numleaffaces) & 0xFFFF; out->firstmarksurface = loadmodel->marksurfaces + firstleafface; if ((firstleafface + out->nummarksurfaces) > loadmodel->nummarksurfaces) { ri.Sys_Error(ERR_DROP, "%s: wrong marksurfaces position in %s", __func__, loadmodel->name); } } } static void Mod_LoadMarksurfaces(gl3model_t *loadmodel, byte *mod_base, 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, "%s: funny lump size in %s", __func__, 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, "%s: bad surface number", __func__); } out[i] = loadmodel->surfaces + j; } } static void Mod_LoadBrushModel(gl3model_t *mod, void *buffer, int modfilelen) { int i; dheader_t *header; byte *mod_base; if (mod != 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, "%s: %s has wrong version number (%i should be %i)", __func__, 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]); } // calculate the needed hunksize from the lumps int hunkSize = 0; hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_VERTEXES], sizeof(dvertex_t), sizeof(mvertex_t), 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_EDGES], sizeof(dedge_t), sizeof(medge_t), 0); hunkSize += sizeof(medge_t) + 31; // for count+1 in Mod_LoadEdges() int surfEdgeCount = (header->lumps[LUMP_SURFEDGES].filelen+sizeof(int)-1)/sizeof(int); if(surfEdgeCount < MAX_MAP_SURFEDGES) // else it errors out later anyway hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_SURFEDGES], sizeof(int), sizeof(int), 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_LIGHTING], 1, 1, 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_PLANES], sizeof(dplane_t), sizeof(cplane_t)*2, 0); hunkSize += calcTexinfoAndFacesSize(mod_base, &header->lumps[LUMP_FACES], &header->lumps[LUMP_TEXINFO]); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_LEAFFACES], sizeof(short), sizeof(msurface_t *), 0); // yes, out is indeed a pointer! hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_VISIBILITY], 1, 1, 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_LEAFS], sizeof(dleaf_t), sizeof(mleaf_t), 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_NODES], sizeof(dnode_t), sizeof(mnode_t), 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_MODELS], sizeof(dmodel_t), sizeof(gl3model_t), 0); mod->extradata = Hunk_Begin(hunkSize); mod->type = mod_brush; /* load into heap */ Mod_LoadVertexes(mod->name, &mod->vertexes, &mod->numvertexes, mod_base, &header->lumps[LUMP_VERTEXES], 0); Mod_LoadEdges(mod->name, &mod->edges, &mod->numedges, mod_base, &header->lumps[LUMP_EDGES], 1); Mod_LoadSurfedges(mod->name, &mod->surfedges, &mod->numsurfedges, mod_base, &header->lumps[LUMP_SURFEDGES], 0); Mod_LoadLighting(&mod->lightdata, mod_base, &header->lumps[LUMP_LIGHTING]); Mod_LoadPlanes (mod->name, &mod->planes, &mod->numplanes, mod_base, &header->lumps[LUMP_PLANES], 0); Mod_LoadTexinfo (mod->name, &mod->texinfo, &mod->numtexinfo, mod_base, &header->lumps[LUMP_TEXINFO], (findimage_t)GL3_FindImage, gl3_notexture, 0); Mod_LoadFaces(mod, mod_base, &header->lumps[LUMP_FACES]); Mod_LoadMarksurfaces(mod, mod_base, &header->lumps[LUMP_LEAFFACES]); Mod_LoadVisibility(&mod->vis, mod_base, &header->lumps[LUMP_VISIBILITY]); Mod_LoadLeafs(mod, mod_base, &header->lumps[LUMP_LEAFS]); Mod_LoadNodes(mod->name, mod->planes, mod->numplanes, mod->leafs, mod->numleafs, &mod->nodes, &mod->numnodes, mod_base, &header->lumps[LUMP_NODES]); Mod_LoadSubmodels (mod, mod_base, &header->lumps[LUMP_MODELS]); mod->numframes = 2; /* regular and alternate animation */ } 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]); } } } /* * Loads in a model for the given name */ static gl3model_t * Mod_ForName (char *name, gl3model_t *parent_model, qboolean crash) { gl3model_t *mod; void *buf; int i, modfilelen; if (!name[0]) { ri.Sys_Error(ERR_DROP, "%s: NULL name", __func__); } /* inline models are grabbed only from worldmodel */ if (name[0] == '*' && parent_model) { i = (int)strtol(name + 1, (char **)NULL, 10); if (i < 1 || i >= parent_model->numsubmodels) { ri.Sys_Error(ERR_DROP, "%s: bad inline model number", __func__); } return &parent_model->submodels[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, "%s: %s not found", __func__, mod->name); } memset(mod->name, 0, sizeof(mod->name)); return NULL; } /* call the apropriate loader */ switch (LittleLong(*(unsigned *)buf)) { case IDALIASHEADER: { mod->extradata = Mod_LoadMD2(mod->name, buf, modfilelen, mod->mins, mod->maxs, (struct image_s **)mod->skins, (findimage_t)GL3_FindImage, &(mod->type)); if (!mod->extradata) { ri.Sys_Error(ERR_DROP, "%s: Failed to load %s", __func__, mod->name); } }; break; case IDSPRITEHEADER: { mod->extradata = Mod_LoadSP2(mod->name, buf, modfilelen, (struct image_s **)mod->skins, (findimage_t)GL3_FindImage, &(mod->type)); if (!mod->extradata) { ri.Sys_Error(ERR_DROP, "%s: Failed to load %s", __func__, mod->name); } } break; case IDBSPHEADER: Mod_LoadBrushModel(mod, buf, modfilelen); break; default: ri.Sys_Error(ERR_DROP, "%s: unknown fileid for %s", __func__, mod->name); break; } mod->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, NULL, true); gl3_viewcluster = -1; } struct model_s * GL3_RegisterModel(char *name) { gl3model_t *mod; mod = Mod_ForName(name, gl3_worldmodel, false); if (mod) { mod->registration_sequence = registration_sequence; /* register any images used by the models */ if (mod->type == mod_brush) { int i; for (i = 0; i < mod->numtexinfo; i++) { mod->texinfo[i].image->registration_sequence = registration_sequence; } } else { /* numframes is unused for SP2 but lets set it also */ mod->numframes = Mod_ReLoadSkins((struct image_s **)mod->skins, (findimage_t)GL3_FindImage, mod->extradata, mod->type); } } return mod; } void GL3_EndRegistration(void) { int i; gl3model_t *mod; if (Mod_HasFreeSpace() && GL3_ImageHasFreeSpace()) { // should be enough space for load next maps return; } 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_8_40/src/client/refresh/gl3/gl3_sdl.c000066400000000000000000000265121465112212000220020ustar00rootroot00000000000000/* * 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 backend for the GL3 renderer. Everything that needs to be on the * renderer side of thing. Also all glad (or whatever OpenGL loader I * end up using) specific things. * * ======================================================================= */ #include "header/local.h" #ifdef USE_SDL3 #include #else #include #endif static SDL_Window* window = NULL; static SDL_GLContext context = NULL; static qboolean vsyncActive = false; qboolean IsHighDPIaware = false; // -------- enum { // Not all GL.h header know about GL_DEBUG_SEVERITY_NOTIFICATION_*. // DG: yes, it's the same value in GLES3.2 QGL_DEBUG_SEVERITY_NOTIFICATION = 0x826B }; /* * Callback function for debug output. */ 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) { #ifdef YQ2_GL3_GLES #define SVRCASE(X, STR) case GL_DEBUG_SEVERITY_ ## X ## _KHR : severityStr = STR; break; #else // Desktop GL #define SVRCASE(X, STR) case GL_DEBUG_SEVERITY_ ## X ## _ARB : severityStr = STR; break; #endif case QGL_DEBUG_SEVERITY_NOTIFICATION: return; SVRCASE(HIGH, "Severity: High") SVRCASE(MEDIUM, "Severity: Medium") SVRCASE(LOW, "Severity: Low") #undef SVRCASE } switch (source) { #ifdef YQ2_GL3_GLES #define SRCCASE(X) case GL_DEBUG_SOURCE_ ## X ## _KHR: sourceStr = "Source: " #X; break; #else #define SRCCASE(X) case GL_DEBUG_SOURCE_ ## X ## _ARB: sourceStr = "Source: " #X; break; #endif SRCCASE(API); SRCCASE(WINDOW_SYSTEM); SRCCASE(SHADER_COMPILER); SRCCASE(THIRD_PARTY); SRCCASE(APPLICATION); SRCCASE(OTHER); #undef SRCCASE } switch(type) { #ifdef YQ2_GL3_GLES #define TYPECASE(X) case GL_DEBUG_TYPE_ ## X ## _KHR: typeStr = "Type: " #X; break; #else #define TYPECASE(X) case GL_DEBUG_TYPE_ ## X ## _ARB: typeStr = "Type: " #X; break; #endif 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); } // --------- /* * Swaps the buffers and shows the next frame. */ void GL3_EndFrame(void) { if(gl3config.useBigVBO) { // I think this is a good point to orphan the VBO and get a fresh one GL3_BindVAO(gl3state.vao3D); GL3_BindVBO(gl3state.vbo3D); glBufferData(GL_ARRAY_BUFFER, gl3state.vbo3Dsize, NULL, GL_STREAM_DRAW); gl3state.vbo3DcurOffset = 0; } SDL_GL_SwapWindow(window); } /* * Returns whether the vsync is enabled. */ qboolean GL3_IsVsyncActive(void) { return vsyncActive; } /* * Enables or disables the vsync. */ void GL3_SetVsync(void) { // Make sure that the user given // value is SDL compatible... int vsync = 0; if (r_vsync->value == 1) { vsync = 1; } else if (r_vsync->value == 2) { vsync = -1; } if (SDL_GL_SetSwapInterval(vsync) == -1) { if (vsync == -1) { // Not every system supports adaptive // vsync, fallback to normal vsync. R_Printf(PRINT_ALL, "Failed to set adaptive vsync, reverting to normal vsync.\n"); SDL_GL_SetSwapInterval(1); } } #ifdef USE_SDL3 int vsyncState; if (SDL_GL_GetSwapInterval(&vsyncState) != 0) { R_Printf(PRINT_ALL, "Failed to get vsync state, assuming vsync inactive.\n"); vsyncActive = false; } else { vsyncActive = vsyncState ? true : false; } #else vsyncActive = SDL_GL_GetSwapInterval() != 0; #endif } /* * This function returns the flags used at the SDL window * creation by GLimp_InitGraphics(). In case of error -1 * is returned. */ int GL3_PrepareForWindow(void) { // Mkay, let's try to load the libGL, const char *libgl; cvar_t *gl3_libgl = ri.Cvar_Get("gl3_libgl", "", CVAR_ARCHIVE); if (strlen(gl3_libgl->string) == 0) { libgl = NULL; } else { libgl = gl3_libgl->string; } while (1) { if (SDL_GL_LoadLibrary(libgl) < 0) { if (libgl == NULL) { ri.Sys_Error(ERR_FATAL, "%s: Couldn't load libGL: %s!", __func__, SDL_GetError()); return -1; } else { R_Printf(PRINT_ALL, "%s: Couldn't load libGL: %s!\n", __func__, SDL_GetError()); R_Printf(PRINT_ALL, "Retrying with default...\n"); ri.Cvar_Set("gl3_libgl", ""); libgl = NULL; } } else { break; } } // Set GL context attributs bound to the window. 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); if (SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8) == 0) { gl3config.stencil = true; } else { gl3config.stencil = false; } #ifdef YQ2_GL3_GLES3 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); #else // Desktop GL 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); #endif // Set GL context flags. int contextFlags = 0; #ifndef YQ2_GL3_GLES // Desktop GL (at least RPi4 doesn't like this for GLES3) contextFlags |= SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG; #endif if (gl3_debugcontext && gl3_debugcontext->value) { contextFlags |= SDL_GL_CONTEXT_DEBUG_FLAG; } if (contextFlags != 0) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, contextFlags); } // Let's see if the driver supports MSAA. int msaa_samples = 0; 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 ("r_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("r_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); } return SDL_WINDOW_OPENGL; } /* * Initializes the OpenGL context. Returns true at * success and false at failure. */ int GL3_InitContext(void* win) { // Coders are stupid. if (win == NULL) { ri.Sys_Error(ERR_FATAL, "R_InitContext() must not be called with NULL argument!"); return false; } window = (SDL_Window *)win; // Initialize GL context. 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; } // Check if we've got the requested MSAA. int msaa_samples = 0; if (gl_msaa_samples->value) { if (SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &msaa_samples) == 0) { ri.Cvar_SetValue("r_msaa_samples", msaa_samples); } } // Check if we've got at least 8 stencil bits int stencil_bits = 0; if (gl3config.stencil) { if (SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil_bits) < 0 || stencil_bits < 8) { gl3config.stencil = false; } } // Enable vsync if requested. GL3_SetVsync(); // Load GL pointers through GLAD and check context. #ifdef YQ2_GL3_GLES if( !gladLoadGLES2Loader((void *)SDL_GL_GetProcAddress)) #else // Desktop GL if( !gladLoadGLLoader((void *)SDL_GL_GetProcAddress)) #endif { R_Printf(PRINT_ALL, "GL3_InitContext(): ERROR: loading OpenGL function pointers failed!\n"); return false; } #ifdef YQ2_GL3_GLES3 else if (GLVersion.major < 3) #else // Desktop GL else if (GLVersion.major < 3 || (GLVersion.major == 3 && GLVersion.minor < 2)) #endif { 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); } #ifdef YQ2_GL3_GLES gl3config.debug_output = GLAD_GL_KHR_debug != 0; #else // Desktop GL gl3config.debug_output = GLAD_GL_ARB_debug_output != 0; #endif gl3config.anisotropic = GLAD_GL_EXT_texture_filter_anisotropic != 0; gl3config.major_version = GLVersion.major; gl3config.minor_version = GLVersion.minor; // Debug context setup. if (gl3_debugcontext && gl3_debugcontext->value && gl3config.debug_output) { #ifdef YQ2_GL3_GLES glDebugMessageCallbackKHR(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_KHR); #else // Desktop GL glDebugMessageCallbackARB(DebugCallback, NULL); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); #endif } // Window title - set here so we can display renderer name in it. char title[40] = {0}; #ifdef YQ2_GL3_GLES3 snprintf(title, sizeof(title), "Yamagi Quake II %s - OpenGL ES 3.0", YQ2VERSION); #else snprintf(title, sizeof(title), "Yamagi Quake II %s - OpenGL 3.2", YQ2VERSION); #endif SDL_SetWindowTitle(window, title); #if SDL_VERSION_ATLEAST(2, 26, 0) // Figure out if we are high dpi aware. int flags = SDL_GetWindowFlags(win); #ifdef USE_SDL3 IsHighDPIaware = (flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) ? true : false; #else IsHighDPIaware = (flags & SDL_WINDOW_ALLOW_HIGHDPI) ? true : false; #endif #endif return true; } /* * Fills the actual size of the drawable into width and height. */ void GL3_GetDrawableSize(int* width, int* height) { #ifdef USE_SDL3 SDL_GetWindowSizeInPixels(window, width, height); #else SDL_GL_GetDrawableSize(window, width, height); #endif } /* * Shuts the GL context down. */ void GL3_ShutdownContext() { if (window) { if(context) { SDL_GL_DeleteContext(context); context = NULL; } } } /* * Returns the SDL major version. Implemented * here to not polute gl3_main.c with the SDL * headers. */ int GL3_GetSDLVersion() { #ifdef USE_SDL3 SDL_Version ver; #else SDL_version ver; #endif SDL_VERSION(&ver); return ver.major; } yquake2-QUAKE2_8_40/src/client/refresh/gl3/gl3_shaders.c000066400000000000000000001201541465112212000226460ustar00rootroot00000000000000/* * 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); #ifdef YQ2_GL3_GLES3 const char* version = "#version 300 es\nprecision mediump float;\n"; #else // Desktop GL const char* version = "#version 150\n"; #endif const char* sources[3] = { version, shaderSrc, shaderSrc2 }; int numSources = shaderSrc2 != NULL ? 3 : 2; 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; // we don't use geometry shaders and GLES3.0 doesn't support them // 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 helps if the lightsource is so close to the surface (e.g. grenades, rockets) // that the dot product below would return 0 // (light sources that are below the surface are filtered out by lightFlags) lightToPos += passNormal*32.0; // also factor in angle between light and point on surface fact *= max(0.0, dot(passNormal, normalize(lightToPos))); lmTex.rgb += dynLights[i].lightColor.rgb * fact * (1.0/256.0); } } lmTex.rgb *= overbrightbits; outColor = lmTex*texel; outColor.rgb = pow(outColor.rgb, vec3(gamma)); // apply gamma correction to result outColor.a = 1.0; // lightmaps aren't used with translucent surfaces } ); static const char* fragmentSrc3DlmNoColor = MULTILINE_STRING( // it gets attributes and uniforms from fragmentCommon3D struct DynLight { // gl3UniDynLight in C vec3 lightOrigin; float _pad; //vec3 lightColor; //float lightIntensity; vec4 lightColor; // .a is intensity; this way it also works on OSX... // (otherwise lightIntensity always contained 1 there) }; layout (std140) uniform uniLights { DynLight dynLights[32]; uint numDynLights; uint _pad1; uint _pad2; uint _pad3; // FFS, AMD! }; uniform sampler2D tex; uniform sampler2D lightmap0; uniform sampler2D lightmap1; uniform sampler2D lightmap2; uniform sampler2D lightmap3; uniform vec4 lmScales[4]; in vec2 passLMcoord; in vec3 passWorldCoord; in vec3 passNormal; flat in uint passLightFlags; void main() { vec4 texel = texture(tex, passTexCoord); // apply intensity texel.rgb *= intensity; // apply lightmap vec4 lmTex = texture(lightmap0, passLMcoord) * lmScales[0]; lmTex += texture(lightmap1, passLMcoord) * lmScales[1]; lmTex += texture(lightmap2, passLMcoord) * lmScales[2]; lmTex += texture(lightmap3, passLMcoord) * lmScales[3]; if(passLightFlags != 0u) { // TODO: or is hardcoding 32 better? for(uint i=0u; i helps if the lightsource is so close to the surface (e.g. grenades, rockets) // that the dot product below would return 0 // (light sources that are below the surface are filtered out by lightFlags) lightToPos += passNormal*32.0; // also factor in angle between light and point on surface fact *= max(0.0, dot(passNormal, normalize(lightToPos))); lmTex.rgb += dynLights[i].lightColor.rgb * fact * (1.0/256.0); } } // turn lightcolor into grey for gl3_colorlight 0 lmTex.rgb = vec3(0.333 * (lmTex.r+lmTex.g+lmTex.b)); lmTex.rgb *= overbrightbits; outColor = lmTex*texel; outColor.rgb = pow(outColor.rgb, vec3(gamma)); // apply gamma correction to result outColor.a = 1; // lightmaps aren't used with translucent surfaces } ); static const char* fragmentSrc3Dcolor = MULTILINE_STRING( // it gets attributes and uniforms from fragmentCommon3D void main() { vec4 texel = color; // apply gamma correction and intensity // texel.rgb *= intensity; TODO: use intensity here? (this is used for beams) outColor.rgb = pow(texel.rgb, vec3(gamma)); outColor.a = texel.a*alpha; // I think alpha shouldn't be modified by gamma and intensity } ); static const char* fragmentSrc3Dsky = MULTILINE_STRING( // it gets attributes and uniforms from fragmentCommon3D uniform sampler2D tex; void main() { vec4 texel = texture(tex, passTexCoord); // TODO: something about GL_BLEND vs GL_ALPHATEST etc // apply gamma correction // texel.rgb *= intensity; // TODO: really no intensity for sky? outColor.rgb = pow(texel.rgb, vec3(gamma)); outColor.a = texel.a*alpha; // I think alpha shouldn't be modified by gamma and intensity } ); static const char* fragmentSrc3Dsprite = MULTILINE_STRING( // it gets attributes and uniforms from fragmentCommon3D uniform sampler2D tex; void main() { vec4 texel = texture(tex, passTexCoord); // apply gamma correction and intensity texel.rgb *= intensity; outColor.rgb = pow(texel.rgb, vec3(gamma)); outColor.a = texel.a*alpha; // I think alpha shouldn't be modified by gamma and intensity } ); static const char* fragmentSrc3DspriteAlpha = MULTILINE_STRING( // it gets attributes and uniforms from fragmentCommon3D uniform sampler2D tex; void main() { vec4 texel = texture(tex, passTexCoord); if(texel.a <= 0.666) discard; // apply gamma correction and intensity texel.rgb *= intensity; outColor.rgb = pow(texel.rgb, vec3(gamma)); outColor.a = texel.a*alpha; // I think alpha shouldn't be modified by gamma and intensity } ); static const char* vertexSrc3Dwater = MULTILINE_STRING( // it gets attributes and uniforms from vertexCommon3D void main() { passTexCoord = texCoord; gl_Position = transProjView * transModel * vec4(position, 1.0); } ); static const char* vertexSrcAlias = MULTILINE_STRING( // it gets attributes and uniforms from vertexCommon3D out vec4 passColor; void main() { passColor = vertColor*overbrightbits; passTexCoord = texCoord; gl_Position = transProjView* transModel * vec4(position, 1.0); } ); static const char* fragmentSrcAlias = MULTILINE_STRING( // it gets attributes and uniforms from fragmentCommon3D uniform sampler2D tex; in vec4 passColor; void main() { vec4 texel = texture(tex, passTexCoord); // apply gamma correction and intensity texel.rgb *= intensity; texel.a *= alpha; // is alpha even used here? texel *= min(vec4(1.5), passColor); outColor.rgb = pow(texel.rgb, vec3(gamma)); outColor.a = texel.a; // I think alpha shouldn't be modified by gamma and intensity } ); static const char* fragmentSrcAliasColor = MULTILINE_STRING( // it gets attributes and uniforms from fragmentCommon3D in vec4 passColor; void main() { vec4 texel = passColor; // apply gamma correction and intensity // texel.rgb *= intensity; // TODO: color-only rendering probably shouldn't use intensity? texel.a *= alpha; // is alpha even used here? outColor.rgb = pow(texel.rgb, vec3(gamma)); outColor.a = texel.a; // I think alpha shouldn't be modified by gamma and intensity } ); static const char* vertexSrcParticles = MULTILINE_STRING( // it gets attributes and uniforms from vertexCommon3D out vec4 passColor; void main() { passColor = vertColor; gl_Position = transProjView * transModel * vec4(position, 1.0); // abusing texCoord for pointSize, pointDist for particles float pointDist = texCoord.y*0.1; // with factor 0.1 it looks good. gl_PointSize = texCoord.x/pointDist; } ); static const char* fragmentSrcParticles = MULTILINE_STRING( // it gets attributes and uniforms from fragmentCommon3D in vec4 passColor; void main() { vec2 offsetFromCenter = 2.0*(gl_PointCoord - vec2(0.5, 0.5)); // normalize so offset is between 0 and 1 instead 0 and 0.5 float distSquared = dot(offsetFromCenter, offsetFromCenter); if(distSquared > 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; // so far we didn't use gamma correction for square particles, but this way // uniCommon is referenced so hopefully Intels Ivy Bridge HD4000 GPU driver // for Windows stops shitting itself (see https://github.com/yquake2/yquake2/issues/391) outColor.rgb = pow(passColor.rgb, vec3(gamma)); outColor.a = passColor.a; } ); #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->uniLmScalesOrTime = -1; shaderInfo->uniVblend = -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; } shaderInfo->uniLmScalesOrTime = glGetUniformLocation(prog, "time"); if(shaderInfo->uniLmScalesOrTime != -1) { glUniform1f(shaderInfo->uniLmScalesOrTime, 0.0f); } shaderInfo->uniVblend = glGetUniformLocation(prog, "v_blend"); if(shaderInfo->uniVblend != -1) { glUniform4f(shaderInfo->uniVblend, 0, 0, 0, 0); } return true; err_cleanup: 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->uniLmScalesOrTime = -1; shaderInfo->uniVblend = -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->uniLmScalesOrTime = 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: glDeleteShader(shaders3D[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.transProjViewMat4 = HMM_Mat4(); gl3state.uni3DData.transModelMat4 = gl3_identityMat4; gl3state.uni3DData.scroll = 0.0f; gl3state.uni3DData.time = 0.0f; gl3state.uni3DData.alpha = 1.0f; // gl3_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; gl3state.uni3DData.lightScaleForTurb = 1.0f; 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(!initShader2D(&gl3state.si2DpostProcess, vertexSrc2D, fragmentSrc2Dpostprocess)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program to render framebuffer object!\n"); return false; } if(!initShader2D(&gl3state.si2DpostProcessWater, vertexSrc2D, fragmentSrc2DpostprocessWater)) { R_Printf(PRINT_ALL, "WARNING: Failed to create shader program to render framebuffer object under water!\n"); return false; } const char* lightmappedFrag = (gl3_colorlight->value == 0.0f) ? fragmentSrc3DlmNoColor : fragmentSrc3Dlm; if(!initShader3D(&gl3state.si3Dlm, vertexSrc3Dlm, lightmappedFrag)) { 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, lightmappedFrag)) { 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_8_40/src/client/refresh/gl3/gl3_surf.c000066400000000000000000000471061465112212000222010ustar00rootroot00000000000000/* * 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 // ofsetof() #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) ; lightFlags - last two groups for lightmap/dynlights glGenVertexArrays(1, &gl3state.vao3D); GL3_BindVAO(gl3state.vao3D); glGenBuffers(1, &gl3state.vbo3D); GL3_BindVBO(gl3state.vbo3D); if(gl3config.useBigVBO) { gl3state.vbo3Dsize = 5*1024*1024; // a 5MB buffer seems to work well? gl3state.vbo3DcurOffset = 0; glBufferData(GL_ARRAY_BUFFER, gl3state.vbo3Dsize, NULL, GL_STREAM_DRAW); // allocate/reserve that data } 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; } 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); GL3_BufferAndDraw3D(p->vertices, p->numverts, GL_TRIANGLE_FAN); } 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); GL3_BufferAndDraw3D(p->vertices, p->numverts, GL_TRIANGLE_FAN); } 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->uniLmScalesOrTime, MAX_LIGHTMAPS_PER_SURFACE, si->lmScales[0].Elements); } } static void RenderBrushPoly(entity_t *currententity, msurface_t *fa) { int map; gl3image_t *image; c_brush_polys++; image = R_TextureAnimation(currententity, 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(entity_t *currententity) { 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(currententity, s); } image->texturechain = NULL; } // TODO: maybe one loop for normal faces and one for SURF_DRAWTURB ??? } static void RenderLightmappedPoly(entity_t *currententity, msurface_t *surf) { int map; gl3image_t *image = R_TextureAnimation(currententity, 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(entity_t *currententity, gl3model_t *currentmodel) { 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++) { R_MarkLights(lt, 1 << k, currentmodel->nodes + currentmodel->firstnode, r_dlightframecount, GL3_MarkSurfaceLights); } 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(currententity, psurf); } else { RenderBrushPoly(currententity, psurf); } } } if (currententity->flags & RF_TRANSLUCENT) { glDisable(GL_BLEND); } } void GL3_DrawBrushModel(entity_t *e, gl3model_t *currentmodel) { vec3_t mins, maxs; int i; qboolean rotated; if (currentmodel->nummodelsurfaces == 0) { return; } 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 (r_cull->value && R_CullBox(mins, maxs, frustum)) { 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(e, currentmodel); // glPopMatrix(); gl3state.uni3DData.transModelMat4 = oldMat; GL3_UpdateUBO3D(); if (gl_zfix->value) { glDisable(GL_POLYGON_OFFSET_FILL); } } static void RecursiveWorldNode(entity_t *currententity, 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 (r_cull->value && R_CullBox(node->minmaxs, node->minmaxs + 3, frustum)) { return; } /* if a leaf node, draw stuff */ if (node->contents != CONTENTS_NODE) { pleaf = (mleaf_t *)node; /* check for door connected areas */ // check for door connected areas if (!R_AreaVisible(gl3_newrefdef.areabits, pleaf)) 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(currententity, 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 = R_TextureAnimation(currententity, 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 = R_TextureAnimation(currententity, surf->texinfo); surf->texturechain = image->texturechain; image->texturechain = surf; } } } /* recurse down the back side */ RecursiveWorldNode(currententity, node->children[!side]); } void GL3_DrawWorld(void) { entity_t ent; if (!r_drawworld->value) { return; } if (gl3_newrefdef.rdflags & RDF_NOWORLDMODEL) { return; } 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); gl3state.currenttexture = -1; GL3_ClearSkyBox(); RecursiveWorldNode(&ent, gl3_worldmodel->nodes); DrawTextureChains(&ent); GL3_DrawSkyBox(); DrawTriangleOutlines(); } /* * Mark the leaves and nodes that are * in the PVS for the current cluster */ void GL3_MarkLeaves(void) { const byte *vis; YQ2_ALIGNAS_TYPE(int) 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) && !r_novis->value && (gl3_viewcluster != -1)) { return; } /* development aid to let you run around and see exactly where the pvs ends */ if (r_lockpvs->value) { return; } gl3_visframecount++; gl3_oldviewcluster = gl3_viewcluster; gl3_oldviewcluster2 = gl3_viewcluster2; if (r_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_8_40/src/client/refresh/gl3/gl3_warp.c000066400000000000000000000327411465112212000221720ustar00rootroot00000000000000/* * 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; } } qboolean updateUni3D = false; if(gl3state.uni3DData.scroll != scroll) { gl3state.uni3DData.scroll = scroll; updateUni3D = true; } // these surfaces (mostly water and lava, I think?) don't have a lightmap. // rendering water at full brightness looks bad (esp. for water in dark environments) // so default use a factor of 0.5 (ontop of intensity) // but lava should be bright and glowing, so use full brightness there float lightScale = fa->texinfo->image->is_lava ? 1.0f : 0.5f; if(lightScale != gl3state.uni3DData.lightScaleForTurb) { gl3state.uni3DData.lightScaleForTurb = lightScale; updateUni3D = true; } if(updateUni3D) { GL3_UpdateUBO3D(); } GL3_UseProgram(gl3state.si3Dturb.shaderProgram); GL3_BindVAO(gl3state.vao3D); GL3_BindVBO(gl3state.vbo3D); for (bp = fa->polys; bp != NULL; bp = bp->next) { GL3_BufferAndDraw3D(bp->vertices, bp->numverts, GL_TRIANGLE_FAN); } } // ########### 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) { char skyname[MAX_QPATH]; int i; Q_strlcpy(skyname, name, sizeof(skyname)); skyrotate = rotate; VectorCopy(axis, skyaxis); for (i = 0; i < 6; i++) { gl3image_t *image; image = (gl3image_t *)GetSkyImage(skyname, suf[i], r_palettedtexture->value, (findimage_t)GL3_FindImage); if (!image) { R_Printf(PRINT_ALL, "%s: can't load %s:%s sky\n", __func__, skyname, suf[i]); image = gl3_notexture; } sky_images[i] = image; } 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 = (r_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] ); GL3_BufferAndDraw3D(skyVertices, 4, GL_TRIANGLE_FAN); } // glPopMatrix(); gl3state.uni3DData.transModelMat4 = origModelMat; GL3_UpdateUBO3D(); } yquake2-QUAKE2_8_40/src/client/refresh/gl3/glad-gles3/000077500000000000000000000000001465112212000222235ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/gl3/glad-gles3/include/000077500000000000000000000000001465112212000236465ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/gl3/glad-gles3/include/KHR/000077500000000000000000000000001465112212000242725ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/gl3/glad-gles3/include/KHR/khrplatform.h000066400000000000000000000255731465112212000270100ustar00rootroot00000000000000#ifndef __khrplatform_h_ #define __khrplatform_h_ /* ** Copyright (c) 2008-2018 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. * * The master copy of khrplatform.h is maintained in the Khronos EGL * Registry repository at https://github.com/KhronosGroup/EGL-Registry * The last semantic modification to khrplatform.h was at commit ID: * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 * * 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 filing pull requests or issues on * the EGL Registry repository linked above. * * * 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; */ #if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) # define KHRONOS_STATIC 1 #endif /*------------------------------------------------------------------------- * Definition of KHRONOS_APICALL *------------------------------------------------------------------------- * This precedes the return type of the function in the function prototype. */ #if defined(KHRONOS_STATIC) /* If the preprocessor constant KHRONOS_STATIC is defined, make the * header compatible with static linking. */ # define KHRONOS_APICALL #elif defined(_WIN32) # define KHRONOS_APICALL __declspec(dllimport) #elif defined (__SYMBIAN32__) # define KHRONOS_APICALL IMPORT_C #elif defined(__ANDROID__) # define KHRONOS_APICALL __attribute__((visibility("default"))) #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 /* * To support platform where unsigned long cannot be used interchangeably with * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. * Ideally, we could just use (u)intptr_t everywhere, but this could result in * ABI breakage if khronos_uintptr_t is changed from unsigned long to * unsigned long long or similar (this results in different C++ name mangling). * To avoid changes for existing platforms, we restrict usage of intptr_t to * platforms where the size of a pointer is larger than the size of long. */ #if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) #if __SIZEOF_POINTER__ > __SIZEOF_LONG__ #define KHRONOS_USE_INTPTR_T #endif #endif #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 KHRONOS_USE_INTPTR_T typedef intptr_t khronos_intptr_t; typedef uintptr_t khronos_uintptr_t; #elif defined(_WIN64) typedef signed long long int khronos_intptr_t; typedef unsigned long long int khronos_uintptr_t; #else typedef signed long int khronos_intptr_t; typedef unsigned long int khronos_uintptr_t; #endif #if defined(_WIN64) typedef signed long long int khronos_ssize_t; typedef unsigned long long int khronos_usize_t; #else 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_8_40/src/client/refresh/gl3/glad-gles3/include/glad/000077500000000000000000000000001465112212000245555ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/gl3/glad-gles3/include/glad/glad.h000066400000000000000000002444361465112212000256520ustar00rootroot00000000000000/* OpenGL ES loader generated by glad 0.1.36 on Sun May 14 07:56:21 2023. Language/Generator: C/C++ Specification: gl APIs: gles2=3.0 Profile: core Extensions: GL_EXT_texture_filter_anisotropic, GL_KHR_debug Loader: False Local files: False Omit khrplatform: False Reproducible: False Commandline: --profile="core" --api="gles2=3.0" --generator="c" --spec="gl" --no-loader --extensions="GL_EXT_texture_filter_anisotropic,GL_KHR_debug" Online: https://glad.dav1d.de/#profile=core&language=c&specification=gl&api=gles2%3D3.0&extensions=GL_EXT_texture_filter_anisotropic&extensions=GL_KHR_debug */ #ifndef __glad_h_ #define __glad_h_ #ifdef __gl2_h_ #error OpenGL ES 2 header already included, remove this include, glad already provides it #endif #define __gl2_h_ #ifdef __gl3_h_ #error OpenGL ES 3 header already included, remove this include, glad already provides it #endif #define __gl3_h_ #if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) #define APIENTRY __stdcall #endif #ifndef APIENTRY #define APIENTRY #endif #ifndef APIENTRYP #define APIENTRYP APIENTRY * #endif #ifndef GLAPIENTRY #define GLAPIENTRY 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 gladLoadGLES2Loader(GLADloadproc); #include typedef unsigned int GLenum; typedef unsigned char GLboolean; typedef unsigned int GLbitfield; typedef void GLvoid; typedef khronos_int8_t GLbyte; typedef khronos_uint8_t GLubyte; typedef khronos_int16_t GLshort; typedef khronos_uint16_t GLushort; typedef int GLint; typedef unsigned int GLuint; typedef khronos_int32_t GLclampx; typedef int GLsizei; typedef khronos_float_t GLfloat; typedef khronos_float_t GLclampf; typedef double GLdouble; typedef double GLclampd; typedef void *GLeglClientBufferEXT; typedef void *GLeglImageOES; typedef char GLchar; typedef char GLcharARB; #ifdef __APPLE__ typedef void *GLhandleARB; #else typedef unsigned int GLhandleARB; #endif typedef khronos_uint16_t GLhalf; typedef khronos_uint16_t GLhalfARB; typedef khronos_int32_t GLfixed; typedef khronos_intptr_t GLintptr; typedef khronos_intptr_t GLintptrARB; typedef khronos_ssize_t GLsizeiptr; typedef khronos_ssize_t GLsizeiptrARB; typedef khronos_int64_t GLint64; typedef khronos_int64_t GLint64EXT; typedef khronos_uint64_t GLuint64; typedef khronos_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; typedef void (APIENTRY *GLVULKANPROCNV)(void); #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_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_FUNC_ADD 0x8006 #define GL_BLEND_EQUATION 0x8009 #define GL_BLEND_EQUATION_RGB 0x8009 #define GL_BLEND_EQUATION_ALPHA 0x883D #define GL_FUNC_SUBTRACT 0x800A #define GL_FUNC_REVERSE_SUBTRACT 0x800B #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_CONSTANT_COLOR 0x8001 #define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 #define GL_CONSTANT_ALPHA 0x8003 #define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 #define GL_BLEND_COLOR 0x8005 #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_STREAM_DRAW 0x88E0 #define GL_STATIC_DRAW 0x88E4 #define GL_DYNAMIC_DRAW 0x88E8 #define GL_BUFFER_SIZE 0x8764 #define GL_BUFFER_USAGE 0x8765 #define GL_CURRENT_VERTEX_ATTRIB 0x8626 #define GL_FRONT 0x0404 #define GL_BACK 0x0405 #define GL_FRONT_AND_BACK 0x0408 #define GL_TEXTURE_2D 0x0DE1 #define GL_CULL_FACE 0x0B44 #define GL_BLEND 0x0BE2 #define GL_DITHER 0x0BD0 #define GL_STENCIL_TEST 0x0B90 #define GL_DEPTH_TEST 0x0B71 #define GL_SCISSOR_TEST 0x0C11 #define GL_POLYGON_OFFSET_FILL 0x8037 #define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E #define GL_SAMPLE_COVERAGE 0x80A0 #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_LINE_WIDTH 0x0B21 #define GL_ALIASED_POINT_SIZE_RANGE 0x846D #define GL_ALIASED_LINE_WIDTH_RANGE 0x846E #define GL_CULL_FACE_MODE 0x0B45 #define GL_FRONT_FACE 0x0B46 #define GL_DEPTH_RANGE 0x0B70 #define GL_DEPTH_WRITEMASK 0x0B72 #define GL_DEPTH_CLEAR_VALUE 0x0B73 #define GL_DEPTH_FUNC 0x0B74 #define GL_STENCIL_CLEAR_VALUE 0x0B91 #define GL_STENCIL_FUNC 0x0B92 #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_VALUE_MASK 0x0B93 #define GL_STENCIL_WRITEMASK 0x0B98 #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_STENCIL_BACK_REF 0x8CA3 #define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 #define GL_STENCIL_BACK_WRITEMASK 0x8CA5 #define GL_VIEWPORT 0x0BA2 #define GL_SCISSOR_BOX 0x0C10 #define GL_COLOR_CLEAR_VALUE 0x0C22 #define GL_COLOR_WRITEMASK 0x0C23 #define GL_UNPACK_ALIGNMENT 0x0CF5 #define GL_PACK_ALIGNMENT 0x0D05 #define GL_MAX_TEXTURE_SIZE 0x0D33 #define GL_MAX_VIEWPORT_DIMS 0x0D3A #define GL_SUBPIXEL_BITS 0x0D50 #define GL_RED_BITS 0x0D52 #define GL_GREEN_BITS 0x0D53 #define GL_BLUE_BITS 0x0D54 #define GL_ALPHA_BITS 0x0D55 #define GL_DEPTH_BITS 0x0D56 #define GL_STENCIL_BITS 0x0D57 #define GL_POLYGON_OFFSET_UNITS 0x2A00 #define GL_POLYGON_OFFSET_FACTOR 0x8038 #define GL_TEXTURE_BINDING_2D 0x8069 #define GL_SAMPLE_BUFFERS 0x80A8 #define GL_SAMPLES 0x80A9 #define GL_SAMPLE_COVERAGE_VALUE 0x80AA #define GL_SAMPLE_COVERAGE_INVERT 0x80AB #define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 #define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 #define GL_DONT_CARE 0x1100 #define GL_FASTEST 0x1101 #define GL_NICEST 0x1102 #define GL_GENERATE_MIPMAP_HINT 0x8192 #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_FIXED 0x140C #define GL_DEPTH_COMPONENT 0x1902 #define GL_ALPHA 0x1906 #define GL_RGB 0x1907 #define GL_RGBA 0x1908 #define GL_LUMINANCE 0x1909 #define GL_LUMINANCE_ALPHA 0x190A #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 #define GL_UNSIGNED_SHORT_5_6_5 0x8363 #define GL_FRAGMENT_SHADER 0x8B30 #define GL_VERTEX_SHADER 0x8B31 #define GL_MAX_VERTEX_ATTRIBS 0x8869 #define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB #define GL_MAX_VARYING_VECTORS 0x8DFC #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D #define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C #define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 #define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD #define GL_SHADER_TYPE 0x8B4F #define GL_DELETE_STATUS 0x8B80 #define GL_LINK_STATUS 0x8B82 #define GL_VALIDATE_STATUS 0x8B83 #define GL_ATTACHED_SHADERS 0x8B85 #define GL_ACTIVE_UNIFORMS 0x8B86 #define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 #define GL_ACTIVE_ATTRIBUTES 0x8B89 #define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A #define GL_SHADING_LANGUAGE_VERSION 0x8B8C #define GL_CURRENT_PROGRAM 0x8B8D #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_KEEP 0x1E00 #define GL_REPLACE 0x1E01 #define GL_INCR 0x1E02 #define GL_DECR 0x1E03 #define GL_INVERT 0x150A #define GL_INCR_WRAP 0x8507 #define GL_DECR_WRAP 0x8508 #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_TEXTURE 0x1702 #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_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C #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_REPEAT 0x2901 #define GL_CLAMP_TO_EDGE 0x812F #define GL_MIRRORED_REPEAT 0x8370 #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_2D 0x8B5E #define GL_SAMPLER_CUBE 0x8B60 #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_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A #define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 #define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F #define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A #define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B #define GL_COMPILE_STATUS 0x8B81 #define GL_INFO_LOG_LENGTH 0x8B84 #define GL_SHADER_SOURCE_LENGTH 0x8B88 #define GL_SHADER_COMPILER 0x8DFA #define GL_SHADER_BINARY_FORMATS 0x8DF8 #define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 #define GL_LOW_FLOAT 0x8DF0 #define GL_MEDIUM_FLOAT 0x8DF1 #define GL_HIGH_FLOAT 0x8DF2 #define GL_LOW_INT 0x8DF3 #define GL_MEDIUM_INT 0x8DF4 #define GL_HIGH_INT 0x8DF5 #define GL_FRAMEBUFFER 0x8D40 #define GL_RENDERBUFFER 0x8D41 #define GL_RGBA4 0x8056 #define GL_RGB5_A1 0x8057 #define GL_RGB565 0x8D62 #define GL_DEPTH_COMPONENT16 0x81A5 #define GL_STENCIL_INDEX8 0x8D48 #define GL_RENDERBUFFER_WIDTH 0x8D42 #define GL_RENDERBUFFER_HEIGHT 0x8D43 #define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 #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_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_COLOR_ATTACHMENT0 0x8CE0 #define GL_DEPTH_ATTACHMENT 0x8D00 #define GL_STENCIL_ATTACHMENT 0x8D20 #define GL_NONE 0 #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 #define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9 #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD #define GL_FRAMEBUFFER_BINDING 0x8CA6 #define GL_RENDERBUFFER_BINDING 0x8CA7 #define GL_MAX_RENDERBUFFER_SIZE 0x84E8 #define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 #define GL_READ_BUFFER 0x0C02 #define GL_UNPACK_ROW_LENGTH 0x0CF2 #define GL_UNPACK_SKIP_ROWS 0x0CF3 #define GL_UNPACK_SKIP_PIXELS 0x0CF4 #define GL_PACK_ROW_LENGTH 0x0D02 #define GL_PACK_SKIP_ROWS 0x0D03 #define GL_PACK_SKIP_PIXELS 0x0D04 #define GL_COLOR 0x1800 #define GL_DEPTH 0x1801 #define GL_STENCIL 0x1802 #define GL_RED 0x1903 #define GL_RGB8 0x8051 #define GL_RGBA8 0x8058 #define GL_RGB10_A2 0x8059 #define GL_TEXTURE_BINDING_3D 0x806A #define GL_UNPACK_SKIP_IMAGES 0x806D #define GL_UNPACK_IMAGE_HEIGHT 0x806E #define GL_TEXTURE_3D 0x806F #define GL_TEXTURE_WRAP_R 0x8072 #define GL_MAX_3D_TEXTURE_SIZE 0x8073 #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 #define GL_MAX_ELEMENTS_VERTICES 0x80E8 #define GL_MAX_ELEMENTS_INDICES 0x80E9 #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_MIN 0x8007 #define GL_MAX 0x8008 #define GL_DEPTH_COMPONENT24 0x81A6 #define GL_MAX_TEXTURE_LOD_BIAS 0x84FD #define GL_TEXTURE_COMPARE_MODE 0x884C #define GL_TEXTURE_COMPARE_FUNC 0x884D #define GL_CURRENT_QUERY 0x8865 #define GL_QUERY_RESULT 0x8866 #define GL_QUERY_RESULT_AVAILABLE 0x8867 #define GL_BUFFER_MAPPED 0x88BC #define GL_BUFFER_MAP_POINTER 0x88BD #define GL_STREAM_READ 0x88E1 #define GL_STREAM_COPY 0x88E2 #define GL_STATIC_READ 0x88E5 #define GL_STATIC_COPY 0x88E6 #define GL_DYNAMIC_READ 0x88E9 #define GL_DYNAMIC_COPY 0x88EA #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_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 #define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A #define GL_SAMPLER_3D 0x8B5F #define GL_SAMPLER_2D_SHADOW 0x8B62 #define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B #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_SRGB8_ALPHA8 0x8C43 #define GL_COMPARE_REF_TO_TEXTURE 0x884E #define GL_MAJOR_VERSION 0x821B #define GL_MINOR_VERSION 0x821C #define GL_NUM_EXTENSIONS 0x821D #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_MAX_VARYING_COMPONENTS 0x8B4B #define GL_TEXTURE_2D_ARRAY 0x8C1A #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_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_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_RGB_INTEGER 0x8D98 #define GL_RGBA_INTEGER 0x8D99 #define GL_SAMPLER_2D_ARRAY 0x8DC1 #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_2D 0x8DCA #define GL_INT_SAMPLER_3D 0x8DCB #define GL_INT_SAMPLER_CUBE 0x8DCC #define GL_INT_SAMPLER_2D_ARRAY 0x8DCF #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_2D_ARRAY 0x8DD7 #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_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_DEPTH_STENCIL 0x84F9 #define GL_UNSIGNED_INT_24_8 0x84FA #define GL_DEPTH24_STENCIL8 0x88F0 #define GL_UNSIGNED_NORMALIZED 0x8C17 #define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 #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_TEXTURE_LAYER 0x8CD4 #define GL_MAX_COLOR_ATTACHMENTS 0x8CDF #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_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 #define GL_MAX_SAMPLES 0x8D57 #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_RG 0x8227 #define GL_RG_INTEGER 0x8228 #define GL_R8 0x8229 #define GL_RG8 0x822B #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_R8_SNORM 0x8F94 #define GL_RG8_SNORM 0x8F95 #define GL_RGB8_SNORM 0x8F96 #define GL_RGBA8_SNORM 0x8F97 #define GL_SIGNED_NORMALIZED 0x8F9C #define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 #define GL_COPY_READ_BUFFER 0x8F36 #define GL_COPY_WRITE_BUFFER 0x8F37 #define GL_COPY_READ_BUFFER_BINDING 0x8F36 #define GL_COPY_WRITE_BUFFER_BINDING 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_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_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_FRAGMENT_SHADER 0x8A46 #define GL_INVALID_INDEX 0xFFFFFFFF #define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 #define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 #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_SYNC_FLUSH_COMMANDS_BIT 0x00000001 #define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF #define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE #define GL_ANY_SAMPLES_PASSED 0x8C2F #define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A #define GL_SAMPLER_BINDING 0x8919 #define GL_RGB10_A2UI 0x906F #define GL_TEXTURE_SWIZZLE_R 0x8E42 #define GL_TEXTURE_SWIZZLE_G 0x8E43 #define GL_TEXTURE_SWIZZLE_B 0x8E44 #define GL_TEXTURE_SWIZZLE_A 0x8E45 #define GL_GREEN 0x1904 #define GL_BLUE 0x1905 #define GL_INT_2_10_10_10_REV 0x8D9F #define GL_TRANSFORM_FEEDBACK 0x8E22 #define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 #define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 #define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 #define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 #define GL_PROGRAM_BINARY_LENGTH 0x8741 #define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE #define GL_PROGRAM_BINARY_FORMATS 0x87FF #define GL_COMPRESSED_R11_EAC 0x9270 #define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 #define GL_COMPRESSED_RG11_EAC 0x9272 #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 #define GL_COMPRESSED_RGB8_ETC2 0x9274 #define GL_COMPRESSED_SRGB8_ETC2 0x9275 #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 #define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 #define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 #define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F #define GL_MAX_ELEMENT_INDEX 0x8D6B #define GL_NUM_SAMPLE_COUNTS 0x9380 #define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF #ifndef GL_ES_VERSION_2_0 #define GL_ES_VERSION_2_0 1 GLAPI int GLAD_GL_ES_VERSION_2_0; typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum texture); GLAPI PFNGLACTIVETEXTUREPROC glad_glActiveTexture; #define glActiveTexture glad_glActiveTexture 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 PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); GLAPI PFNGLBINDBUFFERPROC glad_glBindBuffer; #define glBindBuffer glad_glBindBuffer typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); GLAPI PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer; #define glBindFramebuffer glad_glBindFramebuffer typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); GLAPI PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer; #define glBindRenderbuffer glad_glBindRenderbuffer typedef void (APIENTRYP PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); GLAPI PFNGLBINDTEXTUREPROC glad_glBindTexture; #define glBindTexture glad_glBindTexture 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 typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); GLAPI PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate; #define glBlendEquationSeparate glad_glBlendEquationSeparate typedef void (APIENTRYP PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); GLAPI PFNGLBLENDFUNCPROC glad_glBlendFunc; #define glBlendFunc glad_glBlendFunc typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); GLAPI PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate; #define glBlendFuncSeparate glad_glBlendFuncSeparate 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 GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); GLAPI PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus; #define glCheckFramebufferStatus glad_glCheckFramebufferStatus 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 PFNGLCLEARDEPTHFPROC)(GLfloat d); GLAPI PFNGLCLEARDEPTHFPROC glad_glClearDepthf; #define glClearDepthf glad_glClearDepthf typedef void (APIENTRYP PFNGLCLEARSTENCILPROC)(GLint s); GLAPI PFNGLCLEARSTENCILPROC glad_glClearStencil; #define glClearStencil glad_glClearStencil typedef void (APIENTRYP PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); GLAPI PFNGLCOLORMASKPROC glad_glColorMask; #define glColorMask glad_glColorMask typedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader); GLAPI PFNGLCOMPILESHADERPROC glad_glCompileShader; #define glCompileShader glad_glCompileShader 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 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 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 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 GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)(void); 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 PFNGLCULLFACEPROC)(GLenum mode); GLAPI PFNGLCULLFACEPROC glad_glCullFace; #define glCullFace glad_glCullFace typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers); GLAPI PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers; #define glDeleteBuffers glad_glDeleteBuffers typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint *framebuffers); GLAPI PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers; #define glDeleteFramebuffers glad_glDeleteFramebuffers typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint program); GLAPI PFNGLDELETEPROGRAMPROC glad_glDeleteProgram; #define glDeleteProgram glad_glDeleteProgram typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint *renderbuffers); GLAPI PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers; #define glDeleteRenderbuffers glad_glDeleteRenderbuffers typedef void (APIENTRYP PFNGLDELETESHADERPROC)(GLuint shader); GLAPI PFNGLDELETESHADERPROC glad_glDeleteShader; #define glDeleteShader glad_glDeleteShader typedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint *textures); GLAPI PFNGLDELETETEXTURESPROC glad_glDeleteTextures; #define glDeleteTextures glad_glDeleteTextures typedef void (APIENTRYP PFNGLDEPTHFUNCPROC)(GLenum func); GLAPI PFNGLDEPTHFUNCPROC glad_glDepthFunc; #define glDepthFunc glad_glDepthFunc typedef void (APIENTRYP PFNGLDEPTHMASKPROC)(GLboolean flag); GLAPI PFNGLDEPTHMASKPROC glad_glDepthMask; #define glDepthMask glad_glDepthMask typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC)(GLfloat n, GLfloat f); GLAPI PFNGLDEPTHRANGEFPROC glad_glDepthRangef; #define glDepthRangef glad_glDepthRangef typedef void (APIENTRYP PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); GLAPI PFNGLDETACHSHADERPROC glad_glDetachShader; #define glDetachShader glad_glDetachShader typedef void (APIENTRYP PFNGLDISABLEPROC)(GLenum cap); GLAPI PFNGLDISABLEPROC glad_glDisable; #define glDisable glad_glDisable typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); GLAPI PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray; #define glDisableVertexAttribArray glad_glDisableVertexAttribArray 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 PFNGLENABLEPROC)(GLenum cap); GLAPI PFNGLENABLEPROC glad_glEnable; #define glEnable glad_glEnable typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); GLAPI PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray; #define glEnableVertexAttribArray glad_glEnableVertexAttribArray typedef void (APIENTRYP PFNGLFINISHPROC)(void); GLAPI PFNGLFINISHPROC glad_glFinish; #define glFinish glad_glFinish typedef void (APIENTRYP PFNGLFLUSHPROC)(void); GLAPI PFNGLFLUSHPROC glad_glFlush; #define glFlush glad_glFlush typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); GLAPI PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer; #define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer 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 PFNGLFRONTFACEPROC)(GLenum mode); GLAPI PFNGLFRONTFACEPROC glad_glFrontFace; #define glFrontFace glad_glFrontFace typedef void (APIENTRYP PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers); GLAPI PFNGLGENBUFFERSPROC glad_glGenBuffers; #define glGenBuffers glad_glGenBuffers typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC)(GLenum target); GLAPI PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap; #define glGenerateMipmap glad_glGenerateMipmap typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers); GLAPI PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers; #define glGenFramebuffers glad_glGenFramebuffers typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers); GLAPI PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers; #define glGenRenderbuffers glad_glGenRenderbuffers typedef void (APIENTRYP PFNGLGENTEXTURESPROC)(GLsizei n, GLuint *textures); GLAPI PFNGLGENTEXTURESPROC glad_glGenTextures; #define glGenTextures glad_glGenTextures 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 PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean *data); GLAPI PFNGLGETBOOLEANVPROC glad_glGetBooleanv; #define glGetBooleanv glad_glGetBooleanv typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv; #define glGetBufferParameteriv glad_glGetBufferParameteriv typedef GLenum (APIENTRYP PFNGLGETERRORPROC)(void); 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 PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint *params); GLAPI PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv; #define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv typedef void (APIENTRYP PFNGLGETINTEGERVPROC)(GLenum pname, GLint *data); GLAPI PFNGLGETINTEGERVPROC glad_glGetIntegerv; #define glGetIntegerv glad_glGetIntegerv 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 PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); GLAPI PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv; #define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv 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 PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); GLAPI PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat; #define glGetShaderPrecisionFormat glad_glGetShaderPrecisionFormat typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); GLAPI PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource; #define glGetShaderSource glad_glGetShaderSource typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGPROC)(GLenum name); GLAPI PFNGLGETSTRINGPROC glad_glGetString; #define glGetString glad_glGetString 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 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 GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar *name); GLAPI PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation; #define glGetUniformLocation glad_glGetUniformLocation 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 void (APIENTRYP PFNGLHINTPROC)(GLenum target, GLenum mode); GLAPI PFNGLHINTPROC glad_glHint; #define glHint glad_glHint typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC)(GLuint buffer); GLAPI PFNGLISBUFFERPROC glad_glIsBuffer; #define glIsBuffer glad_glIsBuffer typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum cap); GLAPI PFNGLISENABLEDPROC glad_glIsEnabled; #define glIsEnabled glad_glIsEnabled typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); GLAPI PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer; #define glIsFramebuffer glad_glIsFramebuffer typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC)(GLuint program); GLAPI PFNGLISPROGRAMPROC glad_glIsProgram; #define glIsProgram glad_glIsProgram typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); GLAPI PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer; #define glIsRenderbuffer glad_glIsRenderbuffer typedef GLboolean (APIENTRYP PFNGLISSHADERPROC)(GLuint shader); GLAPI PFNGLISSHADERPROC glad_glIsShader; #define glIsShader glad_glIsShader typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC)(GLuint texture); GLAPI PFNGLISTEXTUREPROC glad_glIsTexture; #define glIsTexture glad_glIsTexture typedef void (APIENTRYP PFNGLLINEWIDTHPROC)(GLfloat width); GLAPI PFNGLLINEWIDTHPROC glad_glLineWidth; #define glLineWidth glad_glLineWidth typedef void (APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint program); GLAPI PFNGLLINKPROGRAMPROC glad_glLinkProgram; #define glLinkProgram glad_glLinkProgram typedef void (APIENTRYP PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); GLAPI PFNGLPIXELSTOREIPROC glad_glPixelStorei; #define glPixelStorei glad_glPixelStorei typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); GLAPI PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset; #define glPolygonOffset glad_glPolygonOffset 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 PFNGLRELEASESHADERCOMPILERPROC)(void); GLAPI PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler; #define glReleaseShaderCompiler glad_glReleaseShaderCompiler typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); GLAPI PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage; #define glRenderbufferStorage glad_glRenderbufferStorage typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); GLAPI PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage; #define glSampleCoverage glad_glSampleCoverage typedef void (APIENTRYP PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); GLAPI PFNGLSCISSORPROC glad_glScissor; #define glScissor glad_glScissor typedef void (APIENTRYP PFNGLSHADERBINARYPROC)(GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length); GLAPI PFNGLSHADERBINARYPROC glad_glShaderBinary; #define glShaderBinary glad_glShaderBinary 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 PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); GLAPI PFNGLSTENCILFUNCPROC glad_glStencilFunc; #define glStencilFunc glad_glStencilFunc typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); GLAPI PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate; #define glStencilFuncSeparate glad_glStencilFuncSeparate typedef void (APIENTRYP PFNGLSTENCILMASKPROC)(GLuint mask); GLAPI PFNGLSTENCILMASKPROC glad_glStencilMask; #define glStencilMask glad_glStencilMask typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); GLAPI PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate; #define glStencilMaskSeparate glad_glStencilMaskSeparate typedef void (APIENTRYP PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); GLAPI PFNGLSTENCILOPPROC glad_glStencilOp; #define glStencilOp glad_glStencilOp typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); GLAPI PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate; #define glStencilOpSeparate glad_glStencilOpSeparate 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 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 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 PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); GLAPI PFNGLUNIFORM1FPROC glad_glUniform1f; #define glUniform1f glad_glUniform1f typedef void (APIENTRYP PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM1FVPROC glad_glUniform1fv; #define glUniform1fv glad_glUniform1fv typedef void (APIENTRYP PFNGLUNIFORM1IPROC)(GLint location, GLint v0); GLAPI PFNGLUNIFORM1IPROC glad_glUniform1i; #define glUniform1i glad_glUniform1i typedef void (APIENTRYP PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM1IVPROC glad_glUniform1iv; #define glUniform1iv glad_glUniform1iv typedef void (APIENTRYP PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); GLAPI PFNGLUNIFORM2FPROC glad_glUniform2f; #define glUniform2f glad_glUniform2f typedef void (APIENTRYP PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM2FVPROC glad_glUniform2fv; #define glUniform2fv glad_glUniform2fv typedef void (APIENTRYP PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); GLAPI PFNGLUNIFORM2IPROC glad_glUniform2i; #define glUniform2i glad_glUniform2i typedef void (APIENTRYP PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM2IVPROC glad_glUniform2iv; #define glUniform2iv glad_glUniform2iv typedef void (APIENTRYP PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); GLAPI PFNGLUNIFORM3FPROC glad_glUniform3f; #define glUniform3f glad_glUniform3f typedef void (APIENTRYP PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM3FVPROC glad_glUniform3fv; #define glUniform3fv glad_glUniform3fv typedef void (APIENTRYP PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); GLAPI PFNGLUNIFORM3IPROC glad_glUniform3i; #define glUniform3i glad_glUniform3i typedef void (APIENTRYP PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint *value); GLAPI PFNGLUNIFORM3IVPROC glad_glUniform3iv; #define glUniform3iv glad_glUniform3iv 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 PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat *value); GLAPI PFNGLUNIFORM4FVPROC glad_glUniform4fv; #define glUniform4fv glad_glUniform4fv 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 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 PFNGLUSEPROGRAMPROC)(GLuint program); GLAPI PFNGLUSEPROGRAMPROC glad_glUseProgram; #define glUseProgram glad_glUseProgram typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC)(GLuint program); GLAPI PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram; #define glValidateProgram glad_glValidateProgram 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 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 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 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 PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); GLAPI PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer; #define glVertexAttribPointer glad_glVertexAttribPointer typedef void (APIENTRYP PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height); GLAPI PFNGLVIEWPORTPROC glad_glViewport; #define glViewport glad_glViewport #endif #ifndef GL_ES_VERSION_3_0 #define GL_ES_VERSION_3_0 1 GLAPI int GLAD_GL_ES_VERSION_3_0; typedef void (APIENTRYP PFNGLREADBUFFERPROC)(GLenum src); GLAPI PFNGLREADBUFFERPROC glad_glReadBuffer; #define glReadBuffer glad_glReadBuffer 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 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 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 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 PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint *params); GLAPI PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv; #define glGetQueryObjectuiv glad_glGetQueryObjectuiv typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC)(GLenum target); GLAPI PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer; #define glUnmapBuffer glad_glUnmapBuffer typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void **params); GLAPI PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv; #define glGetBufferPointerv glad_glGetBufferPointerv typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum *bufs); GLAPI PFNGLDRAWBUFFERSPROC glad_glDrawBuffers; #define glDrawBuffers glad_glDrawBuffers 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 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 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 PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode); GLAPI PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback; #define glBeginTransformFeedback glad_glBeginTransformFeedback typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC)(void); 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 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 PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w); GLAPI PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i; #define glVertexAttribI4i glad_glVertexAttribI4i 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 PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint *v); GLAPI PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv; #define glVertexAttribI4iv glad_glVertexAttribI4iv typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint *v); GLAPI PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv; #define glVertexAttribI4uiv glad_glVertexAttribI4uiv typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint *params); GLAPI PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv; #define glGetUniformuiv glad_glGetUniformuiv 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 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 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 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 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 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 count, 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 PFNGLGENSAMPLERSPROC)(GLsizei count, GLuint *samplers); GLAPI PFNGLGENSAMPLERSPROC glad_glGenSamplers; #define glGenSamplers glad_glGenSamplers typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC)(GLsizei count, const GLuint *samplers); GLAPI PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers; #define glDeleteSamplers glad_glDeleteSamplers typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC)(GLuint sampler); GLAPI PFNGLISSAMPLERPROC glad_glIsSampler; #define glIsSampler glad_glIsSampler typedef void (APIENTRYP PFNGLBINDSAMPLERPROC)(GLuint unit, GLuint sampler); GLAPI PFNGLBINDSAMPLERPROC glad_glBindSampler; #define glBindSampler glad_glBindSampler typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC)(GLuint sampler, GLenum pname, GLint param); GLAPI PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri; #define glSamplerParameteri glad_glSamplerParameteri typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, const GLint *param); GLAPI PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv; #define glSamplerParameteriv glad_glSamplerParameteriv typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC)(GLuint sampler, GLenum pname, GLfloat param); GLAPI PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf; #define glSamplerParameterf glad_glSamplerParameterf typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, const GLfloat *param); GLAPI PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv; #define glSamplerParameterfv glad_glSamplerParameterfv typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, GLint *params); GLAPI PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv; #define glGetSamplerParameteriv glad_glGetSamplerParameteriv typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, GLfloat *params); GLAPI PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv; #define glGetSamplerParameterfv glad_glGetSamplerParameterfv typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC)(GLuint index, GLuint divisor); GLAPI PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor; #define glVertexAttribDivisor glad_glVertexAttribDivisor typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC)(GLenum target, GLuint id); GLAPI PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback; #define glBindTransformFeedback glad_glBindTransformFeedback typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC)(GLsizei n, const GLuint *ids); GLAPI PFNGLDELETETRANSFORMFEEDBACKSPROC glad_glDeleteTransformFeedbacks; #define glDeleteTransformFeedbacks glad_glDeleteTransformFeedbacks typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint *ids); GLAPI PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks; #define glGenTransformFeedbacks glad_glGenTransformFeedbacks typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC)(GLuint id); GLAPI PFNGLISTRANSFORMFEEDBACKPROC glad_glIsTransformFeedback; #define glIsTransformFeedback glad_glIsTransformFeedback typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC)(void); GLAPI PFNGLPAUSETRANSFORMFEEDBACKPROC glad_glPauseTransformFeedback; #define glPauseTransformFeedback glad_glPauseTransformFeedback typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC)(void); GLAPI PFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback; #define glResumeTransformFeedback glad_glResumeTransformFeedback typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); GLAPI PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary; #define glGetProgramBinary glad_glGetProgramBinary typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC)(GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); GLAPI PFNGLPROGRAMBINARYPROC glad_glProgramBinary; #define glProgramBinary glad_glProgramBinary typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC)(GLuint program, GLenum pname, GLint value); GLAPI PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri; #define glProgramParameteri glad_glProgramParameteri typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum *attachments); GLAPI PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer; #define glInvalidateFramebuffer glad_glInvalidateFramebuffer typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); GLAPI PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer; #define glInvalidateSubFramebuffer glad_glInvalidateSubFramebuffer typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); GLAPI PFNGLTEXSTORAGE2DPROC glad_glTexStorage2D; #define glTexStorage2D glad_glTexStorage2D typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); GLAPI PFNGLTEXSTORAGE3DPROC glad_glTexStorage3D; #define glTexStorage3D glad_glTexStorage3D typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC)(GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params); GLAPI PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ; #define glGetInternalformativ glad_glGetInternalformativ #endif #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF #define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 #define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 #define GL_DEBUG_CALLBACK_FUNCTION 0x8244 #define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 #define GL_DEBUG_SOURCE_API 0x8246 #define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 #define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 #define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 #define GL_DEBUG_SOURCE_APPLICATION 0x824A #define GL_DEBUG_SOURCE_OTHER 0x824B #define GL_DEBUG_TYPE_ERROR 0x824C #define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E #define GL_DEBUG_TYPE_PORTABILITY 0x824F #define GL_DEBUG_TYPE_PERFORMANCE 0x8250 #define GL_DEBUG_TYPE_OTHER 0x8251 #define GL_DEBUG_TYPE_MARKER 0x8268 #define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 #define GL_DEBUG_TYPE_POP_GROUP 0x826A #define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B #define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C #define GL_DEBUG_GROUP_STACK_DEPTH 0x826D #define GL_BUFFER 0x82E0 #define GL_SHADER 0x82E1 #define GL_PROGRAM 0x82E2 #define GL_VERTEX_ARRAY 0x8074 #define GL_QUERY 0x82E3 #define GL_PROGRAM_PIPELINE 0x82E4 #define GL_SAMPLER 0x82E6 #define GL_MAX_LABEL_LENGTH 0x82E8 #define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 #define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 #define GL_DEBUG_LOGGED_MESSAGES 0x9145 #define GL_DEBUG_SEVERITY_HIGH 0x9146 #define GL_DEBUG_SEVERITY_MEDIUM 0x9147 #define GL_DEBUG_SEVERITY_LOW 0x9148 #define GL_DEBUG_OUTPUT 0x92E0 #define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 #define GL_STACK_OVERFLOW 0x0503 #define GL_STACK_UNDERFLOW 0x0504 #define GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR 0x8242 #define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR 0x8243 #define GL_DEBUG_CALLBACK_FUNCTION_KHR 0x8244 #define GL_DEBUG_CALLBACK_USER_PARAM_KHR 0x8245 #define GL_DEBUG_SOURCE_API_KHR 0x8246 #define GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR 0x8247 #define GL_DEBUG_SOURCE_SHADER_COMPILER_KHR 0x8248 #define GL_DEBUG_SOURCE_THIRD_PARTY_KHR 0x8249 #define GL_DEBUG_SOURCE_APPLICATION_KHR 0x824A #define GL_DEBUG_SOURCE_OTHER_KHR 0x824B #define GL_DEBUG_TYPE_ERROR_KHR 0x824C #define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR 0x824D #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR 0x824E #define GL_DEBUG_TYPE_PORTABILITY_KHR 0x824F #define GL_DEBUG_TYPE_PERFORMANCE_KHR 0x8250 #define GL_DEBUG_TYPE_OTHER_KHR 0x8251 #define GL_DEBUG_TYPE_MARKER_KHR 0x8268 #define GL_DEBUG_TYPE_PUSH_GROUP_KHR 0x8269 #define GL_DEBUG_TYPE_POP_GROUP_KHR 0x826A #define GL_DEBUG_SEVERITY_NOTIFICATION_KHR 0x826B #define GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR 0x826C #define GL_DEBUG_GROUP_STACK_DEPTH_KHR 0x826D #define GL_BUFFER_KHR 0x82E0 #define GL_SHADER_KHR 0x82E1 #define GL_PROGRAM_KHR 0x82E2 #define GL_VERTEX_ARRAY_KHR 0x8074 #define GL_QUERY_KHR 0x82E3 #define GL_PROGRAM_PIPELINE_KHR 0x82E4 #define GL_SAMPLER_KHR 0x82E6 #define GL_MAX_LABEL_LENGTH_KHR 0x82E8 #define GL_MAX_DEBUG_MESSAGE_LENGTH_KHR 0x9143 #define GL_MAX_DEBUG_LOGGED_MESSAGES_KHR 0x9144 #define GL_DEBUG_LOGGED_MESSAGES_KHR 0x9145 #define GL_DEBUG_SEVERITY_HIGH_KHR 0x9146 #define GL_DEBUG_SEVERITY_MEDIUM_KHR 0x9147 #define GL_DEBUG_SEVERITY_LOW_KHR 0x9148 #define GL_DEBUG_OUTPUT_KHR 0x92E0 #define GL_CONTEXT_FLAG_DEBUG_BIT_KHR 0x00000002 #define GL_STACK_OVERFLOW_KHR 0x0503 #define GL_STACK_UNDERFLOW_KHR 0x0504 #define GL_DISPLAY_LIST 0x82E7 #ifndef GL_EXT_texture_filter_anisotropic #define GL_EXT_texture_filter_anisotropic 1 GLAPI int GLAD_GL_EXT_texture_filter_anisotropic; #endif #ifndef GL_KHR_debug #define GL_KHR_debug 1 GLAPI int GLAD_GL_KHR_debug; typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); GLAPI PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl; #define glDebugMessageControl glad_glDebugMessageControl typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); GLAPI PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert; #define glDebugMessageInsert glad_glDebugMessageInsert typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback, const void *userParam); GLAPI PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback; #define glDebugMessageCallback glad_glDebugMessageCallback typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); GLAPI PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog; #define glGetDebugMessageLog glad_glGetDebugMessageLog typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length, const GLchar *message); GLAPI PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup; #define glPushDebugGroup glad_glPushDebugGroup typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC)(void); GLAPI PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup; #define glPopDebugGroup glad_glPopDebugGroup typedef void (APIENTRYP PFNGLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label); GLAPI PFNGLOBJECTLABELPROC glad_glObjectLabel; #define glObjectLabel glad_glObjectLabel typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); GLAPI PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel; #define glGetObjectLabel glad_glGetObjectLabel typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC)(const void *ptr, GLsizei length, const GLchar *label); GLAPI PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel; #define glObjectPtrLabel glad_glObjectPtrLabel typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); GLAPI PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel; #define glGetObjectPtrLabel glad_glGetObjectPtrLabel typedef void (APIENTRYP PFNGLGETPOINTERVPROC)(GLenum pname, void **params); GLAPI PFNGLGETPOINTERVPROC glad_glGetPointerv; #define glGetPointerv glad_glGetPointerv typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLKHRPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); GLAPI PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR; #define glDebugMessageControlKHR glad_glDebugMessageControlKHR typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTKHRPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); GLAPI PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR; #define glDebugMessageInsertKHR glad_glDebugMessageInsertKHR typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKKHRPROC)(GLDEBUGPROCKHR callback, const void *userParam); GLAPI PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR; #define glDebugMessageCallbackKHR glad_glDebugMessageCallbackKHR typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGKHRPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); GLAPI PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR; #define glGetDebugMessageLogKHR glad_glGetDebugMessageLogKHR typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPKHRPROC)(GLenum source, GLuint id, GLsizei length, const GLchar *message); GLAPI PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR; #define glPushDebugGroupKHR glad_glPushDebugGroupKHR typedef void (APIENTRYP PFNGLPOPDEBUGGROUPKHRPROC)(void); GLAPI PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR; #define glPopDebugGroupKHR glad_glPopDebugGroupKHR typedef void (APIENTRYP PFNGLOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label); GLAPI PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR; #define glObjectLabelKHR glad_glObjectLabelKHR typedef void (APIENTRYP PFNGLGETOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); GLAPI PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR; #define glGetObjectLabelKHR glad_glGetObjectLabelKHR typedef void (APIENTRYP PFNGLOBJECTPTRLABELKHRPROC)(const void *ptr, GLsizei length, const GLchar *label); GLAPI PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR; #define glObjectPtrLabelKHR glad_glObjectPtrLabelKHR typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELKHRPROC)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); GLAPI PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR; #define glGetObjectPtrLabelKHR glad_glGetObjectPtrLabelKHR typedef void (APIENTRYP PFNGLGETPOINTERVKHRPROC)(GLenum pname, void **params); GLAPI PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR; #define glGetPointervKHR glad_glGetPointervKHR #endif #ifdef __cplusplus } #endif #endif yquake2-QUAKE2_8_40/src/client/refresh/gl3/glad-gles3/src/000077500000000000000000000000001465112212000230125ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/gl3/glad-gles3/src/glad.c000066400000000000000000001204111465112212000240640ustar00rootroot00000000000000/* OpenGL ES loader generated by glad 0.1.36 on Sun May 14 07:56:21 2023. Language/Generator: C/C++ Specification: gl APIs: gles2=3.0 Profile: core Extensions: GL_EXT_texture_filter_anisotropic, GL_KHR_debug Loader: False Local files: False Omit khrplatform: False Reproducible: False Commandline: --profile="core" --api="gles2=3.0" --generator="c" --spec="gl" --no-loader --extensions="GL_EXT_texture_filter_anisotropic,GL_KHR_debug" Online: https://glad.dav1d.de/#profile=core&language=c&specification=gl&api=gles2%3D3.0&extensions=GL_EXT_texture_filter_anisotropic&extensions=GL_KHR_debug */ #include #include #include #include struct gladGLversionStruct GLVersion = { 0, 0 }; #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 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 { unsigned int index; num_exts_i = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts_i); if (num_exts_i > 0) { exts_i = (char **)malloc((size_t)num_exts_i * (sizeof *exts_i)); } if (exts_i == NULL) { return 0; } for(index = 0; index < (unsigned)num_exts_i; index++) { const char *gl_str_tmp = (const char*)glGetStringi(GL_EXTENSIONS, index); size_t len = strlen(gl_str_tmp); char *local_str = (char*)malloc((len+1) * sizeof(char)); if(local_str != NULL) { memcpy(local_str, gl_str_tmp, (len+1) * sizeof(char)); } exts_i[index] = local_str; } } #endif return 1; } static void free_exts(void) { if (exts_i != NULL) { int index; for(index = 0; index < num_exts_i; index++) { free((char *)exts_i[index]); } free((void *)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; if(exts_i == NULL) return 0; for(index = 0; index < num_exts_i; index++) { const char *e = exts_i[index]; if(exts_i[index] != NULL && strcmp(e, ext) == 0) { return 1; } } } #endif return 0; } int GLAD_GL_ES_VERSION_2_0 = 0; int GLAD_GL_ES_VERSION_3_0 = 0; PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL; PFNGLATTACHSHADERPROC glad_glAttachShader = NULL; PFNGLBEGINQUERYPROC glad_glBeginQuery = NULL; PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback = NULL; PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL; PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL; PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL; PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL; PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL; PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL; PFNGLBINDSAMPLERPROC glad_glBindSampler = NULL; PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL; PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback = NULL; PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL; PFNGLBLENDCOLORPROC glad_glBlendColor = NULL; PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL; PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL; PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL; PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL; PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL; PFNGLBUFFERDATAPROC glad_glBufferData = NULL; PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL; PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL; PFNGLCLEARPROC glad_glClear = NULL; PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi = NULL; PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv = NULL; PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv = NULL; PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL; PFNGLCLEARCOLORPROC glad_glClearColor = NULL; PFNGLCLEARDEPTHFPROC glad_glClearDepthf = NULL; PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL; PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL; PFNGLCOLORMASKPROC glad_glColorMask = NULL; PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL; PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL; PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL; PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL; PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL; PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL; PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL; PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL; PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL; PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL; PFNGLCREATESHADERPROC glad_glCreateShader = NULL; PFNGLCULLFACEPROC glad_glCullFace = NULL; PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL; PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL; PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL; PFNGLDELETEQUERIESPROC glad_glDeleteQueries = NULL; PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL; PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers = NULL; PFNGLDELETESHADERPROC glad_glDeleteShader = NULL; PFNGLDELETESYNCPROC glad_glDeleteSync = NULL; PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL; PFNGLDELETETRANSFORMFEEDBACKSPROC glad_glDeleteTransformFeedbacks = NULL; PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays = NULL; PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL; PFNGLDEPTHMASKPROC glad_glDepthMask = NULL; PFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL; PFNGLDETACHSHADERPROC glad_glDetachShader = NULL; PFNGLDISABLEPROC glad_glDisable = NULL; PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL; PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL; PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL; PFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL; PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL; PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced = NULL; PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL; PFNGLENABLEPROC glad_glEnable = NULL; PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL; PFNGLENDQUERYPROC glad_glEndQuery = NULL; PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback = NULL; PFNGLFENCESYNCPROC glad_glFenceSync = NULL; PFNGLFINISHPROC glad_glFinish = NULL; PFNGLFLUSHPROC glad_glFlush = NULL; PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL; PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL; PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL; PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer = NULL; PFNGLFRONTFACEPROC glad_glFrontFace = NULL; PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL; PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL; PFNGLGENQUERIESPROC glad_glGenQueries = NULL; PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL; PFNGLGENSAMPLERSPROC glad_glGenSamplers = NULL; PFNGLGENTEXTURESPROC glad_glGenTextures = NULL; PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks = NULL; PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL; PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL; PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL; PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL; PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName = NULL; PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv = NULL; PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv = NULL; PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL; PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL; PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL; PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v = NULL; PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL; PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL; PFNGLGETERRORPROC glad_glGetError = NULL; PFNGLGETFLOATVPROC glad_glGetFloatv = NULL; PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation = NULL; PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL; PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v = NULL; PFNGLGETINTEGER64VPROC glad_glGetInteger64v = NULL; PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v = NULL; PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL; PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ = NULL; PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary = NULL; PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL; PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL; PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv = NULL; PFNGLGETQUERYIVPROC glad_glGetQueryiv = NULL; PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL; PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv = NULL; PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv = NULL; PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL; PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat = NULL; PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL; PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL; PFNGLGETSTRINGPROC glad_glGetString = NULL; PFNGLGETSTRINGIPROC glad_glGetStringi = NULL; PFNGLGETSYNCIVPROC glad_glGetSynciv = NULL; PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL; PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL; PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL; PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL; PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL; PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL; PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL; PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL; PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL; PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL; PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL; PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL; PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL; PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL; PFNGLHINTPROC glad_glHint = NULL; PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer = NULL; PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer = NULL; PFNGLISBUFFERPROC glad_glIsBuffer = NULL; PFNGLISENABLEDPROC glad_glIsEnabled = NULL; PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL; PFNGLISPROGRAMPROC glad_glIsProgram = NULL; PFNGLISQUERYPROC glad_glIsQuery = NULL; PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL; PFNGLISSAMPLERPROC glad_glIsSampler = NULL; PFNGLISSHADERPROC glad_glIsShader = NULL; PFNGLISSYNCPROC glad_glIsSync = NULL; PFNGLISTEXTUREPROC glad_glIsTexture = NULL; PFNGLISTRANSFORMFEEDBACKPROC glad_glIsTransformFeedback = NULL; PFNGLISVERTEXARRAYPROC glad_glIsVertexArray = NULL; PFNGLLINEWIDTHPROC glad_glLineWidth = NULL; PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL; PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL; PFNGLPAUSETRANSFORMFEEDBACKPROC glad_glPauseTransformFeedback = NULL; PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL; PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL; PFNGLPROGRAMBINARYPROC glad_glProgramBinary = NULL; PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri = NULL; PFNGLREADBUFFERPROC glad_glReadBuffer = NULL; PFNGLREADPIXELSPROC glad_glReadPixels = NULL; PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler = NULL; PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL; PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL; PFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback = NULL; PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL; PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf = NULL; PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv = NULL; PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri = NULL; PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv = NULL; PFNGLSCISSORPROC glad_glScissor = NULL; PFNGLSHADERBINARYPROC glad_glShaderBinary = NULL; PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL; PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL; PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL; PFNGLSTENCILMASKPROC glad_glStencilMask = NULL; PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL; PFNGLSTENCILOPPROC glad_glStencilOp = NULL; PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL; PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL; PFNGLTEXIMAGE3DPROC glad_glTexImage3D = NULL; PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL; PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL; PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL; PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL; PFNGLTEXSTORAGE2DPROC glad_glTexStorage2D = NULL; PFNGLTEXSTORAGE3DPROC glad_glTexStorage3D = NULL; PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL; PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL; PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL; PFNGLUNIFORM1FPROC glad_glUniform1f = NULL; PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL; PFNGLUNIFORM1IPROC glad_glUniform1i = NULL; PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL; PFNGLUNIFORM1UIPROC glad_glUniform1ui = NULL; PFNGLUNIFORM1UIVPROC glad_glUniform1uiv = NULL; PFNGLUNIFORM2FPROC glad_glUniform2f = NULL; PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL; PFNGLUNIFORM2IPROC glad_glUniform2i = NULL; PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL; PFNGLUNIFORM2UIPROC glad_glUniform2ui = NULL; PFNGLUNIFORM2UIVPROC glad_glUniform2uiv = NULL; PFNGLUNIFORM3FPROC glad_glUniform3f = NULL; PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL; PFNGLUNIFORM3IPROC glad_glUniform3i = NULL; PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL; PFNGLUNIFORM3UIPROC glad_glUniform3ui = NULL; PFNGLUNIFORM3UIVPROC glad_glUniform3uiv = NULL; PFNGLUNIFORM4FPROC glad_glUniform4f = NULL; PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL; PFNGLUNIFORM4IPROC glad_glUniform4i = NULL; PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL; PFNGLUNIFORM4UIPROC glad_glUniform4ui = NULL; PFNGLUNIFORM4UIVPROC glad_glUniform4uiv = NULL; PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding = NULL; PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL; PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv = NULL; PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv = NULL; PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL; PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv = NULL; PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv = NULL; PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL; PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv = NULL; PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL; PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL; PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL; PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL; PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL; PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL; PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL; PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL; PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL; PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL; PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL; PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL; PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor = NULL; PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i = NULL; PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv = NULL; PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui = NULL; PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv = NULL; PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer = NULL; PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL; PFNGLVIEWPORTPROC glad_glViewport = NULL; PFNGLWAITSYNCPROC glad_glWaitSync = NULL; int GLAD_GL_EXT_texture_filter_anisotropic = 0; int GLAD_GL_KHR_debug = 0; PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl = NULL; PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert = NULL; PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback = NULL; PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog = NULL; PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup = NULL; PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup = NULL; PFNGLOBJECTLABELPROC glad_glObjectLabel = NULL; PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel = NULL; PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel = NULL; PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel = NULL; PFNGLGETPOINTERVPROC glad_glGetPointerv = NULL; PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR = NULL; PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR = NULL; PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR = NULL; PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR = NULL; PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR = NULL; PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR = NULL; PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR = NULL; PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR = NULL; PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR = NULL; PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR = NULL; PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR = NULL; static void load_GL_ES_VERSION_2_0(GLADloadproc load) { if(!GLAD_GL_ES_VERSION_2_0) return; glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC)load("glActiveTexture"); glad_glAttachShader = (PFNGLATTACHSHADERPROC)load("glAttachShader"); glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)load("glBindAttribLocation"); glad_glBindBuffer = (PFNGLBINDBUFFERPROC)load("glBindBuffer"); glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)load("glBindFramebuffer"); glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)load("glBindRenderbuffer"); glad_glBindTexture = (PFNGLBINDTEXTUREPROC)load("glBindTexture"); glad_glBlendColor = (PFNGLBLENDCOLORPROC)load("glBlendColor"); glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC)load("glBlendEquation"); glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)load("glBlendEquationSeparate"); glad_glBlendFunc = (PFNGLBLENDFUNCPROC)load("glBlendFunc"); glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)load("glBlendFuncSeparate"); glad_glBufferData = (PFNGLBUFFERDATAPROC)load("glBufferData"); glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC)load("glBufferSubData"); glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)load("glCheckFramebufferStatus"); glad_glClear = (PFNGLCLEARPROC)load("glClear"); glad_glClearColor = (PFNGLCLEARCOLORPROC)load("glClearColor"); glad_glClearDepthf = (PFNGLCLEARDEPTHFPROC)load("glClearDepthf"); glad_glClearStencil = (PFNGLCLEARSTENCILPROC)load("glClearStencil"); glad_glColorMask = (PFNGLCOLORMASKPROC)load("glColorMask"); glad_glCompileShader = (PFNGLCOMPILESHADERPROC)load("glCompileShader"); glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)load("glCompressedTexImage2D"); glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)load("glCompressedTexSubImage2D"); glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC)load("glCopyTexImage2D"); glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC)load("glCopyTexSubImage2D"); glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC)load("glCreateProgram"); glad_glCreateShader = (PFNGLCREATESHADERPROC)load("glCreateShader"); glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace"); glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)load("glDeleteBuffers"); glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)load("glDeleteFramebuffers"); glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC)load("glDeleteProgram"); glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)load("glDeleteRenderbuffers"); glad_glDeleteShader = (PFNGLDELETESHADERPROC)load("glDeleteShader"); glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC)load("glDeleteTextures"); glad_glDepthFunc = (PFNGLDEPTHFUNCPROC)load("glDepthFunc"); glad_glDepthMask = (PFNGLDEPTHMASKPROC)load("glDepthMask"); glad_glDepthRangef = (PFNGLDEPTHRANGEFPROC)load("glDepthRangef"); glad_glDetachShader = (PFNGLDETACHSHADERPROC)load("glDetachShader"); glad_glDisable = (PFNGLDISABLEPROC)load("glDisable"); glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load("glDisableVertexAttribArray"); glad_glDrawArrays = (PFNGLDRAWARRAYSPROC)load("glDrawArrays"); glad_glDrawElements = (PFNGLDRAWELEMENTSPROC)load("glDrawElements"); glad_glEnable = (PFNGLENABLEPROC)load("glEnable"); glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load("glEnableVertexAttribArray"); glad_glFinish = (PFNGLFINISHPROC)load("glFinish"); glad_glFlush = (PFNGLFLUSHPROC)load("glFlush"); glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)load("glFramebufferRenderbuffer"); glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)load("glFramebufferTexture2D"); glad_glFrontFace = (PFNGLFRONTFACEPROC)load("glFrontFace"); glad_glGenBuffers = (PFNGLGENBUFFERSPROC)load("glGenBuffers"); glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)load("glGenerateMipmap"); glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)load("glGenFramebuffers"); glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)load("glGenRenderbuffers"); glad_glGenTextures = (PFNGLGENTEXTURESPROC)load("glGenTextures"); glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)load("glGetActiveAttrib"); glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)load("glGetActiveUniform"); glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC)load("glGetAttachedShaders"); glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)load("glGetAttribLocation"); glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC)load("glGetBooleanv"); glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load("glGetBufferParameteriv"); glad_glGetError = (PFNGLGETERRORPROC)load("glGetError"); glad_glGetFloatv = (PFNGLGETFLOATVPROC)load("glGetFloatv"); glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load("glGetFramebufferAttachmentParameteriv"); glad_glGetIntegerv = (PFNGLGETINTEGERVPROC)load("glGetIntegerv"); glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC)load("glGetProgramiv"); glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)load("glGetProgramInfoLog"); glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)load("glGetRenderbufferParameteriv"); glad_glGetShaderiv = (PFNGLGETSHADERIVPROC)load("glGetShaderiv"); glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)load("glGetShaderInfoLog"); glad_glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC)load("glGetShaderPrecisionFormat"); glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC)load("glGetShaderSource"); glad_glGetString = (PFNGLGETSTRINGPROC)load("glGetString"); glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC)load("glGetTexParameterfv"); glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC)load("glGetTexParameteriv"); glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC)load("glGetUniformfv"); glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC)load("glGetUniformiv"); glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)load("glGetUniformLocation"); glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC)load("glGetVertexAttribfv"); glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC)load("glGetVertexAttribiv"); glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC)load("glGetVertexAttribPointerv"); glad_glHint = (PFNGLHINTPROC)load("glHint"); glad_glIsBuffer = (PFNGLISBUFFERPROC)load("glIsBuffer"); glad_glIsEnabled = (PFNGLISENABLEDPROC)load("glIsEnabled"); glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)load("glIsFramebuffer"); glad_glIsProgram = (PFNGLISPROGRAMPROC)load("glIsProgram"); glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)load("glIsRenderbuffer"); glad_glIsShader = (PFNGLISSHADERPROC)load("glIsShader"); glad_glIsTexture = (PFNGLISTEXTUREPROC)load("glIsTexture"); glad_glLineWidth = (PFNGLLINEWIDTHPROC)load("glLineWidth"); glad_glLinkProgram = (PFNGLLINKPROGRAMPROC)load("glLinkProgram"); glad_glPixelStorei = (PFNGLPIXELSTOREIPROC)load("glPixelStorei"); glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC)load("glPolygonOffset"); glad_glReadPixels = (PFNGLREADPIXELSPROC)load("glReadPixels"); glad_glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC)load("glReleaseShaderCompiler"); glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)load("glRenderbufferStorage"); glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)load("glSampleCoverage"); glad_glScissor = (PFNGLSCISSORPROC)load("glScissor"); glad_glShaderBinary = (PFNGLSHADERBINARYPROC)load("glShaderBinary"); glad_glShaderSource = (PFNGLSHADERSOURCEPROC)load("glShaderSource"); glad_glStencilFunc = (PFNGLSTENCILFUNCPROC)load("glStencilFunc"); glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)load("glStencilFuncSeparate"); glad_glStencilMask = (PFNGLSTENCILMASKPROC)load("glStencilMask"); glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC)load("glStencilMaskSeparate"); glad_glStencilOp = (PFNGLSTENCILOPPROC)load("glStencilOp"); glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)load("glStencilOpSeparate"); glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC)load("glTexImage2D"); glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC)load("glTexParameterf"); glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC)load("glTexParameterfv"); glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC)load("glTexParameteri"); glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC)load("glTexParameteriv"); glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC)load("glTexSubImage2D"); glad_glUniform1f = (PFNGLUNIFORM1FPROC)load("glUniform1f"); glad_glUniform1fv = (PFNGLUNIFORM1FVPROC)load("glUniform1fv"); glad_glUniform1i = (PFNGLUNIFORM1IPROC)load("glUniform1i"); glad_glUniform1iv = (PFNGLUNIFORM1IVPROC)load("glUniform1iv"); glad_glUniform2f = (PFNGLUNIFORM2FPROC)load("glUniform2f"); glad_glUniform2fv = (PFNGLUNIFORM2FVPROC)load("glUniform2fv"); glad_glUniform2i = (PFNGLUNIFORM2IPROC)load("glUniform2i"); glad_glUniform2iv = (PFNGLUNIFORM2IVPROC)load("glUniform2iv"); glad_glUniform3f = (PFNGLUNIFORM3FPROC)load("glUniform3f"); glad_glUniform3fv = (PFNGLUNIFORM3FVPROC)load("glUniform3fv"); glad_glUniform3i = (PFNGLUNIFORM3IPROC)load("glUniform3i"); glad_glUniform3iv = (PFNGLUNIFORM3IVPROC)load("glUniform3iv"); glad_glUniform4f = (PFNGLUNIFORM4FPROC)load("glUniform4f"); glad_glUniform4fv = (PFNGLUNIFORM4FVPROC)load("glUniform4fv"); glad_glUniform4i = (PFNGLUNIFORM4IPROC)load("glUniform4i"); glad_glUniform4iv = (PFNGLUNIFORM4IVPROC)load("glUniform4iv"); glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)load("glUniformMatrix2fv"); glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)load("glUniformMatrix3fv"); glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)load("glUniformMatrix4fv"); glad_glUseProgram = (PFNGLUSEPROGRAMPROC)load("glUseProgram"); glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)load("glValidateProgram"); glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)load("glVertexAttrib1f"); glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC)load("glVertexAttrib1fv"); glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)load("glVertexAttrib2f"); glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)load("glVertexAttrib2fv"); glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)load("glVertexAttrib3f"); glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)load("glVertexAttrib3fv"); glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)load("glVertexAttrib4f"); glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)load("glVertexAttrib4fv"); glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load("glVertexAttribPointer"); glad_glViewport = (PFNGLVIEWPORTPROC)load("glViewport"); } static void load_GL_ES_VERSION_3_0(GLADloadproc load) { if(!GLAD_GL_ES_VERSION_3_0) return; glad_glReadBuffer = (PFNGLREADBUFFERPROC)load("glReadBuffer"); glad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)load("glDrawRangeElements"); glad_glTexImage3D = (PFNGLTEXIMAGE3DPROC)load("glTexImage3D"); glad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)load("glTexSubImage3D"); glad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)load("glCopyTexSubImage3D"); glad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)load("glCompressedTexImage3D"); glad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)load("glCompressedTexSubImage3D"); 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_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)load("glGetQueryObjectuiv"); glad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)load("glUnmapBuffer"); glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)load("glGetBufferPointerv"); glad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC)load("glDrawBuffers"); 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"); 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"); glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load("glGetIntegeri_v"); 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_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)load("glVertexAttribIPointer"); glad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC)load("glGetVertexAttribIiv"); glad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC)load("glGetVertexAttribIuiv"); glad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC)load("glVertexAttribI4i"); glad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC)load("glVertexAttribI4ui"); glad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC)load("glVertexAttribI4iv"); glad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC)load("glVertexAttribI4uiv"); glad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC)load("glGetUniformuiv"); 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_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_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC)load("glCopyBufferSubData"); glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)load("glGetUniformIndices"); glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)load("glGetActiveUniformsiv"); glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)load("glGetUniformBlockIndex"); glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)load("glGetActiveUniformBlockiv"); glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)load("glGetActiveUniformBlockName"); glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)load("glUniformBlockBinding"); glad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)load("glDrawArraysInstanced"); glad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)load("glDrawElementsInstanced"); 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_glGenSamplers = (PFNGLGENSAMPLERSPROC)load("glGenSamplers"); glad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC)load("glDeleteSamplers"); glad_glIsSampler = (PFNGLISSAMPLERPROC)load("glIsSampler"); glad_glBindSampler = (PFNGLBINDSAMPLERPROC)load("glBindSampler"); glad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC)load("glSamplerParameteri"); glad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC)load("glSamplerParameteriv"); glad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC)load("glSamplerParameterf"); glad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC)load("glSamplerParameterfv"); glad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)load("glGetSamplerParameteriv"); glad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)load("glGetSamplerParameterfv"); glad_glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)load("glVertexAttribDivisor"); glad_glBindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC)load("glBindTransformFeedback"); glad_glDeleteTransformFeedbacks = (PFNGLDELETETRANSFORMFEEDBACKSPROC)load("glDeleteTransformFeedbacks"); glad_glGenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC)load("glGenTransformFeedbacks"); glad_glIsTransformFeedback = (PFNGLISTRANSFORMFEEDBACKPROC)load("glIsTransformFeedback"); glad_glPauseTransformFeedback = (PFNGLPAUSETRANSFORMFEEDBACKPROC)load("glPauseTransformFeedback"); glad_glResumeTransformFeedback = (PFNGLRESUMETRANSFORMFEEDBACKPROC)load("glResumeTransformFeedback"); glad_glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC)load("glGetProgramBinary"); glad_glProgramBinary = (PFNGLPROGRAMBINARYPROC)load("glProgramBinary"); glad_glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)load("glProgramParameteri"); glad_glInvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC)load("glInvalidateFramebuffer"); glad_glInvalidateSubFramebuffer = (PFNGLINVALIDATESUBFRAMEBUFFERPROC)load("glInvalidateSubFramebuffer"); glad_glTexStorage2D = (PFNGLTEXSTORAGE2DPROC)load("glTexStorage2D"); glad_glTexStorage3D = (PFNGLTEXSTORAGE3DPROC)load("glTexStorage3D"); glad_glGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC)load("glGetInternalformativ"); } static void load_GL_KHR_debug(GLADloadproc load) { if(!GLAD_GL_KHR_debug) return; glad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)load("glDebugMessageControl"); glad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC)load("glDebugMessageInsert"); glad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)load("glDebugMessageCallback"); glad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC)load("glGetDebugMessageLog"); glad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC)load("glPushDebugGroup"); glad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC)load("glPopDebugGroup"); glad_glObjectLabel = (PFNGLOBJECTLABELPROC)load("glObjectLabel"); glad_glGetObjectLabel = (PFNGLGETOBJECTLABELPROC)load("glGetObjectLabel"); glad_glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC)load("glObjectPtrLabel"); glad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)load("glGetObjectPtrLabel"); glad_glGetPointerv = (PFNGLGETPOINTERVPROC)load("glGetPointerv"); glad_glDebugMessageControlKHR = (PFNGLDEBUGMESSAGECONTROLKHRPROC)load("glDebugMessageControlKHR"); glad_glDebugMessageInsertKHR = (PFNGLDEBUGMESSAGEINSERTKHRPROC)load("glDebugMessageInsertKHR"); glad_glDebugMessageCallbackKHR = (PFNGLDEBUGMESSAGECALLBACKKHRPROC)load("glDebugMessageCallbackKHR"); glad_glGetDebugMessageLogKHR = (PFNGLGETDEBUGMESSAGELOGKHRPROC)load("glGetDebugMessageLogKHR"); glad_glPushDebugGroupKHR = (PFNGLPUSHDEBUGGROUPKHRPROC)load("glPushDebugGroupKHR"); glad_glPopDebugGroupKHR = (PFNGLPOPDEBUGGROUPKHRPROC)load("glPopDebugGroupKHR"); glad_glObjectLabelKHR = (PFNGLOBJECTLABELKHRPROC)load("glObjectLabelKHR"); glad_glGetObjectLabelKHR = (PFNGLGETOBJECTLABELKHRPROC)load("glGetObjectLabelKHR"); glad_glObjectPtrLabelKHR = (PFNGLOBJECTPTRLABELKHRPROC)load("glObjectPtrLabelKHR"); glad_glGetObjectPtrLabelKHR = (PFNGLGETOBJECTPTRLABELKHRPROC)load("glGetObjectPtrLabelKHR"); glad_glGetPointervKHR = (PFNGLGETPOINTERVKHRPROC)load("glGetPointervKHR"); } static int find_extensionsGLES2(void) { if (!get_exts()) return 0; GLAD_GL_EXT_texture_filter_anisotropic = has_ext("GL_EXT_texture_filter_anisotropic"); GLAD_GL_KHR_debug = has_ext("GL_KHR_debug"); free_exts(); return 1; } static void find_coreGLES2(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_ES_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2; GLAD_GL_ES_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3; if (GLVersion.major > 3 || (GLVersion.major >= 3 && GLVersion.minor >= 0)) { max_loaded_major = 3; max_loaded_minor = 0; } } int gladLoadGLES2Loader(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_coreGLES2(); load_GL_ES_VERSION_2_0(load); load_GL_ES_VERSION_3_0(load); if (!find_extensionsGLES2()) return 0; load_GL_KHR_debug(load); return GLVersion.major != 0 || GLVersion.minor != 0; } yquake2-QUAKE2_8_40/src/client/refresh/gl3/glad/000077500000000000000000000000001465112212000212105ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/gl3/glad/include/000077500000000000000000000000001465112212000226335ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/gl3/glad/include/KHR/000077500000000000000000000000001465112212000232575ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/gl3/glad/include/KHR/khrplatform.h000066400000000000000000000255731465112212000257750ustar00rootroot00000000000000#ifndef __khrplatform_h_ #define __khrplatform_h_ /* ** Copyright (c) 2008-2018 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. * * The master copy of khrplatform.h is maintained in the Khronos EGL * Registry repository at https://github.com/KhronosGroup/EGL-Registry * The last semantic modification to khrplatform.h was at commit ID: * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 * * 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 filing pull requests or issues on * the EGL Registry repository linked above. * * * 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; */ #if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) # define KHRONOS_STATIC 1 #endif /*------------------------------------------------------------------------- * Definition of KHRONOS_APICALL *------------------------------------------------------------------------- * This precedes the return type of the function in the function prototype. */ #if defined(KHRONOS_STATIC) /* If the preprocessor constant KHRONOS_STATIC is defined, make the * header compatible with static linking. */ # define KHRONOS_APICALL #elif defined(_WIN32) # define KHRONOS_APICALL __declspec(dllimport) #elif defined (__SYMBIAN32__) # define KHRONOS_APICALL IMPORT_C #elif defined(__ANDROID__) # define KHRONOS_APICALL __attribute__((visibility("default"))) #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 /* * To support platform where unsigned long cannot be used interchangeably with * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. * Ideally, we could just use (u)intptr_t everywhere, but this could result in * ABI breakage if khronos_uintptr_t is changed from unsigned long to * unsigned long long or similar (this results in different C++ name mangling). * To avoid changes for existing platforms, we restrict usage of intptr_t to * platforms where the size of a pointer is larger than the size of long. */ #if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) #if __SIZEOF_POINTER__ > __SIZEOF_LONG__ #define KHRONOS_USE_INTPTR_T #endif #endif #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 KHRONOS_USE_INTPTR_T typedef intptr_t khronos_intptr_t; typedef uintptr_t khronos_uintptr_t; #elif defined(_WIN64) typedef signed long long int khronos_intptr_t; typedef unsigned long long int khronos_uintptr_t; #else typedef signed long int khronos_intptr_t; typedef unsigned long int khronos_uintptr_t; #endif #if defined(_WIN64) typedef signed long long int khronos_ssize_t; typedef unsigned long long int khronos_usize_t; #else 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_8_40/src/client/refresh/gl3/glad/include/glad/000077500000000000000000000000001465112212000235425ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/gl3/glad/include/glad/glad.h000066400000000000000000003016331465112212000246300ustar00rootroot00000000000000/* OpenGL loader generated by glad 0.1.36 on Sun May 14 07:53:38 2023. 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 Reproducible: 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: https://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__) #define APIENTRY __stdcall #endif #ifndef APIENTRY #define APIENTRY #endif #ifndef APIENTRYP #define APIENTRYP APIENTRY * #endif #ifndef GLAPIENTRY #define GLAPIENTRY 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 typedef unsigned int GLenum; typedef unsigned char GLboolean; typedef unsigned int GLbitfield; typedef void GLvoid; typedef khronos_int8_t GLbyte; typedef khronos_uint8_t GLubyte; typedef khronos_int16_t GLshort; typedef khronos_uint16_t GLushort; typedef int GLint; typedef unsigned int GLuint; typedef khronos_int32_t GLclampx; typedef int GLsizei; typedef khronos_float_t GLfloat; typedef khronos_float_t GLclampf; typedef double GLdouble; typedef double GLclampd; typedef void *GLeglClientBufferEXT; typedef void *GLeglImageOES; typedef char GLchar; typedef char GLcharARB; #ifdef __APPLE__ typedef void *GLhandleARB; #else typedef unsigned int GLhandleARB; #endif typedef khronos_uint16_t GLhalf; typedef khronos_uint16_t GLhalfARB; typedef khronos_int32_t GLfixed; typedef khronos_intptr_t GLintptr; typedef khronos_intptr_t GLintptrARB; typedef khronos_ssize_t GLsizeiptr; typedef khronos_ssize_t GLsizeiptrARB; typedef khronos_int64_t GLint64; typedef khronos_int64_t GLint64EXT; typedef khronos_uint64_t GLuint64; typedef khronos_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; typedef void (APIENTRY *GLVULKANPROCNV)(void); #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_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_TEXTURE_WIDTH 0x1000 #define GL_TEXTURE_HEIGHT 0x1001 #define GL_TEXTURE_BORDER_COLOR 0x1004 #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_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_REPEAT 0x2901 #define GL_COLOR_LOGIC_OP 0x0BF2 #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_INTERNAL_FORMAT 0x1003 #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_DOUBLE 0x140A #define GL_PROXY_TEXTURE_1D 0x8063 #define GL_PROXY_TEXTURE_2D 0x8064 #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_BLEND_COLOR 0x8005 #define GL_BLEND_EQUATION 0x8009 #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_FUNC_ADD 0x8006 #define GL_FUNC_REVERSE_SUBTRACT 0x800B #define GL_FUNC_SUBTRACT 0x800A #define GL_MIN 0x8007 #define GL_MAX 0x8008 #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_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)(void); GLAPI PFNGLFINISHPROC glad_glFinish; #define glFinish glad_glFinish typedef void (APIENTRYP PFNGLFLUSHPROC)(void); 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)(void); 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 n, GLdouble f); 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)(void); 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)(void); 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)(void); 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 count, 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_8_40/src/client/refresh/gl3/glad/src/000077500000000000000000000000001465112212000217775ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/gl3/glad/src/glad.c000066400000000000000000001422171465112212000230610ustar00rootroot00000000000000/* OpenGL loader generated by glad 0.1.36 on Sun May 14 07:53:38 2023. 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 Reproducible: 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: https://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 = { 0, 0 }; #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 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 { unsigned int index; num_exts_i = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts_i); if (num_exts_i > 0) { exts_i = (char **)malloc((size_t)num_exts_i * (sizeof *exts_i)); } if (exts_i == NULL) { return 0; } for(index = 0; index < (unsigned)num_exts_i; index++) { const char *gl_str_tmp = (const char*)glGetStringi(GL_EXTENSIONS, index); size_t len = strlen(gl_str_tmp); char *local_str = (char*)malloc((len+1) * sizeof(char)); if(local_str != NULL) { memcpy(local_str, gl_str_tmp, (len+1) * sizeof(char)); } exts_i[index] = local_str; } } #endif return 1; } static void free_exts(void) { if (exts_i != NULL) { int index; for(index = 0; index < num_exts_i; index++) { free((char *)exts_i[index]); } free((void *)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; if(exts_i == NULL) return 0; for(index = 0; index < num_exts_i; index++) { const char *e = exts_i[index]; if(exts_i[index] != NULL && strcmp(e, ext) == 0) { return 1; } } } #endif return 0; } int GLAD_GL_VERSION_1_0 = 0; int GLAD_GL_VERSION_1_1 = 0; int GLAD_GL_VERSION_1_2 = 0; int GLAD_GL_VERSION_1_3 = 0; int GLAD_GL_VERSION_1_4 = 0; int GLAD_GL_VERSION_1_5 = 0; int GLAD_GL_VERSION_2_0 = 0; int GLAD_GL_VERSION_2_1 = 0; int GLAD_GL_VERSION_3_0 = 0; int GLAD_GL_VERSION_3_1 = 0; int GLAD_GL_VERSION_3_2 = 0; PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL; PFNGLATTACHSHADERPROC glad_glAttachShader = NULL; PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender = NULL; PFNGLBEGINQUERYPROC glad_glBeginQuery = NULL; PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback = NULL; PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL; PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL; PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL; PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL; PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation = NULL; PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL; PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL; PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL; PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL; PFNGLBLENDCOLORPROC glad_glBlendColor = NULL; PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL; PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL; PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL; PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL; PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL; PFNGLBUFFERDATAPROC glad_glBufferData = NULL; PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL; PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL; PFNGLCLAMPCOLORPROC glad_glClampColor = NULL; PFNGLCLEARPROC glad_glClear = NULL; PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi = NULL; PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv = NULL; PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv = NULL; PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL; PFNGLCLEARCOLORPROC glad_glClearColor = NULL; PFNGLCLEARDEPTHPROC glad_glClearDepth = NULL; PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL; PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL; PFNGLCOLORMASKPROC glad_glColorMask = NULL; PFNGLCOLORMASKIPROC glad_glColorMaski = NULL; PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL; PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D = NULL; PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL; PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL; PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL; PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL; PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL; PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL; PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL; PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL; PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL; PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL; PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL; PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL; PFNGLCREATESHADERPROC glad_glCreateShader = NULL; PFNGLCULLFACEPROC glad_glCullFace = NULL; PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL; PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL; PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL; PFNGLDELETEQUERIESPROC glad_glDeleteQueries = NULL; PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL; PFNGLDELETESHADERPROC glad_glDeleteShader = NULL; PFNGLDELETESYNCPROC glad_glDeleteSync = NULL; PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL; PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays = NULL; PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL; PFNGLDEPTHMASKPROC glad_glDepthMask = NULL; PFNGLDEPTHRANGEPROC glad_glDepthRange = NULL; PFNGLDETACHSHADERPROC glad_glDetachShader = NULL; PFNGLDISABLEPROC glad_glDisable = NULL; PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL; PFNGLDISABLEIPROC glad_glDisablei = NULL; PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL; PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL; PFNGLDRAWBUFFERPROC glad_glDrawBuffer = NULL; PFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL; PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL; PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex = NULL; PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced = NULL; PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex = NULL; PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL; PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex = NULL; PFNGLENABLEPROC glad_glEnable = NULL; PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL; PFNGLENABLEIPROC glad_glEnablei = NULL; PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL; PFNGLENDQUERYPROC glad_glEndQuery = NULL; PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback = NULL; PFNGLFENCESYNCPROC glad_glFenceSync = NULL; PFNGLFINISHPROC glad_glFinish = NULL; PFNGLFLUSHPROC glad_glFlush = NULL; PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL; PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL; PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture = NULL; PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D = NULL; PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL; PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D = NULL; PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer = NULL; PFNGLFRONTFACEPROC glad_glFrontFace = NULL; PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL; PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL; PFNGLGENQUERIESPROC glad_glGenQueries = NULL; PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL; PFNGLGENTEXTURESPROC glad_glGenTextures = NULL; PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL; PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL; PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL; PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL; PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName = NULL; PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv = NULL; PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName = NULL; PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv = NULL; PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL; PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL; PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v = NULL; PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL; PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v = NULL; PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL; PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL; PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL; PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL; PFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL; PFNGLGETERRORPROC glad_glGetError = NULL; PFNGLGETFLOATVPROC glad_glGetFloatv = NULL; PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation = NULL; PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL; PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v = NULL; PFNGLGETINTEGER64VPROC glad_glGetInteger64v = NULL; PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v = NULL; PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL; PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv = NULL; PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL; PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL; PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL; PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv = NULL; PFNGLGETQUERYIVPROC glad_glGetQueryiv = NULL; PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL; PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL; PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL; PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL; PFNGLGETSTRINGPROC glad_glGetString = NULL; PFNGLGETSTRINGIPROC glad_glGetStringi = NULL; PFNGLGETSYNCIVPROC glad_glGetSynciv = NULL; PFNGLGETTEXIMAGEPROC glad_glGetTexImage = NULL; PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv = NULL; PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv = NULL; PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv = NULL; PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL; PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL; PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL; PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL; PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL; PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL; PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL; PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL; PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL; PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL; PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL; PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL; PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL; PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL; PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL; PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL; PFNGLHINTPROC glad_glHint = NULL; PFNGLISBUFFERPROC glad_glIsBuffer = NULL; PFNGLISENABLEDPROC glad_glIsEnabled = NULL; PFNGLISENABLEDIPROC glad_glIsEnabledi = NULL; PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL; PFNGLISPROGRAMPROC glad_glIsProgram = NULL; PFNGLISQUERYPROC glad_glIsQuery = NULL; PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL; PFNGLISSHADERPROC glad_glIsShader = NULL; PFNGLISSYNCPROC glad_glIsSync = NULL; PFNGLISTEXTUREPROC glad_glIsTexture = NULL; PFNGLISVERTEXARRAYPROC glad_glIsVertexArray = NULL; PFNGLLINEWIDTHPROC glad_glLineWidth = NULL; PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL; PFNGLLOGICOPPROC glad_glLogicOp = NULL; PFNGLMAPBUFFERPROC glad_glMapBuffer = NULL; PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL; PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL; PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL; PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex = NULL; PFNGLPIXELSTOREFPROC glad_glPixelStoref = NULL; PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL; PFNGLPOINTPARAMETERFPROC glad_glPointParameterf = NULL; PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv = NULL; PFNGLPOINTPARAMETERIPROC glad_glPointParameteri = NULL; PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv = NULL; PFNGLPOINTSIZEPROC glad_glPointSize = NULL; PFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL; PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL; PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL; PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex = NULL; PFNGLREADBUFFERPROC glad_glReadBuffer = NULL; PFNGLREADPIXELSPROC glad_glReadPixels = NULL; PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL; PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL; PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL; PFNGLSAMPLEMASKIPROC glad_glSampleMaski = NULL; PFNGLSCISSORPROC glad_glScissor = NULL; PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL; PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL; PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL; PFNGLSTENCILMASKPROC glad_glStencilMask = NULL; PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL; PFNGLSTENCILOPPROC glad_glStencilOp = NULL; PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL; PFNGLTEXBUFFERPROC glad_glTexBuffer = NULL; PFNGLTEXIMAGE1DPROC glad_glTexImage1D = NULL; PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL; PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample = NULL; PFNGLTEXIMAGE3DPROC glad_glTexImage3D = NULL; PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample = NULL; PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv = NULL; PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv = NULL; PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL; PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL; PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL; PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL; PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL; PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL; PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL; PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL; PFNGLUNIFORM1FPROC glad_glUniform1f = NULL; PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL; PFNGLUNIFORM1IPROC glad_glUniform1i = NULL; PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL; PFNGLUNIFORM1UIPROC glad_glUniform1ui = NULL; PFNGLUNIFORM1UIVPROC glad_glUniform1uiv = NULL; PFNGLUNIFORM2FPROC glad_glUniform2f = NULL; PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL; PFNGLUNIFORM2IPROC glad_glUniform2i = NULL; PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL; PFNGLUNIFORM2UIPROC glad_glUniform2ui = NULL; PFNGLUNIFORM2UIVPROC glad_glUniform2uiv = NULL; PFNGLUNIFORM3FPROC glad_glUniform3f = NULL; PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL; PFNGLUNIFORM3IPROC glad_glUniform3i = NULL; PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL; PFNGLUNIFORM3UIPROC glad_glUniform3ui = NULL; PFNGLUNIFORM3UIVPROC glad_glUniform3uiv = NULL; PFNGLUNIFORM4FPROC glad_glUniform4f = NULL; PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL; PFNGLUNIFORM4IPROC glad_glUniform4i = NULL; PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL; PFNGLUNIFORM4UIPROC glad_glUniform4ui = NULL; PFNGLUNIFORM4UIVPROC glad_glUniform4uiv = NULL; PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding = NULL; PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL; PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv = NULL; PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv = NULL; PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL; PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv = NULL; PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv = NULL; PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL; PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv = NULL; PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL; PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL; PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL; PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL; PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL; PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL; PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL; PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL; PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s = NULL; PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv = NULL; PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d = NULL; PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv = NULL; PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL; PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL; PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s = NULL; PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv = NULL; PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d = NULL; PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv = NULL; PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL; PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL; PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s = NULL; PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv = NULL; PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv = NULL; PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv = NULL; PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv = NULL; PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub = NULL; PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv = NULL; PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv = NULL; PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv = NULL; PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv = NULL; PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d = NULL; PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv = NULL; PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL; PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL; PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv = NULL; PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s = NULL; PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv = NULL; PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv = NULL; PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv = NULL; PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv = NULL; PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i = NULL; PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv = NULL; PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui = NULL; PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv = NULL; PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i = NULL; PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv = NULL; PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui = NULL; PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv = NULL; PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i = NULL; PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv = NULL; PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui = NULL; PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv = NULL; PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv = NULL; PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i = NULL; PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv = NULL; PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv = NULL; PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv = NULL; PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui = NULL; PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv = NULL; PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv = NULL; PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer = NULL; PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL; PFNGLVIEWPORTPROC glad_glViewport = NULL; PFNGLWAITSYNCPROC glad_glWaitSync = NULL; int GLAD_GL_ARB_debug_output = 0; int GLAD_GL_EXT_texture_filter_anisotropic = 0; PFNGLDEBUGMESSAGECONTROLARBPROC glad_glDebugMessageControlARB = NULL; PFNGLDEBUGMESSAGEINSERTARBPROC glad_glDebugMessageInsertARB = NULL; PFNGLDEBUGMESSAGECALLBACKARBPROC glad_glDebugMessageCallbackARB = NULL; PFNGLGETDEBUGMESSAGELOGARBPROC glad_glGetDebugMessageLogARB = NULL; 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_8_40/src/client/refresh/gl3/header/000077500000000000000000000000001465112212000215315ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/gl3/header/DG_dynarr.h000066400000000000000000001052371465112212000235630ustar00rootroot00000000000000/* * 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_8_40/src/client/refresh/gl3/header/HandmadeMath.h000066400000000000000000001555021465112212000242250ustar00rootroot00000000000000/* HandmadeMath.h v1.5.0 This is a single header file with a bunch of useful functions for game and graphics 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. ============================================================================= 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_NO_SSE #include "HandmadeMath.h" ============================================================================= To use HandmadeMath without the CRT, you MUST #define HMM_SINF MySinF #define HMM_COSF MyCosF #define HMM_TANF MyTanF #define HMM_SQRTF MySqrtF #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_SQRTF MySqrtF #define HMM_EXPF MyExpF #define HMM_LOGF MyLogF #define HMM_ACOSF MyACosF #define HMM_ATANF MyATanF #define HMM_ATAN2F MyATan2F #define HANDMADE_MATH_IMPLEMENTATION #include "HandmadeMath.h" If you do not define all 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 1.1.4 (*) Fixed SSE being included on platforms that don't support it (*) Fixed divide-by-zero errors when normalizing zero vectors. 1.1.5 (*) Add Width and Height to HMM_Vec2 (*) Made it so you can supply your own SqrtF 1.2.0 (*) Added equality functions for HMM_Vec2, HMM_Vec3, and HMM_Vec4. (*) Added HMM_EqualsVec2, HMM_EqualsVec3, and HMM_EqualsVec4 (*) Added C++ overloaded HMM_Equals for all three (*) Added C++ == and != operators for all three (*) SSE'd HMM_MultiplyMat4 (this is _WAY_ faster) (*) SSE'd HMM_Transpose 1.3.0 (*) Remove need to #define HANDMADE_MATH_CPP_MODE 1.4.0 (*) Fixed bug when using HandmadeMath in C mode (*) SSEd all vec4 operations (*) Removed all zero-ing 1.5.0 (*) Changed internal structure for better performance and inlining. (*) As a result, HANDMADE_MATH_NO_INLINE has been removed and no longer has any effect. 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@strangedev.net && @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) Daniel Gibson (@DanielGibson) */ /* 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 */ #include // This is for types #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 #define HMM_INLINE static inline #define HMM_EXTERN extern #if !defined(HMM_SINF) || !defined(HMM_COSF) || !defined(HMM_TANF) || \ !defined(HMM_SQRTF) || !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_SQRTF #define HMM_SQRTF sqrtf #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; }; struct { float Width, Height; }; 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]; #ifdef HANDMADE_MATH__USE_SSE __m128 InternalElementsSSE; #endif } hmm_vec4; typedef union hmm_mat4 { float Elements[4][4]; #ifdef HANDMADE_MATH__USE_SSE __m128 Rows[4]; #endif } hmm_mat4; typedef union hmm_quaternion { struct { union { hmm_vec3 XYZ; struct { float X, Y, Z; }; }; float W; }; float Elements[4]; } hmm_quaternion; typedef int32_t hmm_bool; typedef hmm_vec2 hmm_v2; typedef hmm_vec3 hmm_v3; typedef hmm_vec4 hmm_v4; typedef hmm_mat4 hmm_m4; /* * Floating-point math functions */ HMM_INLINE float HMM_SinF(float Radians) { float Result = HMM_SINF(Radians); return (Result); } HMM_INLINE float HMM_CosF(float Radians) { float Result = HMM_COSF(Radians); return (Result); } HMM_INLINE float HMM_TanF(float Radians) { float Result = HMM_TANF(Radians); return (Result); } HMM_INLINE float HMM_ACosF(float Radians) { float Result = HMM_ACOSF(Radians); return (Result); } HMM_INLINE float HMM_ATanF(float Radians) { float Result = HMM_ATANF(Radians); return (Result); } HMM_INLINE float HMM_ATan2F(float Left, float Right) { float Result = HMM_ATAN2F(Left, Right); return (Result); } HMM_INLINE float HMM_ExpF(float Float) { float Result = HMM_EXPF(Float); return (Result); } HMM_INLINE float HMM_LogF(float Float) { float Result = HMM_LOGF(Float); return (Result); } HMM_INLINE float HMM_SquareRootF(float Float) { float Result; #ifdef HANDMADE_MATH__USE_SSE __m128 In = _mm_set_ss(Float); __m128 Out = _mm_sqrt_ss(In); Result = _mm_cvtss_f32(Out); #else Result = HMM_SQRTF(Float); #endif return(Result); } HMM_INLINE float HMM_RSquareRootF(float Float) { float Result; #ifdef HANDMADE_MATH__USE_SSE __m128 In = _mm_set_ss(Float); __m128 Out = _mm_rsqrt_ss(In); Result = _mm_cvtss_f32(Out); #else Result = 1.0f/HMM_SquareRootF(Float); #endif return(Result); } HMM_EXTERN float HMM_Power(float Base, int Exponent); HMM_INLINE float HMM_PowerF(float Base, float Exponent) { float Result = HMM_EXPF(Exponent * HMM_LOGF(Base)); return (Result); } /* * Utility functions */ HMM_INLINE float HMM_ToRadians(float Degrees) { float Result = Degrees * (HMM_PI32 / 180.0f); return (Result); } HMM_INLINE float HMM_Lerp(float A, float Time, float B) { float Result = (1.0f - Time) * A + Time * B; return (Result); } HMM_INLINE 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); } /* * Vector initialization */ HMM_INLINE hmm_vec2 HMM_Vec2(float X, float Y) { hmm_vec2 Result; Result.X = X; Result.Y = Y; return (Result); } HMM_INLINE hmm_vec2 HMM_Vec2i(int X, int Y) { hmm_vec2 Result; Result.X = (float)X; Result.Y = (float)Y; return (Result); } HMM_INLINE hmm_vec3 HMM_Vec3(float X, float Y, float Z) { hmm_vec3 Result; Result.X = X; Result.Y = Y; Result.Z = Z; return (Result); } HMM_INLINE hmm_vec3 HMM_Vec3i(int X, int Y, int Z) { hmm_vec3 Result; Result.X = (float)X; Result.Y = (float)Y; Result.Z = (float)Z; return (Result); } HMM_INLINE hmm_vec4 HMM_Vec4(float X, float Y, float Z, float W) { hmm_vec4 Result; #ifdef HANDMADE_MATH__USE_SSE Result.InternalElementsSSE = _mm_setr_ps(X, Y, Z, W); #else Result.X = X; Result.Y = Y; Result.Z = Z; Result.W = W; #endif return (Result); } HMM_INLINE hmm_vec4 HMM_Vec4i(int X, int Y, int Z, int W) { hmm_vec4 Result; #ifdef HANDMADE_MATH__USE_SSE Result.InternalElementsSSE = _mm_setr_ps((float)X, (float)Y, (float)Z, (float)W); #else Result.X = (float)X; Result.Y = (float)Y; Result.Z = (float)Z; Result.W = (float)W; #endif return (Result); } HMM_INLINE hmm_vec4 HMM_Vec4v(hmm_vec3 Vector, float W) { hmm_vec4 Result; #ifdef HANDMADE_MATH__USE_SSE Result.InternalElementsSSE = _mm_setr_ps(Vector.X, Vector.Y, Vector.Z, W); #else Result.XYZ = Vector; Result.W = W; #endif return (Result); } /* * Binary vector operations */ HMM_INLINE hmm_vec2 HMM_AddVec2(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result; Result.X = Left.X + Right.X; Result.Y = Left.Y + Right.Y; return (Result); } HMM_INLINE hmm_vec3 HMM_AddVec3(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result; Result.X = Left.X + Right.X; Result.Y = Left.Y + Right.Y; Result.Z = Left.Z + Right.Z; return (Result); } HMM_INLINE hmm_vec4 HMM_AddVec4(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result; #ifdef HANDMADE_MATH__USE_SSE Result.InternalElementsSSE = _mm_add_ps(Left.InternalElementsSSE, Right.InternalElementsSSE); #else Result.X = Left.X + Right.X; Result.Y = Left.Y + Right.Y; Result.Z = Left.Z + Right.Z; Result.W = Left.W + Right.W; #endif return (Result); } HMM_INLINE hmm_vec2 HMM_SubtractVec2(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result; Result.X = Left.X - Right.X; Result.Y = Left.Y - Right.Y; return (Result); } HMM_INLINE hmm_vec3 HMM_SubtractVec3(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result; Result.X = Left.X - Right.X; Result.Y = Left.Y - Right.Y; Result.Z = Left.Z - Right.Z; return (Result); } HMM_INLINE hmm_vec4 HMM_SubtractVec4(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result; #ifdef HANDMADE_MATH__USE_SSE Result.InternalElementsSSE = _mm_sub_ps(Left.InternalElementsSSE, Right.InternalElementsSSE); #else Result.X = Left.X - Right.X; Result.Y = Left.Y - Right.Y; Result.Z = Left.Z - Right.Z; Result.W = Left.W - Right.W; #endif return (Result); } HMM_INLINE hmm_vec2 HMM_MultiplyVec2(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result; Result.X = Left.X * Right.X; Result.Y = Left.Y * Right.Y; return (Result); } HMM_INLINE hmm_vec2 HMM_MultiplyVec2f(hmm_vec2 Left, float Right) { hmm_vec2 Result; Result.X = Left.X * Right; Result.Y = Left.Y * Right; return (Result); } HMM_INLINE hmm_vec3 HMM_MultiplyVec3(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result; Result.X = Left.X * Right.X; Result.Y = Left.Y * Right.Y; Result.Z = Left.Z * Right.Z; return (Result); } HMM_INLINE hmm_vec3 HMM_MultiplyVec3f(hmm_vec3 Left, float Right) { hmm_vec3 Result; Result.X = Left.X * Right; Result.Y = Left.Y * Right; Result.Z = Left.Z * Right; return (Result); } HMM_INLINE hmm_vec4 HMM_MultiplyVec4(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result; #ifdef HANDMADE_MATH__USE_SSE Result.InternalElementsSSE = _mm_mul_ps(Left.InternalElementsSSE, Right.InternalElementsSSE); #else Result.X = Left.X * Right.X; Result.Y = Left.Y * Right.Y; Result.Z = Left.Z * Right.Z; Result.W = Left.W * Right.W; #endif return (Result); } HMM_INLINE hmm_vec4 HMM_MultiplyVec4f(hmm_vec4 Left, float Right) { hmm_vec4 Result; #ifdef HANDMADE_MATH__USE_SSE __m128 Scalar = _mm_set1_ps(Right); Result.InternalElementsSSE = _mm_mul_ps(Left.InternalElementsSSE, Scalar); #else Result.X = Left.X * Right; Result.Y = Left.Y * Right; Result.Z = Left.Z * Right; Result.W = Left.W * Right; #endif return (Result); } HMM_INLINE hmm_vec2 HMM_DivideVec2(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result; Result.X = Left.X / Right.X; Result.Y = Left.Y / Right.Y; return (Result); } HMM_INLINE hmm_vec2 HMM_DivideVec2f(hmm_vec2 Left, float Right) { hmm_vec2 Result; Result.X = Left.X / Right; Result.Y = Left.Y / Right; return (Result); } HMM_INLINE hmm_vec3 HMM_DivideVec3(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result; Result.X = Left.X / Right.X; Result.Y = Left.Y / Right.Y; Result.Z = Left.Z / Right.Z; return (Result); } HMM_INLINE hmm_vec3 HMM_DivideVec3f(hmm_vec3 Left, float Right) { hmm_vec3 Result; Result.X = Left.X / Right; Result.Y = Left.Y / Right; Result.Z = Left.Z / Right; return (Result); } HMM_INLINE hmm_vec4 HMM_DivideVec4(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result; #ifdef HANDMADE_MATH__USE_SSE Result.InternalElementsSSE = _mm_div_ps(Left.InternalElementsSSE, Right.InternalElementsSSE); #else Result.X = Left.X / Right.X; Result.Y = Left.Y / Right.Y; Result.Z = Left.Z / Right.Z; Result.W = Left.W / Right.W; #endif return (Result); } HMM_INLINE hmm_vec4 HMM_DivideVec4f(hmm_vec4 Left, float Right) { hmm_vec4 Result; #ifdef HANDMADE_MATH__USE_SSE __m128 Scalar = _mm_set1_ps(Right); Result.InternalElementsSSE = _mm_div_ps(Left.InternalElementsSSE, Scalar); #else Result.X = Left.X / Right; Result.Y = Left.Y / Right; Result.Z = Left.Z / Right; Result.W = Left.W / Right; #endif return (Result); } HMM_INLINE hmm_bool HMM_EqualsVec2(hmm_vec2 Left, hmm_vec2 Right) { hmm_bool Result = (Left.X == Right.X && Left.Y == Right.Y); return (Result); } HMM_INLINE hmm_bool HMM_EqualsVec3(hmm_vec3 Left, hmm_vec3 Right) { hmm_bool Result = (Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z); return (Result); } HMM_INLINE hmm_bool HMM_EqualsVec4(hmm_vec4 Left, hmm_vec4 Right) { hmm_bool Result = (Left.X == Right.X && Left.Y == Right.Y && Left.Z == Right.Z && Left.W == Right.W); return (Result); } HMM_INLINE float HMM_DotVec2(hmm_vec2 VecOne, hmm_vec2 VecTwo) { float Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y); return (Result); } HMM_INLINE float HMM_DotVec3(hmm_vec3 VecOne, hmm_vec3 VecTwo) { float Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y) + (VecOne.Z * VecTwo.Z); return (Result); } HMM_INLINE float HMM_DotVec4(hmm_vec4 VecOne, hmm_vec4 VecTwo) { float Result; // NOTE(zak): IN the future if we wanna check what version SSE is support // we can use _mm_dp_ps (4.3) but for now we will use the old way. // Or a r = _mm_mul_ps(v1, v2), r = _mm_hadd_ps(r, r), r = _mm_hadd_ps(r, r) for SSE3 #ifdef HANDMADE_MATH__USE_SSE __m128 SSEResultOne = _mm_mul_ps(VecOne.InternalElementsSSE, VecTwo.InternalElementsSSE); __m128 SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(2, 3, 0, 1)); SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo); SSEResultTwo = _mm_shuffle_ps(SSEResultOne, SSEResultOne, _MM_SHUFFLE(0, 1, 2, 3)); SSEResultOne = _mm_add_ps(SSEResultOne, SSEResultTwo); _mm_store_ss(&Result, SSEResultOne); #else Result = (VecOne.X * VecTwo.X) + (VecOne.Y * VecTwo.Y) + (VecOne.Z * VecTwo.Z) + (VecOne.W * VecTwo.W); #endif return (Result); } HMM_INLINE hmm_vec3 HMM_Cross(hmm_vec3 VecOne, hmm_vec3 VecTwo) { hmm_vec3 Result; 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); } /* * Unary vector operations */ HMM_INLINE float HMM_LengthSquaredVec2(hmm_vec2 A) { float Result = HMM_DotVec2(A, A); return(Result); } HMM_INLINE float HMM_LengthSquaredVec3(hmm_vec3 A) { float Result = HMM_DotVec3(A, A); return (Result); } HMM_INLINE float HMM_LengthSquaredVec4(hmm_vec4 A) { float Result = HMM_DotVec4(A, A); return (Result); } HMM_INLINE float HMM_LengthVec2(hmm_vec2 A) { float Result = HMM_SquareRootF(HMM_LengthSquaredVec2(A)); return (Result); } HMM_INLINE float HMM_LengthVec3(hmm_vec3 A) { float Result = HMM_SquareRootF(HMM_LengthSquaredVec3(A)); return (Result); } HMM_INLINE float HMM_LengthVec4(hmm_vec4 A) { float Result = HMM_SquareRootF(HMM_LengthSquaredVec4(A)); return(Result); } HMM_INLINE hmm_vec2 HMM_NormalizeVec2(hmm_vec2 A) { hmm_vec2 Result = {0}; float VectorLength = HMM_LengthVec2(A); /* NOTE(kiljacken): We need a zero check to not divide-by-zero */ if (VectorLength != 0.0f) { Result.X = A.X * (1.0f / VectorLength); Result.Y = A.Y * (1.0f / VectorLength); } return (Result); } HMM_INLINE hmm_vec3 HMM_NormalizeVec3(hmm_vec3 A) { hmm_vec3 Result = {0}; float VectorLength = HMM_LengthVec3(A); /* NOTE(kiljacken): We need a zero check to not divide-by-zero */ if (VectorLength != 0.0f) { Result.X = A.X * (1.0f / VectorLength); Result.Y = A.Y * (1.0f / VectorLength); Result.Z = A.Z * (1.0f / VectorLength); } return (Result); } HMM_INLINE hmm_vec4 HMM_NormalizeVec4(hmm_vec4 A) { hmm_vec4 Result = {0}; float VectorLength = HMM_LengthVec4(A); /* NOTE(kiljacken): We need a zero check to not divide-by-zero */ if (VectorLength != 0.0f) { float Multiplier = 1.0f / VectorLength; #ifdef HANDMADE_MATH__USE_SSE __m128 SSEMultiplier = _mm_set1_ps(Multiplier); Result.InternalElementsSSE = _mm_mul_ps(A.InternalElementsSSE, SSEMultiplier); #else Result.X = A.X * Multiplier; Result.Y = A.Y * Multiplier; Result.Z = A.Z * Multiplier; Result.W = A.W * Multiplier; #endif } return (Result); } /* * SSE stuff */ #ifdef HANDMADE_MATH__USE_SSE HMM_INLINE __m128 HMM_LinearCombineSSE(__m128 Left, hmm_mat4 Right) { __m128 Result; Result = _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0x00), Right.Rows[0]); Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0x55), Right.Rows[1])); Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0xaa), Right.Rows[2])); Result = _mm_add_ps(Result, _mm_mul_ps(_mm_shuffle_ps(Left, Left, 0xff), Right.Rows[3])); return (Result); } #endif /* * Matrix functions */ HMM_INLINE hmm_mat4 HMM_Mat4(void) { hmm_mat4 Result = {0}; return (Result); } HMM_INLINE 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); } #ifdef HANDMADE_MATH__USE_SSE HMM_INLINE hmm_mat4 HMM_Transpose(hmm_mat4 Matrix) { hmm_mat4 Result = Matrix; _MM_TRANSPOSE4_PS(Result.Rows[0], Result.Rows[1], Result.Rows[2], Result.Rows[3]); return (Result); } #else HMM_EXTERN hmm_mat4 HMM_Transpose(hmm_mat4 Matrix); #endif #ifdef HANDMADE_MATH__USE_SSE HMM_INLINE hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result; Result.Rows[0] = _mm_add_ps(Left.Rows[0], Right.Rows[0]); Result.Rows[1] = _mm_add_ps(Left.Rows[1], Right.Rows[1]); Result.Rows[2] = _mm_add_ps(Left.Rows[2], Right.Rows[2]); Result.Rows[3] = _mm_add_ps(Left.Rows[3], Right.Rows[3]); return (Result); } #else HMM_EXTERN hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right); #endif #ifdef HANDMADE_MATH__USE_SSE HMM_INLINE hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result; Result.Rows[0] = _mm_sub_ps(Left.Rows[0], Right.Rows[0]); Result.Rows[1] = _mm_sub_ps(Left.Rows[1], Right.Rows[1]); Result.Rows[2] = _mm_sub_ps(Left.Rows[2], Right.Rows[2]); Result.Rows[3] = _mm_sub_ps(Left.Rows[3], Right.Rows[3]); return (Result); } #else HMM_EXTERN hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right); #endif HMM_EXTERN hmm_mat4 HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right); #ifdef HANDMADE_MATH__USE_SSE HMM_INLINE hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar) { hmm_mat4 Result; __m128 SSEScalar = _mm_set1_ps(Scalar); Result.Rows[0] = _mm_mul_ps(Matrix.Rows[0], SSEScalar); Result.Rows[1] = _mm_mul_ps(Matrix.Rows[1], SSEScalar); Result.Rows[2] = _mm_mul_ps(Matrix.Rows[2], SSEScalar); Result.Rows[3] = _mm_mul_ps(Matrix.Rows[3], SSEScalar); return (Result); } #else HMM_EXTERN hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar); #endif HMM_EXTERN hmm_vec4 HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector); #ifdef HANDMADE_MATH__USE_SSE HMM_INLINE hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar) { hmm_mat4 Result; __m128 SSEScalar = _mm_set1_ps(Scalar); Result.Rows[0] = _mm_div_ps(Matrix.Rows[0], SSEScalar); Result.Rows[1] = _mm_div_ps(Matrix.Rows[1], SSEScalar); Result.Rows[2] = _mm_div_ps(Matrix.Rows[2], SSEScalar); Result.Rows[3] = _mm_div_ps(Matrix.Rows[3], SSEScalar); return (Result); } #else HMM_EXTERN hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar); #endif /* * Common graphics transformations */ HMM_INLINE hmm_mat4 HMM_Orthographic(float Left, float Right, float Bottom, float Top, float Near, float Far) { hmm_mat4 Result = HMM_Mat4(); 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][3] = 1.0f; 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); } HMM_INLINE hmm_mat4 HMM_Perspective(float FOV, float AspectRatio, float Near, float Far) { hmm_mat4 Result = HMM_Mat4(); 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); } HMM_INLINE 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); } HMM_EXTERN hmm_mat4 HMM_Rotate(float Angle, hmm_vec3 Axis); HMM_INLINE 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); } HMM_EXTERN hmm_mat4 HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up); /* * Quaternion operations */ HMM_INLINE hmm_quaternion HMM_Quaternion(float X, float Y, float Z, float W) { hmm_quaternion Result; Result.X = X; Result.Y = Y; Result.Z = Z; Result.W = W; return (Result); } HMM_INLINE hmm_quaternion HMM_QuaternionV4(hmm_vec4 Vector) { hmm_quaternion Result; Result.X = Vector.X; Result.Y = Vector.Y; Result.Z = Vector.Z; Result.W = Vector.W; return (Result); } HMM_INLINE hmm_quaternion HMM_AddQuaternion(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result; 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); } HMM_INLINE hmm_quaternion HMM_SubtractQuaternion(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result; 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); } HMM_INLINE hmm_quaternion HMM_MultiplyQuaternion(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result; 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); } HMM_INLINE hmm_quaternion HMM_MultiplyQuaternionF(hmm_quaternion Left, float Multiplicative) { hmm_quaternion Result; Result.X = Left.X * Multiplicative; Result.Y = Left.Y * Multiplicative; Result.Z = Left.Z * Multiplicative; Result.W = Left.W * Multiplicative; return (Result); } HMM_INLINE hmm_quaternion HMM_DivideQuaternionF(hmm_quaternion Left, float Dividend) { hmm_quaternion Result; Result.X = Left.X / Dividend; Result.Y = Left.Y / Dividend; Result.Z = Left.Z / Dividend; Result.W = Left.W / Dividend; return (Result); } HMM_EXTERN hmm_quaternion HMM_InverseQuaternion(hmm_quaternion Left); HMM_INLINE float HMM_DotQuaternion(hmm_quaternion Left, hmm_quaternion Right) { float Result = (Left.X * Right.X) + (Left.Y * Right.Y) + (Left.Z * Right.Z) + (Left.W * Right.W); return (Result); } HMM_INLINE hmm_quaternion HMM_NormalizeQuaternion(hmm_quaternion Left) { hmm_quaternion Result; float Length = HMM_SquareRootF(HMM_DotQuaternion(Left, Left)); Result = HMM_DivideQuaternionF(Left, Length); return (Result); } HMM_INLINE hmm_quaternion HMM_NLerp(hmm_quaternion Left, float Time, hmm_quaternion Right) { hmm_quaternion Result; 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); } HMM_EXTERN hmm_quaternion HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right); HMM_EXTERN hmm_mat4 HMM_QuaternionToMat4(hmm_quaternion Left); HMM_EXTERN hmm_quaternion HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotation); #ifdef __cplusplus } #endif #ifdef __cplusplus HMM_INLINE float HMM_Length(hmm_vec2 A) { float Result = HMM_LengthVec2(A); return (Result); } HMM_INLINE float HMM_Length(hmm_vec3 A) { float Result = HMM_LengthVec3(A); return (Result); } HMM_INLINE float HMM_Length(hmm_vec4 A) { float Result = HMM_LengthVec4(A); return (Result); } HMM_INLINE float HMM_LengthSquared(hmm_vec2 A) { float Result = HMM_LengthSquaredVec2(A); return (Result); } HMM_INLINE float HMM_LengthSquared(hmm_vec3 A) { float Result = HMM_LengthSquaredVec3(A); return (Result); } HMM_INLINE float HMM_LengthSquared(hmm_vec4 A) { float Result = HMM_LengthSquaredVec4(A); return (Result); } HMM_INLINE hmm_vec2 HMM_Normalize(hmm_vec2 A) { hmm_vec2 Result = HMM_NormalizeVec2(A); return (Result); } HMM_INLINE hmm_vec3 HMM_Normalize(hmm_vec3 A) { hmm_vec3 Result = HMM_NormalizeVec3(A); return (Result); } HMM_INLINE hmm_vec4 HMM_Normalize(hmm_vec4 A) { hmm_vec4 Result = HMM_NormalizeVec4(A); return (Result); } HMM_INLINE hmm_quaternion HMM_Normalize(hmm_quaternion A) { hmm_quaternion Result = HMM_NormalizeQuaternion(A); return (Result); } HMM_INLINE float HMM_Dot(hmm_vec2 VecOne, hmm_vec2 VecTwo) { float Result = HMM_DotVec2(VecOne, VecTwo); return (Result); } HMM_INLINE float HMM_Dot(hmm_vec3 VecOne, hmm_vec3 VecTwo) { float Result = HMM_DotVec3(VecOne, VecTwo); return (Result); } HMM_INLINE float HMM_Dot(hmm_vec4 VecOne, hmm_vec4 VecTwo) { float Result = HMM_DotVec4(VecOne, VecTwo); return (Result); } HMM_INLINE float HMM_Dot(hmm_quaternion QuatOne, hmm_quaternion QuatTwo) { float Result = HMM_DotQuaternion(QuatOne, QuatTwo); return (Result); } HMM_INLINE hmm_vec2 HMM_Add(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = HMM_AddVec2(Left, Right); return (Result); } HMM_INLINE hmm_vec3 HMM_Add(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = HMM_AddVec3(Left, Right); return (Result); } HMM_INLINE hmm_vec4 HMM_Add(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = HMM_AddVec4(Left, Right); return (Result); } HMM_INLINE hmm_mat4 HMM_Add(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = HMM_AddMat4(Left, Right); return (Result); } HMM_INLINE hmm_quaternion HMM_Add(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = HMM_AddQuaternion(Left, Right); return (Result); } HMM_INLINE hmm_vec2 HMM_Subtract(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = HMM_SubtractVec2(Left, Right); return (Result); } HMM_INLINE hmm_vec3 HMM_Subtract(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = HMM_SubtractVec3(Left, Right); return (Result); } HMM_INLINE hmm_vec4 HMM_Subtract(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = HMM_SubtractVec4(Left, Right); return (Result); } HMM_INLINE hmm_mat4 HMM_Subtract(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = HMM_SubtractMat4(Left, Right); return (Result); } HMM_INLINE hmm_quaternion HMM_Subtract(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = HMM_SubtractQuaternion(Left, Right); return (Result); } HMM_INLINE hmm_vec2 HMM_Multiply(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = HMM_MultiplyVec2(Left, Right); return (Result); } HMM_INLINE hmm_vec2 HMM_Multiply(hmm_vec2 Left, float Right) { hmm_vec2 Result = HMM_MultiplyVec2f(Left, Right); return (Result); } HMM_INLINE hmm_vec3 HMM_Multiply(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = HMM_MultiplyVec3(Left, Right); return (Result); } HMM_INLINE hmm_vec3 HMM_Multiply(hmm_vec3 Left, float Right) { hmm_vec3 Result = HMM_MultiplyVec3f(Left, Right); return (Result); } HMM_INLINE hmm_vec4 HMM_Multiply(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = HMM_MultiplyVec4(Left, Right); return (Result); } HMM_INLINE hmm_vec4 HMM_Multiply(hmm_vec4 Left, float Right) { hmm_vec4 Result = HMM_MultiplyVec4f(Left, Right); return (Result); } HMM_INLINE hmm_mat4 HMM_Multiply(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = HMM_MultiplyMat4(Left, Right); return (Result); } HMM_INLINE hmm_mat4 HMM_Multiply(hmm_mat4 Left, float Right) { hmm_mat4 Result = HMM_MultiplyMat4f(Left, Right); return (Result); } HMM_INLINE hmm_vec4 HMM_Multiply(hmm_mat4 Matrix, hmm_vec4 Vector) { hmm_vec4 Result = HMM_MultiplyMat4ByVec4(Matrix, Vector); return (Result); } HMM_INLINE hmm_quaternion HMM_Multiply(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = HMM_MultiplyQuaternion(Left, Right); return (Result); } HMM_INLINE hmm_quaternion HMM_Multiply(hmm_quaternion Left, float Right) { hmm_quaternion Result = HMM_MultiplyQuaternionF(Left, Right); return (Result); } HMM_INLINE hmm_vec2 HMM_Divide(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = HMM_DivideVec2(Left, Right); return (Result); } HMM_INLINE hmm_vec2 HMM_Divide(hmm_vec2 Left, float Right) { hmm_vec2 Result = HMM_DivideVec2f(Left, Right); return (Result); } HMM_INLINE hmm_vec3 HMM_Divide(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = HMM_DivideVec3(Left, Right); return (Result); } HMM_INLINE hmm_vec3 HMM_Divide(hmm_vec3 Left, float Right) { hmm_vec3 Result = HMM_DivideVec3f(Left, Right); return (Result); } HMM_INLINE hmm_vec4 HMM_Divide(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = HMM_DivideVec4(Left, Right); return (Result); } HMM_INLINE hmm_vec4 HMM_Divide(hmm_vec4 Left, float Right) { hmm_vec4 Result = HMM_DivideVec4f(Left, Right); return (Result); } HMM_INLINE hmm_mat4 HMM_Divide(hmm_mat4 Left, float Right) { hmm_mat4 Result = HMM_DivideMat4f(Left, Right); return (Result); } HMM_INLINE hmm_quaternion HMM_Divide(hmm_quaternion Left, float Right) { hmm_quaternion Result = HMM_DivideQuaternionF(Left, Right); return (Result); } HMM_INLINE hmm_bool HMM_Equals(hmm_vec2 Left, hmm_vec2 Right) { hmm_bool Result = HMM_EqualsVec2(Left, Right); return (Result); } HMM_INLINE hmm_bool HMM_Equals(hmm_vec3 Left, hmm_vec3 Right) { hmm_bool Result = HMM_EqualsVec3(Left, Right); return (Result); } HMM_INLINE hmm_bool HMM_Equals(hmm_vec4 Left, hmm_vec4 Right) { hmm_bool Result = HMM_EqualsVec4(Left, Right); return (Result); } HMM_INLINE hmm_vec2 operator+(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = HMM_AddVec2(Left, Right); return (Result); } HMM_INLINE hmm_vec3 operator+(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = HMM_AddVec3(Left, Right); return (Result); } HMM_INLINE hmm_vec4 operator+(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = HMM_AddVec4(Left, Right); return (Result); } HMM_INLINE hmm_mat4 operator+(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = HMM_AddMat4(Left, Right); return (Result); } HMM_INLINE hmm_quaternion operator+(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = HMM_AddQuaternion(Left, Right); return (Result); } HMM_INLINE hmm_vec2 operator-(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = HMM_SubtractVec2(Left, Right); return (Result); } HMM_INLINE hmm_vec3 operator-(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = HMM_SubtractVec3(Left, Right); return (Result); } HMM_INLINE hmm_vec4 operator-(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = HMM_SubtractVec4(Left, Right); return (Result); } HMM_INLINE hmm_mat4 operator-(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = HMM_SubtractMat4(Left, Right); return (Result); } HMM_INLINE hmm_quaternion operator-(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = HMM_SubtractQuaternion(Left, Right); return (Result); } HMM_INLINE hmm_vec2 operator*(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = HMM_MultiplyVec2(Left, Right); return (Result); } HMM_INLINE hmm_vec3 operator*(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = HMM_MultiplyVec3(Left, Right); return (Result); } HMM_INLINE hmm_vec4 operator*(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = HMM_MultiplyVec4(Left, Right); return (Result); } HMM_INLINE hmm_mat4 operator*(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result = HMM_MultiplyMat4(Left, Right); return (Result); } HMM_INLINE hmm_quaternion operator*(hmm_quaternion Left, hmm_quaternion Right) { hmm_quaternion Result = HMM_MultiplyQuaternion(Left, Right); return (Result); } HMM_INLINE hmm_vec2 operator*(hmm_vec2 Left, float Right) { hmm_vec2 Result = HMM_MultiplyVec2f(Left, Right); return (Result); } HMM_INLINE hmm_vec3 operator*(hmm_vec3 Left, float Right) { hmm_vec3 Result = HMM_MultiplyVec3f(Left, Right); return (Result); } HMM_INLINE hmm_vec4 operator*(hmm_vec4 Left, float Right) { hmm_vec4 Result = HMM_MultiplyVec4f(Left, Right); return (Result); } HMM_INLINE hmm_mat4 operator*(hmm_mat4 Left, float Right) { hmm_mat4 Result = HMM_MultiplyMat4f(Left, Right); return (Result); } HMM_INLINE hmm_quaternion operator*(hmm_quaternion Left, float Right) { hmm_quaternion Result = HMM_MultiplyQuaternionF(Left, Right); return (Result); } HMM_INLINE hmm_vec2 operator*(float Left, hmm_vec2 Right) { hmm_vec2 Result = HMM_MultiplyVec2f(Right, Left); return (Result); } HMM_INLINE hmm_vec3 operator*(float Left, hmm_vec3 Right) { hmm_vec3 Result = HMM_MultiplyVec3f(Right, Left); return (Result); } HMM_INLINE hmm_vec4 operator*(float Left, hmm_vec4 Right) { hmm_vec4 Result = HMM_MultiplyVec4f(Right, Left); return (Result); } HMM_INLINE hmm_mat4 operator*(float Left, hmm_mat4 Right) { hmm_mat4 Result = HMM_MultiplyMat4f(Right, Left); return (Result); } HMM_INLINE hmm_quaternion operator*(float Left, hmm_quaternion Right) { hmm_quaternion Result = HMM_MultiplyQuaternionF(Right, Left); return (Result); } HMM_INLINE hmm_vec4 operator*(hmm_mat4 Matrix, hmm_vec4 Vector) { hmm_vec4 Result = HMM_MultiplyMat4ByVec4(Matrix, Vector); return (Result); } HMM_INLINE hmm_vec2 operator/(hmm_vec2 Left, hmm_vec2 Right) { hmm_vec2 Result = HMM_DivideVec2(Left, Right); return (Result); } HMM_INLINE hmm_vec3 operator/(hmm_vec3 Left, hmm_vec3 Right) { hmm_vec3 Result = HMM_DivideVec3(Left, Right); return (Result); } HMM_INLINE hmm_vec4 operator/(hmm_vec4 Left, hmm_vec4 Right) { hmm_vec4 Result = HMM_DivideVec4(Left, Right); return (Result); } HMM_INLINE hmm_vec2 operator/(hmm_vec2 Left, float Right) { hmm_vec2 Result = HMM_DivideVec2f(Left, Right); return (Result); } HMM_INLINE hmm_vec3 operator/(hmm_vec3 Left, float Right) { hmm_vec3 Result = HMM_DivideVec3f(Left, Right); return (Result); } HMM_INLINE hmm_vec4 operator/(hmm_vec4 Left, float Right) { hmm_vec4 Result = HMM_DivideVec4f(Left, Right); return (Result); } HMM_INLINE hmm_mat4 operator/(hmm_mat4 Left, float Right) { hmm_mat4 Result = HMM_DivideMat4f(Left, Right); return (Result); } HMM_INLINE hmm_quaternion operator/(hmm_quaternion Left, float Right) { hmm_quaternion Result = HMM_DivideQuaternionF(Left, Right); return (Result); } HMM_INLINE hmm_vec2 &operator+=(hmm_vec2 &Left, hmm_vec2 Right) { return (Left = Left + Right); } HMM_INLINE hmm_vec3 &operator+=(hmm_vec3 &Left, hmm_vec3 Right) { return (Left = Left + Right); } HMM_INLINE hmm_vec4 &operator+=(hmm_vec4 &Left, hmm_vec4 Right) { return (Left = Left + Right); } HMM_INLINE hmm_mat4 &operator+=(hmm_mat4 &Left, hmm_mat4 Right) { return (Left = Left + Right); } HMM_INLINE hmm_quaternion &operator+=(hmm_quaternion &Left, hmm_quaternion Right) { return (Left = Left + Right); } HMM_INLINE hmm_vec2 &operator-=(hmm_vec2 &Left, hmm_vec2 Right) { return (Left = Left - Right); } HMM_INLINE hmm_vec3 &operator-=(hmm_vec3 &Left, hmm_vec3 Right) { return (Left = Left - Right); } HMM_INLINE hmm_vec4 &operator-=(hmm_vec4 &Left, hmm_vec4 Right) { return (Left = Left - Right); } HMM_INLINE hmm_mat4 &operator-=(hmm_mat4 &Left, hmm_mat4 Right) { return (Left = Left - Right); } HMM_INLINE hmm_quaternion &operator-=(hmm_quaternion &Left, hmm_quaternion Right) { return (Left = Left - Right); } HMM_INLINE hmm_vec2 &operator*=(hmm_vec2 &Left, hmm_vec2 Right) { return (Left = Left * Right); } HMM_INLINE hmm_vec3 &operator*=(hmm_vec3 &Left, hmm_vec3 Right) { return (Left = Left * Right); } HMM_INLINE hmm_vec4 &operator*=(hmm_vec4 &Left, hmm_vec4 Right) { return (Left = Left * Right); } HMM_INLINE hmm_vec2 &operator*=(hmm_vec2 &Left, float Right) { return (Left = Left * Right); } HMM_INLINE hmm_vec3 &operator*=(hmm_vec3 &Left, float Right) { return (Left = Left * Right); } HMM_INLINE hmm_vec4 &operator*=(hmm_vec4 &Left, float Right) { return (Left = Left * Right); } HMM_INLINE hmm_mat4 &operator*=(hmm_mat4 &Left, float Right) { return (Left = Left * Right); } HMM_INLINE hmm_quaternion &operator*=(hmm_quaternion &Left, float Right) { return (Left = Left * Right); } HMM_INLINE hmm_vec2 &operator/=(hmm_vec2 &Left, hmm_vec2 Right) { return (Left = Left / Right); } HMM_INLINE hmm_vec3 &operator/=(hmm_vec3 &Left, hmm_vec3 Right) { return (Left = Left / Right); } HMM_INLINE hmm_vec4 &operator/=(hmm_vec4 &Left, hmm_vec4 Right) { return (Left = Left / Right); } HMM_INLINE hmm_vec2 &operator/=(hmm_vec2 &Left, float Right) { return (Left = Left / Right); } HMM_INLINE hmm_vec3 &operator/=(hmm_vec3 &Left, float Right) { return (Left = Left / Right); } HMM_INLINE hmm_vec4 &operator/=(hmm_vec4 &Left, float Right) { return (Left = Left / Right); } HMM_INLINE hmm_mat4 &operator/=(hmm_mat4 &Left, float Right) { return (Left = Left / Right); } HMM_INLINE hmm_quaternion &operator/=(hmm_quaternion &Left, float Right) { return (Left = Left / Right); } HMM_INLINE hmm_bool operator==(hmm_vec2 Left, hmm_vec2 Right) { return HMM_EqualsVec2(Left, Right); } HMM_INLINE hmm_bool operator==(hmm_vec3 Left, hmm_vec3 Right) { return HMM_EqualsVec3(Left, Right); } HMM_INLINE hmm_bool operator==(hmm_vec4 Left, hmm_vec4 Right) { return HMM_EqualsVec4(Left, Right); } HMM_INLINE hmm_bool operator!=(hmm_vec2 Left, hmm_vec2 Right) { return !HMM_EqualsVec2(Left, Right); } HMM_INLINE hmm_bool operator!=(hmm_vec3 Left, hmm_vec3 Right) { return !HMM_EqualsVec3(Left, Right); } HMM_INLINE hmm_bool operator!=(hmm_vec4 Left, hmm_vec4 Right) { return !HMM_EqualsVec4(Left, Right); } #endif /* __cplusplus */ #ifdef __clang__ #pragma GCC diagnostic pop #endif #endif /* HANDMADE_MATH_H */ #ifdef HANDMADE_MATH_IMPLEMENTATION 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); } #ifndef HANDMADE_MATH__USE_SSE hmm_mat4 HMM_Transpose(hmm_mat4 Matrix) { hmm_mat4 Result; 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); } #endif #ifndef HANDMADE_MATH__USE_SSE hmm_mat4 HMM_AddMat4(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result; 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); } #endif #ifndef HANDMADE_MATH__USE_SSE hmm_mat4 HMM_SubtractMat4(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result; 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); } #endif hmm_mat4 HMM_MultiplyMat4(hmm_mat4 Left, hmm_mat4 Right) { hmm_mat4 Result; #ifdef HANDMADE_MATH__USE_SSE hmm_mat4 TransposedLeft = HMM_Transpose(Left); hmm_mat4 TransposedRight = HMM_Transpose(Right); Result.Rows[0] = HMM_LinearCombineSSE(TransposedLeft.Rows[0], TransposedRight); Result.Rows[1] = HMM_LinearCombineSSE(TransposedLeft.Rows[1], TransposedRight); Result.Rows[2] = HMM_LinearCombineSSE(TransposedLeft.Rows[2], TransposedRight); Result.Rows[3] = HMM_LinearCombineSSE(TransposedLeft.Rows[3], TransposedRight); Result = HMM_Transpose(Result); #else 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; } } #endif return (Result); } #ifndef HANDMADE_MATH__USE_SSE hmm_mat4 HMM_MultiplyMat4f(hmm_mat4 Matrix, float Scalar) { hmm_mat4 Result; 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); } #endif hmm_vec4 HMM_MultiplyMat4ByVec4(hmm_mat4 Matrix, hmm_vec4 Vector) { hmm_vec4 Result; 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); } #ifndef HANDMADE_MATH__USE_SSE hmm_mat4 HMM_DivideMat4f(hmm_mat4 Matrix, float Scalar) { hmm_mat4 Result; 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); } #endif 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); } hmm_mat4 HMM_LookAt(hmm_vec3 Eye, hmm_vec3 Center, hmm_vec3 Up) { hmm_mat4 Result; 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); } hmm_quaternion HMM_InverseQuaternion(hmm_quaternion Left) { hmm_quaternion Conjugate; hmm_quaternion Result; 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); } hmm_quaternion HMM_Slerp(hmm_quaternion Left, float Time, hmm_quaternion Right) { hmm_quaternion Result; hmm_quaternion QuaternionLeft; hmm_quaternion QuaternionRight; 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); } hmm_mat4 HMM_QuaternionToMat4(hmm_quaternion Left) { hmm_mat4 Result; 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); } hmm_quaternion HMM_QuaternionFromAxisAngle(hmm_vec3 Axis, float AngleOfRotation) { hmm_quaternion Result; hmm_vec3 RotatedVector; float AxisNorm = 0; float SineOfRotation = 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); } #endif /* HANDMADE_MATH_IMPLEMENTATION */ yquake2-QUAKE2_8_40/src/client/refresh/gl3/header/local.h000066400000000000000000000426371465112212000230100ustar00rootroot00000000000000/* * 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!) #ifdef YQ2_GL3_GLES3 #include #else // desktop GL3 #define GL_GLEXT_PROTOTYPES 1 #include #include #endif #else #ifdef YQ2_GL3_GLES3 #include "../glad-gles3/include/glad/glad.h" // yes, this is a bit hacky, but it works :-P #define glDepthRange glDepthRangef #else // desktop GL3 #include "../glad/include/glad/glad.h" #endif #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" }; // always using RGBA now, GLES3 on RPi4 doesn't work otherwise // and I think all modern GPUs prefer 4byte pixels over 3bytes static const int gl3_solid_format = GL_RGBA; static const int gl3_alpha_format = GL_RGBA; static const int gl3_tex_solid_format = GL_RGBA; 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; int major_version; int minor_version; // ---- qboolean anisotropic; // is GL_EXT_texture_filter_anisotropic supported? qboolean debug_output; // is GL_ARB_debug_output supported? qboolean stencil; // Do we have a stencil buffer? qboolean useBigVBO; // workaround for AMDs windows driver for fewer calls to glBufferData() // ---- float max_anisotropy; } gl3config_t; typedef struct { GLuint shaderProgram; GLint uniVblend; GLint uniLmScalesOrTime; // for 3D it's lmScales, for 2D underwater PP it's time 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 transProjViewMat4; // gl3state.projMat3D * gl3state.viewMat3D - so we don't have to do this in the shader 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 lightScaleForTurb; // surfaces with SURF_DRAWTURB (water, lava) don't have lightmaps, use this instead GLfloat _padding[2]; // 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; // 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 // FBO for postprocess effects (like under-water-warping) GLuint ppFBO; GLuint ppFBtex; // ppFBO's texture for color buffer int ppFBtexWidth, ppFBtexHeight; GLuint ppFBrbo; // ppFBO's renderbuffer object for depth and stencil buffer qboolean ppFBObound; // is it currently bound (rendered to)? //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 si2DpostProcess; // shader to render postprocess FBO, when *not* underwater gl3ShaderInfo_t si2DpostProcessWater; // shader to apply water-warp postprocess effect 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 10 floats and one uint as vertex input (x,y,z, s,t, lms,lmt, normX,normY,normZ ; lightFlags) // the next two are for gl3config.useBigVBO == true int vbo3Dsize; int vbo3DcurOffset; 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; hmm_mat4 projMat3D; hmm_mat4 viewMat3D; } 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; extern qboolean IsHighDPIaware; /* 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; qboolean is_lava; // DG: added for lava brightness hack } 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 float gl3depthmin, gl3depthmax; extern cplane_t frustum[4]; extern vec3_t gl3_origin; 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 void GL3_BufferAndDraw3D(const gl3_3D_vtx_t* verts, int numVerts, GLenum drawMode); extern void GL3_RotateForEntity(entity_t *e); // gl3_sdl.c extern int GL3_InitContext(void* win); extern void GL3_GetDrawableSize(int* width, int* height); extern int GL3_PrepareForWindow(void); extern qboolean GL3_IsVsyncActive(void); extern void GL3_EndFrame(void); extern void GL3_SetVsync(void); extern void GL3_ShutdownContext(void); extern int GL3_GetSDLVersion(void); // 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 const byte* GL3_Mod_ClusterPVS(int cluster, const 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 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_DrawFrameBufferObject(int x, int y, int w, int h, GLuint fboTexture, const float v_blend[4]); 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, const byte *data, int bits); // 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, size_t data_size, imagetype_t type, int bits); extern gl3image_t *GL3_FindImage(const char *name, imagetype_t type); extern gl3image_t *GL3_RegisterSkin(char *name); extern void GL3_ShutdownImages(void); extern void GL3_FreeUnusedImages(void); extern qboolean GL3_ImageHasFreeSpace(void); extern void GL3_ImageList_f(void); // gl3_light.c extern int r_dlightframecount; extern void GL3_MarkSurfaceLights(dlight_t *light, int bit, mnode_t *node, int r_dlightframecount); extern void GL3_PushDlights(void); extern void GL3_LightPoint(entity_t *currententity, 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(gl3model_t *currentmodel, 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, gl3model_t *currentmodel); 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 *r_vsync; extern cvar_t *r_retexturing; extern cvar_t *r_scale8bittextures; extern cvar_t *vid_fullscreen; extern cvar_t *r_mode; extern cvar_t *r_customwidth; extern cvar_t *r_customheight; extern cvar_t *r_2D_unfiltered; extern cvar_t *r_videos_unfiltered; extern cvar_t *gl_nolerp_list; extern cvar_t *r_lerp_list; extern cvar_t *gl_nobind; extern cvar_t *r_lockpvs; extern cvar_t *r_novis; extern cvar_t *r_cull; extern cvar_t *gl_zfix; extern cvar_t *r_fullbright; extern cvar_t *r_norefresh; extern cvar_t *gl_lefthand; extern cvar_t *r_gunfov; extern cvar_t *r_farsee; extern cvar_t *r_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_texturemode; extern cvar_t *r_lightlevel; extern cvar_t *gl3_overbrightbits; extern cvar_t *gl3_particle_fade_factor; extern cvar_t *gl3_particle_square; extern cvar_t *gl3_colorlight; extern cvar_t *gl_polyblend; extern cvar_t *r_modulate; extern cvar_t *gl_lightmap; extern cvar_t *gl_shadows; extern cvar_t *r_fixsurfsky; extern cvar_t *r_palettedtexture; extern cvar_t *r_validation; extern cvar_t *gl3_debugcontext; #endif /* SRC_CLIENT_REFRESH_GL3_HEADER_LOCAL_H_ */ yquake2-QUAKE2_8_40/src/client/refresh/gl3/header/model.h000066400000000000000000000100211465112212000227740ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_ // 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 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; /* 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; struct model_s *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; // submodules vec3_t origin; // for sounds or lights } gl3model_t; #endif /* SRC_CLIENT_REFRESH_GL3_HEADER_MODEL_H_ */ yquake2-QUAKE2_8_40/src/client/refresh/ref_shared.h000066400000000000000000000172431465112212000220760ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 "../vid/header/ref.h" #ifdef _MSC_VER #include #define YQ2_VLA(TYPE, VARNAME, NUMELEMS) \ TYPE * VARNAME = (TYPE *) _malloca(sizeof(TYPE) * NUMELEMS) #define YQ2_VLAFREE(VARNAME) \ _freea(VARNAME); VARNAME=NULL; #else // other compilers hopefully support C99 VLAs (gcc/mingw and clang do) #define YQ2_VLA(TYPE, VARNAME, NUMELEMS) \ TYPE VARNAME[NUMELEMS] #define YQ2_VLAFREE(VARNAME) #endif /* * 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, ...) PRINTF_ATTR(2, 3); /* Shared images load */ typedef struct image_s* (*loadimage_t)(const char *name, byte *pic, int width, int realwidth, int height, int realheight, size_t data_size, imagetype_t type, int bits); extern struct image_s* LoadWal(const char *origname, imagetype_t type, loadimage_t load_image); extern struct image_s* LoadM8(const char *origname, imagetype_t type, loadimage_t load_image); extern struct image_s* LoadM32(const char *origname, imagetype_t type, loadimage_t load_image); extern void FixFileExt(const char *origname, const char *ext, char *filename, size_t size); extern void GetPCXPalette(byte **colormap, unsigned *d_8to24table); extern void LoadPCX(const char *origname, byte **pic, byte **palette, int *width, int *height); extern void GetPCXInfo(const char *origname, int *width, int *height); extern void GetWalInfo(const char *name, int *width, int *height); extern void GetM8Info(const char *name, int *width, int *height); extern void GetM32Info(const char *name, int *width, int *height); extern qboolean ResizeSTB(const byte *input_pixels, int input_width, int input_height, byte *output_pixels, int output_width, int output_height); extern void SmoothColorImage(unsigned *dst, size_t size, size_t rstep); extern void scale2x(const byte *src, byte *dst, int width, int height); extern void scale3x(const byte *src, byte *dst, int width, int height); extern float Mod_RadiusFromBounds(const vec3_t mins, const vec3_t maxs); extern const byte* Mod_DecompressVis(const byte *in, int row); /* Shared models struct */ enum { SIDE_FRONT = 0, SIDE_BACK = 1, SIDE_ON = 2 }; // FIXME: differentiate from texinfo SURF_ flags enum { SURF_PLANEBACK = 0x02, SURF_DRAWSKY = 0x04, // sky brush face SURF_DRAWTURB = 0x10, SURF_DRAWBACKGROUND = 0x40, SURF_UNDERWATER = 0x80 }; typedef struct mvertex_s { vec3_t position; } mvertex_t; typedef struct medge_s { 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 */ struct image_s *image; } mtexinfo_t; #define CONTENTS_NODE -1 typedef struct mnode_s { /* common with leaf */ int contents; /* CONTENTS_NODE, 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 leaf */ int contents; /* CONTENTS_NODE, to differentiate from leafs */ 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; struct msurface_s **firstmarksurface; int nummarksurfaces; int key; /* BSP sequence number for leaf's contents */ } mleaf_t; /* Shared models func */ typedef struct image_s* (*findimage_t)(const char *name, imagetype_t type); extern void *Mod_LoadMD2 (const char *mod_name, const void *buffer, int modfilelen, vec3_t mins, vec3_t maxs, struct image_s **skins, findimage_t find_image, modtype_t *type); extern void *Mod_LoadSP2 (const char *mod_name, const void *buffer, int modfilelen, struct image_s **skins, findimage_t find_image, modtype_t *type); extern int Mod_ReLoadSkins(struct image_s **skins, findimage_t find_image, void *extradata, modtype_t type); extern struct image_s *GetSkyImage(const char *skyname, const char* surfname, qboolean palettedtexture, findimage_t find_image); extern struct image_s *GetTexImage(const char *name, findimage_t find_image); extern struct image_s *R_FindPic(const char *name, findimage_t find_image); extern struct image_s* R_LoadImage(const char *name, const char* namewe, const char *ext, imagetype_t type, qboolean r_retexturing, loadimage_t load_image); extern void Mod_LoadNodes(const char *name, cplane_t *planes, int numplanes, mleaf_t *leafs, int numleafs, mnode_t **nodes, int *numnodes, const byte *mod_base, const lump_t *l); extern void Mod_LoadVertexes(const char *name, mvertex_t **vertexes, int *numvertexes, const byte *mod_base, const lump_t *l, int extra); extern void Mod_LoadVisibility(dvis_t **vis, const byte *mod_base, const lump_t *l); extern void Mod_LoadLighting(byte **lightdata, const byte *mod_base, const lump_t *l); extern void Mod_LoadTexinfo(const char *name, mtexinfo_t **texinfo, int *numtexinfo, const byte *mod_base, const lump_t *l, findimage_t find_image, struct image_s *notexture, int extra); extern void Mod_LoadEdges(const char *name, medge_t **edges, int *numedges, const byte *mod_base, const lump_t *l, int extra); extern void Mod_LoadPlanes (const char *name, cplane_t **planes, int *numplanes, const byte *mod_base, const lump_t *l, int extra); extern void Mod_LoadSurfedges (const char *name, int **surfedges, int *numsurfedges, const byte *mod_base, const lump_t *l, int extra); extern int Mod_CalcLumpHunkSize(const lump_t *l, int inSize, int outSize, int extra); extern mleaf_t *Mod_PointInLeaf(const vec3_t p, mnode_t *node); /* Surface logic */ #define DLIGHT_CUTOFF 64 typedef void (*marksurfacelights_t)(dlight_t *light, int bit, mnode_t *node, int lightframecount); extern void R_MarkLights(dlight_t *light, int bit, mnode_t *node, int lightframecount, marksurfacelights_t mark_surface_lights); extern struct image_s *R_TextureAnimation(const entity_t *currententity, const mtexinfo_t *tex); extern qboolean R_AreaVisible(const byte *areabits, mleaf_t *pleaf); extern qboolean R_CullBox(vec3_t mins, vec3_t maxs, cplane_t *frustum); extern void R_SetFrustum(vec3_t vup, vec3_t vpn, vec3_t vright, vec3_t r_origin, float fov_x, float fov_y, cplane_t *frustum); #endif /* SRC_CLIENT_REFRESH_REF_SHARED_H_ */ yquake2-QUAKE2_8_40/src/client/refresh/soft/000077500000000000000000000000001465112212000205675ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/soft/header/000077500000000000000000000000001465112212000220175ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/refresh/soft/header/local.h000066400000000000000000000410031465112212000232600ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __R_LOCAL__ #define __R_LOCAL__ #include #include #include #include "../../ref_shared.h" #include #include #include #define REF_VERSION "SOFT 0.01" // up / down #define PITCH 0 // left / right #define YAW 1 // fall over #define ROLL 2 /* 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 */ #define NUM_MIPS 4 typedef struct image_s { char name[MAX_QPATH]; // game path, including extension imagetype_t type; int width, height; int asset_width, asset_height; // asset texture size qboolean transparent; // true if any 255 pixels in image int registration_sequence; // 0 = free byte *pixels[NUM_MIPS]; // mip levels int mip_levels; // count of mip levels } image_t; //=================================================================== typedef unsigned char pixel_t; typedef int shift20_t; typedef int zvalue_t; typedef unsigned int light_t; typedef int light3_t[3]; // xyz-prescale to 16.16 fixed-point #define SHIFT16XYZ 16 #define SHIFT16XYZ_MULT (1 << SHIFT16XYZ) typedef enum { rserr_ok, rserr_invalid_mode, rserr_unknown } rserr_t; /* 64 light grades available */ #define LIGHTMASK 0xFF00 extern viddef_t vid; extern pixel_t *vid_buffer; // invisible buffer extern pixel_t *vid_colormap; // 256 * VID_GRADES size extern pixel_t *vid_alphamap; // 256 * 256 translucency map extern light_t vid_lightthreshold; // full light distance maximum extern char shift_size; // shift size in fixed-point typedef struct { vrect_t vrect; // subwindow in video for refresh // FIXME: not need vrect next field here? vrect_t aliasvrect; // scaled Alias version shift20_t vrectright, vrectbottom; // right & bottom screen coords shift20_t aliasvrectright, aliasvrectbottom; // scaled Alias versions float vrectrightedge; // rightmost right edge we care about, // for use in edge list float fvrectx, fvrecty; // for floating-point compares float fvrectx_adj, fvrecty_adj; // left and top edges, for clamping shift20_t vrect_x_adj_shift20; // (vrect.x + 0.5 - epsilon) << 20 shift20_t vrectright_adj_shift20; // (vrectright + 0.5 - epsilon) << 20 float fvrectright_adj, fvrectbottom_adj; // right and bottom edges, for clamping float fvrectright; // rightmost edge, for Alias clamping float fvrectbottom; // bottommost edge, for Alias clamping float horizontalFieldOfView; // at Z = 1.0, this many X is visible // 2.0 = 90 degrees float xOrigin; // should probably always be 0.5 float yOrigin; // between be around 0.3 to 0.5 vec3_t vieworg; vec3_t viewangles; int ambientlight; } oldrefdef_t; extern oldrefdef_t r_refdef; #include "model.h" /* ==================================================== CONSTANTS ==================================================== */ #define VID_CBITS 6 #define VID_GRADES (1 << VID_CBITS) // sw_local.h: general refresh-related stuff shared between the refresh and the // driver #define MAXVERTS 64 // max points in a surface polygon #define MAXWORKINGVERTS (MAXVERTS+4) // max points in an intermediate // polygon (while processing) #define PARTICLE_Z_CLIP 8.0 #define TRANSPARENT_COLOR 0xFF #define CYCLE 128 // turbulent cycle size // flags in finalvert_t.flags #define ALIAS_LEFT_CLIP 0x0001 #define ALIAS_TOP_CLIP 0x0002 #define ALIAS_RIGHT_CLIP 0x0004 #define ALIAS_BOTTOM_CLIP 0x0008 #define ALIAS_Z_CLIP 0x0010 #define ALIAS_XY_CLIP_MASK 0x000F #define BMODEL_FULLY_CLIPPED 0x10 // value returned by R_BmodelCheckBBox () // if bbox is trivially rejected #define XCENTERING (1.0 / 2.0) #define YCENTERING (1.0 / 2.0) #define BACKFACE_EPSILON 0.01 #define NEAR_CLIP 0.01 #define ALIAS_Z_CLIP_PLANE 4 // turbulence stuff #define AMP 8*0x10000 #define AMP2 3 #define SPEED 20 /* ==================================================== TYPES ==================================================== */ typedef struct { float u, v; float s, t; float zi; } emitpoint_t; /* ** if you change this structure be sure to change the #defines ** listed after it! */ typedef struct compactvert_s { int u, v, s, t; light3_t l; // full color light zvalue_t zi; } compactvert_t; typedef struct finalvert_s { compactvert_t cv; // reuse compacted type int flags; float xyz[3]; // eye space } finalvert_t; typedef struct { pixel_t *pskin; int skinwidth; int skinheight; float scalewidth; float scaleheight; } affinetridesc_t; typedef struct { byte *surfdat; // destination for generated surface int rowbytes; // destination logical width in bytes msurface_t *surf; // description for surface to generate fixed8_t lightadj[MAXLIGHTMAPS]; // adjust for lightmap levels for dynamic lighting image_t *image; int surfmip; // mipmapped ratio of surface texels / world pixels int surfwidth; // in mipmapped texels int surfheight; // in mipmapped texels } drawsurf_t; // clipped bmodel edges typedef struct bedge_s { mvertex_t *v[2]; struct bedge_s *pnext; } bedge_t; typedef struct clipplane_s { vec3_t normal; float dist; struct clipplane_s *next; byte leftedge; byte rightedge; byte reserved[2]; } clipplane_t; typedef struct surfcache_s { struct surfcache_s *next; struct surfcache_s **owner; // NULL is an empty chunk of memory int lightadj[MAXLIGHTMAPS]; // checked for strobe flush int dlight; int size; // including header unsigned width; unsigned height; // DEBUG only needed for debug float mipscale; image_t *image; byte data[4]; // width*height elements } surfcache_t; typedef struct espan_s { int u, v, count; struct espan_s *pnext; } espan_t; extern espan_t *vid_polygon_spans; // space for spans in r_poly // used by the polygon drawer (sw_poly.c) and sprite setup code (sw_sprite.c) typedef struct { int nump; emitpoint_t *pverts; byte *pixels; // image int pixel_width; // image width int pixel_height; // image height vec3_t vup, vright, vpn; // in worldspace, for plane eq float dist; float s_offset, t_offset; float viewer_position[3]; void (*drawspanlet)(const int *r_turb_turb); int stipple_parity; } polydesc_t; // FIXME: compress, make a union if that will help // insubmodel is only 1, flags is fewer than 32, spanstate could be a byte typedef struct surf_s { struct surf_s *next; // active surface stack in r_edge.c struct surf_s *prev; // used in r_edge.c for active surf stack struct espan_s *spans; // pointer to linked list of spans to draw int key; // sorting key (BSP order) shift20_t last_u; // set during tracing int spanstate; // 0 = not in span // 1 = in span // -1 = in inverted span (end before // start) int flags; // currentface flags msurface_t *msurf; entity_t *entity; float nearzi; // nearest 1/z on surface, for mipmapping qboolean insubmodel; float d_ziorigin, d_zistepu, d_zistepv; } surf_t; typedef unsigned short surfindex_t; // surface index size #define SURFINDEX_MAX (1 << (sizeof(surfindex_t) * 8)) typedef struct edge_s { shift20_t u; shift20_t u_step; struct edge_s *prev, *next; surfindex_t surfs[2]; struct edge_s *nextremove; float nearzi; medge_t *owner; } edge_t; /* ==================================================== VARS ==================================================== */ extern int r_framecount; // sequence # of current frame since Quake // started extern float r_aliasuvscale; // scale-up factor for screen u and v // on Alias vertices passed to driver extern qboolean r_dowarp; extern affinetridesc_t r_affinetridesc; void D_WarpScreen(void); //=======================================================================// // callbacks to Quake extern int c_surf; extern pixel_t *r_warpbuffer; extern float scale_for_mip; extern float d_sdivzstepu, d_tdivzstepu; extern float d_sdivzstepv, d_tdivzstepv; extern float d_sdivzorigin, d_tdivzorigin; void D_DrawSpansPow2(espan_t *pspan, float d_ziorigin, float d_zistepu, float d_zistepv); void D_DrawZSpans(espan_t *pspan, float d_ziorigin, float d_zistepu, float d_zistepv); void TurbulentPow2(espan_t *pspan, float d_ziorigin, float d_zistepu, float d_zistepv); void NonTurbulentPow2(espan_t *pspan, float d_ziorigin, float d_zistepu, float d_zistepv); surfcache_t *D_CacheSurface(const entity_t *currententity, msurface_t *surface, int miplevel); extern int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; extern int d_pix_min, d_pix_max, d_pix_mul; extern pixel_t *d_viewbuffer; extern zvalue_t *d_pzbuffer; extern int d_minmip; extern float d_scalemip[NUM_MIPS-1]; extern int vid_buffer_height; extern int vid_buffer_width; //=================================================================== extern int cachewidth; extern pixel_t *cacheblock; extern int r_drawnpolycount; extern int *sintable; extern int *intsintable; extern int *blanktable; extern vec3_t vup, base_vup; extern vec3_t vpn, base_vpn; extern vec3_t vright, base_vright; extern surf_t *surfaces, *surface_p, *surf_max; // allow some very large lightmaps extern light_t *blocklights, *blocklight_max; // surfaces are generated in back to front order by the bsp, so if a surf // pointer is greater than another one, it should be drawn in front // surfaces[1] is the background, and is used as the active surface stack. // surfaces[0] is a dummy, because index 0 is used to indicate no surface // attached to an edge_t //=================================================================== extern float xcenter, ycenter; extern float xscale, yscale; extern float xscaleinv, yscaleinv; extern float xscaleshrink, yscaleshrink; extern void TransformVector(const vec3_t in, vec3_t out); //=========================================================================== extern cvar_t *sw_clearcolor; extern cvar_t *sw_drawflat; extern cvar_t *sw_draworder; extern cvar_t *sw_mipcap; extern cvar_t *sw_mipscale; extern cvar_t *sw_stipplealpha; extern cvar_t *sw_surfcacheoverride; extern cvar_t *sw_waterwarp; extern cvar_t *sw_gunzposition; extern cvar_t *r_validation; extern cvar_t *r_retexturing; extern cvar_t *r_scale8bittextures; extern cvar_t *r_fullbright; extern cvar_t *r_lefthand; extern cvar_t *r_gunfov; extern cvar_t *r_farsee; extern cvar_t *r_lightmap; extern cvar_t *r_colorlight; extern cvar_t *r_drawworld; extern cvar_t *r_lerpmodels; extern cvar_t *r_lightlevel; extern cvar_t *r_modulate; extern cvar_t *r_fixsurfsky; extern cvar_t *r_cull; extern clipplane_t view_clipplanes[4]; extern int *pfrustum_indexes[4]; extern cplane_t frustum[4]; //============================================================================= void R_RenderWorld(entity_t *currententity); //============================================================================= extern cplane_t screenedge[4]; extern vec3_t r_origin; extern vec3_t modelorg; extern vec3_t r_entorigin; extern int r_visframecount; extern msurface_t *r_alpha_surfaces; //============================================================================= // // current entity info // void R_DrawAlphaSurfaces(const entity_t *currententity); void R_DrawSprite(entity_t *currententity, const model_t *currentmodel); void R_RenderFace(entity_t *currententity, const model_t *currentmodel, msurface_t *fa, int clipflags, qboolean insubmodel); void R_RenderBmodelFace(entity_t *currententity, bedge_t *pedges, msurface_t *psurf, int r_currentbkey); void R_TransformFrustum(void); void R_DrawSubmodelPolygons(entity_t *currententity, const model_t *currentmodel, int clipflags, mnode_t *topnode); void R_DrawSolidClippedSubmodelPolygons(entity_t *currententity, const model_t *currentmodel, mnode_t *topnode); void R_AliasDrawModel(entity_t *currententity, const model_t *currentmodel); void R_BeginEdgeFrame(void); void R_ScanEdges(entity_t *currententity, surf_t *surface); void R_PushDlights(const model_t *model); void R_RotateBmodel(const entity_t *currententity); extern int c_faceclip; extern int r_polycount; extern int sadjust, tadjust; extern int bbextents, bbextentt; extern int r_currentkey; void R_DrawParticles (void); extern int r_amodels_drawn; extern int r_numallocatededges; extern edge_t *r_edges, *edge_p, *edge_max; extern edge_t **newedges; extern edge_t **removeedges; typedef struct { int u, v, count; pixel_t *ptex; int sfrac, tfrac; light3_t light; zvalue_t zi; } spanpackage_t; extern spanpackage_t *triangle_spans, *triangles_max; void R_PolysetDrawSpans8_33(const entity_t *currententity, spanpackage_t *pspanpackage); void R_PolysetDrawSpans8_66(const entity_t *currententity, spanpackage_t *pspanpackage); void R_PolysetDrawSpans8_Opaque(const entity_t *currententity, spanpackage_t *pspanpackage); extern byte **warp_rowptr; extern int *warp_column; extern espan_t *edge_basespans; extern espan_t *max_span_p; extern int r_numallocatedverts; extern int r_numallocatededgebasespans; extern finalvert_t *finalverts, *finalverts_max; extern int r_aliasblendcolor; extern float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; extern qboolean r_outofsurfaces; extern qboolean r_outofedges; extern qboolean r_outofverts; extern qboolean r_outoftriangles; extern qboolean r_outoflights; extern qboolean r_outedgebasespans; extern mvertex_t *r_pcurrentvertbase; void R_DrawTriangle(const entity_t *currententity, const finalvert_t *a, const finalvert_t *b, const finalvert_t *c); void R_AliasClipTriangle(const entity_t *currententity, const finalvert_t *index0, const finalvert_t *index1, const finalvert_t *index2); extern float r_time1; extern float da_time1, da_time2; extern float dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; extern float se_time1, se_time2, de_time1, de_time2; extern int r_viewcluster, r_oldviewcluster; extern int r_clipflags; extern image_t *r_notexture_mip; extern model_t *r_worldmodel; void R_PrintAliasStats (void); void R_PrintTimes (void); void R_PrintDSpeeds (void); void R_LightPoint (const entity_t *currententity, vec3_t p, vec3_t color); void R_SetupFrame (void); extern refdef_t r_newrefdef; extern surfcache_t *sc_base; extern void *colormap; //==================================================================== void R_NewMap (void); void Draw_InitLocal(void); void R_InitCaches(void); void D_FlushCaches(void); void RE_BeginRegistration (char *model); struct model_s *RE_RegisterModel (char *name); void RE_EndRegistration (void); struct image_s *RE_Draw_FindPic (char *name); void RE_Draw_GetPicSize (int *w, int *h, char *name); void RE_Draw_PicScaled (int x, int y, char *name, float scale); void RE_Draw_StretchPic (int x, int y, int w, int h, char *name); void RE_Draw_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int bits); void RE_Draw_CharScaled (int x, int y, int c, float scale); void RE_Draw_TileClear (int x, int y, int w, int h, char *name); void RE_Draw_Fill (int x, int y, int w, int h, int c); void RE_Draw_FadeScreen (void); extern byte d_8to24table[256 * 4]; void R_InitImages(void); void R_ShutdownImages(void); image_t *R_FindImage(const char *name, imagetype_t type); byte *Get_BestImageSize(const image_t *image, int *req_width, int *req_height); void R_FreeUnusedImages(void); qboolean R_ImageHasFreeSpace(void); pixel_t R_ApplyLight(pixel_t pix, const light3_t light); void R_Convert32To8bit(const unsigned char* pic_in, pixel_t* pic_out, size_t size, qboolean transparent); void R_InitSkyBox(model_t *loadmodel); void R_IMFlatShadedQuad( const vec3_t a, const vec3_t b, const vec3_t c, const vec3_t d, int color, float alpha ); // VID Buffer damage void VID_DamageBuffer(int u, int v); // VID zBuffer damage extern qboolean fastmoving; void VID_DamageZBuffer(int u, int v); qboolean VID_CheckDamageZBuffer(int u, int v, int ucount, int vcount); /* ==================================================================== IMPORTED FUNCTIONS ==================================================================== */ extern refimport_t ri; #endif yquake2-QUAKE2_8_40/src/client/refresh/soft/header/model.h000066400000000000000000000060071465112212000232730ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef __MODEL__ #define __MODEL__ /* d*_t structures are on-disk representations m*_t structures are in-memory */ /* ============================================================================== BRUSH MODELS ============================================================================== */ // // in memory representation // typedef struct msurface_s { int visframe; // should be drawn when node is crossed int dlightframe; int dlightbits; cplane_t *plane; int flags; int firstedge; // look up in model->surfedges[], negative numbers int numedges; // are backwards edges // surface generation data struct surfcache_s *cachespots[MIPLEVELS]; short texturemins[2]; short extents[2]; mtexinfo_t *texinfo; // lighting info byte styles[MAXLIGHTMAPS]; byte *samples; // [numstyles*surfsize*3] struct msurface_s *nextalphasurface; } msurface_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 (sent from server) // qboolean clipbox; vec3_t clipmins, clipmaxs; // // brush model // int firstmodelsurface, nummodelsurfaces; int numsubmodels; struct model_s *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 sprites image_t *skins[MAX_MD2SKINS]; void *extradata; int extradatasize; // submodules vec3_t origin; // for sounds or lights } model_t; //============================================================================ void Mod_Init(void); const byte *Mod_ClusterPVS(int cluster, const model_t *model); void Mod_Modellist_f(void); void Mod_FreeAll(void); void Mod_Free(model_t *mod); extern int registration_sequence; #endif // __MODEL__ yquake2-QUAKE2_8_40/src/client/refresh/soft/sw_aclip.c000066400000000000000000000154761465112212000225510ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sw_aclip.c: clip routines for drawing Alias models directly to the screen #include "header/local.h" void R_AliasProjectAndClipTestFinalVert (finalvert_t *fv); /* ================ R_Alias_clip_z pfv0 is the unclipped vertex, pfv1 is the z-clipped vertex ================ */ static void R_Alias_clip_z (const finalvert_t *pfv0, const finalvert_t *pfv1, finalvert_t *out) { float scale; int i; scale = (ALIAS_Z_CLIP_PLANE - pfv0->xyz[2]) / (pfv1->xyz[2] - pfv0->xyz[2]); out->xyz[0] = pfv0->xyz[0] + (pfv1->xyz[0] - pfv0->xyz[0]) * scale; out->xyz[1] = pfv0->xyz[1] + (pfv1->xyz[1] - pfv0->xyz[1]) * scale; out->xyz[2] = ALIAS_Z_CLIP_PLANE; out->cv.s = pfv0->cv.s + (pfv1->cv.s - pfv0->cv.s) * scale; out->cv.t = pfv0->cv.t + (pfv1->cv.t - pfv0->cv.t) * scale; for(i=0; i<3; i++) out->cv.l[i] = pfv0->cv.l[i] + (pfv1->cv.l[i] - pfv0->cv.l[i]) * scale; R_AliasProjectAndClipTestFinalVert (out); } static void R_Alias_clip_scale (const finalvert_t *pfv0, const finalvert_t *pfv1, float scale, finalvert_t *out) { int i; out->cv.u = pfv1->cv.u + ( pfv0->cv.u - pfv1->cv.u ) * scale + 0.5; out->cv.v = pfv1->cv.v + ( pfv0->cv.v - pfv1->cv.v ) * scale + 0.5; out->cv.s = pfv1->cv.s + ( pfv0->cv.s - pfv1->cv.s ) * scale + 0.5; out->cv.t = pfv1->cv.t + ( pfv0->cv.t - pfv1->cv.t ) * scale + 0.5; for(i=0; i<3; i++) out->cv.l[i] = pfv1->cv.l[i] + ( pfv0->cv.l[i] - pfv1->cv.l[i] ) * scale + 0.5; out->cv.zi = pfv1->cv.zi + ( pfv0->cv.zi - pfv1->cv.zi) * scale + 0.5; } static void R_Alias_clip_left (const finalvert_t *pfv0, const finalvert_t *pfv1, finalvert_t *out) { float scale; if (pfv0->cv.v >= pfv1->cv.v ) { scale = (float)(r_refdef.aliasvrect.x - pfv0->cv.u) / (pfv1->cv.u - pfv0->cv.u); R_Alias_clip_scale (pfv1, pfv0, scale, out); } else { scale = (float)(r_refdef.aliasvrect.x - pfv1->cv.u) / (pfv0->cv.u - pfv1->cv.u); R_Alias_clip_scale (pfv0, pfv1, scale, out); } } static void R_Alias_clip_right (const finalvert_t *pfv0, const finalvert_t *pfv1, finalvert_t *out) { float scale; if ( pfv0->cv.v >= pfv1->cv.v ) { scale = (float)(r_refdef.aliasvrectright - pfv0->cv.u ) / (pfv1->cv.u - pfv0->cv.u ); R_Alias_clip_scale (pfv1, pfv0, scale, out); } else { scale = (float)(r_refdef.aliasvrectright - pfv1->cv.u ) / (pfv0->cv.u - pfv1->cv.u ); R_Alias_clip_scale (pfv0, pfv1, scale, out); } } static void R_Alias_clip_top (const finalvert_t *pfv0, const finalvert_t *pfv1, finalvert_t *out) { float scale; if (pfv0->cv.v >= pfv1->cv.v) { scale = (float)(r_refdef.aliasvrect.y - pfv0->cv.v) / (pfv1->cv.v - pfv0->cv.v); R_Alias_clip_scale (pfv1, pfv0, scale, out); } else { scale = (float)(r_refdef.aliasvrect.y - pfv1->cv.v) / (pfv0->cv.v - pfv1->cv.v); R_Alias_clip_scale (pfv0, pfv1, scale, out); } } static void R_Alias_clip_bottom (const finalvert_t *pfv0, const finalvert_t *pfv1, finalvert_t *out) { float scale; if (pfv0->cv.v >= pfv1->cv.v) { scale = (float)(r_refdef.aliasvrectbottom - pfv0->cv.v) / (pfv1->cv.v - pfv0->cv.v); R_Alias_clip_scale (pfv1, pfv0, scale, out); } else { scale = (float)(r_refdef.aliasvrectbottom - pfv1->cv.v) / (pfv0->cv.v - pfv1->cv.v); R_Alias_clip_scale (pfv0, pfv1, scale, out); } } int R_AliasClip (const finalvert_t *in, finalvert_t *out, int flag, int count, void(*clip)(const finalvert_t *pfv0, const finalvert_t *pfv1, finalvert_t *out) ) { int i,j,k; j = count-1; k = 0; for (i=0 ; i r_refdef.aliasvrectright) out[k].flags |= ALIAS_RIGHT_CLIP; if (out[k].cv.v > r_refdef.aliasvrectbottom) out[k].flags |= ALIAS_BOTTOM_CLIP; k++; } if (!flags) { out[k] = in[i]; k++; } j = i; } return k; } /* ================ R_AliasClipTriangle ================ */ void R_AliasClipTriangle(const entity_t *currententity, const finalvert_t *index0, const finalvert_t *index1, const finalvert_t *index2) { int i, k, pingpong; unsigned clipflags; finalvert_t fv[2][8]; // copy vertexes and fix seam texture coordinates fv[0][0] = *index0; fv[0][1] = *index1; fv[0][2] = *index2; // clip clipflags = fv[0][0].flags | fv[0][1].flags | fv[0][2].flags; if (clipflags & ALIAS_Z_CLIP) { k = R_AliasClip (fv[0], fv[1], ALIAS_Z_CLIP, 3, R_Alias_clip_z); if (k == 0) return; pingpong = 1; clipflags = fv[1][0].flags | fv[1][1].flags | fv[1][2].flags; } else { pingpong = 0; k = 3; } if (clipflags & ALIAS_LEFT_CLIP) { k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], ALIAS_LEFT_CLIP, k, R_Alias_clip_left); if (k == 0) return; pingpong ^= 1; } if (clipflags & ALIAS_RIGHT_CLIP) { k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], ALIAS_RIGHT_CLIP, k, R_Alias_clip_right); if (k == 0) return; pingpong ^= 1; } if (clipflags & ALIAS_BOTTOM_CLIP) { k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], ALIAS_BOTTOM_CLIP, k, R_Alias_clip_bottom); if (k == 0) return; pingpong ^= 1; } if (clipflags & ALIAS_TOP_CLIP) { k = R_AliasClip (fv[pingpong], fv[pingpong ^ 1], ALIAS_TOP_CLIP, k, R_Alias_clip_top); if (k == 0) return; pingpong ^= 1; } for (i=0 ; i r_refdef.aliasvrectright) fv[pingpong][i].cv.u = r_refdef.aliasvrectright; if (fv[pingpong][i].cv.v < r_refdef.aliasvrect.y) fv[pingpong][i].cv.v = r_refdef.aliasvrect.y; else if (fv[pingpong][i].cv.v > r_refdef.aliasvrectbottom) fv[pingpong][i].cv.v = r_refdef.aliasvrectbottom; fv[pingpong][i].flags = 0; } // draw triangles for (i=1 ; i #include "header/local.h" #define LIGHT_MIN 5 // lowest light value we'll allow, to avoid the // need for inner-loop light clamping int r_amodels_drawn; affinetridesc_t r_affinetridesc; static vec3_t r_plightvec; static vec3_t r_lerp_frontv, r_lerp_backv, r_lerp_move; static light3_t r_ambientlight; int r_aliasblendcolor; static vec3_t r_shadelight; static daliasframe_t *r_thisframe, *r_lastframe; static dmdl_t *s_pmdl; static float aliastransform[3][4]; static float aliasworldtransform[3][4]; static float aliasoldworldtransform[3][4]; static float s_ziscale; static vec3_t s_alias_forward, s_alias_right, s_alias_up; #define NUMVERTEXNORMALS 162 static const float r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "../constants/anorms.h" }; static void R_AliasTransformVector(const vec3_t in, vec3_t out, const float xf[3][4]); static void R_AliasTransformFinalVerts(const entity_t *currententity, int numpoints, finalvert_t *fv, dtrivertx_t *oldv, dtrivertx_t *newv ); void R_AliasProjectAndClipTestFinalVert(finalvert_t *fv); /* ================ R_AliasCheckBBox ================ */ #define BBOX_TRIVIAL_ACCEPT 0 #define BBOX_MUST_CLIP_XY 1 #define BBOX_MUST_CLIP_Z 2 #define BBOX_TRIVIAL_REJECT 8 /* ** R_AliasCheckFrameBBox ** ** Checks a specific alias frame bounding box */ static unsigned long R_AliasCheckFrameBBox( daliasframe_t *frame, float worldxf[3][4] ) { // FIXME: should this really be using long and not int32_t or sth? unsigned long aggregate_and_clipcode = ~0U, aggregate_or_clipcode = 0; int i; vec3_t mins, maxs; vec3_t transformed_min, transformed_max; qboolean zfullyclipped = true; /* ** get the exact frame bounding box */ for (i=0 ; i<3 ; i++) { mins[i] = frame->translate[i]; maxs[i] = mins[i] + frame->scale[i]*255; } /* ** transform the min and max values into view space */ R_AliasTransformVector( mins, transformed_min, aliastransform ); R_AliasTransformVector( maxs, transformed_max, aliastransform ); if ( transformed_min[2] >= ALIAS_Z_CLIP_PLANE ) zfullyclipped = false; if ( transformed_max[2] >= ALIAS_Z_CLIP_PLANE ) zfullyclipped = false; if ( zfullyclipped ) { return BBOX_TRIVIAL_REJECT; } /* ** build a transformed bounding box from the given min and max */ for ( i = 0; i < 8; i++ ) { int j; vec3_t tmp, transformed; unsigned long clipcode = 0; 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]; R_AliasTransformVector( tmp, transformed, worldxf ); for ( j = 0; j < 4; j++ ) { float dp = DotProduct( transformed, view_clipplanes[j].normal ); if ( ( dp - view_clipplanes[j].dist ) < 0.0F ) clipcode |= 1 << j; } aggregate_and_clipcode &= clipcode; aggregate_or_clipcode |= clipcode; } if ( aggregate_and_clipcode ) { return BBOX_TRIVIAL_REJECT; } if ( !aggregate_or_clipcode ) { return BBOX_TRIVIAL_ACCEPT; } return BBOX_MUST_CLIP_XY; } static int R_AliasCheckBBox (const entity_t *currententity) { unsigned long ccodes[2] = { 0, 0 }; ccodes[0] = R_AliasCheckFrameBBox( r_thisframe, aliasworldtransform ); /* ** non-lerping model */ if ( currententity->backlerp == 0 ) { if ( ccodes[0] == BBOX_TRIVIAL_ACCEPT ) return BBOX_TRIVIAL_ACCEPT; else if ( ccodes[0] & BBOX_TRIVIAL_REJECT ) return BBOX_TRIVIAL_REJECT; else return ( ccodes[0] & ~BBOX_TRIVIAL_REJECT ); } ccodes[1] = R_AliasCheckFrameBBox( r_lastframe, aliasoldworldtransform ); if ( ( ccodes[0] | ccodes[1] ) == BBOX_TRIVIAL_ACCEPT ) return BBOX_TRIVIAL_ACCEPT; else if ( ( ccodes[0] & ccodes[1] ) & BBOX_TRIVIAL_REJECT ) return BBOX_TRIVIAL_REJECT; else return ( ccodes[0] | ccodes[1] ) & ~BBOX_TRIVIAL_REJECT; } /* ================ R_AliasTransformVector ================ */ static void R_AliasTransformVector(const vec3_t in, vec3_t out, const float xf[3][4] ) { out[0] = DotProduct(in, xf[0]) + xf[0][3]; out[1] = DotProduct(in, xf[1]) + xf[1][3]; out[2] = DotProduct(in, xf[2]) + xf[2][3]; } /* ================ R_AliasPreparePoints General clipped case ================ */ static void R_AliasPreparePoints (const entity_t *currententity, finalvert_t *verts, const finalvert_t *verts_max) { int i; dstvert_t *pstverts; dtriangle_t *ptri; finalvert_t *pfv[3]; if ((verts + s_pmdl->num_xyz) >= verts_max) { r_outofverts = true; return; } R_AliasTransformFinalVerts(currententity, s_pmdl->num_xyz, verts, // destination for transformed verts r_lastframe->verts, // verts from the last frame r_thisframe->verts // verts from this frame ); // clip and draw all triangles // pstverts = (dstvert_t *)((byte *)s_pmdl + s_pmdl->ofs_st); ptri = (dtriangle_t *)((byte *)s_pmdl + s_pmdl->ofs_tris); if ( ( currententity->flags & RF_WEAPONMODEL ) && ( r_lefthand->value == 1.0F ) ) { for (i=0 ; inum_tris ; i++, ptri++) { pfv[0] = &verts[ptri->index_xyz[0]]; pfv[1] = &verts[ptri->index_xyz[1]]; pfv[2] = &verts[ptri->index_xyz[2]]; if ( pfv[0]->flags & pfv[1]->flags & pfv[2]->flags ) continue; // completely clipped // insert s/t coordinates pfv[0]->cv.s = pstverts[ptri->index_st[0]].s << SHIFT16XYZ; pfv[0]->cv.t = pstverts[ptri->index_st[0]].t << SHIFT16XYZ; pfv[1]->cv.s = pstverts[ptri->index_st[1]].s << SHIFT16XYZ; pfv[1]->cv.t = pstverts[ptri->index_st[1]].t << SHIFT16XYZ; pfv[2]->cv.s = pstverts[ptri->index_st[2]].s << SHIFT16XYZ; pfv[2]->cv.t = pstverts[ptri->index_st[2]].t << SHIFT16XYZ; if ( ! (pfv[0]->flags | pfv[1]->flags | pfv[2]->flags) ) { // totally unclipped R_DrawTriangle(currententity, pfv[2], pfv[1], pfv[0]); } else { R_AliasClipTriangle(currententity, pfv[2], pfv[1], pfv[0]); } } } else { for (i=0 ; inum_tris ; i++, ptri++) { pfv[0] = &verts[ptri->index_xyz[0]]; pfv[1] = &verts[ptri->index_xyz[1]]; pfv[2] = &verts[ptri->index_xyz[2]]; if ( pfv[0]->flags & pfv[1]->flags & pfv[2]->flags ) continue; // completely clipped // insert s/t coordinates pfv[0]->cv.s = pstverts[ptri->index_st[0]].s << SHIFT16XYZ; pfv[0]->cv.t = pstverts[ptri->index_st[0]].t << SHIFT16XYZ; pfv[1]->cv.s = pstverts[ptri->index_st[1]].s << SHIFT16XYZ; pfv[1]->cv.t = pstverts[ptri->index_st[1]].t << SHIFT16XYZ; pfv[2]->cv.s = pstverts[ptri->index_st[2]].s << SHIFT16XYZ; pfv[2]->cv.t = pstverts[ptri->index_st[2]].t << SHIFT16XYZ; if ( ! (pfv[0]->flags | pfv[1]->flags | pfv[2]->flags) ) { // totally unclipped R_DrawTriangle(currententity, pfv[0], pfv[1], pfv[2]); } else { // partially clipped R_AliasClipTriangle(currententity, pfv[0], pfv[1], pfv[2]); } } } } /* ================ R_AliasSetUpTransform ================ */ static void R_AliasSetUpTransform(const entity_t *currententity) { int i; static float viewmatrix[3][4]; // TODO: should really be stored with the entity instead of being reconstructed // TODO: should use a look-up table // TODO: could cache lazily, stored in the entity // // AngleVectors never change angles, we can convert from const AngleVectors((float *)currententity->angles, s_alias_forward, s_alias_right, s_alias_up ); // TODO: can do this with simple matrix rearrangement memset( aliasworldtransform, 0, sizeof( aliasworldtransform ) ); memset( aliasoldworldtransform, 0, sizeof( aliasworldtransform ) ); for (i=0 ; i<3 ; i++) { aliasoldworldtransform[i][0] = aliasworldtransform[i][0] = s_alias_forward[i]; aliasoldworldtransform[i][0] = aliasworldtransform[i][1] = -s_alias_right[i]; aliasoldworldtransform[i][0] = aliasworldtransform[i][2] = s_alias_up[i]; } aliasworldtransform[0][3] = currententity->origin[0]-r_origin[0]; aliasworldtransform[1][3] = currententity->origin[1]-r_origin[1]; aliasworldtransform[2][3] = currententity->origin[2]-r_origin[2]; aliasoldworldtransform[0][3] = currententity->oldorigin[0]-r_origin[0]; aliasoldworldtransform[1][3] = currententity->oldorigin[1]-r_origin[1]; aliasoldworldtransform[2][3] = currententity->oldorigin[2]-r_origin[2]; // FIXME: can do more efficiently than full concatenation // memcpy( rotationmatrix, t2matrix, sizeof( rotationmatrix ) ); // R_ConcatTransforms (t2matrix, tmatrix, rotationmatrix); // TODO: should be global, set when vright, etc., set VectorCopy (vright, viewmatrix[0]); VectorCopy (vup, viewmatrix[1]); VectorInverse (viewmatrix[1]); VectorCopy (vpn, viewmatrix[2]); if ( currententity->flags & RF_WEAPONMODEL ) { viewmatrix[0][3] = 0; viewmatrix[1][3] = 0; viewmatrix[2][3] = sw_gunzposition->value; } else { viewmatrix[0][3] = 0; viewmatrix[1][3] = 0; viewmatrix[2][3] = 0; } // memcpy( aliasworldtransform, rotationmatrix, sizeof( aliastransform ) ); R_ConcatTransforms (viewmatrix, aliasworldtransform, aliastransform); aliasworldtransform[0][3] = currententity->origin[0]; aliasworldtransform[1][3] = currententity->origin[1]; aliasworldtransform[2][3] = currententity->origin[2]; aliasoldworldtransform[0][3] = currententity->oldorigin[0]; aliasoldworldtransform[1][3] = currententity->oldorigin[1]; aliasoldworldtransform[2][3] = currententity->oldorigin[2]; } /* ================ R_AliasTransformFinalVerts ================ */ static void R_AliasTransformFinalVerts(const entity_t *currententity, int numpoints, finalvert_t *fv, dtrivertx_t *oldv, dtrivertx_t *newv ) { int i; for ( i = 0; i < numpoints; i++, fv++, oldv++, newv++ ) { float lightcos; const float *plightnormal; vec3_t lerped_vert; lerped_vert[0] = r_lerp_move[0] + oldv->v[0]*r_lerp_backv[0] + newv->v[0]*r_lerp_frontv[0]; lerped_vert[1] = r_lerp_move[1] + oldv->v[1]*r_lerp_backv[1] + newv->v[1]*r_lerp_frontv[1]; lerped_vert[2] = r_lerp_move[2] + oldv->v[2]*r_lerp_backv[2] + newv->v[2]*r_lerp_frontv[2]; plightnormal = r_avertexnormals[newv->lightnormalindex]; // added double damage shell if ( currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM) ) { lerped_vert[0] += plightnormal[0] * POWERSUIT_SCALE; lerped_vert[1] += plightnormal[1] * POWERSUIT_SCALE; lerped_vert[2] += plightnormal[2] * POWERSUIT_SCALE; } fv->xyz[0] = DotProduct(lerped_vert, aliastransform[0]) + aliastransform[0][3]; fv->xyz[1] = DotProduct(lerped_vert, aliastransform[1]) + aliastransform[1][3]; fv->xyz[2] = DotProduct(lerped_vert, aliastransform[2]) + aliastransform[2][3]; fv->flags = 0; // lighting lightcos = DotProduct (plightnormal, r_plightvec); if (lightcos < 0) { int j; for(j=0; j<3; j++) { int temp; temp = r_ambientlight[j]; temp += (r_shadelight[j] * lightcos); // clamp; because we limited the minimum ambient and shading light, we // don't have to clamp low light, just bright if (temp < 0) temp = 0; fv->cv.l[j] = temp; } } else memcpy(fv->cv.l, r_ambientlight, sizeof(light3_t)); if ( fv->xyz[2] < ALIAS_Z_CLIP_PLANE ) { fv->flags |= ALIAS_Z_CLIP; } else { R_AliasProjectAndClipTestFinalVert( fv ); } } } /* ================ R_AliasProjectAndClipTestFinalVert ================ */ void R_AliasProjectAndClipTestFinalVert( finalvert_t *fv ) { float zi; float x, y, z; // project points x = fv->xyz[0]; y = fv->xyz[1]; z = fv->xyz[2]; zi = 1.0 / z; fv->cv.zi = zi * s_ziscale; fv->cv.u = (x * aliasxscale * zi) + aliasxcenter; fv->cv.v = (y * aliasyscale * zi) + aliasycenter; if (fv->cv.u < r_refdef.aliasvrect.x) fv->flags |= ALIAS_LEFT_CLIP; if (fv->cv.v < r_refdef.aliasvrect.y) fv->flags |= ALIAS_TOP_CLIP; if (fv->cv.u > r_refdef.aliasvrectright) fv->flags |= ALIAS_RIGHT_CLIP; if (fv->cv.v > r_refdef.aliasvrectbottom) fv->flags |= ALIAS_BOTTOM_CLIP; } /* =============== R_AliasSetupSkin =============== */ static qboolean R_AliasSetupSkin(const entity_t *currententity, const model_t *currentmodel) { image_t *pskindesc; if (currententity->skin) { pskindesc = currententity->skin; } else { int skinnum; skinnum = currententity->skinnum; if ((skinnum >= s_pmdl->num_skins) || (skinnum < 0)) { R_Printf(PRINT_ALL, "%s %s: no such skin # %d\n", __func__, currentmodel->name, skinnum); skinnum = 0; } pskindesc = currentmodel->skins[skinnum]; } if ( !pskindesc ) return false; r_affinetridesc.pskin = pskindesc->pixels[0]; r_affinetridesc.skinwidth = pskindesc->asset_width; r_affinetridesc.skinheight = pskindesc->asset_height; r_affinetridesc.scalewidth = (float)pskindesc->asset_width / s_pmdl->skinwidth; r_affinetridesc.scaleheight = (float)pskindesc->asset_height / s_pmdl->skinheight; return true; } /* ================ R_AliasSetupLighting FIXME: put lighting into tables ================ */ static void R_AliasSetupLighting(entity_t *currententity) { const vec3_t lightvec = {-1, 0, 0}; vec3_t light; int i; // all components of light should be identical in software if ( currententity->flags & RF_FULLBRIGHT ) { for (i=0 ; i<3 ; i++) light[i] = 1.0; } else { R_LightPoint (currententity, currententity->origin, light); } // save off light value for server to look at (BIG HACK!) if ( currententity->flags & RF_WEAPONMODEL ) r_lightlevel->value = 150.0 * light[0]; if ( currententity->flags & RF_MINLIGHT ) { for (i=0 ; i<3 ; i++) if (light[i] < 0.1) light[i] = 0.1; } if ( currententity->flags & RF_GLOW ) { // bonus items will pulse with time float scale; scale = 0.1 * sin(r_newrefdef.time*7); for (i=0 ; i<3 ; i++) { float min; min = light[i] * 0.8; light[i] += scale; if (light[i] < min) light[i] = min; } } if(r_colorlight->value == 0) { float temp = (light[0] + light[1] + light[2]) / 3.0; light[0] = light[1] = light[2] = temp; } for(i=0; i<3; i++) { int j; j = light[i] * 255; r_ambientlight[i] = j; r_shadelight[i] = j; // clamp lighting so it doesn't overbright as much if (r_ambientlight[i] > 128) r_ambientlight[i] = 128; if (r_ambientlight[i] + r_shadelight[i] > 192) r_shadelight[i] = 192 - r_ambientlight[i]; // guarantee that no vertex will ever be lit below LIGHT_MIN, so we don't have // to clamp off the bottom if (r_ambientlight[i] < LIGHT_MIN) r_ambientlight[i] = LIGHT_MIN; r_ambientlight[i] = (255 - r_ambientlight[i]) << VID_CBITS; if (r_shadelight[i] < 0) r_shadelight[i] = 0; r_shadelight[i] *= VID_GRADES; } // rotate the lighting vector into the model's frame of reference r_plightvec[0] = DotProduct( lightvec, s_alias_forward ); r_plightvec[1] = -DotProduct( lightvec, s_alias_right ); r_plightvec[2] = DotProduct( lightvec, s_alias_up ); } /* ================= R_AliasSetupFrames ================= */ static void R_AliasSetupFrames(const entity_t *currententity, const model_t *currentmodel, dmdl_t *pmdl) { int thisframe = currententity->frame; int lastframe = currententity->oldframe; if ( ( thisframe >= pmdl->num_frames ) || ( thisframe < 0 ) ) { R_Printf(PRINT_ALL, "%s %s: no such thisframe %d\n", __func__, currentmodel->name, thisframe); thisframe = 0; } if ( ( lastframe >= pmdl->num_frames ) || ( lastframe < 0 ) ) { R_Printf(PRINT_ALL, "%s %s: no such lastframe %d\n", __func__, currentmodel->name, lastframe); lastframe = 0; } r_thisframe = (daliasframe_t *)((byte *)pmdl + pmdl->ofs_frames + thisframe * pmdl->framesize); r_lastframe = (daliasframe_t *)((byte *)pmdl + pmdl->ofs_frames + lastframe * pmdl->framesize); } /* ** R_AliasSetUpLerpData ** ** Precomputes lerp coefficients used for the whole frame. */ static void R_AliasSetUpLerpData(entity_t *currententity, dmdl_t *pmdl, float backlerp) { float frontlerp; vec3_t translation, vectors[3]; int i; frontlerp = 1.0F - backlerp; /* ** convert entity's angles into discrete vectors for R, U, and F */ AngleVectors (currententity->angles, vectors[0], vectors[1], vectors[2]); /* ** translation is the vector from last position to this position */ VectorSubtract (currententity->oldorigin, currententity->origin, translation); /* ** move should be the delta back to the previous frame * backlerp */ r_lerp_move[0] = DotProduct(translation, vectors[0]); // forward r_lerp_move[1] = -DotProduct(translation, vectors[1]); // left r_lerp_move[2] = DotProduct(translation, vectors[2]); // up VectorAdd( r_lerp_move, r_lastframe->translate, r_lerp_move ); for (i=0 ; i<3 ; i++) { r_lerp_move[i] = backlerp*r_lerp_move[i] + frontlerp * r_thisframe->translate[i]; } for (i=0 ; i<3 ; i++) { r_lerp_frontv[i] = frontlerp * r_thisframe->scale[i]; r_lerp_backv[i] = backlerp * r_lastframe->scale[i]; } } finalvert_t *finalverts = NULL, *finalverts_max = NULL; extern void (*d_pdrawspans)(const entity_t *currententity, spanpackage_t *pspanpackage); void R_PolysetDrawSpans8_Opaque(const entity_t *currententity, spanpackage_t *pspanpackage); void R_PolysetDrawSpans8_33(const entity_t *currententity, spanpackage_t *pspanpackage); void R_PolysetDrawSpans8_66(const entity_t *currententity, spanpackage_t *pspanpackage); void R_PolysetDrawSpansConstant8_33(const entity_t *currententity, spanpackage_t *pspanpackage); void R_PolysetDrawSpansConstant8_66(const entity_t *currententity, spanpackage_t *pspanpackage); /* ================ R_AliasDrawModel ================ */ void R_AliasDrawModel(entity_t *currententity, const model_t *currentmodel) { s_pmdl = (dmdl_t *)currentmodel->extradata; if ( r_lerpmodels->value == 0 ) currententity->backlerp = 0; float oldAliasxscale = aliasxscale; float oldAliasyscale = aliasyscale; if ( currententity->flags & RF_WEAPONMODEL ) { if ( r_lefthand->value == 2.0F ) { return; } if (r_gunfov->value >= 0) { float gunfov = 2 * tan((float)r_gunfov->value / 360 * M_PI); aliasxscale = ((float)r_refdef.vrect.width / gunfov) * r_aliasuvscale; aliasyscale = aliasxscale; } if ( r_lefthand->value == 1.0F ) aliasxscale = -aliasxscale; } /* ** we have to set our frame pointers and transformations before ** doing any real work */ R_AliasSetupFrames(currententity, currentmodel, s_pmdl); R_AliasSetUpTransform(currententity); // see if the bounding box lets us trivially reject, also sets // trivial accept status if ( R_AliasCheckBBox(currententity) == BBOX_TRIVIAL_REJECT ) { if ( currententity->flags & RF_WEAPONMODEL ) { aliasxscale = oldAliasxscale; aliasyscale = oldAliasyscale; } return; } // set up the skin and verify it exists if ( !R_AliasSetupSkin(currententity, currentmodel) ) { R_Printf(PRINT_ALL, "R_AliasDrawModel %s: NULL skin found\n", currentmodel->name); aliasxscale = oldAliasxscale; aliasyscale = oldAliasyscale; return; } r_amodels_drawn++; R_AliasSetupLighting(currententity); /* ** select the proper span routine based on translucency */ // added double damage shell // reordered to handle blending if ( currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM) ) { int color; // added double color = currententity->flags & ( RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE | RF_SHELL_HALF_DAM); // reordered, new shells after old shells (so they get overriden) if ( color == RF_SHELL_RED ) r_aliasblendcolor = SHELL_RED_COLOR; else if ( color == RF_SHELL_GREEN ) r_aliasblendcolor = SHELL_GREEN_COLOR; else if ( color == RF_SHELL_BLUE ) r_aliasblendcolor = SHELL_BLUE_COLOR; else if ( color == (RF_SHELL_RED | RF_SHELL_GREEN) ) r_aliasblendcolor = SHELL_RG_COLOR; else if ( color == (RF_SHELL_RED | RF_SHELL_BLUE) ) r_aliasblendcolor = SHELL_RB_COLOR; else if ( color == (RF_SHELL_BLUE | RF_SHELL_GREEN) ) r_aliasblendcolor = SHELL_BG_COLOR; // added this .. it's yellowish else if ( color == (RF_SHELL_DOUBLE) ) r_aliasblendcolor = SHELL_DOUBLE_COLOR; else if ( color == (RF_SHELL_HALF_DAM) ) r_aliasblendcolor = SHELL_HALF_DAM_COLOR; else r_aliasblendcolor = SHELL_WHITE_COLOR; if ( currententity->alpha > 0.33 ) d_pdrawspans = R_PolysetDrawSpansConstant8_66; else d_pdrawspans = R_PolysetDrawSpansConstant8_33; } else if ( currententity->flags & RF_TRANSLUCENT ) { if ( currententity->alpha > 0.66 ) d_pdrawspans = R_PolysetDrawSpans8_Opaque; else if ( currententity->alpha > 0.33 ) d_pdrawspans = R_PolysetDrawSpans8_66; else d_pdrawspans = R_PolysetDrawSpans8_33; } else { d_pdrawspans = R_PolysetDrawSpans8_Opaque; } /* ** compute this_frame and old_frame addresses */ R_AliasSetUpLerpData(currententity, s_pmdl, currententity->backlerp); if (currententity->flags & RF_DEPTHHACK) s_ziscale = (float)0x8000 * (float)SHIFT16XYZ_MULT * 3.0; else s_ziscale = (float)0x8000 * (float)SHIFT16XYZ_MULT; R_AliasPreparePoints(currententity, finalverts, finalverts_max); if ( currententity->flags & RF_WEAPONMODEL ) { aliasxscale = oldAliasxscale; aliasyscale = oldAliasyscale; } } yquake2-QUAKE2_8_40/src/client/refresh/soft/sw_bsp.c000066400000000000000000000343721465112212000222410ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sw_bsp.c #include "header/local.h" // // current entity info // vec3_t modelorg; // modelorg is the viewpoint reletive to // the currently rendering entity vec3_t r_entorigin; // the currently rendering entity in world // coordinates static float entity_rotation[3][3]; typedef enum {touchessolid, drawnode, nodrawnode} solidstate_t; #define MAX_BMODEL_VERTS 500 // 6K #define MAX_BMODEL_EDGES 1000 // 12K static int numbverts, numbedges; static mvertex_t bverts[MAX_BMODEL_VERTS]; static bedge_t bedges[MAX_BMODEL_EDGES]; //=========================================================================== /* ================ R_EntityRotate ================ */ static void R_EntityRotate (vec3_t vec) { vec3_t tvec; VectorCopy (vec, tvec); vec[0] = DotProduct (entity_rotation[0], tvec); vec[1] = DotProduct (entity_rotation[1], tvec); vec[2] = DotProduct (entity_rotation[2], tvec); } /* ================ R_RotateBmodel ================ */ void R_RotateBmodel(const entity_t *currententity) { float angle, s, c, temp1[3][3], temp2[3][3], temp3[3][3]; // TODO: should use a look-up table // TODO: should really be stored with the entity instead of being reconstructed // TODO: could cache lazily, stored in the entity // TODO: share work with R_SetUpAliasTransform // yaw angle = currententity->angles[YAW]; angle = angle * M_PI*2 / 360; s = sin(angle); c = cos(angle); temp1[0][0] = c; temp1[0][1] = s; temp1[0][2] = 0; temp1[1][0] = -s; temp1[1][1] = c; temp1[1][2] = 0; temp1[2][0] = 0; temp1[2][1] = 0; temp1[2][2] = 1; // pitch angle = currententity->angles[PITCH]; angle = angle * M_PI*2 / 360; s = sin(angle); c = cos(angle); temp2[0][0] = c; temp2[0][1] = 0; temp2[0][2] = -s; temp2[1][0] = 0; temp2[1][1] = 1; temp2[1][2] = 0; temp2[2][0] = s; temp2[2][1] = 0; temp2[2][2] = c; R_ConcatRotations (temp2, temp1, temp3); // roll angle = currententity->angles[ROLL]; angle = angle * M_PI*2 / 360; s = sin(angle); c = cos(angle); temp1[0][0] = 1; temp1[0][1] = 0; temp1[0][2] = 0; temp1[1][0] = 0; temp1[1][1] = c; temp1[1][2] = s; temp1[2][0] = 0; temp1[2][1] = -s; temp1[2][2] = c; R_ConcatRotations (temp1, temp3, entity_rotation); // // rotate modelorg and the transformation matrix // R_EntityRotate (modelorg); R_EntityRotate (vpn); R_EntityRotate (vright); R_EntityRotate (vup); R_TransformFrustum (); } /* ================ R_RecursiveClipBPoly Clip a bmodel poly down the world bsp tree ================ */ static void R_RecursiveClipBPoly(entity_t *currententity, bedge_t *pedges, mnode_t *pnode, msurface_t *psurf) { bedge_t *psideedges[2], *pnextedge; int i, side, lastside; cplane_t *splitplane, tplane; mvertex_t *pvert, *plastvert, *ptvert; mnode_t *pn; mvertex_t *prevclipvert = NULL; psideedges[0] = psideedges[1] = NULL; // transform the BSP plane into model space // FIXME: cache these? splitplane = pnode->plane; tplane.dist = splitplane->dist - DotProduct(r_entorigin, splitplane->normal); tplane.normal[0] = DotProduct (entity_rotation[0], splitplane->normal); tplane.normal[1] = DotProduct (entity_rotation[1], splitplane->normal); tplane.normal[2] = DotProduct (entity_rotation[2], splitplane->normal); // clip edges to BSP plane for (; pedges; pedges = pnextedge) { float dist, lastdist; bedge_t *ptedge; pnextedge = pedges->pnext; // set the status for the last point as the previous point // FIXME: cache this stuff somehow? plastvert = pedges->v[0]; lastdist = DotProduct (plastvert->position, tplane.normal) - tplane.dist; if (lastdist >= 0) lastside = 0; else lastside = 1; pvert = pedges->v[1]; dist = DotProduct (pvert->position, tplane.normal) - tplane.dist; if (dist >= 0) side = 0; else side = 1; if (side != lastside) { float frac; // clipped if (numbverts >= MAX_BMODEL_VERTS) return; // generate the clipped vertex frac = lastdist / (lastdist - dist); ptvert = &bverts[numbverts++]; ptvert->position[0] = plastvert->position[0] + frac * (pvert->position[0] - plastvert->position[0]); ptvert->position[1] = plastvert->position[1] + frac * (pvert->position[1] - plastvert->position[1]); ptvert->position[2] = plastvert->position[2] + frac * (pvert->position[2] - plastvert->position[2]); // split into two edges, one on each side, and remember entering // and exiting points // FIXME: share the clip edge by having a winding direction flag? if (numbedges + 4 > MAX_BMODEL_EDGES) { R_Printf(PRINT_ALL,"Out of edges for bmodel\n"); return; } // outside: last vert, clip vert ptedge = &bedges[numbedges++]; ptedge->pnext = psideedges[lastside]; psideedges[lastside] = ptedge; ptedge->v[0] = plastvert; ptedge->v[1] = ptvert; // each two clipped verts we get a clipped-off contour; // connect the verts by two edges (one per side) // going in opposite directions // this fixes surface 0 of model *50 (fan) in mine2: // teleport 1231 770 -579 if (prevclipvert) { ptedge = &bedges[numbedges++]; ptedge->pnext = psideedges[lastside]; psideedges[lastside] = ptedge; ptedge->v[0] = ptvert; ptedge->v[1] = prevclipvert; ptedge = &bedges[numbedges++]; ptedge->pnext = psideedges[side]; psideedges[side] = ptedge; ptedge->v[0] = prevclipvert; ptedge->v[1] = ptvert; prevclipvert = NULL; } else prevclipvert = ptvert; // inside: clip vert, current vert ptedge = &bedges[numbedges++]; ptedge->pnext = psideedges[side]; psideedges[side] = ptedge; ptedge->v[0] = ptvert; ptedge->v[1] = pvert; } else { // add the edge to the appropriate side pedges->pnext = psideedges[side]; psideedges[side] = pedges; } } // draw or recurse further for (i=0 ; i<2 ; i++) { if (psideedges[i]) { // draw if we've reached a non-solid leaf, done if all that's left is a // solid leaf, and continue down the tree if it's not a leaf pn = pnode->children[i]; // we're done with this branch if the node or leaf isn't in the PVS if (pn->visframe == r_visframecount) { if (pn->contents != CONTENTS_NODE) { if (pn->contents != CONTENTS_SOLID) { int r_currentbkey; if (!R_AreaVisible(r_newrefdef.areabits, (mleaf_t *)pn)) continue; r_currentbkey = ((mleaf_t *)pn)->key; R_RenderBmodelFace(currententity, psideedges[i], psurf, r_currentbkey); } } else { R_RecursiveClipBPoly(currententity, psideedges[i], pnode->children[i], psurf); } } } } } /* ================ R_DrawSolidClippedSubmodelPolygons Bmodel crosses multiple leafs ================ */ void R_DrawSolidClippedSubmodelPolygons(entity_t *currententity, const model_t *currentmodel, mnode_t *topnode) { int i; msurface_t *psurf; int numsurfaces; medge_t *pedges; // FIXME: use bounding-box-based frustum clipping info? psurf = ¤tmodel->surfaces[currentmodel->firstmodelsurface]; numsurfaces = currentmodel->nummodelsurfaces; pedges = currentmodel->edges; for (i=0 ; iplane; 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))) continue; // FIXME: use bounding-box-based frustum clipping info? // copy the edges to bedges, flipping if necessary so always // clockwise winding // FIXME: if edges and vertices get caches, these assignments must move // outside the loop, and overflow checking must be done here numbverts = numbedges = 0; pbedge = &bedges[numbedges]; numbedges += psurf->numedges; for (j=0 ; jnumedges ; j++) { int lindex; lindex = currentmodel->surfedges[psurf->firstedge+j]; if (lindex > 0) { medge_t *pedge; pedge = &pedges[lindex]; pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[0]]; pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[1]]; } else { medge_t *pedge; lindex = -lindex; pedge = &pedges[lindex]; pbedge[j].v[0] = &r_pcurrentvertbase[pedge->v[1]]; pbedge[j].v[1] = &r_pcurrentvertbase[pedge->v[0]]; } pbedge[j].pnext = &pbedge[j+1]; } pbedge[j-1].pnext = NULL; // mark end of edges if ( !( psurf->texinfo->flags & ( SURF_TRANS66 | SURF_TRANS33 ) )) { R_RecursiveClipBPoly(currententity, pbedge, topnode, psurf); } else { R_RenderBmodelFace(currententity, pbedge, psurf, ((mleaf_t *)topnode)->key); } } } /* ================ R_DrawSubmodelPolygons All in one leaf ================ */ void R_DrawSubmodelPolygons(entity_t *currententity, const model_t *currentmodel, int clipflags, mnode_t *topnode) { int i; msurface_t *psurf; int numsurfaces; // FIXME: use bounding-box-based frustum clipping info? psurf = ¤tmodel->surfaces[currentmodel->firstmodelsurface]; numsurfaces = currentmodel->nummodelsurfaces; for (i=0 ; iplane; dot = DotProduct (modelorg, pplane->normal) - pplane->dist; // draw the polygon if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { r_currentkey = ((mleaf_t *)topnode)->key; // FIXME: use bounding-box-based frustum clipping info? R_RenderFace(currententity, currentmodel, psurf, clipflags, true); } } } /* ================ R_RecursiveWorldNode ================ */ static void R_RecursiveWorldNode (entity_t *currententity, const model_t *currentmodel, mnode_t *node, int clipflags, qboolean insubmodel) { int c; vec3_t acceptpt, rejectpt; mleaf_t *pleaf; if (node->contents == CONTENTS_SOLID) { return; // solid } if (node->visframe != r_visframecount) { return; } if (r_cull->value && R_CullBox(node->minmaxs, node->minmaxs + 3, frustum)) { return; } // cull the clipping planes if not trivial accept // FIXME: the compiler is doing a lousy job of optimizing here; it could be // twice as fast in ASM if (clipflags) { int i; for (i=0 ; i<4 ; i++) { int *pindex; float d; if (! (clipflags & (1<minmaxs[pindex[0]]; rejectpt[1] = node->minmaxs[pindex[1]]; rejectpt[2] = node->minmaxs[pindex[2]]; d = DotProduct (rejectpt, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; if (d <= 0) return; acceptpt[0] = node->minmaxs[pindex[3+0]]; acceptpt[1] = node->minmaxs[pindex[3+1]]; acceptpt[2] = node->minmaxs[pindex[3+2]]; d = DotProduct (acceptpt, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; if (d >= 0) clipflags &= ~(1<contents != CONTENTS_NODE) { msurface_t **mark; pleaf = (mleaf_t *)node; if (!R_AreaVisible(r_newrefdef.areabits, pleaf)) return; // not visible mark = pleaf->firstmarksurface; c = pleaf->nummarksurfaces; if (c) { do { (*mark)->visframe = r_framecount; mark++; } while (--c); } pleaf->key = r_currentkey; r_currentkey++; // all bmodels in a leaf share the same key } else { float dot; int side; cplane_t *plane; // node is just a decision point, so go down the apropriate sides // find which side of the node we are on plane = node->plane; switch (plane->type) { case PLANE_X: dot = modelorg[0] - plane->dist; break; case PLANE_Y: dot = modelorg[1] - plane->dist; break; case PLANE_Z: dot = modelorg[2] - plane->dist; break; default: dot = DotProduct (modelorg, plane->normal) - plane->dist; break; } if (dot >= 0) side = 0; else side = 1; // recurse down the children, front side first R_RecursiveWorldNode (currententity, currentmodel, node->children[side], clipflags, insubmodel); // draw stuff c = node->numsurfaces; if (c) { msurface_t *surf; surf = currentmodel->surfaces + node->firstsurface; if (dot < -BACKFACE_EPSILON) { do { if ((surf->flags & SURF_PLANEBACK) && (surf->visframe == r_framecount)) { R_RenderFace (currententity, currentmodel, surf, clipflags, insubmodel); } surf++; } while (--c); } else if (dot > BACKFACE_EPSILON) { do { if (!(surf->flags & SURF_PLANEBACK) && (surf->visframe == r_framecount)) { R_RenderFace (currententity, currentmodel, surf, clipflags, insubmodel); } surf++; } while (--c); } // all surfaces on the same node share the same sequence number r_currentkey++; } // recurse down the back side R_RecursiveWorldNode (currententity, currentmodel, node->children[!side], clipflags, insubmodel); } } /* ================ R_RenderWorld ================ */ void R_RenderWorld (entity_t *currententity) { const model_t *currentmodel = r_worldmodel; if (!r_drawworld->value) return; if ( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) return; // auto cycle the world frame for texture animation currententity->frame = (int)(r_newrefdef.time*2); VectorCopy (r_origin, modelorg); r_pcurrentvertbase = currentmodel->vertexes; R_RecursiveWorldNode (currententity, currentmodel, currentmodel->nodes, ALIAS_XY_CLIP_MASK, false); } yquake2-QUAKE2_8_40/src/client/refresh/soft/sw_draw.c000066400000000000000000000222771465112212000224130ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // draw.c #include "header/local.h" static image_t *draw_chars; // 8*8 graphic characters //============================================================================= /* ================ RE_Draw_FindPic ================ */ image_t * RE_Draw_FindPic (char *name) { return R_FindPic(name, (findimage_t)R_FindImage); } /* =============== Draw_InitLocal =============== */ void Draw_InitLocal (void) { draw_chars = R_FindPic ("conchars", (findimage_t)R_FindImage); if (!draw_chars) { ri.Sys_Error(ERR_FATAL, "%s: Couldn't load pics/conchars.pcx", __func__); } } /* ================ Draw_Char Draws one 8*8 graphics character It can be clipped to the top of the screen to allow the console to be smoothly scrolled off. ================ */ void RE_Draw_CharScaled(int x, int y, int c, float scale) { pixel_t *dest; byte *source, *pic_pixels; int drawline; int row, col, v, iscale, sscale, width, height; iscale = (int) scale; if (iscale < 1) return; c &= 255; if ((c&127) == 32) return; if (y <= -8) return; // totally off screen if ( ( y + 8 ) > vid_buffer_height ) // status text was missing in sw... return; row = c>>4; col = c&15; width = draw_chars->asset_width * iscale; height = draw_chars->asset_height * iscale; pic_pixels = Get_BestImageSize(draw_chars, &width, &height); sscale = width / draw_chars->asset_width; pic_pixels += ((row<<10) * sscale + (col<<3)) * sscale; if (y < 0) { // clipped drawline = 8 + y; pic_pixels -= width * y * sscale; y = 0; } else drawline = 8; dest = vid_buffer + y * vid_buffer_width + x; // clipped last lines if ((y + iscale * (drawline + 1)) > vid_buffer_height) { drawline = (vid_buffer_height - y) / scale; } VID_DamageBuffer(x, y); VID_DamageBuffer(x + (scale * 8), y + (scale * drawline)); drawline = drawline * scale; for (v=0 ; v < drawline; v++, dest += vid_buffer_width) { int f, fstep, u; int sv = v * height / (iscale * draw_chars->asset_height); source = pic_pixels + sv * width; f = 0; fstep = (width << SHIFT16XYZ) / (scale * draw_chars->asset_width); for (u=0 ; u < (8 * iscale) ; u++) { if (source[f>>16] != TRANSPARENT_COLOR) { dest[u] = source[f>>16]; } f += fstep; } } } /* ============= RE_Draw_GetPicSize ============= */ void RE_Draw_GetPicSize (int *w, int *h, char *name) { image_t *image; image = R_FindPic (name, (findimage_t)R_FindImage); if (!image) { *w = *h = -1; return; } *w = image->asset_width; *h = image->asset_height; } /* ============= RE_Draw_StretchPicImplementation ============= */ static void RE_Draw_StretchPicImplementation (int x, int y, int w, int h, const image_t *pic) { pixel_t *dest; byte *source; int height; int skip; int pic_height, pic_width; byte *pic_pixels; if ((x < 0) || (x + w > vid_buffer_width) || (y + h > vid_buffer_height)) { R_Printf(PRINT_ALL, "%s: bad coordinates %dx%d[%dx%d]", __func__, x, y, w, h); return; } VID_DamageBuffer(x, y); VID_DamageBuffer(x + w, y + h); height = h; if (y < 0) { skip = -y; height += y; y = 0; } else skip = 0; dest = vid_buffer + y * vid_buffer_width + x; pic_height = h; pic_width = w; pic_pixels = Get_BestImageSize(pic, &pic_width, &pic_height); if (!pic->transparent) { if (w == pic_width) { int v; for (v=0 ; v>16]; f += fstep; } if (picupscale > 1) { int i; int pu = Q_min(height-v, picupscale); pixel_t *dest_orig = dest; // copy first line to fill whole sector for (i=1; i < pu; i++) { // go to next line dest += vid_buffer_width; memcpy (dest, dest_orig, w); } // skip updated lines v += (picupscale - 1); } } } } else { source = pic_pixels; if (h == pic_height && w == pic_width) { int v; for (v = 0; v < pic_height; v++) { int u; for (u = 0; u < pic_width; u++) { if (source[u] != TRANSPARENT_COLOR) dest[u] = source[u]; } dest += vid_buffer_width; source += pic_width; } } else { int v; for (v=0 ; v>16] != TRANSPARENT_COLOR) { dest[u] = source[f>>16]; } f += fstep; } } } } } /* ============= RE_Draw_StretchPic ============= */ void RE_Draw_StretchPic (int x, int y, int w, int h, char *name) { image_t *pic; pic = R_FindPic (name, (findimage_t)R_FindImage); if (!pic) { R_Printf(PRINT_ALL, "Can't find pic: %s\n", name); return; } RE_Draw_StretchPicImplementation (x, y, w, h, pic); } /* ============= RE_Draw_StretchRaw ============= */ void RE_Draw_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int bits) { image_t pic; byte *image_scaled; // we have only one image size pic.mip_levels = 1; if (bits == 32) { image_scaled = malloc(cols * rows); R_Convert32To8bit(data, image_scaled, cols * rows, false); } else if (r_retexturing->value) { if (cols < (w / 3) || rows < (h / 3)) { image_scaled = malloc(cols * rows * 9); scale3x(data, image_scaled, cols, rows); cols = cols * 3; rows = rows * 3; } else { image_scaled = malloc(cols * rows * 4); scale2x(data, image_scaled, cols, rows); cols = cols * 2; rows = rows * 2; } } else { image_scaled = (byte *)data; } pic.pixels[0] = image_scaled; pic.width = cols; pic.height = rows; pic.asset_width = cols; pic.asset_height = rows; RE_Draw_StretchPicImplementation (x, y, w, h, &pic); if (r_retexturing->value) { free(image_scaled); } } /* ============= Draw_Pic ============= */ void RE_Draw_PicScaled(int x, int y, char *name, float scale) { image_t *pic; pic = R_FindPic (name, (findimage_t)R_FindImage); if (!pic) { R_Printf(PRINT_ALL, "Can't find pic: %s\n", name); return; } RE_Draw_StretchPicImplementation ( x, y, scale * pic->asset_width, scale * pic->asset_height, pic); } /* ============= RE_Draw_TileClear This repeats a 64*64 tile graphic to fill the screen around a sized down refresh window. ============= */ void RE_Draw_TileClear (int x, int y, int w, int h, char *name) { int i, j; byte *psrc; pixel_t *pdest; image_t *pic; int x2; if (x < 0) { w += x; x = 0; } if (y < 0) { h += y; y = 0; } if (x + w > vid_buffer_width) w = vid_buffer_width - x; if (y + h > vid_buffer_height) h = vid_buffer_height - y; if (w <= 0 || h <= 0) return; VID_DamageBuffer(x, y); VID_DamageBuffer(x + w, y + h); pic = R_FindPic (name, (findimage_t)R_FindImage); if (!pic) { R_Printf(PRINT_ALL, "Can't find pic: %s\n", name); return; } x2 = x + w; pdest = vid_buffer + y * vid_buffer_width; for (i=0 ; ipixels[0] + pic->width * ((i+y) % pic->height); for (j=x ; jwidth]; } } /* ============= RE_Draw_Fill Fills a box of pixels with a single color ============= */ void RE_Draw_Fill (int x, int y, int w, int h, int c) { pixel_t *dest; int v; if (x+w > vid_buffer_width) w = vid_buffer_width - x; if (y+h > vid_buffer_height) h = vid_buffer_height - y; if (x < 0) { w += x; x = 0; } if (y < 0) { h += y; y = 0; } if (w < 0 || h < 0) return; VID_DamageBuffer(x, y); VID_DamageBuffer(x + w, y + h); dest = vid_buffer + y * vid_buffer_width + x; for (v=0 ; v #include #include "header/local.h" /* the complex cases add new polys on most lines, so dont optimize for keeping them the same have multiple free span lists to try to get better coherence? low depth complexity -- 1 to 3 or so have a sentinal at both ends? */ // surfaces are generated in back to front order by the bsp, so if a surf // pointer is greater than another one, it should be drawn in front // surfaces[1] is the background, and is used as the active surface stack edge_t **newedges; edge_t **removeedges; espan_t *edge_basespans; espan_t *max_span_p; static espan_t *span_p; int r_currentkey; static shift20_t current_iv; static shift20_t edge_head_u_shift20, edge_tail_u_shift20; static void (*pdrawfunc)(void); static edge_t edge_head; static edge_t edge_tail; static edge_t edge_aftertail; static edge_t edge_sentinel; static float fv; static int miplevel; float scale_for_mip; static void R_GenerateSpans (void); static void R_GenerateSpansBackward (void); static void R_TrailingEdge (surf_t *surf, edge_t *edge); /* =============================================================================== EDGE SCANNING =============================================================================== */ /* ============== R_BeginEdgeFrame ============== */ void R_BeginEdgeFrame (void) { surfaces[1].spans = NULL; // no background spans yet surfaces[1].flags = SURF_DRAWBACKGROUND; // put the background behind everything in the world if (sw_draworder->value) { pdrawfunc = R_GenerateSpansBackward; surfaces[1].key = 0; r_currentkey = 1; } else { pdrawfunc = R_GenerateSpans; surfaces[1].key = 0x7FFfFFFF; r_currentkey = 0; } if ((r_refdef.vrectbottom - r_refdef.vrect.y) > 0) { memset(newedges + r_refdef.vrect.y, 0, (r_refdef.vrectbottom - r_refdef.vrect.y) * sizeof(edge_t*)); memset(removeedges + r_refdef.vrect.y, 0, (r_refdef.vrectbottom - r_refdef.vrect.y) * sizeof(edge_t*)); } } /* ============== R_InsertNewEdges Adds the edges in the linked list edgestoadd, adding them to the edges in the linked list edgelist. edgestoadd is assumed to be sorted on u, and non-empty (this is actually newedges[v]). edgelist is assumed to be sorted on u, with a sentinel at the end (actually, this is the active edge table starting at edge_head.next). ============== */ static void R_InsertNewEdges (edge_t *edgestoadd, edge_t *edgelist) { edge_t *next_edge; do { next_edge = edgestoadd->next; while (edgelist->u < edgestoadd->u) { edgelist = edgelist->next; } // insert edgestoadd before edgelist edgestoadd->next = edgelist; edgestoadd->prev = edgelist->prev; edgelist->prev->next = edgestoadd; edgelist->prev = edgestoadd; } while ((edgestoadd = next_edge) != NULL); } /* ============== R_RemoveEdges ============== */ static void R_RemoveEdges (edge_t *pedge) { do { pedge->next->prev = pedge->prev; pedge->prev->next = pedge->next; } while ((pedge = pedge->nextremove) != NULL); } /* ============== R_StepActiveU ============== */ static void R_StepActiveU (edge_t *pedge) { edge_t *pnext_edge, *pwedge; while (1) { pedge->u += pedge->u_step; if (pedge->u < pedge->prev->u) { if (pedge == &edge_aftertail) return; if (pedge->prev == &edge_head) { R_Printf(PRINT_ALL,"Already in head.\n"); } // push it back to keep it sorted pnext_edge = pedge->next; // pull the edge out of the edge list pedge->next->prev = pedge->prev; pedge->prev->next = pedge->next; // find out where the edge goes in the edge list pwedge = pedge->prev->prev; while (pwedge->u > pedge->u) { pwedge = pwedge->prev; } // put the edge back into the edge list pedge->next = pwedge->next; pedge->prev = pwedge; pedge->next->prev = pedge; pwedge->next = pedge; pedge = pnext_edge; if (pedge == &edge_tail) return; } else { pedge = pedge->next; } } } static void R_InsertSpan (surf_t *surf, shift20_t iu) { if (iu > surf->last_u) { espan_t *span; span = span_p++; span->u = surf->last_u; span->count = iu - span->u; span->v = current_iv; span->pnext = surf->spans; surf->spans = span; } } /* ============== R_CleanupSpan ============== */ static void R_CleanupSpan (void) { surf_t *surf; // now that we've reached the right edge of the screen, we're done with any // unfinished surfaces, so emit a span for whatever's on top surf = surfaces[1].next; R_InsertSpan (surf, edge_tail_u_shift20); // reset spanstate for all surfaces in the surface stack do { surf->spanstate = 0; surf = surf->next; } while (surf != &surfaces[1]); } /* ============== D_SurfSearchBackwards ============== */ static surf_t* D_SurfSearchBackwards(const surf_t *surf, surf_t *surf2) { do { do { surf2 = surf2->next; } while (surf->key < surf2->key); // if it's two surfaces on the same plane, the one that's already // active is in front, so keep going unless it's a bmodel // must be two bmodels in the same leaf; don't care which is really // in front, because they'll never be farthest anyway } while (surf->key == surf2->key && !surf->insubmodel); return surf2; } /* ============== R_LeadingEdgeBackwards ============== */ static void R_LeadingEdgeBackwards (const edge_t *edge) { surf_t *surf, *surf2; // it's adding a new surface in, so find the correct place surf = &surfaces[edge->surfs[1]]; // don't start a span if this is an inverted span, with the end // edge preceding the start edge (that is, we've already seen the // end edge) if (++surf->spanstate == 1) { surf2 = surfaces[1].next; // if it's two surfaces on the same plane, the one that's already // active is in front, so keep going unless it's a bmodel // must be two bmodels in the same leaf; don't care, because they'll // never be farthest anyway if (surf->key > surf2->key || (surf->insubmodel && (surf->key == surf2->key))) { shift20_t iu; // emit a span (obscures current top) iu = edge->u >> shift_size; R_InsertSpan (surf2, iu); // set last_u on the new span surf->last_u = iu; } else { surf2 = D_SurfSearchBackwards(surf, surf2); } // insert before surf2 surf->next = surf2; surf->prev = surf2->prev; surf2->prev->next = surf; surf2->prev = surf; } } /* ============== R_TrailingEdge ============== */ static void R_TrailingEdge (surf_t *surf, edge_t *edge) { // don't generate a span if this is an inverted span, with the end // edge preceding the start edge (that is, we haven't seen the // start edge yet) if (--surf->spanstate == 0) { if (surf == surfaces[1].next) { shift20_t iu; // emit a span (current top going away) iu = edge->u >> shift_size; R_InsertSpan (surf, iu); // set last_u on the surface below surf->next->last_u = iu; } surf->prev->next = surf->next; surf->next->prev = surf->prev; } } /* ============== D_SurfSearchForward ============== */ static surf_t* D_SurfSearchForward(const surf_t *surf, surf_t *surf2) { do { do { surf2 = surf2->next; } while (surf->key > surf2->key); // if it's two surfaces on the same plane, the one that's already // active is in front, so keep going unless it's a bmodel } while (surf->key == surf2->key && !surf->insubmodel); return surf2; } /* ============== R_LeadingEdgeSearch ============== */ static surf_t* R_LeadingEdgeSearch (const edge_t *edge, const surf_t *surf, surf_t *surf2) { float testzi, newzitop; do { float fu, newzi, newzibottom; surf2 = D_SurfSearchForward(surf, surf2); if (surf->key != surf2->key) return surf2; // must be two bmodels in the same leaf; sort on 1/z fu = (float)(edge->u - (1<d_ziorigin + fv*surf->d_zistepv + fu*surf->d_zistepu; newzibottom = newzi * 0.99; testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + fu*surf2->d_zistepu; if (newzibottom >= testzi) { return surf2; } newzitop = newzi * 1.01; } while(newzitop < testzi || surf->d_zistepu < surf2->d_zistepu); return surf2; } /* ============== R_InsertBeforeSurf ============== */ static void R_InsertBeforeSurf(surf_t *surf, surf_t *surf2) { surf->next = surf2; surf->prev = surf2->prev; surf2->prev->next = surf; surf2->prev = surf; } /* ============== R_EmitSpanBeforeTop ============== */ static void R_EmitSpanBeforeTop(const edge_t *edge, surf_t *surf, surf_t *surf2) { shift20_t iu; iu = edge->u >> shift_size; R_InsertSpan (surf2, iu); // set last_u on the new span surf->last_u = iu; // insert before surf2 R_InsertBeforeSurf(surf, surf2); } /* ============== R_LeadingEdge ============== */ static void R_LeadingEdge (const edge_t *edge) { if (edge->surfs[1]) { surf_t *surf; // it's adding a new surface in, so find the correct place surf = &surfaces[edge->surfs[1]]; // don't start a span if this is an inverted span, with the end // edge preceding the start edge (that is, we've already seen the // end edge) if (++surf->spanstate == 1) { surf_t *surf2; surf2 = surfaces[1].next; if (surf->key < surf2->key) { // emit a span (obscures current top) R_EmitSpanBeforeTop(edge, surf, surf2); return; } // if it's two surfaces on the same plane, the one that's already // active is in front, so keep going unless it's a bmodel if (surf->insubmodel && (surf->key == surf2->key)) { float fu, newzi, testzi, newzitop, newzibottom; // must be two bmodels in the same leaf; sort on 1/z fu = (float)(edge->u - (1<d_ziorigin + fv*surf->d_zistepv + fu*surf->d_zistepu; newzibottom = newzi * 0.99; testzi = surf2->d_ziorigin + fv*surf2->d_zistepv + fu*surf2->d_zistepu; if (newzibottom >= testzi) { // emit a span (obscures current top) R_EmitSpanBeforeTop(edge, surf, surf2); return; } newzitop = newzi * 1.01; if (newzitop >= testzi) { if (surf->d_zistepu >= surf2->d_zistepu) { // emit a span (obscures current top) R_EmitSpanBeforeTop(edge, surf, surf2); return; } } } surf2 = R_LeadingEdgeSearch (edge, surf, surf2); // insert before surf2 R_InsertBeforeSurf(surf, surf2); } } } /* ============== R_GenerateSpans ============== */ static void R_GenerateSpans (void) { edge_t *edge; surf_t *surf; // clear active surfaces to just the background surface surfaces[1].next = surfaces[1].prev = &surfaces[1]; surfaces[1].last_u = edge_head_u_shift20; // generate spans for (edge=edge_head.next ; edge != &edge_tail; edge=edge->next) { if (edge->surfs[0]) { // it has a left surface, so a surface is going away for this span surf = &surfaces[edge->surfs[0]]; R_TrailingEdge (surf, edge); if (!edge->surfs[1]) continue; } R_LeadingEdge (edge); } R_CleanupSpan (); } /* ============== R_GenerateSpansBackward ============== */ static void R_GenerateSpansBackward (void) { edge_t *edge; // clear active surfaces to just the background surface surfaces[1].next = surfaces[1].prev = &surfaces[1]; surfaces[1].last_u = edge_head_u_shift20; // generate spans for (edge=edge_head.next ; edge != &edge_tail; edge=edge->next) { if (edge->surfs[0]) R_TrailingEdge (&surfaces[edge->surfs[0]], edge); if (edge->surfs[1]) R_LeadingEdgeBackwards (edge); } R_CleanupSpan (); } static void D_DrawSurfaces (entity_t *currententity, surf_t *surface); /* ============== R_ScanEdges Input: newedges[] array this has links to edges, which have links to surfaces Output: Each surface has a linked list of its visible spans ============== */ void R_ScanEdges (entity_t *currententity, surf_t *surface) { shift20_t iv, bottom; surf_t *s; // clear the surface span pointers for (s = &surfaces[1] ; slast_u = 0; s->spans = NULL; } span_p = edge_basespans; if (span_p + r_refdef.vrect.width >= max_span_p) { // Need to more space r_outedgebasespans = true; return; } // clear active edges to just the background edges around the whole screen // FIXME: most of this only needs to be set up once edge_head.u = r_refdef.vrect.x << shift_size; edge_head_u_shift20 = edge_head.u >> shift_size; edge_head.u_step = 0; edge_head.prev = NULL; edge_head.next = &edge_tail; edge_head.surfs[0] = 0; edge_head.surfs[1] = 1; edge_tail.u = (r_refdef.vrectright << shift_size) + (1 << shift_size) - 1; edge_tail_u_shift20 = edge_tail.u >> shift_size; edge_tail.u_step = 0; edge_tail.prev = &edge_head; edge_tail.next = &edge_aftertail; edge_tail.surfs[0] = 1; edge_tail.surfs[1] = 0; edge_aftertail.u = -1; // force a move edge_aftertail.u_step = 0; edge_aftertail.next = &edge_sentinel; edge_aftertail.prev = &edge_tail; // FIXME: do we need this now that we clamp x in r_draw.c? edge_sentinel.u = INT_MAX; // make sure nothing sorts past this edge_sentinel.prev = &edge_aftertail; // // process all scan lines // bottom = r_refdef.vrectbottom - 1; for (iv=r_refdef.vrect.y ; iv= max_span_p) { // Draw stuff on screen D_DrawSurfaces (currententity, surface); // clear the surface span pointers for (s = &surfaces[1] ; sspans = NULL; span_p = edge_basespans; // Need to more space r_outedgebasespans = true; } if (removeedges[iv]) R_RemoveEdges (removeedges[iv]); if (edge_head.next != &edge_tail) R_StepActiveU (edge_head.next); } // do the last scan (no need to step or sort or remove on the last scan) current_iv = iv; fv = (float)iv; // mark that the head (background start) span is pre-included surfaces[1].spanstate = 1; // Flush span buffer if (newedges[iv]) // Update AET with GET event R_InsertNewEdges (newedges[iv], edge_head.next); // Update AET with GET event (*pdrawfunc) (); // draw whatever's left in the span list D_DrawSurfaces (currententity, surface); } /* ========================================================================= SURFACE FILLING ========================================================================= */ static msurface_t *pface; static surfcache_t *pcurrentcache; static vec3_t transformed_modelorg; static vec3_t world_transformed_modelorg; /* ============= D_MipLevelForScale ============= */ static int D_MipLevelForScale (float scale) { int lmiplevel = NUM_MIPS-1, i; for (i=0; i < NUM_MIPS-1; i ++) { if (scale >= d_scalemip[i]) { lmiplevel = i; break; } } if (lmiplevel < d_minmip) lmiplevel = d_minmip; return lmiplevel; } /* ============== D_FlatFillSurface Simple single color fill with no texture mapping ============== */ static void D_FlatFillSurface (surf_t *surf, pixel_t color) { espan_t *span; for (span=surf->spans ; span ; span=span->pnext) { pixel_t *pdest; pdest = d_viewbuffer + vid_buffer_width*span->v + span->u; memset(pdest, color&0xFF, span->count * sizeof(pixel_t)); } } /* ============== D_CalcGradients ============== */ static void D_CalcGradients (msurface_t *pface, float d_ziorigin, float d_zistepu, float d_zistepv) { float mipscale; vec3_t p_temp1; vec3_t p_saxis, p_taxis; float t; mipscale = 1.0 / (float)(1 << miplevel); TransformVector (pface->texinfo->vecs[0], p_saxis); TransformVector (pface->texinfo->vecs[1], p_taxis); t = xscaleinv * mipscale; d_sdivzstepu = p_saxis[0] * t; d_tdivzstepu = p_taxis[0] * t; t = yscaleinv * mipscale; d_sdivzstepv = -p_saxis[1] * t; d_tdivzstepv = -p_taxis[1] * t; d_sdivzorigin = p_saxis[2] * mipscale - xcenter * d_sdivzstepu - ycenter * d_sdivzstepv; d_tdivzorigin = p_taxis[2] * mipscale - xcenter * d_tdivzstepu - ycenter * d_tdivzstepv; VectorScale (transformed_modelorg, mipscale, p_temp1); t = SHIFT16XYZ_MULT * mipscale; sadjust = ((int)(DotProduct (p_temp1, p_saxis) * SHIFT16XYZ_MULT + 0.5)) - ((pface->texturemins[0] << SHIFT16XYZ) >> miplevel) + pface->texinfo->vecs[0][3]*t; tadjust = ((int)(DotProduct (p_temp1, p_taxis) * SHIFT16XYZ_MULT + 0.5)) - ((pface->texturemins[1] << SHIFT16XYZ) >> miplevel) + pface->texinfo->vecs[1][3]*t; // changing flow speed for non-warping textures. if (pface->texinfo->flags & SURF_FLOWING) { if(pface->texinfo->flags & SURF_WARP) sadjust += SHIFT16XYZ_MULT * (-128 * ( (r_newrefdef.time * 0.25) - (int)(r_newrefdef.time * 0.25) )); else sadjust += SHIFT16XYZ_MULT * (-128 * ( (r_newrefdef.time * 0.77) - (int)(r_newrefdef.time * 0.77) )); } // // -1 (-epsilon) so we never wander off the edge of the texture // bbextents = ((pface->extents[0] << SHIFT16XYZ) >> miplevel) - 1; bbextentt = ((pface->extents[1] << SHIFT16XYZ) >> miplevel) - 1; } /* ============== D_BackgroundSurf The grey background filler seen when there is a hole in the map ============== */ static void D_BackgroundSurf (surf_t *s) { D_FlatFillSurface (s, (int)sw_clearcolor->value & 0xFF); // set up a gradient for the background surface that places it // effectively at infinity distance from the viewpoint D_DrawZSpans (s->spans, -0.9, 0, 0); } /* ================= D_TurbulentSurf ================= */ static void D_TurbulentSurf(surf_t *s) { pface = s->msurf; miplevel = 0; cacheblock = pface->texinfo->image->pixels[0]; cachewidth = 64; if (s->insubmodel) { entity_t *currententity; vec3_t local_modelorg; // FIXME: we don't want to do all this for every polygon! // TODO: store once at start of frame currententity = s->entity; VectorSubtract (r_origin, currententity->origin, local_modelorg); TransformVector (local_modelorg, transformed_modelorg); R_RotateBmodel(currententity); // FIXME: don't mess with the frustum, // make entity passed in } D_CalcGradients (pface, s->d_ziorigin, s->d_zistepu, s->d_zistepv); //============ // textures that aren't warping are just flowing. Use NonTurbulentPow2 instead if(!(pface->texinfo->flags & SURF_WARP)) NonTurbulentPow2 (s->spans, s->d_ziorigin, s->d_zistepu, s->d_zistepv); else TurbulentPow2 (s->spans, s->d_ziorigin, s->d_zistepu, s->d_zistepv); //============ D_DrawZSpans (s->spans, s->d_ziorigin, s->d_zistepu, s->d_zistepv); if (s->insubmodel) { // // restore the old drawing state // FIXME: we don't want to do this every time! // TODO: speed up // VectorCopy (world_transformed_modelorg, transformed_modelorg); VectorCopy (base_vpn, vpn); VectorCopy (base_vup, vup); VectorCopy (base_vright, vright); R_TransformFrustum (); } } /* ============== D_SkySurf ============== */ static void D_SkySurf (surf_t *s) { pface = s->msurf; miplevel = 0; if (!pface->texinfo->image) return; cacheblock = pface->texinfo->image->pixels[0]; cachewidth = 256; D_CalcGradients (pface, s->d_ziorigin, s->d_zistepu, s->d_zistepv); D_DrawSpansPow2 (s->spans, s->d_ziorigin, s->d_zistepu, s->d_zistepv); // set up a gradient for the background surface that places it // effectively at infinity distance from the viewpoint D_DrawZSpans (s->spans, -0.9, 0, 0); } /* ============== D_SolidSurf Normal surface cached, texture mapped surface ============== */ static void D_SolidSurf (entity_t *currententity, surf_t *s) { float len1, len2, mipadjust; if (s->insubmodel) { vec3_t local_modelorg; // FIXME: we don't want to do all this for every polygon! // TODO: store once at start of frame currententity = s->entity; VectorSubtract(r_origin, currententity->origin, local_modelorg); TransformVector(local_modelorg, transformed_modelorg); R_RotateBmodel(currententity); // FIXME: don't mess with the frustum, // make entity passed in } pface = s->msurf; len1 = VectorLength (pface->texinfo->vecs[0]); len2 = VectorLength (pface->texinfo->vecs[1]); mipadjust = sqrt(len1*len1 + len2*len2); if (mipadjust < 0.01) { mipadjust = 0.01; } miplevel = D_MipLevelForScale(s->nearzi * scale_for_mip * mipadjust); // FIXME: make this passed in to D_CacheSurface pcurrentcache = D_CacheSurface (currententity, pface, miplevel); cacheblock = (pixel_t *)pcurrentcache->data; cachewidth = pcurrentcache->width; D_CalcGradients (pface, s->d_ziorigin, s->d_zistepu, s->d_zistepv); D_DrawSpansPow2 (s->spans, s->d_ziorigin, s->d_zistepu, s->d_zistepv); D_DrawZSpans (s->spans, s->d_ziorigin, s->d_zistepu, s->d_zistepv); if (s->insubmodel) { // // restore the old drawing state // FIXME: we don't want to do this every time! // TODO: speed up // VectorCopy (world_transformed_modelorg, transformed_modelorg); VectorCopy (base_vpn, vpn); VectorCopy (base_vup, vup); VectorCopy (base_vright, vright); R_TransformFrustum (); } } /* ============= D_DrawflatSurfaces To allow developers to see the polygon carving of the world ============= */ static void D_DrawflatSurfaces (const surf_t *surface) { surf_t *s; int color = 0; for (s = &surfaces[1] ; sspans) continue; // make a stable color for each surface by taking the low // bits of the msurface pointer D_FlatFillSurface (s, color & 0xFF); D_DrawZSpans (s->spans, s->d_ziorigin, s->d_zistepu, s->d_zistepv); color ++; } } /* ============== D_DrawSurfaces Rasterize all the span lists. Guaranteed zero overdraw. May be called more than once a frame if the surf list overflows (higher res) ============== */ static void D_DrawSurfaces (entity_t *currententity, surf_t *surface) { VectorSubtract (r_origin, vec3_origin, modelorg); TransformVector (modelorg, transformed_modelorg); VectorCopy (transformed_modelorg, world_transformed_modelorg); if (!sw_drawflat->value) { surf_t *s; for (s = &surfaces[1] ; sspans) continue; r_drawnpolycount++; if (! (s->flags & (SURF_DRAWSKY|SURF_DRAWBACKGROUND|SURF_DRAWTURB) ) ) D_SolidSurf (currententity, s); else if (s->flags & SURF_DRAWSKY) D_SkySurf (s); else if (s->flags & SURF_DRAWBACKGROUND) D_BackgroundSurf (s); else if (s->flags & SURF_DRAWTURB) D_TurbulentSurf (s); } } else D_DrawflatSurfaces (surface); VectorSubtract (r_origin, vec3_origin, modelorg); R_TransformFrustum (); } yquake2-QUAKE2_8_40/src/client/refresh/soft/sw_image.c000066400000000000000000000351471465112212000225400ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "header/local.h" #define MAX_RIMAGES 1024 static image_t *r_whitetexture_mip = NULL; static image_t r_images[MAX_RIMAGES]; static int numr_images; static int image_max = 0; /* =============== R_ImageList_f =============== */ void R_ImageList_f (void) { int i, used, texels; image_t *image; qboolean freeup; R_Printf(PRINT_ALL, "------------------\n"); texels = 0; used = 0; for (i=0, image=r_images ; iregistration_sequence == registration_sequence) { in_use = "*"; used++; } if (image->registration_sequence <= 0) continue; texels += image->width*image->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 (%dx%d) %s\n", image->asset_width, image->asset_height, image->name, image->width, image->height, in_use); } R_Printf(PRINT_ALL, "Total texel count: %i\n", texels); freeup = R_ImageHasFreeSpace(); R_Printf(PRINT_ALL, "Used %d of %d images%s.\n", used, image_max, freeup ? ", has free space" : ""); } //======================================================= static image_t * R_FindFreeImage (void) { image_t *image; int i; // find a free image_t for (i=0, image=r_images ; iregistration_sequence) break; } if (i == numr_images) { if (numr_images == MAX_RIMAGES) ri.Sys_Error(ERR_DROP, "%s: Max images", __func__); numr_images++; } image = &r_images[i]; return image; } static void R_ImageShrink(const unsigned char* src, unsigned char *dst, int width, int realwidth, int height, int realheight) { int x, y; float xstep, ystep; xstep = (float)width / realwidth; ystep = (float)height / realheight; for (y=0; ypixels[i], image->pixels[i + 1], image->width >> i, image->width >> (i + 1), image->height >> i, image->height >> (i + 1)); } } static size_t R_GetImageMipsSize(size_t mip1_size) { size_t full_size = 0; int i; for (i=0; i> (i * 2); } return full_size; } static void R_RestoreImagePointers(image_t *image, int min_mips) { int i; for (i=min_mips; ipixels[i + 1] = image->pixels[i] + ( image->width * image->height / (1 << (i * 2))); } image->mip_levels = NUM_MIPS; } byte * Get_BestImageSize(const image_t *image, int *req_width, int *req_height) { int width, height, i; width = image->width; height = image->height; // return last mip before smaller image size for (i = 0; i < (image->mip_levels - 1); i++) { if (image->pixels[i] && ((width / 2) < *req_width || (height / 2) < *req_height)) { *req_width = width; *req_height = height; return image->pixels[i]; } width = width / 2; height = height / 2; } if (image->pixels[image->mip_levels - 1]) { *req_width = image->width >> (image->mip_levels - 1); *req_height = image->height >> (image->mip_levels - 1); return image->pixels[image->mip_levels - 1]; } else { *req_width = image->width; *req_height = image->height; return image->pixels[0]; } } static byte *d_16to8table = NULL; // 16 to 8 bit conversion table void R_Convert32To8bit(const unsigned char* pic_in, pixel_t* pic_out, size_t size, qboolean transparent) { size_t i; if (!d_16to8table) return; for(i=0; i < size; i++) { if (pic_in[3] > 128 || !transparent) { unsigned int r, g, b, c; r = ( pic_in[0] >> 3 ) & 31; g = ( pic_in[1] >> 2 ) & 63; b = ( pic_in[2] >> 3 ) & 31; c = r | ( g << 5 ) | ( b << 11 ); pic_out[i] = d_16to8table[c & 0xFFFF]; } else { pic_out[i] = TRANSPARENT_COLOR; } pic_in += 4; } } /* ================ R_LoadPic ================ */ static image_t * R_LoadPic8 (char *name, byte *pic, int width, int realwidth, int height, int realheight, size_t data_size, imagetype_t type) { image_t *image; size_t size, full_size; size = width * height; /* data_size/size are unsigned */ if (!pic || data_size == 0 || width <= 0 || height <= 0 || size == 0) return NULL; image = R_FindFreeImage(); if (strlen(name) >= sizeof(image->name)) ri.Sys_Error(ERR_DROP, "%s: '%s' is too long", __func__, name); strcpy (image->name, name); image->registration_sequence = registration_sequence; image->width = width; image->height = height; image->asset_width = realwidth; image->asset_height = realheight; image->type = type; full_size = R_GetImageMipsSize(size); image->pixels[0] = malloc(full_size); if (!image->pixels[0]) { ri.Sys_Error(ERR_FATAL, "%s: Can't allocate image.", __func__); // code never returns after ERR_FATAL return NULL; } // some file types can have more data in file than code needs if (data_size > full_size) { data_size = full_size; } image->transparent = false; memcpy(image->pixels[0], pic, data_size); if (type != it_wall) { size_t i; for (i=0 ; ipixels[0][i] == TRANSPARENT_COLOR) { image->transparent = true; break; } } } // restore mips R_RestoreImagePointers(image, 0); if (full_size > data_size) { // looks short, restore everything from first image R_RestoreMips(image, 0); } return image; } static image_t * R_LoadPic (char *name, byte *pic, int width, int realwidth, int height, int realheight, size_t data_size, imagetype_t type, int bits) { if (!realwidth || !realheight) { realwidth = width; realheight = height; } if (data_size <= 0 || !width || !height) { return NULL; } /* Code used with HIColor calls */ if (bits == 32) { image_t *image = NULL; byte *pic8; pic8 = malloc(data_size); if (!pic8) { ri.Sys_Error(ERR_FATAL, "%s: Can't allocate image.", __func__); // code never returns after ERR_FATAL return NULL; } if (width != realwidth || height != realheight) { // temporary place for shrinked image byte* pic32 = NULL; // temporary image memory size int uploadwidth, uploadheight; if (type == it_pic) { uploadwidth = realwidth; uploadheight = realheight; // search next scale up while ((uploadwidth < width) && (uploadheight < height)) { uploadwidth *= 2; uploadheight *= 2; } // one step back if ((uploadwidth > width) || (uploadheight > height)) { uploadwidth /= 2; uploadheight /= 2; } } else { uploadwidth = realwidth; uploadheight = realheight; } // resize image pic32 = malloc(uploadwidth * uploadheight * 4); if (ResizeSTB(pic, width, height, pic32, uploadwidth, uploadheight)) { R_Convert32To8bit(pic32, pic8, uploadwidth * uploadheight, type != it_wall); image = R_LoadPic8(name, pic8, uploadwidth, realwidth, uploadheight, realheight, uploadwidth * uploadheight, type); } else { R_Convert32To8bit(pic, pic8, data_size, type != it_wall); image = R_LoadPic8 (name, pic8, width, realwidth, height, realheight, data_size, type); } free(pic32); } else { R_Convert32To8bit(pic, pic8, data_size, type != it_wall); image = R_LoadPic8 (name, pic8, width, realwidth, height, realheight, data_size, type); } free(pic8); return image; } else /* used with WAL and 8bit textures */ { if (r_scale8bittextures->value && type == it_pic) { byte *scaled = NULL; image_t *image; scaled = malloc(width * height * 4); if (!scaled) return NULL; scale2x(pic, scaled, width, height); width *= 2; height *= 2; image = R_LoadPic8(name, scaled, width, realwidth, height, realheight, width * height, type); free(scaled); return image; } else { return R_LoadPic8 (name, pic, width, realwidth, height, realheight, data_size, type); } } } /* * Apply color light to texture pixel * * TODO: -22% fps lost */ pixel_t R_ApplyLight(pixel_t pix, const light3_t light) { light3_t light_masked; pixel_t i_r, i_g, i_b; byte b_r, b_g, b_b; int i_c; light_masked[0] = light[0] & LIGHTMASK; light_masked[1] = light[1] & LIGHTMASK; light_masked[2] = light[2] & LIGHTMASK; /* same light or colorlight == 0 */ if (light_masked[0] == light_masked[1] && light_masked[0] == light_masked[2]) return vid_colormap[pix + light_masked[0]]; /* get index of color component of each component */ i_r = vid_colormap[light_masked[0] + pix]; i_g = vid_colormap[light_masked[1] + pix]; i_b = vid_colormap[light_masked[2] + pix]; /* get color component for each component */ b_r = d_8to24table[i_r * 4 + 0]; b_g = d_8to24table[i_g * 4 + 1]; b_b = d_8to24table[i_b * 4 + 2]; /* convert back to indexed color */ b_r = ( b_r >> 3 ) & 31; b_g = ( b_g >> 2 ) & 63; b_b = ( b_b >> 3 ) & 31; i_c = b_r | ( b_g << 5 ) | ( b_b << 11 ); return d_16to8table[i_c & 0xFFFF]; } /* =============== R_FindImage Finds or loads the given image or NULL =============== */ image_t * R_FindImage(const char *name, imagetype_t type) { image_t *image; int i, len; char *ptr; char namewe[256]; const char* ext; if (!name) { return NULL; } /* just return white image if show lightmap only */ if ((type == it_wall || type == it_skin) && r_lightmap->value) { return r_whitetexture_mip; } 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 - (strlen(ext) + 1)); if (len < 5) { return NULL; } /* fix backslashes */ while ((ptr = strchr(name, '\\'))) { *ptr = '/'; } // look for it for (i=0, image=r_images ; iname)) { image->registration_sequence = registration_sequence; return image; } } // // load the pic from disk // image = (image_t *)R_LoadImage(name, namewe, ext, type, r_retexturing->value, (loadimage_t)R_LoadPic); if (!image && r_validation->value) { R_Printf(PRINT_ALL, "%s: can't load %s\n", __func__, name); } return image; } /* ================ R_FreeUnusedImages Any image that was not touched on this registration sequence will be freed. ================ */ void R_FreeUnusedImages (void) { int i; image_t *image; for (i=0, image=r_images ; iregistration_sequence == registration_sequence) { continue; // used this sequence } if (!image->registration_sequence) continue; // free texture if (image->type == it_pic) continue; // don't free pics // free it free (image->pixels[0]); // the other mip levels just follow memset(image, 0, sizeof(*image)); } } qboolean R_ImageHasFreeSpace(void) { int i, used; image_t *image; used = 0; for (i = 0, image = r_images; i < numr_images; i++, image++) { if (!image->name[0]) continue; if (image->registration_sequence == registration_sequence) { used ++; } } if (image_max < used) { image_max = used; } // should same size of free slots as currently used return (numr_images + used) < MAX_RIMAGES; } static struct texture_buffer { image_t image; byte buffer[4096]; } r_notexture_buffer, r_whitetexture_buffer; static void R_InitNoTexture(void) { int m; // create a simple checkerboard texture for the default r_notexture_mip = &r_notexture_buffer.image; r_notexture_mip->width = r_notexture_mip->height = 16; r_notexture_mip->asset_width = r_notexture_mip->asset_height = 16; r_notexture_mip->pixels[0] = r_notexture_buffer.buffer; R_RestoreImagePointers(r_notexture_mip, 0); for (m=0 ; mpixels[m]; for (y=0 ; y< (16>>m) ; y++) for (x=0 ; x< (16>>m) ; x++) { if ( (y< (8>>m) ) ^ (x< (8>>m) ) ) *dest++ = d_16to8table[0x0000]; else *dest++ = d_16to8table[0xFFFF]; } } } static void R_InitWhiteTexture(void) { // create a simple white texture for the default r_whitetexture_mip = &r_whitetexture_buffer.image; r_whitetexture_mip->width = r_whitetexture_mip->height = 16; r_whitetexture_mip->asset_width = r_whitetexture_mip->asset_height = 16; r_whitetexture_mip->pixels[0] = r_whitetexture_buffer.buffer; R_RestoreImagePointers(r_whitetexture_mip, 0); memset(r_whitetexture_buffer.buffer, d_16to8table[0xFFFF], sizeof(r_whitetexture_buffer.buffer)); } /* ================== R_InitTextures ================== */ static void R_InitTextures (void) { R_InitNoTexture(); /* empty white texture for r_lightmap = 1*/ R_InitWhiteTexture(); } /* =============== R_InitImages =============== */ void R_InitImages (void) { unsigned char * table16to8; registration_sequence = 1; image_max = 0; d_16to8table = NULL; ri.FS_LoadFile("pics/16to8.dat", (void **)&table16to8); if ( !table16to8 ) { ri.Sys_Error(ERR_FATAL, "%s: Couldn't load pics/16to8.dat", __func__); // code never returns after ERR_FATAL return; } d_16to8table = malloc(0x10000); if ( !d_16to8table ) { ri.Sys_Error(ERR_FATAL, "%s: Couldn't allocate memory for d_16to8table", __func__); // code never returns after ERR_FATAL return; } memcpy(d_16to8table, table16to8, 0x10000); ri.FS_FreeFile((void *)table16to8); R_InitTextures (); } /* =============== R_ShutdownImages =============== */ void R_ShutdownImages (void) { int i; image_t *image; for (i=0, image=r_images ; iregistration_sequence) continue; // free texture // free it if (image->pixels[0]) free(image->pixels[0]); // the other mip levels just follow memset(image, 0, sizeof(*image)); } if (d_16to8table) free(d_16to8table); } yquake2-QUAKE2_8_40/src/client/refresh/soft/sw_light.c000066400000000000000000000223201465112212000225520ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sw_light.c #include "header/local.h" /* ============================================================================= DYNAMIC LIGHTS ============================================================================= */ static void R_MarkSurfaceLights(dlight_t *light, int bit, mnode_t *node, int lightframecount) { msurface_t *surf; int i; // mark the polygons surf = r_worldmodel->surfaces + node->firstsurface; for (i = 0; i < node->numsurfaces; i++, surf++) { if (surf->dlightframe != lightframecount) { surf->dlightbits = 0; surf->dlightframe = lightframecount; } surf->dlightbits |= bit; } } /* ============= R_PushDlights ============= */ void R_PushDlights (const model_t *model) { int i; dlight_t *l; for (i=0, l = r_newrefdef.dlights ; inodes + model->firstnode, r_framecount, R_MarkSurfaceLights); } } /* ============================================================================= LIGHT SAMPLING ============================================================================= */ static int RecursiveLightPoint (mnode_t *node, vec3_t start, vec3_t end, vec3_t pointcolor) { float front, back, frac; qboolean side; cplane_t *plane; vec3_t mid; msurface_t *surf; int s, t, ds, dt; int i; mtexinfo_t *tex; int maps; int r; if (node->contents != CONTENTS_NODE) return -1; // didn't hit anything // calculate mid point // FIXME: optimize for axial 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, pointcolor); 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; if (plane->type < 3) // axial planes mid[plane->type] = plane->dist; // go down front side r = RecursiveLightPoint (node->children[side], start, mid, pointcolor); if (r >= 0) return r; // hit something // check for impact on this node surf = r_worldmodel->surfaces + node->firstsurface; for (i=0 ; inumsurfaces ; i++, surf++) { byte *lightmap; 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); lightmap += 3 * (dt * ((surf->extents[0] >> 4) + 1) + ds); for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; maps++) { const float *rgb; int j; rgb = r_newrefdef.lightstyles[surf->styles[maps]].rgb; /* Apply light level to models */ for (j = 0; j < 3; j++) { float scale; scale = rgb[j] * r_modulate->value; pointcolor[j] += lightmap[j] * scale * (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, pointcolor); } /* =============== R_LightPoint =============== */ void R_LightPoint (const entity_t *currententity, vec3_t p, vec3_t color) { vec3_t end; float r; int lnum; dlight_t *dl; vec3_t dist; vec3_t pointcolor; 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 = RecursiveLightPoint (r_worldmodel->nodes, p, end, pointcolor); if (r == -1) { VectorCopy (vec3_origin, color); } else { VectorCopy (pointcolor, color); } // // add dynamic lights // for (lnum=0 ; lnumorigin, dl->origin, dist); add = dl->intensity - VectorLength(dist); add *= (1.0/256); if (add > 0) { VectorMA (color, add, dl->color, color); } } } //=================================================================== light_t *blocklights = NULL, *blocklight_max = NULL; /* =============== R_AddDynamicLights =============== */ static void R_AddDynamicLights (drawsurf_t* drawsurf) { msurface_t *surf; int lnum; int smax, tmax; mtexinfo_t *tex; surf = drawsurf->surf; smax = (surf->extents[0]>>4)+1; tmax = (surf->extents[1]>>4)+1; tex = surf->texinfo; if (blocklight_max <= blocklights + smax*tmax*3) { r_outoflights = true; return; } for (lnum=0; lnum < r_newrefdef.num_dlights; lnum++) { vec3_t impact, local, color; float dist, rad, minlight; int t; int i; dlight_t *dl; int negativeLight; light_t *plightdest = blocklights; if (!(surf->dlightbits & (1<intensity; if(r_colorlight->value == 0) { for(i=0; i<3; i++) color[i] = 256; } else { for(i=0; i<3; i++) color[i] = 256 * dl->color[i]; } //===== negativeLight = 0; if(rad < 0) { negativeLight = 1; rad = -rad; } //===== dist = DotProduct (dl->origin, surf->plane->normal) - surf->plane->dist; rad -= fabs(dist); minlight = DLIGHT_CUTOFF; // dl->minlight; if (rad < minlight) continue; minlight = rad - minlight; for (i=0 ; i<3 ; i++) { impact[i] = dl->origin[i] - surf->plane->normal[i]*dist; } local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3]; local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3]; local[0] -= surf->texturemins[0]; local[1] -= surf->texturemins[1]; for (t = 0 ; t td) dist = sd + (td>>1); else dist = td + (sd>>1); for (i=0; i<3; i++) { //==== if(!negativeLight) { if (dist < minlight) *plightdest += (rad - dist) * color[i]; } else { if (dist < minlight) *plightdest -= (rad - dist) * color[i]; if(*plightdest < minlight) *plightdest = minlight; } //==== plightdest ++; } } } } } /* =============== R_BuildLightMap Combine and scale multiple lightmaps into the 8.8 format in blocklights =============== */ void R_BuildLightMap (drawsurf_t* drawsurf) { int smax, tmax; int size; byte *lightmap; msurface_t *surf; surf = drawsurf->surf; smax = (surf->extents[0]>>4)+1; tmax = (surf->extents[1]>>4)+1; size = smax*tmax*3; if (blocklight_max <= blocklights + size) { r_outoflights = true; return; } // clear to no light memset(blocklights, 0, size * sizeof(light_t)); if (r_fullbright->value || !r_worldmodel->lightdata) { return; } // add all the lightmaps lightmap = surf->samples; if (lightmap) { int maps; for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; maps++) { unsigned scale; light_t *curr_light, *max_light; curr_light = blocklights; max_light = blocklights + size; scale = drawsurf->lightadj[maps]; // 8.8 fraction if(r_colorlight->value == 0) { do { light_t light; light = lightmap[0]; if (light < lightmap[1]) light = lightmap[1]; if (light < lightmap[2]) light = lightmap[2]; light *= scale; *curr_light += light; curr_light++; *curr_light += light; curr_light++; *curr_light += light; curr_light++; lightmap += 3; /* skip to next lightmap */ } while(curr_light < max_light); } else { do { *curr_light += *lightmap * scale; curr_light++; lightmap ++; /* skip to next lightmap */ } while(curr_light < max_light); } } } // add all the dynamic lights if (surf->dlightframe == r_framecount) R_AddDynamicLights (drawsurf); // bound, invert, and shift { light_t *curr_light, *max_light; curr_light = blocklights; max_light = blocklights + size; do { int t; t = (int)*curr_light; if (t < 0) t = 0; t = (255*256 - t) >> (8 - VID_CBITS); if (t < (1 << 6)) t = (1 << 6); *curr_light = t; curr_light++; } while(curr_light < max_light); } } yquake2-QUAKE2_8_40/src/client/refresh/soft/sw_main.c000066400000000000000000001542471465112212000224050ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sw_main.c #include #ifdef USE_SDL3 #include #else #include #include #endif #include "header/local.h" #define NUMSTACKEDGES 2048 #define NUMSTACKSURFACES 1024 #define MAXALIASVERTS 2048 #define MAXLIGHTS 1024 // allow some very large lightmaps viddef_t vid; pixel_t *vid_buffer = NULL; static pixel_t *swap_buffers = NULL; static pixel_t *swap_frames[2] = {NULL, NULL}; static int swap_current = 0; espan_t *vid_polygon_spans = NULL; pixel_t *vid_colormap = NULL; pixel_t *vid_alphamap = NULL; light_t vid_lightthreshold = 0; static int vid_minu, vid_minv, vid_maxu, vid_maxv; static int vid_zminu, vid_zminv, vid_zmaxu, vid_zmaxv; static qboolean IsHighDPIaware; // last position on map static vec3_t lastvieworg; static vec3_t lastviewangles; qboolean fastmoving; static qboolean palette_changed; refimport_t ri; byte d_8to24table[256 * 4]; vec3_t skyaxis; refdef_t r_newrefdef; model_t *r_worldmodel; pixel_t *r_warpbuffer; typedef struct swstate_s { qboolean fullscreen; int prev_mode; // last valid SW mode unsigned char gammatable[256]; unsigned char currentpalette[1024]; } swstate_t; static swstate_t sw_state; void *colormap; float r_time1; int r_numallocatededges; int r_numallocatedverts; int r_numallocatedtriangles; int r_numallocatedlights; int r_numallocatededgebasespans; float r_aliasuvscale = 1.0; qboolean r_outofsurfaces; qboolean r_outofedges; qboolean r_outofverts; qboolean r_outoftriangles; qboolean r_outoflights; qboolean r_outedgebasespans; qboolean r_dowarp; mvertex_t *r_pcurrentvertbase; int c_surf; static int r_cnumsurfs; int r_clipflags; // // view origin // vec3_t vup, base_vup; vec3_t vpn, base_vpn; vec3_t vright, base_vright; vec3_t r_origin; // // screen size info // oldrefdef_t r_refdef; float xscale, yscale; float xscaleinv, yscaleinv; float xscaleshrink, yscaleshrink; float aliasxscale, aliasyscale, aliasxcenter, aliasycenter; cplane_t screenedge[4]; // // refresh flags // int r_framecount = 1; // so frame counts initialized to 0 don't match int r_visframecount; int r_polycount; int r_drawnpolycount; int *pfrustum_indexes[4]; int r_viewcluster, r_oldviewcluster; image_t *r_notexture_mip; float da_time1, da_time2, dp_time1, dp_time2, db_time1, db_time2, rw_time1, rw_time2; float se_time1, se_time2, de_time1, de_time2; cvar_t *r_lefthand; cvar_t *r_gunfov; cvar_t *r_farsee; cvar_t *r_lightmap; cvar_t *r_colorlight; static cvar_t *sw_aliasstats; cvar_t *sw_clearcolor; cvar_t *sw_drawflat; cvar_t *sw_draworder; static cvar_t *r_mode; cvar_t *sw_stipplealpha; cvar_t *sw_surfcacheoverride; cvar_t *sw_waterwarp; static cvar_t *sw_overbrightbits; cvar_t *sw_custom_particles; static cvar_t *sw_anisotropic; cvar_t *sw_texture_filtering; cvar_t *r_retexturing; cvar_t *r_scale8bittextures; cvar_t *sw_gunzposition; cvar_t *r_validation; static cvar_t *sw_partialrefresh; cvar_t *r_drawworld; static cvar_t *r_drawentities; static cvar_t *r_dspeeds; cvar_t *r_fullbright; cvar_t *r_lerpmodels; static cvar_t *r_novis; cvar_t *r_modulate; static cvar_t *r_vsync; static cvar_t *r_customwidth; static cvar_t *r_customheight; static cvar_t *r_speeds; cvar_t *r_lightlevel; //FIXME HACK cvar_t *r_fixsurfsky; static cvar_t *vid_fullscreen; static cvar_t *vid_gamma; static cvar_t *r_lockpvs; static cvar_t *r_palettedtexture; cvar_t *r_cull; // sw_vars.c // all global and static refresh variables are collected in a contiguous block // to avoid cache conflicts. //------------------------------------------------------- // global refresh variables //------------------------------------------------------- // FIXME: make into one big structure, like cl or sv // FIXME: do separately for refresh engine and driver // d_vars.c // all global and static refresh variables are collected in a contiguous block // to avoid cache conflicts. //------------------------------------------------------- // global refresh variables //------------------------------------------------------- // FIXME: make into one big structure, like cl or sv // FIXME: do separately for refresh engine and driver float d_sdivzstepu, d_tdivzstepu; float d_sdivzstepv, d_tdivzstepv; float d_sdivzorigin, d_tdivzorigin; int sadjust, tadjust, bbextents, bbextentt; pixel_t *cacheblock; int cachewidth; pixel_t *d_viewbuffer; zvalue_t *d_pzbuffer; static void RE_BeginFrame( float camera_separation ); static void Draw_BuildGammaTable(void); static void RE_FlushFrame(int vmin, int vmax); static void RE_CleanFrame(void); static void RE_EndFrame(void); static void R_DrawBeam(const entity_t *e); /* ================ VID_DamageZBuffer Mark VID Z buffer as damaged and need to be recalculated ================ */ void VID_DamageZBuffer(int u, int v) { // update U if (vid_zminu > u) { vid_zminu = u; } if (vid_zmaxu < u) { vid_zmaxu = u; } // update V if (vid_zminv > v) { vid_zminv = v; } if (vid_zmaxv < v) { vid_zmaxv = v; } } // clean z damage state static void VID_NoDamageZBuffer(void) { vid_zminu = vid_buffer_width; vid_zmaxu = 0; vid_zminv = vid_buffer_height; vid_zmaxv = 0; } qboolean VID_CheckDamageZBuffer(int u, int v, int ucount, int vcount) { if (vid_zminv > (v + vcount) || vid_zmaxv < v) { // can be used previous zbuffer return false; } if (vid_zminu > u && vid_zminu > (u + ucount)) { // can be used previous zbuffer return false; } if (vid_zmaxu < u && vid_zmaxu < (u + ucount)) { // can be used previous zbuffer return false; } return true; } // Need to recalculate whole z buffer static void VID_WholeDamageZBuffer(void) { vid_zminu = 0; vid_zmaxu = vid_buffer_width; vid_zminv = 0; vid_zmaxv = vid_buffer_height; } /* ================ VID_DamageBuffer Mark VID buffer as damaged and need to be rewritten ================ */ void VID_DamageBuffer(int u, int v) { // update U if (vid_minu > u) { vid_minu = u; } if (vid_maxu < u) { vid_maxu = u; } // update V if (vid_minv > v) { vid_minv = v; } if (vid_maxv < v) { vid_maxv = v; } } // clean damage state static void VID_NoDamageBuffer(void) { vid_minu = vid_buffer_width; vid_maxu = 0; vid_minv = vid_buffer_height; vid_maxv = 0; } // Need to rewrite whole buffer static void VID_WholeDamageBuffer(void) { vid_minu = 0; vid_maxu = vid_buffer_width; vid_minv = 0; vid_maxv = vid_buffer_height; } /* ================ R_InitTurb ================ */ static void R_InitTurb (int width) { int i; memset(blanktable, 0, (width+CYCLE) * sizeof(int)); for (i = 0; i < (width+CYCLE); i++) { sintable[i] = AMP + sin(i*3.14159*2/CYCLE)*AMP; intsintable[i] = AMP2 + sin(i*3.14159*2/CYCLE)*AMP2; // AMP2, not 20 } } void R_ImageList_f(void); static void R_ScreenShot_f(void); static void R_RegisterVariables (void) { sw_aliasstats = ri.Cvar_Get ("sw_polymodelstats", "0", 0); sw_clearcolor = ri.Cvar_Get ("sw_clearcolor", "2", 0); sw_drawflat = ri.Cvar_Get ("sw_drawflat", "0", 0); sw_draworder = ri.Cvar_Get ("sw_draworder", "0", 0); sw_mipcap = ri.Cvar_Get ("sw_mipcap", "0", 0); sw_mipscale = ri.Cvar_Get ("sw_mipscale", "1", 0); sw_stipplealpha = ri.Cvar_Get( "sw_stipplealpha", "0", CVAR_ARCHIVE ); sw_surfcacheoverride = ri.Cvar_Get ("sw_surfcacheoverride", "0", 0); sw_waterwarp = ri.Cvar_Get ("sw_waterwarp", "1", 0); sw_overbrightbits = ri.Cvar_Get("sw_overbrightbits", "1.0", CVAR_ARCHIVE); sw_custom_particles = ri.Cvar_Get("sw_custom_particles", "0", CVAR_ARCHIVE); sw_texture_filtering = ri.Cvar_Get("sw_texture_filtering", "0", CVAR_ARCHIVE); sw_anisotropic = ri.Cvar_Get("r_anisotropic", "0", CVAR_ARCHIVE); r_retexturing = ri.Cvar_Get("r_retexturing", "1", CVAR_ARCHIVE); r_scale8bittextures = ri.Cvar_Get("r_scale8bittextures", "0", CVAR_ARCHIVE); sw_gunzposition = ri.Cvar_Get("sw_gunzposition", "8", CVAR_ARCHIVE); r_validation = ri.Cvar_Get("r_validation", "0", CVAR_ARCHIVE); // On MacOS texture is cleaned up after render and code have to copy a whole // screen to texture, other platforms save previous texture content and can be // copied only changed parts #if defined(__APPLE__) sw_partialrefresh = ri.Cvar_Get("sw_partialrefresh", "0", CVAR_ARCHIVE); #else sw_partialrefresh = ri.Cvar_Get("sw_partialrefresh", "1", CVAR_ARCHIVE); #endif r_mode = ri.Cvar_Get( "r_mode", "0", CVAR_ARCHIVE ); r_lefthand = ri.Cvar_Get( "hand", "0", CVAR_USERINFO | CVAR_ARCHIVE ); r_gunfov = ri.Cvar_Get( "r_gunfov", "80", CVAR_ARCHIVE ); r_farsee = ri.Cvar_Get("r_farsee", "0", CVAR_LATCH | CVAR_ARCHIVE); r_lightmap = ri.Cvar_Get("r_lightmap", "0", 0); r_colorlight = ri.Cvar_Get("sw_colorlight", "0", CVAR_ARCHIVE); r_speeds = ri.Cvar_Get ("r_speeds", "0", 0); r_fullbright = ri.Cvar_Get ("r_fullbright", "0", 0); r_drawentities = ri.Cvar_Get ("r_drawentities", "1", 0); r_drawworld = ri.Cvar_Get ("r_drawworld", "1", 0); r_dspeeds = ri.Cvar_Get ("r_dspeeds", "0", 0); r_lightlevel = ri.Cvar_Get ("r_lightlevel", "0", 0); r_lerpmodels = ri.Cvar_Get( "r_lerpmodels", "1", 0 ); r_novis = ri.Cvar_Get( "r_novis", "0", 0 ); r_modulate = ri.Cvar_Get("r_modulate", "1", CVAR_ARCHIVE); r_vsync = ri.Cvar_Get("r_vsync", "1", CVAR_ARCHIVE); r_customwidth = ri.Cvar_Get("r_customwidth", "1024", CVAR_ARCHIVE); r_customheight = ri.Cvar_Get("r_customheight", "768", CVAR_ARCHIVE); r_fixsurfsky = ri.Cvar_Get("r_fixsurfsky", "0", CVAR_ARCHIVE); r_palettedtexture = ri.Cvar_Get("r_palettedtexture", "0", 0); r_cull = ri.Cvar_Get("r_cull", "1", 0); vid_fullscreen = ri.Cvar_Get( "vid_fullscreen", "0", CVAR_ARCHIVE ); vid_gamma = ri.Cvar_Get( "vid_gamma", "1.0", CVAR_ARCHIVE ); ri.Cmd_AddCommand("modellist", Mod_Modellist_f); ri.Cmd_AddCommand("screenshot", R_ScreenShot_f); ri.Cmd_AddCommand("imagelist", R_ImageList_f); r_mode->modified = true; // force us to do mode specific stuff later vid_gamma->modified = true; // force us to rebuild the gamma table later sw_overbrightbits->modified = true; // force us to rebuild palette later r_lockpvs = ri.Cvar_Get ("r_lockpvs", "0", 0); } static void R_UnRegister (void) { ri.Cmd_RemoveCommand( "screenshot" ); ri.Cmd_RemoveCommand( "modellist" ); ri.Cmd_RemoveCommand( "imagelist" ); } static void RE_ShutdownContext(void); static void SWimp_CreateRender(int width, int height); static int RE_InitContext(void *win); static void RE_GetDrawableSize(int* width, int* height); static qboolean RE_SetMode(void); /* =============== R_Init =============== */ static qboolean RE_Init(void) { R_RegisterVariables (); R_InitImages (); Mod_Init (); Draw_InitLocal (); view_clipplanes[0].leftedge = true; view_clipplanes[1].rightedge = true; view_clipplanes[1].leftedge = view_clipplanes[2].leftedge = view_clipplanes[3].leftedge = false; view_clipplanes[0].rightedge = view_clipplanes[2].rightedge = view_clipplanes[3].rightedge = false; r_refdef.xOrigin = XCENTERING; r_refdef.yOrigin = YCENTERING; r_aliasuvscale = 1.0; GetPCXPalette (&vid_colormap, (unsigned *)d_8to24table); vid_alphamap = vid_colormap + 64*256; /* set our "safe" mode */ sw_state.prev_mode = 4; /* create the window and set up the context */ if (!RE_SetMode()) { R_Printf(PRINT_ALL, "%s() could not R_SetMode()\n", __func__); return false; } // create the window ri.Vid_MenuInit(); R_Printf(PRINT_ALL, "ref_soft version: "REF_VERSION"\n"); return true; } /* =============== RE_Shutdown =============== */ static void RE_Shutdown (void) { // free z buffer if (d_pzbuffer) { free (d_pzbuffer); d_pzbuffer = NULL; } // free surface cache if (sc_base) { D_FlushCaches (); free (sc_base); sc_base = NULL; } // free colormap if (vid_colormap) { free (vid_colormap); vid_colormap = NULL; } R_UnRegister (); Mod_FreeAll (); R_ShutdownImages (); RE_ShutdownContext(); } /* =============== R_NewMap =============== */ void R_NewMap (void) { r_viewcluster = -1; } static surf_t *lsurfs; /* =============== R_ReallocateMapBuffers =============== */ static void R_ReallocateMapBuffers (void) { if (!r_cnumsurfs || r_outofsurfaces) { if(lsurfs) { free(lsurfs); } if (r_outofsurfaces) { r_cnumsurfs *= 2; r_outofsurfaces = false; } if ((r_farsee->value > 0) && (r_cnumsurfs < NUMSTACKSURFACES)) r_cnumsurfs = NUMSTACKSURFACES * 2; else if (r_cnumsurfs < NUMSTACKSURFACES) r_cnumsurfs = NUMSTACKSURFACES; // edge_t->surf limited size to short if (r_cnumsurfs > SURFINDEX_MAX) { r_cnumsurfs = SURFINDEX_MAX; R_Printf(PRINT_ALL, "%s: Code has limitation to surfaces count.\n", __func__); } lsurfs = malloc (r_cnumsurfs * sizeof(surf_t)); if (!lsurfs) { R_Printf(PRINT_ALL, "%s: Couldn't malloc %d bytes\n", __func__, (int)(r_cnumsurfs * sizeof(surf_t))); return; } surfaces = lsurfs; // set limits surf_max = &surfaces[r_cnumsurfs]; // surface 0 doesn't really exist; it's just a dummy because index 0 // is used to indicate no edge attached to surface surfaces--; surface_p = &surfaces[2]; // background is surface 1, // surface 0 is a dummy R_Printf(PRINT_ALL, "Allocated %d surfaces.\n", r_cnumsurfs); } if (!r_numallocatedlights || r_outoflights) { free(blocklights); if (r_outoflights) { r_numallocatedlights *= 2; r_outoflights = false; } if (r_numallocatedlights < MAXLIGHTS) r_numallocatedlights = MAXLIGHTS; blocklights = malloc (r_numallocatedlights * sizeof(light_t)); if (!blocklights) { R_Printf(PRINT_ALL, "%s: Couldn't malloc %d bytes\n", __func__, (int)(r_numallocatedlights * sizeof(light_t))); return; } // set limits blocklight_max = &blocklights[r_numallocatedlights]; R_Printf(PRINT_ALL, "Allocated %d lights.\n", r_numallocatedlights); } if (!r_numallocatededges || r_outofedges) { free(r_edges); if (r_outofedges) { r_numallocatededges *= 2; r_outofedges = false; } if ((r_farsee->value > 0) && (r_numallocatededges < NUMSTACKEDGES * 2)) r_numallocatededges = NUMSTACKEDGES * 2; else if (r_numallocatededges < NUMSTACKEDGES) r_numallocatededges = NUMSTACKEDGES; r_edges = malloc (r_numallocatededges * sizeof(edge_t)); if (!r_edges) { R_Printf(PRINT_ALL, "%s: Couldn't malloc %d bytes\n", __func__, (int)(r_numallocatededges * sizeof(edge_t))); return; } // set limits edge_max = &r_edges[r_numallocatededges]; edge_p = r_edges; R_Printf(PRINT_ALL, "Allocated %d edges.\n", r_numallocatededges); } if (!r_numallocatedverts || r_outofverts) { if (finalverts) { free(finalverts); } if (r_outofverts) { r_numallocatedverts *= 2; r_outofverts = false; } if (r_numallocatedverts < MAXALIASVERTS) r_numallocatedverts = MAXALIASVERTS; finalverts = malloc(r_numallocatedverts * sizeof(finalvert_t)); if (!finalverts) { R_Printf(PRINT_ALL, "%s: Couldn't malloc %d bytes\n", __func__, (int)(r_numallocatedverts * sizeof(finalvert_t))); return; } finalverts_max = &finalverts[r_numallocatedverts]; R_Printf(PRINT_ALL, "Allocated %d verts.\n", r_numallocatedverts); } if (!r_numallocatedtriangles || r_outoftriangles) { if (triangle_spans) { free(triangle_spans); } if (r_outoftriangles) { r_numallocatedtriangles *= 2; r_outoftriangles = false; } // one more for the terminator if (r_numallocatedtriangles < vid.height+1) r_numallocatedtriangles = vid.height+1; triangle_spans = malloc(r_numallocatedtriangles * sizeof(spanpackage_t)); if (!triangle_spans) { R_Printf(PRINT_ALL, "%s: Couldn't malloc %d bytes\n", __func__, (int)(r_numallocatedtriangles * sizeof(spanpackage_t))); return; } triangles_max = &triangle_spans[r_numallocatedtriangles]; R_Printf(PRINT_ALL, "Allocated %d triangle spans.\n", r_numallocatedtriangles); } if (!r_numallocatededgebasespans || r_outedgebasespans) { if (edge_basespans) { free(edge_basespans); } if (r_outedgebasespans) { r_numallocatededgebasespans *= 2; r_outedgebasespans = false; } // used up to 8 * width spans for render, allocate once before use if (r_numallocatededgebasespans < vid_buffer_width * 8) r_numallocatededgebasespans = vid_buffer_width * 8; edge_basespans = malloc(r_numallocatededgebasespans * sizeof(espan_t)); if (!edge_basespans) { R_Printf(PRINT_ALL, "%s: Couldn't malloc %d bytes\n", __func__, (int)(r_numallocatededgebasespans * sizeof(espan_t))); return; } max_span_p = &edge_basespans[r_numallocatededgebasespans]; R_Printf(PRINT_ALL, "Allocated %d edgespans.\n", r_numallocatededgebasespans); } } /* =============== R_MarkLeaves Mark the leaves and nodes that are in the PVS for the current cluster =============== */ static void R_MarkLeaves (void) { const byte *vis; mnode_t *node; int i; mleaf_t *leaf; if (r_oldviewcluster == r_viewcluster && !r_novis->value && r_viewcluster != -1) return; // development aid to let you run around and see exactly where // the pvs ends if (r_lockpvs->value) return; r_visframecount++; r_oldviewcluster = r_viewcluster; if (r_novis->value || r_viewcluster == -1 || !r_worldmodel->vis) { // mark everything for (i=0 ; inumleafs ; i++) r_worldmodel->leafs[i].visframe = r_visframecount; for (i=0 ; inumnodes ; i++) r_worldmodel->nodes[i].visframe = r_visframecount; return; } vis = Mod_ClusterPVS (r_viewcluster, r_worldmodel); for (i=0,leaf=r_worldmodel->leafs ; inumleafs ; i++, leaf++) { int cluster; 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); } } } /* ** R_DrawNullModel ** ** IMPLEMENT THIS! */ static void R_DrawNullModel(void) { } /* ============= R_DrawEntitiesOnList ============= */ static void R_DrawEntitiesOnList (void) { int i; qboolean translucent_entities = false; if (!r_drawentities->value) return; // all bmodels have already been drawn by the edge list for (i=0 ; iflags & RF_TRANSLUCENT ) { translucent_entities = true; continue; } if ( currententity->flags & RF_BEAM ) { modelorg[0] = -r_origin[0]; modelorg[1] = -r_origin[1]; modelorg[2] = -r_origin[2]; VectorCopy(vec3_origin, r_entorigin); R_DrawBeam(currententity); } else { const model_t *currentmodel = currententity->model; if (!currentmodel) { R_DrawNullModel(); continue; } VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); switch (currentmodel->type) { case mod_sprite: R_DrawSprite(currententity, currentmodel); break; case mod_alias: R_AliasDrawModel(currententity, currentmodel); break; case mod_brush: break; default: R_Printf(PRINT_ALL, "%s: Bad modeltype %d\n", __func__, currentmodel->type); return; } } } if ( !translucent_entities ) return; for (i=0 ; iflags & RF_TRANSLUCENT ) ) continue; if ( currententity->flags & RF_BEAM ) { modelorg[0] = -r_origin[0]; modelorg[1] = -r_origin[1]; modelorg[2] = -r_origin[2]; VectorCopy(vec3_origin, r_entorigin); R_DrawBeam(currententity); } else { const model_t *currentmodel = currententity->model; if (!currentmodel) { R_DrawNullModel(); continue; } VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); switch (currentmodel->type) { case mod_sprite: R_DrawSprite(currententity, currentmodel); break; case mod_alias: R_AliasDrawModel(currententity, currentmodel); break; case mod_brush: break; default: R_Printf(PRINT_ALL, "%s: Bad modeltype %d\n", __func__, currentmodel->type); return; } } } } /* ============= R_BmodelCheckBBox ============= */ static int R_BmodelCheckBBox (const float *minmaxs) { int i, clipflags; clipflags = 0; for (i=0 ; i<4 ; i++) { vec3_t acceptpt, rejectpt; int *pindex; float d; // generate accept and reject points // FIXME: do with fast look-ups or integer tests based on the sign bit // of the floating point values pindex = pfrustum_indexes[i]; rejectpt[0] = minmaxs[pindex[0]]; rejectpt[1] = minmaxs[pindex[1]]; rejectpt[2] = minmaxs[pindex[2]]; d = DotProduct (rejectpt, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; if (d <= 0) return BMODEL_FULLY_CLIPPED; acceptpt[0] = minmaxs[pindex[3+0]]; acceptpt[1] = minmaxs[pindex[3+1]]; acceptpt[2] = minmaxs[pindex[3+2]]; d = DotProduct (acceptpt, view_clipplanes[i].normal); d -= view_clipplanes[i].dist; if (d <= 0) clipflags |= (1<nodes; while (1) { cplane_t *splitplane; int sides; if (node->visframe != r_visframecount) return NULL; // not visible at all if (node->contents != CONTENTS_NODE) { if (node->contents != CONTENTS_SOLID) return node; // we've reached a non-solid leaf, so it's // visible and not BSP clipped return NULL; // in solid, so not visible } splitplane = node->plane; sides = BOX_ON_PLANE_SIDE(mins, maxs, splitplane); if (sides == 3) return node; // this is the splitter // not split yet; recurse down the contacted side if (sides & 1) node = node->children[0]; else node = node->children[1]; } } /* ============= RotatedBBox Returns an axially aligned box that contains the input box at the given rotation ============= */ static void RotatedBBox (const vec3_t mins, const vec3_t maxs, vec3_t angles, vec3_t tmins, vec3_t tmaxs) { vec3_t tmp, v; int i, j; vec3_t forward, right, up; if (!angles[0] && !angles[1] && !angles[2]) { VectorCopy (mins, tmins); VectorCopy (maxs, tmaxs); return; } for (i=0 ; i<3 ; i++) { tmins[i] = (vec_t)INT_MAX; // Set maximum values for world range tmaxs[i] = (vec_t)INT_MIN; // Set minimal values for world range } AngleVectors (angles, forward, right, up); for ( i = 0; i < 8; i++ ) { 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]; VectorScale (forward, tmp[0], v); VectorMA (v, -tmp[1], right, v); VectorMA (v, tmp[2], up, v); for (j=0 ; j<3 ; j++) { if (v[j] < tmins[j]) tmins[j] = v[j]; if (v[j] > tmaxs[j]) tmaxs[j] = v[j]; } } } /* ============= R_DrawBEntitiesOnList ============= */ static void R_DrawBEntitiesOnList (void) { int i, clipflags; vec3_t oldorigin; vec3_t mins, maxs; float minmaxs[6]; mnode_t *topnode; if (!r_drawentities->value) return; VectorCopy (modelorg, oldorigin); for (i=0 ; imodel; if ( currententity->flags & RF_BEAM ) continue; if (!currentmodel) continue; if (currentmodel->nummodelsurfaces == 0) continue; // clip brush only if (currentmodel->type != mod_brush) continue; // see if the bounding box lets us trivially reject, also sets // trivial accept status RotatedBBox (currentmodel->mins, currentmodel->maxs, currententity->angles, mins, maxs); VectorAdd (mins, currententity->origin, minmaxs); VectorAdd (maxs, currententity->origin, (minmaxs+3)); clipflags = R_BmodelCheckBBox (minmaxs); if (clipflags == BMODEL_FULLY_CLIPPED) continue; // off the edge of the screen topnode = R_FindTopnode (minmaxs, minmaxs+3); if (!topnode) continue; // no part in a visible leaf VectorCopy (currententity->origin, r_entorigin); VectorSubtract (r_origin, r_entorigin, modelorg); r_pcurrentvertbase = currentmodel->vertexes; // FIXME: stop transforming twice R_RotateBmodel(currententity); // calculate dynamic lighting for bmodel R_PushDlights (currentmodel); if (topnode->contents == CONTENTS_NODE) { // not a leaf; has to be clipped to the world BSP r_clipflags = clipflags; R_DrawSolidClippedSubmodelPolygons(currententity, currentmodel, topnode); } else { // falls entirely in one leaf, so we just put all the // edges in the edge list and let 1/z sorting handle // drawing order R_DrawSubmodelPolygons(currententity, currentmodel, clipflags, topnode); } // put back world rotation and frustum clipping // FIXME: R_RotateBmodel should just work off base_vxx VectorCopy (base_vpn, vpn); VectorCopy (base_vup, vup); VectorCopy (base_vright, vright); VectorCopy (oldorigin, modelorg); R_TransformFrustum (); } } /* ================ R_EdgeDrawing Render the map ================ */ static void R_EdgeDrawing (entity_t *currententity) { if ( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) return; // Set function pointer pdrawfunc used later in this function R_BeginEdgeFrame (); edge_p = r_edges; surface_p = &surfaces[2]; // background is surface 1, // surface 0 is a dummy if (r_dspeeds->value) { rw_time1 = SDL_GetTicks(); } // Build the Global Edget Table // Also populate the surface stack and count # surfaces to render (surf_max is the max) R_RenderWorld (currententity); if (r_dspeeds->value) { rw_time2 = SDL_GetTicks(); db_time1 = rw_time2; } R_DrawBEntitiesOnList (); if (r_dspeeds->value) { db_time2 = SDL_GetTicks(); se_time1 = db_time2; } // Use the Global Edge Table to maintin the Active Edge Table: Draw the world as scanlines // Write the Z-Buffer (but no read) R_ScanEdges (currententity, surface_p); } //======================================================================= static void R_GammaCorrectAndSetPalette(const unsigned char *palette); /* ============= R_CalcPalette ============= */ static void R_CalcPalette (void) { static qboolean modified; byte palette[256][4], *in, *out; int i, j; float alpha, one_minus_alpha; vec3_t premult; int v; alpha = r_newrefdef.blend[3]; if (alpha <= 0) { if (modified) { // set back to default modified = false; R_GammaCorrectAndSetPalette( d_8to24table ); return; } return; } modified = true; if (alpha > 1) alpha = 1; premult[0] = r_newrefdef.blend[0]*alpha*255; premult[1] = r_newrefdef.blend[1]*alpha*255; premult[2] = r_newrefdef.blend[2]*alpha*255; one_minus_alpha = (1.0 - alpha); in = d_8to24table; out = palette[0]; for (i=0 ; i<256 ; i++, in+=4, out+=4) { for (j=0 ; j<3 ; j++) { v = premult[j] + one_minus_alpha * in[j]; if (v > 255) v = 255; out[j] = v; } out[3] = 255; } R_GammaCorrectAndSetPalette( ( const unsigned char * ) palette[0] ); } //======================================================================= static void R_SetLightLevel (const entity_t *currententity) { vec3_t light; if ((r_newrefdef.rdflags & RDF_NOWORLDMODEL) || (!r_drawentities->value) || (!currententity)) { r_lightlevel->value = 150.0; return; } // save off light value for server to look at (BIG HACK!) R_LightPoint (currententity, r_newrefdef.vieworg, light); r_lightlevel->value = 150.0 * light[0]; } static int VectorCompareRound(const vec3_t v1, const vec3_t v2) { if ((int)(v1[0] - v2[0]) || (int)(v1[1] - v2[1]) || (int)(v1[2] - v2[2])) { return 0; } return 1; } cplane_t frustum[4]; /* ================ RE_RenderFrame ================ */ static void RE_RenderFrame (refdef_t *fd) { r_newrefdef = *fd; entity_t ent; if (!r_worldmodel && !( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) ) { ri.Sys_Error(ERR_FATAL, "%s: NULL worldmodel", __func__); } // Need to rerender whole frame VID_WholeDamageBuffer(); VectorCopy (fd->vieworg, r_refdef.vieworg); VectorCopy (fd->viewangles, r_refdef.viewangles); // compare current position with old if (vid_buffer_width <= 640 || !VectorCompareRound(fd->vieworg, lastvieworg) || !VectorCompareRound(fd->viewangles, lastviewangles)) { fastmoving = true; } else { fastmoving = false; } // save position for next check VectorCopy (fd->vieworg, lastvieworg); VectorCopy (fd->viewangles, lastviewangles); if (r_speeds->value || r_dspeeds->value) r_time1 = SDL_GetTicks(); R_SetupFrame (); R_SetFrustum(vup, vpn, vright, r_origin, r_newrefdef.fov_x, r_newrefdef.fov_y, frustum); // Using the current view cluster (r_viewcluster), retrieve and decompress // the PVS (Potentially Visible Set) R_MarkLeaves (); // done here so we know if we're in water // For each dlight_t* passed via r_newrefdef.dlights, mark polygons affected by a light. R_PushDlights (r_worldmodel); // TODO: rearrange code same as in GL*_DrawWorld? /* auto cycle the world frame for texture animation */ memset(&ent, 0, sizeof(ent)); ent.frame = (int)(r_newrefdef.time * 2); // Build the Global Edge Table and render it via the Active Edge Table // Render the map R_EdgeDrawing (&ent); if (r_dspeeds->value) { se_time2 = SDL_GetTicks(); de_time1 = se_time2; } if (fastmoving) { // redraw all VID_WholeDamageZBuffer(); } else { // No Z rewrite required VID_NoDamageZBuffer(); } // Draw enemies, barrel etc... // Use Z-Buffer mostly in read mode only. R_DrawEntitiesOnList (); if (r_dspeeds->value) { de_time2 = SDL_GetTicks(); dp_time1 = SDL_GetTicks(); } // Duh ! R_DrawParticles (); if (r_dspeeds->value) dp_time2 = SDL_GetTicks(); // Perform pixel palette blending ia the pics/colormap.pcx lower part lookup table. R_DrawAlphaSurfaces(&ent); // Save off light value for server to look at (BIG HACK!) R_SetLightLevel (&ent); if (r_dowarp) D_WarpScreen (); if (r_dspeeds->value) { da_time1 = SDL_GetTicks(); da_time2 = SDL_GetTicks(); } // Modify the palette (when taking hit or pickup item) so all colors are modified R_CalcPalette (); if (sw_aliasstats->value) R_PrintAliasStats (); if (r_speeds->value) R_PrintTimes (); if (r_dspeeds->value) R_PrintDSpeeds (); R_ReallocateMapBuffers(); } /* ** R_InitGraphics */ static void R_InitGraphics( int width, int height ) { // free z buffer if ( d_pzbuffer ) { free(d_pzbuffer); d_pzbuffer = NULL; } // free surface cache if ( sc_base ) { D_FlushCaches(); free(sc_base); sc_base = NULL; } d_pzbuffer = malloc(width * height * sizeof(zvalue_t)); R_InitCaches(); R_GammaCorrectAndSetPalette(d_8to24table); } static rserr_t SWimp_SetMode(int *pwidth, int *pheight, int mode, int fullscreen); /* ** RE_BeginFrame */ static void RE_BeginFrame( float camera_separation ) { // pallete without changes palette_changed = false; // run without speed optimization fastmoving = false; while (r_vsync->modified) { RE_SetMode(); } /* ** rebuild the gamma correction palette if necessary */ if ( vid_gamma->modified || sw_overbrightbits->modified ) { Draw_BuildGammaTable(); R_GammaCorrectAndSetPalette(d_8to24table); // we need redraw everything VID_WholeDamageBuffer(); // and backbuffer should be zeroed memset(swap_buffers + ((swap_current + 1)&1), 0, vid_buffer_height * vid_buffer_width * sizeof(pixel_t)); vid_gamma->modified = false; sw_overbrightbits->modified = false; } } /* ================== R_SetMode ================== */ static qboolean RE_SetMode(void) { int err; int fullscreen; fullscreen = (int)vid_fullscreen->value; r_vsync->modified = false; /* a bit hackish approach to enable custom resolutions: Glimp_SetMode needs these values set for mode -1 */ vid.width = r_customwidth->value; vid.height = r_customheight->value; /* ** if this returns rserr_invalid_mode then it set previous resolution */ if ((err = SWimp_SetMode(&vid.width, &vid.height, r_mode->value, fullscreen)) == rserr_ok) { if (r_mode->value == -1) { sw_state.prev_mode = 4; /* safe default for custom mode */ } else { sw_state.prev_mode = r_mode->value; } } else { if (err == rserr_invalid_mode) { R_Printf(PRINT_ALL, "%s() - invalid mode\n", __func__); if(r_mode->value == sw_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("r_mode", sw_state.prev_mode); r_mode->modified = false; } /* try setting it back to something safe */ if (SWimp_SetMode(&vid.width, &vid.height, sw_state.prev_mode, 0) != rserr_ok) { R_Printf(PRINT_ALL, "%s() - could not revert to safe mode\n", __func__); return false; } } return true; } /* ** R_GammaCorrectAndSetPalette */ static void R_GammaCorrectAndSetPalette( const unsigned char *palette ) { int i; // Replace palette for ( i = 0; i < 256; i++ ) { if (sw_state.currentpalette[i*4+0] != sw_state.gammatable[palette[i*4+2]] || sw_state.currentpalette[i*4+1] != sw_state.gammatable[palette[i*4+1]] || sw_state.currentpalette[i*4+2] != sw_state.gammatable[palette[i*4+0]]) { sw_state.currentpalette[i*4+0] = sw_state.gammatable[palette[i*4+2]]; // blue sw_state.currentpalette[i*4+1] = sw_state.gammatable[palette[i*4+1]]; // green sw_state.currentpalette[i*4+2] = sw_state.gammatable[palette[i*4+0]]; // red sw_state.currentpalette[i*4+3] = 0xFF; // alpha palette_changed = true; } } } /* ** RE_SetPalette */ static void RE_SetPalette(const unsigned char *palette) { // clear screen to black to avoid any palette flash RE_CleanFrame(); if (palette) { byte palette32[1024]; int i; for ( i = 0; i < 256; i++ ) { palette32[i*4+0] = palette[i*3+0]; palette32[i*4+1] = palette[i*3+1]; palette32[i*4+2] = palette[i*3+2]; palette32[i*4+3] = 0xFF; } R_GammaCorrectAndSetPalette( palette32 ); } else { R_GammaCorrectAndSetPalette(d_8to24table); } } /* ================ Draw_BuildGammaTable ================ */ static void Draw_BuildGammaTable (void) { int i; float g; float overbright; overbright = sw_overbrightbits->value; if(overbright < 0.5) overbright = 0.5; if(overbright > 4.0) overbright = 4.0; g = (2.1 - vid_gamma->value); if (g == 1.0) { for (i=0 ; i<256 ; i++) { int inf; inf = i * overbright; if (inf < 0) inf = 0; if (inf > 255) inf = 255; sw_state.gammatable[i] = inf; } } else for (i=0 ; i<256 ; i++) { int inf; inf = (255 * pow ( (i+0.5)/255.5 , g ) + 0.5) * overbright; if (inf < 0) inf = 0; if (inf > 255) inf = 255; sw_state.gammatable[i] = inf; } } /* ** R_DrawBeam */ static void R_DrawBeam(const entity_t *e) { #define NUM_BEAM_SEGS 6 int i; vec3_t perpvec; vec3_t direction, normalized_direction; vec3_t start_points[NUM_BEAM_SEGS], end_points[NUM_BEAM_SEGS]; vec3_t oldorigin, origin; oldorigin[0] = e->oldorigin[0]; oldorigin[1] = e->oldorigin[1]; oldorigin[2] = e->oldorigin[2]; origin[0] = e->origin[0]; origin[1] = e->origin[1]; origin[2] = e->origin[2]; normalized_direction[0] = direction[0] = oldorigin[0] - origin[0]; normalized_direction[1] = direction[1] = oldorigin[1] - origin[1]; normalized_direction[2] = direction[2] = oldorigin[2] - origin[2]; if ( VectorNormalize( normalized_direction ) == 0 ) return; PerpendicularVector( perpvec, normalized_direction ); VectorScale( perpvec, e->frame / 2, perpvec ); for ( i = 0; i < NUM_BEAM_SEGS; 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] ); } for ( i = 0; i < NUM_BEAM_SEGS; i++ ) { R_IMFlatShadedQuad( start_points[i], end_points[i], end_points[(i+1)%NUM_BEAM_SEGS], start_points[(i+1)%NUM_BEAM_SEGS], e->skinnum & 0xFF, e->alpha ); } } //=================================================================== /* ============ RE_SetSky ============ */ // 3dstudio environment map names static const char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; static const int r_skysideimage[6] = {5, 2, 4, 1, 0, 3}; extern mtexinfo_t r_skytexinfo[6]; static void RE_SetSky (char *name, float rotate, vec3_t axis) { char skyname[MAX_QPATH]; int i; Q_strlcpy (skyname, name, sizeof(skyname)); VectorCopy (axis, skyaxis); for (i=0 ; i<6 ; i++) { image_t *image; image = (image_t *)GetSkyImage(skyname, suf[r_skysideimage[i]], r_palettedtexture->value, (findimage_t)R_FindImage); if (!image) { R_Printf(PRINT_ALL, "%s: can't load %s:%s sky\n", __func__, skyname, suf[r_skysideimage[i]]); image = r_notexture_mip; } r_skytexinfo[i].image = image; } } /* =============== RE_RegisterSkin =============== */ static struct image_s * RE_RegisterSkin (char *name) { return R_FindImage (name, it_skin); } void R_Printf(int level, const char* msg, ...) { va_list argptr; va_start(argptr, msg); ri.Com_VPrintf(level, msg, argptr); va_end(argptr); } static qboolean RE_IsVsyncActive(void) { if (r_vsync->value) { return true; } else { return false; } } static int RE_PrepareForWindow(void) { int flags = SDL_SWSURFACE; return flags; } /* ===================== RE_EndWorldRenderpass ===================== */ static qboolean RE_EndWorldRenderpass( void ) { return true; } /* =============== GetRefAPI =============== */ Q2_DLL_EXPORTED refexport_t GetRefAPI(refimport_t imp) { // struct for save refexport callbacks, copy of re struct from main file // used different variable name for prevent confusion and cppcheck warnings refexport_t refexport; // Need to communicate the SDL major version to the client. #ifdef USE_SDL3 SDL_Version ver; #else SDL_version ver; #endif SDL_VERSION(&ver); memset(&refexport, 0, sizeof(refexport_t)); ri = imp; refexport.api_version = API_VERSION; refexport.framework_version = ver.major; refexport.BeginRegistration = RE_BeginRegistration; refexport.RegisterModel = RE_RegisterModel; refexport.RegisterSkin = RE_RegisterSkin; refexport.DrawFindPic = RE_Draw_FindPic; refexport.SetSky = RE_SetSky; refexport.EndRegistration = RE_EndRegistration; refexport.RenderFrame = RE_RenderFrame; refexport.DrawGetPicSize = RE_Draw_GetPicSize; refexport.DrawPicScaled = RE_Draw_PicScaled; refexport.DrawStretchPic = RE_Draw_StretchPic; refexport.DrawCharScaled = RE_Draw_CharScaled; refexport.DrawTileClear = RE_Draw_TileClear; refexport.DrawFill = RE_Draw_Fill; refexport.DrawFadeScreen = RE_Draw_FadeScreen; refexport.DrawStretchRaw = RE_Draw_StretchRaw; refexport.Init = RE_Init; refexport.IsVSyncActive = RE_IsVsyncActive; refexport.Shutdown = RE_Shutdown; refexport.InitContext = RE_InitContext; refexport.GetDrawableSize = RE_GetDrawableSize; refexport.ShutdownContext = RE_ShutdownContext; refexport.PrepareForWindow = RE_PrepareForWindow; refexport.SetPalette = RE_SetPalette; refexport.BeginFrame = RE_BeginFrame; refexport.EndWorldRenderpass = RE_EndWorldRenderpass; refexport.EndFrame = RE_EndFrame; // Tell the client that we're unsing the // new renderer restart API. ri.Vid_RequestRestart(RESTART_NO); Swap_Init (); return refexport; } /* * FIXME: The following functions implement the render backend * through SDL renderer. Only small parts belong here, refresh.c * (at client side) needs to grow support funtions for software * renderers and the renderer must use them. What's left here * should be moved to a new file sw_sdl.c. * * Very, very problematic is at least the SDL initalization and * window creation in this code. That is guaranteed to clash with * the GL renderers (when switching GL -> Soft or the other way * round) and works only by pure luck. And only as long as there * is only one software renderer. */ static SDL_Window *window = NULL; static SDL_Texture *texture = NULL; static SDL_Renderer *renderer = NULL; int vid_buffer_height = 0; int vid_buffer_width = 0; static int RE_InitContext(void *win) { char title[40] = {0}; if(win == NULL) { ri.Sys_Error(ERR_FATAL, "%s() must not be called with NULL argument!", __func__); return false; } window = (SDL_Window *)win; /* Window title - set here so we can display renderer name in it */ snprintf(title, sizeof(title), "Yamagi Quake II %s - Soft Render", YQ2VERSION); SDL_SetWindowTitle(window, title); if (r_vsync->value) { #ifdef USE_SDL3 renderer = SDL_CreateRenderer(window, NULL, SDL_RENDERER_PRESENTVSYNC); #else renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); #endif } else { #ifdef USE_SDL3 renderer = SDL_CreateRenderer(window, NULL, 0); #else renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); #endif } /* Select the color for drawing. It is set to black here. */ SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); /* Clear the entire screen to our selected color. */ SDL_RenderClear(renderer); /* Up until now everything was drawn behind the scenes. This will show the new, black contents of the window. */ SDL_RenderPresent(renderer); #if SDL_VERSION_ATLEAST(2, 26, 0) // Figure out if we are high dpi aware. int flags = SDL_GetWindowFlags(win); #ifdef USE_SDL3 IsHighDPIaware = (flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) ? true : false; #else IsHighDPIaware = (flags & SDL_WINDOW_ALLOW_HIGHDPI) ? true : false; #endif #endif /* We can't rely on vid, because the context is created before we had a chance to overwrite it with the drawable size. */ if (IsHighDPIaware) { RE_GetDrawableSize(&vid_buffer_width, &vid_buffer_height); } else { vid_buffer_height = vid.height; vid_buffer_width = vid.width; } texture = SDL_CreateTexture(renderer, #if SDL_BYTEORDER == SDL_BIG_ENDIAN SDL_PIXELFORMAT_BGRA8888, #else SDL_PIXELFORMAT_ARGB8888, #endif SDL_TEXTUREACCESS_STREAMING, vid_buffer_width, vid_buffer_height); R_InitGraphics(vid_buffer_width, vid_buffer_height); SWimp_CreateRender(vid_buffer_width, vid_buffer_height); return true; } /* * Fills the actual size of the drawable into width and height. */ void RE_GetDrawableSize(int* width, int* height) { #ifdef USE_SDL3 SDL_GetCurrentRenderOutputSize(renderer, width, height); #else SDL_GetRendererOutputSize(renderer, width, height); #endif } static void RE_ShutdownContext(void) { if (swap_buffers) { free(swap_buffers); } swap_buffers = NULL; vid_buffer = NULL; swap_frames[0] = NULL; swap_frames[1] = NULL; if (sintable) { free(sintable); } sintable = NULL; if (intsintable) { free(intsintable); } intsintable = NULL; if (blanktable) { free(blanktable); } blanktable = NULL; if (vid_polygon_spans) { free(vid_polygon_spans); } vid_polygon_spans = NULL; if (newedges) { free(newedges); } newedges = NULL; if (removeedges) { free(removeedges); } removeedges = NULL; if (triangle_spans) { free(triangle_spans); } triangle_spans = NULL; if (warp_rowptr) { free(warp_rowptr); } warp_rowptr = NULL; if (warp_column) { free(warp_column); } warp_column = NULL; if (edge_basespans) { free(edge_basespans); } edge_basespans = NULL; if (finalverts) { free(finalverts); } finalverts = NULL; if(blocklights) { free(blocklights); } blocklights = NULL; if(r_edges) { free(r_edges); } r_edges = NULL; if(lsurfs) { free(lsurfs); } lsurfs = NULL; if(r_warpbuffer) { free(r_warpbuffer); } r_warpbuffer = NULL; if (texture) { SDL_DestroyTexture(texture); } texture = NULL; if (renderer) { SDL_DestroyRenderer(renderer); } renderer = NULL; } /* be careful if you ever want to change width: 12.20 fixed point math used in R_ScanEdges() overflows at width 2048 !! */ char shift_size; static void RE_CopyFrame (Uint32 * pixels, int pitch, int vmin, int vmax) { Uint32 *sdl_palette = (Uint32 *)sw_state.currentpalette; // no gaps between images rows if (pitch == vid_buffer_width) { const Uint32 *max_pixels; Uint32 *pixels_pos; pixel_t *buffer_pos; max_pixels = pixels + vmax; buffer_pos = vid_buffer + vmin; pixels_pos = pixels + vmin; while ( pixels_pos < max_pixels) { *pixels_pos = sdl_palette[*buffer_pos]; buffer_pos++; pixels_pos++; } } else { int y,x, buffer_pos, ymin, ymax; ymin = vmin / vid_buffer_width; ymax = vmax / vid_buffer_width; buffer_pos = ymin * vid_buffer_width; pixels += ymin * pitch; for (y=ymin; y < ymax; y++) { for (x=0; x < vid_buffer_width; x ++) { pixels[x] = sdl_palette[vid_buffer[buffer_pos + x]]; } pixels += pitch; buffer_pos += vid_buffer_width; } } } static int RE_BufferDifferenceStart(int vmin, int vmax) { int *front_buffer, *back_buffer, *back_max; back_buffer = (int*)(swap_frames[0] + vmin); front_buffer = (int*)(swap_frames[1] + vmin); back_max = (int*)(swap_frames[0] + vmax); while (back_buffer < back_max && *back_buffer == *front_buffer) { back_buffer ++; front_buffer ++; } return (pixel_t*)back_buffer - swap_frames[0]; } static int RE_BufferDifferenceEnd(int vmin, int vmax) { int *front_buffer, *back_buffer, *back_min; back_buffer = (int*)(swap_frames[0] + vmax); front_buffer = (int*)(swap_frames[1] + vmax); back_min = (int*)(swap_frames[0] + vmin); do { back_buffer --; front_buffer --; } while (back_buffer > back_min && *back_buffer == *front_buffer); // +1 for fully cover changes return (pixel_t*)back_buffer - swap_frames[0] + sizeof(int); } static void RE_CleanFrame(void) { int pitch; Uint32 *pixels; memset(swap_buffers, 0, vid_buffer_height * vid_buffer_width * sizeof(pixel_t) * 2); if (SDL_LockTexture(texture, NULL, (void**)&pixels, &pitch)) { Com_Printf("Can't lock texture: %s\n", SDL_GetError()); return; } // only cleanup texture without flush texture to screen memset(pixels, 0, pitch * vid_buffer_height); SDL_UnlockTexture(texture); // All changes flushed VID_NoDamageBuffer(); } static void RE_FlushFrame(int vmin, int vmax) { int pitch; Uint32 *pixels; if (SDL_LockTexture(texture, NULL, (void**)&pixels, &pitch)) { Com_Printf("Can't lock texture: %s\n", SDL_GetError()); return; } if (sw_partialrefresh->value) { RE_CopyFrame (pixels, pitch / sizeof(Uint32), vmin, vmax); } else { // On MacOS texture is cleaned up after render, // code have to copy a whole screen to the texture RE_CopyFrame (pixels, pitch / sizeof(Uint32), 0, vid_buffer_height * vid_buffer_width); } if ((sw_anisotropic->value > 0) && !fastmoving) { SmoothColorImage(pixels + vmin, vmax - vmin, sw_anisotropic->value); } SDL_UnlockTexture(texture); #ifdef USE_SDL3 SDL_RenderTexture(renderer, texture, NULL, NULL); #else SDL_RenderCopy(renderer, texture, NULL, NULL); #endif SDL_RenderPresent(renderer); // replace use next buffer swap_current ++; vid_buffer = swap_frames[swap_current&1]; // All changes flushed VID_NoDamageBuffer(); } /* ** RE_EndFrame ** ** This does an implementation specific copy from the backbuffer to the ** front buffer. */ static void RE_EndFrame (void) { int vmin, vmax; // fix possible issue with min/max if (vid_minu < 0) { vid_minu = 0; } if (vid_minv < 0) { vid_minv = 0; } if (vid_maxu > vid_buffer_width) { vid_maxu = vid_buffer_width; } if (vid_maxv > vid_buffer_height) { vid_maxv = vid_buffer_height; } vmin = vid_minu + vid_minv * vid_buffer_width; vmax = vid_maxu + vid_maxv * vid_buffer_width; // fix to correct limit if (vmax > (vid_buffer_height * vid_buffer_width)) { vmax = vid_buffer_height * vid_buffer_width; } // if palette changed need to flush whole buffer if (!palette_changed) { // search real begin/end of difference vmin = RE_BufferDifferenceStart(vmin, vmax); // no differences found if (vmin >= vmax) { return; } // search difference end vmax = RE_BufferDifferenceEnd(vmin, vmax); if (vmax > (vid_buffer_height * vid_buffer_width)) { vmax = vid_buffer_height * vid_buffer_width; } } RE_FlushFrame(vmin, vmax); } /* ** SWimp_SetMode */ static rserr_t SWimp_SetMode(int *pwidth, int *pheight, int mode, int fullscreen ) { rserr_t retval = rserr_ok; R_Printf (PRINT_ALL, "Setting mode %d:", mode ); if ((mode >= 0) && !ri.Vid_GetModeInfo( pwidth, pheight, mode ) ) { R_Printf(PRINT_ALL, " invalid mode\n"); return rserr_invalid_mode; } /* We trying to get resolution from desktop */ if (mode == -2) { if(!ri.GLimp_GetDesktopMode(pwidth, pheight)) { R_Printf(PRINT_ALL, " can't detect mode\n"); return rserr_invalid_mode; } } R_Printf(PRINT_ALL, " %dx%d (vid_fullscreen %i)\n", *pwidth, *pheight, fullscreen); if (fullscreen == 2) { int real_height, real_width; if(ri.GLimp_GetDesktopMode(&real_width, &real_height)) { if (real_height) { if (real_height != *pheight) { *pwidth = ((*pheight) * real_width) / real_height; } else { *pwidth = real_width; } } } R_Printf(PRINT_ALL, "Used corrected %dx%d mode\n", *pwidth, *pheight); } if (!ri.GLimp_InitGraphics(fullscreen, pwidth, pheight)) { // failed to set a valid mode in windowed mode return rserr_invalid_mode; } /* This is totaly obscure: For some strange reasons the renderer maintains three(!) repesentations of the resolution. One comes from the client and is saved in r_newrefdef. The other one is determined here and saved in vid. The third one is used by the backbuffer. Some calculations take all three representations into account. The values will always be the same. The GLimp_InitGraphics() call above communicates the requested resolution to the client where it ends up in the vid subsystem and the vid system writes it into r_newrefdef. The backbuffer is derived from vid. We can't avoid the client roundtrip, because we can get the real size of the drawable (which can differ from the resolution due to high dpi awareness) only after the render context was created by GLimp_InitGraphics() and need to communicate it somehow to the client. So we just overwrite the values saved in vid with a call to RE_GetDrawableSize(), just like the client does. This makes sure that both values are the same and everything is okay. We also need to take the special case fullscreen window into account. With the fullscreen windows we cannot use the drawable size, it would scale all cases to the size of the window. Instead use the drawable size when the user wants native resolution (the fullscreen window fills the screen) and use the requested resolution in all other cases. */ if (IsHighDPIaware) { if (vid_fullscreen->value != 2) { RE_GetDrawableSize(pwidth, pheight); } else { if (r_mode->value == -2) { /* User requested native resolution. */ RE_GetDrawableSize(pwidth, pheight); } } } return retval; } static void SWimp_CreateRender(int width, int height) { swap_current = 0; swap_buffers = malloc(height * width * sizeof(pixel_t) * 2); if (!swap_buffers) { ri.Sys_Error(ERR_FATAL, "%s: Can't allocate swapbuffer.", __func__); // code never returns after ERR_FATAL return; } swap_frames[0] = swap_buffers; swap_frames[1] = swap_buffers + height * width * sizeof(pixel_t); vid_buffer = swap_frames[swap_current&1]; // Need to rewrite whole frame VID_WholeDamageBuffer(); sintable = malloc((width+CYCLE) * sizeof(int)); intsintable = malloc((width+CYCLE) * sizeof(int)); blanktable = malloc((width+CYCLE) * sizeof(int)); newedges = malloc(width * sizeof(edge_t *)); removeedges = malloc(width * sizeof(edge_t *)); warp_rowptr = malloc((width+AMP2*2) * sizeof(byte*)); warp_column = malloc((width+AMP2*2) * sizeof(int)); // count of "out of items" r_outofsurfaces = false; r_outofedges = false; r_outofverts = false; r_outoftriangles = false; r_outoflights = false; r_outedgebasespans = false; // pointers to allocated buffers finalverts = NULL; r_edges = NULL; lsurfs = NULL; triangle_spans = NULL; blocklights = NULL; edge_basespans = NULL; // curently allocated items r_cnumsurfs = 0; r_numallocatededges = 0; r_numallocatedverts = 0; r_numallocatedtriangles = 0; r_numallocatedlights = 0; r_numallocatededgebasespans = 0; R_ReallocateMapBuffers(); r_warpbuffer = malloc(height * width * sizeof(pixel_t)); // 2k+ resolution and 32 == shift20_t if ((width >= 2048) && (sizeof(shift20_t) == 4)) { shift_size = 18; } else { shift_size = 20; } R_InitTurb (width); vid_polygon_spans = malloc(sizeof(espan_t) * (height + 1)); memset(sw_state.currentpalette, 0, sizeof(sw_state.currentpalette)); R_GammaCorrectAndSetPalette( d_8to24table ); } // this is only here so the functions in q_shared.c and q_shwin.c can link void Sys_Error (const char *error, ...) { va_list argptr; char text[4096]; // MAXPRINTMSG == 4096 va_start(argptr, error); vsnprintf(text, sizeof(text), error, argptr); va_end(argptr); ri.Sys_Error (ERR_FATAL, "%s", text); } void Com_Printf(const char *msg, ...) { va_list argptr; va_start(argptr, msg); ri.Com_VPrintf(PRINT_ALL, msg, argptr); va_end(argptr); } /* ============================================================================== SCREEN SHOTS ============================================================================== */ /* ================== R_ScreenShot_f ================== */ static void R_ScreenShot_f(void) { int x, y; byte *buffer = malloc(vid_buffer_width * vid_buffer_height * 3); const unsigned char *palette = sw_state.currentpalette; if (!buffer) { R_Printf(PRINT_ALL, "R_ScreenShot: Couldn't malloc %d bytes\n", vid_buffer_width * vid_buffer_height * 3); return; } for (x=0; x < vid_buffer_width; x ++) { for (y=0; y < vid_buffer_height; y ++) { int buffer_pos = y * vid_buffer_width + x; buffer[buffer_pos * 3 + 0] = palette[vid_buffer[buffer_pos] * 4 + 2]; // red buffer[buffer_pos * 3 + 1] = palette[vid_buffer[buffer_pos] * 4 + 1]; // green buffer[buffer_pos * 3 + 2] = palette[vid_buffer[buffer_pos] * 4 + 0]; // blue } } ri.Vid_WriteScreenshot(vid_buffer_width, vid_buffer_height, 3, buffer); free(buffer); } yquake2-QUAKE2_8_40/src/client/refresh/soft/sw_misc.c000066400000000000000000000227321465112212000224050ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef USE_SDL3 #include #else #include #endif #include "header/local.h" cvar_t *sw_mipcap; cvar_t *sw_mipscale; float verticalFieldOfView; int d_minmip; float d_scalemip[NUM_MIPS-1]; static int r_frustum_indexes[4*6]; static const float basemip[NUM_MIPS-1] = {1.0, 0.5*0.8, 0.25*0.8}; int d_vrectx, d_vrecty, d_vrectright_particle, d_vrectbottom_particle; float xcenter, ycenter; int d_pix_min, d_pix_max, d_pix_mul; /* ================ D_ViewChanged ================ */ static void D_ViewChanged (void) { scale_for_mip = sqrt(xscale*xscale + yscale*yscale); d_pix_min = r_refdef.vrect.height / 240; if (d_pix_min < 1) d_pix_min = 1; d_pix_max = (int)((float)r_refdef.vrect.height / (240.0 / 4.0) + 0.5); if (d_pix_max < 1) d_pix_max = 1; d_pix_mul = (int)((float)r_refdef.vrect.height / 240.0 + 0.5); d_vrectx = r_refdef.vrect.x; d_vrecty = r_refdef.vrect.y; d_vrectright_particle = r_refdef.vrectright - d_pix_max; d_vrectbottom_particle = r_refdef.vrectbottom - d_pix_max; /* ** clear Z-buffer and color-buffers if we're doing the gallery */ if ( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) { memset( d_pzbuffer, 0xff, vid_buffer_width * vid_buffer_height * sizeof(zvalue_t) ); RE_Draw_Fill( r_newrefdef.x, r_newrefdef.y, r_newrefdef.width, r_newrefdef.height,( int ) sw_clearcolor->value & 0xff ); } } /* ============= R_PrintTimes ============= */ void R_PrintTimes (void) { int r_time2; int ms; r_time2 = SDL_GetTicks(); ms = r_time2 - r_time1; R_Printf(PRINT_ALL,"%5i ms %3i/%3i/%3i poly %3i surf\n", ms, c_faceclip, r_polycount, r_drawnpolycount, c_surf); c_surf = 0; } /* ============= R_PrintDSpeeds ============= */ void R_PrintDSpeeds (void) { int ms, dp_time, r_time2, rw_time, db_time, se_time, de_time, da_time; r_time2 = SDL_GetTicks(); da_time = (da_time2 - da_time1); dp_time = (dp_time2 - dp_time1); rw_time = (rw_time2 - rw_time1); db_time = (db_time2 - db_time1); se_time = (se_time2 - se_time1); de_time = (de_time2 - de_time1); ms = (r_time2 - r_time1); R_Printf(PRINT_ALL,"%3i %2ip %2iw %2ib %2is %2ie %2ia\n", ms, dp_time, rw_time, db_time, se_time, de_time, da_time); } /* ============= R_PrintAliasStats ============= */ void R_PrintAliasStats (void) { R_Printf(PRINT_ALL,"%3i polygon model drawn\n", r_amodels_drawn); } /* =================== R_TransformFrustum =================== */ void R_TransformFrustum (void) { int i; vec3_t v, v2; for (i=0 ; i<4 ; i++) { v[0] = screenedge[i].normal[2]; v[1] = -screenedge[i].normal[0]; v[2] = screenedge[i].normal[1]; v2[0] = v[1]*vright[0] + v[2]*vup[0] + v[0]*vpn[0]; v2[1] = v[1]*vright[1] + v[2]*vup[1] + v[0]*vpn[1]; v2[2] = v[1]*vright[2] + v[2]*vup[2] + v[0]*vpn[2]; VectorCopy (v2, view_clipplanes[i].normal); view_clipplanes[i].dist = DotProduct (modelorg, v2); } } /* ================ TransformVector ================ */ void TransformVector (const vec3_t in, vec3_t out) { out[0] = DotProduct(in,vright); out[1] = DotProduct(in,vup); out[2] = DotProduct(in,vpn); } /* =============== R_SetUpFrustumIndexes =============== */ static void R_SetUpFrustumIndexes (void) { int i, j, *pindex; pindex = r_frustum_indexes; for (i=0 ; i<4 ; i++) { for (j=0 ; j<3 ; j++) { if (view_clipplanes[i].normal[j] < 0) { pindex[j] = j; pindex[j+3] = j+3; } else { pindex[j] = j+3; pindex[j+3] = j; } } // FIXME: do just once at start pfrustum_indexes[i] = pindex; pindex += 6; } } /* =============== R_ViewChanged Called every time the vid structure or r_refdef changes. Guaranteed to be called before the first refresh =============== */ static void R_ViewChanged (const vrect_t *vr) { int i; float xOrigin, yOrigin; r_refdef.vrect = *vr; r_refdef.horizontalFieldOfView = 2*tan((float)r_newrefdef.fov_x/360*M_PI);; verticalFieldOfView = 2*tan((float)r_newrefdef.fov_y/360*M_PI); r_refdef.fvrectx = (float)r_refdef.vrect.x; r_refdef.fvrectx_adj = (float)r_refdef.vrect.x - 0.5; r_refdef.vrect_x_adj_shift20 = (r_refdef.vrect.x<modified) { r_fullbright->modified = false; D_FlushCaches (); // so all lighting changes } r_framecount++; // build the transformation matrix for the given view angles VectorCopy (r_refdef.vieworg, modelorg); VectorCopy (r_refdef.vieworg, r_origin); AngleVectors (r_refdef.viewangles, vpn, vright, vup); // current viewleaf if ( !( r_newrefdef.rdflags & RDF_NOWORLDMODEL ) ) { mleaf_t *r_viewleaf; if (!r_worldmodel) { ri.Sys_Error(ERR_DROP, "%s: bad world model", __func__); return; } // Determine what is the current view cluster (walking the BSP tree) // and store it in r_viewcluster r_viewleaf = Mod_PointInLeaf (r_origin, r_worldmodel->nodes); r_viewcluster = r_viewleaf->cluster; } if (sw_waterwarp->value && (r_newrefdef.rdflags & RDF_UNDERWATER) ) r_dowarp = true; else r_dowarp = false; if (r_dowarp) { // warp into off screen buffer vrect.x = 0; vrect.y = 0; vrect.width = r_newrefdef.width; vrect.height = r_newrefdef.height; d_viewbuffer = r_warpbuffer; } else { vrect.x = r_newrefdef.x; vrect.y = r_newrefdef.y; vrect.width = r_newrefdef.width; vrect.height = r_newrefdef.height; d_viewbuffer = vid_buffer; } R_ViewChanged (&vrect); // start off with just the four screen edge clip planes R_TransformFrustum (); R_SetUpFrustumIndexes (); // save base values VectorCopy (vpn, base_vpn); VectorCopy (vright, base_vright); VectorCopy (vup, base_vup); // clear frame counts c_faceclip = 0; r_polycount = 0; r_drawnpolycount = 0; r_amodels_drawn = 0; // d_setup d_minmip = sw_mipcap->value; if (d_minmip > 3) d_minmip = 3; else if (d_minmip < 0) d_minmip = 0; for (i=0 ; i<(NUM_MIPS-1) ; i++) d_scalemip[i] = basemip[i] * sw_mipscale->value; } yquake2-QUAKE2_8_40/src/client/refresh/soft/sw_model.c000066400000000000000000000436331465112212000225550ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // models.c -- model loading and caching // models are the only shared resource between a client and server running // on the same machine. #include #include "header/local.h" static void Mod_LoadBrushModel(model_t *mod, void *buffer, int modfilelen); static YQ2_ALIGNAS_TYPE(int) byte mod_novis[MAX_MAP_LEAFS/8]; #define MAX_MOD_KNOWN 512 static model_t mod_known[MAX_MOD_KNOWN]; static int mod_numknown; static int mod_max = 0; int registration_sequence; //=============================================================================== static qboolean Mod_HasFreeSpace(void) { int i, used; model_t *mod; used = 0; for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) { if (!mod->name[0]) continue; if (mod->registration_sequence == registration_sequence) { used ++; } } if (mod_max < used) { mod_max = used; } // should same size of free slots as currently used return (mod_numknown + mod_max) < MAX_MOD_KNOWN; } /* ================ Mod_Modellist_f ================ */ void Mod_Modellist_f (void) { int i, total, used; model_t *mod; qboolean freeup; total = 0; used = 0; R_Printf(PRINT_ALL,"Loaded models:\n"); for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) { char *in_use = ""; if (mod->registration_sequence == registration_sequence) { in_use = "*"; used ++; } if (!mod->name[0]) continue; R_Printf(PRINT_ALL, "%8i : %s %s\n", mod->extradatasize, mod->name, in_use); total += mod->extradatasize; } R_Printf(PRINT_ALL, "Total resident: %i\n", total); // update statistics freeup = Mod_HasFreeSpace(); R_Printf(PRINT_ALL, "Used %d of %d models%s.\n", used, mod_max, freeup ? ", has free space" : ""); } /* =============== Mod_Init =============== */ void Mod_Init (void) { mod_max = 0; memset (mod_novis, 0xff, sizeof(mod_novis)); } /* ================== Mod_ForName Loads in a model for the given name ================== */ static model_t * Mod_ForName (char *name, model_t *parent_model, qboolean crash) { model_t *mod; void *buf; int i, modfilelen; if (!name[0]) { ri.Sys_Error(ERR_DROP, "%s: NULL name", __func__); } // // inline models are grabbed only from worldmodel // if (name[0] == '*' && parent_model) { i = atoi(name+1); if (i < 1 || i >= parent_model->numsubmodels) { ri.Sys_Error(ERR_DROP, "%s: bad inline model number", __func__); } return &parent_model->submodels[i]; } // // search the currently loaded models // for (i=0 , mod=mod_known ; iname, name) ) return mod; // // find a free model slot spot // for (i=0 , mod=mod_known ; iname[0]) break; // free spot } if (i == mod_numknown) { if (mod_numknown == MAX_MOD_KNOWN) ri.Sys_Error(ERR_DROP, "%s: mod_numknown == MAX_MOD_KNOWN", __func__); 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, "%s: %s not found", __func__, mod->name); } memset (mod->name, 0, sizeof(mod->name)); return NULL; } // // fill it in // // call the apropriate loader switch (LittleLong(*(unsigned *)buf)) { case IDALIASHEADER: { mod->extradata = Mod_LoadMD2(mod->name, buf, modfilelen, mod->mins, mod->maxs, (struct image_s **)mod->skins, (findimage_t)R_FindImage, &(mod->type)); if (!mod->extradata) { ri.Sys_Error(ERR_DROP, "%s: Failed to load %s", __func__, mod->name); } }; break; case IDSPRITEHEADER: { mod->extradata = Mod_LoadSP2(mod->name, buf, modfilelen, (struct image_s **)mod->skins, (findimage_t)R_FindImage, &(mod->type)); if (!mod->extradata) { ri.Sys_Error(ERR_DROP, "%s: Failed to load %s", __func__, mod->name); } } break; case IDBSPHEADER: Mod_LoadBrushModel(mod, buf, modfilelen); break; default: ri.Sys_Error(ERR_DROP, "%s: unknown fileid for %s", __func__, mod->name); break; } mod->extradatasize = Hunk_End(); ri.FS_FreeFile(buf); return mod; } /* ============== Mod_ClusterPVS ============== */ const byte * Mod_ClusterPVS (int cluster, const model_t *model) { if (cluster == -1 || !model->vis) return mod_novis; return Mod_DecompressVis ( (byte *)model->vis + model->vis->bitofs[cluster][DVIS_PVS], (model->vis->numclusters+7)>>3); } /* =============================================================================== BRUSHMODEL LOADING =============================================================================== */ /* ================= Mod_LoadSubmodels ================= */ static void Mod_LoadSubmodels (model_t *loadmodel, byte *mod_base, lump_t *l) { dmodel_t *in; model_t *out; int i, j, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s", __func__, loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count*sizeof(*out)); loadmodel->submodels = out; loadmodel->numsubmodels = count; for ( i=0 ; isubmodels, sizeof(*out)); } Com_sprintf (out->name, sizeof(out->name), "*%d", 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->radius = Mod_RadiusFromBounds (out->mins, out->maxs); out->firstnode = LittleLong (in->headnode); out->firstmodelsurface = LittleLong (in->firstface); out->nummodelsurfaces = LittleLong (in->numfaces); // visleafs out->numleafs = 0; // check limits if (out->firstnode >= loadmodel->numnodes) { ri.Sys_Error(ERR_DROP, "%s: Inline model %i has bad firstnode", __func__, i); } } } /* ================ CalcSurfaceExtents Fills in s->texturemins[] and s->extents[] ================ */ static void CalcSurfaceExtents (model_t *loadmodel, msurface_t *s) { float mins[2], maxs[2], val; int i; mtexinfo_t *tex; int bmins[2], bmaxs[2]; mins[0] = mins[1] = (float)INT_MAX; // Set maximum values for world range maxs[0] = maxs[1] = (float)INT_MIN; // Set minimal values for world range tex = s->texinfo; for (i=0 ; inumedges ; i++) { int e, j; mvertex_t *v; 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; if (s->extents[i] < 16) s->extents[i] = 16; // take at least one cache block if ( !(tex->flags & (SURF_WARP|SURF_SKY)) && s->extents[i] > 256) { ri.Sys_Error(ERR_DROP, "%s: Bad surface extents", __func__); } } } /* ================= Mod_LoadFaces ================= */ static void Mod_LoadFaces (model_t *loadmodel, byte *mod_base, lump_t *l) { dface_t *in; msurface_t *out; int i, count, surfnum; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s", __func__, loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc((count+6)*sizeof(*out)); // extra for skybox loadmodel->surfaces = out; loadmodel->numsurfaces = count; for ( surfnum=0 ; surfnumfirstedge = LittleLong(in->firstedge); out->numedges = LittleShort(in->numedges); if (out->numedges < 3) { ri.Sys_Error(ERR_DROP, "%s: Surface with %d edges", __func__, out->numedges); } out->flags = 0; planenum = LittleShort(in->planenum); side = LittleShort(in->side); if (side) out->flags |= SURF_PLANEBACK; if (planenum < 0 || planenum >= loadmodel->numplanes) { ri.Sys_Error(ERR_DROP, "%s: Incorrect %d planenum.", __func__, planenum); } out->plane = loadmodel->planes + planenum; ti = LittleShort(in->texinfo); if (ti < 0 || ti >= loadmodel->numtexinfo) { ri.Sys_Error(ERR_DROP, "%s: bad texinfo number", __func__); } out->texinfo = loadmodel->texinfo + ti; CalcSurfaceExtents (loadmodel, out); // lighting is saved as its with 24 bit color for (i=0 ; istyles[i] = in->styles[i]; } i = LittleLong(in->lightofs); if (i == -1) { out->samples = NULL; } else { out->samples = loadmodel->lightdata + i; } // set the drawing flags flag if (!out->texinfo->image) continue; if (r_fixsurfsky->value) { if (out->texinfo->flags & SURF_SKY) { out->flags |= SURF_DRAWSKY; continue; } } if (out->texinfo->flags & SURF_WARP) { out->flags |= SURF_DRAWTURB; for (i=0 ; i<2 ; i++) { out->extents[i] = 16384; out->texturemins[i] = -8192; } continue; } //============== // this marks flowing surfaces as turbulent. if (out->texinfo->flags & SURF_FLOWING) { out->flags |= SURF_DRAWTURB; for (i=0 ; i<2 ; i++) { out->extents[i] = 16384; out->texturemins[i] = -8192; } continue; } //============== } } /* ================= Mod_LoadLeafs ================= */ static void Mod_LoadLeafs (model_t *loadmodel, byte *mod_base, lump_t *l) { dleaf_t *in; mleaf_t *out; int i, j, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s", __func__, loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count*sizeof(*out)); loadmodel->leafs = out; loadmodel->numleafs = count; for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } out->contents = LittleLong(in->contents); out->cluster = LittleShort(in->cluster); out->area = LittleShort(in->area); // make unsigned long from signed short firstleafface = LittleShort(in->firstleafface) & 0xFFFF; out->nummarksurfaces = LittleShort(in->numleaffaces) & 0xFFFF; out->firstmarksurface = loadmodel->marksurfaces + firstleafface; if ((firstleafface + out->nummarksurfaces) > loadmodel->nummarksurfaces) { ri.Sys_Error(ERR_DROP, "%s: wrong marksurfaces position in %s", __func__, loadmodel->name); } } } /* ================= Mod_LoadMarksurfaces ================= */ static void Mod_LoadMarksurfaces (model_t *loadmodel, byte *mod_base, lump_t *l) { int i, count; short *in; msurface_t **out; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { ri.Sys_Error(ERR_DROP, "%s: funny lump size in %s", __func__, loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_Alloc(count*sizeof(*out)); loadmodel->marksurfaces = out; loadmodel->nummarksurfaces = count; for ( i=0 ; i= loadmodel->numsurfaces) { ri.Sys_Error(ERR_DROP, "%s: bad surface number", __func__); } out[i] = loadmodel->surfaces + j; } } /* ================= Mod_LoadBrushModel ================= */ static void Mod_LoadBrushModel(model_t *mod, void *buffer, int modfilelen) { int i; dheader_t *header; byte *mod_base; if (mod != mod_known) ri.Sys_Error(ERR_DROP, "%s: Loaded a brush model after the world", __func__); header = (dheader_t *)buffer; i = LittleLong (header->version); if (i != BSPVERSION) { ri.Sys_Error(ERR_DROP, "%s: %s has wrong version number (%i should be %i)", __func__, mod->name, i, BSPVERSION); } // swap all the lumps mod_base = (byte *)header; for (i=0 ; ilumps[LUMP_VERTEXES], sizeof(dvertex_t), sizeof(mvertex_t), 8); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_EDGES], sizeof(dedge_t), sizeof(medge_t), 13); float surfEdgeCount = (float)header->lumps[LUMP_SURFEDGES].filelen / sizeof(int); if(surfEdgeCount < MAX_MAP_SURFEDGES) // else it errors out later anyway hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_SURFEDGES], sizeof(int), sizeof(int), 24); // lighting is a special case, because we keep only 1 byte out of 3 // (=> no colored lighting in soft renderer by default) { int size = header->lumps[LUMP_LIGHTING].filelen; size = (size + 31) & ~31; /* save color data */ hunkSize += size; } hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_PLANES], sizeof(dplane_t), sizeof(cplane_t)*2, 6); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_TEXINFO], sizeof(texinfo_t), sizeof(mtexinfo_t), 6); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_FACES], sizeof(dface_t), sizeof(msurface_t), 6); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_LEAFFACES], sizeof(short), sizeof(msurface_t *), 0); // yes, out is indeed a pointer! hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_VISIBILITY], 1, 1, 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_LEAFS], sizeof(dleaf_t), sizeof(mleaf_t), 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_NODES], sizeof(dnode_t), sizeof(mnode_t), 0); hunkSize += Mod_CalcLumpHunkSize(&header->lumps[LUMP_MODELS], sizeof(dmodel_t), sizeof(model_t), 0); hunkSize += 1048576; // 1MB extra just in case mod->extradata = Hunk_Begin(hunkSize); mod->type = mod_brush; // load into heap Mod_LoadVertexes (mod->name, &mod->vertexes, &mod->numvertexes, mod_base, &header->lumps[LUMP_VERTEXES], 8); Mod_LoadEdges (mod->name, &mod->edges, &mod->numedges, mod_base, &header->lumps[LUMP_EDGES], 13); Mod_LoadSurfedges (mod->name, &mod->surfedges, &mod->numsurfedges, mod_base, &header->lumps[LUMP_SURFEDGES], 24); Mod_LoadLighting (&mod->lightdata, mod_base, &header->lumps[LUMP_LIGHTING]); Mod_LoadPlanes (mod->name, &mod->planes, &mod->numplanes, mod_base, &header->lumps[LUMP_PLANES], 6); Mod_LoadTexinfo (mod->name, &mod->texinfo, &mod->numtexinfo, mod_base, &header->lumps[LUMP_TEXINFO], (findimage_t)R_FindImage, r_notexture_mip, 6); Mod_LoadFaces (mod, mod_base, &header->lumps[LUMP_FACES]); Mod_LoadMarksurfaces (mod, mod_base, &header->lumps[LUMP_LEAFFACES]); Mod_LoadVisibility (&mod->vis, mod_base, &header->lumps[LUMP_VISIBILITY]); Mod_LoadLeafs (mod, mod_base, &header->lumps[LUMP_LEAFS]); Mod_LoadNodes (mod->name, mod->planes, mod->numplanes, mod->leafs, mod->numleafs, &mod->nodes, &mod->numnodes, mod_base, &header->lumps[LUMP_NODES]); Mod_LoadSubmodels (mod, mod_base, &header->lumps[LUMP_MODELS]); R_InitSkyBox (mod); } //============================================================================= /* ===================== RE_BeginRegistration Specifies the model that will be used as the world ===================== */ void RE_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); D_FlushCaches (); // 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 = RE_RegisterModel (fullname); R_NewMap (); } /* ===================== RE_RegisterModel ===================== */ struct model_s * RE_RegisterModel (char *name) { model_t *mod; mod = Mod_ForName (name, r_worldmodel, false); if (mod) { mod->registration_sequence = registration_sequence; // register any images used by the models if (mod->type == mod_brush) { int i; for (i=0 ; inumtexinfo ; i++) mod->texinfo[i].image->registration_sequence = registration_sequence; } else { /* numframes is unused for SP2 but lets set it also */ mod->numframes = Mod_ReLoadSkins((struct image_s **)mod->skins, (findimage_t)R_FindImage, mod->extradata, mod->type); } } return mod; } /* ===================== RE_EndRegistration ===================== */ void RE_EndRegistration (void) { int i; model_t *mod; if (Mod_HasFreeSpace() && R_ImageHasFreeSpace()) { // should be enough space for load next maps return; } for (i=0, mod=mod_known ; iname[0]) continue; if (mod->registration_sequence != registration_sequence) { // don't need this model Mod_Free (mod); } } R_FreeUnusedImages (); } //============================================================================= /* ================ Mod_Free ================ */ void Mod_Free (model_t *mod) { Hunk_Free (mod->extradata); memset (mod, 0, sizeof(*mod)); } /* ================ Mod_FreeAll ================ */ void Mod_FreeAll (void) { int i; for (i=0 ; icolor; int i, pix, count, u, v; zvalue_t izi; int custom_particle = (int)sw_custom_particles->value; /* ** transform the particle */ VectorSubtract (pparticle->origin, r_origin, local); transformed[0] = DotProduct(local, r_pright); transformed[1] = DotProduct(local, r_pup); transformed[2] = DotProduct(local, r_ppn); if (transformed[2] < PARTICLE_Z_CLIP) return; /* ** project the point */ // FIXME: preadjust xcenter and ycenter zi = 1.0 / transformed[2]; u = (int)(xcenter + zi * transformed[0] + 0.5); v = (int)(ycenter - zi * transformed[1] + 0.5); if ((v > d_vrectbottom_particle) || (u > d_vrectright_particle) || (v < d_vrecty) || (u < d_vrectx)) { return; } /* ** compute addresses of zbuffer, framebuffer, and ** compute the Z-buffer reference value. */ pz = d_pzbuffer + (vid_buffer_width * v) + u; pdest = d_viewbuffer + vid_buffer_width * v + u; izi = (int)(zi * 0x8000); /* ** determine the screen area covered by the particle, ** which also means clamping to a min and max */ pix = (izi * d_pix_mul) >> 7; if (pix < d_pix_min) pix = d_pix_min; else if (pix > d_pix_max) pix = d_pix_max; /* ** render the appropriate pixels */ count = pix; if ((pz[(vid_buffer_width * count / 2) + (count / 2)]) > izi) { // looks like under some object return; } // zbuffer particles damage VID_DamageZBuffer(u, v); VID_DamageZBuffer(u + count, v + count); if (custom_particle == 0) { switch (level) { case PARTICLE_33 : for ( ; count ; count--, pz += vid_buffer_width, pdest += vid_buffer_width) { //FIXME--do it in blocks of 8? for (i=0 ; i= min_int && pos <= max_int && pz[i] <= izi) { pz[i] = izi; pdest[i] = vid_alphamap[color + ((int)pdest[i]<<8)]; } } } break; case PARTICLE_66 : { int color_part = (color<<8); for ( ; count ; count--, pz += vid_buffer_width, pdest += vid_buffer_width) { for (i=0 ; i= min_int && pos <= max_int && pz[i] <= izi) { pz[i] = izi; pdest[i] = vid_alphamap[color_part + (int)pdest[i]]; } } } break; } default: //100 for ( ; count ; count--, pz += vid_buffer_width, pdest += vid_buffer_width) { for (i=0 ; i= min_int && pos <= max_int && pz[i] <= izi) { pz[i] = izi; pdest[i] = color; } } } break; } } } /* ** R_DrawParticles ** ** Responsible for drawing all of the particles in the particle list ** throughout the world. Doesn't care if we're using the C path or ** if we're using the asm path, it simply assigns a function pointer ** and goes. */ void R_DrawParticles (void) { particle_t *p; int i; VectorScale( vright, xscaleshrink, r_pright ); VectorScale( vup, yscaleshrink, r_pup ); VectorCopy( vpn, r_ppn ); for (p=r_newrefdef.particles, i=0 ; ialpha > 0.66 ) level = PARTICLE_OPAQUE; else if ( p->alpha > 0.33 ) level = PARTICLE_66; else level = PARTICLE_33; R_DrawParticle(p, level); } } yquake2-QUAKE2_8_40/src/client/refresh/soft/sw_poly.c000066400000000000000000000750251465112212000224400ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include "header/local.h" #define AFFINE_SPANLET_SIZE 16 #define AFFINE_SPANLET_SIZE_BITS 4 typedef struct { pixel_t *pbase, *pdest; zvalue_t *pz; int s, t; int sstep, tstep; int izi, izistep, izistep_times_2; int spancount; unsigned u, v; } spanletvars_t; static spanletvars_t s_spanletvars; static int r_polyblendcolor; polydesc_t r_polydesc; msurface_t *r_alpha_surfaces; static int clip_current; vec5_t r_clip_verts[2][MAXWORKINGVERTS+2]; static emitpoint_t outverts[MAXWORKINGVERTS+3]; static int s_minindex, s_maxindex; static void R_DrawPoly(int iswater, espan_t *spans); /* ** R_DrawSpanletOpaque */ static void R_DrawSpanletOpaque(const int *r_turb_turb) { do { unsigned btemp; unsigned ts, tt; ts = s_spanletvars.s >> SHIFT16XYZ; tt = s_spanletvars.t >> SHIFT16XYZ; btemp = *(s_spanletvars.pbase + (ts) + (tt) * cachewidth); if (btemp != TRANSPARENT_COLOR) { if (*s_spanletvars.pz <= (s_spanletvars.izi >> SHIFT16XYZ)) { *s_spanletvars.pz = s_spanletvars.izi >> SHIFT16XYZ; *s_spanletvars.pdest = btemp; } } s_spanletvars.izi += s_spanletvars.izistep; s_spanletvars.pdest++; s_spanletvars.pz++; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; } while (--s_spanletvars.spancount > 0); } /* ** R_DrawSpanletTurbulentStipple33 */ static void R_DrawSpanletTurbulentStipple33(const int *r_turb_turb) { pixel_t *pdest = s_spanletvars.pdest; zvalue_t *pz = s_spanletvars.pz; zvalue_t izi = s_spanletvars.izi; if ( s_spanletvars.v & 1 ) { s_spanletvars.pdest += s_spanletvars.spancount; s_spanletvars.pz += s_spanletvars.spancount; if ( s_spanletvars.spancount == AFFINE_SPANLET_SIZE ) s_spanletvars.izi += s_spanletvars.izistep << AFFINE_SPANLET_SIZE_BITS; else s_spanletvars.izi += s_spanletvars.izistep * s_spanletvars.izistep; if ( s_spanletvars.u & 1 ) { izi += s_spanletvars.izistep; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; pdest++; pz++; s_spanletvars.spancount--; } s_spanletvars.sstep *= 2; s_spanletvars.tstep *= 2; while ( s_spanletvars.spancount > 0 ) { unsigned btemp; int sturb, tturb; sturb = ((s_spanletvars.s + r_turb_turb[(s_spanletvars.t>>16)&(CYCLE-1)])>>16)&63; tturb = ((s_spanletvars.t + r_turb_turb[(s_spanletvars.s>>16)&(CYCLE-1)])>>16)&63; btemp = *( s_spanletvars.pbase + ( sturb ) + ( tturb << 6 ) ); if ( *pz <= ( izi >> SHIFT16XYZ ) ) *pdest = btemp; izi += s_spanletvars.izistep_times_2; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; pdest += 2; pz += 2; s_spanletvars.spancount -= 2; } } } /* ** R_DrawSpanletTurbulentStipple66 */ static void R_DrawSpanletTurbulentStipple66(const int *r_turb_turb) { unsigned btemp; int sturb, tturb; pixel_t *pdest = s_spanletvars.pdest; zvalue_t *pz = s_spanletvars.pz; zvalue_t izi = s_spanletvars.izi; if ( !( s_spanletvars.v & 1 ) ) { s_spanletvars.pdest += s_spanletvars.spancount; s_spanletvars.pz += s_spanletvars.spancount; if ( s_spanletvars.spancount == AFFINE_SPANLET_SIZE ) s_spanletvars.izi += s_spanletvars.izistep << AFFINE_SPANLET_SIZE_BITS; else s_spanletvars.izi += s_spanletvars.izistep * s_spanletvars.izistep; if ( s_spanletvars.u & 1 ) { izi += s_spanletvars.izistep; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; pdest++; pz++; s_spanletvars.spancount--; } s_spanletvars.sstep *= 2; s_spanletvars.tstep *= 2; while ( s_spanletvars.spancount > 0 ) { sturb = ((s_spanletvars.s + r_turb_turb[(s_spanletvars.t>>16)&(CYCLE-1)])>>16)&63; tturb = ((s_spanletvars.t + r_turb_turb[(s_spanletvars.s>>16)&(CYCLE-1)])>>16)&63; btemp = *( s_spanletvars.pbase + ( sturb ) + ( tturb << 6 ) ); if ( *pz <= ( izi >> SHIFT16XYZ ) ) *pdest = btemp; izi += s_spanletvars.izistep_times_2; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; pdest += 2; pz += 2; s_spanletvars.spancount -= 2; } } else { s_spanletvars.pdest += s_spanletvars.spancount; s_spanletvars.pz += s_spanletvars.spancount; if ( s_spanletvars.spancount == AFFINE_SPANLET_SIZE ) s_spanletvars.izi += s_spanletvars.izistep << AFFINE_SPANLET_SIZE_BITS; else s_spanletvars.izi += s_spanletvars.izistep * s_spanletvars.izistep; while ( s_spanletvars.spancount > 0 ) { sturb = ((s_spanletvars.s + r_turb_turb[(s_spanletvars.t>>16)&(CYCLE-1)])>>16)&63; tturb = ((s_spanletvars.t + r_turb_turb[(s_spanletvars.s>>16)&(CYCLE-1)])>>16)&63; btemp = *( s_spanletvars.pbase + ( sturb ) + ( tturb << 6 ) ); if ( *pz <= ( izi >> SHIFT16XYZ ) ) *pdest = btemp; izi += s_spanletvars.izistep; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; pdest++; pz++; s_spanletvars.spancount--; } } } /* ** R_DrawSpanletTurbulentBlended */ static void R_DrawSpanletTurbulentBlended66(const int *r_turb_turb) { do { unsigned btemp; int sturb, tturb; sturb = ((s_spanletvars.s + r_turb_turb[(s_spanletvars.t>>16)&(CYCLE-1)])>>16)&63; tturb = ((s_spanletvars.t + r_turb_turb[(s_spanletvars.s>>16)&(CYCLE-1)])>>16)&63; btemp = *( s_spanletvars.pbase + ( sturb ) + ( tturb << 6 ) ); if ( *s_spanletvars.pz <= ( s_spanletvars.izi >> SHIFT16XYZ ) ) *s_spanletvars.pdest = vid_alphamap[btemp*256+*s_spanletvars.pdest]; s_spanletvars.izi += s_spanletvars.izistep; s_spanletvars.pdest++; s_spanletvars.pz++; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; } while ( --s_spanletvars.spancount > 0 ); } static void R_DrawSpanletTurbulentBlended33(const int *r_turb_turb) { do { unsigned btemp; int sturb, tturb; sturb = ((s_spanletvars.s + r_turb_turb[(s_spanletvars.t>>16)&(CYCLE-1)])>>16)&63; tturb = ((s_spanletvars.t + r_turb_turb[(s_spanletvars.s>>16)&(CYCLE-1)])>>16)&63; btemp = *( s_spanletvars.pbase + ( sturb ) + ( tturb << 6 ) ); if ( *s_spanletvars.pz <= ( s_spanletvars.izi >> SHIFT16XYZ ) ) *s_spanletvars.pdest = vid_alphamap[btemp+*s_spanletvars.pdest*256]; s_spanletvars.izi += s_spanletvars.izistep; s_spanletvars.pdest++; s_spanletvars.pz++; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; } while ( --s_spanletvars.spancount > 0 ); } /* ** R_DrawSpanlet33 */ static void R_DrawSpanlet33(const int *r_turb_turb) { do { unsigned btemp; unsigned ts, tt; ts = s_spanletvars.s >> SHIFT16XYZ; tt = s_spanletvars.t >> SHIFT16XYZ; btemp = *(s_spanletvars.pbase + (ts) + (tt) * cachewidth); if ( btemp != TRANSPARENT_COLOR ) { if (*s_spanletvars.pz <= (s_spanletvars.izi >> SHIFT16XYZ)) { *s_spanletvars.pdest = vid_alphamap[btemp+*s_spanletvars.pdest*256]; } } s_spanletvars.izi += s_spanletvars.izistep; s_spanletvars.pdest++; s_spanletvars.pz++; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; } while (--s_spanletvars.spancount > 0); } static void R_DrawSpanletConstant33(const int *r_turb_turb) { do { if (*s_spanletvars.pz <= (s_spanletvars.izi >> SHIFT16XYZ)) { *s_spanletvars.pdest = vid_alphamap[r_polyblendcolor+*s_spanletvars.pdest*256]; } s_spanletvars.izi += s_spanletvars.izistep; s_spanletvars.pdest++; s_spanletvars.pz++; } while (--s_spanletvars.spancount > 0); } /* ** R_DrawSpanlet66 */ static void R_DrawSpanlet66(const int *r_turb_turb) { do { unsigned btemp; unsigned ts, tt; ts = s_spanletvars.s >> SHIFT16XYZ; tt = s_spanletvars.t >> SHIFT16XYZ; btemp = *(s_spanletvars.pbase + (ts) + (tt) * cachewidth); if ( btemp != TRANSPARENT_COLOR ) { if (*s_spanletvars.pz <= (s_spanletvars.izi >> SHIFT16XYZ)) { *s_spanletvars.pdest = vid_alphamap[btemp*256+*s_spanletvars.pdest]; } } s_spanletvars.izi += s_spanletvars.izistep; s_spanletvars.pdest++; s_spanletvars.pz++; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; } while (--s_spanletvars.spancount > 0); } /* ** R_DrawSpanlet33Stipple */ static void R_DrawSpanlet33Stipple(const int *r_turb_turb) { pixel_t *pdest = s_spanletvars.pdest; zvalue_t *pz = s_spanletvars.pz; zvalue_t izi = s_spanletvars.izi; if ( r_polydesc.stipple_parity ^ ( s_spanletvars.v & 1 ) ) { s_spanletvars.pdest += s_spanletvars.spancount; s_spanletvars.pz += s_spanletvars.spancount; if ( s_spanletvars.spancount == AFFINE_SPANLET_SIZE ) s_spanletvars.izi += s_spanletvars.izistep << AFFINE_SPANLET_SIZE_BITS; else s_spanletvars.izi += s_spanletvars.izistep * s_spanletvars.izistep; if ( r_polydesc.stipple_parity ^ ( s_spanletvars.u & 1 ) ) { izi += s_spanletvars.izistep; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; pdest++; pz++; s_spanletvars.spancount--; } s_spanletvars.sstep *= 2; s_spanletvars.tstep *= 2; while ( s_spanletvars.spancount > 0 ) { unsigned btemp; unsigned s = s_spanletvars.s >> SHIFT16XYZ; unsigned t = s_spanletvars.t >> SHIFT16XYZ; btemp = *( s_spanletvars.pbase + ( s ) + ( t * cachewidth ) ); if ( btemp != TRANSPARENT_COLOR ) { if ( *pz <= ( izi >> SHIFT16XYZ ) ) *pdest = btemp; } izi += s_spanletvars.izistep_times_2; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; pdest += 2; pz += 2; s_spanletvars.spancount -= 2; } } } /* ** R_DrawSpanlet66Stipple */ static void R_DrawSpanlet66Stipple(const int *r_turb_turb) { unsigned btemp; pixel_t *pdest = s_spanletvars.pdest; zvalue_t *pz = s_spanletvars.pz; zvalue_t izi = s_spanletvars.izi; s_spanletvars.pdest += s_spanletvars.spancount; s_spanletvars.pz += s_spanletvars.spancount; if ( s_spanletvars.spancount == AFFINE_SPANLET_SIZE ) s_spanletvars.izi += s_spanletvars.izistep << AFFINE_SPANLET_SIZE_BITS; else s_spanletvars.izi += s_spanletvars.izistep * s_spanletvars.izistep; if ( r_polydesc.stipple_parity ^ ( s_spanletvars.v & 1 ) ) { if ( r_polydesc.stipple_parity ^ ( s_spanletvars.u & 1 ) ) { izi += s_spanletvars.izistep; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; pdest++; pz++; s_spanletvars.spancount--; } s_spanletvars.sstep *= 2; s_spanletvars.tstep *= 2; while ( s_spanletvars.spancount > 0 ) { unsigned s = s_spanletvars.s >> SHIFT16XYZ; unsigned t = s_spanletvars.t >> SHIFT16XYZ; btemp = *( s_spanletvars.pbase + ( s ) + ( t * cachewidth ) ); if ( btemp != TRANSPARENT_COLOR ) { if ( *pz <= ( izi >> SHIFT16XYZ ) ) *pdest = btemp; } izi += s_spanletvars.izistep_times_2; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; pdest += 2; pz += 2; s_spanletvars.spancount -= 2; } } else { while ( s_spanletvars.spancount > 0 ) { unsigned s = s_spanletvars.s >> SHIFT16XYZ; unsigned t = s_spanletvars.t >> SHIFT16XYZ; btemp = *( s_spanletvars.pbase + ( s ) + ( t * cachewidth ) ); if ( btemp != TRANSPARENT_COLOR ) { if ( *pz <= ( izi >> SHIFT16XYZ ) ) *pdest = btemp; } izi += s_spanletvars.izistep; s_spanletvars.s += s_spanletvars.sstep; s_spanletvars.t += s_spanletvars.tstep; pdest++; pz++; s_spanletvars.spancount--; } } } /* ** R_ClipPolyFace ** ** Clips the winding at clip_verts[clip_current] and changes clip_current ** Throws out the back side */ static int R_ClipPolyFace (int nump, clipplane_t *pclipplane) { int i, outcount; float frac, clipdist, *pclipnormal; float *in, *instep, *outstep, *vert2; float dists[MAXWORKINGVERTS+3] = {0}; clipdist = pclipplane->dist; pclipnormal = pclipplane->normal; // calc dists if (clip_current) { in = r_clip_verts[1][0]; outstep = r_clip_verts[0][0]; clip_current = 0; } else { in = r_clip_verts[0][0]; outstep = r_clip_verts[1][0]; clip_current = 1; } instep = in; for (i=0 ; i= 0) { memcpy (outstep, instep, sizeof (vec5_t)); outstep += sizeof (vec5_t) / sizeof (vec_t); outcount++; } if (dists[i] == 0 || dists[i+1] == 0) continue; if ( (dists[i] > 0) == (dists[i+1] > 0) ) continue; // split it into a new vertex frac = dists[i] / (dists[i] - dists[i+1]); vert2 = instep + sizeof (vec5_t) / sizeof (vec_t); outstep[0] = instep[0] + frac*(vert2[0] - instep[0]); outstep[1] = instep[1] + frac*(vert2[1] - instep[1]); outstep[2] = instep[2] + frac*(vert2[2] - instep[2]); outstep[3] = instep[3] + frac*(vert2[3] - instep[3]); outstep[4] = instep[4] + frac*(vert2[4] - instep[4]); outstep += sizeof (vec5_t) / sizeof (vec_t); outcount++; } return outcount; } /* ** R_PolygonDrawSpans */ // iswater was qboolean. changed to allow passing more flags static void R_PolygonDrawSpans(espan_t *pspan, int iswater, float d_ziorigin, float d_zistepu, float d_zistepv) { int snext, tnext; float sdivz, tdivz, zi, z, du, dv, spancountminus1; float sdivzspanletstepu, tdivzspanletstepu, zispanletstepu; int *r_turb_turb; s_spanletvars.pbase = cacheblock; if ( iswater & SURF_WARP) r_turb_turb = sintable + ((int)(r_newrefdef.time*SPEED)&(CYCLE-1)); else // iswater & SURF_FLOWING r_turb_turb = blanktable; sdivzspanletstepu = d_sdivzstepu * AFFINE_SPANLET_SIZE; tdivzspanletstepu = d_tdivzstepu * AFFINE_SPANLET_SIZE; zispanletstepu = d_zistepu * AFFINE_SPANLET_SIZE; // we count on FP exceptions being turned off to avoid range problems s_spanletvars.izistep = (int)(d_zistepu * 0x8000 * SHIFT16XYZ_MULT); s_spanletvars.izistep_times_2 = s_spanletvars.izistep * 2; s_spanletvars.pz = 0; do { int count; s_spanletvars.pdest = d_viewbuffer + (vid_buffer_width * pspan->v) + pspan->u; s_spanletvars.pz = d_pzbuffer + (vid_buffer_width * pspan->v) + pspan->u; s_spanletvars.u = pspan->u; s_spanletvars.v = pspan->v; count = pspan->count; if (count > 0) { // transparent spans damage z buffer VID_DamageZBuffer(pspan->u, pspan->v); VID_DamageZBuffer(pspan->u + count, pspan->v); // calculate the initial s/z, t/z, 1/z, s, and t and clamp du = (float)pspan->u; dv = (float)pspan->v; sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; z = (float)SHIFT16XYZ_MULT / zi; // prescale to 16.16 fixed-point // we count on FP exceptions being turned off to avoid range problems s_spanletvars.izi = (int)(zi * 0x8000 * SHIFT16XYZ_MULT); s_spanletvars.s = (int)(sdivz * z) + sadjust; s_spanletvars.t = (int)(tdivz * z) + tadjust; if ( !iswater ) { if (s_spanletvars.s > bbextents) s_spanletvars.s = bbextents; else if (s_spanletvars.s < 0) s_spanletvars.s = 0; if (s_spanletvars.t > bbextentt) s_spanletvars.t = bbextentt; else if (s_spanletvars.t < 0) s_spanletvars.t = 0; } do { // calculate s and t at the far end of the span if (count >= AFFINE_SPANLET_SIZE ) s_spanletvars.spancount = AFFINE_SPANLET_SIZE; else s_spanletvars.spancount = count; count -= s_spanletvars.spancount; if (count) { // calculate s/z, t/z, zi->fixed s and t at far end of span, // calculate s and t steps across span by shifting sdivz += sdivzspanletstepu; tdivz += tdivzspanletstepu; zi += zispanletstepu; z = (float)SHIFT16XYZ_MULT / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; tnext = (int)(tdivz * z) + tadjust; if ( !iswater ) { if (snext > bbextents) snext = bbextents; else if (snext < AFFINE_SPANLET_SIZE) snext = AFFINE_SPANLET_SIZE; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture if (tnext > bbextentt) tnext = bbextentt; else if (tnext < AFFINE_SPANLET_SIZE) tnext = AFFINE_SPANLET_SIZE; // guard against round-off error on <0 steps } s_spanletvars.sstep = (snext - s_spanletvars.s) >> AFFINE_SPANLET_SIZE_BITS; s_spanletvars.tstep = (tnext - s_spanletvars.t) >> AFFINE_SPANLET_SIZE_BITS; } else { // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so // can't step off polygon), clamp, calculate s and t steps across // span by division, biasing steps low so we don't run off the // texture spancountminus1 = (float)(s_spanletvars.spancount - 1); sdivz += d_sdivzstepu * spancountminus1; tdivz += d_tdivzstepu * spancountminus1; zi += d_zistepu * spancountminus1; z = (float)SHIFT16XYZ_MULT / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; tnext = (int)(tdivz * z) + tadjust; if ( !iswater ) { if (snext > bbextents) snext = bbextents; else if (snext < AFFINE_SPANLET_SIZE) snext = AFFINE_SPANLET_SIZE; // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture if (tnext > bbextentt) tnext = bbextentt; else if (tnext < AFFINE_SPANLET_SIZE) tnext = AFFINE_SPANLET_SIZE; // guard against round-off error on <0 steps } if (s_spanletvars.spancount > 1) { s_spanletvars.sstep = (snext - s_spanletvars.s) / (s_spanletvars.spancount - 1); s_spanletvars.tstep = (tnext - s_spanletvars.t) / (s_spanletvars.spancount - 1); } } if ( iswater ) { s_spanletvars.s = s_spanletvars.s & ((CYCLE<<16)-1); s_spanletvars.t = s_spanletvars.t & ((CYCLE<<16)-1); } r_polydesc.drawspanlet(r_turb_turb); s_spanletvars.s = snext; s_spanletvars.t = tnext; } while (count > 0); } pspan++; } while (pspan->count != INT_MIN); } /* ** ** R_PolygonScanLeftEdge ** ** Goes through the polygon and scans the left edge, filling in ** screen coordinate data for the spans */ static void R_PolygonScanLeftEdge (espan_t *s_polygon_spans) { int i, lmaxindex; emitpoint_t *pvert, *pnext; espan_t *pspan; float du, dv, vtop, u_step; pspan = s_polygon_spans; i = s_minindex; if (i == 0) i = r_polydesc.nump; lmaxindex = s_maxindex; if (lmaxindex == 0) lmaxindex = r_polydesc.nump; vtop = ceil (r_polydesc.pverts[i].v); do { float vbottom; pvert = &r_polydesc.pverts[i]; pnext = pvert - 1; vbottom = ceil (pnext->v); if (vtop < vbottom) { int v, u, istep, itop, ibottom; du = pnext->u - pvert->u; dv = pnext->v - pvert->v; u_step = (du * SHIFT16XYZ_MULT) / dv; // adjust u to ceil the integer portion u = (int)((pvert->u * SHIFT16XYZ_MULT) + u_step * (vtop - pvert->v)) + (SHIFT16XYZ_MULT - 1); itop = (int)vtop; ibottom = (int)vbottom; istep = (int)u_step; for (v=itop ; vu = u >> SHIFT16XYZ; pspan->v = v; u += istep; pspan++; } } vtop = vbottom; i--; if (i == 0) i = r_polydesc.nump; } while (i != lmaxindex); pspan->count = INT_MIN; // mark the end of the span list } /* ** R_PolygonScanRightEdge ** ** Goes through the polygon and scans the right edge, filling in ** count values. */ static void R_PolygonScanRightEdge(espan_t *s_polygon_spans) { int i; emitpoint_t *pvert, *pnext; espan_t *pspan; float du, dv, vtop, u_step, uvert, unext, vvert; pspan = s_polygon_spans; i = s_minindex; vvert = r_polydesc.pverts[i].v; if (vvert < r_refdef.fvrecty_adj) vvert = r_refdef.fvrecty_adj; if (vvert > r_refdef.fvrectbottom_adj) vvert = r_refdef.fvrectbottom_adj; vtop = ceil (vvert); do { float vbottom, vnext; pvert = &r_polydesc.pverts[i]; pnext = pvert + 1; vnext = pnext->v; if (vnext < r_refdef.fvrecty_adj) vnext = r_refdef.fvrecty_adj; if (vnext > r_refdef.fvrectbottom_adj) vnext = r_refdef.fvrectbottom_adj; vbottom = ceil (vnext); if (vtop < vbottom) { int v, u, istep, itop, ibottom; uvert = pvert->u; if (uvert < r_refdef.fvrectx_adj) uvert = r_refdef.fvrectx_adj; if (uvert > r_refdef.fvrectright_adj) uvert = r_refdef.fvrectright_adj; unext = pnext->u; if (unext < r_refdef.fvrectx_adj) unext = r_refdef.fvrectx_adj; if (unext > r_refdef.fvrectright_adj) unext = r_refdef.fvrectright_adj; du = unext - uvert; dv = vnext - vvert; u_step = (du * SHIFT16XYZ_MULT) / dv; // adjust u to ceil the integer portion u = (int)((uvert * SHIFT16XYZ_MULT) + u_step * (vtop - vvert)) + (SHIFT16XYZ_MULT - 1); itop = (int)vtop; ibottom = (int)vbottom; istep = (int)u_step; for (v=itop ; vcount = (u >> SHIFT16XYZ) - pspan->u; u += istep; pspan++; } } vtop = vbottom; vvert = vnext; i++; if (i == r_polydesc.nump) i = 0; } while (i != s_maxindex); pspan->count = INT_MIN; // mark the end of the span list } /* ** R_ClipAndDrawPoly */ // isturbulent was qboolean. changed to int to allow passing more flags void R_ClipAndDrawPoly ( float alpha, int isturbulent, qboolean textured ) { vec_t *pv; int i, nump; vec3_t transformed, local; if ( !textured ) { r_polydesc.drawspanlet = R_DrawSpanletConstant33; } else { /* ** choose the correct spanlet routine based on alpha */ if ( alpha == 1 ) { // isturbulent is ignored because we know that turbulent surfaces // can't be opaque r_polydesc.drawspanlet = R_DrawSpanletOpaque; } else { if ( sw_stipplealpha->value ) { if ( isturbulent ) { if ( alpha > 0.33 ) r_polydesc.drawspanlet = R_DrawSpanletTurbulentStipple66; else r_polydesc.drawspanlet = R_DrawSpanletTurbulentStipple33; } else { if ( alpha > 0.33 ) r_polydesc.drawspanlet = R_DrawSpanlet66Stipple; else r_polydesc.drawspanlet = R_DrawSpanlet33Stipple; } } else { if ( isturbulent ) { if ( alpha > 0.33 ) r_polydesc.drawspanlet = R_DrawSpanletTurbulentBlended66; else r_polydesc.drawspanlet = R_DrawSpanletTurbulentBlended33; } else { if ( alpha > 0.33 ) r_polydesc.drawspanlet = R_DrawSpanlet66; else r_polydesc.drawspanlet = R_DrawSpanlet33; } } } } // clip to the frustum in worldspace nump = r_polydesc.nump; clip_current = 0; for (i=0 ; i<4 ; i++) { nump = R_ClipPolyFace (nump, &view_clipplanes[i]); if (nump < 3) return; if (nump > MAXWORKINGVERTS) { ri.Sys_Error(ERR_DROP, "%s: too many points: %d", __func__, nump); } } // transform vertices into viewspace and project pv = &r_clip_verts[clip_current][0][0]; for (i=0 ; izi = 1.0 / transformed[2]; pout->s = pv[3]; pout->t = pv[4]; scale = xscale * pout->zi; pout->u = (xcenter + scale * transformed[0]); scale = yscale * pout->zi; pout->v = (ycenter - scale * transformed[1]); pv += sizeof (vec5_t) / sizeof (vec_t); } // draw it r_polydesc.nump = nump; r_polydesc.pverts = outverts; R_DrawPoly(isturbulent, vid_polygon_spans); } /* ** R_BuildPolygonFromSurface */ static void R_BuildPolygonFromSurface(const entity_t *currententity, const model_t *currentmodel, msurface_t *fa) { int i, lnumverts; medge_t *pedges, *r_pedge; float *vec; vec5_t *pverts; float tmins[2] = { 0, 0 }; r_polydesc.nump = 0; // reconstruct the polygon pedges = currentmodel->edges; lnumverts = fa->numedges; pverts = r_clip_verts[0]; for (i=0 ; isurfedges[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; } VectorCopy (vec, pverts[i] ); } VectorCopy( fa->texinfo->vecs[0], r_polydesc.vright ); VectorCopy( fa->texinfo->vecs[1], r_polydesc.vup ); VectorCopy( fa->plane->normal, r_polydesc.vpn ); VectorCopy( r_origin, r_polydesc.viewer_position ); if ( fa->flags & SURF_PLANEBACK ) { VectorSubtract( vec3_origin, r_polydesc.vpn, r_polydesc.vpn ); } if ( fa->texinfo->flags & (SURF_WARP|SURF_FLOWING) ) { r_polydesc.pixels = fa->texinfo->image->pixels[0]; r_polydesc.pixel_width = fa->texinfo->image->width; r_polydesc.pixel_height = fa->texinfo->image->height; } else { surfcache_t *scache; scache = D_CacheSurface(currententity, fa, 0); r_polydesc.pixels = scache->data; r_polydesc.pixel_width = scache->width; r_polydesc.pixel_height = scache->height; tmins[0] = fa->texturemins[0]; tmins[1] = fa->texturemins[1]; } r_polydesc.dist = DotProduct( r_polydesc.vpn, pverts[0] ); r_polydesc.s_offset = fa->texinfo->vecs[0][3] - tmins[0]; r_polydesc.t_offset = fa->texinfo->vecs[1][3] - tmins[1]; // scrolling texture addition if (fa->texinfo->flags & SURF_FLOWING) { r_polydesc.s_offset += -128 * ( (r_newrefdef.time*0.25) - (int)(r_newrefdef.time*0.25) ); } r_polydesc.nump = lnumverts; } /* ** R_PolygonCalculateGradients */ static void R_PolygonCalculateGradients (float *p_ziorigin, float *p_zistepu, float *p_zistepv) { vec3_t p_normal, p_saxis, p_taxis; float distinv; float d_ziorigin, d_zistepu, d_zistepv; TransformVector (r_polydesc.vpn, p_normal); TransformVector (r_polydesc.vright, p_saxis); TransformVector (r_polydesc.vup, p_taxis); distinv = 1.0 / (-(DotProduct (r_polydesc.viewer_position, r_polydesc.vpn)) + r_polydesc.dist ); d_sdivzstepu = p_saxis[0] * xscaleinv; d_sdivzstepv = -p_saxis[1] * yscaleinv; d_sdivzorigin = p_saxis[2] - xcenter * d_sdivzstepu - ycenter * d_sdivzstepv; d_tdivzstepu = p_taxis[0] * xscaleinv; d_tdivzstepv = -p_taxis[1] * yscaleinv; d_tdivzorigin = p_taxis[2] - xcenter * d_tdivzstepu - ycenter * d_tdivzstepv; d_zistepu = p_normal[0] * xscaleinv * distinv; d_zistepv = -p_normal[1] * yscaleinv * distinv; d_ziorigin = p_normal[2] * distinv - xcenter * d_zistepu - ycenter * d_zistepv; sadjust = (int) ( ( DotProduct( r_polydesc.viewer_position, r_polydesc.vright) + r_polydesc.s_offset ) * SHIFT16XYZ_MULT ); tadjust = (int) ( ( DotProduct( r_polydesc.viewer_position, r_polydesc.vup ) + r_polydesc.t_offset ) * SHIFT16XYZ_MULT ); // -1 (-epsilon) so we never wander off the edge of the texture bbextents = (r_polydesc.pixel_width << SHIFT16XYZ) - 1; bbextentt = (r_polydesc.pixel_height << SHIFT16XYZ) - 1; *p_zistepu = d_zistepu; *p_zistepv = d_zistepv; *p_ziorigin = d_ziorigin; } /* ** R_DrawPoly ** ** Polygon drawing function. Uses the polygon described in r_polydesc ** to calculate edges and gradients, then renders the resultant spans. ** ** This should NOT be called externally since it doesn't do clipping! */ // iswater was qboolean. changed to support passing more flags static void R_DrawPoly(int iswater, espan_t *spans) { int i, nump; float ymin, ymax; emitpoint_t *pverts; float d_ziorigin, d_zistepu, d_zistepv; // find the top and bottom vertices, and make sure there's at least one scan to // draw ymin = (float)INT_MAX; // Set maximum values for world range ymax = (float)INT_MIN; // Set minimal values for world range pverts = r_polydesc.pverts; for (i=0 ; iv < ymin) { ymin = pverts->v; s_minindex = i; } if (pverts->v > ymax) { ymax = pverts->v; s_maxindex = i; } pverts++; } ymin = ceil (ymin); ymax = ceil (ymax); if (ymin >= ymax) return; // doesn't cross any scans at all cachewidth = r_polydesc.pixel_width; cacheblock = r_polydesc.pixels; // copy the first vertex to the last vertex, so we don't have to deal with // wrapping nump = r_polydesc.nump; pverts = r_polydesc.pverts; pverts[nump] = pverts[0]; R_PolygonCalculateGradients(&d_ziorigin, &d_zistepu, &d_zistepv); R_PolygonScanLeftEdge(spans); R_PolygonScanRightEdge(spans); R_PolygonDrawSpans(spans, iswater, d_ziorigin, d_zistepu, d_zistepv); } /* ** R_DrawAlphaSurfaces */ void R_DrawAlphaSurfaces(const entity_t *currententity) { msurface_t *s = r_alpha_surfaces; const model_t *currentmodel = r_worldmodel; modelorg[0] = -r_origin[0]; modelorg[1] = -r_origin[1]; modelorg[2] = -r_origin[2]; while ( s ) { R_BuildPolygonFromSurface(currententity, currentmodel, s); // pass down all the texinfo flags, not just SURF_WARP. if (s->texinfo->flags & SURF_TRANS66) R_ClipAndDrawPoly( 0.60f, (s->texinfo->flags & (SURF_WARP|SURF_FLOWING)), true ); else R_ClipAndDrawPoly( 0.30f, (s->texinfo->flags & (SURF_WARP|SURF_FLOWING)), true ); s = s->nextalphasurface; } r_alpha_surfaces = NULL; } /* ** R_IMFlatShadedQuad */ void R_IMFlatShadedQuad( const vec3_t a, const vec3_t b, const vec3_t c, const vec3_t d, int color, float alpha ) { vec3_t s0, s1; r_polydesc.nump = 4; VectorCopy( r_origin, r_polydesc.viewer_position ); VectorCopy( a, r_clip_verts[0][0] ); VectorCopy( b, r_clip_verts[0][1] ); VectorCopy( c, r_clip_verts[0][2] ); VectorCopy( d, r_clip_verts[0][3] ); r_clip_verts[0][0][3] = 0; r_clip_verts[0][1][3] = 0; r_clip_verts[0][2][3] = 0; r_clip_verts[0][3][3] = 0; r_clip_verts[0][0][4] = 0; r_clip_verts[0][1][4] = 0; r_clip_verts[0][2][4] = 0; r_clip_verts[0][3][4] = 0; VectorSubtract( d, c, s0 ); VectorSubtract( c, b, s1 ); CrossProduct( s0, s1, r_polydesc.vpn ); VectorNormalize( r_polydesc.vpn ); r_polydesc.dist = DotProduct( r_polydesc.vpn, r_clip_verts[0][0] ); r_polyblendcolor = color; R_ClipAndDrawPoly( alpha, false, false ); } yquake2-QUAKE2_8_40/src/client/refresh/soft/sw_polyset.c000066400000000000000000000547451465112212000231620ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sw_polyset.c: routines for drawing sets of polygons sharing the same // texture (used for Alias models) #include "header/local.h" #include typedef struct { int numleftedges; compactvert_t *pleftedgevert0; compactvert_t *pleftedgevert1; compactvert_t *pleftedgevert2; int numrightedges; compactvert_t *prightedgevert0; compactvert_t *prightedgevert1; compactvert_t *prightedgevert2; } edgetable; static int ubasestep, errorterm, erroradjustup, erroradjustdown; static compactvert_t r_p0, r_p1, r_p2; static int d_xdenom; static edgetable *pedgetable; static edgetable edgetables[12] = { {1, &r_p0, &r_p2, &r_p2, 2, &r_p0, &r_p1, &r_p2}, {2, &r_p1, &r_p0, &r_p2, 1, &r_p1, &r_p2, &r_p2}, {1, &r_p0, &r_p2, &r_p2, 1, &r_p1, &r_p2, &r_p2}, // flat top {1, &r_p1, &r_p0, &r_p0, 2, &r_p1, &r_p2, &r_p0}, {2, &r_p0, &r_p2, &r_p1, 1, &r_p0, &r_p1, &r_p1}, {1, &r_p2, &r_p1, &r_p1, 1, &r_p2, &r_p0, &r_p0}, {1, &r_p2, &r_p1, &r_p1, 2, &r_p2, &r_p0, &r_p1}, {2, &r_p2, &r_p1, &r_p0, 1, &r_p2, &r_p0, &r_p0}, {1, &r_p1, &r_p0, &r_p0, 1, &r_p1, &r_p2, &r_p2}, {1, &r_p2, &r_p1, &r_p1, 1, &r_p0, &r_p1, &r_p1}, // flat top {1, &r_p1, &r_p0, &r_p0, 1, &r_p2, &r_p0, &r_p0}, // flat top {1, &r_p0, &r_p2, &r_p2, 1, &r_p0, &r_p1, &r_p1}, }; // FIXME: some of these can become statics static int a_sstepxfrac, a_tstepxfrac, a_ststepxwhole; static int r_sstepx, r_tstepx, r_sstepy, r_tstepy; static light3_t r_lstepx, r_lstepy; static zvalue_t r_zistepx, r_zistepy; static int d_aspancount; static spanpackage_t *d_pedgespanpackage; spanpackage_t *triangle_spans, *triangles_max; static int d_sfrac, d_tfrac; static light3_t d_light; static zvalue_t d_zi; static int d_ptexextrastep, d_sfracextrastep; static int d_tfracextrastep, d_ptexbasestep; static light3_t d_lightbasestep, d_lightextrastep; static int d_sfracbasestep, d_tfracbasestep; static zvalue_t d_ziextrastep, d_zibasestep; void (*d_pdrawspans)(const entity_t *currententity, spanpackage_t *pspanpackage); static void R_PolysetSetEdgeTable(void); static void R_RasterizeAliasPolySmooth(const entity_t *currententity); // ====================== // 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 static const byte irtable[256] = { 79, 78, 77, 76, 75, 74, 73, 72, // black/white 71, 70, 69, 68, 67, 66, 65, 64, 64, 65, 66, 67, 68, 69, 70, 71, // dark taupe 72, 73, 74, 75, 76, 77, 78, 79, 64, 65, 66, 67, 68, 69, 70, 71, // slate grey 72, 73, 74, 75, 76, 77, 78, 79, 208, 208, 208, 208, 208, 208, 208, 208, // unused?' 64, 66, 68, 70, 72, 74, 76, 78, // dark yellow 64, 65, 66, 67, 68, 69, 70, 71, // dark red 72, 73, 74, 75, 76, 77, 78, 79, 64, 65, 66, 67, 68, 69, 70, 71, // grey/tan 72, 73, 74, 75, 76, 77, 78, 79, 64, 66, 68, 70, 72, 74, 76, 78, // chocolate 68, 67, 66, 65, 64, 65, 66, 67, // mauve / teal 68, 69, 70, 71, 72, 73, 74, 75, 76, 76, 77, 77, 78, 78, 79, 79, 64, 65, 66, 67, 68, 69, 70, 71, // more mauve 72, 73, 74, 75, 76, 77, 78, 79, 64, 65, 66, 67, 68, 69, 70, 71, // olive 72, 73, 74, 75, 76, 77, 78, 79, 64, 65, 66, 67, 68, 69, 70, 71, // maroon 72, 73, 74, 75, 76, 77, 78, 79, 64, 65, 66, 67, 68, 69, 70, 71, // sky blue 72, 73, 74, 75, 76, 77, 78, 79, 64, 65, 66, 67, 68, 69, 70, 71, // olive again 72, 73, 74, 75, 76, 77, 78, 79, 64, 65, 66, 67, 68, 69, 70, 71, // nuclear green 64, 65, 66, 67, 68, 69, 70, 71, // bright yellow 64, 65, 66, 67, 68, 69, 70, 71, // fire colors 72, 73, 74, 75, 76, 77, 78, 79, 208, 208, 64, 64, 70, 71, 72, 64, // mishmash1 66, 68, 70, 64, 65, 66, 67, 68}; // mishmash2 // ====================== /* ================ R_DrawTriangle ================ */ void R_DrawTriangle(const entity_t *currententity, const finalvert_t *a, const finalvert_t *b, const finalvert_t *c) { int dv1_ab, dv0_ac; int dv0_ab, dv1_ac; /* d_xdenom = ( a->v[1] - b->v[1] ) * ( a->v[0] - c->v[0] ) - ( a->v[0] - b->v[0] ) * ( a->v[1] - c->v[1] ); */ dv0_ab = a->cv.u - b->cv.u; dv1_ab = a->cv.v - b->cv.v; if ( !( dv0_ab | dv1_ab ) ) return; dv0_ac = a->cv.u - c->cv.u; dv1_ac = a->cv.v - c->cv.v; if ( !( dv0_ac | dv1_ac ) ) return; d_xdenom = ( dv0_ac * dv1_ab ) - ( dv0_ab * dv1_ac ); if ( d_xdenom < 0 ) { memcpy(&r_p0, &a->cv, sizeof(compactvert_t)); memcpy(&r_p1, &b->cv, sizeof(compactvert_t)); memcpy(&r_p2, &c->cv, sizeof(compactvert_t)); R_PolysetSetEdgeTable (); R_RasterizeAliasPolySmooth(currententity); } } static void R_PushEdgesSpan(int u, int v, int count, pixel_t* d_ptex, int d_sfrac, int d_tfrac, light3_t d_light, zvalue_t d_zi) { d_pedgespanpackage->u = u; d_pedgespanpackage->v = v; d_pedgespanpackage->count = count; d_pedgespanpackage->ptex = d_ptex; d_pedgespanpackage->sfrac = d_sfrac; d_pedgespanpackage->tfrac = d_tfrac; // FIXME: need to clamp l, s, t, at both ends? memcpy(d_pedgespanpackage->light, d_light, sizeof(light3_t)); d_pedgespanpackage->zi = d_zi; d_pedgespanpackage++; } /* =================== R_PolysetScanLeftEdge_C ==================== */ static void R_PolysetScanLeftEdge_C(int height, pixel_t *d_ptex, int u, int v) { do { R_PushEdgesSpan(u, v, d_aspancount, d_ptex, d_sfrac, d_tfrac, d_light, d_zi); v ++; u += ubasestep; d_aspancount += ubasestep; errorterm += erroradjustup; if (errorterm >= 0) { int i; // addtional step for compensate error u ++; d_aspancount ++; d_ptex += d_ptexextrastep; d_sfrac += d_sfracextrastep; d_ptex += d_sfrac >> SHIFT16XYZ; d_sfrac &= 0xFFFF; d_tfrac += d_tfracextrastep; if (d_tfrac & 0x10000) { d_ptex += r_affinetridesc.skinwidth; d_tfrac &= 0xFFFF; } for(i=0; i<3; i++) d_light[i] += d_lightextrastep[i]; d_zi += d_ziextrastep; errorterm -= erroradjustdown; } else { int i; d_ptex += d_ptexbasestep; d_sfrac += d_sfracbasestep; d_ptex += d_sfrac >> SHIFT16XYZ; d_sfrac &= 0xFFFF; d_tfrac += d_tfracbasestep; if (d_tfrac & 0x10000) { d_ptex += r_affinetridesc.skinwidth; d_tfrac &= 0xFFFF; } for(i=0; i<3; i++) d_light[i] += d_lightbasestep[i]; d_zi += d_zibasestep; } } while (--height); } /* =================== FloorDivMod Returns mathematically correct (floor-based) quotient and remainder for numer and denom, both of which should contain no fractional part. The quotient must fit in 32 bits. FIXME: GET RID OF THIS! (FloorDivMod) ==================== */ static void FloorDivMod(int numer, int denom, int *quo, int *rem) { int q, r; // just exclude ARM32 with FPU, e.g. Pi 1 #if defined(__i386__) || defined(__amd64__) q = numer / denom; r = numer - q * denom; if (-1/2 || 1/-2 || -1/-2) { // long live C89 if (r < 0 && r < denom) { q += 1; r -= denom; } } else { // C99 always truncates to 0 if ((numer ^ denom) < 0 && r != 0) { q -= 1; r += denom; } } if ((numer < 0) ^ (denom < 0)) { assert(q <= 0); } else { assert(q >= 0); } if (denom < 0) { assert(r > denom && r <= 0); } else { assert(r >= 0 && r < denom); } #else float num = numer, den = denom; float x; if (numer >= 0) { x = floor(num / den); q = (int)x; r = (int)floor(num - (x * den)); } else { // // perform operations with positive values, and fix mod to make floor-based // x = floor(-num / den); q = -(int)x; r = (int)floor(-num - (x * den)); if (r != 0) { q--; r = denom - r; } } #endif *quo = q; *rem = r; } /* =================== R_PolysetSetUpForLineScan ==================== */ static void R_PolysetSetUpForLineScan(fixed8_t startvertu, fixed8_t startvertv, fixed8_t endvertu, fixed8_t endvertv) { int tm, tn; tm = endvertu - startvertu; tn = endvertv - startvertv; errorterm = -1; erroradjustdown = tn; FloorDivMod (tm, tn, &ubasestep, &erroradjustup); } /* ================ R_PolysetCalcGradients ================ */ static void R_PolysetCalcGradients (int skinwidth) { float xstepdenominv, ystepdenominv, t0, t1; float p01_minus_p21, p11_minus_p21, p00_minus_p20, p10_minus_p20; int i; p00_minus_p20 = r_p0.u - r_p2.u; p01_minus_p21 = r_p0.v - r_p2.v; p10_minus_p20 = r_p1.u - r_p2.u; p11_minus_p21 = r_p1.v - r_p2.v; xstepdenominv = 1.0 / (float)d_xdenom; ystepdenominv = -xstepdenominv; // ceil () for light so positive steps are exaggerated, negative steps // diminished, pushing us away from underflow toward overflow. Underflow is // very visible, overflow is very unlikely, because of ambient lighting for (i=0; i<3; i++) { t0 = r_p0.l[i] - r_p2.l[i]; t1 = r_p1.l[i] - r_p2.l[i]; r_lstepx[i] = (int) ceil((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); r_lstepy[i] = (int) ceil((t1 * p00_minus_p20 - t0 * p10_minus_p20) * ystepdenominv); } t0 = r_p0.s - r_p2.s; t1 = r_p1.s - r_p2.s; r_sstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); r_sstepy = (int)((t1 * p00_minus_p20 - t0* p10_minus_p20) * ystepdenominv); t0 = r_p0.t - r_p2.t; t1 = r_p1.t - r_p2.t; r_tstepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); r_tstepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * ystepdenominv); t0 = r_p0.zi - r_p2.zi; t1 = r_p1.zi - r_p2.zi; r_zistepx = (int)((t1 * p01_minus_p21 - t0 * p11_minus_p21) * xstepdenominv); r_zistepy = (int)((t1 * p00_minus_p20 - t0 * p10_minus_p20) * ystepdenominv); { a_sstepxfrac = r_sstepx & 0xFFFF; a_tstepxfrac = r_tstepx & 0xFFFF; } a_ststepxwhole = skinwidth * (r_tstepx >> SHIFT16XYZ) + (r_sstepx >> SHIFT16XYZ); } /* ================ R_PolysetDrawSpans8 ================ */ void R_PolysetDrawSpans8_33(const entity_t *currententity, spanpackage_t *pspanpackage) { pixel_t *lpdest; pixel_t *lptex; int lsfrac, ltfrac; light3_t llight; zvalue_t lzi; zvalue_t *lpz; do { int lcount; lcount = d_aspancount - pspanpackage->count; errorterm += erroradjustup; d_aspancount += ubasestep; if (errorterm >= 0) { // addtional step for compensate error d_aspancount ++; errorterm -= erroradjustdown; } if (lcount > 0) { int pos_shift = (pspanpackage->v * vid_buffer_width) + pspanpackage->u; lpdest = d_viewbuffer + pos_shift; lpz = d_pzbuffer + pos_shift; lptex = pspanpackage->ptex; lsfrac = pspanpackage->sfrac; ltfrac = pspanpackage->tfrac; memcpy(llight, pspanpackage->light, sizeof(light3_t)); lzi = pspanpackage->zi; do { int i; if ((lzi >> SHIFT16XYZ) >= *lpz) { int temp = R_ApplyLight(*lptex, llight); *lpdest = vid_alphamap[temp + *lpdest*256]; } lpdest++; lzi += r_zistepx; lpz++; for(i=0; i<3; i++) llight[i] += r_lstepx[i]; lptex += a_ststepxwhole; lsfrac += a_sstepxfrac; lptex += lsfrac >> SHIFT16XYZ; lsfrac &= 0xFFFF; ltfrac += a_tstepxfrac; if (ltfrac & 0x10000) { lptex += r_affinetridesc.skinwidth; ltfrac &= 0xFFFF; } } while (--lcount); } pspanpackage++; } while (pspanpackage->count != INT_MIN); } void R_PolysetDrawSpansConstant8_33(const entity_t *currententity, spanpackage_t *pspanpackage) { pixel_t *lpdest; int lzi; zvalue_t *lpz; do { int lcount; lcount = d_aspancount - pspanpackage->count; errorterm += erroradjustup; d_aspancount += ubasestep; if (errorterm >= 0) { // addtional step for compensate error d_aspancount ++; errorterm -= erroradjustdown; } if (lcount > 0) { int pos_shift = (pspanpackage->v * vid_buffer_width) + pspanpackage->u; lpdest = d_viewbuffer + pos_shift; lpz = d_pzbuffer + pos_shift; lzi = pspanpackage->zi; do { if ((lzi >> SHIFT16XYZ) >= *lpz) { *lpdest = vid_alphamap[r_aliasblendcolor + *lpdest*256]; } lpdest++; lzi += r_zistepx; lpz++; } while (--lcount); } pspanpackage++; } while (pspanpackage->count != INT_MIN); } void R_PolysetDrawSpans8_66(const entity_t *currententity, spanpackage_t *pspanpackage) { pixel_t *lpdest; pixel_t *lptex; int lsfrac, ltfrac; light3_t llight; zvalue_t lzi; zvalue_t *lpz; do { int lcount; lcount = d_aspancount - pspanpackage->count; errorterm += erroradjustup; d_aspancount += ubasestep; if (errorterm >= 0) { // addtional step for compensate error d_aspancount ++; errorterm -= erroradjustdown; } if (lcount > 0) { int pos_shift = (pspanpackage->v * vid_buffer_width) + pspanpackage->u; qboolean zdamaged = false; lpdest = d_viewbuffer + pos_shift; lpz = d_pzbuffer + pos_shift; lptex = pspanpackage->ptex; lsfrac = pspanpackage->sfrac; ltfrac = pspanpackage->tfrac; memcpy(llight, pspanpackage->light, sizeof(light3_t)); lzi = pspanpackage->zi; do { int i; if ((lzi >> SHIFT16XYZ) >= *lpz) { int temp = R_ApplyLight(*lptex, llight); *lpdest = vid_alphamap[temp*256 + *lpdest]; *lpz = lzi >> SHIFT16XYZ; zdamaged = true; } lpdest++; lzi += r_zistepx; lpz++; for(i=0; i<3; i++) llight[i] += r_lstepx[i]; lptex += a_ststepxwhole; lsfrac += a_sstepxfrac; lptex += lsfrac >> SHIFT16XYZ; lsfrac &= 0xFFFF; ltfrac += a_tstepxfrac; if (ltfrac & 0x10000) { lptex += r_affinetridesc.skinwidth; ltfrac &= 0xFFFF; } } while (--lcount); if (zdamaged) { // damaged only current line VID_DamageZBuffer(pspanpackage->u, pspanpackage->v); VID_DamageZBuffer( pspanpackage->u + d_aspancount - pspanpackage->count, pspanpackage->v); } } pspanpackage++; } while (pspanpackage->count != INT_MIN); } void R_PolysetDrawSpansConstant8_66(const entity_t *currententity, spanpackage_t *pspanpackage) { pixel_t *lpdest; zvalue_t lzi; zvalue_t *lpz; do { int lcount; lcount = d_aspancount - pspanpackage->count; errorterm += erroradjustup; d_aspancount += ubasestep; if (errorterm >= 0) { // addtional step for compensate error d_aspancount ++; errorterm -= erroradjustdown; } if (lcount > 0) { int pos_shift = (pspanpackage->v * vid_buffer_width) + pspanpackage->u; qboolean zdamaged = false; lpdest = d_viewbuffer + pos_shift; lpz = d_pzbuffer + pos_shift; lzi = pspanpackage->zi; do { if ((lzi >> SHIFT16XYZ) >= *lpz) { *lpdest = vid_alphamap[r_aliasblendcolor*256 + *lpdest]; zdamaged = true; } lpdest++; lzi += r_zistepx; lpz++; } while (--lcount); if (zdamaged) { // damaged only current line VID_DamageZBuffer(pspanpackage->u, pspanpackage->v); VID_DamageZBuffer( pspanpackage->u + d_aspancount - pspanpackage->count, pspanpackage->v); } } pspanpackage++; } while (pspanpackage->count != INT_MIN); } void R_PolysetDrawSpans8_Opaque (const entity_t *currententity, spanpackage_t *pspanpackage) { do { int lcount; lcount = d_aspancount - pspanpackage->count; errorterm += erroradjustup; d_aspancount += ubasestep; if (errorterm >= 0) { // addtional step for compensate error d_aspancount ++; errorterm -= erroradjustdown; } if (lcount > 0) { int lsfrac, ltfrac; pixel_t *lpdest; pixel_t *lptex; light3_t llight; zvalue_t lzi; zvalue_t *lpz; int pos_shift = (pspanpackage->v * vid_buffer_width) + pspanpackage->u; qboolean zdamaged = false; lpdest = d_viewbuffer + pos_shift; lpz = d_pzbuffer + pos_shift; lptex = pspanpackage->ptex; lsfrac = pspanpackage->sfrac; ltfrac = pspanpackage->tfrac; memcpy(llight, pspanpackage->light, sizeof(light3_t)); lzi = pspanpackage->zi; do { int i; if ((lzi >> SHIFT16XYZ) >= *lpz) { if(r_newrefdef.rdflags & RDF_IRGOGGLES && currententity->flags & RF_IR_VISIBLE) *lpdest = vid_colormap[irtable[*lptex]]; else *lpdest = R_ApplyLight(*lptex, llight); *lpz = lzi >> SHIFT16XYZ; zdamaged = true; } lpdest++; lzi += r_zistepx; lpz++; for(i=0; i<3; i++) llight[i] += r_lstepx[i]; lptex += a_ststepxwhole; lsfrac += a_sstepxfrac; lptex += lsfrac >> SHIFT16XYZ; lsfrac &= 0xFFFF; ltfrac += a_tstepxfrac; if (ltfrac & 0x10000) { lptex += r_affinetridesc.skinwidth; ltfrac &= 0xFFFF; } } while (--lcount); if (zdamaged) { // damaged only current line VID_DamageZBuffer(pspanpackage->u, pspanpackage->v); VID_DamageZBuffer( pspanpackage->u + d_aspancount - pspanpackage->count, pspanpackage->v); } } pspanpackage++; } while (pspanpackage->count != INT_MIN); } static void R_ProcessLeftEdge(const compactvert_t *plefttop, const compactvert_t *prighttop, const compactvert_t *pleftbottom) { light3_t working_lstepx; pixel_t *d_ptex; int u, v; int s, t; int height; int i; u = plefttop->u; v = plefttop->v; d_aspancount = plefttop->u - prighttop->u; s = plefttop->s * r_affinetridesc.scalewidth; t = plefttop->t * r_affinetridesc.scaleheight; i = (s >> SHIFT16XYZ) + (t >> SHIFT16XYZ) * r_affinetridesc.skinwidth; d_ptex = &r_affinetridesc.pskin[i]; d_sfrac = s & 0xFFFF; d_tfrac = t & 0xFFFF; memcpy(d_light, plefttop->l, sizeof(light3_t)); d_zi = plefttop->zi; height = pleftbottom->v - plefttop->v; if (height == 1) { // skip calculations needed for 2+ spans R_PushEdgesSpan(u, v, d_aspancount, d_ptex, d_sfrac, d_tfrac, d_light, d_zi); return; } R_PolysetSetUpForLineScan(plefttop->u, plefttop->v, pleftbottom->u, pleftbottom->v); // for negative steps in x along left edge, bias toward overflow rather than // underflow (sort of turning the floor () we did in the gradient calcs into // ceil (), but plus a little bit) t = ubasestep < 0; for (i = 0; i < 3; i++) { working_lstepx[i] = r_lstepx[i] - t; } // base steps for drawers s = r_sstepy + r_sstepx * ubasestep; t = r_tstepy + r_tstepx * ubasestep; d_ptexbasestep = (s >> SHIFT16XYZ) + (t >> SHIFT16XYZ) * r_affinetridesc.skinwidth; d_sfracbasestep = s & 0xFFFF; d_tfracbasestep = t & 0xFFFF; for (i = 0; i < 3; i++) { d_lightbasestep[i] = r_lstepy[i] + working_lstepx[i] * ubasestep; } d_zibasestep = r_zistepy + r_zistepx * ubasestep; // extra steps for drawers s += r_sstepx; t += r_tstepx; d_ptexextrastep = (s >> SHIFT16XYZ) + (t >> SHIFT16XYZ) * r_affinetridesc.skinwidth; d_sfracextrastep = s & 0xFFFF; d_tfracextrastep = t & 0xFFFF; for (i = 0; i < 3; i++) { d_lightextrastep[i] = d_lightbasestep[i] + working_lstepx[i]; } d_ziextrastep = d_zibasestep + r_zistepx; R_PolysetScanLeftEdge_C(height, d_ptex, u, v); } /* ================ R_RasterizeAliasPolySmooth ================ */ static void R_RasterizeAliasPolySmooth(const entity_t *currententity) { const compactvert_t *pleftbottom, *prightbottom; const compactvert_t *plefttop, *prighttop; spanpackage_t *pstart; int originalcount; int leftheight, rightheight; // set the s, t, and light gradients, which are consistent across // the triangle because being a triangle, things are affine R_PolysetCalcGradients (r_affinetridesc.skinwidth); plefttop = pedgetable->pleftedgevert0; prighttop = pedgetable->prightedgevert0; pleftbottom = pedgetable->pleftedgevert1; prightbottom = pedgetable->prightedgevert1; // make sure we have enough spans for the full height // plus 1 more for the list terminator. // vert2 in the edgetable is always the lowest. // (this is silly anyway, just allocate vid.height+1) leftheight = pedgetable->pleftedgevert2->v - plefttop->v; rightheight = pedgetable->prightedgevert2->v - prighttop->v; if (leftheight < rightheight) leftheight = rightheight; if (&triangle_spans[leftheight] >= triangles_max) { r_outoftriangles = true; return; } // init span allocation ptr d_pedgespanpackage = triangle_spans; // 1. scan out the top (and possibly only) part of the left edge R_ProcessLeftEdge(plefttop, prighttop, pleftbottom); // 2. scan out the bottom part of the left edge, if it exists if (pedgetable->numleftedges == 2) { plefttop = pleftbottom; pleftbottom = pedgetable->pleftedgevert2; R_ProcessLeftEdge(plefttop, prighttop, pleftbottom); } assert(d_pedgespanpackage <= triangles_max); // 3. scan out the top (and possibly only) part of the right edge, // drawing spans that terminate on it rightheight = prightbottom->v - prighttop->v; // reset u delta before drawing the top d_aspancount = 0; // save for step 4 pstart = &triangle_spans[rightheight]; originalcount = pstart->count; // mark end of the spanpackages pstart->count = INT_MIN; R_PolysetSetUpForLineScan(prighttop->u, prighttop->v, prightbottom->u, prightbottom->v); (*d_pdrawspans) (currententity, triangle_spans); // 4. scan out the bottom part of the right edge, if it exists if (pedgetable->numrightedges == 2) { // 4.1 restore the starting span count pstart->count = originalcount; d_aspancount = prightbottom->u - prighttop->u; // 4.2 shift verts like in step 2 prighttop = prightbottom; prightbottom = pedgetable->prightedgevert2; // 4.3 increase right height rightheight += prightbottom->v - prighttop->v; // mark end of the spanpackages triangle_spans[rightheight].count = INT_MIN; R_PolysetSetUpForLineScan(prighttop->u, prighttop->v, prightbottom->u, prightbottom->v); (*d_pdrawspans) (currententity, pstart); } } /* ================ R_PolysetSetEdgeTable ================ */ static void R_PolysetSetEdgeTable(void) { int edgetableindex; edgetableindex = 0; // assume the vertices are already in // top to bottom order // // determine which edges are right & left, and the order in which // to rasterize them // if (r_p0.v >= r_p1.v) { if (r_p0.v == r_p1.v) { if (r_p0.v < r_p2.v) pedgetable = &edgetables[2]; else pedgetable = &edgetables[5]; return; } else { edgetableindex = 1; } } if (r_p0.v == r_p2.v) { if (edgetableindex) pedgetable = &edgetables[8]; else pedgetable = &edgetables[9]; return; } else if (r_p1.v == r_p2.v) { if (edgetableindex) pedgetable = &edgetables[10]; else pedgetable = &edgetables[11]; return; } if (r_p0.v > r_p2.v) edgetableindex += 2; if (r_p1.v > r_p2.v) edgetableindex += 4; pedgetable = &edgetables[edgetableindex]; } yquake2-QUAKE2_8_40/src/client/refresh/soft/sw_rast.c000066400000000000000000000473421465112212000224270ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sw_rast.c #include #include #include "header/local.h" #define MAXLEFTCLIPEDGES 100 #define FULLY_CLIPPED_CACHED 0x80000000 #define FRAMECOUNT_MASK 0x7FFFFFFF static unsigned int cacheoffset; int c_faceclip; // number of faces clipped clipplane_t view_clipplanes[4]; edge_t *r_edges = NULL, *edge_p = NULL, *edge_max = NULL; surf_t *surfaces = NULL, *surface_p = NULL, *surf_max = NULL; int *sintable; int *intsintable; int *blanktable; static mvertex_t r_leftenter, r_leftexit; static mvertex_t r_rightenter, r_rightexit; static int r_emitted; static float r_nearzi; static float r_u1, r_v1, r_lzi1; static int r_ceilv1; static qboolean r_lastvertvalid; static int r_skyframe; static msurface_t *r_skyfaces; static cplane_t r_skyplanes[6]; mtexinfo_t r_skytexinfo[6]; static mvertex_t *r_skyverts; static medge_t *r_skyedges; static int *r_skysurfedges; // I just copied this data from a box map... static const int skybox_planes[12] = {2,-128, 0,-128, 2,128, 1,128, 0,128, 1,-128}; static const int box_surfedges[24] = { 1,2,3,4, -1,5,6,7, 8,9,-6,10, -2,-7,-9,11, 12,-3,-11,-8, -12,-10,-5,-4}; static const int box_edges[24] = { 1,2, 2,3, 3,4, 4,1, 1,5, 5,6, 6,2, 7,8, 8,6, 5,7, 8,3, 7,4}; static const int box_faces[6] = { 0, 0, SURF_PLANEBACK, SURF_PLANEBACK, SURF_PLANEBACK, 0 }; static const vec3_t box_vecs[6][2] = { { {0,-1,0}, {-1,0,0} }, { {0,1,0}, {0,0,-1} }, { {0,-1,0}, {1,0,0} }, { {1,0,0}, {0,0,-1} }, { {0,-1,0}, {0,0,-1} }, { {-1,0,0}, {0,0,-1} } }; static const float box_verts[8][3] = { {-1,-1,-1}, {-1,1,-1}, {1,1,-1}, {1,-1,-1}, {-1,-1,1}, {-1,1,1}, {1,-1,1}, {1,1,1} }; // down, west, up, north, east, south // {"rt", "bk", "lf", "ft", "up", "dn"}; /* ================ R_InitSkyBox ================ */ void R_InitSkyBox (model_t *loadmodel) { int i; r_skyfaces = loadmodel->surfaces + loadmodel->numsurfaces; loadmodel->numsurfaces += 6; r_skyverts = loadmodel->vertexes + loadmodel->numvertexes; loadmodel->numvertexes += 8; r_skyedges = loadmodel->edges + loadmodel->numedges; loadmodel->numedges += 12; r_skysurfedges = loadmodel->surfedges + loadmodel->numsurfedges; loadmodel->numsurfedges += 24; if (loadmodel->numsurfaces > MAX_MAP_FACES || loadmodel->numvertexes > MAX_MAP_VERTS || loadmodel->numedges > MAX_MAP_EDGES) ri.Sys_Error (ERR_DROP, "InitSkyBox: map overflow"); memset (r_skyfaces, 0, 6*sizeof(*r_skyfaces)); for (i=0 ; i<6 ; i++) { r_skyplanes[i].normal[skybox_planes[i*2]] = 1; r_skyplanes[i].dist = skybox_planes[i*2+1]; VectorCopy (box_vecs[i][0], r_skytexinfo[i].vecs[0]); VectorCopy (box_vecs[i][1], r_skytexinfo[i].vecs[1]); r_skyfaces[i].plane = &r_skyplanes[i]; r_skyfaces[i].numedges = 4; r_skyfaces[i].flags = box_faces[i] | SURF_DRAWSKY; r_skyfaces[i].firstedge = loadmodel->numsurfedges-24+i*4; r_skyfaces[i].texinfo = &r_skytexinfo[i]; r_skyfaces[i].texturemins[0] = -128; r_skyfaces[i].texturemins[1] = -128; r_skyfaces[i].extents[0] = 256; r_skyfaces[i].extents[1] = 256; } for (i=0 ; i<24 ; i++) if (box_surfedges[i] > 0) r_skysurfedges[i] = loadmodel->numedges-13 + box_surfedges[i]; else r_skysurfedges[i] = - (loadmodel->numedges-13 + -box_surfedges[i]); for(i=0 ; i<12 ; i++) { r_skyedges[i].v[0] = loadmodel->numvertexes-9+box_edges[i*2+0]; r_skyedges[i].v[1] = loadmodel->numvertexes-9+box_edges[i*2+1]; r_skyedges[i].cachededgeoffset = 0; } } /* ================ R_EmitSkyBox ================ */ static void R_EmitSkyBox(entity_t *currententity, const model_t *currentmodel, qboolean insubmodel) { int i, j; int oldkey; if (insubmodel) return; // submodels should never have skies if (r_skyframe == r_framecount) return; // already set this frame r_skyframe = r_framecount; // set the eight fake vertexes for (i=0 ; i<8 ; i++) for (j=0 ; j<3 ; j++) r_skyverts[i].position[j] = r_origin[j] + box_verts[i][j]*128; // set the six fake planes for (i=0 ; i<6 ; i++) if (skybox_planes[i*2+1] > 0) r_skyplanes[i].dist = r_origin[skybox_planes[i*2]]+128; else r_skyplanes[i].dist = r_origin[skybox_planes[i*2]]-128; // fix texture offseets for (i=0 ; i<6 ; i++) { r_skytexinfo[i].vecs[0][3] = -DotProduct (r_origin, r_skytexinfo[i].vecs[0]); r_skytexinfo[i].vecs[1][3] = -DotProduct (r_origin, r_skytexinfo[i].vecs[1]); } // emit the six faces oldkey = r_currentkey; r_currentkey = 0x7ffffff0; for (i=0 ; i<6 ; i++) { R_RenderFace(currententity, currentmodel, r_skyfaces + i, ALIAS_XY_CLIP_MASK, insubmodel); } r_currentkey = oldkey; // bsp sorting order } /* ================ R_EmitEdge ================ */ static void R_EmitEdge (mvertex_t *pv0, mvertex_t *pv1, medge_t *r_pedge, qboolean r_nearzionly) { edge_t *edge, *pcheck; int u_check; float u, u_step; vec3_t local, transformed; float *world; int v, v2, ceilv0; float scale, lzi0, u0, v0; int side; if (r_lastvertvalid) { u0 = r_u1; v0 = r_v1; lzi0 = r_lzi1; ceilv0 = r_ceilv1; } else { world = &pv0->position[0]; // transform and project VectorSubtract (world, modelorg, local); TransformVector (local, transformed); if (transformed[2] < NEAR_CLIP) transformed[2] = NEAR_CLIP; lzi0 = 1.0 / transformed[2]; // FIXME: build x/yscale into transform? scale = xscale * lzi0; u0 = (xcenter + scale*transformed[0]); if (u0 < r_refdef.fvrectx_adj) u0 = r_refdef.fvrectx_adj; if (u0 > r_refdef.fvrectright_adj) u0 = r_refdef.fvrectright_adj; scale = yscale * lzi0; v0 = (ycenter - scale*transformed[1]); if (v0 < r_refdef.fvrecty_adj) v0 = r_refdef.fvrecty_adj; if (v0 > r_refdef.fvrectbottom_adj) v0 = r_refdef.fvrectbottom_adj; ceilv0 = (int) ceil(v0); } world = &pv1->position[0]; // transform and project VectorSubtract (world, modelorg, local); TransformVector (local, transformed); if (transformed[2] < NEAR_CLIP) transformed[2] = NEAR_CLIP; r_lzi1 = 1.0 / transformed[2]; scale = xscale * r_lzi1; r_u1 = (xcenter + scale*transformed[0]); if (r_u1 < r_refdef.fvrectx_adj) r_u1 = r_refdef.fvrectx_adj; if (r_u1 > r_refdef.fvrectright_adj) r_u1 = r_refdef.fvrectright_adj; scale = yscale * r_lzi1; r_v1 = (ycenter - scale*transformed[1]); if (r_v1 < r_refdef.fvrecty_adj) r_v1 = r_refdef.fvrecty_adj; if (r_v1 > r_refdef.fvrectbottom_adj) r_v1 = r_refdef.fvrectbottom_adj; if (r_lzi1 > lzi0) lzi0 = r_lzi1; if (lzi0 > r_nearzi) // for mipmap finding r_nearzi = lzi0; // for right edges, all we want is the effect on 1/z if (r_nearzionly) return; r_emitted = 1; r_ceilv1 = (int) ceil(r_v1); // create the edge if (ceilv0 == r_ceilv1) { // we cache unclipped horizontal edges as fully clipped if (cacheoffset != 0x7FFFFFFF) { cacheoffset = FULLY_CLIPPED_CACHED | (r_framecount & FRAMECOUNT_MASK); } return; // horizontal edge } side = ceilv0 > r_ceilv1; edge = edge_p++; edge->owner = r_pedge; edge->nearzi = lzi0; if (side == 0) { // trailing edge (go from p1 to p2) v = ceilv0; v2 = r_ceilv1 - 1; edge->surfs[0] = surface_p - surfaces; edge->surfs[1] = 0; u_step = ((r_u1 - u0) / (r_v1 - v0)); u = u0 + ((float)v - v0) * u_step; } else { // leading edge (go from p2 to p1) v2 = ceilv0 - 1; v = r_ceilv1; edge->surfs[0] = 0; edge->surfs[1] = surface_p - surfaces; u_step = ((u0 - r_u1) / (v0 - r_v1)); u = r_u1 + ((float)v - r_v1) * u_step; } edge->u_step = u_step*(1<u = u*(1<u < r_refdef.vrect_x_adj_shift20) { edge->u = r_refdef.vrect_x_adj_shift20; } else if (edge->u > r_refdef.vrectright_adj_shift20) { edge->u = r_refdef.vrectright_adj_shift20; } // // sort the edge in normally // u_check = edge->u; if (edge->surfs[0]) u_check++; // sort trailers after leaders if (!newedges[v] || newedges[v]->u >= u_check) { edge->next = newedges[v]; newedges[v] = edge; } else { pcheck = newedges[v]; while (pcheck->next && pcheck->next->u < u_check) pcheck = pcheck->next; edge->next = pcheck->next; pcheck->next = edge; } edge->nextremove = removeedges[v2]; removeedges[v2] = edge; } /* ================ R_ClipEdge ================ */ static void R_ClipEdge (mvertex_t *pv0, mvertex_t *pv1, clipplane_t *clip, medge_t *r_pedge, qboolean *r_leftclipped, qboolean *r_rightclipped, qboolean r_nearzionly) { if (clip) { do { float d0, d1, f; mvertex_t clipvert; d0 = DotProduct (pv0->position, clip->normal) - clip->dist; d1 = DotProduct (pv1->position, clip->normal) - clip->dist; if (d0 >= 0) { // point 0 is unclipped if (d1 >= 0) { // both points are unclipped continue; } // only point 1 is clipped // we don't cache clipped edges cacheoffset = 0x7FFFFFFF; f = d0 / (d0 - d1); clipvert.position[0] = pv0->position[0] + f * (pv1->position[0] - pv0->position[0]); clipvert.position[1] = pv0->position[1] + f * (pv1->position[1] - pv0->position[1]); clipvert.position[2] = pv0->position[2] + f * (pv1->position[2] - pv0->position[2]); if (clip->leftedge) { *r_leftclipped = true; r_leftexit = clipvert; } else if (clip->rightedge) { *r_rightclipped = true; r_rightexit = clipvert; } R_ClipEdge (pv0, &clipvert, clip->next, r_pedge, r_leftclipped, r_rightclipped, r_nearzionly); return; } else { // point 0 is clipped if (d1 < 0) { // both points are clipped // we do cache fully clipped edges if (!r_leftclipped) cacheoffset = FULLY_CLIPPED_CACHED | (r_framecount & FRAMECOUNT_MASK); return; } // only point 0 is clipped r_lastvertvalid = false; // we don't cache partially clipped edges cacheoffset = 0x7FFFFFFF; f = d0 / (d0 - d1); clipvert.position[0] = pv0->position[0] + f * (pv1->position[0] - pv0->position[0]); clipvert.position[1] = pv0->position[1] + f * (pv1->position[1] - pv0->position[1]); clipvert.position[2] = pv0->position[2] + f * (pv1->position[2] - pv0->position[2]); if (clip->leftedge) { *r_leftclipped = true; r_leftenter = clipvert; } else if (clip->rightedge) { *r_rightclipped = true; r_rightenter = clipvert; } R_ClipEdge (&clipvert, pv1, clip->next, r_pedge, r_leftclipped, r_rightclipped, r_nearzionly); return; } } while ((clip = clip->next) != NULL); } // add the edge R_EmitEdge (pv0, pv1, r_pedge, r_nearzionly); } /* ================ R_EmitCachedEdge ================ */ static void R_EmitCachedEdge (medge_t *r_pedge) { edge_t *pedge_t; pedge_t = (edge_t *)((uintptr_t)r_edges + r_pedge->cachededgeoffset); if (!pedge_t->surfs[0]) pedge_t->surfs[0] = surface_p - surfaces; else pedge_t->surfs[1] = surface_p - surfaces; if (pedge_t->nearzi > r_nearzi) // for mipmap finding r_nearzi = pedge_t->nearzi; r_emitted = 1; } /* ================ R_RenderFace ================ */ void R_RenderFace (entity_t *currententity, const model_t *currentmodel, msurface_t *fa, int clipflags, qboolean insubmodel) { int i; unsigned mask; cplane_t *pplane; float distinv; vec3_t p_normal; medge_t *pedges, tedge; clipplane_t *pclip; qboolean r_leftclipped, r_rightclipped; qboolean makeleftedge, makerightedge; qboolean r_nearzionly; // translucent surfaces are not drawn by the edge renderer if (fa->texinfo->flags & (SURF_TRANS33|SURF_TRANS66)) { fa->nextalphasurface = r_alpha_surfaces; r_alpha_surfaces = fa; return; } // sky surfaces encountered in the world will cause the // environment box surfaces to be emited if ( fa->texinfo->flags & SURF_SKY ) { R_EmitSkyBox (currententity, currentmodel, insubmodel); return; } // skip out if no more surfs if ((surface_p) >= surf_max) { r_outofsurfaces = true; return; } // ditto if not enough edges left if ((edge_p + fa->numedges + 4) >= edge_max) { r_outofedges = true; return; } c_faceclip++; // set up clip planes pclip = NULL; for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1) { if (clipflags & mask) { view_clipplanes[i].next = pclip; pclip = &view_clipplanes[i]; } } // push the edges through r_emitted = 0; r_nearzi = 0; r_nearzionly = false; makeleftedge = makerightedge = false; pedges = currentmodel->edges; r_lastvertvalid = false; for (i=0 ; inumedges ; i++) { int lindex; lindex = currentmodel->surfedges[fa->firstedge + i]; if (lindex > 0) { medge_t *r_pedge = &pedges[lindex]; // if the edge is cached, we can just reuse the edge if (!insubmodel) { if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) { if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == r_framecount) { r_lastvertvalid = false; continue; } } else { if ((((uintptr_t)edge_p - (uintptr_t)r_edges) > r_pedge->cachededgeoffset) && (((edge_t *)((uintptr_t)r_edges + r_pedge->cachededgeoffset))->owner == r_pedge)) { R_EmitCachedEdge (r_pedge); r_lastvertvalid = false; continue; } } } // assume it's cacheable cacheoffset = (byte *)edge_p - (byte *)r_edges; r_leftclipped = r_rightclipped = false; R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[0]], &r_pcurrentvertbase[r_pedge->v[1]], pclip, r_pedge, &r_leftclipped, &r_rightclipped, r_nearzionly); r_pedge->cachededgeoffset = cacheoffset; if (r_leftclipped) makeleftedge = true; if (r_rightclipped) makerightedge = true; r_lastvertvalid = true; } else { medge_t *r_pedge; lindex = -lindex; r_pedge = &pedges[lindex]; // if the edge is cached, we can just reuse the edge if (!insubmodel) { if (r_pedge->cachededgeoffset & FULLY_CLIPPED_CACHED) { if ((r_pedge->cachededgeoffset & FRAMECOUNT_MASK) == r_framecount) { r_lastvertvalid = false; continue; } } else { // it's cached if the cached edge is valid and is owned // by this medge_t if ((((uintptr_t)edge_p - (uintptr_t)r_edges) > r_pedge->cachededgeoffset) && (((edge_t *)((uintptr_t)r_edges + r_pedge->cachededgeoffset))->owner == r_pedge)) { R_EmitCachedEdge (r_pedge); r_lastvertvalid = false; continue; } } } // assume it's cacheable cacheoffset = (byte *)edge_p - (byte *)r_edges; r_leftclipped = r_rightclipped = false; R_ClipEdge (&r_pcurrentvertbase[r_pedge->v[1]], &r_pcurrentvertbase[r_pedge->v[0]], pclip, r_pedge, &r_leftclipped, &r_rightclipped, r_nearzionly); r_pedge->cachededgeoffset = cacheoffset; if (r_leftclipped) makeleftedge = true; if (r_rightclipped) makerightedge = true; r_lastvertvalid = true; } } // if there was a clip off the left edge, add that edge too // FIXME: faster to do in screen space? // FIXME: share clipped edges? if (makeleftedge) { r_lastvertvalid = false; R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next, &tedge, &r_leftclipped, &r_rightclipped, r_nearzionly); } // if there was a clip off the right edge, get the right r_nearzi if (makerightedge) { r_lastvertvalid = false; r_nearzionly = true; R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next, &tedge, &r_leftclipped, &r_rightclipped, r_nearzionly); } // if no edges made it out, return without posting the surface if (!r_emitted) return; r_polycount++; surface_p->msurf = fa; surface_p->nearzi = r_nearzi; surface_p->flags = fa->flags; surface_p->insubmodel = insubmodel; surface_p->spanstate = 0; surface_p->entity = currententity; surface_p->key = r_currentkey++; surface_p->spans = NULL; pplane = fa->plane; // FIXME: cache this? TransformVector (pplane->normal, p_normal); // FIXME: cache this? distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal)); surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; surface_p->d_ziorigin = p_normal[2] * distinv - xcenter * surface_p->d_zistepu - ycenter * surface_p->d_zistepv; surface_p++; } /* ================ R_RenderBmodelFace ================ */ void R_RenderBmodelFace(entity_t *currententity, bedge_t *pedges, msurface_t *psurf, int r_currentbkey) { int i; unsigned mask; cplane_t *pplane; float distinv; vec3_t p_normal; medge_t tedge; clipplane_t *pclip; qboolean r_leftclipped, r_rightclipped; qboolean makeleftedge, makerightedge; qboolean r_nearzionly; if (psurf->texinfo->flags & (SURF_TRANS33|SURF_TRANS66)) { psurf->nextalphasurface = r_alpha_surfaces; r_alpha_surfaces = psurf; return; } // skip out if no more surfs if (surface_p >= surf_max) { r_outofsurfaces = true; return; } // ditto if not enough edges left if ((edge_p + psurf->numedges + 4) >= edge_max) { r_outofedges = true; return; } c_faceclip++; // set up clip planes pclip = NULL; for (i=3, mask = 0x08 ; i>=0 ; i--, mask >>= 1) { if (r_clipflags & mask) { view_clipplanes[i].next = pclip; pclip = &view_clipplanes[i]; } } // push the edges through r_emitted = 0; r_nearzi = 0; r_nearzionly = false; makeleftedge = makerightedge = false; // FIXME: keep clipped bmodel edges in clockwise order so last vertex caching // can be used? r_lastvertvalid = false; for ( ; pedges ; pedges = pedges->pnext) { r_leftclipped = r_rightclipped = false; R_ClipEdge (pedges->v[0], pedges->v[1], pclip, &tedge, &r_leftclipped, &r_rightclipped, r_nearzionly); if (r_leftclipped) makeleftedge = true; if (r_rightclipped) makerightedge = true; } // if there was a clip off the left edge, add that edge too // FIXME: faster to do in screen space? // FIXME: share clipped edges? if (makeleftedge) { R_ClipEdge (&r_leftexit, &r_leftenter, pclip->next, &tedge, &r_leftclipped, &r_rightclipped, r_nearzionly); } // if there was a clip off the right edge, get the right r_nearzi if (makerightedge) { r_nearzionly = true; R_ClipEdge (&r_rightexit, &r_rightenter, view_clipplanes[1].next, &tedge, &r_leftclipped, &r_rightclipped, r_nearzionly); } // if no edges made it out, return without posting the surface if (!r_emitted) return; r_polycount++; surface_p->msurf = psurf; surface_p->nearzi = r_nearzi; surface_p->flags = psurf->flags; surface_p->insubmodel = true; surface_p->spanstate = 0; surface_p->entity = currententity; surface_p->key = r_currentbkey; surface_p->spans = NULL; pplane = psurf->plane; // FIXME: cache this? TransformVector (pplane->normal, p_normal); // FIXME: cache this? distinv = 1.0 / (pplane->dist - DotProduct (modelorg, pplane->normal)); surface_p->d_zistepu = p_normal[0] * xscaleinv * distinv; surface_p->d_zistepv = -p_normal[1] * yscaleinv * distinv; surface_p->d_ziorigin = p_normal[2] * distinv - xcenter * surface_p->d_zistepu - ycenter * surface_p->d_zistepv; surface_p++; } yquake2-QUAKE2_8_40/src/client/refresh/soft/sw_scan.c000066400000000000000000000472511465112212000224010ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // d_scan.c // // Portable C scan-level rasterization code, all pixel depths. #include "header/local.h" #define SPANSTEP_SHIFT 4 byte **warp_rowptr; int *warp_column; /* ============= D_WarpScreen this performs a slight compression of the screen at the same time as the sine warp, to keep the edges from wrapping ============= */ void D_WarpScreen (void) { int w, h; int u,v; pixel_t *dest; int *turb; byte **row; static int cached_width, cached_height; // // these are constant over resolutions, and can be saved // w = r_newrefdef.width; h = r_newrefdef.height; if (w != cached_width || h != cached_height) { cached_width = w; cached_height = h; for (v=0 ; v> SHIFT16XYZ == 0 && count < vid_buffer_width) { count <<= 1; } return count; } /* ============= D_DrawTurbulentSpan ============= */ static pixel_t * D_DrawTurbulentSpan (pixel_t *pdest, const pixel_t *pbase, int s, int t, int sstep, int tstep, int spancount, const int *turb) { do { int sturb, tturb; sturb = ((s + turb[(t>>SHIFT16XYZ)&(CYCLE-1)])>>16)&63; tturb = ((t + turb[(s>>SHIFT16XYZ)&(CYCLE-1)])>>16)&63; *pdest++ = *(pbase + (tturb<<6) + sturb); s += sstep; t += tstep; } while (--spancount > 0); return pdest; } /* ============= TurbulentPow2 ============= */ void TurbulentPow2 (espan_t *pspan, float d_ziorigin, float d_zistepu, float d_zistepv) { float spancountminus1; float sdivzpow2stepu, tdivzpow2stepu, zipow2stepu; pixel_t *r_turb_pbase; int *r_turb_turb; int spanstep_shift, spanstep_value; spanstep_shift = D_DrawSpanGetStep(d_zistepu, d_zistepv); spanstep_value = (1 << spanstep_shift); r_turb_turb = sintable + ((int)(r_newrefdef.time*SPEED)&(CYCLE-1)); r_turb_pbase = (unsigned char *)cacheblock; sdivzpow2stepu = d_sdivzstepu * spanstep_value; tdivzpow2stepu = d_tdivzstepu * spanstep_value; zipow2stepu = d_zistepu * spanstep_value; do { int count, r_turb_s, r_turb_t; float sdivz, tdivz, zi, z, du, dv; pixel_t *r_turb_pdest; r_turb_pdest = d_viewbuffer + (vid_buffer_width * pspan->v) + pspan->u; count = pspan->count; // calculate the initial s/z, t/z, 1/z, s, and t and clamp du = (float)pspan->u; dv = (float)pspan->v; sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; z = (float)SHIFT16XYZ_MULT / zi; // prescale to 16.16 fixed-point r_turb_s = (int)(sdivz * z) + sadjust; if (r_turb_s > bbextents) r_turb_s = bbextents; else if (r_turb_s < 0) r_turb_s = 0; r_turb_t = (int)(tdivz * z) + tadjust; if (r_turb_t > bbextentt) r_turb_t = bbextentt; else if (r_turb_t < 0) r_turb_t = 0; do { int snext, tnext; int r_turb_sstep, r_turb_tstep; int r_turb_spancount; r_turb_sstep = 0; // keep compiler happy r_turb_tstep = 0; // ditto // calculate s and t at the far end of the span if (count >= spanstep_value) r_turb_spancount = spanstep_value; else r_turb_spancount = count; count -= r_turb_spancount; if (count) { // calculate s/z, t/z, zi->fixed s and t at far end of span, // calculate s and t steps across span by shifting sdivz += sdivzpow2stepu; tdivz += tdivzpow2stepu; zi += zipow2stepu; z = (float)SHIFT16XYZ_MULT / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < spanstep_value) // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture snext = spanstep_value; tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < spanstep_value) // guard against round-off error on <0 steps tnext = spanstep_value; r_turb_sstep = (snext - r_turb_s) >> spanstep_shift; r_turb_tstep = (tnext - r_turb_t) >> spanstep_shift; } else { // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so // can't step off polygon), clamp, calculate s and t steps across // span by division, biasing steps low so we don't run off the // texture spancountminus1 = (float)(r_turb_spancount - 1); sdivz += d_sdivzstepu * spancountminus1; tdivz += d_tdivzstepu * spancountminus1; zi += d_zistepu * spancountminus1; z = (float)SHIFT16XYZ_MULT / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < spanstep_value) // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture snext = spanstep_value; tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < spanstep_value) // guard against round-off error on <0 steps tnext = spanstep_value; if (r_turb_spancount > 1) { r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1); r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1); } } r_turb_s = r_turb_s & ((CYCLE< 0); } while ((pspan = pspan->pnext) != NULL); } //==================== /* ============= NonTurbulentPow2 - this is for drawing scrolling textures. they're warping water textures but the turbulence is automatically 0. ============= */ void NonTurbulentPow2 (espan_t *pspan, float d_ziorigin, float d_zistepu, float d_zistepv) { float spancountminus1; float sdivzpow2stepu, tdivzpow2stepu, zipow2stepu; pixel_t *r_turb_pbase; int *r_turb_turb; int spanstep_shift, spanstep_value; spanstep_shift = D_DrawSpanGetStep(d_zistepu, d_zistepv); spanstep_value = (1 << spanstep_shift); r_turb_turb = blanktable; r_turb_pbase = (unsigned char *)cacheblock; sdivzpow2stepu = d_sdivzstepu * spanstep_value; tdivzpow2stepu = d_tdivzstepu * spanstep_value; zipow2stepu = d_zistepu * spanstep_value; do { int count, r_turb_s, r_turb_t; float sdivz, tdivz, zi, z, dv, du; pixel_t *r_turb_pdest; r_turb_pdest = d_viewbuffer + (vid_buffer_width * pspan->v) + pspan->u; count = pspan->count; // calculate the initial s/z, t/z, 1/z, s, and t and clamp du = (float)pspan->u; dv = (float)pspan->v; sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; z = (float)SHIFT16XYZ_MULT / zi; // prescale to 16.16 fixed-point r_turb_s = (int)(sdivz * z) + sadjust; if (r_turb_s > bbextents) r_turb_s = bbextents; else if (r_turb_s < 0) r_turb_s = 0; r_turb_t = (int)(tdivz * z) + tadjust; if (r_turb_t > bbextentt) r_turb_t = bbextentt; else if (r_turb_t < 0) r_turb_t = 0; do { int snext, tnext; int r_turb_sstep, r_turb_tstep; int r_turb_spancount; r_turb_sstep = 0; // keep compiler happy r_turb_tstep = 0; // ditto // calculate s and t at the far end of the span if (count >= spanstep_value) r_turb_spancount = spanstep_value; else r_turb_spancount = count; count -= r_turb_spancount; if (count) { // calculate s/z, t/z, zi->fixed s and t at far end of span, // calculate s and t steps across span by shifting sdivz += sdivzpow2stepu; tdivz += tdivzpow2stepu; zi += zipow2stepu; z = (float)SHIFT16XYZ_MULT / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < spanstep_value) // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture snext = spanstep_value; tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < spanstep_value) // guard against round-off error on <0 steps tnext = spanstep_value; r_turb_sstep = (snext - r_turb_s) >> spanstep_shift; r_turb_tstep = (tnext - r_turb_t) >> spanstep_shift; } else { // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so // can't step off polygon), clamp, calculate s and t steps across // span by division, biasing steps low so we don't run off the // texture spancountminus1 = (float)(r_turb_spancount - 1); sdivz += d_sdivzstepu * spancountminus1; tdivz += d_tdivzstepu * spancountminus1; zi += d_zistepu * spancountminus1; z = (float)SHIFT16XYZ_MULT / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < spanstep_value) // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture snext = spanstep_value; tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < spanstep_value) // guard against round-off error on <0 steps tnext = spanstep_value; if (r_turb_spancount > 1) { r_turb_sstep = (snext - r_turb_s) / (r_turb_spancount - 1); r_turb_tstep = (tnext - r_turb_t) / (r_turb_spancount - 1); } } r_turb_s = r_turb_s & ((CYCLE< 0); } while ((pspan = pspan->pnext) != NULL); } //==================== // Enable custom filtering extern cvar_t *sw_texture_filtering; static const int filtering_kernel[2][2][2] = { { {0x1 << (SHIFT16XYZ-2), 0x0}, {0xC << (SHIFT16XYZ-4), 0x1 << (SHIFT16XYZ-1)} }, { {0x1 << (SHIFT16XYZ-1), 0xC << (SHIFT16XYZ-4)}, {0x0, 0x1 << (SHIFT16XYZ-2)} } }; /* ============= D_DrawSpan ============= */ static pixel_t * D_DrawSpan(pixel_t *pdest, const pixel_t *pbase, int s, int t, int sstep, int tstep, int spancount) { const pixel_t *tdest_max = pdest + spancount; // horisontal span (span in same row) if (((t + tstep * spancount) >> SHIFT16XYZ) == (t >> SHIFT16XYZ)) { // position in texture const pixel_t *tbase = pbase + (t >> SHIFT16XYZ) * cachewidth; do { *pdest++ = *(tbase + (s >> SHIFT16XYZ)); s += sstep; } while (pdest < tdest_max); } // vertical span (span in same column) else if (((s + sstep * spancount) >> SHIFT16XYZ) == (s >> SHIFT16XYZ)) { // position in texture const pixel_t *tbase = pbase + (s >> SHIFT16XYZ); do { *pdest++ = *(tbase + (t >> SHIFT16XYZ) * cachewidth); t += tstep; } while (pdest < tdest_max); } // diagonal span else { do { *pdest++ = *(pbase + (s >> SHIFT16XYZ) + (t >> SHIFT16XYZ) * cachewidth); s += sstep; t += tstep; } while (pdest < tdest_max); } return pdest; } /* ============= D_DrawSpanFiltered ============= */ static pixel_t * D_DrawSpanFiltered(pixel_t *pdest, pixel_t *pbase, int s, int t, int sstep, int tstep, int spancount, const espan_t *pspan) { do { int idiths = s; int iditht = t; int X = (pspan->u + spancount) & 1; int Y = (pspan->v)&1; //Using the kernel idiths += filtering_kernel[X][Y][0]; iditht += filtering_kernel[X][Y][1]; idiths = idiths >> SHIFT16XYZ; idiths = idiths ? idiths -1 : idiths; iditht = iditht >> SHIFT16XYZ; iditht = iditht ? iditht -1 : iditht; *pdest++ = *(pbase + idiths + iditht * cachewidth); s += sstep; t += tstep; } while (--spancount > 0); return pdest; } /* ============= D_DrawSpansPow2 ============= */ void D_DrawSpansPow2 (espan_t *pspan, float d_ziorigin, float d_zistepu, float d_zistepv) { int spancount; pixel_t *pbase; int snext, tnext; float spancountminus1; float sdivzpow2stepu, tdivzpow2stepu, zipow2stepu; int texture_filtering; int spanstep_shift, spanstep_value; spanstep_shift = D_DrawSpanGetStep(d_zistepu, d_zistepv); spanstep_value = (1 << spanstep_shift); pbase = (unsigned char *)cacheblock; texture_filtering = (int)sw_texture_filtering->value; sdivzpow2stepu = d_sdivzstepu * spanstep_value; tdivzpow2stepu = d_tdivzstepu * spanstep_value; zipow2stepu = d_zistepu * spanstep_value; do { pixel_t *pdest; int count, s, t; float sdivz, tdivz, zi, z, du, dv; pdest = d_viewbuffer + (vid_buffer_width * pspan->v) + pspan->u; count = pspan->count; // calculate the initial s/z, t/z, 1/z, s, and t and clamp du = (float)pspan->u; dv = (float)pspan->v; sdivz = d_sdivzorigin + dv*d_sdivzstepv + du*d_sdivzstepu; tdivz = d_tdivzorigin + dv*d_tdivzstepv + du*d_tdivzstepu; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; z = (float)SHIFT16XYZ_MULT / zi; // prescale to 16.16 fixed-point s = (int)(sdivz * z) + sadjust; if (s > bbextents) s = bbextents; else if (s < 0) s = 0; t = (int)(tdivz * z) + tadjust; if (t > bbextentt) t = bbextentt; else if (t < 0) t = 0; do { int sstep, tstep; sstep = 0; // keep compiler happy tstep = 0; // ditto // calculate s and t at the far end of the span if (count >= spanstep_value) spancount = spanstep_value; else spancount = count; count -= spancount; if (count) { // calculate s/z, t/z, zi->fixed s and t at far end of span, // calculate s and t steps across span by shifting sdivz += sdivzpow2stepu; tdivz += tdivzpow2stepu; zi += zipow2stepu; z = (float)SHIFT16XYZ_MULT / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < spanstep_value) // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture snext = spanstep_value; tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < spanstep_value) // guard against round-off error on <0 steps tnext = spanstep_value; sstep = (snext - s) >> spanstep_shift; tstep = (tnext - t) >> spanstep_shift; } else { // calculate s/z, t/z, zi->fixed s and t at last pixel in span (so // can't step off polygon), clamp, calculate s and t steps across // span by division, biasing steps low so we don't run off the // texture spancountminus1 = (float)(spancount - 1); sdivz += d_sdivzstepu * spancountminus1; tdivz += d_tdivzstepu * spancountminus1; zi += d_zistepu * spancountminus1; z = (float)SHIFT16XYZ_MULT / zi; // prescale to 16.16 fixed-point snext = (int)(sdivz * z) + sadjust; if (snext > bbextents) snext = bbextents; else if (snext < spanstep_value) // prevent round-off error on <0 steps from // from causing overstepping & running off the // edge of the texture snext = spanstep_value; tnext = (int)(tdivz * z) + tadjust; if (tnext > bbextentt) tnext = bbextentt; else if (tnext < spanstep_value) // guard against round-off error on <0 steps tnext = spanstep_value; if (spancount > 1) { sstep = (snext - s) / (spancount - 1); tstep = (tnext - t) / (spancount - 1); } } // Drawing phrase if ((texture_filtering == 0) || fastmoving) { pdest = D_DrawSpan(pdest, pbase, s, t, sstep, tstep, spancount); } else { pdest = D_DrawSpanFiltered(pdest, pbase, s, t, sstep, tstep, spancount, pspan); } s = snext; t = tnext; } while (count > 0); } while ((pspan = pspan->pnext) != NULL); } /* ============= D_DrawZSpans ============= */ void D_DrawZSpans (espan_t *pspan, float d_ziorigin, float d_zistepu, float d_zistepv) { zvalue_t izistep; int safe_step; // FIXME: check for clamping/range problems // we count on FP exceptions being turned off to avoid range problems izistep = (int)(d_zistepu * 0x8000 * (float)SHIFT16XYZ_MULT); safe_step = D_DrawZSpanGetStepValue(izistep); do { int count; zvalue_t izi; zvalue_t *pdest; float zi; float du, dv; if (!VID_CheckDamageZBuffer(pspan->u, pspan->v, pspan->count, 0)) { continue; } // solid map walls damage VID_DamageZBuffer(pspan->u, pspan->v); VID_DamageZBuffer(pspan->u + pspan->count, pspan->v); pdest = d_pzbuffer + (vid_buffer_width * pspan->v) + pspan->u; count = pspan->count; // calculate the initial 1/z du = (float)pspan->u; dv = (float)pspan->v; zi = d_ziorigin + dv*d_zistepv + du*d_zistepu; // we count on FP exceptions being turned off to avoid range problems izi = (int)(zi * 0x8000 * (float)SHIFT16XYZ_MULT); if (safe_step > 1) { const zvalue_t *tdest_max = pdest + count; do { int step; zvalue_t izi_shifted = izi >> SHIFT16XYZ; for(step = 0; (step < safe_step) && (pdest < tdest_max); step++) { *pdest++ = izi_shifted; } izi += (izistep * safe_step); } while (pdest < tdest_max); } else { while (count > 0) { *pdest++ = izi >> SHIFT16XYZ; izi += izistep; count--; } } } while ((pspan = pspan->pnext) != NULL); } yquake2-QUAKE2_8_40/src/client/refresh/soft/sw_sprite.c000066400000000000000000000067551465112212000227670ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sw_sprite.c #include "header/local.h" extern polydesc_t r_polydesc; extern vec5_t r_clip_verts[2][MAXWORKINGVERTS+2]; extern void R_ClipAndDrawPoly(float alpha, qboolean isturbulent, qboolean textured); /* ** R_DrawSprite ** ** Draw currententity / currentmodel as a single texture ** mapped polygon */ void R_DrawSprite(entity_t *currententity, const model_t *currentmodel) { vec5_t *pverts; vec3_t left, up, right, down; dsprite_t *s_psprite; dsprframe_t *s_psprframe; image_t *skin; s_psprite = (dsprite_t *)currentmodel->extradata; currententity->frame %= s_psprite->numframes; s_psprframe = &s_psprite->frames[currententity->frame]; skin = currentmodel->skins[currententity->frame]; if (!skin) { skin = r_notexture_mip; } r_polydesc.pixels = skin->pixels[0]; r_polydesc.pixel_width = Q_min(s_psprframe->width, skin->width); r_polydesc.pixel_height = Q_min(s_psprframe->height, skin->height); r_polydesc.dist = 0; // generate the sprite's axes, completely parallel to the viewplane. VectorCopy (vup, r_polydesc.vup); VectorCopy (vright, r_polydesc.vright); VectorCopy (vpn, r_polydesc.vpn); // build the sprite poster in worldspace VectorScale (r_polydesc.vright, s_psprframe->width - s_psprframe->origin_x, right); VectorScale (r_polydesc.vup, s_psprframe->height - s_psprframe->origin_y, up); VectorScale (r_polydesc.vright, -s_psprframe->origin_x, left); VectorScale (r_polydesc.vup, -s_psprframe->origin_y, down); // invert UP vector for sprites VectorInverse( r_polydesc.vup ); pverts = r_clip_verts[0]; pverts[0][0] = r_entorigin[0] + up[0] + left[0]; pverts[0][1] = r_entorigin[1] + up[1] + left[1]; pverts[0][2] = r_entorigin[2] + up[2] + left[2]; pverts[0][3] = 0; pverts[0][4] = 0; pverts[1][0] = r_entorigin[0] + up[0] + right[0]; pverts[1][1] = r_entorigin[1] + up[1] + right[1]; pverts[1][2] = r_entorigin[2] + up[2] + right[2]; pverts[1][3] = s_psprframe->width; pverts[1][4] = 0; pverts[2][0] = r_entorigin[0] + down[0] + right[0]; pverts[2][1] = r_entorigin[1] + down[1] + right[1]; pverts[2][2] = r_entorigin[2] + down[2] + right[2]; pverts[2][3] = s_psprframe->width; pverts[2][4] = s_psprframe->height; pverts[3][0] = r_entorigin[0] + down[0] + left[0]; pverts[3][1] = r_entorigin[1] + down[1] + left[1]; pverts[3][2] = r_entorigin[2] + down[2] + left[2]; pverts[3][3] = 0; pverts[3][4] = s_psprframe->height; r_polydesc.nump = 4; r_polydesc.s_offset = ( r_polydesc.pixel_width >> 1); r_polydesc.t_offset = ( r_polydesc.pixel_height >> 1); VectorCopy( modelorg, r_polydesc.viewer_position ); r_polydesc.stipple_parity = 1; if ( currententity->flags & RF_TRANSLUCENT ) R_ClipAndDrawPoly ( currententity->alpha, false, true ); else R_ClipAndDrawPoly ( 1.0F, false, true ); r_polydesc.stipple_parity = 0; } yquake2-QUAKE2_8_40/src/client/refresh/soft/sw_surf.c000066400000000000000000000250731465112212000224320ustar00rootroot00000000000000/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // sw_surf.c: surface-related refresh code #include "header/local.h" static int sourcetstep; static void *prowdestbase; static unsigned char *pbasesource; static int r_stepback; static int r_lightwidth; static int r_numvblocks; static unsigned char *r_source, *r_sourcemax; static unsigned *r_lightptr; void R_BuildLightMap (drawsurf_t *drawsurf); static int sc_size; static surfcache_t *sc_rover; surfcache_t *sc_base; /* * Color light apply is not required */ static qboolean R_GreyscaledLight(const light3_t light) { light3_t light_masked; light_masked[0] = light[0] & LIGHTMASK; light_masked[1] = light[1] & LIGHTMASK; light_masked[2] = light[2] & LIGHTMASK; if (light_masked[0] == light_masked[1] && light_masked[0] == light_masked[2]) return light_masked[0]; return LIGHTMASK; } static void R_DrawSurfaceBlock_Light (pixel_t *prowdest, pixel_t *psource, size_t size, int level, light3_t lightleft, light3_t lightright) { int light_masked_right, light_masked_left; light_masked_right = R_GreyscaledLight(lightright); if (light_masked_right != LIGHTMASK) { light_masked_left = R_GreyscaledLight(lightleft); } // Full same light from both side if (light_masked_right != LIGHTMASK && light_masked_left == light_masked_right) { pixel_t *dest, *dest_max, *src; dest = prowdest; dest_max = prowdest + size; src = psource; while (dest < dest_max) { *dest = vid_colormap[*src + light_masked_right]; dest++; src++; } return; } // same color light shades { int b, j; light3_t lightstep, light; for(j=0; j<3; j++) { int lighttemp; lighttemp = lightleft[j] - lightright[j]; lightstep[j] = lighttemp >> level; } memcpy(light, lightright, sizeof(light3_t)); for (b=(size-1); b>=0; b--) { pixel_t pix; pix = psource[b]; prowdest[b] = R_ApplyLight(pix, light); for(j=0; j<3; j++) light[j] += lightstep[j]; } } } /* ================ R_DrawSurfaceBlock8_anymip ================ */ static void R_DrawSurfaceBlock8_anymip (int level, int surfrowbytes) { int v, i, size; pixel_t *psource, *prowdest; size = 1 << level; psource = pbasesource; prowdest = prowdestbase; for (v=0 ; v> level; lightrightstep[i] = (r_lightptr[i + 3] - lightright[i]) >> level; } for (i=0 ; i= r_sourcemax) psource -= r_stepback; } } /* =============== R_DrawSurface =============== */ static void R_DrawSurface (drawsurf_t *drawsurf) { unsigned char *basetptr; int smax, tmax, twidth; int u; int soffset, basetoffset, texwidth; int blocksize; unsigned char *pcolumndest; image_t *mt; int blockdivshift; int r_numhblocks; mt = drawsurf->image; r_source = mt->pixels[drawsurf->surfmip]; // the fractional light values should range from 0 to (VID_GRADES - 1) << 16 // from a source range of 0 - 255 texwidth = mt->width >> drawsurf->surfmip; blocksize = 16 >> drawsurf->surfmip; blockdivshift = NUM_MIPS - drawsurf->surfmip; r_lightwidth = (drawsurf->surf->extents[0]>>4)+1; r_numhblocks = drawsurf->surfwidth >> blockdivshift; r_numvblocks = drawsurf->surfheight >> blockdivshift; //============================== smax = mt->width >> drawsurf->surfmip; twidth = texwidth; tmax = mt->height >> drawsurf->surfmip; sourcetstep = texwidth; r_stepback = tmax * twidth; r_sourcemax = r_source + (tmax * smax); soffset = drawsurf->surf->texturemins[0]; basetoffset = drawsurf->surf->texturemins[1]; // << 16 components are to guarantee positive values for % soffset = ((soffset >> drawsurf->surfmip) + (smax << SHIFT16XYZ)) % smax; basetptr = &r_source[((((basetoffset >> drawsurf->surfmip) + (tmax << SHIFT16XYZ)) % tmax) * twidth)]; pcolumndest = drawsurf->surfdat; for (u=0 ; u= blocklight_max) { r_outoflights = true; continue; } prowdestbase = pcolumndest; pbasesource = basetptr + soffset; R_DrawSurfaceBlock8_anymip(NUM_MIPS - drawsurf->surfmip, drawsurf->rowbytes); soffset = soffset + blocksize; if (soffset >= smax) soffset = 0; pcolumndest += blocksize; } } //============================================================================= /* ================ R_InitCaches ================ */ void R_InitCaches (void) { int size; // calculate size to allocate int pix; // surface cache size at 320X240 size = 1024*768; pix = vid_buffer_width*vid_buffer_height; if (pix > 64000) size += (pix-64000)*3; if (r_farsee->value > 0) size *= 2; if (sw_surfcacheoverride->value > size) { size = sw_surfcacheoverride->value; } // round up to page size size = (size + 8191) & ~8191; R_Printf(PRINT_ALL,"%ik surface cache.\n", size/1024); sc_size = size; sc_base = (surfcache_t *)malloc(size); if (!sc_base) { ri.Sys_Error(ERR_FATAL, "%s: Can't allocate cache.", __func__); // code never returns after ERR_FATAL return; } sc_rover = sc_base; sc_base->next = NULL; sc_base->owner = NULL; sc_base->size = sc_size; } /* ================== D_FlushCaches ================== */ void D_FlushCaches (void) { surfcache_t *c; if (!sc_base) return; for (c = sc_base ; c ; c = c->next) { if (c->owner) *c->owner = NULL; } sc_rover = sc_base; sc_base->next = NULL; sc_base->owner = NULL; sc_base->size = sc_size; } /* ================= D_SCAlloc ================= */ static surfcache_t * D_SCAlloc (int width, int size) { surfcache_t *new; if ((width < 0) || (width > 256)) { ri.Sys_Error(ERR_FATAL, "%s: bad cache width %d\n", __func__, width); } if ((size <= 0) || (size > 0x10000)) { ri.Sys_Error(ERR_FATAL, "%s: bad cache size %d\n", __func__, size); } // Add header size size += ((char*)sc_base->data - (char*)sc_base); size = (size + 3) & ~3; if (size > sc_size) { ri.Sys_Error(ERR_FATAL, "%s: %i > cache size of %i", __func__, size, sc_size); } // if there is not size bytes after the rover, reset to the start if ( !sc_rover || (byte *)sc_rover - (byte *)sc_base > sc_size - size) { sc_rover = sc_base; } // colect and free surfcache_t blocks until the rover block is large enough new = sc_rover; if (sc_rover->owner) *sc_rover->owner = NULL; while (new->size < size) { // free another sc_rover = sc_rover->next; if (!sc_rover) { ri.Sys_Error(ERR_FATAL, "%s: hit the end of memory", __func__); } if (sc_rover->owner) *sc_rover->owner = NULL; new->size += sc_rover->size; new->next = sc_rover->next; } // create a fragment out of any leftovers if (new->size - size > 256) { sc_rover = (surfcache_t *)( (byte *)new + size); sc_rover->size = new->size - size; sc_rover->next = new->next; sc_rover->width = 0; sc_rover->owner = NULL; new->next = sc_rover; new->size = size; } else sc_rover = new->next; new->width = width; // DEBUG if (width > 0) new->height = (size - sizeof(*new) + sizeof(new->data)) / width; new->owner = NULL; // should be set properly after return return new; } //============================================================================= static drawsurf_t r_drawsurf; /* ================ D_CacheSurface ================ */ surfcache_t * D_CacheSurface (const entity_t *currententity, msurface_t *surface, int miplevel) { surfcache_t *cache; float surfscale; // // if the surface is animating or flashing, flush the cache // r_drawsurf.image = R_TextureAnimation (currententity, surface->texinfo); r_drawsurf.lightadj[0] = r_newrefdef.lightstyles[surface->styles[0]].white*128; r_drawsurf.lightadj[1] = r_newrefdef.lightstyles[surface->styles[1]].white*128; r_drawsurf.lightadj[2] = r_newrefdef.lightstyles[surface->styles[2]].white*128; r_drawsurf.lightadj[3] = r_newrefdef.lightstyles[surface->styles[3]].white*128; // // see if the cache holds apropriate data // cache = surface->cachespots[miplevel]; if (cache && !cache->dlight && surface->dlightframe != r_framecount && cache->image == r_drawsurf.image && cache->lightadj[0] == r_drawsurf.lightadj[0] && cache->lightadj[1] == r_drawsurf.lightadj[1] && cache->lightadj[2] == r_drawsurf.lightadj[2] && cache->lightadj[3] == r_drawsurf.lightadj[3] ) return cache; // // determine shape of surface // surfscale = 1.0 / (1<extents[0] >> miplevel; r_drawsurf.rowbytes = r_drawsurf.surfwidth; r_drawsurf.surfheight = surface->extents[1] >> miplevel; // // allocate memory if needed // if (!cache) // if a texture just animated, don't reallocate it { cache = D_SCAlloc (r_drawsurf.surfwidth, r_drawsurf.surfwidth * r_drawsurf.surfheight); surface->cachespots[miplevel] = cache; cache->owner = &surface->cachespots[miplevel]; cache->mipscale = surfscale; } if (surface->dlightframe == r_framecount) cache->dlight = 1; else cache->dlight = 0; r_drawsurf.surfdat = (pixel_t *)cache->data; cache->image = r_drawsurf.image; cache->lightadj[0] = r_drawsurf.lightadj[0]; cache->lightadj[1] = r_drawsurf.lightadj[1]; cache->lightadj[2] = r_drawsurf.lightadj[2]; cache->lightadj[3] = r_drawsurf.lightadj[3]; // // draw and light the surface texture // r_drawsurf.surf = surface; c_surf++; // calculate the lightings R_BuildLightMap (&r_drawsurf); // rasterize the surface into the cache R_DrawSurface (&r_drawsurf); return cache; } yquake2-QUAKE2_8_40/src/client/sound/000077500000000000000000000000001465112212000173065ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/sound/header/000077500000000000000000000000001465112212000205365ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/sound/header/local.h000066400000000000000000000167141465112212000220120ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; /* effect length */ /* begin<->attack..fade<->end */ int begin; int end; int attack; int fade; /* effect volume */ short volume; byte data[1]; } sfxcache_t; /* * Holds a SFX */ typedef struct sfx_s { char name[MAX_QPATH]; int registration_sequence; sfxcache_t *cache; char *truename; qboolean is_silenced_muzzle_flash; } 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; extern cvar_t* s_occlusion_strength; extern cvar_t* s_reverb_preset; /* * 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 information 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, short volume, int begin_length, int end_length, int attack_length, int fade_length); /* * 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, short volume, int begin_length, int end_length, int attack_length, int fade_length); /* * 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_8_40/src/client/sound/header/qal.h000066400000000000000000000125321465112212000214670ustar00rootroot00000000000000/* * 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 #include "AL/efx-presets.h" /* 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; extern LPALGENEFFECTS qalGenEffects; extern LPALEFFECTF qalEffectf; extern LPALEFFECTI qalEffecti; extern LPALEFFECTFV qalEffectfv; extern LPALAUXILIARYEFFECTSLOTI qalAuxiliaryEffectSloti; extern LPALGENAUXILIARYEFFECTSLOTS qalGenAuxiliaryEffectSlots; extern LPALDELETEAUXILIARYEFFECTSLOTS qalDeleteAuxiliaryEffectSlots; extern LPALDELETEEFFECTS qalDeleteEffects; /* * Gives information over the OpenAL * implementation and it's state */ void QAL_SoundInfo(void); /* * Checks if the output device is still connected. Returns true * if it is, false otherwise. Should be called every frame, if * disconnected a 'snd_restart' is injected after waiting for 50 * frame. * * This is mostly a work around for broken Sound driver. For * example the _good_ Intel display driver for Windows 10 * destroys the DisplayPort sound device when the display * resolution changes and recreates it after an unspecified * amount of time... */ qboolean QAL_RecoverLostDevice(); /* * 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_8_40/src/client/sound/header/sound.h000066400000000000000000000040251465112212000220400ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_Activate(qboolean active); 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 *sound); 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 forward, vec3_t right, vec3_t up); void S_Activate(qboolean active); void S_BeginRegistration(void); struct sfx_s *S_RegisterSound(char *name); void S_EndRegistration(void); /* 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_8_40/src/client/sound/header/stb_vorbis.h000066400000000000000000005704261465112212000231010ustar00rootroot00000000000000// Ogg Vorbis audio decoder - v1.22 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. // // Originally sponsored by RAD Game Tools. Seeking implementation // sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker, // Elias Software, Aras Pranckevicius, and Sean Barrett. // // LICENSE // // See end of file for license information. // // Limitations: // // - floor 0 not supported (used in old ogg vorbis files pre-2004) // - lossless sample-truncation at beginning ignored // - cannot concatenate multiple vorbis streams // - sample positions are 32-bit, limiting seekable 192Khz // files to around 6 hours (Ogg supports 64-bit) // // Feature contributors: // Dougall Johnson (sample-exact seeking) // // Bugfix/warning contributors: // Terje Mathisen Niklas Frykholm Andy Hill // Casey Muratori John Bolton Gargaj // Laurent Gomila Marc LeBlanc Ronny Chevalier // Bernhard Wodo Evan Balster github:alxprd // Tom Beaumont Ingo Leitgeb Nicolas Guillemot // Phillip Bennefall Rohit Thiago Goulart // github:manxorist Saga Musix github:infatum // Timur Gagiev Maxwell Koo Peter Waller // github:audinowho Dougall Johnson David Reid // github:Clownacy Pedro J. Estebanez Remi Verschelde // AnthoFoxo github:morlat Gabriel Ravier // // Partial history: // 1.22 - 2021-07-11 - various small fixes // 1.21 - 2021-07-02 - fix bug for files with no comments // 1.20 - 2020-07-11 - several small fixes // 1.19 - 2020-02-05 - warnings // 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc. // 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure) // 1.16 - 2019-03-04 - fix warnings // 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found // 1.14 - 2018-02-11 - delete bogus dealloca usage // 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) // 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files // 1.11 - 2017-07-23 - fix MinGW compilation // 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory // 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version // 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame // 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const // 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) // some crash fixes when out of memory or with corrupt files // fix some inappropriately signed shifts // 1.05 - 2015-04-19 - don't define __forceinline if it's redundant // 1.04 - 2014-08-27 - fix missing const-correct case in API // 1.03 - 2014-08-07 - warning fixes // 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows // 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct) // 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel; // (API change) report sample rate for decode-full-file funcs // // See end of file for full version history. ////////////////////////////////////////////////////////////////////////////// // // HEADER BEGINS HERE // #ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H #define STB_VORBIS_INCLUDE_STB_VORBIS_H #if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) #define STB_VORBIS_NO_STDIO 1 #endif #ifndef STB_VORBIS_NO_STDIO #include #endif #ifdef __cplusplus extern "C" { #endif /////////// THREAD SAFETY // Individual stb_vorbis* handles are not thread-safe; you cannot decode from // them from multiple threads at the same time. However, you can have multiple // stb_vorbis* handles and decode from them independently in multiple thrads. /////////// MEMORY ALLOCATION // normally stb_vorbis uses malloc() to allocate memory at startup, // and alloca() to allocate temporary memory during a frame on the // stack. (Memory consumption will depend on the amount of setup // data in the file and how you set the compile flags for speed // vs. size. In my test files the maximal-size usage is ~150KB.) // // You can modify the wrapper functions in the source (setup_malloc, // setup_temp_malloc, temp_malloc) to change this behavior, or you // can use a simpler allocation model: you pass in a buffer from // which stb_vorbis will allocate _all_ its memory (including the // temp memory). "open" may fail with a VORBIS_outofmem if you // do not pass in enough data; there is no way to determine how // much you do need except to succeed (at which point you can // query get_info to find the exact amount required. yes I know // this is lame). // // If you pass in a non-NULL buffer of the type below, allocation // will occur from it as described above. Otherwise just pass NULL // to use malloc()/alloca() typedef struct { char *alloc_buffer; int alloc_buffer_length_in_bytes; } stb_vorbis_alloc; /////////// FUNCTIONS USEABLE WITH ALL INPUT MODES typedef struct stb_vorbis stb_vorbis; typedef struct { unsigned int sample_rate; int channels; unsigned int setup_memory_required; unsigned int setup_temp_memory_required; unsigned int temp_memory_required; int max_frame_size; } stb_vorbis_info; typedef struct { char *vendor; int comment_list_length; char **comment_list; } stb_vorbis_comment; // get general information about the file extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); // get ogg comments extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f); // get the last error detected (clears it, too) extern int stb_vorbis_get_error(stb_vorbis *f); // close an ogg vorbis file and free all memory in use extern void stb_vorbis_close(stb_vorbis *f); // this function returns the offset (in samples) from the beginning of the // file that will be returned by the next decode, if it is known, or -1 // otherwise. after a flush_pushdata() call, this may take a while before // it becomes valid again. // NOT WORKING YET after a seek with PULLDATA API extern int stb_vorbis_get_sample_offset(stb_vorbis *f); // returns the current seek point within the file, or offset from the beginning // of the memory buffer. In pushdata mode it returns 0. extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); /////////// PUSHDATA API #ifndef STB_VORBIS_NO_PUSHDATA_API // this API allows you to get blocks of data from any source and hand // them to stb_vorbis. you have to buffer them; stb_vorbis will tell // you how much it used, and you have to give it the rest next time; // and stb_vorbis may not have enough data to work with and you will // need to give it the same data again PLUS more. Note that the Vorbis // specification does not bound the size of an individual frame. extern stb_vorbis *stb_vorbis_open_pushdata( const unsigned char * datablock, int datablock_length_in_bytes, int *datablock_memory_consumed_in_bytes, int *error, const stb_vorbis_alloc *alloc_buffer); // create a vorbis decoder by passing in the initial data block containing // the ogg&vorbis headers (you don't need to do parse them, just provide // the first N bytes of the file--you're told if it's not enough, see below) // on success, returns an stb_vorbis *, does not set error, returns the amount of // data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; // on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed // if returns NULL and *error is VORBIS_need_more_data, then the input block was // incomplete and you need to pass in a larger block from the start of the file extern int stb_vorbis_decode_frame_pushdata( stb_vorbis *f, const unsigned char *datablock, int datablock_length_in_bytes, int *channels, // place to write number of float * buffers float ***output, // place to write float ** array of float * buffers int *samples // place to write number of output samples ); // decode a frame of audio sample data if possible from the passed-in data block // // return value: number of bytes we used from datablock // // possible cases: // 0 bytes used, 0 samples output (need more data) // N bytes used, 0 samples output (resynching the stream, keep going) // N bytes used, M samples output (one frame of data) // note that after opening a file, you will ALWAYS get one N-bytes,0-sample // frame, because Vorbis always "discards" the first frame. // // Note that on resynch, stb_vorbis will rarely consume all of the buffer, // instead only datablock_length_in_bytes-3 or less. This is because it wants // to avoid missing parts of a page header if they cross a datablock boundary, // without writing state-machiney code to record a partial detection. // // The number of channels returned are stored in *channels (which can be // NULL--it is always the same as the number of channels reported by // get_info). *output will contain an array of float* buffers, one per // channel. In other words, (*output)[0][0] contains the first sample from // the first channel, and (*output)[1][0] contains the first sample from // the second channel. // // *output points into stb_vorbis's internal output buffer storage; these // buffers are owned by stb_vorbis and application code should not free // them or modify their contents. They are transient and will be overwritten // once you ask for more data to get decoded, so be sure to grab any data // you need before then. extern void stb_vorbis_flush_pushdata(stb_vorbis *f); // inform stb_vorbis that your next datablock will not be contiguous with // previous ones (e.g. you've seeked in the data); future attempts to decode // frames will cause stb_vorbis to resynchronize (as noted above), and // once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it // will begin decoding the _next_ frame. // // if you want to seek using pushdata, you need to seek in your file, then // call stb_vorbis_flush_pushdata(), then start calling decoding, then once // decoding is returning you data, call stb_vorbis_get_sample_offset, and // if you don't like the result, seek your file again and repeat. #endif ////////// PULLING INPUT API #ifndef STB_VORBIS_NO_PULLDATA_API // This API assumes stb_vorbis is allowed to pull data from a source-- // either a block of memory containing the _entire_ vorbis stream, or a // FILE * that you or it create, or possibly some other reading mechanism // if you go modify the source to replace the FILE * case with some kind // of callback to your code. (But if you don't support seeking, you may // just want to go ahead and use pushdata.) #if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); #endif #if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); #endif // decode an entire file and output the data interleaved into a malloc()ed // buffer stored in *output. The return value is the number of samples // decoded, or -1 if the file could not be opened or was not an ogg vorbis file. // When you're done with it, just free() the pointer returned in *output. extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from an ogg vorbis stream in memory (note // this must be the entire stream!). on failure, returns NULL and sets *error #ifndef STB_VORBIS_NO_STDIO extern stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from a filename via fopen(). on failure, // returns NULL and sets *error (possibly to VORBIS_file_open_failure). extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from an open FILE *, looking for a stream at // the _current_ seek point (ftell). on failure, returns NULL and sets *error. // note that stb_vorbis must "own" this stream; if you seek it in between // calls to stb_vorbis, it will become confused. Moreover, if you attempt to // perform stb_vorbis_seek_*() operations on this file, it will assume it // owns the _entire_ rest of the file after the start point. Use the next // function, stb_vorbis_open_file_section(), to limit it. extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); // create an ogg vorbis decoder from an open FILE *, looking for a stream at // the _current_ seek point (ftell); the stream will be of length 'len' bytes. // on failure, returns NULL and sets *error. note that stb_vorbis must "own" // this stream; if you seek it in between calls to stb_vorbis, it will become // confused. #endif extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); // these functions seek in the Vorbis file to (approximately) 'sample_number'. // after calling seek_frame(), the next call to get_frame_*() will include // the specified sample. after calling stb_vorbis_seek(), the next call to // stb_vorbis_get_samples_* will start with the specified sample. If you // do not need to seek to EXACTLY the target sample when using get_samples_*, // you can also use seek_frame(). extern int stb_vorbis_seek_start(stb_vorbis *f); // this function is equivalent to stb_vorbis_seek(f,0) extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); // these functions return the total length of the vorbis stream extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); // decode the next frame and return the number of samples. the number of // channels returned are stored in *channels (which can be NULL--it is always // the same as the number of channels reported by get_info). *output will // contain an array of float* buffers, one per channel. These outputs will // be overwritten on the next call to stb_vorbis_get_frame_*. // // You generally should not intermix calls to stb_vorbis_get_frame_*() // and stb_vorbis_get_samples_*(), since the latter calls the former. #ifndef STB_VORBIS_NO_INTEGER_CONVERSION extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); #endif // decode the next frame and return the number of *samples* per channel. // Note that for interleaved data, you pass in the number of shorts (the // size of your array), but the return value is the number of samples per // channel, not the total number of samples. // // The data is coerced to the number of channels you request according to the // channel coercion rules (see below). You must pass in the size of your // buffer(s) so that stb_vorbis will not overwrite the end of the buffer. // The maximum buffer size needed can be gotten from get_info(); however, // the Vorbis I specification implies an absolute maximum of 4096 samples // per channel. // Channel coercion rules: // Let M be the number of channels requested, and N the number of channels present, // and Cn be the nth channel; let stereo L be the sum of all L and center channels, // and stereo R be the sum of all R and center channels (channel assignment from the // vorbis spec). // M N output // 1 k sum(Ck) for all k // 2 * stereo L, stereo R // k l k > l, the first l channels, then 0s // k l k <= l, the first k channels // Note that this is not _good_ surround etc. mixing at all! It's just so // you get something useful. extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); // gets num_samples samples, not necessarily on a frame boundary--this requires // buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. // Returns the number of samples stored per channel; it may be less than requested // at the end of the file. If there are no more samples in the file, returns 0. #ifndef STB_VORBIS_NO_INTEGER_CONVERSION extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); #endif // gets num_samples samples, not necessarily on a frame boundary--this requires // buffering so you have to supply the buffers. Applies the coercion rules above // to produce 'channels' channels. Returns the number of samples stored per channel; // it may be less than requested at the end of the file. If there are no more // samples in the file, returns 0. #endif //////// ERROR CODES enum STBVorbisError { VORBIS__no_error, VORBIS_need_more_data=1, // not a real error VORBIS_invalid_api_mixing, // can't mix API modes VORBIS_outofmem, // not enough memory VORBIS_feature_not_supported, // uses floor 0 VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small VORBIS_file_open_failure, // fopen() failed VORBIS_seek_without_length, // can't seek in unknown-length file VORBIS_unexpected_eof=10, // file is truncated? VORBIS_seek_invalid, // seek past EOF // decoding errors (corrupt/invalid stream) -- you probably // don't care about the exact details of these // vorbis errors: VORBIS_invalid_setup=20, VORBIS_invalid_stream, // ogg errors: VORBIS_missing_capture_pattern=30, VORBIS_invalid_stream_structure_version, VORBIS_continued_packet_flag_invalid, VORBIS_incorrect_stream_serial_number, VORBIS_invalid_first_page, VORBIS_bad_packet_type, VORBIS_cant_find_last_page, VORBIS_seek_failed, VORBIS_ogg_skeleton_not_supported }; #ifdef __cplusplus } #endif #endif // STB_VORBIS_INCLUDE_STB_VORBIS_H // // HEADER ENDS HERE // ////////////////////////////////////////////////////////////////////////////// #ifndef STB_VORBIS_HEADER_ONLY // global configuration settings (e.g. set these in the project/makefile), // or just set them in this file at the top (although ideally the first few // should be visible when the header file is compiled too, although it's not // crucial) // STB_VORBIS_NO_PUSHDATA_API // does not compile the code for the various stb_vorbis_*_pushdata() // functions // #define STB_VORBIS_NO_PUSHDATA_API // STB_VORBIS_NO_PULLDATA_API // does not compile the code for the non-pushdata APIs // #define STB_VORBIS_NO_PULLDATA_API // STB_VORBIS_NO_STDIO // does not compile the code for the APIs that use FILE *s internally // or externally (implied by STB_VORBIS_NO_PULLDATA_API) // #define STB_VORBIS_NO_STDIO // STB_VORBIS_NO_INTEGER_CONVERSION // does not compile the code for converting audio sample data from // float to integer (implied by STB_VORBIS_NO_PULLDATA_API) // #define STB_VORBIS_NO_INTEGER_CONVERSION // STB_VORBIS_NO_FAST_SCALED_FLOAT // does not use a fast float-to-int trick to accelerate float-to-int on // most platforms which requires endianness be defined correctly. //#define STB_VORBIS_NO_FAST_SCALED_FLOAT // STB_VORBIS_MAX_CHANNELS [number] // globally define this to the maximum number of channels you need. // The spec does not put a restriction on channels except that // the count is stored in a byte, so 255 is the hard limit. // Reducing this saves about 16 bytes per value, so using 16 saves // (255-16)*16 or around 4KB. Plus anything other memory usage // I forgot to account for. Can probably go as low as 8 (7.1 audio), // 6 (5.1 audio), or 2 (stereo only). #ifndef STB_VORBIS_MAX_CHANNELS #define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? #endif // STB_VORBIS_PUSHDATA_CRC_COUNT [number] // after a flush_pushdata(), stb_vorbis begins scanning for the // next valid page, without backtracking. when it finds something // that looks like a page, it streams through it and verifies its // CRC32. Should that validation fail, it keeps scanning. But it's // possible that _while_ streaming through to check the CRC32 of // one candidate page, it sees another candidate page. This #define // determines how many "overlapping" candidate pages it can search // at once. Note that "real" pages are typically ~4KB to ~8KB, whereas // garbage pages could be as big as 64KB, but probably average ~16KB. // So don't hose ourselves by scanning an apparent 64KB page and // missing a ton of real ones in the interim; so minimum of 2 #ifndef STB_VORBIS_PUSHDATA_CRC_COUNT #define STB_VORBIS_PUSHDATA_CRC_COUNT 4 #endif // STB_VORBIS_FAST_HUFFMAN_LENGTH [number] // sets the log size of the huffman-acceleration table. Maximum // supported value is 24. with larger numbers, more decodings are O(1), // but the table size is larger so worse cache missing, so you'll have // to probe (and try multiple ogg vorbis files) to find the sweet spot. #ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH #define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 #endif // STB_VORBIS_FAST_BINARY_LENGTH [number] // sets the log size of the binary-search acceleration table. this // is used in similar fashion to the fast-huffman size to set initial // parameters for the binary search // STB_VORBIS_FAST_HUFFMAN_INT // The fast huffman tables are much more efficient if they can be // stored as 16-bit results instead of 32-bit results. This restricts // the codebooks to having only 65535 possible outcomes, though. // (At least, accelerated by the huffman table.) #ifndef STB_VORBIS_FAST_HUFFMAN_INT #define STB_VORBIS_FAST_HUFFMAN_SHORT #endif // STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH // If the 'fast huffman' search doesn't succeed, then stb_vorbis falls // back on binary searching for the correct one. This requires storing // extra tables with the huffman codes in sorted order. Defining this // symbol trades off space for speed by forcing a linear search in the // non-fast case, except for "sparse" codebooks. // #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH // STB_VORBIS_DIVIDES_IN_RESIDUE // stb_vorbis precomputes the result of the scalar residue decoding // that would otherwise require a divide per chunk. you can trade off // space for time by defining this symbol. // #define STB_VORBIS_DIVIDES_IN_RESIDUE // STB_VORBIS_DIVIDES_IN_CODEBOOK // vorbis VQ codebooks can be encoded two ways: with every case explicitly // stored, or with all elements being chosen from a small range of values, // and all values possible in all elements. By default, stb_vorbis expands // this latter kind out to look like the former kind for ease of decoding, // because otherwise an integer divide-per-vector-element is required to // unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can // trade off storage for speed. //#define STB_VORBIS_DIVIDES_IN_CODEBOOK #ifdef STB_VORBIS_CODEBOOK_SHORTS #error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" #endif // STB_VORBIS_DIVIDE_TABLE // this replaces small integer divides in the floor decode loop with // table lookups. made less than 1% difference, so disabled by default. // STB_VORBIS_NO_INLINE_DECODE // disables the inlining of the scalar codebook fast-huffman decode. // might save a little codespace; useful for debugging // #define STB_VORBIS_NO_INLINE_DECODE // STB_VORBIS_NO_DEFER_FLOOR // Normally we only decode the floor without synthesizing the actual // full curve. We can instead synthesize the curve immediately. This // requires more memory and is very likely slower, so I don't think // you'd ever want to do it except for debugging. // #define STB_VORBIS_NO_DEFER_FLOOR ////////////////////////////////////////////////////////////////////////////// #ifdef STB_VORBIS_NO_PULLDATA_API #define STB_VORBIS_NO_INTEGER_CONVERSION #define STB_VORBIS_NO_STDIO #endif #if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) #define STB_VORBIS_NO_STDIO 1 #endif #ifndef STB_VORBIS_NO_INTEGER_CONVERSION #ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT // only need endianness for fast-float-to-int, which we don't // use for pushdata #ifndef STB_VORBIS_BIG_ENDIAN #define STB_VORBIS_ENDIAN 0 #else #define STB_VORBIS_ENDIAN 1 #endif #endif #endif #ifndef STB_VORBIS_NO_STDIO #include #endif #ifndef STB_VORBIS_NO_CRT #include #include #include #include // find definition of alloca if it's not in stdlib.h: #if defined(_MSC_VER) || defined(__MINGW32__) #include #endif #if defined(__linux__) || defined(__linux) || defined(__sun__) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__) #include #endif #else // STB_VORBIS_NO_CRT #define NULL 0 #define malloc(s) 0 #define free(s) ((void) 0) #define realloc(s) 0 #endif // STB_VORBIS_NO_CRT #include #ifdef __MINGW32__ // eff you mingw: // "fixed": // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/ // "no that broke the build, reverted, who cares about C": // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/ #ifdef __forceinline #undef __forceinline #endif #define __forceinline #ifndef alloca #define alloca __builtin_alloca #endif #elif !defined(_MSC_VER) #if __GNUC__ #define __forceinline inline #else #define __forceinline #endif #endif #if STB_VORBIS_MAX_CHANNELS > 256 #error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" #endif #if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 #error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" #endif #if 0 #include #define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) #else #define CHECK(f) ((void) 0) #endif #define MAX_BLOCKSIZE_LOG 13 // from specification #define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) typedef unsigned char uint8; typedef signed char int8; typedef unsigned short uint16; typedef signed short int16; typedef unsigned int uint32; typedef signed int int32; #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif typedef float codetype; #ifdef _MSC_VER #define STBV_NOTUSED(v) (void)(v) #else #define STBV_NOTUSED(v) (void)sizeof(v) #endif // @NOTE // // Some arrays below are tagged "//varies", which means it's actually // a variable-sized piece of data, but rather than malloc I assume it's // small enough it's better to just allocate it all together with the // main thing // // Most of the variables are specified with the smallest size I could pack // them into. It might give better performance to make them all full-sized // integers. It should be safe to freely rearrange the structures or change // the sizes larger--nothing relies on silently truncating etc., nor the // order of variables. #define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) #define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) typedef struct { int dimensions, entries; uint8 *codeword_lengths; float minimum_value; float delta_value; uint8 value_bits; uint8 lookup_type; uint8 sequence_p; uint8 sparse; uint32 lookup_values; codetype *multiplicands; uint32 *codewords; #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; #else int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; #endif uint32 *sorted_codewords; int *sorted_values; int sorted_entries; } Codebook; typedef struct { uint8 order; uint16 rate; uint16 bark_map_size; uint8 amplitude_bits; uint8 amplitude_offset; uint8 number_of_books; uint8 book_list[16]; // varies } Floor0; typedef struct { uint8 partitions; uint8 partition_class_list[32]; // varies uint8 class_dimensions[16]; // varies uint8 class_subclasses[16]; // varies uint8 class_masterbooks[16]; // varies int16 subclass_books[16][8]; // varies uint16 Xlist[31*8+2]; // varies uint8 sorted_order[31*8+2]; uint8 neighbors[31*8+2][2]; uint8 floor1_multiplier; uint8 rangebits; int values; } Floor1; typedef union { Floor0 floor0; Floor1 floor1; } Floor; typedef struct { uint32 begin, end; uint32 part_size; uint8 classifications; uint8 classbook; uint8 **classdata; int16 (*residue_books)[8]; } Residue; typedef struct { uint8 magnitude; uint8 angle; uint8 mux; } MappingChannel; typedef struct { uint16 coupling_steps; MappingChannel *chan; uint8 submaps; uint8 submap_floor[15]; // varies uint8 submap_residue[15]; // varies } Mapping; typedef struct { uint8 blockflag; uint8 mapping; uint16 windowtype; uint16 transformtype; } Mode; typedef struct { uint32 goal_crc; // expected crc if match int bytes_left; // bytes left in packet uint32 crc_so_far; // running crc int bytes_done; // bytes processed in _current_ chunk uint32 sample_loc; // granule pos encoded in page } CRCscan; typedef struct { uint32 page_start, page_end; uint32 last_decoded_sample; } ProbedPage; struct stb_vorbis { // user-accessible info unsigned int sample_rate; int channels; unsigned int setup_memory_required; unsigned int temp_memory_required; unsigned int setup_temp_memory_required; char *vendor; int comment_list_length; char **comment_list; // input config #ifndef STB_VORBIS_NO_STDIO FILE *f; uint32 f_start; int close_on_free; #endif uint8 *stream; uint8 *stream_start; uint8 *stream_end; uint32 stream_len; uint8 push_mode; // the page to seek to when seeking to start, may be zero uint32 first_audio_page_offset; // p_first is the page on which the first audio packet ends // (but not necessarily the page on which it starts) ProbedPage p_first, p_last; // memory management stb_vorbis_alloc alloc; int setup_offset; int temp_offset; // run-time results int eof; enum STBVorbisError error; // user-useful data // header info int blocksize[2]; int blocksize_0, blocksize_1; int codebook_count; Codebook *codebooks; int floor_count; uint16 floor_types[64]; // varies Floor *floor_config; int residue_count; uint16 residue_types[64]; // varies Residue *residue_config; int mapping_count; Mapping *mapping; int mode_count; Mode mode_config[64]; // varies uint32 total_samples; // decode buffer float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; float *outputs [STB_VORBIS_MAX_CHANNELS]; float *previous_window[STB_VORBIS_MAX_CHANNELS]; int previous_length; #ifndef STB_VORBIS_NO_DEFER_FLOOR int16 *finalY[STB_VORBIS_MAX_CHANNELS]; #else float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; #endif uint32 current_loc; // sample location of next frame to decode int current_loc_valid; // per-blocksize precomputed data // twiddle factors float *A[2],*B[2],*C[2]; float *window[2]; uint16 *bit_reverse[2]; // current page/packet/segment streaming info uint32 serial; // stream serial number for verification int last_page; int segment_count; uint8 segments[255]; uint8 page_flag; uint8 bytes_in_seg; uint8 first_decode; int next_seg; int last_seg; // flag that we're on the last segment int last_seg_which; // what was the segment number of the last seg? uint32 acc; int valid_bits; int packet_bytes; int end_seg_with_known_loc; uint32 known_loc_for_packet; int discard_samples_deferred; uint32 samples_output; // push mode scanning int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching #ifndef STB_VORBIS_NO_PUSHDATA_API CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; #endif // sample-access int channel_buffer_start; int channel_buffer_end; }; #if defined(STB_VORBIS_NO_PUSHDATA_API) #define IS_PUSH_MODE(f) FALSE #elif defined(STB_VORBIS_NO_PULLDATA_API) #define IS_PUSH_MODE(f) TRUE #else #define IS_PUSH_MODE(f) ((f)->push_mode) #endif typedef struct stb_vorbis vorb; static int error(vorb *f, enum STBVorbisError e) { f->error = e; if (!f->eof && e != VORBIS_need_more_data) { f->error=e; // breakpoint for debugging } return 0; } // these functions are used for allocating temporary memory // while decoding. if you can afford the stack space, use // alloca(); otherwise, provide a temp buffer and it will // allocate out of those. #define array_size_required(count,size) (count*(sizeof(void *)+(size))) #define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) #define temp_free(f,p) (void)0 #define temp_alloc_save(f) ((f)->temp_offset) #define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) #define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) // given a sufficiently large block of memory, make an array of pointers to subblocks of it static void *make_block_array(void *mem, int count, int size) { int i; void ** p = (void **) mem; char *q = (char *) (p + count); for (i=0; i < count; ++i) { p[i] = q; q += size; } return p; } static void *setup_malloc(vorb *f, int sz) { sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. f->setup_memory_required += sz; if (f->alloc.alloc_buffer) { void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; if (f->setup_offset + sz > f->temp_offset) return NULL; f->setup_offset += sz; return p; } return sz ? malloc(sz) : NULL; } static void setup_free(vorb *f, void *p) { if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack free(p); } static void *setup_temp_malloc(vorb *f, int sz) { sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. if (f->alloc.alloc_buffer) { if (f->temp_offset - sz < f->setup_offset) return NULL; f->temp_offset -= sz; return (char *) f->alloc.alloc_buffer + f->temp_offset; } return malloc(sz); } static void setup_temp_free(vorb *f, void *p, int sz) { if (f->alloc.alloc_buffer) { f->temp_offset += (sz+7)&~7; return; } free(p); } #define CRC32_POLY 0x04c11db7 // from spec static uint32 crc_table[256]; static void crc32_init(void) { int i,j; uint32 s; for(i=0; i < 256; i++) { for (s=(uint32) i << 24, j=0; j < 8; ++j) s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); crc_table[i] = s; } } static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) { return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; } // used in setup, and for huffman that doesn't go fast path static unsigned int bit_reverse(unsigned int n) { n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); return (n >> 16) | (n << 16); } static float square(float x) { return x*x; } // this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 // as required by the specification. fast(?) implementation from stb.h // @OPTIMIZE: called multiple times per-packet with "constants"; move to setup static int ilog(int32 n) { static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; if (n < 0) return 0; // signed n returns 0 // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) if (n < (1 << 14)) if (n < (1 << 4)) return 0 + log2_4[n ]; else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; else return 10 + log2_4[n >> 10]; else if (n < (1 << 24)) if (n < (1 << 19)) return 15 + log2_4[n >> 15]; else return 20 + log2_4[n >> 20]; else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; else return 30 + log2_4[n >> 30]; } #ifndef M_PI #define M_PI 3.14159265358979323846264f // from CRC #endif // code length assigned to a value with no huffman encoding #define NO_CODE 255 /////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// // // these functions are only called at setup, and only a few times // per file static float float32_unpack(uint32 x) { // from the specification uint32 mantissa = x & 0x1fffff; uint32 sign = x & 0x80000000; uint32 exp = (x & 0x7fe00000) >> 21; double res = sign ? -(double)mantissa : (double)mantissa; return (float) ldexp((float)res, (int)exp-788); } // zlib & jpeg huffman tables assume that the output symbols // can either be arbitrarily arranged, or have monotonically // increasing frequencies--they rely on the lengths being sorted; // this makes for a very simple generation algorithm. // vorbis allows a huffman table with non-sorted lengths. This // requires a more sophisticated construction, since symbols in // order do not map to huffman codes "in order". static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) { if (!c->sparse) { c->codewords [symbol] = huff_code; } else { c->codewords [count] = huff_code; c->codeword_lengths[count] = len; values [count] = symbol; } } static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) { int i,k,m=0; uint32 available[32]; memset(available, 0, sizeof(available)); // find the first entry for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; if (k == n) { assert(c->sorted_entries == 0); return TRUE; } assert(len[k] < 32); // no error return required, code reading lens checks this // add to the list add_entry(c, 0, k, m++, len[k], values); // add all available leaves for (i=1; i <= len[k]; ++i) available[i] = 1U << (32-i); // note that the above code treats the first case specially, // but it's really the same as the following code, so they // could probably be combined (except the initial code is 0, // and I use 0 in available[] to mean 'empty') for (i=k+1; i < n; ++i) { uint32 res; int z = len[i], y; if (z == NO_CODE) continue; assert(z < 32); // no error return required, code reading lens checks this // find lowest available leaf (should always be earliest, // which is what the specification calls for) // note that this property, and the fact we can never have // more than one free leaf at a given level, isn't totally // trivial to prove, but it seems true and the assert never // fires, so! while (z > 0 && !available[z]) --z; if (z == 0) { return FALSE; } res = available[z]; available[z] = 0; add_entry(c, bit_reverse(res), i, m++, len[i], values); // propagate availability up the tree if (z != len[i]) { for (y=len[i]; y > z; --y) { assert(available[y] == 0); available[y] = res + (1 << (32-y)); } } } return TRUE; } // accelerated huffman table allows fast O(1) match of all symbols // of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH static void compute_accelerated_huffman(Codebook *c) { int i, len; for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) c->fast_huffman[i] = -1; len = c->sparse ? c->sorted_entries : c->entries; #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT if (len > 32767) len = 32767; // largest possible value we can encode! #endif for (i=0; i < len; ++i) { if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; // set table entries for all bit combinations in the higher bits while (z < FAST_HUFFMAN_TABLE_SIZE) { c->fast_huffman[z] = i; z += 1 << c->codeword_lengths[i]; } } } } #ifdef _MSC_VER #define STBV_CDECL __cdecl #else #define STBV_CDECL #endif static int STBV_CDECL uint32_compare(const void *p, const void *q) { uint32 x = * (uint32 *) p; uint32 y = * (uint32 *) q; return x < y ? -1 : x > y; } static int include_in_sort(Codebook *c, uint8 len) { if (c->sparse) { assert(len != NO_CODE); return TRUE; } if (len == NO_CODE) return FALSE; if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; return FALSE; } // if the fast table above doesn't work, we want to binary // search them... need to reverse the bits static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) { int i, len; // build a list of all the entries // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. // this is kind of a frivolous optimization--I don't see any performance improvement, // but it's like 4 extra lines of code, so. if (!c->sparse) { int k = 0; for (i=0; i < c->entries; ++i) if (include_in_sort(c, lengths[i])) c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); assert(k == c->sorted_entries); } else { for (i=0; i < c->sorted_entries; ++i) c->sorted_codewords[i] = bit_reverse(c->codewords[i]); } qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); c->sorted_codewords[c->sorted_entries] = 0xffffffff; len = c->sparse ? c->sorted_entries : c->entries; // now we need to indicate how they correspond; we could either // #1: sort a different data structure that says who they correspond to // #2: for each sorted entry, search the original list to find who corresponds // #3: for each original entry, find the sorted entry // #1 requires extra storage, #2 is slow, #3 can use binary search! for (i=0; i < len; ++i) { int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; if (include_in_sort(c,huff_len)) { uint32 code = bit_reverse(c->codewords[i]); int x=0, n=c->sorted_entries; while (n > 1) { // invariant: sc[x] <= code < sc[x+n] int m = x + (n >> 1); if (c->sorted_codewords[m] <= code) { x = m; n -= (n>>1); } else { n >>= 1; } } assert(c->sorted_codewords[x] == code); if (c->sparse) { c->sorted_values[x] = values[i]; c->codeword_lengths[x] = huff_len; } else { c->sorted_values[x] = i; } } } } // only run while parsing the header (3 times) static int vorbis_validate(uint8 *data) { static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; return memcmp(data, vorbis, 6) == 0; } // called from setup only, once per code book // (formula implied by specification) static int lookup1_values(int entries, int dim) { int r = (int) floor(exp((float) log((float) entries) / dim)); if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; ++r; // floor() to avoid _ftol() when non-CRT if (pow((float) r+1, dim) <= entries) return -1; if ((int) floor(pow((float) r, dim)) > entries) return -1; return r; } // called twice per file static void compute_twiddle_factors(int n, float *A, float *B, float *C) { int n4 = n >> 2, n8 = n >> 3; int k,k2; for (k=k2=0; k < n4; ++k,k2+=2) { A[k2 ] = (float) cos(4*k*M_PI/n); A[k2+1] = (float) -sin(4*k*M_PI/n); B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; } for (k=k2=0; k < n8; ++k,k2+=2) { C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); } } static void compute_window(int n, float *window) { int n2 = n >> 1, i; for (i=0; i < n2; ++i) window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); } static void compute_bitreverse(int n, uint16 *rev) { int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions int i, n8 = n >> 3; for (i=0; i < n8; ++i) rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; } static int init_blocksize(vorb *f, int b, int n) { int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); if (!f->window[b]) return error(f, VORBIS_outofmem); compute_window(n, f->window[b]); f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); compute_bitreverse(n, f->bit_reverse[b]); return TRUE; } static void neighbors(uint16 *x, int n, int *plow, int *phigh) { int low = -1; int high = 65536; int i; for (i=0; i < n; ++i) { if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } } } // this has been repurposed so y is now the original index instead of y typedef struct { uint16 x,id; } stbv__floor_ordering; static int STBV_CDECL point_compare(const void *p, const void *q) { stbv__floor_ordering *a = (stbv__floor_ordering *) p; stbv__floor_ordering *b = (stbv__floor_ordering *) q; return a->x < b->x ? -1 : a->x > b->x; } // /////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// #if defined(STB_VORBIS_NO_STDIO) #define USE_MEMORY(z) TRUE #else #define USE_MEMORY(z) ((z)->stream) #endif static uint8 get8(vorb *z) { if (USE_MEMORY(z)) { if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } return *z->stream++; } #ifndef STB_VORBIS_NO_STDIO { int c = fgetc(z->f); if (c == EOF) { z->eof = TRUE; return 0; } return c; } #endif } static uint32 get32(vorb *f) { uint32 x; x = get8(f); x += get8(f) << 8; x += get8(f) << 16; x += (uint32) get8(f) << 24; return x; } static int getn(vorb *z, uint8 *data, int n) { if (USE_MEMORY(z)) { if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } memcpy(data, z->stream, n); z->stream += n; return 1; } #ifndef STB_VORBIS_NO_STDIO if (fread(data, n, 1, z->f) == 1) return 1; else { z->eof = 1; return 0; } #endif } static void skip(vorb *z, int n) { if (USE_MEMORY(z)) { z->stream += n; if (z->stream >= z->stream_end) z->eof = 1; return; } #ifndef STB_VORBIS_NO_STDIO { long x = ftell(z->f); fseek(z->f, x+n, SEEK_SET); } #endif } static int set_file_offset(stb_vorbis *f, unsigned int loc) { #ifndef STB_VORBIS_NO_PUSHDATA_API if (f->push_mode) return 0; #endif f->eof = 0; if (USE_MEMORY(f)) { if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { f->stream = f->stream_end; f->eof = 1; return 0; } else { f->stream = f->stream_start + loc; return 1; } } #ifndef STB_VORBIS_NO_STDIO if (loc + f->f_start < loc || loc >= 0x80000000) { loc = 0x7fffffff; f->eof = 1; } else { loc += f->f_start; } if (!fseek(f->f, loc, SEEK_SET)) return 1; f->eof = 1; fseek(f->f, f->f_start, SEEK_END); return 0; #endif } static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; static int capture_pattern(vorb *f) { if (0x4f != get8(f)) return FALSE; if (0x67 != get8(f)) return FALSE; if (0x67 != get8(f)) return FALSE; if (0x53 != get8(f)) return FALSE; return TRUE; } #define PAGEFLAG_continued_packet 1 #define PAGEFLAG_first_page 2 #define PAGEFLAG_last_page 4 static int start_page_no_capturepattern(vorb *f) { uint32 loc0,loc1,n; if (f->first_decode && !IS_PUSH_MODE(f)) { f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; } // stream structure version if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); // header flag f->page_flag = get8(f); // absolute granule position loc0 = get32(f); loc1 = get32(f); // @TODO: validate loc0,loc1 as valid positions? // stream serial number -- vorbis doesn't interleave, so discard get32(f); //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); // page sequence number n = get32(f); f->last_page = n; // CRC32 get32(f); // page_segments f->segment_count = get8(f); if (!getn(f, f->segments, f->segment_count)) return error(f, VORBIS_unexpected_eof); // assume we _don't_ know any the sample position of any segments f->end_seg_with_known_loc = -2; if (loc0 != ~0U || loc1 != ~0U) { int i; // determine which packet is the last one that will complete for (i=f->segment_count-1; i >= 0; --i) if (f->segments[i] < 255) break; // 'i' is now the index of the _last_ segment of a packet that ends if (i >= 0) { f->end_seg_with_known_loc = i; f->known_loc_for_packet = loc0; } } if (f->first_decode) { int i,len; len = 0; for (i=0; i < f->segment_count; ++i) len += f->segments[i]; len += 27 + f->segment_count; f->p_first.page_end = f->p_first.page_start + len; f->p_first.last_decoded_sample = loc0; } f->next_seg = 0; return TRUE; } static int start_page(vorb *f) { if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); return start_page_no_capturepattern(f); } static int start_packet(vorb *f) { while (f->next_seg == -1) { if (!start_page(f)) return FALSE; if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_continued_packet_flag_invalid); } f->last_seg = FALSE; f->valid_bits = 0; f->packet_bytes = 0; f->bytes_in_seg = 0; // f->next_seg is now valid return TRUE; } static int maybe_start_packet(vorb *f) { if (f->next_seg == -1) { int x = get8(f); if (f->eof) return FALSE; // EOF at page boundary is not an error! if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); if (!start_page_no_capturepattern(f)) return FALSE; if (f->page_flag & PAGEFLAG_continued_packet) { // set up enough state that we can read this packet if we want, // e.g. during recovery f->last_seg = FALSE; f->bytes_in_seg = 0; return error(f, VORBIS_continued_packet_flag_invalid); } } return start_packet(f); } static int next_segment(vorb *f) { int len; if (f->last_seg) return 0; if (f->next_seg == -1) { f->last_seg_which = f->segment_count-1; // in case start_page fails if (!start_page(f)) { f->last_seg = 1; return 0; } if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); } len = f->segments[f->next_seg++]; if (len < 255) { f->last_seg = TRUE; f->last_seg_which = f->next_seg-1; } if (f->next_seg >= f->segment_count) f->next_seg = -1; assert(f->bytes_in_seg == 0); f->bytes_in_seg = len; return len; } #define EOP (-1) #define INVALID_BITS (-1) static int get8_packet_raw(vorb *f) { if (!f->bytes_in_seg) { // CLANG! if (f->last_seg) return EOP; else if (!next_segment(f)) return EOP; } assert(f->bytes_in_seg > 0); --f->bytes_in_seg; ++f->packet_bytes; return get8(f); } static int get8_packet(vorb *f) { int x = get8_packet_raw(f); f->valid_bits = 0; return x; } static int get32_packet(vorb *f) { uint32 x; x = get8_packet(f); x += get8_packet(f) << 8; x += get8_packet(f) << 16; x += (uint32) get8_packet(f) << 24; return x; } static void flush_packet(vorb *f) { while (get8_packet_raw(f) != EOP); } // @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important // as the huffman decoder? static uint32 get_bits(vorb *f, int n) { uint32 z; if (f->valid_bits < 0) return 0; if (f->valid_bits < n) { if (n > 24) { // the accumulator technique below would not work correctly in this case z = get_bits(f, 24); z += get_bits(f, n-24) << 24; return z; } if (f->valid_bits == 0) f->acc = 0; while (f->valid_bits < n) { int z = get8_packet_raw(f); if (z == EOP) { f->valid_bits = INVALID_BITS; return 0; } f->acc += z << f->valid_bits; f->valid_bits += 8; } } assert(f->valid_bits >= n); z = f->acc & ((1 << n)-1); f->acc >>= n; f->valid_bits -= n; return z; } // @OPTIMIZE: primary accumulator for huffman // expand the buffer to as many bits as possible without reading off end of packet // it might be nice to allow f->valid_bits and f->acc to be stored in registers, // e.g. cache them locally and decode locally static __forceinline void prep_huffman(vorb *f) { if (f->valid_bits <= 24) { if (f->valid_bits == 0) f->acc = 0; do { int z; if (f->last_seg && !f->bytes_in_seg) return; z = get8_packet_raw(f); if (z == EOP) return; f->acc += (unsigned) z << f->valid_bits; f->valid_bits += 8; } while (f->valid_bits <= 24); } } enum { VORBIS_packet_id = 1, VORBIS_packet_comment = 3, VORBIS_packet_setup = 5 }; static int codebook_decode_scalar_raw(vorb *f, Codebook *c) { int i; prep_huffman(f); if (c->codewords == NULL && c->sorted_codewords == NULL) return -1; // cases to use binary search: sorted_codewords && !c->codewords // sorted_codewords && c->entries > 8 if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { // binary search uint32 code = bit_reverse(f->acc); int x=0, n=c->sorted_entries, len; while (n > 1) { // invariant: sc[x] <= code < sc[x+n] int m = x + (n >> 1); if (c->sorted_codewords[m] <= code) { x = m; n -= (n>>1); } else { n >>= 1; } } // x is now the sorted index if (!c->sparse) x = c->sorted_values[x]; // x is now sorted index if sparse, or symbol otherwise len = c->codeword_lengths[x]; if (f->valid_bits >= len) { f->acc >>= len; f->valid_bits -= len; return x; } f->valid_bits = 0; return -1; } // if small, linear search assert(!c->sparse); for (i=0; i < c->entries; ++i) { if (c->codeword_lengths[i] == NO_CODE) continue; if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { if (f->valid_bits >= c->codeword_lengths[i]) { f->acc >>= c->codeword_lengths[i]; f->valid_bits -= c->codeword_lengths[i]; return i; } f->valid_bits = 0; return -1; } } error(f, VORBIS_invalid_stream); f->valid_bits = 0; return -1; } #ifndef STB_VORBIS_NO_INLINE_DECODE #define DECODE_RAW(var, f,c) \ if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ prep_huffman(f); \ var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ var = c->fast_huffman[var]; \ if (var >= 0) { \ int n = c->codeword_lengths[var]; \ f->acc >>= n; \ f->valid_bits -= n; \ if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ } else { \ var = codebook_decode_scalar_raw(f,c); \ } #else static int codebook_decode_scalar(vorb *f, Codebook *c) { int i; if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) prep_huffman(f); // fast huffman table lookup i = f->acc & FAST_HUFFMAN_TABLE_MASK; i = c->fast_huffman[i]; if (i >= 0) { f->acc >>= c->codeword_lengths[i]; f->valid_bits -= c->codeword_lengths[i]; if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } return i; } return codebook_decode_scalar_raw(f,c); } #define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); #endif #define DECODE(var,f,c) \ DECODE_RAW(var,f,c) \ if (c->sparse) var = c->sorted_values[var]; #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) #else #define DECODE_VQ(var,f,c) DECODE(var,f,c) #endif // CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case // where we avoid one addition #define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) #define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) #define CODEBOOK_ELEMENT_BASE(c) (0) static int codebook_decode_start(vorb *f, Codebook *c) { int z = -1; // type 0 is only legal in a scalar context if (c->lookup_type == 0) error(f, VORBIS_invalid_stream); else { DECODE_VQ(z,f,c); if (c->sparse) assert(z < c->sorted_entries); if (z < 0) { // check for EOP if (!f->bytes_in_seg) if (f->last_seg) return z; error(f, VORBIS_invalid_stream); } } return z; } static int codebook_decode(vorb *f, Codebook *c, float *output, int len) { int i,z = codebook_decode_start(f,c); if (z < 0) return FALSE; if (len > c->dimensions) len = c->dimensions; #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK if (c->lookup_type == 1) { float last = CODEBOOK_ELEMENT_BASE(c); int div = 1; for (i=0; i < len; ++i) { int off = (z / div) % c->lookup_values; float val = CODEBOOK_ELEMENT_FAST(c,off) + last; output[i] += val; if (c->sequence_p) last = val + c->minimum_value; div *= c->lookup_values; } return TRUE; } #endif z *= c->dimensions; if (c->sequence_p) { float last = CODEBOOK_ELEMENT_BASE(c); for (i=0; i < len; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; output[i] += val; last = val + c->minimum_value; } } else { float last = CODEBOOK_ELEMENT_BASE(c); for (i=0; i < len; ++i) { output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; } } return TRUE; } static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) { int i,z = codebook_decode_start(f,c); float last = CODEBOOK_ELEMENT_BASE(c); if (z < 0) return FALSE; if (len > c->dimensions) len = c->dimensions; #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK if (c->lookup_type == 1) { int div = 1; for (i=0; i < len; ++i) { int off = (z / div) % c->lookup_values; float val = CODEBOOK_ELEMENT_FAST(c,off) + last; output[i*step] += val; if (c->sequence_p) last = val; div *= c->lookup_values; } return TRUE; } #endif z *= c->dimensions; for (i=0; i < len; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; output[i*step] += val; if (c->sequence_p) last = val; } return TRUE; } static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) { int c_inter = *c_inter_p; int p_inter = *p_inter_p; int i,z, effective = c->dimensions; // type 0 is only legal in a scalar context if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); while (total_decode > 0) { float last = CODEBOOK_ELEMENT_BASE(c); DECODE_VQ(z,f,c); #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK assert(!c->sparse || z < c->sorted_entries); #endif if (z < 0) { if (!f->bytes_in_seg) if (f->last_seg) return FALSE; return error(f, VORBIS_invalid_stream); } // if this will take us off the end of the buffers, stop short! // we check by computing the length of the virtual interleaved // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), // and the length we'll be using (effective) if (c_inter + p_inter*ch + effective > len * ch) { effective = len*ch - (p_inter*ch - c_inter); } #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK if (c->lookup_type == 1) { int div = 1; for (i=0; i < effective; ++i) { int off = (z / div) % c->lookup_values; float val = CODEBOOK_ELEMENT_FAST(c,off) + last; if (outputs[c_inter]) outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } if (c->sequence_p) last = val; div *= c->lookup_values; } } else #endif { z *= c->dimensions; if (c->sequence_p) { for (i=0; i < effective; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; if (outputs[c_inter]) outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } last = val; } } else { for (i=0; i < effective; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; if (outputs[c_inter]) outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } } } } total_decode -= effective; } *c_inter_p = c_inter; *p_inter_p = p_inter; return TRUE; } static int predict_point(int x, int x0, int x1, int y0, int y1) { int dy = y1 - y0; int adx = x1 - x0; // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? int err = abs(dy) * (x - x0); int off = err / adx; return dy < 0 ? y0 - off : y0 + off; } // the following table is block-copied from the specification static float inverse_db_table[256] = { 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, 0.82788260f, 0.88168307f, 0.9389798f, 1.0f }; // @OPTIMIZE: if you want to replace this bresenham line-drawing routine, // note that you must produce bit-identical output to decode correctly; // this specific sequence of operations is specified in the spec (it's // drawing integer-quantized frequency-space lines that the encoder // expects to be exactly the same) // ... also, isn't the whole point of Bresenham's algorithm to NOT // have to divide in the setup? sigh. #ifndef STB_VORBIS_NO_DEFER_FLOOR #define LINE_OP(a,b) a *= b #else #define LINE_OP(a,b) a = b #endif #ifdef STB_VORBIS_DIVIDE_TABLE #define DIVTAB_NUMER 32 #define DIVTAB_DENOM 64 int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB #endif static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) { int dy = y1 - y0; int adx = x1 - x0; int ady = abs(dy); int base; int x=x0,y=y0; int err = 0; int sy; #ifdef STB_VORBIS_DIVIDE_TABLE if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { if (dy < 0) { base = -integer_divide_table[ady][adx]; sy = base-1; } else { base = integer_divide_table[ady][adx]; sy = base+1; } } else { base = dy / adx; if (dy < 0) sy = base - 1; else sy = base+1; } #else base = dy / adx; if (dy < 0) sy = base - 1; else sy = base+1; #endif ady -= abs(base) * adx; if (x1 > n) x1 = n; if (x < x1) { LINE_OP(output[x], inverse_db_table[y&255]); for (++x; x < x1; ++x) { err += ady; if (err >= adx) { err -= adx; y += sy; } else y += base; LINE_OP(output[x], inverse_db_table[y&255]); } } } static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) { int k; if (rtype == 0) { int step = n / book->dimensions; for (k=0; k < step; ++k) if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) return FALSE; } else { for (k=0; k < n; ) { if (!codebook_decode(f, book, target+offset, n-k)) return FALSE; k += book->dimensions; offset += book->dimensions; } } return TRUE; } // n is 1/2 of the blocksize -- // specification: "Correct per-vector decode length is [n]/2" static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) { int i,j,pass; Residue *r = f->residue_config + rn; int rtype = f->residue_types[rn]; int c = r->classbook; int classwords = f->codebooks[c].dimensions; unsigned int actual_size = rtype == 2 ? n*2 : n; unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); int n_read = limit_r_end - limit_r_begin; int part_read = n_read / r->part_size; int temp_alloc_point = temp_alloc_save(f); #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); #else int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); #endif CHECK(f); for (i=0; i < ch; ++i) if (!do_not_decode[i]) memset(residue_buffers[i], 0, sizeof(float) * n); if (rtype == 2 && ch != 1) { for (j=0; j < ch; ++j) if (!do_not_decode[j]) break; if (j == ch) goto done; for (pass=0; pass < 8; ++pass) { int pcount = 0, class_set = 0; if (ch == 2) { while (pcount < part_read) { int z = r->begin + pcount*r->part_size; int c_inter = (z & 1), p_inter = z>>1; if (pass == 0) { Codebook *c = f->codebooks+r->classbook; int q; DECODE(q,f,c); if (q == EOP) goto done; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE part_classdata[0][class_set] = r->classdata[q]; #else for (i=classwords-1; i >= 0; --i) { classifications[0][i+pcount] = q % r->classifications; q /= r->classifications; } #endif } for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { int z = r->begin + pcount*r->part_size; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE int c = part_classdata[0][class_set][i]; #else int c = classifications[0][pcount]; #endif int b = r->residue_books[c][pass]; if (b >= 0) { Codebook *book = f->codebooks + b; #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) goto done; #else // saves 1% if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) goto done; #endif } else { z += r->part_size; c_inter = z & 1; p_inter = z >> 1; } } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE ++class_set; #endif } } else if (ch > 2) { while (pcount < part_read) { int z = r->begin + pcount*r->part_size; int c_inter = z % ch, p_inter = z/ch; if (pass == 0) { Codebook *c = f->codebooks+r->classbook; int q; DECODE(q,f,c); if (q == EOP) goto done; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE part_classdata[0][class_set] = r->classdata[q]; #else for (i=classwords-1; i >= 0; --i) { classifications[0][i+pcount] = q % r->classifications; q /= r->classifications; } #endif } for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { int z = r->begin + pcount*r->part_size; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE int c = part_classdata[0][class_set][i]; #else int c = classifications[0][pcount]; #endif int b = r->residue_books[c][pass]; if (b >= 0) { Codebook *book = f->codebooks + b; if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) goto done; } else { z += r->part_size; c_inter = z % ch; p_inter = z / ch; } } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE ++class_set; #endif } } } goto done; } CHECK(f); for (pass=0; pass < 8; ++pass) { int pcount = 0, class_set=0; while (pcount < part_read) { if (pass == 0) { for (j=0; j < ch; ++j) { if (!do_not_decode[j]) { Codebook *c = f->codebooks+r->classbook; int temp; DECODE(temp,f,c); if (temp == EOP) goto done; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE part_classdata[j][class_set] = r->classdata[temp]; #else for (i=classwords-1; i >= 0; --i) { classifications[j][i+pcount] = temp % r->classifications; temp /= r->classifications; } #endif } } } for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { for (j=0; j < ch; ++j) { if (!do_not_decode[j]) { #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE int c = part_classdata[j][class_set][i]; #else int c = classifications[j][pcount]; #endif int b = r->residue_books[c][pass]; if (b >= 0) { float *target = residue_buffers[j]; int offset = r->begin + pcount * r->part_size; int n = r->part_size; Codebook *book = f->codebooks + b; if (!residue_decode(f, book, target, offset, n, rtype)) goto done; } } } } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE ++class_set; #endif } } done: CHECK(f); #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE temp_free(f,part_classdata); #else temp_free(f,classifications); #endif temp_alloc_restore(f,temp_alloc_point); } #if 0 // slow way for debugging void inverse_mdct_slow(float *buffer, int n) { int i,j; int n2 = n >> 1; float *x = (float *) malloc(sizeof(*x) * n2); memcpy(x, buffer, sizeof(*x) * n2); for (i=0; i < n; ++i) { float acc = 0; for (j=0; j < n2; ++j) // formula from paper: //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); // formula from wikipedia //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); // these are equivalent, except the formula from the paper inverts the multiplier! // however, what actually works is NO MULTIPLIER!?! //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); buffer[i] = acc; } free(x); } #elif 0 // same as above, but just barely able to run in real time on modern machines void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) { float mcos[16384]; int i,j; int n2 = n >> 1, nmask = (n << 2) -1; float *x = (float *) malloc(sizeof(*x) * n2); memcpy(x, buffer, sizeof(*x) * n2); for (i=0; i < 4*n; ++i) mcos[i] = (float) cos(M_PI / 2 * i / n); for (i=0; i < n; ++i) { float acc = 0; for (j=0; j < n2; ++j) acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; buffer[i] = acc; } free(x); } #elif 0 // transform to use a slow dct-iv; this is STILL basically trivial, // but only requires half as many ops void dct_iv_slow(float *buffer, int n) { float mcos[16384]; float x[2048]; int i,j; int n2 = n >> 1, nmask = (n << 3) - 1; memcpy(x, buffer, sizeof(*x) * n); for (i=0; i < 8*n; ++i) mcos[i] = (float) cos(M_PI / 4 * i / n); for (i=0; i < n; ++i) { float acc = 0; for (j=0; j < n; ++j) acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; buffer[i] = acc; } } void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) { int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; float temp[4096]; memcpy(temp, buffer, n2 * sizeof(float)); dct_iv_slow(temp, n2); // returns -c'-d, a-b' for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d } #endif #ifndef LIBVORBIS_MDCT #define LIBVORBIS_MDCT 0 #endif #if LIBVORBIS_MDCT // directly call the vorbis MDCT using an interface documented // by Jeff Roberts... useful for performance comparison typedef struct { int n; int log2n; float *trig; int *bitrev; float scale; } mdct_lookup; extern void mdct_init(mdct_lookup *lookup, int n); extern void mdct_clear(mdct_lookup *l); extern void mdct_backward(mdct_lookup *init, float *in, float *out); mdct_lookup M1,M2; void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) { mdct_lookup *M; if (M1.n == n) M = &M1; else if (M2.n == n) M = &M2; else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } else { if (M2.n) __asm int 3; mdct_init(&M2, n); M = &M2; } mdct_backward(M, buffer, buffer); } #endif // the following were split out into separate functions while optimizing; // they could be pushed back up but eh. __forceinline showed no change; // they're probably already being inlined. static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) { float *ee0 = e + i_off; float *ee2 = ee0 + k_off; int i; assert((n & 3) == 0); for (i=(n>>2); i > 0; --i) { float k00_20, k01_21; k00_20 = ee0[ 0] - ee2[ 0]; k01_21 = ee0[-1] - ee2[-1]; ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; A += 8; k00_20 = ee0[-2] - ee2[-2]; k01_21 = ee0[-3] - ee2[-3]; ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; A += 8; k00_20 = ee0[-4] - ee2[-4]; k01_21 = ee0[-5] - ee2[-5]; ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; A += 8; k00_20 = ee0[-6] - ee2[-6]; k01_21 = ee0[-7] - ee2[-7]; ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; A += 8; ee0 -= 8; ee2 -= 8; } } static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) { int i; float k00_20, k01_21; float *e0 = e + d0; float *e2 = e0 + k_off; for (i=lim >> 2; i > 0; --i) { k00_20 = e0[-0] - e2[-0]; k01_21 = e0[-1] - e2[-1]; e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; A += k1; k00_20 = e0[-2] - e2[-2]; k01_21 = e0[-3] - e2[-3]; e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; A += k1; k00_20 = e0[-4] - e2[-4]; k01_21 = e0[-5] - e2[-5]; e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; A += k1; k00_20 = e0[-6] - e2[-6]; k01_21 = e0[-7] - e2[-7]; e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; e0 -= 8; e2 -= 8; A += k1; } } static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) { int i; float A0 = A[0]; float A1 = A[0+1]; float A2 = A[0+a_off]; float A3 = A[0+a_off+1]; float A4 = A[0+a_off*2+0]; float A5 = A[0+a_off*2+1]; float A6 = A[0+a_off*3+0]; float A7 = A[0+a_off*3+1]; float k00,k11; float *ee0 = e +i_off; float *ee2 = ee0+k_off; for (i=n; i > 0; --i) { k00 = ee0[ 0] - ee2[ 0]; k11 = ee0[-1] - ee2[-1]; ee0[ 0] = ee0[ 0] + ee2[ 0]; ee0[-1] = ee0[-1] + ee2[-1]; ee2[ 0] = (k00) * A0 - (k11) * A1; ee2[-1] = (k11) * A0 + (k00) * A1; k00 = ee0[-2] - ee2[-2]; k11 = ee0[-3] - ee2[-3]; ee0[-2] = ee0[-2] + ee2[-2]; ee0[-3] = ee0[-3] + ee2[-3]; ee2[-2] = (k00) * A2 - (k11) * A3; ee2[-3] = (k11) * A2 + (k00) * A3; k00 = ee0[-4] - ee2[-4]; k11 = ee0[-5] - ee2[-5]; ee0[-4] = ee0[-4] + ee2[-4]; ee0[-5] = ee0[-5] + ee2[-5]; ee2[-4] = (k00) * A4 - (k11) * A5; ee2[-5] = (k11) * A4 + (k00) * A5; k00 = ee0[-6] - ee2[-6]; k11 = ee0[-7] - ee2[-7]; ee0[-6] = ee0[-6] + ee2[-6]; ee0[-7] = ee0[-7] + ee2[-7]; ee2[-6] = (k00) * A6 - (k11) * A7; ee2[-7] = (k11) * A6 + (k00) * A7; ee0 -= k0; ee2 -= k0; } } static __forceinline void iter_54(float *z) { float k00,k11,k22,k33; float y0,y1,y2,y3; k00 = z[ 0] - z[-4]; y0 = z[ 0] + z[-4]; y2 = z[-2] + z[-6]; k22 = z[-2] - z[-6]; z[-0] = y0 + y2; // z0 + z4 + z2 + z6 z[-2] = y0 - y2; // z0 + z4 - z2 - z6 // done with y0,y2 k33 = z[-3] - z[-7]; z[-4] = k00 + k33; // z0 - z4 + z3 - z7 z[-6] = k00 - k33; // z0 - z4 - z3 + z7 // done with k33 k11 = z[-1] - z[-5]; y1 = z[-1] + z[-5]; y3 = z[-3] + z[-7]; z[-1] = y1 + y3; // z1 + z5 + z3 + z7 z[-3] = y1 - y3; // z1 + z5 - z3 - z7 z[-5] = k11 - k22; // z1 - z5 + z2 - z6 z[-7] = k11 + k22; // z1 - z5 - z2 + z6 } static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) { int a_off = base_n >> 3; float A2 = A[0+a_off]; float *z = e + i_off; float *base = z - 16 * n; while (z > base) { float k00,k11; float l00,l11; k00 = z[-0] - z[ -8]; k11 = z[-1] - z[ -9]; l00 = z[-2] - z[-10]; l11 = z[-3] - z[-11]; z[ -0] = z[-0] + z[ -8]; z[ -1] = z[-1] + z[ -9]; z[ -2] = z[-2] + z[-10]; z[ -3] = z[-3] + z[-11]; z[ -8] = k00; z[ -9] = k11; z[-10] = (l00+l11) * A2; z[-11] = (l11-l00) * A2; k00 = z[ -4] - z[-12]; k11 = z[ -5] - z[-13]; l00 = z[ -6] - z[-14]; l11 = z[ -7] - z[-15]; z[ -4] = z[ -4] + z[-12]; z[ -5] = z[ -5] + z[-13]; z[ -6] = z[ -6] + z[-14]; z[ -7] = z[ -7] + z[-15]; z[-12] = k11; z[-13] = -k00; z[-14] = (l11-l00) * A2; z[-15] = (l00+l11) * -A2; iter_54(z); iter_54(z-8); z -= 16; } } static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) { int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; int ld; // @OPTIMIZE: reduce register pressure by using fewer variables? int save_point = temp_alloc_save(f); float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); float *u=NULL,*v=NULL; // twiddle factors float *A = f->A[blocktype]; // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. // kernel from paper // merged: // copy and reflect spectral data // step 0 // note that it turns out that the items added together during // this step are, in fact, being added to themselves (as reflected // by step 0). inexplicable inefficiency! this became obvious // once I combined the passes. // so there's a missing 'times 2' here (for adding X to itself). // this propagates through linearly to the end, where the numbers // are 1/2 too small, and need to be compensated for. { float *d,*e, *AA, *e_stop; d = &buf2[n2-2]; AA = A; e = &buffer[0]; e_stop = &buffer[n2]; while (e != e_stop) { d[1] = (e[0] * AA[0] - e[2]*AA[1]); d[0] = (e[0] * AA[1] + e[2]*AA[0]); d -= 2; AA += 2; e += 4; } e = &buffer[n2-3]; while (d >= buf2) { d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); d -= 2; AA += 2; e -= 4; } } // now we use symbolic names for these, so that we can // possibly swap their meaning as we change which operations // are in place u = buffer; v = buf2; // step 2 (paper output is w, now u) // this could be in place, but the data ends up in the wrong // place... _somebody_'s got to swap it, so this is nominated { float *AA = &A[n2-8]; float *d0,*d1, *e0, *e1; e0 = &v[n4]; e1 = &v[0]; d0 = &u[n4]; d1 = &u[0]; while (AA >= A) { float v40_20, v41_21; v41_21 = e0[1] - e1[1]; v40_20 = e0[0] - e1[0]; d0[1] = e0[1] + e1[1]; d0[0] = e0[0] + e1[0]; d1[1] = v41_21*AA[4] - v40_20*AA[5]; d1[0] = v40_20*AA[4] + v41_21*AA[5]; v41_21 = e0[3] - e1[3]; v40_20 = e0[2] - e1[2]; d0[3] = e0[3] + e1[3]; d0[2] = e0[2] + e1[2]; d1[3] = v41_21*AA[0] - v40_20*AA[1]; d1[2] = v40_20*AA[0] + v41_21*AA[1]; AA -= 8; d0 += 4; d1 += 4; e0 += 4; e1 += 4; } } // step 3 ld = ilog(n) - 1; // ilog is off-by-one from normal definitions // optimized step 3: // the original step3 loop can be nested r inside s or s inside r; // it's written originally as s inside r, but this is dumb when r // iterates many times, and s few. So I have two copies of it and // switch between them halfway. // this is iteration 0 of step 3 imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); // this is iteration 1 of step 3 imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); l=2; for (; l < (ld-3)>>1; ++l) { int k0 = n >> (l+2), k0_2 = k0>>1; int lim = 1 << (l+1); int i; for (i=0; i < lim; ++i) imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); } for (; l < ld-6; ++l) { int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; int rlim = n >> (l+6), r; int lim = 1 << (l+1); int i_off; float *A0 = A; i_off = n2-1; for (r=rlim; r > 0; --r) { imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); A0 += k1*4; i_off -= 8; } } // iterations with count: // ld-6,-5,-4 all interleaved together // the big win comes from getting rid of needless flops // due to the constants on pass 5 & 4 being all 1 and 0; // combining them to be simultaneous to improve cache made little difference imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); // output is u // step 4, 5, and 6 // cannot be in-place because of step 5 { uint16 *bitrev = f->bit_reverse[blocktype]; // weirdly, I'd have thought reading sequentially and writing // erratically would have been better than vice-versa, but in // fact that's not what my testing showed. (That is, with // j = bitreverse(i), do you read i and write j, or read j and write i.) float *d0 = &v[n4-4]; float *d1 = &v[n2-4]; while (d0 >= v) { int k4; k4 = bitrev[0]; d1[3] = u[k4+0]; d1[2] = u[k4+1]; d0[3] = u[k4+2]; d0[2] = u[k4+3]; k4 = bitrev[1]; d1[1] = u[k4+0]; d1[0] = u[k4+1]; d0[1] = u[k4+2]; d0[0] = u[k4+3]; d0 -= 4; d1 -= 4; bitrev += 2; } } // (paper output is u, now v) // data must be in buf2 assert(v == buf2); // step 7 (paper output is v, now v) // this is now in place { float *C = f->C[blocktype]; float *d, *e; d = v; e = v + n2 - 4; while (d < e) { float a02,a11,b0,b1,b2,b3; a02 = d[0] - e[2]; a11 = d[1] + e[3]; b0 = C[1]*a02 + C[0]*a11; b1 = C[1]*a11 - C[0]*a02; b2 = d[0] + e[ 2]; b3 = d[1] - e[ 3]; d[0] = b2 + b0; d[1] = b3 + b1; e[2] = b2 - b0; e[3] = b1 - b3; a02 = d[2] - e[0]; a11 = d[3] + e[1]; b0 = C[3]*a02 + C[2]*a11; b1 = C[3]*a11 - C[2]*a02; b2 = d[2] + e[ 0]; b3 = d[3] - e[ 1]; d[2] = b2 + b0; d[3] = b3 + b1; e[0] = b2 - b0; e[1] = b1 - b3; C += 4; d += 4; e -= 4; } } // data must be in buf2 // step 8+decode (paper output is X, now buffer) // this generates pairs of data a la 8 and pushes them directly through // the decode kernel (pushing rather than pulling) to avoid having // to make another pass later // this cannot POSSIBLY be in place, so we refer to the buffers directly { float *d0,*d1,*d2,*d3; float *B = f->B[blocktype] + n2 - 8; float *e = buf2 + n2 - 8; d0 = &buffer[0]; d1 = &buffer[n2-4]; d2 = &buffer[n2]; d3 = &buffer[n-4]; while (e >= v) { float p0,p1,p2,p3; p3 = e[6]*B[7] - e[7]*B[6]; p2 = -e[6]*B[6] - e[7]*B[7]; d0[0] = p3; d1[3] = - p3; d2[0] = p2; d3[3] = p2; p1 = e[4]*B[5] - e[5]*B[4]; p0 = -e[4]*B[4] - e[5]*B[5]; d0[1] = p1; d1[2] = - p1; d2[1] = p0; d3[2] = p0; p3 = e[2]*B[3] - e[3]*B[2]; p2 = -e[2]*B[2] - e[3]*B[3]; d0[2] = p3; d1[1] = - p3; d2[2] = p2; d3[1] = p2; p1 = e[0]*B[1] - e[1]*B[0]; p0 = -e[0]*B[0] - e[1]*B[1]; d0[3] = p1; d1[0] = - p1; d2[3] = p0; d3[0] = p0; B -= 8; e -= 8; d0 += 4; d2 += 4; d1 -= 4; d3 -= 4; } } temp_free(f,buf2); temp_alloc_restore(f,save_point); } #if 0 // this is the original version of the above code, if you want to optimize it from scratch void inverse_mdct_naive(float *buffer, int n) { float s; float A[1 << 12], B[1 << 12], C[1 << 11]; int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; int n3_4 = n - n4, ld; // how can they claim this only uses N words?! // oh, because they're only used sparsely, whoops float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; // set up twiddle factors for (k=k2=0; k < n4; ++k,k2+=2) { A[k2 ] = (float) cos(4*k*M_PI/n); A[k2+1] = (float) -sin(4*k*M_PI/n); B[k2 ] = (float) cos((k2+1)*M_PI/n/2); B[k2+1] = (float) sin((k2+1)*M_PI/n/2); } for (k=k2=0; k < n8; ++k,k2+=2) { C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); } // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" // Note there are bugs in that pseudocode, presumably due to them attempting // to rename the arrays nicely rather than representing the way their actual // implementation bounces buffers back and forth. As a result, even in the // "some formulars corrected" version, a direct implementation fails. These // are noted below as "paper bug". // copy and reflect spectral data for (k=0; k < n2; ++k) u[k] = buffer[k]; for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; // kernel from paper // step 1 for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; } // step 2 for (k=k4=0; k < n8; k+=1, k4+=4) { w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; } // step 3 ld = ilog(n) - 1; // ilog is off-by-one from normal definitions for (l=0; l < ld-3; ++l) { int k0 = n >> (l+2), k1 = 1 << (l+3); int rlim = n >> (l+4), r4, r; int s2lim = 1 << (l+2), s2; for (r=r4=0; r < rlim; r4+=4,++r) { for (s2=0; s2 < s2lim; s2+=2) { u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; } } if (l+1 < ld-3) { // paper bug: ping-ponging of u&w here is omitted memcpy(w, u, sizeof(u)); } } // step 4 for (i=0; i < n8; ++i) { int j = bit_reverse(i) >> (32-ld+3); assert(j < n8); if (i == j) { // paper bug: original code probably swapped in place; if copying, // need to directly copy in this case int i8 = i << 3; v[i8+1] = u[i8+1]; v[i8+3] = u[i8+3]; v[i8+5] = u[i8+5]; v[i8+7] = u[i8+7]; } else if (i < j) { int i8 = i << 3, j8 = j << 3; v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; } } // step 5 for (k=0; k < n2; ++k) { w[k] = v[k*2+1]; } // step 6 for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { u[n-1-k2] = w[k4]; u[n-2-k2] = w[k4+1]; u[n3_4 - 1 - k2] = w[k4+2]; u[n3_4 - 2 - k2] = w[k4+3]; } // step 7 for (k=k2=0; k < n8; ++k, k2 += 2) { v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; } // step 8 for (k=k2=0; k < n4; ++k,k2 += 2) { X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; } // decode kernel to output // determined the following value experimentally // (by first figuring out what made inverse_mdct_slow work); then matching that here // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) s = 0.5; // theoretically would be n4 // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, // so it needs to use the "old" B values to behave correctly, or else // set s to 1.0 ]]] for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; } #endif static float *get_window(vorb *f, int len) { len <<= 1; if (len == f->blocksize_0) return f->window[0]; if (len == f->blocksize_1) return f->window[1]; return NULL; } #ifndef STB_VORBIS_NO_DEFER_FLOOR typedef int16 YTYPE; #else typedef int YTYPE; #endif static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) { int n2 = n >> 1; int s = map->chan[i].mux, floor; floor = map->submap_floor[s]; if (f->floor_types[floor] == 0) { return error(f, VORBIS_invalid_stream); } else { Floor1 *g = &f->floor_config[floor].floor1; int j,q; int lx = 0, ly = finalY[0] * g->floor1_multiplier; for (q=1; q < g->values; ++q) { j = g->sorted_order[q]; #ifndef STB_VORBIS_NO_DEFER_FLOOR STBV_NOTUSED(step2_flag); if (finalY[j] >= 0) #else if (step2_flag[j]) #endif { int hy = finalY[j] * g->floor1_multiplier; int hx = g->Xlist[j]; if (lx != hx) draw_line(target, lx,ly, hx,hy, n2); CHECK(f); lx = hx, ly = hy; } } if (lx < n2) { // optimization of: draw_line(target, lx,ly, n,ly, n2); for (j=lx; j < n2; ++j) LINE_OP(target[j], inverse_db_table[ly]); CHECK(f); } } return TRUE; } // The meaning of "left" and "right" // // For a given frame: // we compute samples from 0..n // window_center is n/2 // we'll window and mix the samples from left_start to left_end with data from the previous frame // all of the samples from left_end to right_start can be output without mixing; however, // this interval is 0-length except when transitioning between short and long frames // all of the samples from right_start to right_end need to be mixed with the next frame, // which we don't have, so those get saved in a buffer // frame N's right_end-right_start, the number of samples to mix with the next frame, // has to be the same as frame N+1's left_end-left_start (which they are by // construction) static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) { Mode *m; int i, n, prev, next, window_center; f->channel_buffer_start = f->channel_buffer_end = 0; retry: if (f->eof) return FALSE; if (!maybe_start_packet(f)) return FALSE; // check packet type if (get_bits(f,1) != 0) { if (IS_PUSH_MODE(f)) return error(f,VORBIS_bad_packet_type); while (EOP != get8_packet(f)); goto retry; } if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); i = get_bits(f, ilog(f->mode_count-1)); if (i == EOP) return FALSE; if (i >= f->mode_count) return FALSE; *mode = i; m = f->mode_config + i; if (m->blockflag) { n = f->blocksize_1; prev = get_bits(f,1); next = get_bits(f,1); } else { prev = next = 0; n = f->blocksize_0; } // WINDOWING window_center = n >> 1; if (m->blockflag && !prev) { *p_left_start = (n - f->blocksize_0) >> 2; *p_left_end = (n + f->blocksize_0) >> 2; } else { *p_left_start = 0; *p_left_end = window_center; } if (m->blockflag && !next) { *p_right_start = (n*3 - f->blocksize_0) >> 2; *p_right_end = (n*3 + f->blocksize_0) >> 2; } else { *p_right_start = window_center; *p_right_end = n; } return TRUE; } static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) { Mapping *map; int i,j,k,n,n2; int zero_channel[256]; int really_zero_channel[256]; // WINDOWING STBV_NOTUSED(left_end); n = f->blocksize[m->blockflag]; map = &f->mapping[m->mapping]; // FLOORS n2 = n >> 1; CHECK(f); for (i=0; i < f->channels; ++i) { int s = map->chan[i].mux, floor; zero_channel[i] = FALSE; floor = map->submap_floor[s]; if (f->floor_types[floor] == 0) { return error(f, VORBIS_invalid_stream); } else { Floor1 *g = &f->floor_config[floor].floor1; if (get_bits(f, 1)) { short *finalY; uint8 step2_flag[256]; static int range_list[4] = { 256, 128, 86, 64 }; int range = range_list[g->floor1_multiplier-1]; int offset = 2; finalY = f->finalY[i]; finalY[0] = get_bits(f, ilog(range)-1); finalY[1] = get_bits(f, ilog(range)-1); for (j=0; j < g->partitions; ++j) { int pclass = g->partition_class_list[j]; int cdim = g->class_dimensions[pclass]; int cbits = g->class_subclasses[pclass]; int csub = (1 << cbits)-1; int cval = 0; if (cbits) { Codebook *c = f->codebooks + g->class_masterbooks[pclass]; DECODE(cval,f,c); } for (k=0; k < cdim; ++k) { int book = g->subclass_books[pclass][cval & csub]; cval = cval >> cbits; if (book >= 0) { int temp; Codebook *c = f->codebooks + book; DECODE(temp,f,c); finalY[offset++] = temp; } else finalY[offset++] = 0; } } if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec step2_flag[0] = step2_flag[1] = 1; for (j=2; j < g->values; ++j) { int low, high, pred, highroom, lowroom, room, val; low = g->neighbors[j][0]; high = g->neighbors[j][1]; //neighbors(g->Xlist, j, &low, &high); pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); val = finalY[j]; highroom = range - pred; lowroom = pred; if (highroom < lowroom) room = highroom * 2; else room = lowroom * 2; if (val) { step2_flag[low] = step2_flag[high] = 1; step2_flag[j] = 1; if (val >= room) if (highroom > lowroom) finalY[j] = val - lowroom + pred; else finalY[j] = pred - val + highroom - 1; else if (val & 1) finalY[j] = pred - ((val+1)>>1); else finalY[j] = pred + (val>>1); } else { step2_flag[j] = 0; finalY[j] = pred; } } #ifdef STB_VORBIS_NO_DEFER_FLOOR do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); #else // defer final floor computation until _after_ residue for (j=0; j < g->values; ++j) { if (!step2_flag[j]) finalY[j] = -1; } #endif } else { error: zero_channel[i] = TRUE; } // So we just defer everything else to later // at this point we've decoded the floor into buffer } } CHECK(f); // at this point we've decoded all floors if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); // re-enable coupled channels if necessary memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); for (i=0; i < map->coupling_steps; ++i) if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; } CHECK(f); // RESIDUE DECODE for (i=0; i < map->submaps; ++i) { float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; int r; uint8 do_not_decode[256]; int ch = 0; for (j=0; j < f->channels; ++j) { if (map->chan[j].mux == i) { if (zero_channel[j]) { do_not_decode[ch] = TRUE; residue_buffers[ch] = NULL; } else { do_not_decode[ch] = FALSE; residue_buffers[ch] = f->channel_buffers[j]; } ++ch; } } r = map->submap_residue[i]; decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); } if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); CHECK(f); // INVERSE COUPLING for (i = map->coupling_steps-1; i >= 0; --i) { int n2 = n >> 1; float *m = f->channel_buffers[map->chan[i].magnitude]; float *a = f->channel_buffers[map->chan[i].angle ]; for (j=0; j < n2; ++j) { float a2,m2; if (m[j] > 0) if (a[j] > 0) m2 = m[j], a2 = m[j] - a[j]; else a2 = m[j], m2 = m[j] + a[j]; else if (a[j] > 0) m2 = m[j], a2 = m[j] + a[j]; else a2 = m[j], m2 = m[j] - a[j]; m[j] = m2; a[j] = a2; } } CHECK(f); // finish decoding the floors #ifndef STB_VORBIS_NO_DEFER_FLOOR for (i=0; i < f->channels; ++i) { if (really_zero_channel[i]) { memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); } else { do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); } } #else for (i=0; i < f->channels; ++i) { if (really_zero_channel[i]) { memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); } else { for (j=0; j < n2; ++j) f->channel_buffers[i][j] *= f->floor_buffers[i][j]; } } #endif // INVERSE MDCT CHECK(f); for (i=0; i < f->channels; ++i) inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); CHECK(f); // this shouldn't be necessary, unless we exited on an error // and want to flush to get to the next packet flush_packet(f); if (f->first_decode) { // assume we start so first non-discarded sample is sample 0 // this isn't to spec, but spec would require us to read ahead // and decode the size of all current frames--could be done, // but presumably it's not a commonly used feature f->current_loc = 0u - n2; // start of first frame is positioned for discard (NB this is an intentional unsigned overflow/wrap-around) // we might have to discard samples "from" the next frame too, // if we're lapping a large block then a small at the start? f->discard_samples_deferred = n - right_end; f->current_loc_valid = TRUE; f->first_decode = FALSE; } else if (f->discard_samples_deferred) { if (f->discard_samples_deferred >= right_start - left_start) { f->discard_samples_deferred -= (right_start - left_start); left_start = right_start; *p_left = left_start; } else { left_start += f->discard_samples_deferred; *p_left = left_start; f->discard_samples_deferred = 0; } } else if (f->previous_length == 0 && f->current_loc_valid) { // we're recovering from a seek... that means we're going to discard // the samples from this packet even though we know our position from // the last page header, so we need to update the position based on // the discarded samples here // but wait, the code below is going to add this in itself even // on a discard, so we don't need to do it here... } // check if we have ogg information about the sample # for this packet if (f->last_seg_which == f->end_seg_with_known_loc) { // if we have a valid current loc, and this is final: if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { uint32 current_end = f->known_loc_for_packet; // then let's infer the size of the (probably) short final frame if (current_end < f->current_loc + (right_end-left_start)) { if (current_end < f->current_loc) { // negative truncation, that's impossible! *len = 0; } else { *len = current_end - f->current_loc; } *len += left_start; // this doesn't seem right, but has no ill effect on my test files if (*len > right_end) *len = right_end; // this should never happen f->current_loc += *len; return TRUE; } } // otherwise, just set our sample loc // guess that the ogg granule pos refers to the _middle_ of the // last frame? // set f->current_loc to the position of left_start f->current_loc = f->known_loc_for_packet - (n2-left_start); f->current_loc_valid = TRUE; } if (f->current_loc_valid) f->current_loc += (right_start - left_start); if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); *len = right_end; // ignore samples after the window goes to 0 CHECK(f); return TRUE; } static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) { int mode, left_end, right_end; if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); } static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) { int prev,i,j; // we use right&left (the start of the right- and left-window sin()-regions) // to determine how much to return, rather than inferring from the rules // (same result, clearer code); 'left' indicates where our sin() window // starts, therefore where the previous window's right edge starts, and // therefore where to start mixing from the previous buffer. 'right' // indicates where our sin() ending-window starts, therefore that's where // we start saving, and where our returned-data ends. // mixin from previous window if (f->previous_length) { int i,j, n = f->previous_length; float *w = get_window(f, n); if (w == NULL) return 0; for (i=0; i < f->channels; ++i) { for (j=0; j < n; ++j) f->channel_buffers[i][left+j] = f->channel_buffers[i][left+j]*w[ j] + f->previous_window[i][ j]*w[n-1-j]; } } prev = f->previous_length; // last half of this data becomes previous window f->previous_length = len - right; // @OPTIMIZE: could avoid this copy by double-buffering the // output (flipping previous_window with channel_buffers), but // then previous_window would have to be 2x as large, and // channel_buffers couldn't be temp mem (although they're NOT // currently temp mem, they could be (unless we want to level // performance by spreading out the computation)) for (i=0; i < f->channels; ++i) for (j=0; right+j < len; ++j) f->previous_window[i][j] = f->channel_buffers[i][right+j]; if (!prev) // there was no previous packet, so this data isn't valid... // this isn't entirely true, only the would-have-overlapped data // isn't valid, but this seems to be what the spec requires return 0; // truncate a short frame if (len < right) right = len; f->samples_output += right-left; return right - left; } static int vorbis_pump_first_frame(stb_vorbis *f) { int len, right, left, res; res = vorbis_decode_packet(f, &len, &left, &right); if (res) vorbis_finish_frame(f, len, left, right); return res; } #ifndef STB_VORBIS_NO_PUSHDATA_API static int is_whole_packet_present(stb_vorbis *f) { // make sure that we have the packet available before continuing... // this requires a full ogg parse, but we know we can fetch from f->stream // instead of coding this out explicitly, we could save the current read state, // read the next packet with get8() until end-of-packet, check f->eof, then // reset the state? but that would be slower, esp. since we'd have over 256 bytes // of state to restore (primarily the page segment table) int s = f->next_seg, first = TRUE; uint8 *p = f->stream; if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag for (; s < f->segment_count; ++s) { p += f->segments[s]; if (f->segments[s] < 255) // stop at first short segment break; } // either this continues, or it ends it... if (s == f->segment_count) s = -1; // set 'crosses page' flag if (p > f->stream_end) return error(f, VORBIS_need_more_data); first = FALSE; } for (; s == -1;) { uint8 *q; int n; // check that we have the page header ready if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); // validate the page if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); if (p[4] != 0) return error(f, VORBIS_invalid_stream); if (first) { // the first segment must NOT have 'continued_packet', later ones MUST if (f->previous_length) if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); // if no previous length, we're resynching, so we can come in on a continued-packet, // which we'll just drop } else { if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); } n = p[26]; // segment counts q = p+27; // q points to segment table p = q + n; // advance past header // make sure we've read the segment table if (p > f->stream_end) return error(f, VORBIS_need_more_data); for (s=0; s < n; ++s) { p += q[s]; if (q[s] < 255) break; } if (s == n) s = -1; // set 'crosses page' flag if (p > f->stream_end) return error(f, VORBIS_need_more_data); first = FALSE; } return TRUE; } #endif // !STB_VORBIS_NO_PUSHDATA_API static int start_decoder(vorb *f) { uint8 header[6], x,y; int len,i,j,k, max_submaps = 0; int longest_floorlist=0; // first page, first packet f->first_decode = TRUE; if (!start_page(f)) return FALSE; // validate page flag if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); // check for expected packet length if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); if (f->segments[0] != 30) { // check for the Ogg skeleton fishead identifying header to refine our error if (f->segments[0] == 64 && getn(f, header, 6) && header[0] == 'f' && header[1] == 'i' && header[2] == 's' && header[3] == 'h' && header[4] == 'e' && header[5] == 'a' && get8(f) == 'd' && get8(f) == '\0') return error(f, VORBIS_ogg_skeleton_not_supported); else return error(f, VORBIS_invalid_first_page); } // read packet // check packet header if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); // vorbis_version if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); get32(f); // bitrate_maximum get32(f); // bitrate_nominal get32(f); // bitrate_minimum x = get8(f); { int log0,log1; log0 = x & 15; log1 = x >> 4; f->blocksize_0 = 1 << log0; f->blocksize_1 = 1 << log1; if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); if (log0 > log1) return error(f, VORBIS_invalid_setup); } // framing_flag x = get8(f); if (!(x & 1)) return error(f, VORBIS_invalid_first_page); // second packet! if (!start_page(f)) return FALSE; if (!start_packet(f)) return FALSE; if (!next_segment(f)) return FALSE; if (get8_packet(f) != VORBIS_packet_comment) return error(f, VORBIS_invalid_setup); for (i=0; i < 6; ++i) header[i] = get8_packet(f); if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); //file vendor len = get32_packet(f); f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1)); if (f->vendor == NULL) return error(f, VORBIS_outofmem); for(i=0; i < len; ++i) { f->vendor[i] = get8_packet(f); } f->vendor[len] = (char)'\0'; //user comments f->comment_list_length = get32_packet(f); f->comment_list = NULL; if (f->comment_list_length > 0) { f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length)); if (f->comment_list == NULL) return error(f, VORBIS_outofmem); } for(i=0; i < f->comment_list_length; ++i) { len = get32_packet(f); f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem); for(j=0; j < len; ++j) { f->comment_list[i][j] = get8_packet(f); } f->comment_list[i][len] = (char)'\0'; } // framing_flag x = get8_packet(f); if (!(x & 1)) return error(f, VORBIS_invalid_setup); skip(f, f->bytes_in_seg); f->bytes_in_seg = 0; do { len = next_segment(f); skip(f, len); f->bytes_in_seg = 0; } while (len); // third packet! if (!start_packet(f)) return FALSE; #ifndef STB_VORBIS_NO_PUSHDATA_API if (IS_PUSH_MODE(f)) { if (!is_whole_packet_present(f)) { // convert error in ogg header to write type if (f->error == VORBIS_invalid_stream) f->error = VORBIS_invalid_setup; return FALSE; } } #endif crc32_init(); // always init it, to avoid multithread race conditions if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); for (i=0; i < 6; ++i) header[i] = get8_packet(f); if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); // codebooks f->codebook_count = get_bits(f,8) + 1; f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); if (f->codebooks == NULL) return error(f, VORBIS_outofmem); memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); for (i=0; i < f->codebook_count; ++i) { uint32 *values; int ordered, sorted_count; int total=0; uint8 *lengths; Codebook *c = f->codebooks+i; CHECK(f); x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); x = get_bits(f, 8); c->dimensions = (get_bits(f, 8)<<8) + x; x = get_bits(f, 8); y = get_bits(f, 8); c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; ordered = get_bits(f,1); c->sparse = ordered ? 0 : get_bits(f,1); if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); if (c->sparse) lengths = (uint8 *) setup_temp_malloc(f, c->entries); else lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); if (!lengths) return error(f, VORBIS_outofmem); if (ordered) { int current_entry = 0; int current_length = get_bits(f,5) + 1; while (current_entry < c->entries) { int limit = c->entries - current_entry; int n = get_bits(f, ilog(limit)); if (current_length >= 32) return error(f, VORBIS_invalid_setup); if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } memset(lengths + current_entry, current_length, n); current_entry += n; ++current_length; } } else { for (j=0; j < c->entries; ++j) { int present = c->sparse ? get_bits(f,1) : 1; if (present) { lengths[j] = get_bits(f, 5) + 1; ++total; if (lengths[j] == 32) return error(f, VORBIS_invalid_setup); } else { lengths[j] = NO_CODE; } } } if (c->sparse && total >= c->entries >> 2) { // convert sparse items to non-sparse! if (c->entries > (int) f->setup_temp_memory_required) f->setup_temp_memory_required = c->entries; c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); memcpy(c->codeword_lengths, lengths, c->entries); setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! lengths = c->codeword_lengths; c->sparse = 0; } // compute the size of the sorted tables if (c->sparse) { sorted_count = total; } else { sorted_count = 0; #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH for (j=0; j < c->entries; ++j) if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) ++sorted_count; #endif } c->sorted_entries = sorted_count; values = NULL; CHECK(f); if (!c->sparse) { c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); if (!c->codewords) return error(f, VORBIS_outofmem); } else { unsigned int size; if (c->sorted_entries) { c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); if (!c->codeword_lengths) return error(f, VORBIS_outofmem); c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); if (!c->codewords) return error(f, VORBIS_outofmem); values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); if (!values) return error(f, VORBIS_outofmem); } size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; if (size > f->setup_temp_memory_required) f->setup_temp_memory_required = size; } if (!compute_codewords(c, lengths, c->entries, values)) { if (c->sparse) setup_temp_free(f, values, 0); return error(f, VORBIS_invalid_setup); } if (c->sorted_entries) { // allocate an extra slot for sentinels c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); // allocate an extra slot at the front so that c->sorted_values[-1] is defined // so that we can catch that case without an extra if c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); ++c->sorted_values; c->sorted_values[-1] = -1; compute_sorted_huffman(c, lengths, values); } if (c->sparse) { setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); setup_temp_free(f, lengths, c->entries); c->codewords = NULL; } compute_accelerated_huffman(c); CHECK(f); c->lookup_type = get_bits(f, 4); if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); if (c->lookup_type > 0) { uint16 *mults; c->minimum_value = float32_unpack(get_bits(f, 32)); c->delta_value = float32_unpack(get_bits(f, 32)); c->value_bits = get_bits(f, 4)+1; c->sequence_p = get_bits(f,1); if (c->lookup_type == 1) { int values = lookup1_values(c->entries, c->dimensions); if (values < 0) return error(f, VORBIS_invalid_setup); c->lookup_values = (uint32) values; } else { c->lookup_values = c->entries * c->dimensions; } if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); if (mults == NULL) return error(f, VORBIS_outofmem); for (j=0; j < (int) c->lookup_values; ++j) { int q = get_bits(f, c->value_bits); if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } mults[j] = q; } #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK if (c->lookup_type == 1) { int len, sparse = c->sparse; float last=0; // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop if (sparse) { if (c->sorted_entries == 0) goto skip; c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); } else c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } len = sparse ? c->sorted_entries : c->entries; for (j=0; j < len; ++j) { unsigned int z = sparse ? c->sorted_values[j] : j; unsigned int div=1; for (k=0; k < c->dimensions; ++k) { int off = (z / div) % c->lookup_values; float val = mults[off]*c->delta_value + c->minimum_value + last; c->multiplicands[j*c->dimensions + k] = val; if (c->sequence_p) last = val; if (k+1 < c->dimensions) { if (div > UINT_MAX / (unsigned int) c->lookup_values) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } div *= c->lookup_values; } } } c->lookup_type = 2; } else #endif { float last=0; CHECK(f); c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } for (j=0; j < (int) c->lookup_values; ++j) { float val = mults[j] * c->delta_value + c->minimum_value + last; c->multiplicands[j] = val; if (c->sequence_p) last = val; } } #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK skip:; #endif setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); CHECK(f); } CHECK(f); } // time domain transfers (notused) x = get_bits(f, 6) + 1; for (i=0; i < x; ++i) { uint32 z = get_bits(f, 16); if (z != 0) return error(f, VORBIS_invalid_setup); } // Floors f->floor_count = get_bits(f, 6)+1; f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); if (f->floor_config == NULL) return error(f, VORBIS_outofmem); for (i=0; i < f->floor_count; ++i) { f->floor_types[i] = get_bits(f, 16); if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); if (f->floor_types[i] == 0) { Floor0 *g = &f->floor_config[i].floor0; g->order = get_bits(f,8); g->rate = get_bits(f,16); g->bark_map_size = get_bits(f,16); g->amplitude_bits = get_bits(f,6); g->amplitude_offset = get_bits(f,8); g->number_of_books = get_bits(f,4) + 1; for (j=0; j < g->number_of_books; ++j) g->book_list[j] = get_bits(f,8); return error(f, VORBIS_feature_not_supported); } else { stbv__floor_ordering p[31*8+2]; Floor1 *g = &f->floor_config[i].floor1; int max_class = -1; g->partitions = get_bits(f, 5); for (j=0; j < g->partitions; ++j) { g->partition_class_list[j] = get_bits(f, 4); if (g->partition_class_list[j] > max_class) max_class = g->partition_class_list[j]; } for (j=0; j <= max_class; ++j) { g->class_dimensions[j] = get_bits(f, 3)+1; g->class_subclasses[j] = get_bits(f, 2); if (g->class_subclasses[j]) { g->class_masterbooks[j] = get_bits(f, 8); if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } for (k=0; k < 1 << g->class_subclasses[j]; ++k) { g->subclass_books[j][k] = (int16)get_bits(f,8)-1; if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } } g->floor1_multiplier = get_bits(f,2)+1; g->rangebits = get_bits(f,4); g->Xlist[0] = 0; g->Xlist[1] = 1 << g->rangebits; g->values = 2; for (j=0; j < g->partitions; ++j) { int c = g->partition_class_list[j]; for (k=0; k < g->class_dimensions[c]; ++k) { g->Xlist[g->values] = get_bits(f, g->rangebits); ++g->values; } } // precompute the sorting for (j=0; j < g->values; ++j) { p[j].x = g->Xlist[j]; p[j].id = j; } qsort(p, g->values, sizeof(p[0]), point_compare); for (j=0; j < g->values-1; ++j) if (p[j].x == p[j+1].x) return error(f, VORBIS_invalid_setup); for (j=0; j < g->values; ++j) g->sorted_order[j] = (uint8) p[j].id; // precompute the neighbors for (j=2; j < g->values; ++j) { int low = 0,hi = 0; neighbors(g->Xlist, j, &low,&hi); g->neighbors[j][0] = low; g->neighbors[j][1] = hi; } if (g->values > longest_floorlist) longest_floorlist = g->values; } } // Residue f->residue_count = get_bits(f, 6)+1; f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); if (f->residue_config == NULL) return error(f, VORBIS_outofmem); memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); for (i=0; i < f->residue_count; ++i) { uint8 residue_cascade[64]; Residue *r = f->residue_config+i; f->residue_types[i] = get_bits(f, 16); if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); r->begin = get_bits(f, 24); r->end = get_bits(f, 24); if (r->end < r->begin) return error(f, VORBIS_invalid_setup); r->part_size = get_bits(f,24)+1; r->classifications = get_bits(f,6)+1; r->classbook = get_bits(f,8); if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); for (j=0; j < r->classifications; ++j) { uint8 high_bits=0; uint8 low_bits=get_bits(f,3); if (get_bits(f,1)) high_bits = get_bits(f,5); residue_cascade[j] = high_bits*8 + low_bits; } r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); if (r->residue_books == NULL) return error(f, VORBIS_outofmem); for (j=0; j < r->classifications; ++j) { for (k=0; k < 8; ++k) { if (residue_cascade[j] & (1 << k)) { r->residue_books[j][k] = get_bits(f, 8); if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } else { r->residue_books[j][k] = -1; } } } // precompute the classifications[] array to avoid inner-loop mod/divide // call it 'classdata' since we already have r->classifications r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); if (!r->classdata) return error(f, VORBIS_outofmem); memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); for (j=0; j < f->codebooks[r->classbook].entries; ++j) { int classwords = f->codebooks[r->classbook].dimensions; int temp = j; r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); for (k=classwords-1; k >= 0; --k) { r->classdata[j][k] = temp % r->classifications; temp /= r->classifications; } } } f->mapping_count = get_bits(f,6)+1; f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); if (f->mapping == NULL) return error(f, VORBIS_outofmem); memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); for (i=0; i < f->mapping_count; ++i) { Mapping *m = f->mapping + i; int mapping_type = get_bits(f,16); if (mapping_type != 0) return error(f, VORBIS_invalid_setup); m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); if (m->chan == NULL) return error(f, VORBIS_outofmem); if (get_bits(f,1)) m->submaps = get_bits(f,4)+1; else m->submaps = 1; if (m->submaps > max_submaps) max_submaps = m->submaps; if (get_bits(f,1)) { m->coupling_steps = get_bits(f,8)+1; if (m->coupling_steps > f->channels) return error(f, VORBIS_invalid_setup); for (k=0; k < m->coupling_steps; ++k) { m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); m->chan[k].angle = get_bits(f, ilog(f->channels-1)); if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); } } else m->coupling_steps = 0; // reserved field if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); if (m->submaps > 1) { for (j=0; j < f->channels; ++j) { m->chan[j].mux = get_bits(f, 4); if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); } } else // @SPECIFICATION: this case is missing from the spec for (j=0; j < f->channels; ++j) m->chan[j].mux = 0; for (j=0; j < m->submaps; ++j) { get_bits(f,8); // discard m->submap_floor[j] = get_bits(f,8); m->submap_residue[j] = get_bits(f,8); if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); } } // Modes f->mode_count = get_bits(f, 6)+1; for (i=0; i < f->mode_count; ++i) { Mode *m = f->mode_config+i; m->blockflag = get_bits(f,1); m->windowtype = get_bits(f,16); m->transformtype = get_bits(f,16); m->mapping = get_bits(f,8); if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); } flush_packet(f); f->previous_length = 0; for (i=0; i < f->channels; ++i) { f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); #ifdef STB_VORBIS_NO_DEFER_FLOOR f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); #endif } if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; f->blocksize[0] = f->blocksize_0; f->blocksize[1] = f->blocksize_1; #ifdef STB_VORBIS_DIVIDE_TABLE if (integer_divide_table[1][1]==0) for (i=0; i < DIVTAB_NUMER; ++i) for (j=1; j < DIVTAB_DENOM; ++j) integer_divide_table[i][j] = i / j; #endif // compute how much temporary memory is needed // 1. { uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); uint32 classify_mem; int i,max_part_read=0; for (i=0; i < f->residue_count; ++i) { Residue *r = f->residue_config + i; unsigned int actual_size = f->blocksize_1 / 2; unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; int n_read = limit_r_end - limit_r_begin; int part_read = n_read / r->part_size; if (part_read > max_part_read) max_part_read = part_read; } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); #else classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); #endif // maximum reasonable partition size is f->blocksize_1 f->temp_memory_required = classify_mem; if (imdct_mem > f->temp_memory_required) f->temp_memory_required = imdct_mem; } if (f->alloc.alloc_buffer) { assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); // check if there's enough temp memory so we don't error later if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) return error(f, VORBIS_outofmem); } // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page // without PAGEFLAG_continued_packet, so this either points to the first page, or // the page after the end of the headers. It might be cleaner to point to a page // in the middle of the headers, when that's the page where the first audio packet // starts, but we'd have to also correctly skip the end of any continued packet in // stb_vorbis_seek_start. if (f->next_seg == -1) { f->first_audio_page_offset = stb_vorbis_get_file_offset(f); } else { f->first_audio_page_offset = 0; } return TRUE; } static void vorbis_deinit(stb_vorbis *p) { int i,j; setup_free(p, p->vendor); for (i=0; i < p->comment_list_length; ++i) { setup_free(p, p->comment_list[i]); } setup_free(p, p->comment_list); if (p->residue_config) { for (i=0; i < p->residue_count; ++i) { Residue *r = p->residue_config+i; if (r->classdata) { for (j=0; j < p->codebooks[r->classbook].entries; ++j) setup_free(p, r->classdata[j]); setup_free(p, r->classdata); } setup_free(p, r->residue_books); } } if (p->codebooks) { CHECK(p); for (i=0; i < p->codebook_count; ++i) { Codebook *c = p->codebooks + i; setup_free(p, c->codeword_lengths); setup_free(p, c->multiplicands); setup_free(p, c->codewords); setup_free(p, c->sorted_codewords); // c->sorted_values[-1] is the first entry in the array setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); } setup_free(p, p->codebooks); } setup_free(p, p->floor_config); setup_free(p, p->residue_config); if (p->mapping) { for (i=0; i < p->mapping_count; ++i) setup_free(p, p->mapping[i].chan); setup_free(p, p->mapping); } CHECK(p); for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { setup_free(p, p->channel_buffers[i]); setup_free(p, p->previous_window[i]); #ifdef STB_VORBIS_NO_DEFER_FLOOR setup_free(p, p->floor_buffers[i]); #endif setup_free(p, p->finalY[i]); } for (i=0; i < 2; ++i) { setup_free(p, p->A[i]); setup_free(p, p->B[i]); setup_free(p, p->C[i]); setup_free(p, p->window[i]); setup_free(p, p->bit_reverse[i]); } #ifndef STB_VORBIS_NO_STDIO if (p->close_on_free) fclose(p->f); #endif } void stb_vorbis_close(stb_vorbis *p) { if (p == NULL) return; vorbis_deinit(p); setup_free(p,p); } static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) { memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start if (z) { p->alloc = *z; p->alloc.alloc_buffer_length_in_bytes &= ~7; p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; } p->eof = 0; p->error = VORBIS__no_error; p->stream = NULL; p->codebooks = NULL; p->page_crc_tests = -1; #ifndef STB_VORBIS_NO_STDIO p->close_on_free = FALSE; p->f = NULL; #endif } int stb_vorbis_get_sample_offset(stb_vorbis *f) { if (f->current_loc_valid) return f->current_loc; else return -1; } stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) { stb_vorbis_info d; d.channels = f->channels; d.sample_rate = f->sample_rate; d.setup_memory_required = f->setup_memory_required; d.setup_temp_memory_required = f->setup_temp_memory_required; d.temp_memory_required = f->temp_memory_required; d.max_frame_size = f->blocksize_1 >> 1; return d; } stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) { stb_vorbis_comment d; d.vendor = f->vendor; d.comment_list_length = f->comment_list_length; d.comment_list = f->comment_list; return d; } int stb_vorbis_get_error(stb_vorbis *f) { int e = f->error; f->error = VORBIS__no_error; return e; } static stb_vorbis * vorbis_alloc(stb_vorbis *f) { stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); return p; } #ifndef STB_VORBIS_NO_PUSHDATA_API void stb_vorbis_flush_pushdata(stb_vorbis *f) { f->previous_length = 0; f->page_crc_tests = 0; f->discard_samples_deferred = 0; f->current_loc_valid = FALSE; f->first_decode = FALSE; f->samples_output = 0; f->channel_buffer_start = 0; f->channel_buffer_end = 0; } static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) { int i,n; for (i=0; i < f->page_crc_tests; ++i) f->scan[i].bytes_done = 0; // if we have room for more scans, search for them first, because // they may cause us to stop early if their header is incomplete if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { if (data_len < 4) return 0; data_len -= 3; // need to look for 4-byte sequence, so don't miss // one that straddles a boundary for (i=0; i < data_len; ++i) { if (data[i] == 0x4f) { if (0==memcmp(data+i, ogg_page_header, 4)) { int j,len; uint32 crc; // make sure we have the whole page header if (i+26 >= data_len || i+27+data[i+26] >= data_len) { // only read up to this page start, so hopefully we'll // have the whole page header start next time data_len = i; break; } // ok, we have it all; compute the length of the page len = 27 + data[i+26]; for (j=0; j < data[i+26]; ++j) len += data[i+27+j]; // scan everything up to the embedded crc (which we must 0) crc = 0; for (j=0; j < 22; ++j) crc = crc32_update(crc, data[i+j]); // now process 4 0-bytes for ( ; j < 26; ++j) crc = crc32_update(crc, 0); // len is the total number of bytes we need to scan n = f->page_crc_tests++; f->scan[n].bytes_left = len-j; f->scan[n].crc_so_far = crc; f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); // if the last frame on a page is continued to the next, then // we can't recover the sample_loc immediately if (data[i+27+data[i+26]-1] == 255) f->scan[n].sample_loc = ~0; else f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); f->scan[n].bytes_done = i+j; if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) break; // keep going if we still have room for more } } } } for (i=0; i < f->page_crc_tests;) { uint32 crc; int j; int n = f->scan[i].bytes_done; int m = f->scan[i].bytes_left; if (m > data_len - n) m = data_len - n; // m is the bytes to scan in the current chunk crc = f->scan[i].crc_so_far; for (j=0; j < m; ++j) crc = crc32_update(crc, data[n+j]); f->scan[i].bytes_left -= m; f->scan[i].crc_so_far = crc; if (f->scan[i].bytes_left == 0) { // does it match? if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { // Houston, we have page data_len = n+m; // consumption amount is wherever that scan ended f->page_crc_tests = -1; // drop out of page scan mode f->previous_length = 0; // decode-but-don't-output one frame f->next_seg = -1; // start a new page f->current_loc = f->scan[i].sample_loc; // set the current sample location // to the amount we'd have decoded had we decoded this page f->current_loc_valid = f->current_loc != ~0U; return data_len; } // delete entry f->scan[i] = f->scan[--f->page_crc_tests]; } else { ++i; } } return data_len; } // return value: number of bytes we used int stb_vorbis_decode_frame_pushdata( stb_vorbis *f, // the file we're decoding const uint8 *data, int data_len, // the memory available for decoding int *channels, // place to write number of float * buffers float ***output, // place to write float ** array of float * buffers int *samples // place to write number of output samples ) { int i; int len,right,left; if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); if (f->page_crc_tests >= 0) { *samples = 0; return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); } f->stream = (uint8 *) data; f->stream_end = (uint8 *) data + data_len; f->error = VORBIS__no_error; // check that we have the entire packet in memory if (!is_whole_packet_present(f)) { *samples = 0; return 0; } if (!vorbis_decode_packet(f, &len, &left, &right)) { // save the actual error we encountered enum STBVorbisError error = f->error; if (error == VORBIS_bad_packet_type) { // flush and resynch f->error = VORBIS__no_error; while (get8_packet(f) != EOP) if (f->eof) break; *samples = 0; return (int) (f->stream - data); } if (error == VORBIS_continued_packet_flag_invalid) { if (f->previous_length == 0) { // we may be resynching, in which case it's ok to hit one // of these; just discard the packet f->error = VORBIS__no_error; while (get8_packet(f) != EOP) if (f->eof) break; *samples = 0; return (int) (f->stream - data); } } // if we get an error while parsing, what to do? // well, it DEFINITELY won't work to continue from where we are! stb_vorbis_flush_pushdata(f); // restore the error that actually made us bail f->error = error; *samples = 0; return 1; } // success! len = vorbis_finish_frame(f, len, left, right); for (i=0; i < f->channels; ++i) f->outputs[i] = f->channel_buffers[i] + left; if (channels) *channels = f->channels; *samples = len; *output = f->outputs; return (int) (f->stream - data); } stb_vorbis *stb_vorbis_open_pushdata( const unsigned char *data, int data_len, // the memory available for decoding int *data_used, // only defined if result is not NULL int *error, const stb_vorbis_alloc *alloc) { stb_vorbis *f, p; vorbis_init(&p, alloc); p.stream = (uint8 *) data; p.stream_end = (uint8 *) data + data_len; p.push_mode = TRUE; if (!start_decoder(&p)) { if (p.eof) *error = VORBIS_need_more_data; else *error = p.error; vorbis_deinit(&p); return NULL; } f = vorbis_alloc(&p); if (f) { *f = p; *data_used = (int) (f->stream - data); *error = 0; return f; } else { vorbis_deinit(&p); return NULL; } } #endif // STB_VORBIS_NO_PUSHDATA_API unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) { #ifndef STB_VORBIS_NO_PUSHDATA_API if (f->push_mode) return 0; #endif if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); #ifndef STB_VORBIS_NO_STDIO return (unsigned int) (ftell(f->f) - f->f_start); #endif } #ifndef STB_VORBIS_NO_PULLDATA_API // // DATA-PULLING API // static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) { for(;;) { int n; if (f->eof) return 0; n = get8(f); if (n == 0x4f) { // page header candidate unsigned int retry_loc = stb_vorbis_get_file_offset(f); int i; // check if we're off the end of a file_section stream if (retry_loc - 25 > f->stream_len) return 0; // check the rest of the header for (i=1; i < 4; ++i) if (get8(f) != ogg_page_header[i]) break; if (f->eof) return 0; if (i == 4) { uint8 header[27]; uint32 i, crc, goal, len; for (i=0; i < 4; ++i) header[i] = ogg_page_header[i]; for (; i < 27; ++i) header[i] = get8(f); if (f->eof) return 0; if (header[4] != 0) goto invalid; goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24); for (i=22; i < 26; ++i) header[i] = 0; crc = 0; for (i=0; i < 27; ++i) crc = crc32_update(crc, header[i]); len = 0; for (i=0; i < header[26]; ++i) { int s = get8(f); crc = crc32_update(crc, s); len += s; } if (len && f->eof) return 0; for (i=0; i < len; ++i) crc = crc32_update(crc, get8(f)); // finished parsing probable page if (crc == goal) { // we could now check that it's either got the last // page flag set, OR it's followed by the capture // pattern, but I guess TECHNICALLY you could have // a file with garbage between each ogg page and recover // from it automatically? So even though that paranoia // might decrease the chance of an invalid decode by // another 2^32, not worth it since it would hose those // invalid-but-useful files? if (end) *end = stb_vorbis_get_file_offset(f); if (last) { if (header[5] & 0x04) *last = 1; else *last = 0; } set_file_offset(f, retry_loc-1); return 1; } } invalid: // not a valid page, so rewind and look for next one set_file_offset(f, retry_loc); } } } #define SAMPLE_unknown 0xffffffff // seeking is implemented with a binary search, which narrows down the range to // 64K, before using a linear search (because finding the synchronization // pattern can be expensive, and the chance we'd find the end page again is // relatively high for small ranges) // // two initial interpolation-style probes are used at the start of the search // to try to bound either side of the binary search sensibly, while still // working in O(log n) time if they fail. static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) { uint8 header[27], lacing[255]; int i,len; // record where the page starts z->page_start = stb_vorbis_get_file_offset(f); // parse the header getn(f, header, 27); if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') return 0; getn(f, lacing, header[26]); // determine the length of the payload len = 0; for (i=0; i < header[26]; ++i) len += lacing[i]; // this implies where the page ends z->page_end = z->page_start + 27 + header[26] + len; // read the last-decoded sample out of the data z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); // restore file state to where we were set_file_offset(f, z->page_start); return 1; } // rarely used function to seek back to the preceding page while finding the // start of a packet static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) { unsigned int previous_safe, end; // now we want to seek back 64K from the limit if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) previous_safe = limit_offset - 65536; else previous_safe = f->first_audio_page_offset; set_file_offset(f, previous_safe); while (vorbis_find_page(f, &end, NULL)) { if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) return 1; set_file_offset(f, end); } return 0; } // implements the search logic for finding a page and starting decoding. if // the function succeeds, current_loc_valid will be true and current_loc will // be less than or equal to the provided sample number (the closer the // better). static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { ProbedPage left, right, mid; int i, start_seg_with_known_loc, end_pos, page_start; uint32 delta, stream_length, padding, last_sample_limit; double offset = 0.0, bytes_per_sample = 0.0; int probe = 0; // find the last page and validate the target sample stream_length = stb_vorbis_stream_length_in_samples(f); if (stream_length == 0) return error(f, VORBIS_seek_without_length); if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); // this is the maximum difference between the window-center (which is the // actual granule position value), and the right-start (which the spec // indicates should be the granule position (give or take one)). padding = ((f->blocksize_1 - f->blocksize_0) >> 2); if (sample_number < padding) last_sample_limit = 0; else last_sample_limit = sample_number - padding; left = f->p_first; while (left.last_decoded_sample == ~0U) { // (untested) the first page does not have a 'last_decoded_sample' set_file_offset(f, left.page_end); if (!get_seek_page_info(f, &left)) goto error; } right = f->p_last; assert(right.last_decoded_sample != ~0U); // starting from the start is handled differently if (last_sample_limit <= left.last_decoded_sample) { if (stb_vorbis_seek_start(f)) { if (f->current_loc > sample_number) return error(f, VORBIS_seek_failed); return 1; } return 0; } while (left.page_end != right.page_start) { assert(left.page_end < right.page_start); // search range in bytes delta = right.page_start - left.page_end; if (delta <= 65536) { // there's only 64K left to search - handle it linearly set_file_offset(f, left.page_end); } else { if (probe < 2) { if (probe == 0) { // first probe (interpolate) double data_bytes = right.page_end - left.page_start; bytes_per_sample = data_bytes / right.last_decoded_sample; offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample); } else { // second probe (try to bound the other side) double error = ((double) last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; if (error >= 0 && error < 8000) error = 8000; if (error < 0 && error > -8000) error = -8000; offset += error * 2; } // ensure the offset is valid if (offset < left.page_end) offset = left.page_end; if (offset > right.page_start - 65536) offset = right.page_start - 65536; set_file_offset(f, (unsigned int) offset); } else { // binary search for large ranges (offset by 32K to ensure // we don't hit the right page) set_file_offset(f, left.page_end + (delta / 2) - 32768); } if (!vorbis_find_page(f, NULL, NULL)) goto error; } for (;;) { if (!get_seek_page_info(f, &mid)) goto error; if (mid.last_decoded_sample != ~0U) break; // (untested) no frames end on this page set_file_offset(f, mid.page_end); assert(mid.page_start < right.page_start); } // if we've just found the last page again then we're in a tricky file, // and we're close enough (if it wasn't an interpolation probe). if (mid.page_start == right.page_start) { if (probe >= 2 || delta <= 65536) break; } else { if (last_sample_limit < mid.last_decoded_sample) right = mid; else left = mid; } ++probe; } // seek back to start of the last packet page_start = left.page_start; set_file_offset(f, page_start); if (!start_page(f)) return error(f, VORBIS_seek_failed); end_pos = f->end_seg_with_known_loc; assert(end_pos >= 0); for (;;) { for (i = end_pos; i > 0; --i) if (f->segments[i-1] != 255) break; start_seg_with_known_loc = i; if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) break; // (untested) the final packet begins on an earlier page if (!go_to_page_before(f, page_start)) goto error; page_start = stb_vorbis_get_file_offset(f); if (!start_page(f)) goto error; end_pos = f->segment_count - 1; } // prepare to start decoding f->current_loc_valid = FALSE; f->last_seg = FALSE; f->valid_bits = 0; f->packet_bytes = 0; f->bytes_in_seg = 0; f->previous_length = 0; f->next_seg = start_seg_with_known_loc; for (i = 0; i < start_seg_with_known_loc; i++) skip(f, f->segments[i]); // start decoding (optimizable - this frame is generally discarded) if (!vorbis_pump_first_frame(f)) return 0; if (f->current_loc > sample_number) return error(f, VORBIS_seek_failed); return 1; error: // try to restore the file to a valid state stb_vorbis_seek_start(f); return error(f, VORBIS_seek_failed); } // the same as vorbis_decode_initial, but without advancing static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) { int bits_read, bytes_read; if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) return 0; // either 1 or 2 bytes were read, figure out which so we can rewind bits_read = 1 + ilog(f->mode_count-1); if (f->mode_config[*mode].blockflag) bits_read += 2; bytes_read = (bits_read + 7) / 8; f->bytes_in_seg += bytes_read; f->packet_bytes -= bytes_read; skip(f, -bytes_read); if (f->next_seg == -1) f->next_seg = f->segment_count - 1; else f->next_seg--; f->valid_bits = 0; return 1; } int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) { uint32 max_frame_samples; if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); // fast page-level search if (!seek_to_sample_coarse(f, sample_number)) return 0; assert(f->current_loc_valid); assert(f->current_loc <= sample_number); // linear search for the relevant packet max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; while (f->current_loc < sample_number) { int left_start, left_end, right_start, right_end, mode, frame_samples; if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) return error(f, VORBIS_seek_failed); // calculate the number of samples returned by the next frame frame_samples = right_start - left_start; if (f->current_loc + frame_samples > sample_number) { return 1; // the next frame will contain the sample } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { // there's a chance the frame after this could contain the sample vorbis_pump_first_frame(f); } else { // this frame is too early to be relevant f->current_loc += frame_samples; f->previous_length = 0; maybe_start_packet(f); flush_packet(f); } } // the next frame should start with the sample if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed); return 1; } int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) { if (!stb_vorbis_seek_frame(f, sample_number)) return 0; if (sample_number != f->current_loc) { int n; uint32 frame_start = f->current_loc; stb_vorbis_get_frame_float(f, &n, NULL); assert(sample_number > frame_start); assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); f->channel_buffer_start += (sample_number - frame_start); } return 1; } int stb_vorbis_seek_start(stb_vorbis *f) { if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); } set_file_offset(f, f->first_audio_page_offset); f->previous_length = 0; f->first_decode = TRUE; f->next_seg = -1; return vorbis_pump_first_frame(f); } unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) { unsigned int restore_offset, previous_safe; unsigned int end, last_page_loc; if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); if (!f->total_samples) { unsigned int last; uint32 lo,hi; char header[6]; // first, store the current decode position so we can restore it restore_offset = stb_vorbis_get_file_offset(f); // now we want to seek back 64K from the end (the last page must // be at most a little less than 64K, but let's allow a little slop) if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) previous_safe = f->stream_len - 65536; else previous_safe = f->first_audio_page_offset; set_file_offset(f, previous_safe); // previous_safe is now our candidate 'earliest known place that seeking // to will lead to the final page' if (!vorbis_find_page(f, &end, &last)) { // if we can't find a page, we're hosed! f->error = VORBIS_cant_find_last_page; f->total_samples = 0xffffffff; goto done; } // check if there are more pages last_page_loc = stb_vorbis_get_file_offset(f); // stop when the last_page flag is set, not when we reach eof; // this allows us to stop short of a 'file_section' end without // explicitly checking the length of the section while (!last) { set_file_offset(f, end); if (!vorbis_find_page(f, &end, &last)) { // the last page we found didn't have the 'last page' flag // set. whoops! break; } //previous_safe = last_page_loc+1; // NOTE: not used after this point, but note for debugging last_page_loc = stb_vorbis_get_file_offset(f); } set_file_offset(f, last_page_loc); // parse the header getn(f, (unsigned char *)header, 6); // extract the absolute granule position lo = get32(f); hi = get32(f); if (lo == 0xffffffff && hi == 0xffffffff) { f->error = VORBIS_cant_find_last_page; f->total_samples = SAMPLE_unknown; goto done; } if (hi) lo = 0xfffffffe; // saturate f->total_samples = lo; f->p_last.page_start = last_page_loc; f->p_last.page_end = end; f->p_last.last_decoded_sample = lo; done: set_file_offset(f, restore_offset); } return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; } float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) { return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; } int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) { int len, right,left,i; if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); if (!vorbis_decode_packet(f, &len, &left, &right)) { f->channel_buffer_start = f->channel_buffer_end = 0; return 0; } len = vorbis_finish_frame(f, len, left, right); for (i=0; i < f->channels; ++i) f->outputs[i] = f->channel_buffers[i] + left; f->channel_buffer_start = left; f->channel_buffer_end = left+len; if (channels) *channels = f->channels; if (output) *output = f->outputs; return len; } #ifndef STB_VORBIS_NO_STDIO stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) { stb_vorbis *f, p; vorbis_init(&p, alloc); p.f = file; p.f_start = (uint32) ftell(file); p.stream_len = length; p.close_on_free = close_on_free; if (start_decoder(&p)) { f = vorbis_alloc(&p); if (f) { *f = p; vorbis_pump_first_frame(f); return f; } } if (error) *error = p.error; vorbis_deinit(&p); return NULL; } stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) { unsigned int len, start; start = (unsigned int) ftell(file); fseek(file, 0, SEEK_END); len = (unsigned int) (ftell(file) - start); fseek(file, start, SEEK_SET); return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); } stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) { FILE *f; #if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) if (0 != fopen_s(&f, filename, "rb")) f = NULL; #else f = fopen(filename, "rb"); #endif if (f) return stb_vorbis_open_file(f, TRUE, error, alloc); if (error) *error = VORBIS_file_open_failure; return NULL; } #endif // STB_VORBIS_NO_STDIO stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) { stb_vorbis *f, p; if (!data) { if (error) *error = VORBIS_unexpected_eof; return NULL; } vorbis_init(&p, alloc); p.stream = (uint8 *) data; p.stream_end = (uint8 *) data + len; p.stream_start = (uint8 *) p.stream; p.stream_len = len; p.push_mode = FALSE; if (start_decoder(&p)) { f = vorbis_alloc(&p); if (f) { *f = p; vorbis_pump_first_frame(f); if (error) *error = VORBIS__no_error; return f; } } if (error) *error = p.error; vorbis_deinit(&p); return NULL; } #ifndef STB_VORBIS_NO_INTEGER_CONVERSION #define PLAYBACK_MONO 1 #define PLAYBACK_LEFT 2 #define PLAYBACK_RIGHT 4 #define L (PLAYBACK_LEFT | PLAYBACK_MONO) #define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) #define R (PLAYBACK_RIGHT | PLAYBACK_MONO) static int8 channel_position[7][6] = { { 0 }, { C }, { L, R }, { L, C, R }, { L, R, L, R }, { L, C, R, L, R }, { L, C, R, L, R, C }, }; #ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT typedef union { float f; int i; } float_conv; typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; #define FASTDEF(x) float_conv x // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) #define check_endianness() #else #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) #define check_endianness() #define FASTDEF(x) #endif static void copy_samples(short *dest, float *src, int len) { int i; check_endianness(); for (i=0; i < len; ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); if ((unsigned int) (v + 32768) > 65535) v = v < 0 ? -32768 : 32767; dest[i] = v; } } static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) { #define STB_BUFFER_SIZE 32 float buffer[STB_BUFFER_SIZE]; int i,j,o,n = STB_BUFFER_SIZE; check_endianness(); for (o = 0; o < len; o += STB_BUFFER_SIZE) { memset(buffer, 0, sizeof(buffer)); if (o + n > len) n = len - o; for (j=0; j < num_c; ++j) { if (channel_position[num_c][j] & mask) { for (i=0; i < n; ++i) buffer[i] += data[j][d_offset+o+i]; } } for (i=0; i < n; ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); if ((unsigned int) (v + 32768) > 65535) v = v < 0 ? -32768 : 32767; output[o+i] = v; } } #undef STB_BUFFER_SIZE } static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) { #define STB_BUFFER_SIZE 32 float buffer[STB_BUFFER_SIZE]; int i,j,o,n = STB_BUFFER_SIZE >> 1; // o is the offset in the source data check_endianness(); for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) { // o2 is the offset in the output data int o2 = o << 1; memset(buffer, 0, sizeof(buffer)); if (o + n > len) n = len - o; for (j=0; j < num_c; ++j) { int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { for (i=0; i < n; ++i) { buffer[i*2+0] += data[j][d_offset+o+i]; buffer[i*2+1] += data[j][d_offset+o+i]; } } else if (m == PLAYBACK_LEFT) { for (i=0; i < n; ++i) { buffer[i*2+0] += data[j][d_offset+o+i]; } } else if (m == PLAYBACK_RIGHT) { for (i=0; i < n; ++i) { buffer[i*2+1] += data[j][d_offset+o+i]; } } } for (i=0; i < (n<<1); ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); if ((unsigned int) (v + 32768) > 65535) v = v < 0 ? -32768 : 32767; output[o2+i] = v; } } #undef STB_BUFFER_SIZE } static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) { int i; if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; for (i=0; i < buf_c; ++i) compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); } else { int limit = buf_c < data_c ? buf_c : data_c; for (i=0; i < limit; ++i) copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); for ( ; i < buf_c; ++i) memset(buffer[i]+b_offset, 0, sizeof(short) * samples); } } int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) { float **output = NULL; int len = stb_vorbis_get_frame_float(f, NULL, &output); if (len > num_samples) len = num_samples; if (len) convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); return len; } static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) { int i; check_endianness(); if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { assert(buf_c == 2); for (i=0; i < buf_c; ++i) compute_stereo_samples(buffer, data_c, data, d_offset, len); } else { int limit = buf_c < data_c ? buf_c : data_c; int j; for (j=0; j < len; ++j) { for (i=0; i < limit; ++i) { FASTDEF(temp); float f = data[i][d_offset+j]; int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); if ((unsigned int) (v + 32768) > 65535) v = v < 0 ? -32768 : 32767; *buffer++ = v; } for ( ; i < buf_c; ++i) *buffer++ = 0; } } } int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) { float **output; int len; if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); len = stb_vorbis_get_frame_float(f, NULL, &output); if (len) { if (len*num_c > num_shorts) len = num_shorts / num_c; convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); } return len; } int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) { float **outputs; int len = num_shorts / channels; int n=0; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; if (k) convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); buffer += k*channels; n += k; f->channel_buffer_start += k; if (n == len) break; if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; } return n; } int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) { float **outputs; int n=0; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; if (k) convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); n += k; f->channel_buffer_start += k; if (n == len) break; if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; } return n; } #ifndef STB_VORBIS_NO_STDIO int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) { int data_len, offset, total, limit, error; short *data; stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); if (v == NULL) return -1; limit = v->channels * 4096; *channels = v->channels; if (sample_rate) *sample_rate = v->sample_rate; offset = data_len = 0; total = limit; data = (short *) malloc(total * sizeof(*data)); if (data == NULL) { stb_vorbis_close(v); return -2; } for (;;) { int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); if (n == 0) break; data_len += n; offset += n * v->channels; if (offset + limit > total) { short *data2; total *= 2; data2 = (short *) realloc(data, total * sizeof(*data)); if (data2 == NULL) { free(data); stb_vorbis_close(v); return -2; } data = data2; } } *output = data; stb_vorbis_close(v); return data_len; } #endif // NO_STDIO int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) { int data_len, offset, total, limit, error; short *data; stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); if (v == NULL) return -1; limit = v->channels * 4096; *channels = v->channels; if (sample_rate) *sample_rate = v->sample_rate; offset = data_len = 0; total = limit; data = (short *) malloc(total * sizeof(*data)); if (data == NULL) { stb_vorbis_close(v); return -2; } for (;;) { int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); if (n == 0) break; data_len += n; offset += n * v->channels; if (offset + limit > total) { short *data2; total *= 2; data2 = (short *) realloc(data, total * sizeof(*data)); if (data2 == NULL) { free(data); stb_vorbis_close(v); return -2; } data = data2; } } *output = data; stb_vorbis_close(v); return data_len; } #endif // STB_VORBIS_NO_INTEGER_CONVERSION int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) { float **outputs; int len = num_floats / channels; int n=0; int z = f->channels; if (z > channels) z = channels; while (n < len) { int i,j; int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; for (j=0; j < k; ++j) { for (i=0; i < z; ++i) *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; for ( ; i < channels; ++i) *buffer++ = 0; } n += k; f->channel_buffer_start += k; if (n == len) break; if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; } return n; } int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) { float **outputs; int n=0; int z = f->channels; if (z > channels) z = channels; while (n < num_samples) { int i; int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= num_samples) k = num_samples - n; if (k) { for (i=0; i < z; ++i) memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); for ( ; i < channels; ++i) memset(buffer[i]+n, 0, sizeof(float) * k); } n += k; f->channel_buffer_start += k; if (n == num_samples) break; if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; } return n; } #endif // STB_VORBIS_NO_PULLDATA_API /* Version history 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223 found with Mayhem by ForAllSecure 1.16 - 2019-03-04 - fix warnings 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found 1.14 - 2018-02-11 - delete bogus dealloca usage 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files 1.11 - 2017-07-23 - fix MinGW compilation 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; avoid discarding last frame of audio data 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API some more crash fixes when out of memory or with corrupt files 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) some crash fixes when out of memory or with corrupt files 1.05 - 2015-04-19 - don't define __forceinline if it's redundant 1.04 - 2014-08-27 - fix missing const-correct case in API 1.03 - 2014-08-07 - Warning fixes 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel (API change) report sample rate for decode-full-file funcs 0.99996 - bracket #include for macintosh compilation by Laurent Gomila 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence 0.99993 - remove assert that fired on legal files with empty tables 0.99992 - rewind-to-start 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ 0.9998 - add a full-decode function with a memory source 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition 0.9996 - query length of vorbis stream in samples/seconds 0.9995 - bugfix to another optimization that only happened in certain files 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation 0.9992 - performance improvement of IMDCT; now performs close to reference implementation 0.9991 - performance improvement of IMDCT 0.999 - (should have been 0.9990) performance improvement of IMDCT 0.998 - no-CRT support from Casey Muratori 0.997 - bugfixes for bugs found by Terje Mathisen 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen 0.992 - fixes for MinGW warning 0.991 - turn fast-float-conversion on by default 0.990 - fix push-mode seek recovery if you seek into the headers 0.98b - fix to bad release of 0.98 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode 0.97 - builds under c++ (typecasting, don't use 'class' keyword) 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code 0.95 - clamping code for 16-bit functions 0.94 - not publically released 0.93 - fixed all-zero-floor case (was decoding garbage) 0.92 - fixed a memory leak 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION 0.90 - first public release */ #endif // STB_VORBIS_HEADER_ONLY /* ------------------------------------------------------------------------------ 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_8_40/src/client/sound/header/vorbis.h000066400000000000000000000025731465112212000222220ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 * * ======================================================================= */ #ifndef CL_SOUND_VORBIS_H #define CL_SOUND_VORBIS_H #include "local.h" typedef enum { PLAY, PAUSE, STOP } ogg_status_t; int OGG_Status(void); void OGG_InitTrackList(void); void OGG_Init(void); void OGG_PlayTrack(int trackNo, qboolean cdtrack, qboolean immediate); void OGG_RecoverState(void); void OGG_SaveState(void); void OGG_Shutdown(void); void OGG_Stop(void); void OGG_Stream(void); void OGG_LoadAsWav(char *filename, wavinfo_t *info, void **buffer); #endif yquake2-QUAKE2_8_40/src/client/sound/ogg.c000066400000000000000000000463361465112212000202420ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 stb_vorbis.c 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. * * ======================================================================= */ #ifndef _WIN32 #include #endif #include #include "../header/client.h" #include "header/local.h" #include "header/vorbis.h" #define STB_VORBIS_NO_PUSHDATA_API #include "header/stb_vorbis.h" static cvar_t *ogg_pausewithgame; /* Pause music when the game is paused */ static cvar_t *ogg_enabled; /* Backend is enabled */ static cvar_t *ogg_shuffle; /* Shuffle playback */ static cvar_t *ogg_ignoretrack0; /* Toggle track 0 playing */ static cvar_t *ogg_volume; /* Music volume. */ static int ogg_curfile; /* Index of currently played file. */ static int ogg_numbufs; /* Number of buffers for OpenAL */ static int ogg_numsamples; /* Number of sambles read from the current file */ static int ogg_mapcdtrack; /* Index of current map cdtrack */ static ogg_status_t ogg_status; /* Status indicator. */ static stb_vorbis *ogg_file; /* Ogg Vorbis file. */ static qboolean ogg_started; /* Initialization flag. */ static qboolean ogg_mutemusic; /* Mute music */ enum { MAX_NUM_OGGTRACKS = 128 }; static char* ogg_tracks[MAX_NUM_OGGTRACKS]; static int ogg_maxfileindex; enum GameType { other, // incl. baseq2 xatrix, rogue }; struct { qboolean saved; int curfile; int numsamples; } ogg_saved_state; static void OGG_TogglePlayback(void); // -------- /* * The GOG version of Quake2 has the music tracks in music/TrackXX.ogg * That music/ dir is next to baseq2/ (not in it) and contains Track02.ogg to Track21.ogg * There * - Track02 to Track11 correspond to Quake2 (baseq2) CD tracks 2-11 * - Track12 to Track21 correspond to the Ground Zero (rogue) addon's CD tracks 2-11 * - The "The Reckoning" (xatrix) addon also had 11 tracks, that were a mix of the ones * from the main game (baseq2) and the rogue addon. * See below how the CD track is mapped to GOG track numbers */ static int getMappedGOGtrack(int track, enum GameType gameType) { if(track <= 0) return 0; if(track == 1) return 0; // 1 is illegal (=> data track on CD), 0 means "no track" if(gameType == other) return track; if(gameType == rogue) return track + 10; // apparently it's xatrix => map the track to the corresponding TrackXX.ogg from GOG switch(track) { case 2: return 9; // baseq2 9 case 3: return 13; // rogue 3 case 4: return 14; // rogue 4 case 5: return 7; // baseq2 7 case 6: return 16; // rogue 6 case 7: return 2; // baseq2 2 case 8: return 15; // rogue 5 case 9: return 3; // baseq2 3 case 10: return 4; // baseq2 4 case 11: return 18; // rogue 8 default: return track; } } /* * Load list of Ogg Vorbis files in "music/". */ void OGG_InitTrackList(void) { for (int i=0; istring[0] == '\0' || strcmp(BASEDIRNAME, gameCvar->string) == 0) { // baseq2 => only 2 dirs in searchPath potMusicDirs[0] = BASEDIRNAME "/music/"; // baseq2/music/ potMusicDirs[1] = "music/"; // global music dir (GOG) potMusicDirs[2] = NULL; } else { // some other mod/addon snprintf(gameMusicDir, MAX_QPATH, "%s/music/", gameCvar->string); potMusicDirs[0] = gameMusicDir; // $mod/music/ potMusicDirs[1] = "music/"; // global music dir (GOG) potMusicDirs[2] = BASEDIRNAME "/music/"; // baseq2/music/ } enum GameType gameType = other; if (strcmp("xatrix", gameCvar->string) == 0) { gameType = xatrix; } else if (strcmp("rogue", gameCvar->string) == 0) { gameType = rogue; } for (int potMusicDirIdx = 0; potMusicDirIdx < sizeof(potMusicDirs)/sizeof(potMusicDirs[0]); ++potMusicDirIdx) { const char* musicDir = potMusicDirs[potMusicDirIdx]; if (musicDir == NULL) { break; } for (const char* rawPath = FS_GetNextRawPath(NULL); rawPath != NULL; rawPath = FS_GetNextRawPath(rawPath)) { char fullMusicPath[MAX_OSPATH] = {0}; snprintf(fullMusicPath, MAX_OSPATH, "%s/%s", rawPath, musicDir); if(!Sys_IsDir(fullMusicPath)) { continue; } char testFileName[MAX_OSPATH]; // the simple case (like before: $mod/music/02.ogg - 11.ogg or whatever) snprintf(testFileName, MAX_OSPATH, "%s02.ogg", fullMusicPath); if(Sys_IsFile(testFileName)) { ogg_tracks[2] = strdup(testFileName); for(int i=3; ivalue; int read_samples = stb_vorbis_get_samples_short_interleaved(ogg_file, ogg_file->channels, samples, sizeof(samples) / sizeof(short)); if (read_samples > 0) { ogg_numsamples += read_samples; S_RawSamples(read_samples, ogg_file->sample_rate, sizeof(short), ogg_file->channels, (byte *)samples, volume); } else { // We cannot call OGG_Stop() here. It flushes the OpenAL sample // queue, thus about 12 seconds of music are lost. Instead we // just set the OGG state to stop and open a new file. The new // files content is added to the sample queue after the remaining // samples from the old file. stb_vorbis_close(ogg_file); ogg_status = STOP; ogg_numbufs = 0; ogg_numsamples = 0; OGG_PlayTrack(ogg_curfile, false, false); } } /* * Stream music. */ void OGG_Stream(void) { if (!ogg_started) { return; } /* OGG playback was disabled since the last package frame. Shutdown the backend to stop all playing tracks and to prevend OGG_PLayTrack() from starting new ones. */ if (ogg_enabled->value != 1) { OGG_Shutdown(); return; } if (ogg_pausewithgame->modified) { if ((ogg_pausewithgame->value == 0 && ogg_status == PAUSE) || (ogg_pausewithgame->value == 1 && ogg_status == PLAY)) { OGG_TogglePlayback(); } ogg_pausewithgame->modified = false; } 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(); } } } } if (ogg_status == PLAY && ogg_shuffle->modified) { // Shuffle was modified before last track finished. ogg_shuffle->modified = false; } else if (ogg_status == STOP && ogg_shuffle->modified) { // If fullscreen console, clear the map cdtrack. if (cls.state == ca_disconnected) { ogg_mapcdtrack = 0; // Track 0 means "stop music". } // Ogg playback has stopped and shuffle was modified, play the map cdtrack. ogg_shuffle->modified = false; OGG_PlayTrack(ogg_mapcdtrack, true, true); } } // -------- /* * play the ogg file that corresponds to the CD track with the given number */ void OGG_PlayTrack(int trackNo, qboolean cdtrack, qboolean immediate) { if (sound_started == SS_NOT) { return; // sound is not initialized } if (ogg_started == false) { return; } // Track 0 means "stop music". if (trackNo == 0) { if(ogg_ignoretrack0->value == 0) { OGG_Stop(); return; } // Special case: If ogg_ignoretrack0 is 0 we stopped the music (see above) // and ogg_curfile is still holding the last track played (track >1). So // this triggers and we return. If ogg_ignoretrack is 1 we didn't stop the // music, as soon as the tracks ends OGG_Read() starts it over. Until here // everything's okay. // But if ogg_ignoretrack0 is 1, the game was just restarted and a save game // load send us trackNo 0, we would end up without music. Since we have no // way to get the last track before trackNo 0 was set just fall through and // shuffle a random track (see below). if (ogg_curfile > 0) { return; } } if (cdtrack == true) // the map cdtrack. { ogg_mapcdtrack = trackNo; } int playback = ogg_shuffle->value; int curtrack = 0; // If loading a map, restarting sound or video, the ogg backend or console issued // a playtrack or no tracks have been played, apply shuffle parameters to the next // track. if ((immediate == false && (cls.state == ca_connecting || cls.state == ca_connected)) || immediate == true || ogg_curfile == -1) { curtrack = trackNo; playback = 0; } else { curtrack = ogg_curfile; } int newtrack = 0; // These must match those in menu.c - Options_MenuInit(). switch (playback) { case 0: // default { newtrack = curtrack; } break; case 1: // play once { return; } break; case 2: // sequential { newtrack = (curtrack + 1) % (ogg_maxfileindex + 1) != 0 ? (curtrack + 1) : 2; } break; case 3: // random { int retries = 100; newtrack = 0; while (retries-- > 0 && newtrack < 2) { newtrack = randk() % (ogg_maxfileindex + 1); if (newtrack == curtrack) { newtrack = 0; } } } break; } trackNo = newtrack; if (ogg_maxfileindex == -1) { return; // no ogg files at all, ignore this silently instead of printing warnings all the time } if ((trackNo < 2) || (trackNo > ogg_maxfileindex)) { Com_Printf("%s: %d out of range.\n", __func__, trackNo); return; } if (ogg_tracks[trackNo] == NULL) { Com_Printf("%s: Don't have a .ogg file for track %d\n", __func__, trackNo); } /* Check running music. */ if (ogg_status == PLAY) { if (ogg_curfile == trackNo) { return; } else { OGG_Stop(); } } if (ogg_tracks[trackNo] == NULL) { Com_Printf("OGG_PlayTrack: I don't have a file for track %d!\n", trackNo); return; } /* Open ogg vorbis file. */ FILE* f = Q_fopen(ogg_tracks[trackNo], "rb"); if (f == NULL) { Com_Printf("%s: could not open file %s for track %d: %s.\n", __func__, ogg_tracks[trackNo], trackNo, strerror(errno)); ogg_tracks[trackNo] = NULL; return; } int res = 0; // fclose is not required on error with close_on_free=true ogg_file = stb_vorbis_open_file(f, true, &res, NULL); if (res != 0) { Com_Printf("%s: '%s' is not a valid Ogg Vorbis file (error %i).\n", __func__, ogg_tracks[trackNo], res); return; } /* Play file. */ ogg_curfile = trackNo; ogg_numsamples = 0; ogg_status = PLAY; } // ---- /* * List Ogg Vorbis files and print current playback state. */ static void OGG_Info(void) { Com_Printf("Tracks:\n"); for (int i = 2; i <= ogg_maxfileindex; i++) { if(ogg_tracks[i]) { Com_Printf(" - %02d %s\n", i, ogg_tracks[i]); } else { Com_Printf(" - %02d \n", i); } } Com_Printf("Total: %d Ogg/Vorbis files.\n", ogg_maxfileindex+1); switch (ogg_status) { case PLAY: Com_Printf("State: Playing file %d (%s) at %i samples.\n", ogg_curfile, ogg_tracks[ogg_curfile], stb_vorbis_get_sample_offset(ogg_file)); break; case PAUSE: Com_Printf("State: Paused file %d (%s) at %i samples.\n", ogg_curfile, ogg_tracks[ogg_curfile], stb_vorbis_get_sample_offset(ogg_file)); break; case STOP: if (ogg_curfile == -1) { Com_Printf("State: Stopped.\n"); } else { Com_Printf("State: Stopped file %d (%s).\n", ogg_curfile, ogg_tracks[ogg_curfile]); } break; } } /* * 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 stb_vorbis_close(ogg_file); ogg_status = STOP; ogg_numbufs = 0; } /* * Pause or resume playback. */ static void OGG_TogglePlayback(void) { if (ogg_status == PLAY) { ogg_status = PAUSE; ogg_numbufs = 0; #ifdef USE_OPENAL if (sound_started == SS_OAL) { AL_UnqueueRawSamples(); } #endif } else if (ogg_status == PAUSE) { ogg_status = PLAY; } } /* * Prints a help message for the 'ogg' cmd. */ void OGG_HelpMsg(void) { Com_Printf("Unknown sub command %s\n\n", Cmd_Argv(1)); Com_Printf("Commands:\n"); Com_Printf(" - info: Print information about playback state and tracks\n"); Com_Printf(" - play : Play track number \n"); Com_Printf(" - stop: Stop playback\n"); Com_Printf(" - toggle: Toggle pause\n"); Com_Printf(" - mute: Mute playback\n"); } /* * The 'ogg' cmd. Gives some control and information about the playback state. */ void OGG_Cmd(void) { if (Cmd_Argc() < 2) { OGG_HelpMsg(); return; } if (Q_stricmp(Cmd_Argv(1), "info") == 0) { OGG_Info(); } else if (Q_stricmp(Cmd_Argv(1), "play") == 0) { if (Cmd_Argc() != 3) { Com_Printf("ogg play : Play "); return; } int track = (int)strtol(Cmd_Argv(2), NULL, 10); if (track < 2 || track > ogg_maxfileindex) { Com_Printf("invalid track %s, must be an number between 2 and %d\n", Cmd_Argv(1), ogg_maxfileindex); return; } else { OGG_PlayTrack(track, false, true); } } else if (Q_stricmp(Cmd_Argv(1), "stop") == 0) { OGG_Stop(); } else if (Q_stricmp(Cmd_Argv(1), "toggle") == 0) { OGG_TogglePlayback(); } else if (Q_stricmp(Cmd_Argv(1), "mute") == 0) { ogg_mutemusic = !ogg_mutemusic; } else { OGG_HelpMsg(); } } /* * Saves the current state of the subsystem. */ void OGG_SaveState(void) { if (ogg_status != PLAY) { ogg_saved_state.saved = false; return; } ogg_saved_state.saved = true; ogg_saved_state.curfile = ogg_curfile; ogg_saved_state.numsamples = ogg_numsamples; } /* * Recover the previously saved state. */ void OGG_RecoverState(void) { if (!ogg_saved_state.saved) { return; } // Mkay, ultra evil hack to recover the state in case of // shuffled playback. OGG_PlayTrack() does the shuffeling, // so switch it of before and enable after state recovery. int shuffle_state = ogg_shuffle->value; Cvar_SetValue("ogg_shuffle", 0); OGG_PlayTrack(ogg_saved_state.curfile, false, true); stb_vorbis_seek_frame(ogg_file, ogg_saved_state.numsamples); ogg_numsamples = ogg_saved_state.numsamples; Cvar_SetValue("ogg_shuffle", shuffle_state); } /* * Returns ogg status. */ int OGG_Status(void) { return ogg_status; } // -------- /* * Initialize the Ogg Vorbis subsystem. */ void OGG_Init(void) { // Cvars ogg_pausewithgame = Cvar_Get("ogg_pausewithgame", "0", CVAR_ARCHIVE); ogg_shuffle = Cvar_Get("ogg_shuffle", "0", CVAR_ARCHIVE); ogg_ignoretrack0 = Cvar_Get("ogg_ignoretrack0", "0", CVAR_ARCHIVE); ogg_volume = Cvar_Get("ogg_volume", "0.7", CVAR_ARCHIVE); ogg_enabled = Cvar_Get("ogg_enable", "1", CVAR_ARCHIVE); if (ogg_enabled->value != 1) { return; } // Commands Cmd_AddCommand("ogg", OGG_Cmd); // Global variables ogg_curfile = -1; ogg_numsamples = 0; ogg_status = STOP; ogg_mapcdtrack = 0; ogg_mutemusic = false; ogg_started = true; } /* * Shutdown the Ogg Vorbis subsystem. */ void OGG_Shutdown(void) { if (!ogg_started) { return; } // Music must be stopped. OGG_Stop(); // Free file list. for(int i=0; ichannels > 0) { int read_samples = 0; /* fill in wav structure */ info->rate = ogg2wav_file->sample_rate; info->width = 2; info->channels = ogg2wav_file->channels; info->loopstart = -1; /* return length * channels */ info->samples = stb_vorbis_stream_length_in_samples(ogg2wav_file) * info->channels; info->dataofs = 0; /* alloc memory for uncompressed wav */ final_buffer = Z_Malloc(info->samples * sizeof(short)); /* load sampleas to buffer */ read_samples = stb_vorbis_get_samples_short_interleaved( ogg2wav_file, info->channels, final_buffer, info->samples); if (read_samples > 0) { /* fix sample list size*/ if ((read_samples * info->channels) != info->samples) { Com_DPrintf("%s: incorrect size: %d != %d\n", filename, info->samples, read_samples * info->channels); info->samples = read_samples * info->channels; } /* copy to final result */ *buffer = final_buffer; } else { /* something is going wrong */ Z_Free(final_buffer); final_buffer = NULL; } } if (ogg2wav_file) { stb_vorbis_close(ogg2wav_file); } FS_FreeFile(temp_buffer); } yquake2-QUAKE2_8_40/src/client/sound/openal.c000066400000000000000000000743751465112212000207500ustar00rootroot00000000000000/* * 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 #include "../header/client.h" #include "header/local.h" #include "header/qal.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 #define QAL_EFX_MAX 1 #define QAL_REVERB_EFFECT 0 /* Globals */ int active_buffers; /* Locals */ static qboolean streamPlaying; static ALuint s_srcnums[MAX_CHANNELS - 1]; static ALuint streamSource; static int s_framecount; static ALuint underwaterFilter; static ALuint ReverbEffect[QAL_EFX_MAX] = {0}; static ALuint ReverbEffectSlot[QAL_EFX_MAX] = {0}; static int lastreverteffect = -1; /* just some invalid index value */ /* ----------------------------------------------------------------- */ /* * Silence / stop all OpenAL streams */ static void AL_StreamDie(void) { int numBuffers; /* openal might not be initialised yet */ if (!qalSourceStop) return; 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; } } /* ----------------------------------------------------------------- */ static ALenum AL_GetFormat(int width, int channels) { if (width == 1) { if (channels == 1) { return AL_FORMAT_MONO8; } else if (channels == 2) { return AL_FORMAT_STEREO8; } } else if (width == 2) { if (channels == 1) { return AL_FORMAT_MONO16; } else if (channels == 2) { return AL_FORMAT_STEREO16; } } return AL_FORMAT_MONO8; } /* * 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, short volume, int begin_length, int end_length, int attack_length, int fade_length) { sfxcache_t *sc; ALsizei size; ALenum format; ALuint name; size = s_info->samples * s_info->width; /* Work out format */ format = AL_GetFormat(s_info->width, s_info->channels); 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 = ((uint64_t)s_info->samples * 1000) / s_info->rate; sc->loopstart = s_info->loopstart; sc->width = s_info->width; sc->size = size; sc->bufnum = name; sc->stereo = s_info->channels - 1; sc->volume = volume; sc->begin = begin_length * 1000 / s_info->rate; sc->end = end_length * 1000 / s_info->rate; sc->fade = fade_length * 1000 / s_info->rate; sc->attack = attack_length * 1000 / s_info->rate; 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--; } /* ----------------------------------------------------------------- */ static EFXEAXREVERBPROPERTIES ReverbPresets[] = { EFX_REVERB_PRESET_GENERIC, EFX_REVERB_PRESET_PADDEDCELL, EFX_REVERB_PRESET_ROOM, EFX_REVERB_PRESET_BATHROOM, EFX_REVERB_PRESET_LIVINGROOM, EFX_REVERB_PRESET_STONEROOM, EFX_REVERB_PRESET_AUDITORIUM, EFX_REVERB_PRESET_CONCERTHALL, EFX_REVERB_PRESET_CAVE, EFX_REVERB_PRESET_ARENA, EFX_REVERB_PRESET_HANGAR, EFX_REVERB_PRESET_CARPETEDHALLWAY, EFX_REVERB_PRESET_HALLWAY, EFX_REVERB_PRESET_STONECORRIDOR, EFX_REVERB_PRESET_ALLEY, EFX_REVERB_PRESET_FOREST, EFX_REVERB_PRESET_CITY, EFX_REVERB_PRESET_MOUNTAINS, EFX_REVERB_PRESET_QUARRY, EFX_REVERB_PRESET_PLAIN, EFX_REVERB_PRESET_PARKINGLOT, EFX_REVERB_PRESET_SEWERPIPE, EFX_REVERB_PRESET_UNDERWATER, EFX_REVERB_PRESET_DRUGGED, EFX_REVERB_PRESET_DIZZY, EFX_REVERB_PRESET_PSYCHOTIC, EFX_REVERB_PRESET_CASTLE_SMALLROOM, EFX_REVERB_PRESET_CASTLE_SHORTPASSAGE, EFX_REVERB_PRESET_CASTLE_MEDIUMROOM, EFX_REVERB_PRESET_CASTLE_LARGEROOM, EFX_REVERB_PRESET_CASTLE_LONGPASSAGE, EFX_REVERB_PRESET_CASTLE_HALL, EFX_REVERB_PRESET_CASTLE_CUPBOARD, EFX_REVERB_PRESET_CASTLE_COURTYARD, EFX_REVERB_PRESET_CASTLE_ALCOVE, EFX_REVERB_PRESET_FACTORY_SMALLROOM, EFX_REVERB_PRESET_FACTORY_SHORTPASSAGE, EFX_REVERB_PRESET_FACTORY_MEDIUMROOM, EFX_REVERB_PRESET_FACTORY_LARGEROOM, EFX_REVERB_PRESET_FACTORY_LONGPASSAGE, EFX_REVERB_PRESET_FACTORY_HALL, EFX_REVERB_PRESET_FACTORY_CUPBOARD, EFX_REVERB_PRESET_FACTORY_COURTYARD, EFX_REVERB_PRESET_FACTORY_ALCOVE, EFX_REVERB_PRESET_ICEPALACE_SMALLROOM, EFX_REVERB_PRESET_ICEPALACE_SHORTPASSAGE, EFX_REVERB_PRESET_ICEPALACE_MEDIUMROOM, EFX_REVERB_PRESET_ICEPALACE_LARGEROOM, EFX_REVERB_PRESET_ICEPALACE_LONGPASSAGE, EFX_REVERB_PRESET_ICEPALACE_HALL, EFX_REVERB_PRESET_ICEPALACE_CUPBOARD, EFX_REVERB_PRESET_ICEPALACE_COURTYARD, EFX_REVERB_PRESET_ICEPALACE_ALCOVE, EFX_REVERB_PRESET_SPACESTATION_SMALLROOM, EFX_REVERB_PRESET_SPACESTATION_SHORTPASSAGE, EFX_REVERB_PRESET_SPACESTATION_MEDIUMROOM, EFX_REVERB_PRESET_SPACESTATION_LARGEROOM, EFX_REVERB_PRESET_SPACESTATION_LONGPASSAGE, EFX_REVERB_PRESET_SPACESTATION_HALL, EFX_REVERB_PRESET_SPACESTATION_CUPBOARD, EFX_REVERB_PRESET_SPACESTATION_ALCOVE, EFX_REVERB_PRESET_WOODEN_SMALLROOM, EFX_REVERB_PRESET_WOODEN_SHORTPASSAGE, EFX_REVERB_PRESET_WOODEN_MEDIUMROOM, EFX_REVERB_PRESET_WOODEN_LARGEROOM, EFX_REVERB_PRESET_WOODEN_LONGPASSAGE, EFX_REVERB_PRESET_WOODEN_HALL, EFX_REVERB_PRESET_WOODEN_CUPBOARD, EFX_REVERB_PRESET_WOODEN_COURTYARD, EFX_REVERB_PRESET_WOODEN_ALCOVE, EFX_REVERB_PRESET_SPORT_EMPTYSTADIUM, EFX_REVERB_PRESET_SPORT_SQUASHCOURT, EFX_REVERB_PRESET_SPORT_SMALLSWIMMINGPOOL, EFX_REVERB_PRESET_SPORT_LARGESWIMMINGPOOL, EFX_REVERB_PRESET_SPORT_GYMNASIUM, EFX_REVERB_PRESET_SPORT_FULLSTADIUM, EFX_REVERB_PRESET_SPORT_STADIUMTANNOY, EFX_REVERB_PRESET_PREFAB_WORKSHOP, EFX_REVERB_PRESET_PREFAB_SCHOOLROOM, EFX_REVERB_PRESET_PREFAB_PRACTISEROOM, EFX_REVERB_PRESET_PREFAB_OUTHOUSE, EFX_REVERB_PRESET_PREFAB_CARAVAN, EFX_REVERB_PRESET_DOME_TOMB, EFX_REVERB_PRESET_PIPE_SMALL, EFX_REVERB_PRESET_DOME_SAINTPAULS, EFX_REVERB_PRESET_PIPE_LONGTHIN, EFX_REVERB_PRESET_PIPE_LARGE, EFX_REVERB_PRESET_PIPE_RESONANT, EFX_REVERB_PRESET_OUTDOORS_BACKYARD, EFX_REVERB_PRESET_OUTDOORS_ROLLINGPLAINS, EFX_REVERB_PRESET_OUTDOORS_DEEPCANYON, EFX_REVERB_PRESET_OUTDOORS_CREEK, EFX_REVERB_PRESET_OUTDOORS_VALLEY, EFX_REVERB_PRESET_MOOD_HEAVEN, EFX_REVERB_PRESET_MOOD_HELL, EFX_REVERB_PRESET_MOOD_MEMORY, EFX_REVERB_PRESET_DRIVING_COMMENTATOR, EFX_REVERB_PRESET_DRIVING_PITGARAGE, EFX_REVERB_PRESET_DRIVING_INCAR_RACER, EFX_REVERB_PRESET_DRIVING_INCAR_SPORTS, EFX_REVERB_PRESET_DRIVING_INCAR_LUXURY, EFX_REVERB_PRESET_DRIVING_FULLGRANDSTAND, EFX_REVERB_PRESET_DRIVING_EMPTYGRANDSTAND, EFX_REVERB_PRESET_DRIVING_TUNNEL, EFX_REVERB_PRESET_CITY_STREETS, EFX_REVERB_PRESET_CITY_SUBWAY, EFX_REVERB_PRESET_CITY_MUSEUM, EFX_REVERB_PRESET_CITY_LIBRARY, EFX_REVERB_PRESET_CITY_UNDERPASS, EFX_REVERB_PRESET_CITY_ABANDONED, EFX_REVERB_PRESET_DUSTYROOM, EFX_REVERB_PRESET_CHAPEL, EFX_REVERB_PRESET_SMALLWATERROOM }; #define EFX_REVERB_SIZE (sizeof(ReverbPresets) / sizeof(*ReverbPresets)) static char ReverbPresetsNames[][32] = { "Generic", "Padded Cell", "Room", "Bathroom", "LivingRoom", "StoneRoom", "Auditorium", "ConcertHall", "Cave", "Arena", "Hangar", "Carpeted Hallway", "Hallway", "Stone Corridor", "Alley", "Forest", "City", "Mountains", "Quarry", "Plain", "Parking Lot", "Sewer Pipe", "Underwater", "Drugged", "Dizzy", "Psychotic", "Castle Small Room", "Castle Short Passage", "Castle Medium Room", "Castle Large Room", "Castle Long Passage", "Castle Hall", "Castle Cupboard", "Castle Courtyard", "Castle Alcove", "Factory Small Room", "Factory Short Passage", "Factory Medium Room", "Factory Large Room", "Factory Long Passage", "Factory Hall", "Factory Cupboard", "Factory Courtyard", "Factory Alcove", "Icepalace Small Room", "Icepalace Short Passage", "Icepalace Medium Room", "Icepalace Large Room", "Icepalace Long Passage", "Icepalace Hall", "Icepalace Cupboard", "Icepalace Courtyard", "Icepalace Alcove", "Spacestation Small Room", "Spacestation Short Passage", "Spacestation Medium Room", "Spacestation Large Room", "Spacestation Long Passage", "Spacestation Hall", "Spacestation Cupboard", "Spacestation Alcove", "Wooden Small Room", "Wooden Short Passage", "Wooden Medium Room", "Wooden Large Room", "Wooden Long Passage", "Wooden Hall", "Wooden Cupboard", "Wooden CourtYard", "Wooden Alcove", "Sport Empty Stadium", "Sport Squash Court", "Sport Small Swimming Pool", "Sport Large Swimming Pool", "Sport Gymnasium", "Sport Full Stadium", "Sport Stadium Tannoy", "Prefab Workshop", "Prefab School Room", "Prefab Practise Room", "Prefab Outhouse", "Prefab Caravan", "Dome Tomb", "Pipe Small", "Dome Saintpauls", "Pipe Longthin", "Pipe Large", "Pipe Resonant", "Outdoors Backyard", "Outdoors Rolling Plains", "Outdoors Deep Canyon", "Outdoors Creek", "Outdoors Valley", "Mood Heaven", "Mood Hell", "Mood Memory", "Driving Commentator", "Driving Pit Garage", "Driving Incar Racer", "Driving Incar Sports", "Driving Incar Luxury", "Driving Full Grandstand", "Driving Empty Grandstand", "Driving Tunnel", "City Streets", "City Subway", "City Museum", "City Library", "City Underpass", "City Abandoned", "Dusty Room", "Chapel", "Smallwater Room" }; /* * Update reverb setting without apply */ static void AL_SetReverb(int reverb_effect) { EFXEAXREVERBPROPERTIES reverb; if (reverb_effect == lastreverteffect || reverb_effect < 0 || reverb_effect >= EFX_REVERB_SIZE) { return; } reverb = ReverbPresets[reverb_effect]; lastreverteffect = reverb_effect; qalEffectf(ReverbEffect[QAL_REVERB_EFFECT], AL_REVERB_DENSITY, reverb.flDensity); qalEffectf(ReverbEffect[QAL_REVERB_EFFECT], AL_REVERB_DIFFUSION, reverb.flDiffusion); qalEffectf(ReverbEffect[QAL_REVERB_EFFECT], AL_REVERB_GAIN, reverb.flGain); qalEffectf(ReverbEffect[QAL_REVERB_EFFECT], AL_REVERB_GAINHF, reverb.flGainHF); qalEffectf(ReverbEffect[QAL_REVERB_EFFECT], AL_REVERB_DECAY_TIME, reverb.flDecayTime); qalEffectf(ReverbEffect[QAL_REVERB_EFFECT], AL_REVERB_DECAY_HFRATIO, reverb.flDecayHFRatio); qalEffectf(ReverbEffect[QAL_REVERB_EFFECT], AL_REVERB_REFLECTIONS_GAIN, reverb.flReflectionsGain); qalEffectf(ReverbEffect[QAL_REVERB_EFFECT], AL_REVERB_REFLECTIONS_DELAY, reverb.flReflectionsDelay); qalEffectf(ReverbEffect[QAL_REVERB_EFFECT], AL_REVERB_LATE_REVERB_GAIN, reverb.flLateReverbGain); qalEffectf(ReverbEffect[QAL_REVERB_EFFECT], AL_REVERB_LATE_REVERB_DELAY, reverb.flLateReverbDelay); qalEffectf(ReverbEffect[QAL_REVERB_EFFECT], AL_REVERB_AIR_ABSORPTION_GAINHF, reverb.flAirAbsorptionGainHF); qalEffectf(ReverbEffect[QAL_REVERB_EFFECT], AL_REVERB_ROOM_ROLLOFF_FACTOR, reverb.flRoomRolloffFactor); qalEffecti(ReverbEffect[QAL_REVERB_EFFECT], AL_REVERB_DECAY_HFLIMIT, reverb.iDecayHFLimit); qalAuxiliaryEffectSloti(ReverbEffectSlot[QAL_REVERB_EFFECT], AL_EFFECTSLOT_EFFECT, ReverbEffect[QAL_REVERB_EFFECT]); } /* * Apply Reverb effect */ static void AL_ApplyReverb(void) { vec3_t mins = {0, 0, 0}, maxs = {0, 0, 0}; vec3_t direction[6] = { {1000000, 0, 0}, /* forward */ {-1000000, 0, 0}, /* backward */ {0, 1000000, 0}, /* left */ {0, -1000000, 0}, /* right */ { 0, 0, 1000000 }, /* up */ { 0, 0, -1000000 }, /* down */ }; float average = 0; int i; if (ReverbEffect[QAL_REVERB_EFFECT] == 0) return; for (i=0; i < 6; i++) { trace_t trace; vec3_t length; trace = CM_BoxTrace(listener_origin, direction[i], mins, maxs, 0, MASK_DEADSOLID); VectorSubtract(trace.endpos, listener_origin, length); average += VectorLength(length); } /* Use five as down is near to us */ average /= 5; if (average < 100) { AL_SetReverb(41); } if (average >= 100 && average < 200) { AL_SetReverb(26); } if (average >= 200 && average < 330) { AL_SetReverb(5); } if (average >= 330 && average < 450) { AL_SetReverb(12); } if (average >= 450 && average < 650) { AL_SetReverb(18); } if (average >= 650) { AL_SetReverb(17); } } /* * 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 { qboolean source_occluded = false; qboolean reverb_enabled = false; 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)); } if (!snd_is_underwater && s_occlusion_strength->value && underwaterFilter != 0) { trace_t trace; vec3_t mins = { 0, 0, 0 }, maxs = { 0, 0, 0 }; trace = CM_BoxTrace(origin, listener_origin, mins, maxs, 0, MASK_PLAYERSOLID); if (trace.fraction < 1.0 && !(ch->entnum == -1 || ch->entnum == cl.playernum + 1 || !ch->dist_mult) ) { vec3_t distance; float dist; float final; VectorSubtract(origin, listener_origin, distance); dist = VectorLength(distance); final = 1.0 - ((dist / 1000) * (1.0 - s_occlusion_strength->value)); qalSourcef(ch->srcnum, AL_GAIN, Q_min(Q_max(final, 0), 1)); qalSourcei(ch->srcnum, AL_DIRECT_FILTER, underwaterFilter); source_occluded = true; } } if (!source_occluded) { /* Remove filter */ if (!snd_is_underwater) qalSourcei(ch->srcnum, AL_DIRECT_FILTER, 0) ; /* Auto reverb */ if(s_reverb_preset->value == -2) { AL_ApplyReverb(); } /* Forsed reverb effect */ else if (s_reverb_preset->value >= 0) { AL_SetReverb(s_reverb_preset->value); } if(s_reverb_preset->value != -1) /* Non Disabled reverb */ { /* Apply reverb effect */ qalSource3i(ch->srcnum, AL_AUXILIARY_SEND_FILTER, ReverbEffectSlot[QAL_REVERB_EFFECT], 0, AL_FILTER_NULL); reverb_enabled = true; } } if (!reverb_enabled) { /* Disable filtering */ qalSource3i(ch->srcnum, AL_AUXILIARY_SEND_FILTER, 0, 0, AL_FILTER_NULL); } 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 && cl_audiopaused->value) || !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]; 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 */ format = AL_GetFormat(width, channels); /* 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(); } static void AL_UpdateReverb() { int reverb_effect; if (!s_reverb_preset->modified) { return; } s_reverb_preset->modified = false; reverb_effect = s_reverb_preset->value; if (reverb_effect == -2) { Com_Printf("Reverb set to: AUTO\n"); return; } else if (reverb_effect == -1) { Com_Printf("Reverb set to: Disabled\n"); return; } /* We have only EFX_REVERB_SIZE(113) effects */ else if (reverb_effect >= 0 && reverb_effect < EFX_REVERB_SIZE) { Com_Printf("Reverb set to: %s\n", ReverbPresetsNames[reverb_effect]); AL_SetReverb(reverb_effect); } else { int i; Com_Printf("Uknown reverb effect, supported:\n-2: Auto\n-1: Disabled\n"); for (i = 0; i < EFX_REVERB_SIZE; i++) { Com_Printf("%d: %s\n", i, ReverbPresetsNames[i]); } } } static void AL_UpdateUnderwater() { 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; /* Do nothing if we aren't connected to a output device. This is called after increasing paintedtime, because the sound for the current frame needs to be skipped. Otherwise sound and game will become asynchronous and the paint buffer may overflow. */ if (!QAL_RecoverLostDevice()) { return; } /* 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 */ OGG_Stream(); AL_StreamUpdate(); AL_IssuePlaysounds(); AL_UpdateUnderwater(); AL_UpdateReverb(); } /* ----------------------------------------------------------------- */ /* * 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); } AL_SetReverb(22); } /* * 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, AL_FILTER_NULL); } AL_SetReverb(s_reverb_preset->value); } /* ----------------------------------------------------------------- */ /* * 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); } static qboolean AL_Efx_Enabled() { if (qalAuxiliaryEffectSloti && qalDeleteAuxiliaryEffectSlots && qalDeleteEffects && qalDeleteFilters && qalEffectf && qalEffecti && qalFilterf && qalFilteri && qalGenAuxiliaryEffectSlots && qalGenEffects && qalGenFilters) return true; return false; } /* * Set up the underwater filter */ static void AL_InitUnderwaterFilter() { underwaterFilter = 0; if (!AL_Efx_Enabled()) 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; } static void AL_InitReverbEffect(void) { if (!AL_Efx_Enabled()) return; qalGenEffects(QAL_EFX_MAX, ReverbEffect); if (qalGetError() != AL_NO_ERROR) { Com_Printf("Couldn't generate an OpenAL effect!\n"); return; } qalGenAuxiliaryEffectSlots(QAL_EFX_MAX, ReverbEffectSlot); if (qalGetError() != AL_NO_ERROR) { Com_Printf("Couldn't generate an OpenAL auxiliary effect slot!\n"); return; } qalEffecti(ReverbEffect[QAL_REVERB_EFFECT], AL_EFFECT_TYPE, AL_EFFECT_REVERB); AL_SetReverb(s_reverb_preset->value); } /* * 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(); AL_InitReverbEffect(); 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); qalDeleteAuxiliaryEffectSlots(QAL_EFX_MAX, ReverbEffectSlot); qalDeleteEffects(QAL_EFX_MAX, ReverbEffect); 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_8_40/src/client/sound/qal.c000066400000000000000000000443511465112212000202360ustar00rootroot00000000000000/* * Copyright (C) 2012 Yamagi Burmeister * Copyright (C) 2010 skuller.net * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * ======================================================================= * * Low level, platform depended "qal" API implementation. This files * provides functions to load, initialize, shutdown und unload the * OpenAL library and connects the "qal" funtion pointers to the * OpenAL functions. It shopuld work on Windows and unixoid Systems, * other platforms may need an own implementation. This source file * was taken from Q2Pro and modified by the YQ2 authors. * * ======================================================================= */ #ifdef USE_OPENAL #include #include #include #include "../../common/header/common.h" #include "../../client/sound/header/local.h" #include "header/qal.h" static ALCcontext *context; static ALCdevice *device; static cvar_t *al_device; static cvar_t *al_driver; static qboolean hasAlcExtDisconnect; 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; LPALGENEFFECTS qalGenEffects; LPALEFFECTF qalEffectf; LPALEFFECTI qalEffecti; LPALEFFECTFV qalEffectfv; LPALAUXILIARYEFFECTSLOTI qalAuxiliaryEffectSloti; LPALGENAUXILIARYEFFECTSLOTS qalGenAuxiliaryEffectSlots; LPALDELETEAUXILIARYEFFECTSLOTS qalDeleteAuxiliaryEffectSlots; LPALDELETEEFFECTS qalDeleteEffects; /* * 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); } } } /* * Checks if the output device is still connected. Returns true * if it is, false otherwise. Should be called every frame, if * disconnected a 'snd_restart' is injected after waiting for 2.5 * seconds. * * This is mostly a work around for broken Sound driver. For * example the _good_ Intel display driver for Windows 10 * destroys the DisplayPort sound device when the display * resolution changes and recreates it after an unspecified * amount of time... */ qboolean QAL_RecoverLostDevice() { static int discoCount = 0; if (!hasAlcExtDisconnect) { return true; } if (discoCount == 0) { ALCint connected; qalcGetIntegerv(device, ALC_CONNECTED, 1, &connected); if (!connected) { discoCount++; return false; } else { return true; } } else { /* Wait for about 2.5 seconds before trying to reconnect. */ if (discoCount < (int)(Cvar_VariableValue("cl_maxfps") * 2.5)) { discoCount++; return false; } else { Cbuf_AddText("snd_restart\n"); discoCount = 0; return false; } } } /* * 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); /* DEFAULT_OPENAL_DRIVER is defined at compile time via the compiler */ al_driver = Cvar_Get("al_driver", DEFAULT_OPENAL_DRIVER, CVAR_ARCHIVE); if (strstr(al_driver->string, "..") || strchr(al_driver->string, ':') || strchr(al_driver->string, '/') || strchr(al_driver->string, '\\')) { Com_Printf("al_driver must not contain '..', ':', '/' or '\': %s\n", al_driver->string); return false; } /* Load the library */ Com_Printf("Loading library: %s\n", al_driver->string); 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) /* 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 = (LPALGENFILTERS)qalGetProcAddress("alGenFilters"); qalFilteri = (LPALFILTERI)qalGetProcAddress("alFilteri"); qalFilterf = (LPALFILTERF)qalGetProcAddress("alFilterf"); qalDeleteFilters = (LPALDELETEFILTERS)qalGetProcAddress("alDeleteFilters"); qalAuxiliaryEffectSloti = (LPALAUXILIARYEFFECTSLOTI)qalGetProcAddress("alAuxiliaryEffectSloti"); qalGenEffects = (LPALGENEFFECTS)qalGetProcAddress("alGenEffects"); qalGenAuxiliaryEffectSlots = (LPALGENAUXILIARYEFFECTSLOTS)qalGetProcAddress("alGenAuxiliaryEffectSlots"); qalEffectf = (LPALEFFECTF)qalGetProcAddress("alEffectf"); qalEffecti = (LPALEFFECTI)qalGetProcAddress("alEffecti"); qalEffectfv = (LPALEFFECTFV)qalGetProcAddress("alEffectfv"); qalDeleteAuxiliaryEffectSlots = (LPALDELETEAUXILIARYEFFECTSLOTS)qalGetProcAddress("alDeleteAuxiliaryEffectSlots"); qalDeleteEffects = (LPALDELETEEFFECTS)qalGetProcAddress("alDeleteEffects"); } else { qalGenFilters = NULL; qalFilteri = NULL; qalFilterf = NULL; qalDeleteFilters = NULL; qalAuxiliaryEffectSloti = NULL; qalGenEffects = NULL; qalGenAuxiliaryEffectSlots = NULL; qalEffectf = NULL; qalEffecti = NULL; qalEffectfv = NULL; qalDeleteAuxiliaryEffectSlots = NULL; qalDeleteEffects = NULL; } Com_Printf("ok\n"); /* Print OpenAL information */ Com_Printf("\n"); QAL_SoundInfo(); Com_Printf("\n"); /* Check extensions */ if (qalcIsExtensionPresent(device, "ALC_EXT_disconnect") != AL_FALSE) { hasAlcExtDisconnect = true; } return true; } #endif /* USE_OPENAL */ yquake2-QUAKE2_8_40/src/client/sound/sdl.c000066400000000000000000000766301465112212000202500ustar00rootroot00000000000000/* * 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 USE_SDL3 #include #else #include #endif /* 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 */ static cvar_t *s_sdldriver; static 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; /* ------------------------------------------------------------------ */ 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; } } /* * Transfers a mixed "paint buffer" to * the SDL output buffer and places it * at the appropriate position. */ static void SDL_TransferPaintBuffer(int endtime) { int out_mask; int val; 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)) { int ls_paintedtime; snd_p = (int *)paintbuffer; ls_paintedtime = paintedtime; while (ls_paintedtime < endtime) { int i; short *snd_out; int snd_linear_count; int lpos; 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 { int count; int step; int *p; int out_idx; 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; } // FIXME: val might be negative and right-shifting it is implementation defined // on x86 it does sign extension (=> fills up with 1 bits from the left) // so this /might/ break on other platforms - if it does, look at this code again. out[out_idx] = (val >> 8) + 128; out_idx = (out_idx + 1) & out_mask; } } } } /* * Mixes an 8 bit sample into a channel. */ static void SDL_PaintChannelFrom8(channel_t *ch, sfxcache_t *sc, int count, int offset) { 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++) { int data; data = sfx[i]; samp->left += lscale[data]; samp->right += rscale[data]; } ch->pos += count; } /* * Mixes an 16 bit sample into a channel */ static void SDL_PaintChannelFrom16(channel_t *ch, sfxcache_t *sc, int count, int offset) { 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++) { int data; int left, right; 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. */ static void SDL_PaintChannels(int endtime) { int i; channel_t *ch; sfxcache_t *sc; int ltime, count; playsound_t *ps; snd_vol = (int)(s_volume->value * 256); while (paintedtime < endtime) { int end; /* 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) { 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 stop; stop = (end < s_rawend) ? end : s_rawend; for (i = paintedtime; i < stop; i++) { int s; 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. */ static 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; if ((cls.state != ca_active) || (cl_paused->value && cl_audiopaused->value) || !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]; /* 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; if (sound_started == SS_NOT) { return; } s_rawend = 0; if (sound.samplebits == 8) { clear = 0x80; } else { clear = 0; } #ifndef USE_SDL3 SDL_LockAudio(); #endif if (sound.buffer) { int i; unsigned char *ptr = sound.buffer; i = sound.samples * sound.samplebits / 8; while (i--) { *ptr = clear; ptr++; } } #ifndef USE_SDL3 SDL_UnlockAudio(); #endif } /* * Calculates the absolute timecode * of current playback. */ static 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. */ static void SDL_UpdateScaletable(void) { int i; 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++) { int j, scale; 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, short volume, int begin_length, int end_length, int attack_length, int fade_length) { float stepscale; int i; int len; int sample; 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 = info->channels - 1; sc->length = (int)(info->samples / stepscale); sc->speed = sound.speed; sc->volume = volume; sc->begin = begin_length * 1000 / info->rate; sc->end = end_length * 1000 / info->rate; sc->fade = fade_length * 1000 / info->rate; sc->attack = attack_length * 1000 / info->rate; if ((int)(info->samples / stepscale) == 0) { Com_Printf("%s: Invalid sound file '%s' (zero length)\n", __func__, 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++) { int srcsample; 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; 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) { int total; 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); } /* stream music */ OGG_Stream(); if (!sound.buffer) { return; } /* Mix the samples */ #ifndef USE_SDL3 SDL_LockAudio(); #endif /* Updates SDL time */ SDL_UpdateSoundtime(); if (!soundtime) { return; } /* check to make sure that we haven't overshot */ if (paintedtime < soundtime) { Com_DPrintf("%s: overflow\n", __func__); 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); #ifndef USE_SDL3 SDL_UnlockAudio(); #endif } /* ------------------------------------------------------------------ */ /* * 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; } } #ifdef USE_SDL3 /* Global stream handle. */ static SDL_AudioStream *stream; /* Wrapper function, ties the old existing callback logic * from the SDL 1.2 days and later fiddled into SDL 2 to * a SDL 3 compatible callback... */ static void SDL_SDL3Callback(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount) { if (additional_amount > 0) { Uint8 *data = SDL_stack_alloc(Uint8, additional_amount); if (data) { SDL_Callback(userdata, data, additional_amount); SDL_PutAudioStreamData(stream, data, additional_amount); SDL_stack_free(data); } } } /* * Initializes the SDL sound * backend and sets up SDL. */ qboolean SDL_BackendInit(void) { char reqdriver[128]; SDL_AudioSpec spec; int samples, 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 s_sdldriver = (Cvar_Get("s_sdldriver", "directsound", CVAR_ARCHIVE)); #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; } } const char* drivername = SDL_GetCurrentAudioDriver(); if(drivername == NULL) { drivername = "(UNKNOWN)"; } Com_Printf("SDL audio driver is \"%s\".\n", drivername); memset(&samples, '\0', sizeof(samples)); /* Users are stupid */ if ((sndbits != 16) && (sndbits != 8)) { sndbits = 16; } if (sndfreq == 48) { spec.freq = 48000; } else if (sndfreq == 44) { spec.freq = 44100; } else if (sndfreq == 22) { spec.freq = 22050; } else if (sndfreq == 11) { spec.freq = 11025; } spec.format = ((sndbits == 16) ? SDL_AUDIO_S16 : SDL_AUDIO_U8); if (spec.freq <= 11025) { samples = 256; } else if (spec.freq <= 22050) { samples = 512; } else if (spec.freq <= 44100) { samples = 1024; } else { samples = 2048; } spec.channels = sndchans; /* Okay, let's try our luck */ stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_OUTPUT, &spec, SDL_SDL3Callback, NULL); if (stream == NULL) { 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 = spec.format & 0xFF; backend->channels = spec.channels; tmp = (samples * spec.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 = spec.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_ResumeAudioDevice(SDL_GetAudioStreamDevice(stream)); 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_PauseAudioDevice(SDL_GetAudioStreamDevice(stream)); SDL_DestroyAudioStream(stream); 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"); } #else /* * 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 s_sdldriver = (Cvar_Get("s_sdldriver", "directsound", CVAR_ARCHIVE)); #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; } } const char* drivername = SDL_GetCurrentAudioDriver(); if(drivername == NULL) { drivername = "(UNKNOWN)"; } 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"); } #endif yquake2-QUAKE2_8_40/src/client/sound/sound.c000066400000000000000000001017071465112212000206100ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 "header/local.h" #include "header/qal.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 /* Maximum length (seconds) of audio data to test for silence. */ #define S_MAX_LEN_TO_TEST_FOR_SILENCE_S (2) /* Minimum length (milliseconds) of audio data to treat as silence. */ #define S_MIN_LEN_TO_TREAT_AS_SILENCE_MS (3) /* Minimum amplitude absolute value of unsigned 8-bit audio data to treat as silence. */ #define S_MIN_ABS_AMP_8_TO_TREAT_AS_SILENCE (1) /* Minimum amplitude absolute value of signed 16-bit audio data to treat as silence. */ #define S_MIN_ABS_AMP_16_TO_TREAT_AS_SILENCE (2) vec3_t listener_origin; vec3_t listener_forward; vec3_t listener_right; vec3_t listener_up; static playsound_t s_playsounds[MAX_PLAYSOUNDS]; static 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; cvar_t* s_occlusion_strength; cvar_t* s_reverb_preset; static cvar_t* s_ps_sorting; static cvar_t* s_feedback_kind; channel_t channels[MAX_CHANNELS]; static int num_sfx; static int sound_max; int paintedtime; int s_numchannels; int s_rawend; static int s_registration_sequence = 0; portable_samplepair_t s_rawsamples[MAX_RAW_SAMPLES]; static sfx_t known_sfx[MAX_SFX]; sndstarted_t sound_started = SS_NOT; sound_t sound; static qboolean s_registering; qboolean s_active; qboolean snd_is_underwater; qboolean snd_is_underwater_enabled; /* ----------------------------------------------------------------- */ static qboolean S_IsShortSilence(const wavinfo_t* info, const void* raw_data) { /* Test only mono-audio data. */ if (info->channels != 1) { return false; } /* Treat it as silence if sample count too small. */ if (((info->samples * 1000) / info->rate) <= S_MIN_LEN_TO_TREAT_AS_SILENCE_MS) { return true; } /* Test only several second's of audio data. */ if (info->samples > (info->rate * S_MAX_LEN_TO_TEST_FOR_SILENCE_S)) { return false; } if (info->width == 1) { /* Unsigned 8-bit audio data. */ const unsigned char* samples = (const unsigned char*)raw_data; const int sample_count = info->samples * info->width; int i; for (i = 0; i < sample_count; ++i) { const int sample = samples[i] - 0x80; if (abs(sample) > S_MIN_ABS_AMP_8_TO_TREAT_AS_SILENCE) { return false; } } } else if (info->width == 2) { /* Signed 16-bit audio data. */ const short* samples = (const short*)raw_data; const int sample_count = info->samples * info->width; int i; for (i = 0; i < sample_count; ++i) { const int sample = LittleShort(samples[i]); if (abs(sample) > S_MIN_ABS_AMP_16_TO_TREAT_AS_SILENCE) { return false; } } } else { /* Unsupported bit depth. */ return false; } return true; } static qboolean S_IsSilencedMuzzleFlash(const wavinfo_t* info, const void* raw_data, const char* name) { /* Skip the prefix. */ static const size_t base_sound_string_length = 6; //strlen("sound/"); const char* base_name = name + base_sound_string_length; /* Match to well-known muzzle flash sound names. */ const qboolean is_name_matched = strcmp(base_name, "weapons/bfg__f1y.wav") == 0 || strcmp(base_name, "weapons/blastf1a.wav") == 0 || strcmp(base_name, "weapons/disint2.wav") == 0 || strcmp(base_name, "weapons/grenlf1a.wav") == 0 || strcmp(base_name, "weapons/grenlr1b.wav") == 0 || strcmp(base_name, "weapons/hyprbf1a.wav") == 0 || strcmp(base_name, "weapons/machgf1b.wav") == 0 || strcmp(base_name, "weapons/machgf2b.wav") == 0 || strcmp(base_name, "weapons/machgf3b.wav") == 0 || strcmp(base_name, "weapons/machgf4b.wav") == 0 || strcmp(base_name, "weapons/machgf5b.wav") == 0 || strcmp(base_name, "weapons/nail1.wav") == 0 || strcmp(base_name, "weapons/plasshot.wav") == 0 || strcmp(base_name, "weapons/railgf1a.wav") == 0 || strcmp(base_name, "weapons/rippfire.wav") == 0 || strcmp(base_name, "weapons/rocklf1a.wav") == 0 || strcmp(base_name, "weapons/rocklr1b.wav") == 0 || strcmp(base_name, "weapons/shotg2.wav") == 0 || strcmp(base_name, "weapons/shotgf1b.wav") == 0 || strcmp(base_name, "weapons/shotgr1b.wav") == 0 || strcmp(base_name, "weapons/sshotf1b.wav") == 0 || false ; if (!is_name_matched) { return false; } /* Now check for silence. */ if (!S_IsShortSilence(info, raw_data)) { return false; } return true; } static void S_LoadVorbis(const char *path, const char* name, wavinfo_t *info, void **buffer) { int len; char namewe[256]; char filename[MAX_QPATH]; const char* ext; if (!path) { return; } ext = COM_FileExtension(path); if(!ext[0]) { /* file has no extension */ return; } len = strlen(path); if (len < 5) { return; } /* Remove the extension */ memset(namewe, 0, sizeof(namewe)); memcpy(namewe, path, len - (strlen(ext) + 1)); /* Combine with ogg */ Q_strlcpy(filename, namewe, sizeof(filename)); /* Add the extension */ Q_strlcat(filename, ".ogg", sizeof(filename)); OGG_LoadAsWav(filename, info, buffer); } static void S_GetVolume(const byte *data, int sound_length, int width, double *sound_volume) { /* update sound volume */ *sound_volume = 0; if (width == 2) { short *sound_data = (short *)data; short *sound_end = sound_data + sound_length; while (sound_data < sound_end) { short sound_sample = LittleShort(*sound_data); *sound_volume += (sound_sample * sound_sample); sound_data ++; } } else if (width == 1) { byte *sound_data = (byte *)data; byte *sound_end = sound_data + sound_length; while (sound_data < sound_end) { // normilize to 16bit sound; short sound_sample = *sound_data << 8; *sound_volume += (sound_sample * sound_sample); sound_data ++; } } if (sound_length != 0) { *sound_volume /= sound_length; *sound_volume = sqrtf(*sound_volume); } } static void S_GetStatistics(const byte *data, int sound_length, int width, int channels, double sound_volume, int *begin_length, int *end_length, int *attack_length, int *fade_length) { /* attack length */ short sound_max = 0; /* calculate max value*/ if (width == 2) { short *sound_data = (short *)data; short *sound_end = sound_data + sound_length; while (sound_data < sound_end) { short sound_sample = LittleShort(*sound_data); if (sound_max < abs(sound_sample)) { sound_max = abs(sound_sample); } sound_data ++; } } else if (width == 1) { byte *sound_data = (byte *)data; byte *sound_end = sound_data + sound_length; while (sound_data < sound_end) { // normilize to 16bit sound; short sound_sample = *sound_data << 8; if (sound_max < abs(sound_sample)) { sound_max = abs(sound_sample); } sound_data ++; } } // use something in middle sound_max = (sound_max + sound_volume) / 2; // calculate attack/fade length if (width == 2) { // calculate attack/fade length short *sound_data = (short *)data; short *delay_data = sound_data; short *fade_data = sound_data; short *sound_end = sound_data + sound_length; short sound_sample = 0; short sound_treshold = sound_max / 2; /* delay calculate */ do { sound_sample = LittleShort(*sound_data); sound_data ++; } while (sound_data < sound_end && abs(sound_sample) < sound_treshold); /* delay_data == (short *)(data + info.dataofs) */ *begin_length = (sound_data - delay_data) / channels; delay_data = sound_data; fade_data = sound_data; /* attack calculate */ do { sound_sample = LittleShort(*sound_data); sound_data ++; } while (sound_data < sound_end && abs(sound_sample) < sound_max); /* fade_data == delay_data */ *attack_length = (sound_data - delay_data) / channels; fade_data = sound_data; /* end calculate */ sound_data = sound_end; do { sound_data --; sound_sample = LittleShort(*sound_data); } while (sound_data > fade_data && abs(sound_sample) < sound_treshold); *end_length = (sound_end - sound_data) / channels; sound_end = sound_data; /* fade calculate */ do { sound_data --; sound_sample = LittleShort(*sound_data); } while (sound_data > fade_data && abs(sound_sample) < sound_max); *fade_length = (sound_end - sound_data) / channels; } else if (width == 1) { // calculate attack/fade length byte *sound_data = (byte *)data; byte *delay_data = sound_data; byte *fade_data = sound_data; byte *sound_end = sound_data + sound_length; short sound_sample = 0; short sound_treshold = sound_max / 2; /* delay calculate */ do { // normilize to 16bit sound; sound_sample = *sound_data << 8; sound_data ++; } while (sound_data < sound_end && abs(sound_sample) < sound_treshold); /* delay_data == (short *)(data + info.dataofs) */ *begin_length = (sound_data - delay_data) / channels; delay_data = sound_data; fade_data = sound_data; /* attack calculate */ do { // normilize to 16bit sound; sound_sample = *sound_data << 8; sound_data ++; } while (sound_data < sound_end && abs(sound_sample) < sound_max); /* fade_data == delay_data */ *attack_length = (sound_data - delay_data) / channels; fade_data = sound_data; /* end calculate */ sound_data = sound_end; do { sound_data --; // normilize to 16bit sound; sound_sample = *sound_data << 8; } while (sound_data > fade_data && abs(sound_sample) < sound_treshold); *end_length = (sound_end - sound_data) / channels; sound_end = sound_data; /* fade calculate */ do { sound_data --; // normilize to 16bit sound; sound_sample = *sound_data << 8; } while (sound_data > fade_data && abs(sound_sample) < sound_max); *fade_length = (sound_end - sound_data) / channels; } } /* * Loads one sample into memory */ sfxcache_t * S_LoadSound(sfx_t *s) { char namebuffer[MAX_QPATH]; byte *data = NULL; wavinfo_t info; sfxcache_t *sc; double sound_volume = 0; int begin_length = 0; int attack_length = 0; int fade_length = 0; int end_length = 0; 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); } S_LoadVorbis(namebuffer, s->name, &info, (void **)&data); // can't load ogg file if (!data) { int size = FS_LoadFile(namebuffer, (void **)&data); if (data) { info = GetWavinfo(s->name, data, size); } } if (!data) { s->cache = NULL; Com_DPrintf("Couldn't load %s\n", namebuffer); return NULL; } /* Com_Printf("%s: rate:%d\n\twidth:%d\n\tchannels:%d\n\tloopstart:%d\n\tsamples:%d\n\tdataofs:%d\n", s->name, info.rate, info.width, info.channels, info.loopstart, info.samples, info.dataofs); */ if (info.channels < 1 || info.channels > 2) { Com_Printf("%s has an invalid number of channels\n", s->name); FS_FreeFile(data); return NULL; } if (S_IsSilencedMuzzleFlash(&info, data, namebuffer)) { s->is_silenced_muzzle_flash = true; } S_GetVolume(data + info.dataofs, info.samples, info.width, &sound_volume); S_GetStatistics(data + info.dataofs, info.samples, info.width, info.channels, sound_volume, &begin_length, &end_length, &attack_length, &fade_length); #if USE_OPENAL if (sound_started == SS_OAL) { sc = AL_UploadSfx(s, &info, data + info.dataofs, sound_volume, begin_length, end_length, attack_length, fade_length); } else #endif { if (sound_started == SS_SDL) { if (!SDL_Cache(s, &info, data + info.dataofs, sound_volume, begin_length, end_length, attack_length, fade_length)) { Com_Printf("Pansen!\n"); FS_FreeFile(data); return NULL; } } } FS_FreeFile(data); return sc; } /* * Returns the name of a sound */ static sfx_t * S_FindName(char *name, qboolean create) { int i; sfx_t *sfx; if (!name) { Com_Error(ERR_FATAL, "%s: NULL\n", __func__); } if (!name[0]) { Com_Error(ERR_FATAL, "%s: empty name\n", __func__); } if (strlen(name) >= MAX_QPATH) { Com_Error(ERR_FATAL, "%s :Sound name too long: %s", __func__, 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: out of sfx_t", __func__); } num_sfx++; } sfx = &known_sfx[i]; sfx->truename = NULL; strcpy(sfx->name, name); sfx->registration_sequence = s_registration_sequence; sfx->is_silenced_muzzle_flash = false; return sfx; } /* * Registers an alias name * for a sound */ static 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: out of sfx_t", __func__); } 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 == SS_NOT) { return NULL; } sfx = S_FindName(name, true); sfx->registration_sequence = s_registration_sequence; if (!s_registering) { S_LoadSound(sfx); } return sfx; } static struct sfx_s * S_RegisterSexedSound(entity_state_t *ent, char *base) { int n; struct sfx_s *sfx; char model[MAX_QPATH]; char sexedFilename[MAX_QPATH]; /* determine what model the client is using */ model[0] = 0; n = CS_PLAYERSKINS + ent->number - 1; if (cl.configstrings[n][0]) { char *p; 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) { int len; /* 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 { char maleFilename[MAX_QPATH]; /* 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; } static qboolean S_HasFreeSpace(void) { sfx_t *sfx; int i, used; used = 0; /* check used slots */ for (i = 0, sfx = known_sfx; i < num_sfx; i++, sfx++) { if (!sfx->name[0]) { continue; } if (sfx->registration_sequence == s_registration_sequence) { used ++; } } if (sound_max < used) { sound_max = used; } // should same size of free slots as currently used return (num_sfx + used) < MAX_SFX; } /* * Called after registering of * sound has ended */ void S_EndRegistration(void) { int i; sfx_t *sfx; if (!S_HasFreeSpace()) { /* 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: entchannel<0", __func__); } /* 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 */ static 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 */ static 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); } if (s_ps_sorting->value == 3 && ps->sfx->is_silenced_muzzle_flash) { S_FreePlaysound(ps); return; } /* 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 == SS_NOT) { return; } /* A hack to prevent temporary entities generating sounds when the sound backend is not active and the game is not paused */ if (s_active == false) { 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] && s_feedback_kind->value == 1) { vec3_t orientation, direction; vec_t distance_direction; int dir_x, dir_y, dir_z; int effect_volume = -1; VectorSubtract(listener_forward, listener_up, orientation); // with !fixed we have all sounds related directly to player, // e.g. players fire, pain, menu if (!ps->fixed_origin) { VectorCopy(orientation, direction); distance_direction = 0; } else { VectorSubtract(listener_origin, ps->origin, direction); distance_direction = VectorLength(direction); } VectorNormalize(direction); VectorNormalize(orientation); dir_x = 16 * orientation[0] * direction[0]; dir_y = 16 * orientation[1] * direction[1]; dir_z = 16 * orientation[2] * direction[2]; if (sfx->cache) { int effect_duration; effect_duration = sfx->cache->length; if (sfx->cache->stereo) { effect_duration /= 2; } /* sound near player has 16 points */ effect_volume = sfx->cache->volume; /* remove silence duration in the end of sound effect */ effect_duration -= sfx->cache->end; Haptic_Feedback( sfx->name, effect_volume, effect_duration, sfx->cache->begin, sfx->cache->attack, sfx->cache->fade, dir_x, dir_y, dir_z, distance_direction); } } else if (sfx->name[0] && s_feedback_kind->value == 0) { vec3_t direction = {0}; unsigned int effect_duration = 0; unsigned short int effect_volume = 0; // with !ps->fixed we have all sounds related directly to player, // e.g. players fire, pain, menu // else, they come from the environment if (ps->fixed_origin) { VectorSubtract(listener_origin, ps->origin, direction); } if (sfx->cache) { effect_duration = sfx->cache->length; if (sfx->cache->stereo) { effect_duration /= 2; } // The following may be ugly: cache length in SDL is much, much bigger // than the one in OpenAL, so much that it's definitely not in ms. // If that changes in the future, this must be removed. if (sound_started == SS_SDL) { effect_duration /= 45; } effect_volume = sfx->cache->volume; } Controller_Rumble(sfx->name, direction, !ps->fixed_origin, effect_duration, effect_volume); } 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; } } cvar_t *game = Cvar_Get("game", "", CVAR_LATCH | CVAR_SERVERINFO); const qboolean is_mission_pack = (strcmp(game->string, "") == 0) || (strcmp(game->string, "rogue") == 0) || (strcmp(game->string, "xatrix") == 0); if ((s_ps_sorting->value == 1 && is_mission_pack) || s_ps_sorting->value == 2 || s_ps_sorting->value == 3) { for (sort = s_pendingplays.next; sort != &s_pendingplays && sort->begin <= ps->begin; sort = sort->next) { } } else if (!is_mission_pack && s_ps_sorting->value == 1) { for (sort = s_pendingplays.next; sort != &s_pendingplays && sort->begin < ps->begin; sort = sort->next) { } } else { /* No sorting. Just append at the end. */ sort = s_pendingplays.prev; } 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 == SS_NOT) { return; } sfx = S_RegisterSound(sound); if (!sfx) { Com_Printf("%s: can't cache %s\n", __func__, 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 == SS_NOT) { 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; for (i = 0; i < cl.frame.num_entities; i++) { int num; entity_state_t *ent; 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 == SS_NOT) { 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 == SS_NOT) { return; } if (s_active == false) { 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. */ static void S_Play(void) { int i; i = 1; while (i < Cmd_Argc()) { char name[256]; sfx_t *sfx; 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 */ static void S_SoundList(void) { int i; sfx_t *sfx; sfxcache_t *sc; int size, total, used; int numsounds; qboolean freeup; total = 0; used = 0; numsounds = 0; for (sfx = known_sfx, i = 0; i < num_sfx; i++, sfx++) { if (!sfx->name[0]) { continue; } if (sfx->registration_sequence == s_registration_sequence) { used++; } sc = sfx->cache; if (sc) { size = sc->length * sc->width * (sc->stereo + 1); total += size; Com_Printf("%s(%2db) %8i(%d ch) %s %2.1f dB %.1fs:%.1f..%.1f..%.1f..%.1f\n", sc->loopstart != -1 ? "L" : " ", sc->width * 8, size, (sc->stereo + 1), sfx->name, 10 * log10((float)sc->volume / (2 << 15)), (float)sc->length / 1000, (float)sc->begin / 1000, (float)sc->attack / 1000, (float)sc->fade / 1000, (float)sc->end / 1000); } 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); freeup = S_HasFreeSpace(); Com_Printf("Used %d of %d sounds%s.\n", used, sound_max, freeup ? ", has free space" : ""); } /* ----------------------------------------------------------------- */ /* * Prints information about the * active sound backend */ static void S_SoundInfo_f(void) { if (sound_started == SS_NOT) { Com_Printf("Sound system not started\n"); return; } #if USE_OPENAL if (sound_started == SS_OAL) { QAL_SoundInfo(); } else #endif { SDL_SoundInfo(); } } /* * Activate or deactivate sound backend */ void S_Activate(qboolean active) { s_active = active; if (active == false) { S_StopAllSounds(); } } /* * 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", "0", CVAR_ARCHIVE); s_ps_sorting = Cvar_Get("s_ps_sorting", "1", CVAR_ARCHIVE); /* Reverb and occlusion is fully disabled by default */ s_reverb_preset = Cvar_Get("s_reverb_preset", "-1", CVAR_ARCHIVE); s_occlusion_strength = Cvar_Get("s_occlusion_strength", "0", CVAR_ARCHIVE); /* Feedback kind: 0 - rumble, 1 - haptic */ s_feedback_kind = Cvar_Get("s_feedback_kind", "0", CVAR_ARCHIVE); Cmd_AddCommand("play", S_Play); Cmd_AddCommand("stopsound", S_StopAllSounds); Cmd_AddCommand("soundlist", S_SoundList); Cmd_AddCommand("soundinfo", S_SoundInfo_f); #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; sound_max = 0; s_active = true; OGG_Init(); 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 == SS_NOT) { return; } S_StopAllSounds(); OGG_Shutdown(); /* 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"); } yquake2-QUAKE2_8_40/src/client/sound/wave.c000066400000000000000000000072661465112212000204270ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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" static byte *data_p; static byte *iff_end; static byte *last_chunk; static byte *iff_data; static int iff_chunk_len; static short GetLittleShort(void) { short val = 0; val = *data_p; val = val + (*(data_p + 1) << 8); data_p += 2; return val; } static 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; } static 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; } } } static void FindChunk(char *name) { last_chunk = iff_data; FindNextChunk(name); } wavinfo_t GetWavinfo(char *name, byte *wav, int wavlength) { wavinfo_t info; 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)) { int i; /* 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, "%s: Sound %s has a bad loop length", __func__, name); } } else { info.samples = samples; } info.dataofs = (int)(data_p - wav); return info; } yquake2-QUAKE2_8_40/src/client/vid/000077500000000000000000000000001465112212000167405ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/vid/glimp_sdl2.c000066400000000000000000000464651465112212000211570ustar00rootroot00000000000000/* * Copyright (C) 2010 Yamagi Burmeister * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * ======================================================================= * * This is the client side of the render backend, implemented trough SDL. * The SDL window and related functrion (mouse grap, fullscreen switch) * are implemented here, everything else is in the renderers. * * ======================================================================= */ #include "../../common/header/common.h" #include "header/ref.h" #include #include float glimp_refreshRate = -1.0f; static cvar_t *vid_displayrefreshrate; static cvar_t *vid_displayindex; static cvar_t *vid_highdpiaware; static cvar_t *vid_rate; static int last_flags = 0; static int last_display = 0; static int last_position_x = SDL_WINDOWPOS_UNDEFINED; static int last_position_y = SDL_WINDOWPOS_UNDEFINED; static SDL_Window* window = NULL; static qboolean initSuccessful = false; static char **displayindices = NULL; static int num_displays = 0; /* * Resets the display index Cvar if out of bounds */ static void ClampDisplayIndexCvar(void) { if (!vid_displayindex) { // uninitialized render? return; } if (vid_displayindex->value < 0 || vid_displayindex->value >= num_displays) { Cvar_SetValue("vid_displayindex", 0); } } static void ClearDisplayIndices(void) { if ( displayindices ) { for ( int i = 0; i < num_displays; i++ ) { free( displayindices[ i ] ); } free( displayindices ); displayindices = NULL; } } static qboolean CreateSDLWindow(int flags, int w, int h) { if (SDL_WINDOWPOS_ISUNDEFINED(last_position_x) || SDL_WINDOWPOS_ISUNDEFINED(last_position_y) || last_position_x < 0 ||last_position_y < 24) { last_position_x = last_position_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY((int)vid_displayindex->value); } /* Force the window to minimize when focus is lost. This was the * default behavior until SDL 2.0.12 and changed with 2.0.14. * The windows staying maximized has some odd implications for * window ordering under Windows and some X11 window managers * like kwin. See: * * https://github.com/libsdl-org/SDL/issues/4039 * * https://github.com/libsdl-org/SDL/issues/3656 */ SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "1"); window = SDL_CreateWindow("Yamagi Quake II", last_position_x, last_position_y, w, h, flags); if (window) { /* save current display as default */ last_display = SDL_GetWindowDisplayIndex(window); SDL_GetWindowPosition(window, &last_position_x, &last_position_y); /* Check if we're really in the requested diplay mode. There is (or was) an SDL bug were SDL switched into the wrong mode without giving an error code. See the bug report for details: https://bugzilla.libsdl.org/show_bug.cgi?id=4700 */ SDL_DisplayMode real_mode; if ((flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) == SDL_WINDOW_FULLSCREEN) { if (SDL_GetWindowDisplayMode(window, &real_mode) != 0) { SDL_DestroyWindow(window); window = NULL; Com_Printf("Can't get display mode: %s\n", SDL_GetError()); return false; } } /* SDL_WINDOW_FULLSCREEN_DESKTOP implies SDL_WINDOW_FULLSCREEN! */ if (((flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) == SDL_WINDOW_FULLSCREEN) && ((real_mode.w != w) || (real_mode.h != h))) { Com_Printf("Current display mode isn't requested display mode\n"); Com_Printf("Likely SDL bug #4700, trying to work around it\n"); /* Mkay, try to hack around that. */ SDL_DisplayMode wanted_mode = {0}; wanted_mode.w = w; wanted_mode.h = h; if (SDL_SetWindowDisplayMode(window, &wanted_mode) != 0) { SDL_DestroyWindow(window); window = NULL; Com_Printf("Can't force resolution to %ix%i: %s\n", w, h, SDL_GetError()); return false; } /* The SDL doku says, that SDL_SetWindowSize() shouldn't be used on fullscreen windows. But at least in my test with SDL 2.0.9 the subsequent SDL_GetWindowDisplayMode() fails if I don't call it. */ SDL_SetWindowSize(window, wanted_mode.w, wanted_mode.h); if (SDL_GetWindowDisplayMode(window, &real_mode) != 0) { SDL_DestroyWindow(window); window = NULL; Com_Printf("Can't get display mode: %s\n", SDL_GetError()); return false; } if ((real_mode.w != w) || (real_mode.h != h)) { SDL_DestroyWindow(window); window = NULL; Com_Printf("Still in wrong display mode: %ix%i instead of %ix%i\n", real_mode.w, real_mode.h, w, h); return false; } } /* Normally SDL stays at desktop refresh rate or chooses something sane. Some player may want to override that. Reminder: SDL_WINDOW_FULLSCREEN_DESKTOP implies SDL_WINDOW_FULLSCREEN! */ if ((flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) == SDL_WINDOW_FULLSCREEN) { if (vid_rate->value > 0) { SDL_DisplayMode closest_mode; SDL_DisplayMode requested_mode = real_mode; requested_mode.refresh_rate = (int)vid_rate->value; if (SDL_GetClosestDisplayMode(last_display, &requested_mode, &closest_mode) == NULL) { Com_Printf("SDL was unable to find a mode close to %ix%i@%i\n", w, h, requested_mode.refresh_rate); Cvar_SetValue("vid_rate", -1); } else { Com_Printf("User requested %ix%i@%i, setting closest mode %ix%i@%i\n", w, h, requested_mode.refresh_rate, w, h, closest_mode.refresh_rate); if (SDL_SetWindowDisplayMode(window, &closest_mode) != 0) { Com_Printf("Couldn't switch to mode %ix%i@%i, staying at current mode\n", w, h, closest_mode.refresh_rate); Cvar_SetValue("vid_rate", -1); } else { Cvar_SetValue("vid_rate", closest_mode.refresh_rate); } } } } } else { Com_Printf("Creating window failed: %s\n", SDL_GetError()); return false; } return true; } static int GetFullscreenType() { if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP) { return 2; } else if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { return 1; } else { return 0; } } static qboolean GetWindowSize(int* w, int* h) { if (window == NULL || w == NULL || h == NULL) { return false; } 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; return true; } static void InitDisplayIndices() { displayindices = malloc((num_displays + 1) * sizeof(char *)); for ( int i = 0; i < num_displays; i++ ) { /* There are a maximum of 10 digits in 32 bit int + 1 for the NULL terminator. */ displayindices[ i ] = malloc(11 * sizeof( char )); YQ2_COM_CHECK_OOM(displayindices[i], "malloc()", 11 * sizeof( char )) snprintf( displayindices[ i ], 11, "%d", i ); } /* The last entry is NULL to indicate the list of strings ends. */ displayindices[ num_displays ] = 0; } /* * Lists all available display modes. */ static void PrintDisplayModes(void) { int curdisplay = window ? SDL_GetWindowDisplayIndex(window) : 0; // On X11 (at least for me) // curdisplay is always -1. // DG: probably because window was NULL? if (curdisplay < 0) { curdisplay = 0; } int nummodes = SDL_GetNumDisplayModes(curdisplay); if (nummodes < 1) { Com_Printf("Can't get display modes: %s\n", SDL_GetError()); return; } for (int i = 0; i < nummodes; i++) { SDL_DisplayMode mode; if (SDL_GetDisplayMode(curdisplay, i, &mode) != 0) { Com_Printf("Can't get display mode: %s\n", SDL_GetError()); return; } Com_Printf(" - Mode %2i: %ix%i@%i\n", i, mode.w, mode.h, mode.refresh_rate); } } /* * Sets the window icon */ static void SetSDLIcon() { #include "icon/q2icon64.h" // 64x64 32 Bit /* 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); } // FIXME: We need a header for this. // Maybe we could put it in vid.h. void GLimp_GrabInput(qboolean grab); /* * Shuts the SDL render backend down */ static void ShutdownGraphics(void) { ClampDisplayIndexCvar(); if (window) { /* save current display as default */ last_display = SDL_GetWindowDisplayIndex(window); /* or if current display isn't the desired default */ if (last_display != vid_displayindex->value) { last_position_x = last_position_y = SDL_WINDOWPOS_UNDEFINED; last_display = vid_displayindex->value; } else { SDL_GetWindowPosition(window, &last_position_x, &last_position_y); } /* cleanly ungrab input (needs window) */ GLimp_GrabInput(false); SDL_DestroyWindow(window); window = NULL; } // make sure that after vid_restart the refreshrate will be queried from SDL2 again. glimp_refreshRate = -1; initSuccessful = false; // not initialized anymore } // -------- /* * Initializes the SDL video subsystem. Must * be called before anything else. */ qboolean GLimp_Init(void) { vid_displayrefreshrate = Cvar_Get("vid_displayrefreshrate", "-1", CVAR_ARCHIVE); vid_displayindex = Cvar_Get("vid_displayindex", "0", CVAR_ARCHIVE); vid_highdpiaware = Cvar_Get("vid_highdpiaware", "0", CVAR_ARCHIVE); vid_rate = Cvar_Get("vid_rate", "-1", CVAR_ARCHIVE); 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; SDL_GetVersion(&version); Com_Printf("-------- vid initialization --------\n"); 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", SDL_GetCurrentVideoDriver()); num_displays = SDL_GetNumVideoDisplays(); InitDisplayIndices(); ClampDisplayIndexCvar(); Com_Printf("SDL display modes:\n"); PrintDisplayModes(); Com_Printf("------------------------------------\n\n"); } return true; } /* * Shuts the SDL video subsystem down. Must * be called after evrything's finished and * clean up. */ void GLimp_Shutdown(void) { ShutdownGraphics(); // SDL_INIT_VIDEO implies SDL_INIT_EVENTS const Uint32 subsystems = SDL_INIT_VIDEO | SDL_INIT_EVENTS; if (SDL_WasInit(SDL_INIT_EVERYTHING) == subsystems) { SDL_Quit(); } else { SDL_QuitSubSystem(SDL_INIT_VIDEO); } ClearDisplayIndices(); } /* * Determine if we want to be high dpi aware. If * we are we must scale ourself. If we are not the * compositor might scale us. */ static int Glimp_DetermineHighDPISupport(int flags) { #if SDL_VERSION_ATLEAST(2, 26, 0) /* Make sure that high dpi is never set when we don't want it. */ flags &= ~SDL_WINDOW_ALLOW_HIGHDPI; if (vid_highdpiaware->value == 0) { return flags; } /* Handle high dpi awareness based on the render backend. SDL doesn't support high dpi awareness for all backends and the quality and behavior differs between them. */ if ((strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0)) { flags |= SDL_WINDOW_ALLOW_HIGHDPI; } #endif return flags; } /* * (Re)initializes the actual 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 (fullscreen == 1) { fs_flag = SDL_WINDOW_FULLSCREEN; } else if (fullscreen == 2) { fs_flag = SDL_WINDOW_FULLSCREEN_DESKTOP; } /* Only do this if we already have a working window and a 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 (GetFullscreenType()) { SDL_SetWindowFullscreen(window, fs_flag); Cvar_SetValue("vid_fullscreen", fullscreen); } /* Are we now? */ if (GetFullscreenType()) { return true; } } /* Is the surface used? */ if (window) { re.ShutdownContext(); ShutdownGraphics(); window = NULL; } if(last_flags != -1 && (last_flags & SDL_WINDOW_OPENGL)) { /* Reset SDL. */ SDL_GL_ResetAttributes(); } /* Let renderer prepare things (set OpenGL attributes). FIXME: This is no longer necessary, the renderer could and should pass the flags when calling this function. */ flags = re.PrepareForWindow(); if (flags == -1) { /* It's PrepareForWindow() job to log an error */ return false; } if (fs_flag) { flags |= fs_flag; } /* Check for high dpi support. */ flags = Glimp_DetermineHighDPISupport(flags); /* Mkay, now the hard work. Let's create the window. */ cvar_t *gl_msaa_samples = Cvar_Get("r_msaa_samples", "0", CVAR_ARCHIVE); while (1) { if (!CreateSDLWindow(flags, width, height)) { if((flags & SDL_WINDOW_OPENGL) && gl_msaa_samples->value) { int msaa_samples = gl_msaa_samples->value; if (msaa_samples > 0) { msaa_samples /= 2; } Com_Printf("SDL SetVideoMode failed: %s\n", SDL_GetError()); Com_Printf("Reverting to %s r_mode %i (%ix%i) with %dx MSAA.\n", (flags & fs_flag) ? "fullscreen" : "windowed", (int) Cvar_VariableValue("r_mode"), width, height, msaa_samples); /* Try to recover */ Cvar_SetValue("r_msaa_samples", msaa_samples); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, msaa_samples > 0 ? 1 : 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, msaa_samples); } else if (width != 640 || height != 480 || (flags & fs_flag)) { Com_Printf("SDL SetVideoMode failed: %s\n", SDL_GetError()); Com_Printf("Reverting to windowed r_mode 4 (640x480).\n"); /* Try to recover */ Cvar_SetValue("r_mode", 4); Cvar_SetValue("vid_fullscreen", 0); Cvar_SetValue("vid_rate", -1); fullscreen = 0; *pwidth = width = 640; *pheight = height = 480; flags &= ~fs_flag; } else { Com_Printf("Failed to revert to r_mode 4. Will try another render backend...\n"); return false; } } else { break; } } last_flags = flags; /* Now that we've got a working window print it's mode. */ int curdisplay = SDL_GetWindowDisplayIndex(window); if (curdisplay < 0) { curdisplay = 0; } SDL_DisplayMode mode; if (SDL_GetCurrentDisplayMode(curdisplay, &mode) != 0) { Com_Printf("Can't get current display mode: %s\n", SDL_GetError()); } else { Com_Printf("Real display mode: %ix%i@%i\n", mode.w, mode.h, mode.refresh_rate); } /* Initialize rendering context. */ if (!re.InitContext(window)) { /* InitContext() should have logged an error. */ return false; } /* We need the actual drawable size for things like the console, the menus, etc. This might be different to the resolution due to high dpi awareness. The fullscreen window is special. We want it to fill the screen when native resolution is requestes, all other cases should look broken. */ if (flags & SDL_WINDOW_ALLOW_HIGHDPI) { if (fullscreen != 2) { re.GetDrawableSize(&viddef.width, &viddef.height); } else { cvar_t *r_mode = Cvar_Get("r_mode", "4", 0); if (r_mode->value == -2 ) { re.GetDrawableSize(&viddef.width, &viddef.height); } else { /* User likes it broken. */ viddef.width = *pwidth; viddef.height = *pheight; } } } else { /* Another bug or design failure in SDL: When we are not high dpi aware the drawable size returned by SDL may be too small. It seems like the window decoration are taken into account when they shouldn't. It can be seen when creating a fullscreen window. Work around that by always using the resolution and not the drawable size when we are not high dpi aware. */ viddef.width = *pwidth; viddef.height = *pheight; } Com_Printf("Drawable size: %ix%i\n", viddef.width, viddef.height); /* Set the window icon - For SDL2, this must be done after creating the window */ SetSDLIcon(); /* No cursor */ SDL_ShowCursor(0); initSuccessful = true; return true; } /* * Shuts the window down. */ void GLimp_ShutdownGraphics(void) { SDL_GL_ResetAttributes(); ShutdownGraphics(); } /* * (Un)grab Input */ void GLimp_GrabInput(qboolean grab) { 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"); } } /* * Returns the current display refresh rate. */ float GLimp_GetRefreshRate(void) { if (vid_displayrefreshrate->value > -1 || vid_displayrefreshrate->modified) { glimp_refreshRate = vid_displayrefreshrate->value; vid_displayrefreshrate->modified = false; } else if (glimp_refreshRate == -1) { SDL_DisplayMode mode; int i = SDL_GetWindowDisplayIndex(window); if (i >= 0 && SDL_GetCurrentDisplayMode(i, &mode) == 0) { glimp_refreshRate = mode.refresh_rate; } // Something went wrong, use default. if (glimp_refreshRate <= 0) { glimp_refreshRate = 60; } } return glimp_refreshRate; } /* * Detect current desktop mode */ qboolean GLimp_GetDesktopMode(int *pwidth, int *pheight) { // Declare display mode structure to be filled in. SDL_DisplayMode mode; if (window) { /* save current display as default */ last_display = SDL_GetWindowDisplayIndex(window); SDL_GetWindowPosition(window, &last_position_x, &last_position_y); } if (last_display < 0) { // In case of error... Com_Printf("Can't detect current desktop.\n"); last_display = 0; } // We can't get desktop where we start, so use first desktop if(SDL_GetCurrentDisplayMode(last_display, &mode) != 0) { // In case of error... Com_Printf("Can't detect default desktop mode: %s\n", SDL_GetError()); return false; } *pwidth = mode.w; *pheight = mode.h; return true; } const char** GLimp_GetDisplayIndices(void) { return (const char**)displayindices; } int GLimp_GetNumVideoDisplays(void) { return num_displays; } int GLimp_GetWindowDisplayIndex(void) { return last_display; } int GLimp_GetFrameworkVersion(void) { SDL_version ver; SDL_VERSION(&ver); return ver.major; } yquake2-QUAKE2_8_40/src/client/vid/glimp_sdl3.c000066400000000000000000000544061465112212000211520ustar00rootroot00000000000000/* * Copyright (C) 2010 Yamagi Burmeister * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * ======================================================================= * * This is the client side of the render backend, implemented trough SDL. * The SDL window and related functrion (mouse grap, fullscreen switch) * are implemented here, everything else is in the renderers. * * ======================================================================= */ /* TODO SDL3: * * Bump copyright. * * Do we need to request High DPI modes when vid_highdpiaware > 0? * * `fullscreen` should be an enum to make the code more readable. * * Debug fullscreen handling, maybe refactor it further. * * Check if window size handling is correct. * * Check pointers returned by SDL functions for memory leaks. */ #include "../../common/header/common.h" #include "header/ref.h" #include float glimp_refreshRate = -1.0f; static cvar_t *vid_displayrefreshrate; static cvar_t *vid_displayindex; static cvar_t *vid_highdpiaware; static cvar_t *vid_rate; static int last_flags = 0; static int last_display = 0; static int last_position_x = SDL_WINDOWPOS_UNDEFINED; static int last_position_y = SDL_WINDOWPOS_UNDEFINED; static SDL_Window* window = NULL; static qboolean initSuccessful = false; static char **displayindices = NULL; static int num_displays = 0; /* Fullscreen modes */ enum { FULLSCREEN_OFF = 0, FULLSCREEN_EXCLUSIVE = 1, FULLSCREEN_DESKTOP = 2 }; /* * Resets the display index Cvar if out of bounds */ static void ClampDisplayIndexCvar(void) { if (!vid_displayindex) { // uninitialized render? return; } if (vid_displayindex->value < 0 || vid_displayindex->value >= num_displays) { Cvar_SetValue("vid_displayindex", 0); } } static void ClearDisplayIndices(void) { if ( displayindices ) { for ( int i = 0; i < num_displays; i++ ) { free( displayindices[ i ] ); } free( displayindices ); displayindices = NULL; } } static qboolean CreateSDLWindow(int flags, int fullscreen, int w, int h) { if (SDL_WINDOWPOS_ISUNDEFINED(last_position_x) || SDL_WINDOWPOS_ISUNDEFINED(last_position_y) || last_position_x < 0 ||last_position_y < 24) { last_position_x = last_position_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY((int)vid_displayindex->value); } /* Force the window to minimize when focus is lost. This was the * default behavior until SDL 2.0.12 and changed with 2.0.14. * The windows staying maximized has some odd implications for * window ordering under Windows and some X11 window managers * like kwin. See: * * https://github.com/libsdl-org/SDL/issues/4039 * * https://github.com/libsdl-org/SDL/issues/3656 */ SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "1"); SDL_PropertiesID props = SDL_CreateProperties(); SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, "Yamagi Quake II"); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, last_position_x); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, last_position_y); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, w); SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, h); SDL_SetNumberProperty(props, "flags", flags); window = SDL_CreateWindowWithProperties(props); SDL_DestroyProperties(props); if (window) { /* save current display as default */ if ((last_display = SDL_GetDisplayForWindow(window)) == 0) { /* There are some obscure setups were SDL is unable to get the current display,one X11 server with several screen is one of these, so add a fallback to the first display. */ last_display = 1; } /* Set requested fullscreen mode. */ if (flags & SDL_WINDOW_FULLSCREEN) { /* SDLs behavior changed between SDL 2 and SDL 3: In SDL 2 the fullscreen window could be set with whatever mode was requested. In SDL 3 the fullscreen window is always created at desktop resolution. If a fullscreen window is requested, we can't do anything else and are done here. */ if (fullscreen == FULLSCREEN_DESKTOP) { return true; } /* Otherwise try to find a mode near the requested one and switch to it in exclusive fullscreen mode. */ /* TODO SDL3: Leak? */ const SDL_DisplayMode *closestMode = SDL_GetClosestFullscreenDisplayMode(last_display, w, h, vid_rate->value, false); if (closestMode == NULL) { Com_Printf("SDL was unable to find a mode close to %ix%i@%f\n", w, h, vid_rate->value); if (vid_rate->value != 0) { Com_Printf("Retrying with desktop refresh rate\n"); closestMode = SDL_GetClosestFullscreenDisplayMode(last_display, w, h, 0, false); if (closestMode != NULL) { Cvar_SetValue("vid_rate", 0); } else { Com_Printf("SDL was unable to find a mode close to %ix%i@0\n", w, h); return false; } } } Com_Printf("User requested %ix%i@%f, setting closest mode %ix%i@%f\n", w, h, vid_rate->value, closestMode->w, closestMode->h , closestMode->refresh_rate); /* TODO SDL3: Same code is in InitGraphics(), refactor into * a function? */ if (SDL_SetWindowFullscreenMode(window, closestMode) < 0) { Com_Printf("Couldn't set closest mode: %s\n", SDL_GetError()); return false; } if (SDL_SetWindowFullscreen(window, true) < 0) { Com_Printf("Couldn't switch to exclusive fullscreen: %s\n", SDL_GetError()); return false; } int ret = SDL_SyncWindow(window); if (ret > 0) { Com_Printf("Synchronizing window state timed out\n"); return false; } else if (ret < 0) { Com_Printf("Couldn't synchronize window state: %s\n", SDL_GetError()); return false; } } } else { Com_Printf("Creating window failed: %s\n", SDL_GetError()); return false; } return true; } static int GetFullscreenType() { if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) { /* TODO SDL3: Leak? */ const SDL_DisplayMode *fsmode = SDL_GetWindowFullscreenMode(window); if (fsmode != NULL) { return FULLSCREEN_EXCLUSIVE; } else { return FULLSCREEN_DESKTOP; } } return FULLSCREEN_OFF; } static qboolean GetWindowSize(int* w, int* h) { if (window == NULL || w == NULL || h == NULL) { return false; } if (SDL_GetWindowSize(window, w, h) < 0) { Com_Printf("Couldn't get window size: %s\n", SDL_GetError()); return false; } return true; } static void InitDisplayIndices() { displayindices = malloc((num_displays + 1) * sizeof(char *)); for ( int i = 0; i < num_displays; i++ ) { /* There are a maximum of 10 digits in 32 bit int + 1 for the NULL terminator. */ displayindices[ i ] = malloc(11 * sizeof( char )); YQ2_COM_CHECK_OOM(displayindices[i], "malloc()", 11 * sizeof( char )) snprintf( displayindices[ i ], 11, "%d", i ); } /* The last entry is NULL to indicate the list of strings ends. */ displayindices[ num_displays ] = 0; } /* * Lists all available display modes. */ static void PrintDisplayModes(void) { int curdisplay; if (window == NULL) { /* Called without a windows, list modes from the first display. This is the primary display and likely the one the game will run on. */ curdisplay = SDL_GetPrimaryDisplay(); } else { /* Otherwise use the window were the window is displayed. There are some obscure setups were this can fail - one X11 server with several screen is one of these - so add a fallback to the first display. */ if ((curdisplay = SDL_GetDisplayForWindow(window)) == 0) { curdisplay = SDL_GetPrimaryDisplay(); } } int nummodes = 0; const SDL_DisplayMode **modes = SDL_GetFullscreenDisplayModes(curdisplay, &nummodes); if (modes) { for (int i = 0; i < nummodes; ++i) { const SDL_DisplayMode *mode = modes[i]; Com_Printf(" - Mode %2i: %ix%i@%.2f\n", i, mode->w, mode->h, mode->refresh_rate); } SDL_free(modes); } else { Com_Printf("Couldn't get display modes: %s\n", SDL_GetError()); } } /* * Sets the window icon */ static void SetSDLIcon() { #include "icon/q2icon64.h" // 64x64 32 Bit /* 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_CreateSurfaceFrom((void *)q2icon64.pixel_data, q2icon64.width, q2icon64.height, q2icon64.bytes_per_pixel * q2icon64.width, SDL_GetPixelFormatEnumForMasks(q2icon64.bytes_per_pixel * 8, rmask, gmask, bmask, amask)); SDL_SetWindowIcon(window, icon); SDL_DestroySurface(icon); } // FIXME: We need a header for this. // Maybe we could put it in vid.h. void GLimp_GrabInput(qboolean grab); /* * Shuts the SDL render backend down */ static void ShutdownGraphics(void) { ClampDisplayIndexCvar(); if (window) { /* save current display as default */ last_display = SDL_GetDisplayForWindow(window); /* or if current display isn't the desired default */ if (last_display != vid_displayindex->value) { last_position_x = last_position_y = SDL_WINDOWPOS_UNDEFINED; last_display = vid_displayindex->value; } else { SDL_GetWindowPosition(window, &last_position_x, &last_position_y); } /* cleanly ungrab input (needs window) */ GLimp_GrabInput(false); SDL_DestroyWindow(window); window = NULL; } // make sure that after vid_restart the refreshrate will be queried from SDL2 again. glimp_refreshRate = -1; initSuccessful = false; // not initialized anymore } // -------- /* * Initializes the SDL video subsystem. Must * be called before anything else. */ qboolean GLimp_Init(void) { vid_displayrefreshrate = Cvar_Get("vid_displayrefreshrate", "-1", CVAR_ARCHIVE); vid_displayindex = Cvar_Get("vid_displayindex", "0", CVAR_ARCHIVE); vid_highdpiaware = Cvar_Get("vid_highdpiaware", "1", CVAR_ARCHIVE); vid_rate = Cvar_Get("vid_rate", "-1", CVAR_ARCHIVE); 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; SDL_GetVersion(&version); Com_Printf("-------- vid initialization --------\n"); 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", SDL_GetCurrentVideoDriver()); SDL_DisplayID *displays; if ((displays = SDL_GetDisplays(&num_displays)) == NULL) { Com_Printf("Couldn't get number of displays: %s\n", SDL_GetError()); } else { SDL_free(displays); } InitDisplayIndices(); ClampDisplayIndexCvar(); Com_Printf("SDL display modes:\n"); PrintDisplayModes(); Com_Printf("------------------------------------\n\n"); } return true; } /* * Shuts the SDL video subsystem down. Must * be called after evrything's finished and * clean up. */ void GLimp_Shutdown(void) { ShutdownGraphics(); SDL_QuitSubSystem(SDL_INIT_VIDEO); ClearDisplayIndices(); } /* * Determine if we want to be high dpi aware. If * we are we must scale ourself. If we are not the * compositor might scale us. */ static int Glimp_DetermineHighDPISupport(int flags) { /* Make sure that high dpi is never set when we don't want it. */ flags &= ~SDL_WINDOW_HIGH_PIXEL_DENSITY; if (vid_highdpiaware->value == 0) { return flags; } /* Handle high dpi awareness based on the render backend. SDL doesn't support high dpi awareness for all backends and the quality and behavior differs between them. */ if ((strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0)) { flags |= SDL_WINDOW_HIGH_PIXEL_DENSITY; } return flags; } /* * (Re)initializes the actual 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 (fullscreen == FULLSCREEN_EXCLUSIVE || fullscreen == FULLSCREEN_DESKTOP) { fs_flag = SDL_WINDOW_FULLSCREEN; } /* Only do this if we already have a working window and a 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)) { /* TODO SDL3: Leak? */ const SDL_DisplayMode *closestMode = NULL; /* If we want fullscreen, but aren't */ if (GetFullscreenType()) { if (fullscreen == FULLSCREEN_EXCLUSIVE) { closestMode = SDL_GetClosestFullscreenDisplayMode(last_display, width, height, vid_rate->value, false); if (closestMode == NULL) { Com_Printf("SDL was unable to find a mode close to %ix%i@%f\n", width, height, vid_rate->value); if (vid_rate->value != 0) { Com_Printf("Retrying with desktop refresh rate\n"); closestMode = SDL_GetClosestFullscreenDisplayMode(last_display, width, height, 0, false); if (closestMode != NULL) { Cvar_SetValue("vid_rate", 0); } else { Com_Printf("SDL was unable to find a mode close to %ix%i@0\n", width, height); return false; } } } } else if (fullscreen == FULLSCREEN_DESKTOP) { /* Fullscreen window */ closestMode = NULL; } if (SDL_SetWindowFullscreenMode(window, closestMode) < 0) { Com_Printf("Couldn't set fullscreen modmode: %s\n", SDL_GetError()); Cvar_SetValue("vid_fullscreen", 0); } else { if (SDL_SetWindowFullscreen(window, true) < 0) { Com_Printf("Couldn't switch to exclusive fullscreen: %s\n", SDL_GetError()); Cvar_SetValue("vid_fullscreen", 0); } else { int ret = SDL_SyncWindow(window); if (ret > 0) { Com_Printf("Synchronizing window state timed out\n"); Cvar_SetValue("vid_fullscreen", 0); } else if (ret < 0) { Com_Printf("Couldn't synchronize window state: %s\n", SDL_GetError()); Cvar_SetValue("vid_fullscreen", 0); } } } Cvar_SetValue("vid_fullscreen", fullscreen); } /* Are we now? */ if (GetFullscreenType()) { return true; } } /* Is the surface used? */ if (window) { re.ShutdownContext(); ShutdownGraphics(); window = NULL; } if(last_flags != -1 && (last_flags & SDL_WINDOW_OPENGL)) { /* Reset SDL. */ SDL_GL_ResetAttributes(); } /* Let renderer prepare things (set OpenGL attributes). FIXME: This is no longer necessary, the renderer could and should pass the flags when calling this function. */ flags = re.PrepareForWindow(); if (flags == -1) { /* It's PrepareForWindow() job to log an error */ return false; } if (fs_flag) { flags |= fs_flag; } /* Check for high dpi support. */ flags = Glimp_DetermineHighDPISupport(flags); /* Mkay, now the hard work. Let's create the window. */ cvar_t *gl_msaa_samples = Cvar_Get("r_msaa_samples", "0", CVAR_ARCHIVE); while (1) { if (!CreateSDLWindow(flags, fullscreen, width, height)) { if((flags & SDL_WINDOW_OPENGL) && gl_msaa_samples->value) { int msaa_samples = gl_msaa_samples->value; if (msaa_samples > 0) { msaa_samples /= 2; } Com_Printf("SDL SetVideoMode failed: %s\n", SDL_GetError()); Com_Printf("Reverting to %s r_mode %i (%ix%i) with %dx MSAA.\n", (flags & fs_flag) ? "fullscreen" : "windowed", (int) Cvar_VariableValue("r_mode"), width, height, msaa_samples); /* Try to recover */ Cvar_SetValue("r_msaa_samples", msaa_samples); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, msaa_samples > 0 ? 1 : 0); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, msaa_samples); } else if (width != 640 || height != 480 || (flags & fs_flag)) { Com_Printf("SDL SetVideoMode failed: %s\n", SDL_GetError()); Com_Printf("Reverting to windowed r_mode 4 (640x480).\n"); /* Try to recover */ Cvar_SetValue("r_mode", 4); Cvar_SetValue("vid_fullscreen", 0); Cvar_SetValue("vid_rate", -1); fullscreen = FULLSCREEN_OFF; *pwidth = width = 640; *pheight = height = 480; flags &= ~fs_flag; } else { Com_Printf("Failed to revert to r_mode 4. Will try another render backend...\n"); return false; } } else { break; } } last_flags = flags; /* Now that we've got a working window print it's mode. */ int curdisplay; if ((curdisplay = SDL_GetDisplayForWindow(window)) == 0) { /* There are some obscure setups were SDL is unable to get the current display,one X11 server with several screen is one of these, so add a fallback to the first display. */ curdisplay = SDL_GetPrimaryDisplay(); } const SDL_DisplayMode *mode; if ((mode = SDL_GetCurrentDisplayMode(curdisplay)) == NULL) { Com_Printf("Couldn't get current display mode: %s\n", SDL_GetError()); } else { Com_Printf("Real display mode: %ix%i@%.2f\n", mode->w, mode->h, mode->refresh_rate); } /* Initialize rendering context. */ if (!re.InitContext(window)) { /* InitContext() should have logged an error. */ return false; } /* We need the actual drawable size for things like the console, the menus, etc. This might be different to the resolution due to high dpi awareness. The fullscreen window is special. We want it to fill the screen when native resolution is requestes, all other cases should look broken. */ if (flags & SDL_WINDOW_HIGH_PIXEL_DENSITY) { if (fullscreen != FULLSCREEN_DESKTOP) { re.GetDrawableSize(&viddef.width, &viddef.height); } else { cvar_t *r_mode = Cvar_Get("r_mode", "4", 0); if (r_mode->value == -2 ) { re.GetDrawableSize(&viddef.width, &viddef.height); } else { /* User likes it broken. */ viddef.width = *pwidth; viddef.height = *pheight; } } } else { /* Another bug or design failure in SDL: When we are not high dpi aware the drawable size returned by SDL may be too small. It seems like the window decoration are taken into account when they shouldn't. It can be seen when creating a fullscreen window. Work around that by always using the resolution and not the drawable size when we are not high dpi aware. */ viddef.width = *pwidth; viddef.height = *pheight; } Com_Printf("Drawable size: %ix%i\n", viddef.width, viddef.height); /* Set the window icon - For SDL2, this must be done after creating the window */ SetSDLIcon(); /* No cursor */ SDL_ShowCursor(); initSuccessful = true; return true; } /* * Shuts the window down. */ void GLimp_ShutdownGraphics(void) { SDL_GL_ResetAttributes(); ShutdownGraphics(); } /* * (Un)grab Input */ void GLimp_GrabInput(qboolean grab) { if(window != NULL) { SDL_SetWindowMouseGrab(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"); } } /* * Returns the current display refresh rate. */ float GLimp_GetRefreshRate(void) { if (vid_displayrefreshrate->value > -1 || vid_displayrefreshrate->modified) { glimp_refreshRate = vid_displayrefreshrate->value; vid_displayrefreshrate->modified = false; } else if (glimp_refreshRate == -1) { const SDL_DisplayMode *mode; int curdisplay; if (window == NULL) { /* This is paranoia. This function should only be called if there is a working window. Otherwise things will likely break somewhere else in the client. */ curdisplay = SDL_GetPrimaryDisplay(); } else { if ((curdisplay = SDL_GetDisplayForWindow(window)) == 0) { /* There are some obscure setups were SDL is unable to get the current display,one X11 server with several screen is one of these, so add a fallback to the first display. */ curdisplay = SDL_GetPrimaryDisplay(); } } if ((mode = SDL_GetCurrentDisplayMode(curdisplay)) == NULL) { printf("Couldn't get display refresh rate: %s\n", SDL_GetError()); } else { glimp_refreshRate = mode->refresh_rate; } } return glimp_refreshRate; } /* * Detect current desktop mode */ qboolean GLimp_GetDesktopMode(int *pwidth, int *pheight) { if (window == NULL) { /* Renderers call into this function before the window is created. This could be refactored by passing the mode and not the geometry from the renderer to GLimp_InitGraphics(), however that would break the renderer API. */ last_display = SDL_GetPrimaryDisplay(); } else { /* save current display as default */ if ((last_display = SDL_GetDisplayForWindow(window)) == 0) { /* There are some obscure setups were SDL is unable to get the current display,one X11 server with several screen is one of these, so add a fallback to the first display. */ last_display = SDL_GetPrimaryDisplay(); } SDL_GetWindowPosition(window, &last_position_x, &last_position_y); } const SDL_DisplayMode *mode; if ((mode = SDL_GetCurrentDisplayMode(last_display)) == NULL) { Com_Printf("Couldn't detect default desktop mode: %s\n", SDL_GetError()); return false; } *pwidth = mode->w; *pheight = mode->h; return true; } const char** GLimp_GetDisplayIndices(void) { return (const char**)displayindices; } int GLimp_GetNumVideoDisplays(void) { return num_displays; } int GLimp_GetWindowDisplayIndex(void) { return last_display; } int GLimp_GetFrameworkVersion(void) { SDL_Version ver; SDL_VERSION(&ver); return ver.major; } yquake2-QUAKE2_8_40/src/client/vid/header/000077500000000000000000000000001465112212000201705ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/vid/header/ref.h000066400000000000000000000227751465112212000211320ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 used 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; // Renderer restart type. typedef enum { RESTART_UNDEF, RESTART_NO, RESTART_FULL, RESTART_PARTIAL } ref_restart_t; // FIXME: bump API_VERSION? #define API_VERSION 7 #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; // if framework_version is different, the dll cannot be used // necessary because differend SDL major version cannot be // mixed. int framework_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); // called by GLimp_InitGraphics() *after* creating render // context. Returns the actual drawable size in the width // and height variables. This may be different from the // window size due to high dpi awareness. void (EXPORT *GetDrawableSize)(int* width, int* height); // shuts down rendering (OpenGL) context. void (EXPORT *ShutdownContext)(void); // 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 if bits equals to 8). * Note that calls */ void (EXPORT *DrawStretchRaw) (int x, int y, int w, int h, int cols, int rows, const byte *data, int bits); /* ** 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); qboolean (EXPORT *EndWorldRenderpass) (void); // finish world rendering, apply postprocess and switch to UI render pass //void (EXPORT *AppActivate)( qboolean activate ); } refexport_t; typedef struct { YQ2_ATTR_NORETURN_FUNCPTR void (IMPORT *Sys_Error) (int err_level, const char *str, ...) PRINTF_ATTR(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) (const char *name, char *value, int flags); cvar_t *(IMPORT *Cvar_Set) (const char *name, char *value); void (IMPORT *Cvar_SetValue) (const char *name, float value); qboolean (IMPORT *Vid_GetModeInfo)(int *width, int *height, int mode); void (IMPORT *Vid_MenuInit)( void ); // 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 ); qboolean (IMPORT *GLimp_InitGraphics)(int fullscreen, int *pwidth, int *pheight); qboolean (IMPORT *GLimp_GetDesktopMode)(int *pwidth, int *pheight); void (IMPORT *Vid_RequestRestart)(ref_restart_t rs); } 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, const byte *data, int bits); //int R_Init(void *hinstance, void *hWnd); //void R_Shutdown(void); void R_SetPalette(const unsigned char *palette); void R_BeginFrame(float camera_separation); qboolean R_EndWorldRenderpass(void); void R_EndFrame(void); #endif yquake2-QUAKE2_8_40/src/client/vid/header/stb_image_write.h000066400000000000000000002015411465112212000235100ustar00rootroot00000000000000/* stb_image_write - v1.13 - public domain - http://nothings.org/stb 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 or a callback. The PNG output is not optimal; it is 20-50% larger than the file written by a decent optimizing implementation; though providing a custom zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. 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() You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function for PNG compression (instead of the builtin one), it must have the following signature: unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); The returned data will be freed with STBIW_FREE() (free() by default), so it must be heap allocated with STBIW_MALLOC() (malloc() by default), UNICODE: If compiling for Windows and you wish to use Unicode filenames, compile with #define STBIW_WINDOWS_UTF8 and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert Windows wchar_t filenames to utf8. USAGE: There are five 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_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically There are also five 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 configure it with these global variables: int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode 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.) PNG allows you to set the deflate compression level by setting the global variable 'stbi_write_png_compression_level' (it defaults to 8). 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: Sean Barrett - PNG/BMP/TGA Baldur Karlsson - HDR Jean-Sebastien Guay - TGA monochrome Tim Kelsey - misc enhancements Alan Hickman - TGA RLE Emmanuel Julien - initial file IO callback implementation Jon Olick - original jo_jpeg.cpp code Daniel Gibson - integrate JPEG, allow external zlib Aarni Koskela - allow choosing PNG filter bugfixes: github:Chribba Guillaume Chereau github:jry2 github:romigrou Sergio Gonzalez Jonas Karlsson Filip Wasil Thatcher Ulrich github:poppolopoppo Patrick Boettcher github:xeekworx Cap Petschulat Simon Rodriguez Ivan Tikhonov github:ignotion Adam Schackart LICENSE See end of file for license information. */ #ifndef INCLUDE_STB_IMAGE_WRITE_H #define INCLUDE_STB_IMAGE_WRITE_H #include // if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' #ifndef STBIWDEF #ifdef STB_IMAGE_WRITE_STATIC #define STBIWDEF static #else #ifdef __cplusplus #define STBIWDEF extern "C" #else #define STBIWDEF extern #endif #endif #endif #ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations extern int stbi_write_tga_with_rle; extern int stbi_write_png_compression_level; extern int stbi_write_force_png_filter; #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); #ifdef STBI_WINDOWS_UTF8 STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); #endif #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); STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); #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) #ifdef STB_IMAGE_WRITE_STATIC static int stbi__flip_vertically_on_write=0; static int stbi_write_png_compression_level = 8; static int stbi_write_tga_with_rle = 1; static int stbi_write_force_png_filter = -1; #else int stbi_write_png_compression_level = 8; int stbi__flip_vertically_on_write=0; int stbi_write_tga_with_rle = 1; int stbi_write_force_png_filter = -1; #endif STBIWDEF void stbi_flip_vertically_on_write(int flag) { stbi__flip_vertically_on_write = flag; } 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); } #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) #ifdef __cplusplus #define STBIW_EXTERN extern "C" #else #define STBIW_EXTERN extern #endif STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) { return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); } #endif static FILE *stbiw__fopen(char const *filename, char const *mode) { FILE *f; #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) wchar_t wMode[64]; wchar_t wFilename[1024]; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) return 0; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) return 0; #if _MSC_VER >= 1400 if (0 != _wfopen_s(&f, wFilename, wMode)) f = 0; #else f = _wfopen(wFilename, wMode); #endif #elif defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) f=0; #else f = fopen(filename, mode); #endif return f; } static int stbi__start_write_file(stbi__write_context *s, const char *filename) { FILE *f = stbiw__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]; 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 (stbi__flip_vertically_on_write) vdir *= -1; 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; int jend, jdir; 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); if (stbi__flip_vertically_on_write) { j = 0; jend = y; jdir = 1; } else { j = y-1; jend = -1; jdir = -1; } for (; j != jend; j += jdir) { 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)) static 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); } } static 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); } static 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); } static 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); #ifdef __STDC_WANT_SECURE_LIB__ len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #else len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #endif s->func(s->context, buffer, len); for(i=0; i < y; i++) stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); 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 // #ifndef STBIW_ZLIB_COMPRESS // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() #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 STBIWDEF 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 builtin 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; unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); if (hash_table == NULL) return NULL; 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) { #ifdef STBIW_CRC32 return STBIW_CRC32(buffer, len); #else 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; #endif } #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 static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) { static int mapping[] = { 0,1,2,3,4 }; static int firstmap[] = { 0,1,0,5,6 }; int *mymap = (y != 0) ? mapping : firstmap; int i; int type = mymap[filter_type]; unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; if (type==0) { memcpy(line_buffer, z, width*n); return; } // first loop isn't optimized since it's just one pixel for (i = 0; i < n; ++i) { switch (type) { case 1: line_buffer[i] = z[i]; break; case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; case 5: line_buffer[i] = z[i]; break; case 6: line_buffer[i] = z[i]; break; } } switch (type) { case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; } } STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) { int force_filter = stbi_write_force_png_filter; 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 j,zlen; if (stride_bytes == 0) stride_bytes = x * n; if (force_filter >= 5) { force_filter = -1; } 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) { int filter_type; if (force_filter > -1) { filter_type = force_filter; stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); } else { // Estimate the best filter by running through all of them: int best_filter = 0, best_filter_val = 0x7fffffff, est, i; for (filter_type = 0; filter_type < 5; filter_type++) { stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); // Estimate the entropy of the line using this filter; the less, the better. est = 0; for (i = 0; i < x*n; ++i) { est += abs((signed char) line_buffer[i]); } if (est < best_filter_val) { best_filter_val = est; best_filter = filter_type; } } if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); filter_type = best_filter; } } // when we get here, filter_type contains the filter type, and line_buffer contains the data filt[j*(x*n+1)] = (unsigned char) filter_type; 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_write_png_compression_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; } #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((const unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; f = stbiw__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((const 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]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); uvti = (UVQT[i]*quality+50)/100; UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (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,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), 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) { // row >= height => use last input row int clamped_row = (row < height) ? row : height - 1; int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; for(col = x; col < x+8; ++col, ++pos) { float r, g, b; // if col >= width => use pixel from last input column int p = base_p + ((col < width) ? col : (width-1))*comp; 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.11 (2019-08-11) 1.10 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 1.09 (2018-02-11) fix typo in zlib quality API, improve STB_I_W_STATIC in C++ 1.08 (2018-01-29) add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter 1.07 (2017-07-24) doc fix 1.06 (2017-07-23) writing JPEG (using Jon Olick's code) 1.05 ??? 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_8_40/src/client/vid/header/vid.h000066400000000000000000000037501465112212000211300ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * * ======================================================================= * * API between client and renderer. * * ======================================================================= */ #ifndef CL_VID_H #define CL_VID_H #include "../../../common/header/common.h" // FIXME: Remove it, it's unused. typedef struct vrect_s { int x,y,width,height; } vrect_t; // Hold the video state. typedef struct { int height; int width; } viddef_t; // Global video state. extern viddef_t viddef; // Generic stuff. qboolean VID_HasRenderer(const char *renderer); 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); // Stuff provided by platform backend. extern float glimp_refreshRate; const char **GLimp_GetDisplayIndices(void); int GLimp_GetWindowDisplayIndex(void); int GLimp_GetNumVideoDisplays(void); qboolean GLimp_Init(void); void GLimp_Shutdown(void); qboolean GLimp_InitGraphics(int fullscreen, int *pwidth, int *pheight); void GLimp_ShutdownGraphics(void); void GLimp_GrabInput(qboolean grab); float GLimp_GetRefreshRate(void); qboolean GLimp_GetDesktopMode(int *pwidth, int *pheight); int GLimp_GetFrameworkVersion(void); #endif yquake2-QUAKE2_8_40/src/client/vid/icon/000077500000000000000000000000001465112212000176705ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/client/vid/icon/q2icon64.h000066400000000000000000003022771465112212000214210ustar00rootroot00000000000000static const struct { unsigned int width; unsigned int height; unsigned int bytes_per_pixel; /* 3:RGB, 4:RGBA */ unsigned char pixel_data[64 * 64 * 4]; } q2icon64 = { 64, 64, 4, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0xca, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0xe9, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0xe9, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xe5, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0xd1, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0xd5, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0xe9, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xca, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xc9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe3, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xd2, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xca, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0xd2, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0xc9, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xe3, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xb5, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe3, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xbd, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xd6, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0xe9, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd5, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa9, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc9, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } }; yquake2-QUAKE2_8_40/src/client/vid/vid.c000066400000000000000000000404401465112212000176700ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * ======================================================================= * * API between the client and renderers. * * ======================================================================= */ #include #include #include "../../client/header/client.h" #include "../../client/header/keyboard.h" // -------- // Screenshots // ----------- #include "../../common/unzip/miniz/miniz.h" 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 #define STB_IMAGE_WRITE_IMPLEMENTATION #include "header/stb_image_write.h" /* * Writes a screenshot. This function is called with raw image data of * width*height pixels, each pixel has comp bytes. Must be 3 or 4, for * RGB or RGBA. The pixels must be given row-wise, stating at the 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 < numFormats; ++i) { if (Q_stricmp(maybeFormat, supportedFormats[i]) == 0) { format = i; break; } } if (i == numFormats) { Com_Printf("the (optional) second argument to 'screenshot' is the format, one of \"tga\", \"bmp\", \"png\", \"jpg\"\n"); return; } if (argc > 2) { const char* q = Cmd_Argv(2); int qualityStrLen = strlen(q); for (i = 0; i < qualityStrLen; ++i) { if (q[i] < '0' || q[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 9!\n"); return; } } quality = atoi(q); if (format == 2) // png { if (quality < 0) { quality = 0; } else if (quality > 9) { quality = 9; } } 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 = Q_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_write_png_compression_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); } } // -------- // Video mode array // ---------------- typedef struct vidmode_s { const char *description; int width, height; int mode; } vidmode_t; // This must be the same as VID_MenuInit()->resolutions[] 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}, }; #define VID_NUM_MODES (sizeof(vid_modes) / sizeof(vid_modes[0])) /* * Callback function for the 'vid_listmodes' cmd. */ void VID_ListModes_f(void) { int i; Com_Printf("Supported video modes (r_mode):\n"); for (i = 0; i < VID_NUM_MODES; ++i) { Com_Printf(" %s\n", vid_modes[i].description); } Com_Printf(" Mode -1: r_customwidth x r_customheight\n"); } /* * Returns informations about the given mode. */ qboolean VID_GetModeInfo(int *width, int *height, int mode) { if ((mode < 0) || (mode >= VID_NUM_MODES)) { return false; } *width = vid_modes[mode].width; *height = vid_modes[mode].height; return true; } // -------- // Renderer load, restart and shutdown // ----------------------------------- // Global console variables. cvar_t *vid_gamma; cvar_t *vid_fullscreen; cvar_t *vid_renderer; // Global video state, used throughout the client. viddef_t viddef; // Struct with the pointers exported by the renderer. refexport_t re; // Handle / pointer the the loaded renderer DLL. void *reflib_handle = NULL; // Is a renderer loaded and active? qboolean ref_active = false; // Renderer restart type requested. ref_restart_t restart_state = RESTART_UNDEF; // Renderer lib extension. #ifdef __APPLE__ const char* lib_ext = "dylib"; #elif defined(_WIN32) const char* lib_ext = "dll"; #else const char* lib_ext = "so"; #endif /* * Returns platform specific path to a renderer lib. */ static void VID_GetRendererLibPath(const char *renderer, char *path, size_t len) { snprintf(path, len, "%sref_%s.%s", Sys_GetBinaryDir(), renderer, lib_ext); } /* * Checks if a renderer DLL is available. */ qboolean VID_HasRenderer(const char *renderer) { char reflib_path[MAX_OSPATH] = {0}; VID_GetRendererLibPath(renderer, reflib_path, sizeof(reflib_path)); if (Sys_IsFile(reflib_path)) { return true; } return false; } /* * Called by the renderer to request a restart. */ void VID_RequestRestart(ref_restart_t rs) { restart_state = rs; } /* * Restarts the renderer. */ void VID_Restart_f(void) { if (restart_state == RESTART_UNDEF) { vid_fullscreen->modified = true; } else { restart_state = RESTART_FULL; } } /* * Shuts the renderer down and unloads it. */ void VID_ShutdownRenderer(void) { if (ref_active) { /* Shut down the renderer */ re.Shutdown(); GLimp_ShutdownGraphics(); Sys_FreeLibrary(reflib_handle); reflib_handle = NULL; memset(&re, 0, sizeof(re)); } // Declare the refresher as inactive ref_active = false; } /* * Loads and initializes a renderer. */ qboolean VID_LoadRenderer(void) { refimport_t ri; GetRefAPI_t GetRefAPI; char reflib_name[64] = {0}; char reflib_path[MAX_OSPATH] = {0}; // If the refresher is already active we need // to shut it down before loading a new one VID_ShutdownRenderer(); // Log what we're doing. Com_Printf("----- refresher initialization -----\n"); snprintf(reflib_name, sizeof(reflib_name), "ref_%s.%s", vid_renderer->string, lib_ext); VID_GetRendererLibPath(vid_renderer->string, reflib_path, sizeof(reflib_path)); Com_Printf("Loading library: %s\n", reflib_name); // Check if the renderer libs exists. if (!VID_HasRenderer(vid_renderer->string)) { Com_Printf("Library %s cannot be found!\n", reflib_name); return false; } // Mkay, let's load the requested renderer. GetRefAPI = (GetRefAPI_t)Sys_LoadLibrary(reflib_path, "GetRefAPI", &reflib_handle); // Okay, we couldn't load it. It's up to the // caller to recover from this. if (GetRefAPI == NULL) { Com_Printf("Loading %s as renderer lib failed!\n", reflib_name); return false; } // Fill in the struct exported to the renderer. // FIXME: Do we really need all these? ri.Cmd_AddCommand = Cmd_AddCommand; ri.Cmd_Argc = Cmd_Argc; ri.Cmd_Argv = Cmd_Argv; ri.Cmd_ExecuteText = Cbuf_ExecuteText; ri.Cmd_RemoveCommand = Cmd_RemoveCommand; ri.Com_VPrintf = Com_VPrintf; ri.Cvar_Get = Cvar_Get; ri.Cvar_Set = Cvar_Set; ri.Cvar_SetValue = Cvar_SetValue; ri.FS_FreeFile = FS_FreeFile; ri.FS_Gamedir = FS_Gamedir; ri.FS_LoadFile = FS_LoadFile; ri.GLimp_InitGraphics = GLimp_InitGraphics; ri.GLimp_GetDesktopMode = GLimp_GetDesktopMode; ri.Sys_Error = Com_Error; ri.Vid_GetModeInfo = VID_GetModeInfo; ri.Vid_MenuInit = VID_MenuInit; ri.Vid_WriteScreenshot = VID_WriteScreenshot; ri.Vid_RequestRestart = VID_RequestRestart; // Exchange our export struct with the renderers import struct. re = GetRefAPI(ri); // Declare the refresher as active. ref_active = true; // Let's check if we've got a compatible renderer. if (re.api_version != API_VERSION) { Com_Printf("%s has incompatible api_version %d!\n", reflib_name, re.api_version); VID_ShutdownRenderer(); return false; } else if (re.framework_version != GLimp_GetFrameworkVersion()) { Com_Printf("%s has incompatible sdl_version %d!\n", reflib_name, re.framework_version); VID_ShutdownRenderer(); return false; } // Everything seems okay, initialize it. if (!re.Init()) { VID_ShutdownRenderer(); Com_Printf("ERROR: Loading %s as rendering backend failed.\n", reflib_name); Com_Printf("------------------------------------\n\n"); return false; } /* 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; } /* * Checks if a renderer changes was requested and executes it. * Inclusive fallback through all renderers. :) */ void VID_CheckChanges(void) { // Hack around renderers that still abuse vid_fullscreen // to communicate restart requests to the client. ref_restart_t rs; if (restart_state == RESTART_UNDEF) { if (vid_fullscreen->modified) { rs = RESTART_FULL; vid_fullscreen->modified = false; } else { rs = RESTART_NO; } } else { rs = restart_state; restart_state = RESTART_NO; } if (rs == RESTART_FULL) { // Stop sound, because the clients blocks while // we're reloading the renderer. The sound system // would screw up it's internal timings. S_StopAllSounds(); // Reset the client side of the renderer state. cl.refresh_prepped = false; cl.cinematicpalette_active = false; // More or less blocks the client. cls.disable_screen = true; // Mkay, let's try our luck. while (!VID_LoadRenderer()) { // We try: custom -> gl3 -> gles3 -> gl1 -> soft. if ((strcmp(vid_renderer->string, "gl3") != 0) && (strcmp(vid_renderer->string, "gles3") != 0) && (strcmp(vid_renderer->string, "gl1") != 0) && (strcmp(vid_renderer->string, "soft") != 0)) { Com_Printf("Retrying with gl3...\n"); Cvar_Set("vid_renderer", "gl3"); } else if (strcmp(vid_renderer->string, "gl3") == 0) { Com_Printf("Retrying with gles3...\n"); Cvar_Set("vid_renderer", "gles3"); } else if (strcmp(vid_renderer->string, "gles3") == 0) { Com_Printf("Retrying with gl1...\n"); Cvar_Set("vid_renderer", "gl1"); } else if (strcmp(vid_renderer->string, "gl1") == 0) { Com_Printf("Retrying with soft...\n"); Cvar_Set("vid_renderer", "soft"); } else if (strcmp(vid_renderer->string, "soft") == 0) { // Sorry, no usable renderer found. Com_Error(ERR_FATAL, "No usable renderer found!\n"); } } // Unblock the client. cls.disable_screen = false; } if (rs == RESTART_PARTIAL) { cl.refresh_prepped = false; } } /* * Initializes the video stuff. */ void VID_Init(void) { // Console variables vid_gamma = Cvar_Get("vid_gamma", "1.0", CVAR_ARCHIVE); vid_fullscreen = Cvar_Get("vid_fullscreen", "0", CVAR_ARCHIVE); vid_renderer = Cvar_Get("vid_renderer", "gl3", CVAR_ARCHIVE); // Commands Cmd_AddCommand("vid_restart", VID_Restart_f); Cmd_AddCommand("vid_listmodes", VID_ListModes_f); Cmd_AddCommand("r_listmodes", VID_ListModes_f); // more consistent with r_mode // Initializes the video backend. This is NOT the renderer // itself, just the client side support stuff! if (!GLimp_Init()) { Com_Error(ERR_FATAL, "Couldn't initialize the graphics subsystem!\n"); } // Load the renderer and get things going. VID_CheckChanges(); } /* * Shuts the video stuff down. */ void VID_Shutdown(void) { VID_ShutdownRenderer(); GLimp_Shutdown(); } // ---- // Wrappers for the functions provided by the renderer libs. // ========================================================= 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, const byte *data, int bits) { if (ref_active) { re.DrawStretchRaw(x, y, w, h, cols, rows, data, bits); } } 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); } } qboolean R_EndWorldRenderpass(void) { if(ref_active) { return re.EndWorldRenderpass(); } return false; } void R_EndFrame(void) { if(ref_active) { re.EndFrame(); } } qboolean R_IsVSyncActive(void) { if (ref_active) { return re.IsVSyncActive(); } return false; } yquake2-QUAKE2_8_40/src/common/000077500000000000000000000000001465112212000161705ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/common/argproc.c000066400000000000000000000056771465112212000200100ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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(const 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_8_40/src/common/clientserver.c000066400000000000000000000141141465112212000210420ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; cvar_t *color_terminal; 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)(int, char *)) { 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 < 0) { msgLen = MAXPRINTMSG-1; msg[msgLen] = '\0'; } 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 if ((msg[0] == 0x01 || msg[0] == 0x02) && color_terminal && color_terminal->value) { // skip color marker i = 1; } else { i = 0; } // remove unprintable characters for(; 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 ((msg[0] == 0x01) || (msg[0] == 0x02)) { // remove color marker msg[0] = ' '; } if (!logfile) { Com_sprintf(name, sizeof(name), "%s/qconsole.log", FS_Gamedir()); if (logfile_active->value > 2) { logfile = Q_fopen(name, "a"); } else { logfile = Q_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(const 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(const 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(const 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, const 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_8_40/src/common/cmdparser.c000066400000000000000000000441651465112212000203260ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 #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; int 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[32768]; char defer_text_buf[32768]; /* * 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 = Sys_Milliseconds(); } 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; if(cmd_wait > 0) { // make sure that "wait" in scripts waits for ~16.66ms (1 frame at 60fps) // regardless of framerate if (Sys_Milliseconds() - cmd_wait <= 16) { return; } cmd_wait = 0; } 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 > 0) { /* skip out while text still remains in buffer, leaving it for after we're done waiting */ 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") != 0) { 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, c; int argc; qboolean has_args = false; /* 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 */ for (i = 0; i < s - 1; i++) { if (text[i] == '+') { i++; for (j = i; (text[j] != '+') && !(text[j] == '-' && text[j-1] == ' ') && (text[j] != 0); j++) { } c = text[j]; text[j] = 0; Cbuf_AddText(text + i); Cbuf_AddText("\n"); has_args = true; text[j] = c; i = j - 1; } } Z_Free(text); return has_args; } /* * Execute a script file */ 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 */ /* we also add a newline */ f2 = Z_Malloc(len + 2); memcpy(f2, f, len); f2[len] = '\n'; // make sure last line has a newline f2[len+1] = '\0'; Cbuf_InsertText(f2); Z_Free(f2); FS_FreeFile(f); } /* * Inserts the current value of a variable as command text */ void Cmd_Vstr_f( void ) { const char *v; if (Cmd_Argc() != 2) { Com_Printf("vstr : execute a variable command\n"); return; } v = Cvar_VariableString(Cmd_Argv(1)); Cbuf_InsertText(va("%s\n", v)); } /* * 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; } 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]), Q_sort_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; } char * Cmd_CompleteMapCommand(char *partial) { char **mapNames; int i, j, k, nbMatches, len, nMaps; char *mapName; char *pmatch[1024]; qboolean partialFillContinue = true; if ((mapNames = FS_ListFiles2("maps/*.bsp", &nMaps, 0, 0)) != NULL) { len = strlen(partial); nbMatches = 0; memset(retval, 0, strlen(retval)); for (i = 0; i < nMaps - 1; i++) { if (strrchr(mapNames[i], '/')) { mapName = strrchr(mapNames[i], '/') + 1; } else { mapName = mapNames[i]; } mapName = strtok(mapName, "."); /* check for exact match */ if (!Q_strcasecmp(partial, mapName)) { strcpy(retval, partial); } /* check for partial match */ else if (!Q_strncasecmp(partial, mapName, len)) { pmatch[nbMatches] = mapName; nbMatches++; } } if (nbMatches == 1) { strcpy(retval, pmatch[0]); } else if (nbMatches > 1) { Com_Printf("\n=================\n\n"); for (j = 0; j < nbMatches; j++) { Com_Printf("%s\n", pmatch[j]); } //partial complete for (j = 0; j < strlen(pmatch[0]); j++) { for (k = 1; k < nbMatches; k++) { if (j >= strlen(pmatch[k]) || tolower((unsigned char)pmatch[0][j]) != tolower((unsigned char)pmatch[k][j])) { partialFillContinue = false; break; } } if (partialFillContinue) { retval[j] = pmatch[0][j]; } else { break; } } } FS_FreeList(mapNames, nMaps); } return retval; } 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("vstr", Cmd_Vstr_f); Cmd_AddCommand("echo", Cmd_Echo_f); Cmd_AddCommand("alias", Cmd_Alias_f); Cmd_AddCommand("wait", Cmd_Wait_f); } void Cmd_Shutdown(void) { cmdalias_t *next; while (cmd_alias != NULL) { next = cmd_alias->next; Z_Free(cmd_alias->value); Z_Free(cmd_alias); cmd_alias = next; } } yquake2-QUAKE2_8_40/src/common/collision.c000066400000000000000000001042141465112212000203310ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 #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]; // DG: is casted to int32_t* in SV_FatPVS() so align accordingly static YQ2_ALIGNAS_TYPE(int32_t) 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 = (j >= 0) ? &map_surfaces[j] : &nullsurface; } } 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, char *name) { if (sv_entfile->value) { char s[MAX_QPATH]; char *buffer = NULL; int nameLen, bufLen; nameLen = strlen(name); strcpy(s, name); s[nameLen-3] = 'e'; s[nameLen-2] = 'n'; s[nameLen-1] = 't'; bufLen = FS_LoadFile(s, (void **)&buffer); if (buffer != NULL && bufLen > 1) { if (bufLen + 1 > sizeof(map_entitystring)) { Com_Printf("CMod_LoadEntityString: .ent file %s too large: %i > %lu.\n", s, bufLen, (unsigned long)sizeof(map_entitystring)); FS_FreeFile(buffer); } else { Com_Printf ("CMod_LoadEntityString: .ent file %s loaded.\n", s); numentitychars = bufLen; memcpy(map_entitystring, buffer, bufLen); map_entitystring[bufLen] = 0; /* jit entity bug - null terminate the entity string! */ FS_FreeFile(buffer); return; } } else if (bufLen != -1) { /* If the .ent file is too small, don't load. */ Com_Printf("CMod_LoadEntityString: .ent file %s too small.\n", s); FS_FreeFile(buffer); } } numentitychars = l->filelen; if (l->filelen + 1 > sizeof(map_entitystring)) { Com_Error(ERR_DROP, "Map has too large entity lump"); } memcpy(map_entitystring, cmod_base + l->fileofs, l->filelen); map_entitystring[l->filelen] = 0; } /* * 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) == 0 && (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[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]); /* From kmquake2: adding an extra parameter for .ent support. */ CMod_LoadEntityString(&header.lumps[LUMP_ENTITIES], name); 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_8_40/src/common/crc.c000066400000000000000000000235641465112212000171150ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/common/cvar.c000066400000000000000000000374341465112212000173020ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; typedef struct { char *old; char *new; } replacement_t; /* An ugly hack to rewrite CVARs loaded from config.cfg */ replacement_t replacements[] = { {"cd_shuffle", "ogg_shuffle"}, {"cl_anglekicks", "cl_kickangles"}, {"cl_drawfps", "cl_showfps"}, {"gl_drawentities", "r_drawentities"}, {"gl_drawworld", "r_drawworld"}, {"gl_fullbright", "r_fullbright"}, {"gl_lerpmodels", "r_lerpmodels"}, {"gl_lightlevel", "r_lightlevel"}, {"gl_norefresh", "r_norefresh"}, {"gl_novis", "r_novis"}, {"gl_speeds", "r_speeds"}, {"gl_clear", "r_clear"}, {"gl_consolescale", "r_consolescale"}, {"gl_hudscale", "r_hudscale"}, {"gl_menuscale", "r_scale"}, {"gl_customheight", "r_customheight"}, {"gl_customwidth", "r_customheight"}, {"gl_dynamic", "gl1_dynamic"}, {"gl_farsee", "r_farsee"}, {"gl_flashblend", "gl1_flashblend"}, {"gl_lockpvs", "r_lockpvs"}, {"gl_maxfps", "vid_maxfps"}, {"gl_mode", "r_mode"}, {"gl_modulate", "r_modulate"}, {"gl_overbrightbits", "gl1_overbrightbits"}, {"gl_palettedtextures", "r_palettedtextures"}, {"gl1_palettedtextures", "r_palettedtextures"}, {"gl_particle_min_size", "gl1_particle_min_size"}, {"gl_particle_max_size", "gl1_particle_max_size"}, {"gl_particle_size", "gl1_particle_size"}, {"gl_particle_att_a", "gl1_particle_att_a"}, {"gl_particle_att_b", "gl1_particle_att_b"}, {"gl_particle_att_c", "gl1_particle_att_c"}, {"gl_picmip", "gl1_picmip"}, {"gl_pointparameters", "gl1_pointparameters"}, {"gl_round_down", "gl1_round_down"}, {"gl_saturatelightning", "gl1_saturatelightning"}, {"gl_stencilshadows", "gl1_stencilshadows"}, {"gl_stereo", "gl1_stereo"}, {"gl_stereo_separation", "gl1_stereo_separation"}, {"gl_stereo_anaglyph_colors", "gl1_stereo_anaglyph_colors"}, {"gl_stereo_convergence", "gl1_stereo_convergence"}, {"gl_swapinterval", "r_vsync"}, {"gl_texturealphamode", "gl1_texturealphamode"}, {"gl_texturesolidmode", "gl1_texturesolidmode"}, {"gl_ztrick", "gl1_ztrick"}, {"gl_msaa_samples", "r_msaa_samples"}, {"gl_nolerp_list", "r_nolerp_list"}, {"gl_retexturing", "r_retexturing"}, {"gl_shadows", "r_shadows"}, {"gl_anisotropic", "r_anisotropic"}, {"gl_lightmap", "r_lightmap"}, {"gl1_polyblend", "gl_polyblend"}, {"gl_cull", "r_cull"}, {"intensity", "gl1_intensity"} }; static qboolean Cvar_InfoValidate(const 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; int i; /* An ugly hack to rewrite changed CVARs */ for (i = 0; i < sizeof(replacements) / sizeof(replacement_t); i++) { if (!strcmp(var_name, replacements[i].old)) { Com_Printf("cvar %s is deprecated, use %s instead\n", replacements[i].old, replacements[i].new); var_name = replacements[i].new; } } for (var = cvar_vars; var; var = var->next) { if (!strcmp(var_name, var->name)) { return var; } } return NULL; } static qboolean Cvar_IsFloat(const char *s) { int dot = '.'; if (*s == '-') { s++; } if (!*s) { return false; } do { int c; c = *s++; if (c == dot) { dot = 0; } else if (c < '0' || c > '9') { return false; } } while (*s); return true; } float Cvar_VariableValue(const 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; } /* * 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(const 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; if (var->default_string) Z_Free(var->default_string); if (!var_value) { var->default_string = CopyString(""); } else { var->default_string = CopyString(var_value); } 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; } } // if $game is the default one ("baseq2"), then use "" instead because // other code assumes this behavior (e.g. FS_BuildGameSpecificSearchPath()) if(strcmp(var_name, "game") == 0 && strcmp(var_value, BASEDIRNAME) == 0) { var_value = ""; } var = Z_Malloc(sizeof(*var)); var->name = CopyString(var_name); var->string = CopyString(var_value); var->default_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(const 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 $game is the default one ("baseq2"), then use "" instead because // other code assumes this behavior (e.g. FS_BuildGameSpecificSearchPath()) if(strcmp(var_name, "game") == 0 && strcmp(value, BASEDIRNAME) == 0) { value = ""; } 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); var->latched_string = NULL; } 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(const char *var_name, char *value) { return Cvar_Set2(var_name, value, true); } cvar_t * Cvar_Set(const char *var_name, char *value) { return Cvar_Set2(var_name, value, false); } cvar_t * Cvar_FullSet(const 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; } // if $game is the default one ("baseq2"), then use "" instead because // other code assumes this behavior (e.g. FS_BuildGameSpecificSearchPath()) if(strcmp(var_name, "game") == 0 && strcmp(value, BASEDIRNAME) == 0) { value = ""; } Z_Free(var->string); var->string = CopyString(value); var->value = (float)strtod(var->string, (char **)NULL); var->flags = flags; return var; } void Cvar_SetValue(const 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; } /* Another evil hack: The user has just changed 'game' trough the console. We reset userGivenGame to that value, otherwise we would revert to the initialy given game at disconnect. */ if (strcmp(v->name, "game") == 0) { Q_strlcpy(userGivenGame, Cmd_Argv(1), sizeof(userGivenGame)); } Cvar_Set(v->name, Cmd_Argv(1)); return true; } /* * Allows setting and defining of arbitrary cvars from console */ void Cvar_Set_f(void) { char *firstarg; int c, i; c = Cmd_Argc(); if ((c != 3) && (c != 4)) { Com_Printf("usage: set [u / s]\n"); return; } firstarg = Cmd_Argv(1); /* An ugly hack to rewrite changed CVARs */ for (i = 0; i < sizeof(replacements) / sizeof(replacement_t); i++) { if (!strcmp(firstarg, replacements[i].old)) { firstarg = replacements[i].new; } } if (c == 4) { int flags; 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(firstarg, Cmd_Argv(2), flags); } else { Cvar_Set(firstarg, Cmd_Argv(2)); } } /* * Appends lines containing "set variable value" for all variables * with the archive flag set to true. */ void Cvar_WriteVariables(const char *path) { cvar_t *var; char buffer[1024]; FILE *f; f = Q_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); } /* * Increments the given cvar by 1 or adds the * optional given float value to it. */ void Cvar_Inc_f(void) { char string[MAX_QPATH]; cvar_t *var; float value; if (Cmd_Argc() < 2) { Com_Printf("Usage: %s [value]\n", Cmd_Argv(0)); return; } var = Cvar_FindVar(Cmd_Argv(1)); if (!var) { Com_Printf("%s is not a cvar\n", Cmd_Argv(1)); return; } if (!Cvar_IsFloat(var->string)) { Com_Printf("\"%s\" is \"%s\", can't %s\n", var->name, var->string, Cmd_Argv(0)); return; } value = 1; if (Cmd_Argc() > 2) { value = atof(Cmd_Argv(2)); } if (!strcmp(Cmd_Argv(0), "dec")) { value = -value; } Com_sprintf(string, sizeof(string), "%f", var->value + value); Cvar_Set(var->name, string); } /* * Resets a cvar to its default value. */ void Cvar_Reset_f(void) { cvar_t *var; if (Cmd_Argc() < 2) { Com_Printf("Usage: %s \n", Cmd_Argv(0)); return; } var = Cvar_FindVar(Cmd_Argv(1)); if (!var) { Com_Printf("%s is not a cvar\n", Cmd_Argv(1)); return; } Com_Printf("%s: %s\n", var->name, var->default_string); Cvar_Set(var->name, var->default_string); } /* * Resets all known cvar (with the exception of `game') to * their default values. */ void Cvar_ResetAll_f(void) { cvar_t *var; for (var = cvar_vars; var; var = var->next) { if ((var->flags & CVAR_NOSET)) { continue; } else if (strcmp(var->name, "game") == 0) { continue; } Cvar_Set(var->name, var->default_string); } } /* * Toggles a cvar between 0 and 1 or the given values. */ void Cvar_Toggle_f(void) { cvar_t *var; int i, argc = Cmd_Argc(); if (argc < 2) { Com_Printf("Usage: %s [values]\n", Cmd_Argv(0)); return; } var = Cvar_FindVar(Cmd_Argv(1)); if (!var) { Com_Printf("%s is not a cvar\n", Cmd_Argv(1)); return; } if (argc < 3) { if (!strcmp(var->string, "0")) { Cvar_Set(var->name, "1"); } else if (!strcmp(var->string, "1")) { Cvar_Set(var->name, "0"); } else { Com_Printf("\"%s\" is \"%s\", can't toggle\n", var->name, var->string); } return; } for (i = 0; i < argc - 2; i++) { if (!Q_stricmp(var->string, Cmd_Argv(2 + i))) { i = (i + 1) % (argc - 2); Cvar_Set(var->name, Cmd_Argv(2 + i)); return; } } Com_Printf("\"%s\" is \"%s\", can't cycle\n", var->name, var->string); } /* * Reads in all archived cvars */ void Cvar_Init(void) { Cmd_AddCommand("cvarlist", Cvar_List_f); Cmd_AddCommand("dec", Cvar_Inc_f); Cmd_AddCommand("inc", Cvar_Inc_f); Cmd_AddCommand("reset", Cvar_Reset_f); Cmd_AddCommand("resetall", Cvar_ResetAll_f); Cmd_AddCommand("set", Cvar_Set_f); Cmd_AddCommand("toggle", Cvar_Toggle_f); } /* * Free list of cvars */ void Cvar_Fini(void) { cvar_t *var; for (var = cvar_vars; var;) { cvar_t *c = var->next; Z_Free(var->string); Z_Free(var->name); Z_Free(var->default_string); Z_Free(var); var = c; } Cmd_RemoveCommand("cvarlist"); Cmd_RemoveCommand("dec"); Cmd_RemoveCommand("inc"); Cmd_RemoveCommand("reset"); Cmd_RemoveCommand("resetall"); Cmd_RemoveCommand("set"); Cmd_RemoveCommand("toggle"); } yquake2-QUAKE2_8_40/src/common/filesystem.c000066400000000000000000001311551465112212000205260ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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. * * ======================================================================= */ #ifndef _MSC_VER #include #endif #include "header/common.h" #include "header/glob.h" #include "unzip/unzip.h" #include "../client/sound/header/vorbis.h" #define MAX_HANDLES 512 #define MAX_MODS 32 #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. */ unzFile *zip; /* (file or zip) */ } 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; unzFile *pk3; qboolean isProtectedPak; 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, PK3 } 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}, {"pk2", PK3}, {"pk3", PK3}, {"pkz", PK3}, {"zip", PK3} }; char datadir[MAX_OSPATH]; char fs_gamedir[MAX_OSPATH]; qboolean file_from_protected_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; // -------- #if _WIN32 /* * We need some trickery to make minizip Unicode compatible... */ #include #include "unzip/ioapi.h" zlib_filefunc_def zlib_file_api; static voidpf ZCALLBACK fopen_file_func_utf(voidpf opaque, const char *filename, int mode) { FILE* file = NULL; WCHAR *mode_fopen = NULL; WCHAR wfilename[MAX_OSPATH]; if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) { mode_fopen = L"rb"; } else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) { mode_fopen = L"r+b"; } else if (mode & ZLIB_FILEFUNC_MODE_CREATE) { mode_fopen = L"wb"; } if (!((filename == NULL) || (mode_fopen == NULL))) { MultiByteToWideChar(CP_UTF8, 0, filename, -1, wfilename, sizeof(wfilename)); file = _wfopen((const wchar_t *) wfilename, mode_fopen); } return file; } #endif // -------- /* * 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) && (handle->zip == NULL)) { 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); } else if (handle->zip) { unzCloseCurrentFile(handle->zip); unzClose(handle->zip); } 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 *rawname, fileHandle_t *f, qboolean gamedir_only) { char path[MAX_OSPATH], lwrName[MAX_OSPATH]; fsHandle_t *handle; fsPack_t *pack; fsSearchPath_t *search; int i; // Remove self references and empty dirs from the requested path. // ZIPs and PAKs don't support them, but they may be hardcoded in // some custom maps or models. char name[MAX_QPATH] = {0}; size_t namelen = strlen(rawname); for (int input = 0, output = 0; input < namelen; input++) { // Remove self reference. if (rawname[input] == '.') { if (output > 0) { // Inside the path. if (name[output - 1] == '/' && rawname[input + 1] == '/') { input++; continue; } } else { // At the beginning. Note: This is save because the Quake II // VFS doesn't have a current working dir. Paths are always // absolute. if (rawname[input + 1] == '/') { continue; } } } // Empty dir. if (rawname[input] == '/') { if (rawname[input + 1] == '/') { continue; } } // Paths starting with a /. I'm not sure if this is // a problem. It shouldn't hurt to remove the leading // slash, though. if (rawname[input] == '/' && output == 0) { continue; } name[output] = rawname[input]; output++; } file_from_protected_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); } // save the name with *correct case* in the handle // (relevant for savegames, when starting map with wrong case but it's still found // because it's from pak, but save/bla/MAPname.sav/sv2 will have wrong case and can't be found then) Q_strlcpy(handle->name, pack->files[i].name, sizeof(handle->name)); if (pack->pak) { /* PAK */ if (pack->isProtectedPak) { file_from_protected_pak = true; } handle->file = Q_fopen(pack->name, "rb"); if (handle->file) { fseek(handle->file, pack->files[i].offset, SEEK_SET); return pack->files[i].size; } } else if (pack->pk3) { /* PK3 */ if (pack->isProtectedPak) { file_from_protected_pak = true; } #ifdef _WIN32 handle->zip = unzOpen2(pack->name, &zlib_file_api); #else handle->zip = unzOpen(pack->name); #endif 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); } } 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 = Q_fopen(path, "rb"); if (!handle->file) { Com_sprintf(lwrName, sizeof(lwrName), "%s", handle->name); Q_strlwr(lwrName); Com_sprintf(path, sizeof(path), "%s/%s", search->path, lwrName); handle->file = Q_fopen(path, "rb"); } 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); } else if (handle->zip) { r = unzReadCurrentFile(handle->zip, buf, remaining); } 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, "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); } else if (handle->zip) { r = unzReadCurrentFile(handle->zip, buf, remaining); } 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); } fsRawPath_t *FS_FreeRawPaths(fsRawPath_t *start, fsRawPath_t *end) { fsRawPath_t *cur = start; fsRawPath_t *next; while (cur != end) { next = cur->next; Z_Free(cur); cur = next; } return cur; } fsSearchPath_t *FS_FreeSearchPaths(fsSearchPath_t *start, fsSearchPath_t *end) { fsSearchPath_t *cur = start; fsSearchPath_t *next; while (cur != end) { if (cur->pack) { if (cur->pack->pak) { fclose(cur->pack->pak); } if (cur->pack->pk3) { unzClose(cur->pack->pk3); } Z_Free(cur->pack->files); Z_Free(cur->pack); } next = cur->next; Z_Free(cur); cur = next; } return cur; } /* * 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 = NULL; /* PAK info. */ handle = Q_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, "%s: '%s' is not a pack file", __func__, packPath); } header.dirofs = LittleLong(header.dirofs); header.dirlen = LittleLong(header.dirlen); numFiles = header.dirlen / sizeof(dpackfile_t); if ((numFiles == 0) || (header.dirlen < 0) || (header.dirofs < 0)) { fclose(handle); Com_Error(ERR_FATAL, "%s: '%s' is too short.", __func__, packPath); } if (numFiles > MAX_FILES_IN_PACK) { Com_Printf("%s: '%s' has %i > %i files\n", __func__, packPath, numFiles, MAX_FILES_IN_PACK); } info = malloc(header.dirlen); if (!info) { Com_Error(ERR_FATAL, "%s: '%s' is to big for read %d", __func__, packPath, header.dirlen); } 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); } free(info); pack = Z_Malloc(sizeof(fsPack_t)); Q_strlcpy(pack->name, packPath, sizeof(pack->name)); pack->pak = handle; pack->pk3 = NULL; pack->numFiles = numFiles; pack->files = files; Com_Printf("Added packfile '%s' (%i files).\n", pack->name, numFiles); return pack; } /* * 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. */ #ifdef _WIN32 handle = unzOpen2(packPath, &zlib_file_api); #else handle = unzOpen(packPath); #endif 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 <= 0) { unzClose(handle); Com_Error(ERR_FATAL, "%s: '%s' has %i files", __func__, 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; } /* * 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"); Com_Printf("Raw search paths:\n"); for (fsRawPath_t* raw = fs_rawPath; raw != NULL; raw = raw->next) { Com_Printf("%s\n", raw->path); } Com_Printf("\n"); for (i = 0, handle = fs_handles; i < MAX_HANDLES; i++, handle++) { if ((handle->file != NULL) || (handle->zip != NULL)) { 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"); Com_Printf("%i files in PAK/PK2/PK3/ZIP files.\n", totalFiles); } /* * 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 *)); YQ2_COM_CHECK_OOM(list, "calloc()", (size_t)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 *)); YQ2_COM_CHECK_OOM(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 *)); YQ2_COM_CHECK_OOM(list, "realloc()", (size_t)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 *)); YQ2_COM_CHECK_OOM(list, "2nd realloc()", (size_t)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 *)); YQ2_COM_CHECK_OOM(tmplist, "malloc()", (size_t)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 *)); YQ2_COM_CHECK_OOM(list, "3rd realloc()", (size_t)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]); list[i] = 0; } free(list); list = 0; } /* * Comparator for mod sorting */ static int Q_sort_modcmp(const void *p1, const void *p2) { static const char *first_mods[] = {BASEDIRNAME, "xatrix", "rogue", "ctf"}; static const unsigned short int first_mods_qty = 4; const char * s1 = * (char * const *)p1; const char * s2 = * (char * const *)p2; for (unsigned short int i = 0; i < first_mods_qty; i++) { if (!Q_stricmp(first_mods[i], s1)) { return -1; } if (!Q_stricmp(first_mods[i], s2)) { return 1; } } return Q_stricmp(s1, s2); } /* * Combs all Raw search paths to find game dirs containing PAK/PK2/PK3 files. * Returns an alphabetized array of unique relative dir names. */ char** FS_ListMods(int *nummods) { int nmods = 0, numdirchildren, numpacksinchilddir, searchpathlength; char findnamepattern[MAX_OSPATH], modname[MAX_QPATH], searchpath[MAX_OSPATH]; char **dirchildren, **packsinchilddir, **modnames; modnames = malloc((MAX_QPATH + 1) * (MAX_MODS + 1)); memset(modnames, 0, (MAX_QPATH + 1) * (MAX_MODS + 1)); // iterate over all Raw paths for (fsRawPath_t *search = fs_rawPath; search; search = search->next) { searchpathlength = strlen(search->path); if(!searchpathlength) { continue; } // make sure this Raw path ends with a '/' otherwise FS_ListFiles will open its parent dir if(search->path[searchpathlength - 1] != '/') { Com_sprintf(searchpath, sizeof(searchpath), "%s/*", search->path); } else { Com_sprintf(searchpath, sizeof(searchpath), "%s*", search->path); } dirchildren = FS_ListFiles(searchpath, &numdirchildren, 0, 0); if (dirchildren == NULL) { continue; } // iterate over the children of this Raw path (unless we've already got enough mods) for (int i = 0; i < numdirchildren && nmods < MAX_MODS; i++) { if(dirchildren[i] == NULL) { continue; } numpacksinchilddir = 0; // iterate over supported pack types, but ignore ZIP files (they cause false positives) for (int j = 0; j < sizeof(fs_packtypes) / sizeof(fs_packtypes[0]); j++) { if (strcmp("zip", fs_packtypes[j].suffix) != 0) { Com_sprintf(findnamepattern, sizeof(findnamepattern), "%s/*.%s", dirchildren[i], fs_packtypes[j].suffix); packsinchilddir = FS_ListFiles(findnamepattern, &numpacksinchilddir, 0, 0); FS_FreeList(packsinchilddir, numpacksinchilddir); // if this dir has some pack files, add it if not already in the list if (numpacksinchilddir > 0) { qboolean matchfound = false; Com_sprintf(modname, sizeof(modname), "%s", strrchr(dirchildren[i], '/') + 1); for (int k = 0; k < nmods; k++) { if (strcmp(modname, modnames[k]) == 0) { matchfound = true; break; } } if (!matchfound) { modnames[nmods] = malloc(strlen(modname) + 1); strcpy(modnames[nmods], modname); nmods++; } break; } } } } FS_FreeList(dirchildren, numdirchildren); } modnames[nmods] = 0; qsort(modnames, nmods, sizeof(modnames[0]), Q_sort_modcmp); *nummods = nmods; return modnames; } /* * 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"); } } // -------- /* * This function returns true if a real file (e.g. not something * in a pak, somthing in the file system itself) exists in the * current gamedir. */ qboolean FS_FileInGamedir(const char *file) { char path[MAX_OSPATH]; FILE *fd; Com_sprintf(path, sizeof(path), "%s/%s", fs_gamedir, file); if ((fd = Q_fopen(path, "rb")) != NULL) { fclose(fd); return true; } else { return false; } } /* * This function loads the given .pak / .pk3 File from the * fs_gamedir. There's no need to load from other dirs since * fs_gamedir is the only dir written to at runtime. */ qboolean FS_AddPAKFromGamedir(const char *pak) { char path[MAX_OSPATH]; Com_sprintf(path, sizeof(path), "%s/%s", fs_gamedir, pak); // Check of the file really exists. FILE *fd; if ((fd = Q_fopen(path, "rb")) == NULL) { assert(fd && "FS_AddPAKfromGamedir() called with nonexisting file");; } else { fclose(fd); } // Depending on filetype we must load it as .pak or .pk3. for (int i = 0; i < sizeof(fs_packtypes) / sizeof(fs_packtypes[0]); i++) { // Not the current filetype, next one please. if (strncmp(pak + strlen(pak) - strlen(fs_packtypes[i].suffix), fs_packtypes[i].suffix, strlen(fs_packtypes[i].suffix))) { continue; } fsPack_t *pakfile = NULL; switch (fs_packtypes[i].format) { case PAK: pakfile = FS_LoadPAK(path); break; case PK3: pakfile = FS_LoadPK3(path); break; } if (pakfile == NULL) { // Couldn't load it. return false; } else { // Add it. fsSearchPath_t *search = Z_Malloc(sizeof(fsSearchPath_t)); search->pack = pakfile; search->next = fs_searchPaths; fs_searchPaths = search; return true; } } // Apparently we didn't load anything. return false; } const char* FS_GetNextRawPath(const char* lastRawPath) { assert(fs_rawPath != NULL && "Don't call this if before FS_InitFilesystem()"); if (lastRawPath == NULL) { return fs_rawPath->path; } for (fsRawPath_t* rp = fs_rawPath; rp != NULL; rp = rp->next) { if (rp->path == lastRawPath) { return (rp->next != NULL) ? rp->next->path : NULL; } } return NULL; } #ifdef _MSC_VER // looks like MSVC/the Windows CRT doesn't have basename() // returns the last part of the given pathname, after last (back)slash // if the last character is a (back)slash, it's removed (set to '\0') static char* basename( char* n ) { size_t l = strlen(n); while (n[l - 1] == '\\' || n[l - 1] == '/') // cut off trailing (back)slashes, if any { --l; n[l] = '\0'; } char* r1 = strrchr(n, '\\'); char* r2 = strrchr(n, '/'); if (r1 != NULL) return (r2 == NULL || r1 > r2) ? (r1 + 1) : (r2 + 1); return (r2 != NULL) ? (r2 + 1) : n; } #endif // _MSC_VER void FS_AddDirToSearchPath(char *dir, qboolean create) { char *file; char **list; char path[MAX_OSPATH]; char *tmp; int i, j, k; int nfiles; fsPack_t *pack = NULL; fsSearchPath_t *search; qboolean nextpak; 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] == '\\') { 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; // Numbered paks contain the official game data, they // need to be added first and are marked protected. // Files from protected paks are never offered for // download. 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); if (pack) { pack->isProtectedPak = true; } break; case PK3: pack = FS_LoadPK3(path); if (pack) { pack->isProtectedPak = false; } break; } if (pack == NULL) { continue; } search = Z_Malloc(sizeof(fsSearchPath_t)); search->pack = pack; search->next = fs_searchPaths; fs_searchPaths = search; } } // All other pak files are added after the numbered paks. // They aren't sorted in any way, but added in the same // sequence as they're returned by FS_ListFiles. This is // fragile and file system dependend. We cannot change // this, since it might break existing installations. 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, 0)) == NULL) { continue; } for (j = 0; j < nfiles - 1; j++) { // Sort out numbered paks. This is as inefficient as // it can be, but it doesn't matter. This is done only // once at client or game startup. nextpak = false; for (k = 0; k < MAX_PAKS; k++) { // basename() may alter the given string. // We need to work around that... tmp = strdup(list[j]); file = basename(tmp); Com_sprintf(path, sizeof(path), "pak%d.%s", k, fs_packtypes[i].suffix); if (Q_strcasecmp(path, file) == 0) { nextpak = true; free(tmp); break; } free(tmp); } if (nextpak) { continue; } switch (fs_packtypes[i].format) { case PAK: pack = FS_LoadPAK(list[j]); break; case PK3: pack = FS_LoadPK3(list[j]); break; } if (pack == NULL) { continue; } pack->isProtectedPak = false; 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); } // filesystem.c is used by the client and the server, // it includes common.h only. Not the client not the // server header. The game reset logic messes with // client state, so we need some forwar declarations // here. #ifndef DEDICATED_ONLY // Variables extern qboolean menu_startdemoloop; // Functions void CL_WriteConfiguration(void); #endif 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] = {0}; int i; fsRawPath_t *search; #ifndef DEDICATED_ONLY // Write the config. Otherwise changes made by the // current mod are lost. CL_WriteConfiguration(); #endif // empty string means baseq2 if(dir[0] == '\0') { dir = BASEDIRNAME; } // This is against PEBCAK. The user may give us paths like // xatrix/ or even /home/stupid/quake2/xatrix. if (!strcmp(dir, ".") || strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\")) { Com_Printf("Gamedir should be a single filename, not a path.\n"); return; } // We may already have specialised directories in our search // path. This can happen if the server changes the mod. Let's // remove them. fs_searchPaths = FS_FreeSearchPaths(fs_searchPaths, fs_baseSearchPaths); /* 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) || (fs_handles[i].zip != NULL))) { 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) { 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. Com_sprintf(path, sizeof(path), "%s/%s", fs_rawPath->path, BASEDIRNAME); Q_strlcpy(fs_gamedir, 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); // the gamedir has changed, so read in the corresponding configs Qcommon_ExecConfigs(false); #ifndef DEDICATED_ONLY // This function is called whenever the game cvar changes => // the player wants to switch to another mod. In that case the // list of music tracks needs to be loaded again (=> tracks // are possibly from the new mod dir) OGG_InitTrackList(); // ...and the current list of maps in the "start network server" menu is // cleared so that it will be re-initialized when the menu is accessed if (mapnames != NULL) { for (i = 0; i < nummaps; i++) { free(mapnames[i]); } free(mapnames); mapnames = NULL; } // Start the demoloop, if requested. This is kind of hacky: Normaly the // demo loop would be started by the menu, after changeing the 'game' // cvar. However, the demo loop is implemented by aliases. Since the // game isn't changed right after the cvar is set (but when the control // flow enters this function) the aliases evaulate to the wrong game. // Work around that by injection the demo loop into the command buffer // here, right after the game was changed. Do it only when the game was // changed though the menu, otherwise we might break into the demo loop // after we've received a latched cvar from the server. if (menu_startdemoloop) { Cbuf_AddText("d1\n"); menu_startdemoloop = false; } #endif } // returns the filename used to open f, but (if opened from pack) in correct case // returns NULL if f is no valid handle const char* FS_GetFilenameForHandle(fileHandle_t f) { fsHandle_t* fsh = FS_GetFileByHandle(f); if(fsh) { return fsh->name; } return NULL; } // -------- static void FS_AddDirToRawPath (const char *rawdir, qboolean create, qboolean required) { char dir[MAX_OSPATH] = {0}; // Get the realpath. if (!Sys_Realpath(rawdir, dir, sizeof(dir))) { if (required) { Com_Error(ERR_FATAL, "Couldn't add required directory %s to search path\n", rawdir); } return; } // Convert backslashes to forward slashes. for (int i = 0; i < strlen(dir); i++) { if (dir[i] == '\\') { dir[i] = '/'; } } // Make sure that the dir doesn't end with a slash. for (size_t s = strlen(dir) - 1; s > 0; s--) { if (dir[s] == '/') { dir[s] = '\0'; } else { break; } } // Bail out if the dir was already added. for (fsRawPath_t *search = fs_rawPath; search; search = search->next) { if (strcmp(search->path, dir) == 0) { return; } } // Add the directory. fsRawPath_t *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! Required, // otherwise the config cannot be written. if (!is_portable) { const char *homedir = Sys_GetHomeDir(); if (homedir != NULL) { FS_AddDirToRawPath(homedir, true, true); } } // Add binary dir. Required, because the renderer // libraries are loaded from it. const char *binarydir = Sys_GetBinaryDir(); if(binarydir[0] != '\0') { FS_AddDirToRawPath(binarydir, false, true); } // Add data dir. Required, when the user gives us // a data dir he expects it in a working state. FS_AddDirToRawPath(datadir, false, true); // Add SYSTEMDIR. Optional, the user may have a // binary compiled with SYSTEMWIDE (installed from // packages), but no systemwide game data. #ifdef SYSTEMWIDE FS_AddDirToRawPath(SYSTEMDIR, false, 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. Required, if the // user sets a CD path, he expects data getting // read from the CD. if (fs_cddir->string[0] != '\0') { FS_AddDirToRawPath(fs_cddir->string, false, true); } } // -------- 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); // Deprecation warning, can be removed at a later time. if (strcmp(fs_basedir->string, ".") != 0) { Com_Printf("+set basedir is deprecated, use -datadir instead\n"); strcpy(datadir, fs_basedir->string); } else if (strlen(datadir) == 0) { strcpy(datadir, "."); } #ifdef _WIN32 // setup minizip for Unicode compatibility fill_fopen_filefunc(&zlib_file_api); zlib_file_api.zopen_file = fopen_file_func_utf; #endif // Build search path FS_BuildRawPath(); FS_BuildGenericSearchPath(); if (fs_gamedirvar->string[0] != '\0') { FS_BuildGameSpecificSearchPath(fs_gamedirvar->string); } #ifndef DEDICATED_ONLY else { // no mod, but we still need to get the list of OGG tracks for background music OGG_InitTrackList(); } #endif // Debug output Com_Printf("Using '%s' for writing.\n", fs_gamedir); } void FS_ShutdownFilesystem(void) { fs_searchPaths = FS_FreeSearchPaths(fs_searchPaths, NULL); fs_rawPath = FS_FreeRawPaths(fs_rawPath, NULL); fs_baseSearchPaths = NULL; } yquake2-QUAKE2_8_40/src/common/frame.c000066400000000000000000000463521465112212000174400ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * ======================================================================= * * Platform independent initialization, main loop and 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 *color_terminal; 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 *busywait; cvar_t *cl_async; cvar_t *cl_timedemo; cvar_t *vid_maxfps; cvar_t *host_speeds; cvar_t *log_stats; cvar_t *showtrace; #endif // Forward declarations #ifndef DEDICATED_ONLY float 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 paths. int curtime; #ifndef DEDICATED_ONLY void Key_Init(void); void Key_Shutdown(void); void SCR_EndLoadingPlaque(void); #endif // Is the game portable? qboolean is_portable; // Game given by user char userGivenGame[MAX_QPATH]; // Game should quit next frame. // Hack for the signal handlers. qboolean quitnextframe; #ifndef DEDICATED_ONLY #ifdef SDL_CPUPauseInstruction # define Sys_CpuPause() SDL_CPUPauseInstruction() #else static YQ2_ATTR_INLINE void Sys_CpuPause(void) { #if defined(__GNUC__) #if (__i386 || __x86_64__) asm volatile("pause"); #elif defined(__aarch64__) || (defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(__ARM_ARCH_6K__) asm volatile("yield"); #elif defined(__powerpc__) || defined(__powerpc64__) asm volatile("or 27,27,27"); #elif defined(__riscv) && __riscv_xlen == 64 asm volatile(".insn i 0x0F, 0, x0, x0, 0x010"); #endif #elif defined(_MSC_VER) #if defined(_M_IX86) || defined(_M_X64) _mm_pause(); #elif defined(_M_ARM) || defined(_M_ARM64) __yield(); #endif #endif } #endif #endif static void Qcommon_Frame(int usec); // ---- static void Qcommon_Buildstring(void) { int i; int verLen; const char* versionString; versionString = va("Yamagi Quake II v%s", YQ2VERSION); verLen = strlen(versionString); printf("\n%s\n", versionString); for( i = 0; i < verLen; ++i) { printf("="); } printf("\n"); #ifndef DEDICATED_ONLY printf("Client build options:\n"); #ifdef USE_CURL printf(" + cURL HTTP downloads\n"); #else printf(" - cURL HTTP downloads\n"); #endif #ifdef USE_OPENAL printf(" + OpenAL audio\n"); #else printf(" - OpenAL audio\n"); #endif #ifdef SYSTEMWIDE printf(" + Systemwide installation\n"); #else printf(" - Systemwide installation\n"); #endif #endif printf("Platform: %s\n", YQ2OSTYPE); printf("Architecture: %s\n", YQ2ARCH); } static void Qcommon_Mainloop(void) { long long newtime; long long oldtime = Sys_Microseconds(); /* The mainloop. The legend. */ while (1) { #ifndef DEDICATED_ONLY if (!cl_timedemo->value) { // Throttle the game a little bit. if (busywait->value) { long long spintime = Sys_Microseconds(); while (1) { /* Give the CPU a hint that this is a very tight spinloop. One PAUSE instruction each loop is enough to reduce power consumption and head dispersion a lot, it's 95°C against 67°C on a Kaby Lake laptop. */ Sys_CpuPause(); if (Sys_Microseconds() - spintime >= 5) { break; } } } else { Sys_Nanosleep(5000); } } #else Sys_Nanosleep(850000); #endif newtime = Sys_Microseconds(); // Save global time for network- und input code. curtime = (int)(newtime / 1000ll); Qcommon_Frame(newtime - oldtime); oldtime = newtime; } } void Qcommon_ExecConfigs(qboolean gameStartUp) { Cbuf_AddText("exec default.cfg\n"); Cbuf_AddText("exec yq2.cfg\n"); Cbuf_AddText("exec config.cfg\n"); Cbuf_AddText("exec autoexec.cfg\n"); if (gameStartUp) { /* Process cmd arguments only startup. */ Cbuf_AddEarlyCommands(true); } Cbuf_Execute(); } static qboolean checkForHelp(int argc, char **argv) { const char* helpArgs[] = { "--help", "-h", "-help", "-?", "/?" }; const int numHelpArgs = sizeof(helpArgs)/sizeof(helpArgs[0]); for (int i=1; i\n"); printf(" set the name of your config directory\n"); printf("-datadir \n"); printf(" set path to your Quake2 game data (the directory baseq2/ is in)\n"); printf("-portable\n"); printf(" Write (savegames, configs, ...) in the binary directory\n"); printf("+exec \n"); printf(" execute the given config (mainly relevant for dedicated servers)\n"); printf("+set \n"); printf(" Set the given cvar to the given value, e.g. +set vid_fullscreen 0\n"); printf("\nSome interesting cvars:\n"); printf("+set game \n"); printf(" start the given addon/mod, e.g. +set game xatrix\n"); #ifndef DEDICATED_ONLY printf("+set vid_fullscreen <0 or 1>\n"); printf(" start game in windowed (0) or desktop fullscreen (1)\n"); printf(" or classic fullscreen (2) mode\n"); printf("+set r_mode \n"); printf(" start game in resolution belonging to ,\n"); printf(" use -1 for custom resolutions:\n"); printf("+set r_customwidth \n"); printf("+set r_customheight \n"); printf(" if r_mode is set to -1, these cvars allow you to specify the\n"); printf(" width/height of your custom resolution\n"); printf("+set vid_renderer \n"); printf(" Selects the render backend. Currently available:\n"); printf(" 'gl1' (the OpenGL 1.x renderer),\n"); printf(" 'gl3' (the OpenGL 3.2 renderer),\n"); printf(" 'soft' (the software renderer)\n"); #endif // DEDICATED_ONLY printf("\nSee https://github.com/yquake2/yquake2/blob/master/doc/04_cvarlist.md\nfor some more cvars\n"); return true; } } } return false; } void Qcommon_Init(int argc, char **argv) { // Jump point used in emergency situations. if (setjmp(abortframe)) { Sys_Error("Error during initialization"); } if (checkForHelp(argc, argv)) { // ok, --help or similar commandline option was given // and info was printed, exit the game now exit(1); } // Print the build and version string Qcommon_Buildstring(); // Seed PRNG randk_seed(); // 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(); // remember the initial game name that might have been set on commandline { cvar_t* gameCvar = Cvar_Get("game", "", CVAR_LATCH | CVAR_SERVERINFO); const char* game = ""; if(gameCvar->string && gameCvar->string[0]) { game = gameCvar->string; } Q_strlcpy(userGivenGame, game, sizeof(userGivenGame)); } // The filesystems needs to be initialized after the cvars. FS_InitFilesystem(); // Add and execute configuration files. Qcommon_ExecConfigs(true); // Zone malloc statistics. Cmd_AddCommand("z_stats", Z_Stats_f); // cvars cl_maxfps = Cvar_Get("cl_maxfps", "-1", CVAR_ARCHIVE); developer = Cvar_Get("developer", "0", 0); fixedtime = Cvar_Get("fixedtime", "0", 0); color_terminal = Cvar_Get("colorterminal", "1", CVAR_ARCHIVE); 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 busywait = Cvar_Get("busywait", "1", CVAR_ARCHIVE); cl_async = Cvar_Get("cl_async", "1", CVAR_ARCHIVE); cl_timedemo = Cvar_Get("timedemo", "0", 0); dedicated = Cvar_Get("dedicated", "0", CVAR_NOSET); vid_maxfps = Cvar_Get("vid_maxfps", "300", 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"); // Call the main loop Qcommon_Mainloop(); } #ifndef DEDICATED_ONLY static void Qcommon_Frame(int usec) { // Used for the dedicated server console. char *s; // Statistics. int time_before = 0; int time_between = 0; int time_after; // Target packetframerate. float pfps; // Target renderframerate. float 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 or the server. The minimal interval is about 1000 microseconds. */ qboolean renderframe = true; /* Tells the client to shutdown. Used by the signal handlers. */ if (quitnextframe) { Cbuf_AddText("quit"); } /* In case of ERR_DROP we're jumping here. Don't know if that's 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 = Q_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) { usec = (int)fixedtime->value; } else if (timescale->value) { usec *= 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; } /* We can render 1000 frames at maximum, because the minimum frametime of the client is 1 millisecond. And of course we need to render something, the framerate can never be less then 1. Cap vid_maxfps between 1 and 999. */ if (vid_maxfps->value > 999 || vid_maxfps->value < 1) { Cvar_SetValue("vid_maxfps", 999); } if (cl_maxfps->value > 250) { Cvar_SetValue("cl_maxfps", 250); } // Calculate target and renderframerate. if (R_IsVSyncActive()) { float refreshrate = GLimp_GetRefreshRate(); // using refreshRate - 2, because targeting a value slightly below the // (possibly not 100% correctly reported) refreshRate would introduce jittering, so only // use vid_maxfps if it looks like the user really means it to be different from refreshRate if (vid_maxfps->value < refreshrate - 2 ) { rfps = vid_maxfps->value; // we can't have more packet frames than render frames, so limit pfps to rfps pfps = (cl_maxfps->value > rfps) ? rfps : cl_maxfps->value; } else // target refresh rate, not vid_maxfps { /* if vsync is active, we increase the target framerate a bit for two reasons 1. On Windows, GLimp_GetFrefreshRate() (or the SDL counterpart, or even the underlying WinAPI function) often returns a too small value, like 58 or 59 when it's really 59.95 and thus (as integer) should be 60 2. vsync will throttle us to refreshrate anyway, so there is no harm in starting the frame *a bit* earlier, instead of risking starting it too late */ rfps = refreshrate * 1.2f; // we can't have more packet frames than render frames, so limit pfps to rfps // but in this case use tolerance for comparison and assign rfps with tolerance pfps = (cl_maxfps->value < refreshrate - 2) ? cl_maxfps->value : rfps; } } else { rfps = vid_maxfps->value; // we can't have more packet frames than render frames, so limit pfps to rfps pfps = (cl_maxfps->value > rfps) ? rfps : cl_maxfps->value; } // cl_maxfps <= 0 means: automatically choose a packet framerate that should work // well with the render framerate, which is the case if rfps is a multiple of pfps if (cl_maxfps->value <= 0.0f && cl_async->value != 0.0f) { // packet framerates between about 45 and 90 should be ok, // with other values the game (esp. movement/clipping) can become glitchy // as pfps must be <= rfps, for rfps < 90 just use that as pfps if (rfps < 90.0f) { pfps = rfps; } else { /* we want an integer divider, so every div-th renderframe is a packetframe. this formula gives nice dividers that keep pfps as close as possible to 60 (which seems to be ideal): - for < 150 rfps div will be 2, so pfps will be between 45 and ~75 => exactly every second renderframe we also run a packetframe - for < 210 rfps div will be 3, so pfps will be between 50 and ~70 => exactly every third renderframe we also run a packetframe - etc, the higher the rfps, the closer the pfps-range will be to 60 (and you probably get the very best results by running at a render framerate that's a multiple of 60) */ float div = round(rfps/60); pfps = rfps/div; } } // Calculate timings. packetdelta += usec; renderdelta += usec; clienttimedelta += usec; servertimedelta += usec; if (!cl_timedemo->value) { if (cl_async->value) { // Render frames. if (renderdelta < (1000000.0f / rfps)) { renderframe = false; } // Network frames. float packettargetdelta = 1000000.0f / pfps; // "packetdelta + renderdelta/2 >= packettargetdelta" if now we're // closer to when we want to run the next packetframe than we'd // (probably) be after the next render frame // also, we only run packetframes together with renderframes, // because we must have at least one render frame between two packet frames // TODO: does it make sense to use the average renderdelta of the last X frames // instead of just the last renderdelta? if (!renderframe || packetdelta + renderdelta/2 < packettargetdelta) { packetframe = false; } } else { // Cap frames at target framerate. if (renderdelta < (1000000.0f / rfps)) { renderframe = false; packetframe = false; } } } // 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 and mark frame. if (packetframe) { packetdelta = 0; } if (renderframe) { renderdelta = 0; } } #else static void Qcommon_Frame(int usec) { // 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; /* Tells the client to shutdown. Used by the signal handlers. */ if (quitnextframe) { Cbuf_AddText("quit"); } /* 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) { usec = (int)fixedtime->value; } else if (timescale->value) { usec *= timescale->value; } // Target framerate. pfps = (int)cl_maxfps->value; // Calculate timings. packetdelta += usec; servertimedelta += usec; // 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. packetdelta = 0; } } #endif void Qcommon_Shutdown(void) { FS_ShutdownFilesystem(); Cvar_Fini(); #ifndef DEDICATED_ONLY Key_Shutdown(); #endif Cmd_Shutdown(); } yquake2-QUAKE2_8_40/src/common/glob.c000066400000000000000000000075151465112212000172670ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/common/header/000077500000000000000000000000001465112212000174205ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/common/header/common.h000066400000000000000000000640451465112212000210720ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 "8.40" #define BASEDIRNAME "baseq2" #ifndef YQ2OSTYPE #error YQ2OSTYPE should be defined by the build system #endif #ifndef BUILD_DATE #define BUILD_DATE __DATE__ #endif #ifdef _WIN32 #define CFGDIR "YamagiQ2" #else #ifndef __HAIKU__ #define CFGDIR ".yq2" #else #define CFGDIR "yq2" #endif #endif #ifndef YQ2ARCH #ifdef _MSC_VER // Setting YQ2ARCH for VisualC++ from CMake doesn't work when using VS integrated CMake // so set it in code instead #ifdef YQ2ARCH #undef YQ2ARCH #endif #ifdef _M_X64 // this matches AMD64 and ARM64EC (but not regular ARM64), but they're supposed to be binary-compatible somehow, so whatever #define YQ2ARCH "x86_64" #elif defined(_M_ARM64) #define YQ2ARCH "arm64" #elif defined(_M_ARM) #define YQ2ARCH "arm" #elif defined(_M_IX86) #define YQ2ARCH "x86" #else // if you're not targeting one of the aforementioned architectures, // check https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros // to find out how to detect yours and add it here - and please send a patch :) #error "Unknown CPU architecture!" // (for a quick and dirty solution, comment out the previous line, but keep in mind // that savegames may not be compatible with other builds of Yamagi Quake II) #define YQ2ARCH "UNKNOWN" #endif // _M_X64 etc #else // other compilers than MSVC #error YQ2ARCH should be defined by the build system #endif // _MSC_VER #endif // YQ2ARCH /* ================================================================== */ 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(const 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_Shutdown(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); char *Cmd_CompleteMapCommand(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(const 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(const char *var_name, char *value); /* will create the variable if it doesn't exist */ cvar_t *Cvar_ForceSet(const char *var_name, char *value); /* will set the variable even if NOSET or LATCH */ cvar_t *Cvar_FullSet(const char *var_name, char *value, int flags); void Cvar_SetValue(const char *var_name, float value); /* expands value to a string and calls Cvar_Set */ float Cvar_VariableValue(const 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(const 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(const 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 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); // returns the filename used to open f, but (if opened from pack) in correct case // returns NULL if f is no valid handle const char* FS_GetFilenameForHandle(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_ShutdownFilesystem(void); void FS_BuildGameSpecificSearchPath(char *dir); char *FS_Gamedir(void); char *FS_NextPath(char *prevpath); int FS_LoadFile(char *path, void **buffer); qboolean FS_FileInGamedir(const char *file); qboolean FS_AddPAKFromGamedir(const char *pak); const char* FS_GetNextRawPath(const char* lastRawPath); char **FS_ListMods(int *nummods); /* 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)(int, char *)); void Com_EndRedirect(void); void Com_Printf(const char *fmt, ...) PRINTF_ATTR(1, 2); void Com_DPrintf(const char *fmt, ...) PRINTF_ATTR(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(const char *fmt, ...) PRINTF_ATTR(1, 2); YQ2_ATTR_NORETURN_FUNCPTR void Com_Error(int code, const char *fmt, ...) PRINTF_ATTR(2, 3); YQ2_ATTR_NORETURN void Com_Quit(void); /* Ugly work around for unsupported * format specifiers unter mingw. */ #ifdef WIN32 #define YQ2_COM_PRId64 "%I64d" #define YQ2_COM_PRIdS "%Id" #else #define YQ2_COM_PRId64 "%ld" #define YQ2_COM_PRIdS "%zd" #endif // terminate yq2 (with Com_Error()) if VAR is NULL (after malloc() or similar) // and print message about it #define YQ2_COM_CHECK_OOM(VAR, ALLOC_FN_NAME, ALLOC_SIZE) \ if(VAR == NULL) { \ Com_Error(ERR_FATAL, "%s for " YQ2_COM_PRIdS " bytes failed in %s() (%s == NULL)! Out of Memory?!\n", \ ALLOC_FN_NAME, (size_t)ALLOC_SIZE, __func__, #VAR); } 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; /* External entity files. */ extern cvar_t *sv_entfile; /* Hack for portable client */ extern qboolean is_portable; /* Hack for external datadir */ extern char datadir[MAX_OSPATH]; /* Hack for external datadir */ extern char cfgdir[MAX_OSPATH]; /* Hack for working 'game' cmd */ extern char userGivenGame[MAX_QPATH]; extern char **mapnames; extern int nummaps; 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_ExecConfigs(qboolean addEarlyCmds); const char* Qcommon_GetInitialGame(void); 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); /* 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 usec); /* ======================================================================= */ // Platform specific functions. // system.c char *Sys_ConsoleInput(void); void Sys_ConsoleOutput(char *string); YQ2_ATTR_NORETURN void Sys_Error(const char *error, ...); YQ2_ATTR_NORETURN void Sys_Quit(void); void Sys_Init(void); char *Sys_GetHomeDir(void); void Sys_Remove(const char *path); int Sys_Rename(const char *from, const char *to); void Sys_RemoveDir(const char *path); long long Sys_Microseconds(void); void Sys_Nanosleep(int); void *Sys_GetProcAddress(void *handle, const char *sym); void Sys_FreeLibrary(void *handle); void *Sys_LoadLibrary(const char *path, const char *sym, void **handle); void *Sys_GetGameAPI(void *parms); void Sys_UnloadGame(void); void Sys_GetWorkDir(char *buffer, size_t len); qboolean Sys_SetWorkDir(char *path); qboolean Sys_Realpath(const char *in, char *out, size_t size); // Windows only (system.c) #ifdef _WIN32 void Sys_RedirectStdout(void); void Sys_SetHighDPIMode(void); #endif // misc.c const char *Sys_GetBinaryDir(void); void Sys_SetupFPU(void); /* ======================================================================= */ #endif yquake2-QUAKE2_8_40/src/common/header/crc.h000066400000000000000000000021161465112212000203400ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/common/header/files.h000066400000000000000000000270161465112212000207010ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; /* .M8 texture file format */ #define M8_MIP_LEVELS 16 #define M8_VERSION 0x2 typedef struct { unsigned char r; unsigned char g; unsigned char b; } rgb_t; typedef struct m8tex_s { unsigned version; char name[32]; unsigned width[M8_MIP_LEVELS]; unsigned height[M8_MIP_LEVELS]; unsigned offsets[M8_MIP_LEVELS]; /* 16 mip maps stored */ char animname[32]; /* next frame in animation chain */ rgb_t palette[256]; int flags; int contents; int value; } m8tex_t; /* .M32 texture file format */ #define M32_VERSION 0x4 #define M32_MIP_LEVELS 16 typedef struct m32tex_s { int version; char name[128]; char altname[128]; // texture substitution char animname[128]; // next frame in animation chain char damagename[128]; // image that should be shown when damaged unsigned width[M32_MIP_LEVELS], height[M32_MIP_LEVELS]; unsigned offsets[M32_MIP_LEVELS]; int flags; int contents; int value; float scale_x, scale_y; int mip_scale; // detail texturing info char dt_name[128]; // detailed texture name float dt_scale_x, dt_scale_y; float dt_u, dt_v; float dt_alpha; int dt_src_blend_mode, dt_dst_blend_mode; int unused[20]; // future expansion to maintain compatibility with h2 } m32tex_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_8_40/src/common/header/glob.h000066400000000000000000000020541465112212000205150ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/common/header/shared.h000066400000000000000000001152341465112212000210450ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 // stuff to align variables/arrays and for noreturn #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L // C11 or newer #define YQ2_ALIGNAS_SIZE(SIZE) _Alignas(SIZE) #define YQ2_ALIGNAS_TYPE(TYPE) _Alignas(TYPE) // must be used as prefix (YQ2_ATTR_NORETURN void bla();)! #define YQ2_ATTR_NORETURN _Noreturn #define YQ2_STATIC_ASSERT(C, M) _Static_assert((C), M) #if defined(__GNUC__) #define YQ2_ATTR_MALLOC __attribute__ ((__malloc__)) #define YQ2_ATTR_INLINE __attribute__((always_inline)) inline #elif defined(_MSC_VER) #define YQ2_ATTR_MALLOC __declspec(restrict) #define YQ2_ATTR_INLINE __forceinline #else // no equivalent per see #define YQ2_ATTR_MALLOC #define YQ2_ATTR_INLINE inline #endif #elif defined(__GNUC__) // GCC and clang should support this attribute #define YQ2_ALIGNAS_SIZE(SIZE) __attribute__(( __aligned__(SIZE) )) #define YQ2_ALIGNAS_TYPE(TYPE) __attribute__(( __aligned__(__alignof__(TYPE)) )) // must be used as prefix (YQ2_ATTR_NORETURN void bla();)! #define YQ2_ATTR_NORETURN __attribute__ ((noreturn)) #define YQ2_ATTR_MALLOC __attribute__ ((__malloc__)) #define YQ2_ATTR_INLINE __attribute__((always_inline)) inline // GCC supports this extension since 4.6 #define YQ2_STATIC_ASSERT(C, M) _Static_assert((C), M) #elif defined(_MSC_VER) // Note: We prefer VS2019 16.8 or newer in C11 mode (/std:c11), // then the __STDC_VERSION__ >= 201112L case above is used #define YQ2_ALIGNAS_SIZE(SIZE) __declspec(align(SIZE)) // FIXME: for some reason, the following line doesn't work //#define YQ2_ALIGNAS_TYPE( TYPE ) __declspec(align(__alignof(TYPE))) #ifdef _WIN64 // (hopefully) good enough workaround #define YQ2_ALIGNAS_TYPE(TYPE) __declspec(align(8)) #else // 32bit #define YQ2_ALIGNAS_TYPE(TYPE) __declspec(align(4)) #endif // _WIN64 // must be used as prefix (YQ2_ATTR_NORETURN void bla();)! #define YQ2_ATTR_NORETURN __declspec(noreturn) #define YQ2_ATTR_MALLOC __declspec(restrict) #define YQ2_ATTR_INLINE __forceinline #define YQ2_STATIC_ASSERT(C, M) assert((C) && M) #else #warning "Please add a case for your compiler here to align correctly" #define YQ2_ALIGNAS_SIZE(SIZE) #define YQ2_ALIGNAS_TYPE(TYPE) #define YQ2_ATTR_NORETURN #define YQ2_ATTR_MALLOC #define YQ2_ATTR_INLINE inline #define YQ2_STATIC_ASSERT(C, M) assert((C) && M) #endif #if defined(__GNUC__) /* ISO C11 _Noreturn can't be attached to function pointers, so * use the gcc/clang-specific version for function pointers, even * in C11 mode. MSVC __declspec(noreturn) seems to have the same * restriction as _Noreturn so can't be used here either. */ #define YQ2_ATTR_NORETURN_FUNCPTR __attribute__ ((noreturn)) #else #define YQ2_ATTR_NORETURN_FUNCPTR /* nothing */ #endif /* angle indexes */ #define PITCH 0 /* up / down */ #define YAW 1 /* left / right */ #define ROLL 2 /* fall over */ #define Q_min(a, b) (((a) < (b)) ? (a) : (b)) #define Q_max(a, b) (((a) > (b)) ? (a) : (b)) #define MAX_STRING_CHARS 2048 /* max length of a string passed to Cmd_TokenizeString */ #define MAX_STRING_TOKENS 80 /* max tokens resulting from Cmd_TokenizeString */ #define MAX_TOKEN_CHARS 1024 /* 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 #ifdef _MSC_VER #define PRINTF_ATTR(FMT, VARGS) #else // at least GCC/mingw and clang support this #define PRINTF_ATTR(FMT, VARGS) __attribute__((format(printf, FMT , VARGS ))) #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) // FIXME: use int instead of long, it's only used with int anyway? #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(const char *s1, const char *s2); int Q_strncasecmp(const char *s1, const char *s2, int n); char *Q_strcasestr(const char *s1, const char *s2); /* 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); /* ============================================= */ /* Unicode wrappers that also make sure it's a regular file around fopen(). */ FILE *Q_fopen(const char *file, const char *mode); /* Comparator function for qsort(), compares case-insensitive strings. */ int Q_sort_stricmp(const void *s1, const void *s2); /* Comparator function for qsort(), compares strings. */ int Q_sort_strcomp(const void *s1, const void *s2); /* ============================================= */ 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(const char *format, ...) PRINTF_ATTR(1, 2); /* ============================================= */ /* 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(const char *path); qboolean Sys_IsDir(const char *path); qboolean Sys_IsFile(const char *path); /* large block stack allocation routines */ YQ2_ATTR_MALLOC void *Hunk_Begin(int maxsize); YQ2_ATTR_MALLOC 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 */ YQ2_ATTR_NORETURN void Sys_Error(const char *error, ...); void Com_Printf(const 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; /* Added by YQ2. Must be at the end to preserve ABI. */ char *default_string; } 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_8_40/src/common/header/zone.h000066400000000000000000000022121465112212000205410ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/common/md4.c000066400000000000000000000101511465112212000170160ustar00rootroot00000000000000/* * 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_8_40/src/common/movemsg.c000066400000000000000000000441111465112212000200120ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/common/netchan.c000066400000000000000000000255211465112212000177610ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 #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; /* This is a little bit fishy: The original code used Sys_Milliseconds() as base. It worked because the original Sys_Milliseconds included some amount of random data (Windows) or was dependend on seconds since epoche (Unix). Our Sys_Milliseconds() always starts at 0, so there's a very high propability - nearly 100 percent for something like `./quake2 +connect example.com - that two or more clients end up with the same qport. We can't use rand() because we're always starting with the same seed. So right after client start we'll nearly always get the same random numbers. Again there's a high propability that two or more clients end up with the same qport. Just calling time() should be portable and is more less what Windows did in the original code. There's still a rather small propability that two clients end up with the same qport, but that needs to fixed somewhere else with some kind of fallback logic. */ port = time(NULL) & 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 & ~(1U << 31)) | (send_reliable << 31); w2 = (chan->incoming_sequence & ~(1U << 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 &= ~(1U << 31); sequence_ack &= ~(1U << 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_8_40/src/common/pmove.c000066400000000000000000000637251465112212000174770ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; } //Used for speedoomter display. void GetPlayerSpeed(float* speed, float* speedxy) { *speedxy = sqrt(pml.velocity[0] * pml.velocity[0] + pml.velocity[1] * pml.velocity[1]); *speed = VectorLength(pml.velocity); } /* * 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_8_40/src/common/shared/000077500000000000000000000000001465112212000174365ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/common/shared/flash.c000066400000000000000000000321671465112212000207100ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/common/shared/rand.c000066400000000000000000000027501465112212000205320ustar00rootroot00000000000000/* * 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; static 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_8_40/src/common/shared/shared.c000066400000000000000000000553471465112212000210660ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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[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) { VectorCopy(v, out); return VectorNormalize(out); } 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) { return sqrtf((v[0] * v[0]) + (v[1] * v[1]) + (v[2] * v[2])); } 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}; short swapTestShort; YQ2_STATIC_ASSERT(sizeof(short) == 2, "invalid short size"); memcpy(&swapTestShort, swaptest, 2); /* set the byte swapping variables in a portable manner */ if (swapTestShort == 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(swapTestShort) != 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(const 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; } static int paged_total = 0; 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) { #ifdef _MSC_VER return stricmp(s1, s2); #else return strcasecmp(s1, s2); #endif } int Q_strncasecmp(const char *s1, const 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 */ } char *Q_strcasestr(const char *haystack, const char *needle) { size_t len = strlen(needle); for (; *haystack; haystack++) { if (!Q_strncasecmp(haystack, needle, len)) { return (char *)haystack; } } return 0; } int Q_strcasecmp(const char *s1, const 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( (unsigned char)*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); } /* * An unicode compatible fopen() Wrapper for Windows. */ #ifdef _WIN32 #include #include #include #include FILE *Q_fopen(const char *file, const char *mode) { WCHAR wfile[MAX_OSPATH]; WCHAR wmode[16]; int len = MultiByteToWideChar(CP_UTF8, 0, file, -1, wfile, MAX_OSPATH); if (len > 0) { if (MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, 16) > 0) { // make sure it's a regular file and not a directory or sth, see #394 struct _stat buf; int statret = _wstat(wfile, &buf); if((statret == 0 && (buf.st_mode & _S_IFREG) != 0) || (statret == -1 && errno == ENOENT)) { return _wfopen(wfile, wmode); } } } return NULL; } #else #include #include FILE *Q_fopen(const char *file, const char *mode) { // make sure it's a regular file and not a directory or sth, see #394 struct stat statbuf; int statret = stat(file, &statbuf); // (it's ok if it doesn't exist though, maybe we wanna write/create) if((statret == -1 && errno != ENOENT) || (statret == 0 && (statbuf.st_mode & S_IFREG) == 0)) { return NULL; } return fopen(file, mode); } #endif int Q_sort_stricmp(const void *s1, const void *s2) { return Q_stricmp(*(char**)s1, *(char**)s2); } int Q_sort_strcomp(const void *s1, const void *s2) { return strcmp(*(char **)s1, *(char **)s2); } /* * ===================================================================== * * 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) { *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) { *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 (!key) { return; } if (strstr(key, "\\") || (value && strstr(value, "\\"))) { Com_Printf("Can't use keys or values with a \\\n"); return; } if (strstr(key, ";")) { Com_Printf("Can't use keys with a semicolon\n"); return; } if (strstr(key, "\"") || (value && strstr(value, "\""))) { Com_Printf("Can't use keys or values with a \"\n"); return; } if ((strlen(key) > MAX_INFO_KEY - 1) || (value && (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_8_40/src/common/szone.c000066400000000000000000000043111465112212000174710ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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) { if(length > 0) 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_8_40/src/common/unzip/000077500000000000000000000000001465112212000173355ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/common/unzip/ioapi.c000066400000000000000000000170731465112212000206120ustar00rootroot00000000000000/* ioapi.h -- IO base function header for compress/uncompress .zip part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt */ #if defined(_WIN32) && (!(defined(_CRT_SECURE_NO_WARNINGS))) #define _CRT_SECURE_NO_WARNINGS #endif #if defined(__APPLE__) || defined(IOAPI_NO_64) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_NO_64) // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions #define FOPEN_FUNC(filename, mode) fopen(filename, mode) #define FTELLO_FUNC(stream) ftello(stream) #define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) #else #define FOPEN_FUNC(filename, mode) fopen64(filename, mode) #define FTELLO_FUNC(stream) ftello64(stream) #define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) #endif #include "ioapi.h" voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc, const void*filename, int mode) { if (pfilefunc->zfile_func64.zopen64_file != NULL) return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); else { return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode); } } long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) { if (pfilefunc->zfile_func64.zseek64_file != NULL) return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); else { uLong offsetTruncated = (uLong)offset; if (offsetTruncated != offset) return -1; else return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); } } ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc, voidpf filestream) { if (pfilefunc->zfile_func64.zseek64_file != NULL) return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); else { uLong tell_uLong = (uLong)(*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); if ((tell_uLong) == MAXU32) return (ZPOS64_T)-1; else return tell_uLong; } } void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32, const zlib_filefunc_def* p_filefunc32) { p_filefunc64_32->zfile_func64.zopen64_file = NULL; p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; p_filefunc64_32->zfile_func64.ztell64_file = NULL; p_filefunc64_32->zfile_func64.zseek64_file = NULL; p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; } static voidpf ZCALLBACK fopen_file_func(voidpf opaque, const char* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; (void)opaque; 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; } static voidpf ZCALLBACK fopen64_file_func(voidpf opaque, const void* filename, int mode) { FILE* file = NULL; const char* mode_fopen = NULL; (void)opaque; 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_FUNC((const char*)filename, mode_fopen); return file; } static uLong ZCALLBACK fread_file_func(voidpf opaque, voidpf stream, void* buf, uLong size) { uLong ret; (void)opaque; ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); return ret; } static uLong ZCALLBACK fwrite_file_func(voidpf opaque, voidpf stream, const void* buf, uLong size) { uLong ret; (void)opaque; ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); return ret; } static long ZCALLBACK ftell_file_func(voidpf opaque, voidpf stream) { long ret; (void)opaque; ret = ftell((FILE *)stream); return ret; } static ZPOS64_T ZCALLBACK ftell64_file_func(voidpf opaque, voidpf stream) { ZPOS64_T ret; (void)opaque; ret = (ZPOS64_T)FTELLO_FUNC((FILE *)stream); return ret; } static long ZCALLBACK fseek_file_func(voidpf opaque, voidpf stream, uLong offset, int origin) { int fseek_origin=0; long ret; (void)opaque; 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, (long)offset, fseek_origin) != 0) ret = -1; return ret; } static long ZCALLBACK fseek64_file_func(voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) { int fseek_origin=0; long ret; (void)opaque; 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(FSEEKO_FUNC((FILE *)stream, (z_off64_t)offset, fseek_origin) != 0) ret = -1; return ret; } static int ZCALLBACK fclose_file_func(voidpf opaque, voidpf stream) { int ret; (void)opaque; ret = fclose((FILE *)stream); return ret; } static int ZCALLBACK ferror_file_func(voidpf opaque, voidpf stream) { int ret; (void)opaque; ret = ferror((FILE *)stream); return ret; } void fill_fopen_filefunc(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; } void fill_fopen64_filefunc(zlib_filefunc64_def* pzlib_filefunc_def) { pzlib_filefunc_def->zopen64_file = fopen64_file_func; pzlib_filefunc_def->zread_file = fread_file_func; pzlib_filefunc_def->zwrite_file = fwrite_file_func; pzlib_filefunc_def->ztell64_file = ftell64_file_func; pzlib_filefunc_def->zseek64_file = fseek64_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_8_40/src/common/unzip/ioapi.h000066400000000000000000000157401465112212000206160ustar00rootroot00000000000000/* ioapi.h -- IO base function header for compress/uncompress .zip part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications for Zip64 support Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt Changes Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. More if/def section may be needed to support other platforms Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. (but you should use iowin32.c for windows instead) */ #ifndef _ZLIBIOAPI64_H #define _ZLIBIOAPI64_H #if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) // Linux needs this to support file operation on files larger then 4+GB // But might need better if/def to select just the platforms that needs them. #ifndef __USE_FILE_OFFSET64 #define __USE_FILE_OFFSET64 #endif #ifndef __USE_LARGEFILE64 #define __USE_LARGEFILE64 #endif #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE #endif #ifndef _FILE_OFFSET_BIT #define _FILE_OFFSET_BIT 64 #endif #endif #include #include #include "miniz/miniz.h" #include "miniz/minizconf.h" #if defined(USE_FILE32API) #define fopen64 fopen #define ftello64 ftell #define fseeko64 fseek #else #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(MINIZIP_FOPEN_NO_64) #define fopen64 fopen #define ftello64 ftello #define fseeko64 fseeko #endif #ifdef _MSC_VER #define fopen64 fopen #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) #define ftello64 _ftelli64 #define fseeko64 _fseeki64 #else // old MSC #define ftello64 ftell #define fseeko64 fseek #endif #endif #endif /* #ifndef ZPOS64_T #ifdef _WIN32 #define ZPOS64_T fpos_t #else #include #define ZPOS64_T uint64_t #endif #endif */ #ifdef HAVE_MINIZIP64_CONF_H #include "mz64conf.h" #endif /* a type chosen by DEFINE */ #ifdef HAVE_64BIT_INT_CUSTOM typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; #else #ifdef HAS_STDINT_H #include "stdint.h" typedef uint64_t ZPOS64_T; #else #if defined(_MSC_VER) || defined(__BORLANDC__) typedef unsigned __int64 ZPOS64_T; #else typedef unsigned long long int ZPOS64_T; #endif #endif #endif /* Maximum unsigned 32-bit value used as placeholder for zip64 */ #ifndef MAXU32 #define MAXU32 (0xffffffff) #endif #ifdef __cplusplus extern "C" { #endif #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) #ifndef ZCALLBACK #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) #define ZCALLBACK CALLBACK #else #define ZCALLBACK #endif #endif typedef voidpf (ZCALLBACK *open_file_func) (voidpf opaque, const char* filename, int mode); typedef uLong (ZCALLBACK *read_file_func) (voidpf opaque, voidpf stream, void* buf, uLong size); typedef uLong (ZCALLBACK *write_file_func) (voidpf opaque, voidpf stream, const void* buf, uLong size); typedef int (ZCALLBACK *close_file_func) (voidpf opaque, voidpf stream); typedef int (ZCALLBACK *testerror_file_func) (voidpf opaque, voidpf stream); typedef long (ZCALLBACK *tell_file_func) (voidpf opaque, voidpf stream); typedef long (ZCALLBACK *seek_file_func) (voidpf opaque, voidpf stream, uLong offset, int origin); /* here is the "old" 32 bits structure */ 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; typedef ZPOS64_T (ZCALLBACK *tell64_file_func) (voidpf opaque, voidpf stream); typedef long (ZCALLBACK *seek64_file_func) (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin); typedef voidpf (ZCALLBACK *open64_file_func) (voidpf opaque, const void* filename, int mode); typedef struct zlib_filefunc64_def_s { open64_file_func zopen64_file; read_file_func zread_file; write_file_func zwrite_file; tell64_file_func ztell64_file; seek64_file_func zseek64_file; close_file_func zclose_file; testerror_file_func zerror_file; voidpf opaque; } zlib_filefunc64_def; void fill_fopen64_filefunc(zlib_filefunc64_def* pzlib_filefunc_def); void fill_fopen_filefunc(zlib_filefunc_def* pzlib_filefunc_def); /* now internal definition, only for zip.c and unzip.h */ typedef struct zlib_filefunc64_32_def_s { zlib_filefunc64_def zfile_func64; open_file_func zopen32_file; tell_file_func ztell32_file; seek_file_func zseek32_file; } zlib_filefunc64_32_def; #define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) #define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) //#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) //#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) #define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) #define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) voidpf call_zopen64(const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode); long call_zseek64(const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin); ZPOS64_T call_ztell64(const zlib_filefunc64_32_def* pfilefunc,voidpf filestream); void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); #define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) #define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) #define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) #ifdef __cplusplus } #endif #endif yquake2-QUAKE2_8_40/src/common/unzip/miniz/000077500000000000000000000000001465112212000204635ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/common/unzip/miniz/miniz.c000066400000000000000000000575621465112212000217740ustar00rootroot00000000000000/************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * 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. * **************************************************************************/ #include "miniz.h" typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; #ifdef __cplusplus extern "C" { #endif /* ------------------- zlib-style API's */ mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; if (!ptr) return MZ_ADLER32_INIT; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } return (s2 << 16) + s1; } /* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ #if 0 mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; mz_uint32 crcu32 = (mz_uint32)crc; if (!ptr) return MZ_CRC32_INIT; crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } return ~crcu32; } #elif defined(USE_EXTERNAL_MZCRC) /* If USE_EXTERNAL_CRC is defined, an external module will export the * mz_crc32() symbol for us to use, e.g. an SSE-accelerated version. * Depending on the impl, it may be necessary to ~ the input/output crc values. */ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len); #else /* Faster, but larger CPU cache footprint. */ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_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 }; mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; while (buf_len >= 4) { crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; pByte_buf += 4; buf_len -= 4; } while (buf_len) { crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; ++pByte_buf; --buf_len; } return ~crc32; } #endif void mz_free(void *p) { MZ_FREE(p); } MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } const char *mz_version(void) { return MZ_VERSION; } #ifndef MINIZ_NO_ZLIB_APIS #ifndef MINIZ_NO_DEFLATE_APIS int mz_deflateInit(mz_streamp pStream, int level) { return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); } int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) { tdefl_compressor *pComp; mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); if (!pStream) return MZ_STREAM_ERROR; if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = MZ_ADLER32_INIT; pStream->msg = NULL; pStream->reserved = 0; pStream->total_in = 0; pStream->total_out = 0; if (!pStream->zalloc) pStream->zalloc = miniz_def_alloc_func; if (!pStream->zfree) pStream->zfree = miniz_def_free_func; pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); if (!pComp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pComp; if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { mz_deflateEnd(pStream); return MZ_PARAM_ERROR; } return MZ_OK; } int mz_deflateReset(mz_streamp pStream) { if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; pStream->total_in = pStream->total_out = 0; tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); return MZ_OK; } int mz_deflate(mz_streamp pStream, int flush) { size_t in_bytes, out_bytes; mz_ulong orig_total_in, orig_total_out; int mz_status = MZ_OK; if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; if (!pStream->avail_out) return MZ_BUF_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; for (;;) { tdefl_status defl_status; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (defl_status < 0) { mz_status = MZ_STREAM_ERROR; break; } else if (defl_status == TDEFL_STATUS_DONE) { mz_status = MZ_STREAM_END; break; } else if (!pStream->avail_out) break; else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) break; return MZ_BUF_ERROR; /* Can't make forward progress without some input. */ } } return mz_status; } int mz_deflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { (void)pStream; /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); } int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) { int status; mz_stream stream; memset(&stream, 0, sizeof(stream)); /* In case mz_ulong is 64-bits (argh I hate longs). */ if ((mz_uint64)(source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_deflateInit(&stream, level); if (status != MZ_OK) return status; status = mz_deflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_deflateEnd(&stream); return (status == MZ_OK) ? MZ_BUF_ERROR : status; } *pDest_len = stream.total_out; return mz_deflateEnd(&stream); } int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); } mz_ulong mz_compressBound(mz_ulong source_len) { return mz_deflateBound(NULL, source_len); } #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #ifndef MINIZ_NO_INFLATE_APIS typedef struct { tinfl_decompressor m_decomp; mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; tinfl_status m_last_status; } inflate_state; int mz_inflateInit2(mz_streamp pStream, int window_bits) { inflate_state *pDecomp; if (!pStream) return MZ_STREAM_ERROR; if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = 0; pStream->msg = NULL; pStream->total_in = 0; pStream->total_out = 0; pStream->reserved = 0; if (!pStream->zalloc) pStream->zalloc = miniz_def_alloc_func; if (!pStream->zfree) pStream->zfree = miniz_def_free_func; pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); if (!pDecomp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pDecomp; tinfl_init(&pDecomp->m_decomp); pDecomp->m_dict_ofs = 0; pDecomp->m_dict_avail = 0; pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; pDecomp->m_first_call = 1; pDecomp->m_has_flushed = 0; pDecomp->m_window_bits = window_bits; return MZ_OK; } int mz_inflateInit(mz_streamp pStream) { return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); } int mz_inflateReset(mz_streamp pStream) { inflate_state *pDecomp; if (!pStream) return MZ_STREAM_ERROR; pStream->data_type = 0; pStream->adler = 0; pStream->msg = NULL; pStream->total_in = 0; pStream->total_out = 0; pStream->reserved = 0; pDecomp = (inflate_state *)pStream->state; tinfl_init(&pDecomp->m_decomp); pDecomp->m_dict_ofs = 0; pDecomp->m_dict_avail = 0; pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; pDecomp->m_first_call = 1; pDecomp->m_has_flushed = 0; /* pDecomp->m_window_bits = window_bits */; return MZ_OK; } int mz_inflate(mz_streamp pStream, int flush) { inflate_state *pState; mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; size_t in_bytes, out_bytes, orig_avail_in; tinfl_status status; if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState = (inflate_state *)pStream->state; if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; orig_avail_in = pStream->avail_in; first_call = pState->m_first_call; pState->m_first_call = 0; if (pState->m_last_status < 0) return MZ_DATA_ERROR; if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState->m_has_flushed |= (flush == MZ_FINISH); if ((flush == MZ_FINISH) && (first_call)) { /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (status < 0) return MZ_DATA_ERROR; else if (status != TINFL_STATUS_DONE) { pState->m_last_status = TINFL_STATUS_FAILED; return MZ_BUF_ERROR; } return MZ_STREAM_END; } /* flush != MZ_FINISH then we must assume there's more input. */ if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; if (pState->m_dict_avail) { n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } for (;;) { in_bytes = pStream->avail_in; out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pState->m_dict_avail = (mz_uint)out_bytes; n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); if (status < 0) return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ else if (flush == MZ_FINISH) { /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ if (status == TINFL_STATUS_DONE) return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ else if (!pStream->avail_out) return MZ_BUF_ERROR; } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) break; } return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } int mz_inflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len) { mz_stream stream; int status; memset(&stream, 0, sizeof(stream)); /* In case mz_ulong is 64-bits (argh I hate longs). */ if ((mz_uint64)(*pSource_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)*pSource_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_inflateInit(&stream); if (status != MZ_OK) return status; status = mz_inflate(&stream, MZ_FINISH); *pSource_len = *pSource_len - stream.avail_in; if (status != MZ_STREAM_END) { mz_inflateEnd(&stream); return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; } *pDest_len = stream.total_out; return mz_inflateEnd(&stream); } int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { return mz_uncompress2(pDest, pDest_len, pSource, &source_len); } #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ const char *mz_error(int err) { static struct { int m_err; const char *m_pDesc; } s_error_descs[] = { { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } }; mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; return NULL; } #endif /*MINIZ_NO_ZLIB_APIS */ #ifdef __cplusplus } #endif /* 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. For more information, please refer to */ yquake2-QUAKE2_8_40/src/common/unzip/miniz/miniz.h000066400000000000000000000603461465112212000217730ustar00rootroot00000000000000/* miniz.c 3.0.2 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). * Low-level Deflate/Inflate implementation notes: Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses approximately as well as zlib. Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory block large enough to hold the entire file. The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. * zlib-style API notes: miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in zlib replacement in many apps: The z_stream struct, optional memory allocation callbacks deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound inflateInit/inflateInit2/inflate/inflateReset/inflateEnd compress, compress2, compressBound, uncompress CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. Supports raw deflate streams or standard zlib streams with adler-32 checking. Limitations: The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but there are no guarantees that miniz.c pulls this off perfectly. * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by Alex Evans. Supports 1-4 bytes/pixel images. * ZIP archive API notes: The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to get the job done with minimal fuss. There are simple API's to retrieve file information, read files from existing archives, create new archives, append new files to existing archives, or clone archive data from one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), or you can specify custom file read/write callbacks. - Archive reading: Just call this function to read a single file from a disk archive: void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); The locate operation can optionally check file comments too, which (as one example) can be used to identify multiple versions of the same file in an archive. This function uses a simple linear search through the central directory, so it's not very fast. Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and retrieve detailed info on each file by calling mz_zip_reader_file_stat(). - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data to disk and builds an exact image of the central directory in memory. The central directory image is written all at once at the end of the archive file when the archive is finalized. The archive writer can optionally align each file's local header and file data to any power of 2 alignment, which can be useful when the archive will be read from optical media. Also, the writer supports placing arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still readable by any ZIP tool. - Archive appending: The simple way to add a single file to an archive is to call this function: mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); The archive will be created if it doesn't already exist, otherwise it'll be appended to. Note the appending is done in-place and is not an atomic operation, so if something goes wrong during the operation it's possible the archive could be left without a central directory (although the local file headers and file data will be fine, so the archive will be recoverable). For more complex archive modification scenarios: 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and you're done. This is safe but requires a bunch of temporary disk space or heap memory. 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), append new files as needed, then finalize the archive which will write an updated central directory to the original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - ZIP archive support limitations: No spanning support. Extraction functions can only handle unencrypted, stored or deflated files. Requires streams capable of seeking. * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. * Important: For best perf. be sure to customize the below macros for your target platform: #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define MINIZ_LITTLE_ENDIAN 1 #define MINIZ_HAS_64BIT_REGISTERS 1 * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). */ #pragma once #include "miniz_export.h" /* Defines to completely disable specific portions of miniz.c: If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */ /* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ /*#define MINIZ_NO_STDIO */ /* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ /* get/set file times, and the C run-time funcs that get/set times won't be called. */ /* The current downside is the times written to your archives will be from 1979. */ /*#define MINIZ_NO_TIME */ /* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */ /*#define MINIZ_NO_DEFLATE_APIS */ /* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */ /*#define MINIZ_NO_INFLATE_APIS */ /* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ /*#define MINIZ_NO_ARCHIVE_APIS */ /* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ /*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ /* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ /*#define MINIZ_NO_ZLIB_APIS */ /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ /*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ /* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ /*#define MINIZ_NO_MALLOC */ #ifdef MINIZ_NO_INFLATE_APIS #define MINIZ_NO_ARCHIVE_APIS #endif #ifdef MINIZ_NO_DEFLATE_APIS #define MINIZ_NO_ARCHIVE_WRITING_APIS #endif #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) /* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ #define MINIZ_NO_TIME #endif #include #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) #include #endif #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) /* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ #define MINIZ_X86_OR_X64_CPU 1 #else #define MINIZ_X86_OR_X64_CPU 0 #endif /* Set MINIZ_LITTLE_ENDIAN only if not set */ #if !defined(MINIZ_LITTLE_ENDIAN) #if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) /* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ #define MINIZ_LITTLE_ENDIAN 1 #else #define MINIZ_LITTLE_ENDIAN 0 #endif #else #if MINIZ_X86_OR_X64_CPU #define MINIZ_LITTLE_ENDIAN 1 #else #define MINIZ_LITTLE_ENDIAN 0 #endif #endif #endif /* Using unaligned loads and stores causes errors when using UBSan */ #if defined(__has_feature) #if __has_feature(undefined_behavior_sanitizer) #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #endif #endif /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ #if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) #if MINIZ_X86_OR_X64_CPU /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #define MINIZ_UNALIGNED_USE_MEMCPY #else #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #endif #endif #if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) /* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ #define MINIZ_HAS_64BIT_REGISTERS 1 #else #define MINIZ_HAS_64BIT_REGISTERS 0 #endif #ifdef __cplusplus extern "C" { #endif /* ------------------- zlib-style API Definitions. */ /* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ typedef unsigned long mz_ulong; /* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ MINIZ_EXPORT void mz_free(void *p); #define MZ_ADLER32_INIT (1) /* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); #define MZ_CRC32_INIT (0) /* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); /* Compression strategies. */ enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; /* Method */ #define MZ_DEFLATED 8 /* Heap allocation callbacks. Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */ typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); typedef void (*mz_free_func)(void *opaque, void *address); typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); /* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; #define MZ_VERSION "11.0.2" #define MZ_VERNUM 0xB002 #define MZ_VER_MAJOR 11 #define MZ_VER_MINOR 2 #define MZ_VER_REVISION 0 #define MZ_VER_SUBREVISION 0 #ifndef MINIZ_NO_ZLIB_APIS /* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; /* Return status codes. MZ_PARAM_ERROR is non-standard. */ enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; /* Window bits */ #define MZ_DEFAULT_WINDOW_BITS 15 struct mz_internal_state; /* Compression/decompression stream struct. */ typedef struct mz_stream_s { const unsigned char *next_in; /* pointer to next byte to read */ unsigned int avail_in; /* number of bytes available at next_in */ mz_ulong total_in; /* total number of bytes consumed so far */ unsigned char *next_out; /* pointer to next byte to write */ unsigned int avail_out; /* number of bytes that can be written to next_out */ mz_ulong total_out; /* total number of bytes produced so far */ char *msg; /* error msg (unused) */ struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ mz_free_func zfree; /* optional heap free function (defaults to free) */ void *opaque; /* heap alloc function user pointer */ int data_type; /* data_type (unused) */ mz_ulong adler; /* adler32 of the source or uncompressed data */ mz_ulong reserved; /* not used */ } mz_stream; typedef mz_stream *mz_streamp; /* Returns the version string of miniz.c. */ MINIZ_EXPORT const char *mz_version(void); #ifndef MINIZ_NO_DEFLATE_APIS /* mz_deflateInit() initializes a compressor with default options: */ /* Parameters: */ /* pStream must point to an initialized mz_stream struct. */ /* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ /* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ /* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ /* Return values: */ /* MZ_OK on success. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_PARAM_ERROR if the input parameters are bogus. */ /* MZ_MEM_ERROR on out of memory. */ MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level); /* mz_deflateInit2() is like mz_deflate(), except with more control: */ /* Additional parameters: */ /* method must be MZ_DEFLATED */ /* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ /* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); /* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream); /* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ /* Parameters: */ /* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ /* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ /* Return values: */ /* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ /* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_PARAM_ERROR if one of the parameters is invalid. */ /* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush); /* mz_deflateEnd() deinitializes a compressor: */ /* Return values: */ /* MZ_OK on success. */ /* MZ_STREAM_ERROR if the stream is bogus. */ MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream); /* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); /* Single-call compression functions mz_compress() and mz_compress2(): */ /* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); /* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #ifndef MINIZ_NO_INFLATE_APIS /* Initializes a decompressor. */ MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); /* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ /* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits); /* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream); /* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ /* Parameters: */ /* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ /* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ /* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ /* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ /* Return values: */ /* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ /* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_DATA_ERROR if the deflate stream is invalid. */ /* MZ_PARAM_ERROR if one of the parameters is invalid. */ /* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ /* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush); /* Deinitializes a decompressor. */ MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); /* Single-call decompression. */ /* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ /* Returns a string description of the specified error code, or NULL if the error code is invalid. */ MINIZ_EXPORT const char *mz_error(int err); /* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES typedef unsigned char Byte; typedef unsigned int uInt; typedef mz_ulong uLong; typedef Byte Bytef; typedef uInt uIntf; typedef char charf; typedef int intf; typedef void *voidpf; typedef uLong uLongf; typedef void *voidp; typedef void *const voidpc; #define Z_NULL 0 #define Z_NO_FLUSH MZ_NO_FLUSH #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH #define Z_SYNC_FLUSH MZ_SYNC_FLUSH #define Z_FULL_FLUSH MZ_FULL_FLUSH #define Z_FINISH MZ_FINISH #define Z_BLOCK MZ_BLOCK #define Z_OK MZ_OK #define Z_STREAM_END MZ_STREAM_END #define Z_NEED_DICT MZ_NEED_DICT #define Z_ERRNO MZ_ERRNO #define Z_STREAM_ERROR MZ_STREAM_ERROR #define Z_DATA_ERROR MZ_DATA_ERROR #define Z_MEM_ERROR MZ_MEM_ERROR #define Z_BUF_ERROR MZ_BUF_ERROR #define Z_VERSION_ERROR MZ_VERSION_ERROR #define Z_PARAM_ERROR MZ_PARAM_ERROR #define Z_NO_COMPRESSION MZ_NO_COMPRESSION #define Z_BEST_SPEED MZ_BEST_SPEED #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY #define Z_FILTERED MZ_FILTERED #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY #define Z_RLE MZ_RLE #define Z_FIXED MZ_FIXED #define Z_DEFLATED MZ_DEFLATED #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS #define alloc_func mz_alloc_func #define free_func mz_free_func #define internal_state mz_internal_state #define z_stream mz_stream #ifndef MINIZ_NO_DEFLATE_APIS #define deflateInit mz_deflateInit #define deflateInit2 mz_deflateInit2 #define deflateReset mz_deflateReset #define deflate mz_deflate #define deflateEnd mz_deflateEnd #define deflateBound mz_deflateBound #define compress mz_compress #define compress2 mz_compress2 #define compressBound mz_compressBound #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #ifndef MINIZ_NO_INFLATE_APIS #define inflateInit mz_inflateInit #define inflateInit2 mz_inflateInit2 #define inflateReset mz_inflateReset #define inflate mz_inflate #define inflateEnd mz_inflateEnd #define uncompress mz_uncompress #define uncompress2 mz_uncompress2 #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ #define crc32 mz_crc32 #define adler32 mz_adler32 #define MAX_WBITS 15 #define MAX_MEM_LEVEL 9 #define zError mz_error #define ZLIB_VERSION MZ_VERSION #define ZLIB_VERNUM MZ_VERNUM #define ZLIB_VER_MAJOR MZ_VER_MAJOR #define ZLIB_VER_MINOR MZ_VER_MINOR #define ZLIB_VER_REVISION MZ_VER_REVISION #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION #define zlibVersion mz_version #define zlib_version mz_version() #endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ #endif /* MINIZ_NO_ZLIB_APIS */ #ifdef __cplusplus } #endif #include "miniz_common.h" #include "miniz_tdef.h" #include "miniz_tinfl.h" #include "miniz_zip.h" yquake2-QUAKE2_8_40/src/common/unzip/miniz/miniz_common.h000066400000000000000000000052461465112212000233410ustar00rootroot00000000000000#pragma once #include #include #include #include #include "miniz_export.h" /* ------------------- Types and macros */ typedef unsigned char mz_uint8; typedef signed short mz_int16; typedef unsigned short mz_uint16; typedef unsigned int mz_uint32; typedef unsigned int mz_uint; typedef int64_t mz_int64; typedef uint64_t mz_uint64; typedef int mz_bool; #define MZ_FALSE (0) #define MZ_TRUE (1) /* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ #ifdef _MSC_VER #define MZ_MACRO_END while (0, 0) #else #define MZ_MACRO_END while (0) #endif #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include #define MZ_FILE FILE #endif /* #ifdef MINIZ_NO_STDIO */ #ifdef MINIZ_NO_TIME typedef struct mz_dummy_time_t_tag { mz_uint32 m_dummy1; mz_uint32 m_dummy2; } mz_dummy_time_t; #define MZ_TIME_T mz_dummy_time_t #else #define MZ_TIME_T time_t #endif #define MZ_ASSERT(x) assert(x) #ifdef MINIZ_NO_MALLOC #define MZ_MALLOC(x) NULL #define MZ_FREE(x) (void)x, ((void)0) #define MZ_REALLOC(p, x) NULL #else #define MZ_MALLOC(x) malloc(x) #define MZ_FREE(x) free(x) #define MZ_REALLOC(p, x) realloc(p, x) #endif #define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) #define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj)) #define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) #else #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) #endif #define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) #ifdef _MSC_VER #define MZ_FORCEINLINE __forceinline #elif defined(__GNUC__) #define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) #else #define MZ_FORCEINLINE inline #endif #ifdef __cplusplus extern "C" { #endif extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address); extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); #define MZ_UINT16_MAX (0xFFFFU) #define MZ_UINT32_MAX (0xFFFFFFFFU) #ifdef __cplusplus } #endif yquake2-QUAKE2_8_40/src/common/unzip/miniz/miniz_export.h000066400000000000000000000015451465112212000233700ustar00rootroot00000000000000#ifndef MINIZ_EXPORT_H #define MINIZ_EXPORT_H #ifdef MINIZ_STATIC_DEFINE # define MINIZ_EXPORT # define MINIZ_NO_EXPORT #else # ifndef MINIZ_EXPORT # ifdef miniz_EXPORTS /* We are building this library */ # define MINIZ_EXPORT # else /* We are using this library */ # define MINIZ_EXPORT # endif # endif # ifndef MINIZ_NO_EXPORT # define MINIZ_NO_EXPORT # endif #endif #ifndef MINIZ_DEPRECATED # define MINIZ_DEPRECATED __attribute__ ((__deprecated__)) #endif #ifndef MINIZ_DEPRECATED_EXPORT # define MINIZ_DEPRECATED_EXPORT MINIZ_EXPORT MINIZ_DEPRECATED #endif #ifndef MINIZ_DEPRECATED_NO_EXPORT # define MINIZ_DEPRECATED_NO_EXPORT MINIZ_NO_EXPORT MINIZ_DEPRECATED #endif #if 0 /* DEFINE_NO_DEPRECATED */ # ifndef MINIZ_NO_DEPRECATED # define MINIZ_NO_DEPRECATED # endif #endif #endif /* MINIZ_EXPORT_H */ yquake2-QUAKE2_8_40/src/common/unzip/miniz/miniz_tdef.c000066400000000000000000002115771465112212000227740ustar00rootroot00000000000000/************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * 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. * **************************************************************************/ #include "miniz.h" #ifndef MINIZ_NO_DEFLATE_APIS #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Compression (independent from all decompression API's) */ /* Purposely making these tables static for faster init and thread safety. */ static const mz_uint16 s_tdefl_len_sym[256] = { 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 }; static const mz_uint8 s_tdefl_len_extra[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 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, 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, 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, 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, 0 }; static const mz_uint8 s_tdefl_small_dist_sym[512] = { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 }; static const mz_uint8 s_tdefl_small_dist_extra[512] = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 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, 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, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; static const mz_uint8 s_tdefl_large_dist_sym[128] = { 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 }; static const mz_uint8 s_tdefl_large_dist_extra[128] = { 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 }; /* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) { mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_ARR(hist); for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { const mz_uint32 *pHist = &hist[pass << 8]; mz_uint offsets[256], cur_ofs = 0; for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; { tdefl_sym_freq *t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } } return pCur_syms; } /* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { int root, leaf, next, avbl, used, dpth; if (n == 0) return; else if (n == 1) { A[0].m_key = 1; return; } A[0].m_key += A[1].m_key; root = 0; leaf = 2; for (next = 1; next < n - 1; next++) { if (leaf >= n || A[root].m_key < A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = (mz_uint16)next; } else A[next].m_key = A[leaf++].m_key; if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) { A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); A[root++].m_key = (mz_uint16)next; } else A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); } A[n - 2].m_key = 0; for (next = n - 3; next >= 0; next--) A[next].m_key = A[A[next].m_key].m_key + 1; avbl = 1; used = dpth = 0; root = n - 2; next = n - 1; while (avbl > 0) { while (root >= 0 && (int)A[root].m_key == dpth) { used++; root--; } while (avbl > used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } avbl = 2 * used; dpth++; used = 0; } } /* Limits canonical Huffman code table's max code size. */ enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) { int i; mz_uint32 total = 0; if (code_list_len <= 1) return; for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); while (total != (1UL << max_code_size)) { pNum_codes[max_code_size]--; for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } total--; } } static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) { int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_ARR(num_codes); if (static_table) { for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; } else { tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; int num_used_syms = 0; const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); MZ_CLEAR_ARR(d->m_huff_code_sizes[table_num]); MZ_CLEAR_ARR(d->m_huff_codes[table_num]); for (i = 1, j = num_used_syms; i <= code_size_limit; i++) for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); } next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); for (i = 0; i < table_len; i++) { mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; } } #define TDEFL_PUT_BITS(b, l) \ do \ { \ mz_uint bits = b; \ mz_uint len = l; \ MZ_ASSERT(bits <= ((1U << len) - 1U)); \ d->m_bit_buffer |= (bits << d->m_bits_in); \ d->m_bits_in += len; \ while (d->m_bits_in >= 8) \ { \ if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ d->m_bit_buffer >>= 8; \ d->m_bits_in -= 8; \ } \ } \ MZ_MACRO_END #define TDEFL_RLE_PREV_CODE_SIZE() \ { \ if (rle_repeat_count) \ { \ if (rle_repeat_count < 3) \ { \ d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ while (rle_repeat_count--) \ packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ } \ else \ { \ d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 16; \ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ } \ rle_repeat_count = 0; \ } \ } #define TDEFL_RLE_ZERO_CODE_SIZE() \ { \ if (rle_z_count) \ { \ if (rle_z_count < 3) \ { \ d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ while (rle_z_count--) \ packed_code_sizes[num_packed_code_sizes++] = 0; \ } \ else if (rle_z_count <= 10) \ { \ d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 17; \ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ } \ else \ { \ d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ packed_code_sizes[num_packed_code_sizes++] = 18; \ packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ } \ rle_z_count = 0; \ } \ } static const mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static void tdefl_start_dynamic_block(tdefl_compressor *d) { int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; d->m_huff_count[0][256] = 1; tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); for (i = 0; i < total_code_sizes_to_pack; i++) { mz_uint8 code_size = code_sizes_to_pack[i]; if (!code_size) { TDEFL_RLE_PREV_CODE_SIZE(); if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } } else { TDEFL_RLE_ZERO_CODE_SIZE(); if (code_size != prev_code_size) { TDEFL_RLE_PREV_CODE_SIZE(); d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; } else if (++rle_repeat_count == 6) { TDEFL_RLE_PREV_CODE_SIZE(); } } prev_code_size = code_size; } if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); TDEFL_PUT_BITS(2, 2); TDEFL_PUT_BITS(num_lit_codes - 257, 5); TDEFL_PUT_BITS(num_dist_codes - 1, 5); for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) { mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); } } static void tdefl_start_static_block(tdefl_compressor *d) { mz_uint i; mz_uint8 *p = &d->m_huff_code_sizes[0][0]; for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) *p++ = 9; for (; i <= 279; ++i) *p++ = 7; for (; i <= 287; ++i) *p++ = 8; memset(d->m_huff_code_sizes[1], 5, 32); tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); TDEFL_PUT_BITS(1, 2); } static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; mz_uint8 *pOutput_buf = d->m_pOutput_buf; mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; mz_uint64 bit_buffer = d->m_bit_buffer; mz_uint bits_in = d->m_bits_in; #define TDEFL_PUT_BITS_FAST(b, l) \ { \ bit_buffer |= (((mz_uint64)(b)) << bits_in); \ bits_in += (l); \ } flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint s0, s1, n0, n1, sym, num_extra_bits; mz_uint match_len = pLZ_codes[0]; mz_uint match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ s0 = s_tdefl_small_dist_sym[match_dist & 511]; n0 = s_tdefl_small_dist_extra[match_dist & 511]; s1 = s_tdefl_large_dist_sym[match_dist >> 8]; n1 = s_tdefl_large_dist_extra[match_dist >> 8]; sym = (match_dist < 512) ? s0 : s1; num_extra_bits = (match_dist < 512) ? n0 : n1; MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } } if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; memcpy(pOutput_buf, &bit_buffer, sizeof(mz_uint64)); pOutput_buf += (bits_in >> 3); bit_buffer >>= (bits_in & ~7); bits_in &= 7; } #undef TDEFL_PUT_BITS_FAST d->m_pOutput_buf = pOutput_buf; d->m_bits_in = 0; d->m_bit_buffer = 0; while (bits_in) { mz_uint32 n = MZ_MIN(bits_in, 16); TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); bit_buffer >>= n; bits_in -= n; } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #else static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); if (match_dist < 512) { sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; } else { sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; } MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { if (static_block) tdefl_start_static_block(d); else tdefl_start_dynamic_block(d); return tdefl_compress_lz_codes(d); } static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; mz_uint8 *pSaved_output_buf; mz_bool comp_block_succeeded = MZ_FALSE; int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; d->m_pOutput_buf = pOutput_buf_start; d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; MZ_ASSERT(!d->m_output_flush_remaining); d->m_output_flush_ofs = 0; d->m_output_flush_remaining = 0; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { const mz_uint8 cmf = 0x78; mz_uint8 flg, flevel = 3; mz_uint header, i, mz_un = sizeof(s_tdefl_num_probes) / sizeof(mz_uint); /* Determine compression level by reversing the process in tdefl_create_comp_flags_from_zip_params() */ for (i = 0; i < mz_un; i++) if (s_tdefl_num_probes[i] == (d->m_flags & 0xFFF)) break; if (i < 2) flevel = 0; else if (i < 6) flevel = 1; else if (i == 6) flevel = 2; header = cmf << 8 | (flevel << 6); header += 31 - (header % 31); flg = header & 0xFF; TDEFL_PUT_BITS(cmf, 8); TDEFL_PUT_BITS(flg, 8); } TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; if (!use_raw_block) comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) { mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; TDEFL_PUT_BITS(0, 2); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); } for (i = 0; i < d->m_total_lz_bytes; ++i) { TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); } } /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ else if (!comp_block_succeeded) { d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; tdefl_compress_block(d, MZ_TRUE); } if (flush) { if (flush == TDEFL_FINISH) { if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } } else { mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } } } MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { if (d->m_pPut_buf_func) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); } else if (pOutput_buf_start == d->m_output_buf) { int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); d->m_out_buf_ofs += bytes_to_copy; if ((n -= bytes_to_copy) != 0) { d->m_output_flush_ofs = bytes_to_copy; d->m_output_flush_remaining = n; } } else { d->m_out_buf_ofs += n; } } return d->m_output_flush_remaining; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #ifdef MINIZ_UNALIGNED_USE_MEMCPY static mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) { mz_uint16 ret; memcpy(&ret, p, sizeof(mz_uint16)); return ret; } static mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) { mz_uint16 ret; memcpy(&ret, p, sizeof(mz_uint16)); return ret; } #else #define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) #define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) #endif static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for (;;) { for (;;) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; q = (const mz_uint16 *)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) continue; p = s; probe_len = 32; do { } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); if (!probe_len) { *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); break; } else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); } } } #else static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint8 *s = d->m_dict + pos, *p, *q; mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for (;;) { for (;;) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; if (probe_len > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; } } } #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #ifdef MINIZ_UNALIGNED_USE_MEMCPY static mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8* p) { mz_uint32 ret; memcpy(&ret, p, sizeof(mz_uint32)); return ret; } #else #define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p) #endif static mz_bool tdefl_compress_fast(tdefl_compressor *d) { /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); d->m_src_buf_left -= num_bytes_to_process; lookahead_size += num_bytes_to_process; while (num_bytes_to_process) { mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); memcpy(d->m_dict + dst_pos, d->m_pSrc, n); if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); d->m_pSrc += n; dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; num_bytes_to_process -= n; } dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; while (lookahead_size >= 4) { mz_uint cur_match_dist, cur_match_len = 1; mz_uint8 *pCur_dict = d->m_dict + cur_pos; mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF; mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; mz_uint probe_pos = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)lookahead_pos; if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) { const mz_uint16 *p = (const mz_uint16 *)pCur_dict; const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); mz_uint32 probe_len = 32; do { } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); if (!probe_len) cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) { cur_match_len = 1; *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } else { mz_uint32 s0, s1; cur_match_len = MZ_MIN(cur_match_len, lookahead_size); MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); cur_match_dist--; pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); #ifdef MINIZ_UNALIGNED_USE_MEMCPY memcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist)); #else *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; #endif pLZ_code_buf += 3; *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; } } else { *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } total_lz_bytes += cur_match_len; lookahead_pos += cur_match_len; dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; MZ_ASSERT(lookahead_size >= cur_match_len); lookahead_size -= cur_match_len; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } while (lookahead_size) { mz_uint8 lit = d->m_dict[cur_pos]; total_lz_bytes++; *pLZ_code_buf++ = lit; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } d->m_huff_count[0][lit]++; lookahead_pos++; dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; lookahead_size--; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } } d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; return MZ_TRUE; } #endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) { d->m_total_lz_bytes++; *d->m_pLZ_code_buf++ = lit; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } d->m_huff_count[0][lit]++; } static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { mz_uint32 s0, s1; MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); d->m_total_lz_bytes += match_len; d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); match_dist -= 1; d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } static mz_bool tdefl_compress_normal(tdefl_compressor *d) { const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; tdefl_flush flush = d->m_flush; while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); const mz_uint8 *pSrc_end = pSrc ? pSrc + num_bytes_to_process : NULL; src_buf_left -= num_bytes_to_process; d->m_lookahead_size += num_bytes_to_process; while (pSrc != pSrc_end) { mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; } } else { while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { mz_uint8 c = *pSrc++; mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; src_buf_left--; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); } } } d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) break; /* Simple lazy/greedy parsing state machine. */ len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; } } else { tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); } if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) { cur_match_dist = cur_match_len = 0; } if (d->m_saved_match_len) { if (cur_match_len > d->m_saved_match_len) { tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); if (cur_match_len >= 128) { tdefl_record_match(d, cur_match_len, cur_match_dist); d->m_saved_match_len = 0; len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } } else { tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; } } else if (!cur_match_dist) tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) { tdefl_record_match(d, cur_match_len, cur_match_dist); len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } /* Move the lookahead forward by len_to_move bytes. */ d->m_lookahead_pos += len_to_move; MZ_ASSERT(d->m_lookahead_size >= len_to_move); d->m_lookahead_size -= len_to_move; d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); /* Check if it's time to flush the current LZ codes to the internal output buffer. */ if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) { int n; d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; } } d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; return MZ_TRUE; } static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { if (d->m_pIn_buf_size) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; } if (d->m_pOut_buf_size) { size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); d->m_output_flush_ofs += (mz_uint)n; d->m_output_flush_remaining -= (mz_uint)n; d->m_out_buf_ofs += n; *d->m_pOut_buf_size = d->m_out_buf_ofs; } return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; } tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) { if (!d) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return TDEFL_STATUS_BAD_PARAM; } d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; d->m_out_buf_ofs = 0; d->m_flush = flush; if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); } d->m_wants_to_finish |= (flush == TDEFL_FINISH); if ((d->m_output_flush_remaining) || (d->m_finished)) return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) { if (!tdefl_compress_fast(d)) return d->m_prev_return_status; } else #endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ { if (!tdefl_compress_normal(d)) return d->m_prev_return_status; } if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) { if (tdefl_flush_block(d, flush) < 0) return d->m_prev_return_status; d->m_finished = (flush == TDEFL_FINISH); if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_ARR(d->m_hash); MZ_CLEAR_ARR(d->m_next); d->m_dict_size = 0; } } return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); } tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) { MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); } tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_ARR(d->m_hash); d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; *d->m_pLZ_flags = 0; d->m_num_flags_left = 8; d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_ARR(d->m_dict); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); return TDEFL_STATUS_OKAY; } tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) { return d->m_prev_return_status; } mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { return d->m_adler32; } mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); MZ_FREE(pComp); return succeeded; } typedef struct { size_t m_size, m_capacity; mz_uint8 *m_pBuf; mz_bool m_expandable; } tdefl_output_buffer; static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) { tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; size_t new_size = p->m_size + len; if (new_size > p->m_capacity) { size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; } memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; return MZ_TRUE; } void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; out_buf.m_expandable = MZ_TRUE; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; *pOut_len = out_buf.m_size; return out_buf.m_pBuf; } size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_buf) return 0; out_buf.m_pBuf = (mz_uint8 *)pOut_buf; out_buf.m_capacity = out_buf_len; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; return out_buf.m_size; } /* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) { mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; return comp_flags; } #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ #endif /* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) { /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; if (!pComp) return NULL; MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } /* write dummy header */ for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); /* compress image data */ tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } /* write real header */ *pLen_out = out_buf.m_size - 41; { static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x44, 0x41, 0x54 }; pnghdr[18] = (mz_uint8)(w >> 8); pnghdr[19] = (mz_uint8)w; pnghdr[22] = (mz_uint8)(h >> 8); pnghdr[23] = (mz_uint8)h; pnghdr[25] = chans[num_chans]; pnghdr[33] = (mz_uint8)(*pLen_out >> 24); pnghdr[34] = (mz_uint8)(*pLen_out >> 16); pnghdr[35] = (mz_uint8)(*pLen_out >> 8); pnghdr[36] = (mz_uint8)*pLen_out; c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); for (i = 0; i < 4; ++i, c <<= 8) ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); memcpy(out_buf.m_pBuf, pnghdr, 41); } /* write footer (IDAT CRC-32, followed by IEND chunk) */ if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); for (i = 0; i < 4; ++i, c <<= 8) (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); /* compute final size of file, grab compressed data buffer and return */ *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; } void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) { /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); } #ifndef MINIZ_NO_MALLOC /* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ /* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ tdefl_compressor *tdefl_compressor_alloc(void) { return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); } void tdefl_compressor_free(tdefl_compressor *pComp) { MZ_FREE(pComp); } #endif #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ yquake2-QUAKE2_8_40/src/common/unzip/miniz/miniz_tdef.h000066400000000000000000000236011465112212000227660ustar00rootroot00000000000000#pragma once #include "miniz_common.h" #ifndef MINIZ_NO_DEFLATE_APIS #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Compression API Definitions */ /* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ #define TDEFL_LESS_MEMORY 0 /* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ /* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ enum { TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF }; /* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ /* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ /* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ /* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ /* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ /* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ /* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ /* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ /* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ enum { TDEFL_WRITE_ZLIB_HEADER = 0x01000, TDEFL_COMPUTE_ADLER32 = 0x02000, TDEFL_GREEDY_PARSING_FLAG = 0x04000, TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, TDEFL_RLE_MATCHES = 0x10000, TDEFL_FILTER_MATCHES = 0x20000, TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 }; /* High level compression functions: */ /* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ /* On entry: */ /* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ /* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ /* On return: */ /* Function returns a pointer to the compressed data, or NULL on failure. */ /* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ /* The caller must free() the returned block when it's no longer needed. */ MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); /* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ /* Returns 0 on failure. */ MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); /* Compresses an image to a compressed PNG file in memory. */ /* On entry: */ /* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ /* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ /* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ /* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ /* On return: */ /* Function returns a pointer to the compressed data, or NULL on failure. */ /* *pLen_out will be set to the size of the PNG image file. */ /* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); /* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); /* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; /* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ #if TDEFL_LESS_MEMORY enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #else enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #endif /* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ typedef enum { TDEFL_STATUS_BAD_PARAM = -2, TDEFL_STATUS_PUT_BUF_FAILED = -1, TDEFL_STATUS_OKAY = 0, TDEFL_STATUS_DONE = 1 } tdefl_status; /* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ typedef enum { TDEFL_NO_FLUSH = 0, TDEFL_SYNC_FLUSH = 2, TDEFL_FULL_FLUSH = 3, TDEFL_FINISH = 4 } tdefl_flush; /* tdefl's compression state structure. */ typedef struct { tdefl_put_buf_func_ptr m_pPut_buf_func; void *m_pPut_buf_user; mz_uint m_flags, m_max_probes[2]; int m_greedy_parsing; mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; tdefl_status m_prev_return_status; const void *m_pIn_buf; void *m_pOut_buf; size_t *m_pIn_buf_size, *m_pOut_buf_size; tdefl_flush m_flush; const mz_uint8 *m_pSrc; size_t m_src_buf_left, m_out_buf_ofs; mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; } tdefl_compressor; /* Initializes the compressor. */ /* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ /* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ /* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ /* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); /* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); /* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ /* tdefl_compress_buffer() always consumes the entire input buffer. */ MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d); /* Create tdefl_compress() flags given zlib-style compression parameters. */ /* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ /* window_bits may be -15 (raw deflate) or 15 (zlib) */ /* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); #ifndef MINIZ_NO_MALLOC /* Allocate the tdefl_compressor structure in C so that */ /* non-C language bindings to tdefl_ API don't need to worry about */ /* structure size and allocation mechanism. */ MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void); MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); #endif #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ yquake2-QUAKE2_8_40/src/common/unzip/miniz/miniz_tinfl.c000066400000000000000000001125651465112212000231630ustar00rootroot00000000000000/************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * 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. * **************************************************************************/ #include "miniz.h" #ifndef MINIZ_NO_INFLATE_APIS #ifdef __cplusplus extern "C" { #endif /* ------------------- Low-level Decompression (completely independent from all compression API's) */ #define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) #define TINFL_MEMSET(p, c, l) memset(p, c, l) #define TINFL_CR_BEGIN \ switch (r->m_state) \ { \ case 0: #define TINFL_CR_RETURN(state_index, result) \ do \ { \ status = result; \ r->m_state = state_index; \ goto common_exit; \ case state_index:; \ } \ MZ_MACRO_END #define TINFL_CR_RETURN_FOREVER(state_index, result) \ do \ { \ for (;;) \ { \ TINFL_CR_RETURN(state_index, result); \ } \ } \ MZ_MACRO_END #define TINFL_CR_FINISH } #define TINFL_GET_BYTE(state_index, c) \ do \ { \ while (pIn_buf_cur >= pIn_buf_end) \ { \ TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ } \ c = *pIn_buf_cur++; \ } \ MZ_MACRO_END #define TINFL_NEED_BITS(state_index, n) \ do \ { \ mz_uint c; \ TINFL_GET_BYTE(state_index, c); \ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ num_bits += 8; \ } while (num_bits < (mz_uint)(n)) #define TINFL_SKIP_BITS(state_index, n) \ do \ { \ if (num_bits < (mz_uint)(n)) \ { \ TINFL_NEED_BITS(state_index, n); \ } \ bit_buf >>= (n); \ num_bits -= (n); \ } \ MZ_MACRO_END #define TINFL_GET_BITS(state_index, b, n) \ do \ { \ if (num_bits < (mz_uint)(n)) \ { \ TINFL_NEED_BITS(state_index, n); \ } \ b = bit_buf & ((1 << (n)) - 1); \ bit_buf >>= (n); \ num_bits -= (n); \ } \ MZ_MACRO_END /* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ /* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ /* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ /* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ #define TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree) \ do \ { \ temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) \ { \ code_len = temp >> 9; \ if ((code_len) && (num_bits >= code_len)) \ break; \ } \ else if (num_bits > TINFL_FAST_LOOKUP_BITS) \ { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do \ { \ temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); \ if (temp >= 0) \ break; \ } \ TINFL_GET_BYTE(state_index, c); \ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ num_bits += 8; \ } while (num_bits < 15); /* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ /* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ /* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ /* The slow path is only executed at the very end of the input buffer. */ /* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ /* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ #define TINFL_HUFF_DECODE(state_index, sym, pLookUp, pTree) \ do \ { \ int temp; \ mz_uint code_len, c; \ if (num_bits < 15) \ { \ if ((pIn_buf_end - pIn_buf_cur) < 2) \ { \ TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree); \ } \ else \ { \ bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ pIn_buf_cur += 2; \ num_bits += 16; \ } \ } \ if ((temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else \ { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do \ { \ temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while (temp < 0); \ } \ sym = temp; \ bit_buf >>= code_len; \ num_bits -= code_len; \ } \ MZ_MACRO_END static void tinfl_clear_tree(tinfl_decompressor *r) { if (r->m_type == 0) MZ_CLEAR_ARR(r->m_tree_0); else if (r->m_type == 1) MZ_CLEAR_ARR(r->m_tree_1); else MZ_CLEAR_ARR(r->m_tree_2); } tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { static const mz_uint16 s_length_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 const mz_uint8 s_length_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 const mz_uint16 s_dist_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 const mz_uint8 s_dist_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 const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static const mz_uint16 s_min_table_sizes[3] = { 257, 1, 4 }; mz_int16 *pTrees[3]; mz_uint8 *pCode_sizes[3]; tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next ? pOut_buf_next + *pOut_buf_size : NULL; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } pTrees[0] = r->m_tree_0; pTrees[1] = r->m_tree_1; pTrees[2] = r->m_tree_2; pCode_sizes[0] = r->m_code_size_0; pCode_sizes[1] = r->m_code_size_1; pCode_sizes[2] = r->m_code_size_2; num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; TINFL_CR_BEGIN bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)((size_t)1 << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } } do { TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; if (r->m_type == 0) { TINFL_SKIP_BITS(5, num_bits & 7); for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } while ((counter) && (num_bits)) { TINFL_GET_BITS(51, dist, 8); while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)dist; counter--; } while (counter) { size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } while (pIn_buf_cur >= pIn_buf_end) { TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); } n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; } } else if (r->m_type == 3) { TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); } else { if (r->m_type == 1) { mz_uint8 *p = r->m_code_size_0; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_code_size_1, 5, 32); for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) *p++ = 9; for (; i <= 279; ++i) *p++ = 7; for (; i <= 287; ++i) *p++ = 8; } else { for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } MZ_CLEAR_ARR(r->m_code_size_2); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_code_size_2[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for (; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; mz_int16 *pLookUp; mz_int16 *pTree; mz_uint8 *pCode_size; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pLookUp = r->m_look_up[r->m_type]; pTree = pTrees[r->m_type]; pCode_size = pCode_sizes[r->m_type]; MZ_CLEAR_ARR(total_syms); TINFL_MEMSET(pLookUp, 0, sizeof(r->m_look_up[0])); tinfl_clear_tree(r); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pCode_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } if ((65536 != total) && (used_syms > 1)) { TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { mz_uint rev_code = 0, l, cur_code, code_size = pCode_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pLookUp[rev_code] = k; rev_code += (1 << code_size); } continue; } if (0 == (tree_cur = pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); if (!pTree[-tree_cur - 1]) { pTree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); pTree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) { mz_uint s; TINFL_HUFF_DECODE(16, dist, r->m_look_up[2], r->m_tree_2); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } if ((dist == 16) && (!counter)) { TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); } num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; } if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } TINFL_MEMCPY(r->m_code_size_0, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_code_size_1, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for (;;) { mz_uint8 *pSrc; for (;;) { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { TINFL_HUFF_DECODE(23, counter, r->m_look_up[0], r->m_tree_0); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)counter; } else { int sym2; mz_uint code_len; #if TINFL_USE_64BIT_BITBUF if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } #else if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; bit_buf >>= code_len; num_bits -= code_len; if (counter & 256) break; #if !TINFL_USE_64BIT_BITBUF if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; num_bits -= code_len; pOut_buf_cur[0] = (mz_uint8)counter; if (sym2 & 256) { pOut_buf_cur++; counter = sym2; break; } pOut_buf_cur[1] = (mz_uint8)sym2; pOut_buf_cur += 2; } } if ((counter &= 511) == 256) break; num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } TINFL_HUFF_DECODE(26, dist, r->m_look_up[1], r->m_tree_1); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); } pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { while (counter--) { while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; } continue; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES else if ((counter >= 9) && (counter <= dist)) { const mz_uint8 *pSrc_end = pSrc + (counter & ~7); do { #ifdef MINIZ_UNALIGNED_USE_MEMCPY memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2); #else ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; #endif pOut_buf_cur += 8; } while ((pSrc += 8) < pSrc_end); if ((counter &= 7) < 3) { if (counter) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } continue; } } #endif while(counter>2) { pOut_buf_cur[0] = pSrc[0]; pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur[2] = pSrc[2]; pOut_buf_cur += 3; pSrc += 3; counter -= 3; } if (counter > 0) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } } } } while (!(r->m_final & 1)); /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ TINFL_SKIP_BITS(32, num_bits & 7); while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { --pIn_buf_cur; num_bits -= 8; } bit_buf &= ~(~(tinfl_bit_buf_t)0 << num_bits); MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } } TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); TINFL_CR_FINISH common_exit: /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) { while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { --pIn_buf_cur; num_bits -= 8; } } r->m_num_bits = num_bits; r->m_bit_buf = bit_buf & ~(~(tinfl_bit_buf_t)0 << num_bits); r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) { const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; } return status; } /* Higher level helper functions. */ void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; *pOut_len = 0; tinfl_init(&decomp); for (;;) { size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } src_buf_ofs += src_buf_size; *pOut_len += dst_buf_size; if (status == TINFL_STATUS_DONE) break; new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); if (!pNew_buf) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; } return pBuf; } size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; } int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { int result = 0; tinfl_decompressor decomp; mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; if (!pDict) return TINFL_STATUS_FAILED; memset(pDict,0,TINFL_LZ_DICT_SIZE); tinfl_init(&decomp); for (;;) { size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); in_buf_ofs += in_buf_size; if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) break; if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { result = (status == TINFL_STATUS_DONE); break; } dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); } MZ_FREE(pDict); *pIn_buf_size = in_buf_ofs; return result; } #ifndef MINIZ_NO_MALLOC tinfl_decompressor *tinfl_decompressor_alloc(void) { tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); if (pDecomp) tinfl_init(pDecomp); return pDecomp; } void tinfl_decompressor_free(tinfl_decompressor *pDecomp) { MZ_FREE(pDecomp); } #endif #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ yquake2-QUAKE2_8_40/src/common/unzip/miniz/miniz_tinfl.h000066400000000000000000000202271465112212000231610ustar00rootroot00000000000000#pragma once #include "miniz_common.h" /* ------------------- Low-level Decompression API Definitions */ #ifndef MINIZ_NO_INFLATE_APIS #ifdef __cplusplus extern "C" { #endif /* Decompression flags used by tinfl_decompress(). */ /* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ /* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ /* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ /* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ enum { TINFL_FLAG_PARSE_ZLIB_HEADER = 1, TINFL_FLAG_HAS_MORE_INPUT = 2, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, TINFL_FLAG_COMPUTE_ADLER32 = 8 }; /* High level decompression functions: */ /* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ /* On entry: */ /* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ /* On return: */ /* Function returns a pointer to the decompressed data, or NULL on failure. */ /* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ /* The caller must call mz_free() on the returned block when it's no longer needed. */ MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); /* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ /* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); /* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ /* Returns 1 on success or 0 on failure. */ typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; #ifndef MINIZ_NO_MALLOC /* Allocate the tinfl_decompressor structure in C so that */ /* non-C language bindings to tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void); MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp); #endif /* Max size of LZ dictionary. */ #define TINFL_LZ_DICT_SIZE 32768 /* Return status. */ typedef enum { /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ TINFL_STATUS_BAD_PARAM = -3, /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ TINFL_STATUS_ADLER32_MISMATCH = -2, /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ TINFL_STATUS_FAILED = -1, /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ TINFL_STATUS_DONE = 0, /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ TINFL_STATUS_NEEDS_MORE_INPUT = 1, /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ /* so I may need to add some code to address this. */ TINFL_STATUS_HAS_MORE_OUTPUT = 2 } tinfl_status; /* Initializes the decompressor to its initial state. */ #define tinfl_init(r) \ do \ { \ (r)->m_state = 0; \ } \ MZ_MACRO_END #define tinfl_get_adler32(r) (r)->m_check_adler32 /* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ /* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); /* Internal/private bits follow. */ enum { TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; #if MINIZ_HAS_64BIT_REGISTERS #define TINFL_USE_64BIT_BITBUF 1 #else #define TINFL_USE_64BIT_BITBUF 0 #endif #if TINFL_USE_64BIT_BITBUF typedef mz_uint64 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (64) #else typedef mz_uint32 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (32) #endif struct tinfl_decompressor_tag { mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE]; mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2]; mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2]; mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0]; mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1]; mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2]; mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; #ifdef __cplusplus } #endif #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ yquake2-QUAKE2_8_40/src/common/unzip/miniz/miniz_zip.h000066400000000000000000000613231465112212000226510ustar00rootroot00000000000000 #pragma once #include "miniz_common.h" /* ------------------- ZIP archive reading/writing */ #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus extern "C" { #endif enum { /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 }; typedef struct { /* Central directory file index. */ mz_uint32 m_file_index; /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ mz_uint64 m_central_dir_ofs; /* These fields are copied directly from the zip's central dir. */ mz_uint16 m_version_made_by; mz_uint16 m_version_needed; mz_uint16 m_bit_flag; mz_uint16 m_method; /* CRC-32 of uncompressed data. */ mz_uint32 m_crc32; /* File's compressed size. */ mz_uint64 m_comp_size; /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ mz_uint64 m_uncomp_size; /* Zip internal and external file attributes. */ mz_uint16 m_internal_attr; mz_uint32 m_external_attr; /* Entry's local header file offset in bytes. */ mz_uint64 m_local_header_ofs; /* Size of comment in bytes. */ mz_uint32 m_comment_size; /* MZ_TRUE if the entry appears to be a directory. */ mz_bool m_is_directory; /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ mz_bool m_is_encrypted; /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ mz_bool m_is_supported; /* Filename. If string ends in '/' it's a subdirectory entry. */ /* Guaranteed to be zero terminated, may be truncated to fit. */ char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; /* Comment field. */ /* Guaranteed to be zero terminated, may be truncated to fit. */ char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; #ifdef MINIZ_NO_TIME MZ_TIME_T m_padding; #else MZ_TIME_T m_time; #endif } mz_zip_archive_file_stat; typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); struct mz_zip_internal_state_tag; typedef struct mz_zip_internal_state_tag mz_zip_internal_state; typedef enum { MZ_ZIP_MODE_INVALID = 0, MZ_ZIP_MODE_READING = 1, MZ_ZIP_MODE_WRITING = 2, MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 } mz_zip_mode; typedef enum { MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000, /*After adding a compressed file, seek back to local file header and set the correct sizes*/ MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000 } mz_zip_flags; typedef enum { MZ_ZIP_TYPE_INVALID = 0, MZ_ZIP_TYPE_USER, MZ_ZIP_TYPE_MEMORY, MZ_ZIP_TYPE_HEAP, MZ_ZIP_TYPE_FILE, MZ_ZIP_TYPE_CFILE, MZ_ZIP_TOTAL_TYPES } mz_zip_type; /* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ typedef enum { MZ_ZIP_NO_ERROR = 0, MZ_ZIP_UNDEFINED_ERROR, MZ_ZIP_TOO_MANY_FILES, MZ_ZIP_FILE_TOO_LARGE, MZ_ZIP_UNSUPPORTED_METHOD, MZ_ZIP_UNSUPPORTED_ENCRYPTION, MZ_ZIP_UNSUPPORTED_FEATURE, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, MZ_ZIP_NOT_AN_ARCHIVE, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, MZ_ZIP_UNSUPPORTED_MULTIDISK, MZ_ZIP_DECOMPRESSION_FAILED, MZ_ZIP_COMPRESSION_FAILED, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, MZ_ZIP_CRC_CHECK_FAILED, MZ_ZIP_UNSUPPORTED_CDIR_SIZE, MZ_ZIP_ALLOC_FAILED, MZ_ZIP_FILE_OPEN_FAILED, MZ_ZIP_FILE_CREATE_FAILED, MZ_ZIP_FILE_WRITE_FAILED, MZ_ZIP_FILE_READ_FAILED, MZ_ZIP_FILE_CLOSE_FAILED, MZ_ZIP_FILE_SEEK_FAILED, MZ_ZIP_FILE_STAT_FAILED, MZ_ZIP_INVALID_PARAMETER, MZ_ZIP_INVALID_FILENAME, MZ_ZIP_BUF_TOO_SMALL, MZ_ZIP_INTERNAL_ERROR, MZ_ZIP_FILE_NOT_FOUND, MZ_ZIP_ARCHIVE_TOO_LARGE, MZ_ZIP_VALIDATION_FAILED, MZ_ZIP_WRITE_CALLBACK_FAILED, MZ_ZIP_TOTAL_ERRORS } mz_zip_error; typedef struct { mz_uint64 m_archive_size; mz_uint64 m_central_directory_file_ofs; /* We only support up to UINT32_MAX files in zip64 mode. */ mz_uint32 m_total_files; mz_zip_mode m_zip_mode; mz_zip_type m_zip_type; mz_zip_error m_last_error; mz_uint64 m_file_offset_alignment; mz_alloc_func m_pAlloc; mz_free_func m_pFree; mz_realloc_func m_pRealloc; void *m_pAlloc_opaque; mz_file_read_func m_pRead; mz_file_write_func m_pWrite; mz_file_needs_keepalive m_pNeeds_keepalive; void *m_pIO_opaque; mz_zip_internal_state *m_pState; } mz_zip_archive; typedef struct { mz_zip_archive *pZip; mz_uint flags; int status; mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf; void *pWrite_buf; size_t out_blk_remain; tinfl_decompressor inflator; #ifdef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS mz_uint padding; #else mz_uint file_crc32; #endif } mz_zip_reader_extract_iter_state; /* -------- ZIP reading */ /* Inits a ZIP archive reader. */ /* These functions read and validate the archive's central directory. */ MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); #ifndef MINIZ_NO_STDIO /* Read a archive from a disk file. */ /* file_start_ofs is the file offset where the archive actually begins, or 0. */ /* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); /* Read an archive from an already opened FILE, beginning at the current file position. */ /* The archive is assumed to be archive_size bytes long. If archive_size is 0, then the entire rest of the file is assumed to contain the archive. */ /* The FILE will NOT be closed when mz_zip_reader_end() is called. */ MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); #endif /* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip); /* -------- ZIP reading or writing */ /* Clears a mz_zip_archive struct to all zeros. */ /* Important: This must be done before passing the struct to any mz_zip functions. */ MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); /* Returns the total number of files in the archive. */ MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); /* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); /* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ /* Note that the m_last_error functionality is not thread safe. */ MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err); /* MZ_TRUE if the archive file entry is a directory entry. */ MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); /* MZ_TRUE if the file is encrypted/strong encrypted. */ MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); /* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); /* Retrieves the filename of an archive file entry. */ /* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); /* Attempts to locates a file in the archive's central directory. */ /* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ /* Returns -1 if the file cannot be found. */ MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); /* Returns detailed information about an archive file entry. */ MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); /* MZ_TRUE if the file is in zip64 format. */ /* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); /* Returns the total central directory size in bytes. */ /* The current max supported size is <= MZ_UINT32_MAX. */ MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); /* Extracts a archive file to a memory buffer using no memory allocation. */ /* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); /* Extracts a archive file to a memory buffer. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); /* Extracts a archive file to a dynamically allocated heap buffer. */ /* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ /* Returns NULL and sets the last error on failure. */ MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); /* Extracts a archive file using a callback function to output the file's data. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); /* Extract a file iteratively */ MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); #ifndef MINIZ_NO_STDIO /* Extracts a archive file to a disk file and sets its last accessed and modified times. */ /* This function only extracts files, not archive directory records. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); /* Extracts a archive file starting at the current position in the destination FILE stream. */ MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); #endif #if 0 /* TODO */ typedef void *mz_zip_streaming_extract_state_ptr; mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); mz_uint64 mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); mz_uint64 mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, mz_uint64 new_ofs); size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); #endif /* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ /* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); /* Validates an entire archive by calling mz_zip_validate_file() on each file. */ MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); /* Misc utils/helpers, valid for ZIP reading or writing */ MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); #ifndef MINIZ_NO_STDIO MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); #endif /* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); /* -------- ZIP writing */ #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS /* Inits a ZIP archive writer. */ /*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ /*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); #ifndef MINIZ_NO_STDIO MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); #endif /* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ /* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ /* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ /* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ /* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ /* the archive is finalized the file's central directory will be hosed. */ MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); /* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ /* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); /* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ /* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); /* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */ /* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/ MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); #ifndef MINIZ_NO_STDIO /* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); /* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); #endif /* Adds a file to an archive by fully cloning the data from another archive. */ /* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); /* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ /* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ /* An archive must be manually finalized by calling this function for it to be valid. */ MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); /* Finalizes a heap archive, returning a pointer to the heap block and its size. */ /* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); /* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ /* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); /* -------- Misc. high-level helper functions: */ /* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ /* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ /* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ /* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); #ifndef MINIZ_NO_STDIO /* Reads a single file from an archive into a heap block. */ /* If pComment is not NULL, only the file with the specified comment will be extracted. */ /* Returns NULL on failure. */ MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); #endif #endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ #ifdef __cplusplus } #endif #endif /* MINIZ_NO_ARCHIVE_APIS */ yquake2-QUAKE2_8_40/src/common/unzip/miniz/minizconf.h000066400000000000000000000037361465112212000226410ustar00rootroot00000000000000/* * Copyright (c) 2018 Yamagi Burmeister * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * -------------------------------------------------------------------- * * Some additional definitions required by minizip and not provided * by the miniz library. * * -------------------------------------------------------------------- */ #ifndef COMMON_UNZIP_MINIZCONF_H #define COMMON_UNZIP_MINIZCONF_H #ifndef OF #define OF(args) args #endif #ifndef ZEXPORT #define ZEXPORT #endif #ifndef z_off_t #define z_off_t long #endif #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t #else # if defined(_WIN32) && !defined(__GNUC__) # define z_off64_t __int64 # else # define z_off64_t z_off_t # endif #endif #endif yquake2-QUAKE2_8_40/src/common/unzip/unzip.c000066400000000000000000002041401465112212000206470ustar00rootroot00000000000000/* unzip.c -- IO for uncompress .zip files using zlib Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications of Unzip for Zip64 Copyright (C) 2007-2008 Even Rouault Modifications for Zip64 support on both zip and unzip Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt ------------------------------------------------------------------------------------ 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). ------------------------------------------------------------------------------------ Changes in unzip.c 2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos 2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* 2007-2008 - Even Rouault - Remove old C style function prototypes 2007-2008 - Even Rouault - Add unzip support for ZIP64 Copyright (C) 2007-2008 Even Rouault Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G should only read the compressed/uncompressed size from the Zip64 format if the size from normal header was 0xFFFFFFFF Oct-2009 - Mathias Svensson - Applied some bug fixes from patches received from Gilles Vollant Oct-2009 - Mathias Svensson - Applied support to unzip files with compression method BZIP2 (bzip2 lib is required) Patch created by Daniel Borca Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson */ #include #include #include #ifndef NOUNCRYPT #define NOUNCRYPT #endif #include "miniz/miniz.h" #include "miniz/minizconf.h" #include "unzip.h" #ifdef STDC # 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 #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_info64_internal_s { ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ } unz_file_info64_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 ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ uLong stream_initialised; /* flag set if stream structure is initialised*/ ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ uInt size_local_extrafield;/* size of the local extra field */ ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ ZPOS64_T total_out_64; uLong crc32; /* crc32 of all data uncompressed */ uLong crc32_wait; /* crc32 we must obtain after decompress all */ ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ zlib_filefunc64_32_def z_filefunc; voidpf filestream; /* io structure of the zipfile */ uLong compression_method; /* compression method (0==store) */ ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ int raw; } file_in_zip64_read_info_s; /* unz64_s contain internal information about the zipfile */ typedef struct { zlib_filefunc64_32_def z_filefunc; int is64bitOpenFunction; voidpf filestream; /* io structure of the zipfile */ unz_global_info64 gi; /* public global information */ ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ ZPOS64_T num_file; /* number of the current file in the zipfile*/ ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ ZPOS64_T central_pos; /* position of the beginning of the central dir*/ ZPOS64_T size_central_dir; /* size of the central directory */ ZPOS64_T offset_central_dir; /* offset of start of central directory with respect to the starting disk number */ unz_file_info64 cur_file_info; /* public info about the current file in zip*/ unz_file_info64_internal cur_file_info_internal; /* private info about it*/ file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current file if we are decompressing it */ int encrypted; int isZip64; # ifndef NOUNCRYPT unsigned long keys[3]; /* keys defining the pseudo-random sequence */ const z_crc_t* pcrc_32_tab; # endif } unz64_s; #ifndef NOUNCRYPT #include "crypt.h" #endif /* =========================================================================== Reads a long in LSB order from the given gz_stream. Sets */ local int unz64local_getShort(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX) { unsigned char c[2]; int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,c,2); if (err==2) { *pX = c[0] | ((uLong)c[1] << 8); return UNZ_OK; } else { *pX = 0; if (ZERROR64(*pzlib_filefunc_def,filestream)) return UNZ_ERRNO; else return UNZ_EOF; } } local int unz64local_getLong(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX) { unsigned char c[4]; int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,c,4); if (err==4) { *pX = c[0] | ((uLong)c[1] << 8) | ((uLong)c[2] << 16) | ((uLong)c[3] << 24); return UNZ_OK; } else { *pX = 0; if (ZERROR64(*pzlib_filefunc_def,filestream)) return UNZ_ERRNO; else return UNZ_EOF; } } local int unz64local_getLong64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) { unsigned char c[8]; int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,c,8); if (err==8) { *pX = c[0] | ((ZPOS64_T)c[1] << 8) | ((ZPOS64_T)c[2] << 16) | ((ZPOS64_T)c[3] << 24) | ((ZPOS64_T)c[4] << 32) | ((ZPOS64_T)c[5] << 40) | ((ZPOS64_T)c[6] << 48) | ((ZPOS64_T)c[7] << 56); return UNZ_OK; } else { *pX = 0; if (ZERROR64(*pzlib_filefunc_def,filestream)) return UNZ_ERRNO; else return UNZ_EOF; } } /* My own strcmpi / strcasecmp */ local int strcmpcasenosensitive_internal(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 filenames (fileName1,fileName2). If iCaseSensitivity = 1, comparison is case sensitive (like strcmp) If iCaseSensitivity = 2, comparison is not case sensitive (like strcmpi or strcasecmp) If iCaseSensitivity = 0, case sensitivity is default of your operating system (like 1 on Unix, 2 on Windows) */ extern int ZEXPORT unzStringFileNameCompare (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 #ifndef CENTRALDIRINVALID #define CENTRALDIRINVALID ((ZPOS64_T)(-1)) #endif /* Locate the Central directory of a zipfile (at the end, just before the global comment) */ local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ ZPOS64_T uPosFound=CENTRALDIRINVALID; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return CENTRALDIRINVALID; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return CENTRALDIRINVALID; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD64(*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+(unsigned)i; break; } if (uPosFound!=CENTRALDIRINVALID) break; } free(buf); return uPosFound; } /* Locate the Central directory 64 of a zipfile (at the end, just before the global comment) */ local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) { unsigned char* buf; ZPOS64_T uSizeFile; ZPOS64_T uBackRead; ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ ZPOS64_T uPosFound=CENTRALDIRINVALID; uLong uL; ZPOS64_T relativeOffset; if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) return CENTRALDIRINVALID; uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); if (buf==NULL) return CENTRALDIRINVALID; uBackRead = 4; while (uBackReaduMaxBack) uBackRead = uMaxBack; else uBackRead+=BUFREADCOMMENT; uReadPos = uSizeFile-uBackRead ; uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) break; if (ZREAD64(*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))==0x06) && ((*(buf+i+3))==0x07)) { uPosFound = uReadPos+(unsigned)i; break; } if (uPosFound!=CENTRALDIRINVALID) break; } free(buf); if (uPosFound == CENTRALDIRINVALID) return CENTRALDIRINVALID; /* Zip64 end of central directory locator */ if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) return CENTRALDIRINVALID; /* the signature, already checked */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return CENTRALDIRINVALID; /* number of the disk with the start of the zip64 end of central directory */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return CENTRALDIRINVALID; if (uL != 0) return CENTRALDIRINVALID; /* relative offset of the zip64 end of central directory record */ if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) return CENTRALDIRINVALID; /* total number of disks */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return CENTRALDIRINVALID; if (uL != 1) return CENTRALDIRINVALID; /* Goto end of central directory record */ if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) return CENTRALDIRINVALID; /* the signature */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return CENTRALDIRINVALID; if (uL != 0x06064b50) return CENTRALDIRINVALID; return relativeOffset; } /* 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. */ local unzFile unzOpenInternal(const void *path, zlib_filefunc64_32_def* pzlib_filefunc64_32_def, int is64bitOpenFunction) { unz64_s us; unz64_s *s; ZPOS64_T central_pos; uLong uL; uLong number_disk; /* number of the current dist, used for spanning ZIP, unsupported, always 0*/ uLong number_disk_with_CD; /* number the the disk with central dir, used for spanning ZIP, unsupported, always 0*/ ZPOS64_T 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; us.z_filefunc.zseek32_file = NULL; us.z_filefunc.ztell32_file = NULL; if (pzlib_filefunc64_32_def==NULL) fill_fopen64_filefunc(&us.z_filefunc.zfile_func64); else us.z_filefunc = *pzlib_filefunc64_32_def; us.is64bitOpenFunction = is64bitOpenFunction; us.filestream = ZOPEN64(us.z_filefunc, path, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING); if (us.filestream==NULL) return NULL; central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); if (central_pos!=CENTRALDIRINVALID) { uLong uS; ZPOS64_T uL64; us.isZip64 = 1; if (ZSEEK64(us.z_filefunc, us.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* the signature, already checked */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; /* size of zip64 end of central directory record */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) err=UNZ_ERRNO; /* version made by */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) err=UNZ_ERRNO; /* version needed to extract */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) err=UNZ_ERRNO; /* number of this disk */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; /* number of the disk with the start of the central directory */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central directory on this disk */ if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; /* total number of entries in the central directory */ if (unz64local_getLong64(&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 (unz64local_getLong64(&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 (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; us.gi.size_comment = 0; } else { central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); if (central_pos==CENTRALDIRINVALID) err=UNZ_ERRNO; us.isZip64 = 0; if (ZSEEK64(us.z_filefunc, us.filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) err=UNZ_ERRNO; /* the signature, already checked */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; /* number of this disk */ if (unz64local_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 (unz64local_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 (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; us.gi.number_entry = uL; /* total number of entries in the central dir */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; number_entry_CD = uL; 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 (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; us.size_central_dir = uL; /* offset of start of central directory with respect to the starting disk number */ if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) err=UNZ_ERRNO; us.offset_central_dir = uL; /* zipfile comment length */ if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; } if ((central_pospfile_in_zip_read!=NULL) unzCloseCurrentFile(file); ZCLOSE64(s->z_filefunc, s->filestream); free(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 unzGetGlobalInfo64(unzFile file, unz_global_info64* pglobal_info) { unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; *pglobal_info=s->gi; return UNZ_OK; } extern int ZEXPORT unzGetGlobalInfo(unzFile file, unz_global_info* pglobal_info32) { unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; /* to do : check if number_entry is not truncated */ pglobal_info32->number_entry = (uLong)s->gi.number_entry; pglobal_info32->size_comment = s->gi.size_comment; return UNZ_OK; } /* Translate date/time from Dos format to tm_unz (readable more easily) */ local void unz64local_DosDateToTmuDate(ZPOS64_T ulDosDate, tm_unz* ptm) { ZPOS64_T uDate; uDate = (ZPOS64_T)(ulDosDate>>16); ptm->tm_mday = (int)(uDate&0x1f) ; ptm->tm_mon = (int)((((uDate)&0x1E0)/0x20)-1) ; ptm->tm_year = (int)(((uDate&0x0FE00)/0x0200)+1980) ; ptm->tm_hour = (int) ((ulDosDate &0xF800)/0x800); ptm->tm_min = (int) ((ulDosDate&0x7E0)/0x20) ; ptm->tm_sec = (int) (2*(ulDosDate&0x1f)) ; } /* Get Info about the current file in the zipfile, with internal only info */ local int unz64local_GetCurrentFileInfoInternal(unzFile file, unz_file_info64 *pfile_info, unz_file_info64_internal *pfile_info_internal, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize) { unz64_s* s; unz_file_info64 file_info; unz_file_info64_internal file_info_internal; int err=UNZ_OK; uLong uMagic; long lSeek=0; uLong uL; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; if (ZSEEK64(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 (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x02014b50) err=UNZ_BADZIPFILE; } if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) err=UNZ_ERRNO; unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) err=UNZ_ERRNO; file_info.compressed_size = uL; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) err=UNZ_ERRNO; file_info.uncompressed_size = uL; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) err=UNZ_ERRNO; // relative offset of local header if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) err=UNZ_ERRNO; file_info_internal.offset_curfile = uL; lSeek+=file_info.size_filename; if ((err==UNZ_OK) && (szFileName!=NULL)) { uLong uSizeRead ; if (file_info.size_filename0) && (fileNameBufferSize>0)) if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek -= uSizeRead; } // Read extrafield if ((err==UNZ_OK) && (extraField!=NULL)) { ZPOS64_T uSizeRead ; if (file_info.size_file_extraz_filefunc, s->filestream,(ZPOS64_T)lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) err=UNZ_ERRNO; lSeek += file_info.size_file_extra - (uLong)uSizeRead; } else lSeek += file_info.size_file_extra; if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) { uLong acc = 0; // since lSeek now points to after the extra field we need to move back lSeek -= file_info.size_file_extra; if (lSeek!=0) { if (ZSEEK64(s->z_filefunc, s->filestream,(ZPOS64_T)lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } while(acc < file_info.size_file_extra) { uLong headerId; uLong dataSize; if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) err=UNZ_ERRNO; /* ZIP64 extra fields */ if (headerId == 0x0001) { if(file_info.uncompressed_size == MAXU32) { if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) err=UNZ_ERRNO; } if(file_info.compressed_size == MAXU32) { if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) err=UNZ_ERRNO; } if(file_info_internal.offset_curfile == MAXU32) { /* Relative Header offset */ if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) err=UNZ_ERRNO; } if(file_info.disk_num_start == 0xffff) { /* Disk Start Number */ if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) err=UNZ_ERRNO; } } else { if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) err=UNZ_ERRNO; } acc += 2 + 2 + dataSize; } } if ((err==UNZ_OK) && (szComment!=NULL)) { uLong uSizeRead ; if (file_info.size_file_commentz_filefunc, s->filestream,(ZPOS64_T)lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) lSeek=0; else err=UNZ_ERRNO; } if ((file_info.size_file_comment>0) && (commentBufferSize>0)) if (ZREAD64(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 unzGetCurrentFileInfo64(unzFile file, unz_file_info64 * pfile_info, char * szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char* szComment, uLong commentBufferSize) { return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, szFileName,fileNameBufferSize, extraField,extraFieldBufferSize, szComment,commentBufferSize); } extern int ZEXPORT unzGetCurrentFileInfo(unzFile file, unz_file_info * pfile_info, char * szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char* szComment, uLong commentBufferSize) { int err; unz_file_info64 file_info64; err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, szFileName,fileNameBufferSize, extraField,extraFieldBufferSize, szComment,commentBufferSize); if ((err==UNZ_OK) && (pfile_info != NULL)) { pfile_info->version = file_info64.version; pfile_info->version_needed = file_info64.version_needed; pfile_info->flag = file_info64.flag; pfile_info->compression_method = file_info64.compression_method; pfile_info->dosDate = file_info64.dosDate; pfile_info->crc = file_info64.crc; pfile_info->size_filename = file_info64.size_filename; pfile_info->size_file_extra = file_info64.size_file_extra; pfile_info->size_file_comment = file_info64.size_file_comment; pfile_info->disk_num_start = file_info64.disk_num_start; pfile_info->internal_fa = file_info64.internal_fa; pfile_info->external_fa = file_info64.external_fa; pfile_info->tmu_date = file_info64.tmu_date; pfile_info->compressed_size = (uLong)file_info64.compressed_size; pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; } return err; } /* Set the current file of the zipfile to the first file. return UNZ_OK if there is no problem */ extern int ZEXPORT unzGoToFirstFile(unzFile file) { int err=UNZ_OK; unz64_s* s; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; s->pos_in_central_dir=s->offset_central_dir; s->num_file=0; err=unz64local_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(unzFile file) { unz64_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_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 = unz64local_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 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 */ extern int ZEXPORT unzLocateFile(unzFile file, const char *szFileName, int iCaseSensitivity) { unz64_s* s; int err; /* We remember the 'current' position in the file so that we can jump * back there if we fail. */ unz_file_info64 cur_file_infoSaved; unz_file_info64_internal cur_file_info_internalSaved; ZPOS64_T num_fileSaved; ZPOS64_T pos_in_central_dirSaved; if (file==NULL) return UNZ_PARAMERROR; if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) return UNZ_PARAMERROR; s=(unz64_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 = unzGetCurrentFileInfo64(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 { ZPOS64_T pos_in_zip_directory; // offset in file ZPOS64_T num_of_file; // # of file } unz_file_pos; */ extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) { unz64_s* s; if (file==NULL || file_pos==NULL) return UNZ_PARAMERROR; s=(unz64_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 unzGetFilePos(unzFile file, unz_file_pos* file_pos) { unz64_file_pos file_pos64; int err = unzGetFilePos64(file,&file_pos64); if (err==UNZ_OK) { file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; file_pos->num_of_file = (uLong)file_pos64.num_of_file; } return err; } extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) { unz64_s* s; int err; if (file==NULL || file_pos==NULL) return UNZ_PARAMERROR; s=(unz64_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 = unz64local_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; } extern int ZEXPORT unzGoToFilePos(unzFile file, unz_file_pos* file_pos) { unz64_file_pos file_pos64; if (file_pos == NULL) return UNZ_PARAMERROR; file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; file_pos64.num_of_file = file_pos->num_of_file; return unzGoToFilePos64(file,&file_pos64); } /* // 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 unz64local_CheckCurrentFileCoherencyHeader(unz64_s* s, uInt* piSizeVar, ZPOS64_T * 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 (ZSEEK64(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 (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) err=UNZ_ERRNO; else if (uMagic!=0x04034b50) err=UNZ_BADZIPFILE; } if (unz64local_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 (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) err=UNZ_ERRNO; if (unz64local_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 (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ err=UNZ_ERRNO; if (unz64local_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 (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ err=UNZ_ERRNO; else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ err=UNZ_ERRNO; else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) err=UNZ_BADZIPFILE; if (unz64local_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 (unz64local_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(unzFile file, int* method, int* level, int raw, const char* password) { int err=UNZ_OK; uInt iSizeVar; unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; ZPOS64_T 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=(unz64_s*)file; if (!s->current_file_ok) return UNZ_PARAMERROR; if (s->pfile_in_zip_read != NULL) unzCloseCurrentFile(file); if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) return UNZ_BADZIPFILE; pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_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) { free(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->total_out_64=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 { free(pfile_in_zip_read_info->read_buffer); free(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 = 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 { free(pfile_in_zip_read_info->read_buffer); free(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 (ZSEEK64(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(ZREAD64(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(unzFile file) { return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); } extern int ZEXPORT unzOpenCurrentFilePassword(unzFile file, const char* password) { return unzOpenCurrentFile3(file, NULL, NULL, 0, password); } extern int ZEXPORT unzOpenCurrentFile2(unzFile file, int* method, int* level, int raw) { return unzOpenCurrentFile3(file, method, level, raw, NULL); } /** Addition for GDAL : START */ extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64(unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; s=(unz64_s*)file; if (file==NULL) return 0; //UNZ_PARAMERROR; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return 0; //UNZ_PARAMERROR; return pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile; } /** Addition for GDAL : END */ /* 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 some 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(unzFile file, voidp buf, unsigned len) { int err=UNZ_OK; uInt iRead = 0; unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_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 (ZSEEK64(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 (ZREAD64(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 : (int)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->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; 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 = (char*)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 = (char*)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 = (const Bytef *)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->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; 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 = (Bytef*)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 = (Bytef*)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 } // end Z_BZIP2ED else { ZPOS64_T uTotalOutBefore,uTotalOutAfter; const Bytef *bufBefore; ZPOS64_T 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; /* Detect overflow, because z_stream.total_out is uLong (32 bits) */ if (uTotalOutAftertotal_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; 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 : (int)iRead; if (err!=Z_OK) break; } } if (err==Z_OK) return (int)iRead; return err; } /* Give the current position in uncompressed data */ extern z_off_t ZEXPORT unztell(unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_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; } extern ZPOS64_T ZEXPORT unztell64(unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return (ZPOS64_T)-1; s=(unz64_s*)file; pfile_in_zip_read_info=s->pfile_in_zip_read; if (pfile_in_zip_read_info==NULL) return (ZPOS64_T)-1; return pfile_in_zip_read_info->total_out_64; } /* return 1 if the end of file was reached, 0 elsewhere */ extern int ZEXPORT unzeof(unzFile file) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_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(unzFile file, voidp buf, unsigned len) { unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; uInt read_now; ZPOS64_T size_to_read; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_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 (ZSEEK64(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 (ZREAD64(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 unzOpenCurrentFile Return UNZ_CRCERROR if all the file was read but the CRC is not good */ extern int ZEXPORT unzCloseCurrentFile(unzFile file) { int err=UNZ_OK; unz64_s* s; file_in_zip64_read_info_s* pfile_in_zip_read_info; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_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; } free(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; free(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(unzFile file, char * szComment, uLong uSizeBuf) { unz64_s* s; uLong uReadThis ; if (file==NULL) return (int)UNZ_PARAMERROR; s=(unz64_s*)file; uReadThis = uSizeBuf; if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) return UNZ_ERRNO; if (uReadThis>0) { *szComment='\0'; if (ZREAD64(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 ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) { unz64_s* s; if (file==NULL) return 0; //UNZ_PARAMERROR; s=(unz64_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 uLong ZEXPORT unzGetOffset(unzFile file) { ZPOS64_T offset64; if (file==NULL) return 0; //UNZ_PARAMERROR; offset64 = unzGetOffset64(file); return (uLong)offset64; } extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) { unz64_s* s; int err; if (file==NULL) return UNZ_PARAMERROR; s=(unz64_s*)file; s->pos_in_central_dir = pos; s->num_file = s->gi.number_entry; /* hack */ err = unz64local_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; } extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) { return unzSetOffset64(file,pos); } yquake2-QUAKE2_8_40/src/common/unzip/unzip.h000066400000000000000000000401441465112212000206560ustar00rootroot00000000000000/* unzip.h -- IO for uncompress .zip files using zlib Version 1.1, February 14h, 2010 part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) Modifications of Unzip for Zip64 Copyright (C) 2007-2008 Even Rouault Modifications for Zip64 support on both zip and unzip Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) For more info read MiniZip_info.txt --------------------------------------------------------------------------------- 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. --------------------------------------------------------------------------------- Changes See header of unzip64.c */ #ifndef _unz64_H #define _unz64_H #ifdef __cplusplus extern "C" { #endif #ifndef _ZLIB_H #include "miniz/miniz.h" #include "miniz/minizconf.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 { int tm_sec; /* seconds after the minute - [0,59] */ int tm_min; /* minutes after the hour - [0,59] */ int tm_hour; /* hours since midnight - [0,23] */ int tm_mday; /* day of the month - [1,31] */ int tm_mon; /* months since January - [0,11] */ int 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_info64_s { ZPOS64_T 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_info64; 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_info64_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 */ ZPOS64_T compressed_size; /* compressed size 8 bytes */ ZPOS64_T uncompressed_size; /* uncompressed size 8 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_info64; 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(const char* fileName1, const char* fileName2, int iCaseSensitivity); /* Compare two filenames (fileName1,fileName2). If iCaseSensitivity = 1, comparison is case sensitive (like strcmp) If iCaseSensitivity = 2, comparison is not case sensitive (like strcmpi or strcasecmp) If iCaseSensitivity = 0, case sensitivity is default of your operating system (like 1 on Unix, 2 on Windows) */ extern unzFile ZEXPORT unzOpen(const char *path); extern unzFile ZEXPORT unzOpen64(const void *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. the "64" function take a const void* pointer, because the path is just the value passed to the open64_file_func callback. Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* does not describe the reality */ extern unzFile ZEXPORT unzOpen2(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 unzFile ZEXPORT unzOpen2_64(const void *path, zlib_filefunc64_def* pzlib_filefunc_def); /* Open a Zip file, like unz64Open, but provide a set of file low level API for read/write the zip file (see ioapi.h) */ extern int ZEXPORT unzClose(unzFile file); /* Close a ZipFile opened with unzOpen. If there is files inside the .Zip opened with unzOpenCurrentFile (see later), these files MUST be closed with unzCloseCurrentFile before call unzClose. return UNZ_OK if there is no problem. */ extern int ZEXPORT unzGetGlobalInfo(unzFile file, unz_global_info *pglobal_info); extern int ZEXPORT unzGetGlobalInfo64(unzFile file, unz_global_info64 *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(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(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(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(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); typedef struct unz64_file_pos_s { ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ ZPOS64_T num_of_file; /* # of file */ } unz64_file_pos; extern int ZEXPORT unzGetFilePos64( unzFile file, unz64_file_pos* file_pos); extern int ZEXPORT unzGoToFilePos64( unzFile file, const unz64_file_pos* file_pos); /* ****************************************** */ extern int ZEXPORT unzGetCurrentFileInfo64(unzFile file, unz_file_info64 *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize); extern int ZEXPORT unzGetCurrentFileInfo(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 some 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) */ /** Addition for GDAL : START */ extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64(unzFile file); /** Addition for GDAL : END */ /***************************************************************************/ /* 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(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(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(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(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(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(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 some 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(unzFile file); extern ZPOS64_T ZEXPORT unztell64(unzFile file); /* Give the current position in uncompressed data */ extern int ZEXPORT unzeof(unzFile file); /* return 1 if the end of file was reached, 0 elsewhere */ extern int ZEXPORT unzGetLocalExtrafield(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 ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); extern uLong ZEXPORT unzGetOffset (unzFile file); /* Set the current file offset */ extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); #ifdef __cplusplus } #endif #endif /* _unz64_H */ yquake2-QUAKE2_8_40/src/common/zone.c000066400000000000000000000041131465112212000173060ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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) { Com_Printf("ERROR: Z_free(%p) failed: bad magic\n", ptr); abort(); } 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_8_40/src/game/000077500000000000000000000000001465112212000156115ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/g_ai.c000066400000000000000000000606331465112212000166640ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; static qboolean enemy_vis; static qboolean enemy_infront; static int enemy_range; static 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 = 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 the entity went away, forget it */ if (!client || !client->inuse || (client->client && level.intermissiontime)) { return false; } if (client == self->enemy) { return true; } if (client->client) { if (client->flags & FL_NOTARGET) { return false; } } else if (client->svflags & SVF_MONSTER) { if (!client->enemy) { return false; } if (client->enemy->flags & FL_NOTARGET) { return false; } } else if (heardit) { if (client->owner->flags & FL_NOTARGET) { return false; } } else { return false; } if (!heardit) { r = range(self, client); if (r == RANGE_FAR) { return false; } /* is client in an spot too dark to be seen? */ if (client->light_level <= 5) { return false; } if (!visible(self, client)) { return false; } if (r == RANGE_NEAR) { if ((client->show_hostile < level.time) && !infront(self, client)) { return false; } } else if (r == RANGE_MID) { if (!infront(self, client)) { return false; } } self->enemy = client; if (strcmp(self->enemy->classname, "player_noise") != 0) { self->monsterinfo.aiflags &= ~AI_SOUND_TARGET; if (!self->enemy->client) { self->enemy = self->enemy->enemy; if (!self->enemy->client) { self->enemy = NULL; return false; } } } } else /* heardit */ { vec3_t temp; if (self->spawnflags & 1) { if (!visible(self, client)) { return false; } } else { if (!gi.inPHS(self->s.origin, client->s.origin)) { return false; } } VectorSubtract(client->s.origin, self->s.origin, temp); if (VectorLength(temp) > 1000) /* too far to hear */ { return false; } /* check area portals - if they are different and not connected then we can't hear it */ if (client->areanum != self->areanum) { if (!gi.AreasConnected(self->areanum, client->areanum)) { return false; } } self->ideal_yaw = vectoyaw(temp); M_ChangeYaw(self); /* hunt the sound for a bit; hopefully find the real player */ self->monsterinfo.aiflags |= AI_SOUND_TARGET; self->enemy = client; } FoundTarget(self); if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight)) { self->monsterinfo.sight(self, self->enemy); } return true; } /* ============================================================================= */ qboolean FacingIdeal(edict_t *self) { float delta; if (!self) { return false; } delta = anglemod(self->s.angles[YAW] - self->ideal_yaw); if ((delta > 45) && (delta < 315)) { return false; } return true; } /* ============================================================================= */ qboolean M_CheckAttack(edict_t *self) { vec3_t spot1, spot2; float chance; trace_t tr; if (!self || !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 == SKILL_EASY) && (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_NEAR) { chance = 0.1; } else if (enemy_range == RANGE_MID) { chance = 0.02; } else { return false; } if (skill->value == SKILL_EASY) { chance *= 0.5; } else if (skill->value >= SKILL_HARD) { 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 */ static qboolean hesDeadJim(const edict_t *self) { const edict_t *enemy = self->enemy; if (!enemy || !enemy->inuse) { return true; } if (self->monsterinfo.aiflags & AI_MEDIC) { return (enemy->health > 0); } if (enemy->client && level.intermissiontime) { return true; } if (self->monsterinfo.aiflags & AI_BRUTAL) { return (enemy->health <= -80); } return (enemy->health <= 0); } qboolean ai_checkattack(edict_t *self) { vec3_t temp; if (!self) { 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 = level.time + 1; return false; } } } enemy_vis = false; /* see if the enemy is dead */ if (hesDeadJim(self)) { self->enemy = NULL; self->monsterinfo.aiflags &= ~AI_MEDIC; if (self->oldenemy && (self->oldenemy->health > 0)) { self->enemy = self->oldenemy; self->oldenemy = NULL; HuntTarget(self); } else { if (self->movetarget) { self->goalentity = self->movetarget; self->monsterinfo.walk(self); } else { /* we need the pausetime otherwise the stand code will just revert to walking with no target and the monsters will wonder around aimlessly trying to hunt the world entity */ self->monsterinfo.pausetime = level.time + 100000000; self->monsterinfo.stand(self); } return true; } } /* wake up other monsters */ self->show_hostile = level.time + 1; /* check knowledge of enemy */ enemy_vis = visible(self, self->enemy); if (enemy_vis) { self->monsterinfo.search_time = level.time + 5; VectorCopy(self->enemy->s.origin, self->monsterinfo.last_sighting); } /* 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) { 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) { /* Special case: Some projectiles like grenades or rockets are classified as an enemy. When they explode they generate a sound entity, triggering this code path. Since they're gone after the explosion their entity pointer is NULL. Therefor self->enemy is also NULL and we're crashing. Work around this by predending that the enemy is still there, and move to it. */ if (self->enemy) { 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; } tempgoal = G_SpawnOptional(); if (!tempgoal) { M_MoveToGoal(self, dist); return; } save = self->goalentity; 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); self->goalentity = save; } yquake2-QUAKE2_8_40/src/game/g_chase.c000066400000000000000000000107651465112212000173570ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/g_cmds.c000066400000000000000000000745341465112212000172260ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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); ent->health = ent->health < 1 ? 1 : ent->health; } 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 (g_quick_weap->value && cl->newweapon) { it = cl->newweapon; } else if (cl->pers.weapon) { it = cl->pers.weapon; } else { return; } selected_weapon = ITEM_INDEX(it); /* 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 || !(it->flags & IT_WEAPON)) { continue; } it->use(ent, it); if (cl->newweapon == it) { if (g_quick_weap->value) { cl->ps.stats[STAT_PICKUP_ICON] = gi.imageindex(cl->newweapon->icon); cl->ps.stats[STAT_PICKUP_STRING] = CS_ITEMS + ITEM_INDEX(cl->newweapon); cl->pickup_msg_time = level.time + 0.9f; } 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 (g_quick_weap->value && cl->newweapon) { it = cl->newweapon; } else if (cl->pers.weapon) { it = cl->pers.weapon; } else { return; } selected_weapon = ITEM_INDEX(it); /* 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 || !(it->flags & IT_WEAPON)) { continue; } it->use(ent, it); if (cl->newweapon == it) { if (g_quick_weap->value) { cl->ps.stats[STAT_PICKUP_ICON] = gi.imageindex(cl->newweapon->icon); cl->ps.stats[STAT_PICKUP_STRING] = CS_ITEMS + ITEM_INDEX(cl->newweapon); cl->pickup_msg_time = level.time + 0.9f; } 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; } } static qboolean flooded(edict_t *ent) { gclient_t *cl; int i; int num_msgs; int mx; if (!ent) { return false; } if (!deathmatch->value && !coop->value) { return false; } num_msgs = flood_msgs->value; if (num_msgs <= 0) { return false; } cl = ent->client; mx = sizeof(cl->flood_when) / sizeof(cl->flood_when[0]); if (num_msgs > mx) { gi.dprintf("flood_msgs lowered to max: 10\n"); num_msgs = mx; gi.cvar_forceset("flood_msgs", "10"); } 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 true; } i = (cl->flood_whenhead - num_msgs) + 1; if (i < 0) { i += mx; } 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 true; } cl->flood_whenhead = (cl->flood_whenhead + 1) % mx; cl->flood_when[cl->flood_whenhead] = level.time; return false; } void Cmd_Say_f(edict_t *ent, qboolean team, qboolean arg0) { int j; edict_t *other; char *p; char text[2048]; if (!ent) { return; } if ((gi.argc() < 2) && !arg0) { return; } if (flooded(ent)) { 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 (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); } static void Cmd_Teleport_f(edict_t *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; } if (gi.argc() != 4) { gi.cprintf(ent, PRINT_HIGH, "Usage: teleport x y z\n"); return; } /* Unlink it to prevent unwanted interactions with other entities. This works because linkentity() uses the first available slot and the player is always at postion 0. */ gi.unlinkentity(ent); /* Set new position */ ent->s.origin[0] = atof(gi.argv(1)); ent->s.origin[1] = atof(gi.argv(2)); ent->s.origin[2] = atof(gi.argv(3)) + 10.0; /* Remove velocity and keep the entity briefly in place to give the server and clients time to catch up. */ VectorClear(ent->velocity); ent->client->ps.pmove.pm_time = 20; ent->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT; /* Remove viewangles. They'll be recalculated by the client at the next frame. */ VectorClear(ent->s.angles); VectorClear(ent->client->ps.viewangles); VectorClear(ent->client->v_angle); /* Telefrag everything that's in the target location. */ KillBox(ent); /* And link it back in. */ gi.linkentity(ent); } static void Cmd_SpawnEntity_f(edict_t *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; } if (gi.argc() < 5 || gi.argc() > 9) { gi.cprintf(ent, PRINT_HIGH, "Usage: spawnentity classname x y z \n"); return; } ent = G_Spawn(); // set position ent->s.origin[0] = atof(gi.argv(2)); ent->s.origin[1] = atof(gi.argv(3)); ent->s.origin[2] = atof(gi.argv(4)); // angles if (gi.argc() >= 8) { ent->s.angles[0] = atof(gi.argv(5)); ent->s.angles[1] = atof(gi.argv(6)); ent->s.angles[2] = atof(gi.argv(7)); } // flags if (gi.argc() >= 9) { ent->spawnflags = atoi(gi.argv(8)); } ent->classname = G_CopyString(gi.argv(1)); ED_CallSpawn(ent); } static void Cmd_SpawnOnStartByClass(char *classname, const vec3_t origin) { edict_t *opponent = G_Spawn(); // set position opponent->s.origin[0] = origin[0]; opponent->s.origin[1] = origin[1]; opponent->s.origin[2] = origin[2]; // and class opponent->classname = G_CopyString(classname); ED_CallSpawn(opponent); gi.dprintf("Spawned entity at %f %f %f\n", origin[0], origin[1], origin[2]); } static void Cmd_SpawnOnStart_f(edict_t *ent) { edict_t *cur = NULL; 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 (gi.argc() != 2) { gi.cprintf(ent, PRINT_HIGH, "Usage: spawnonstart classname\n"); return; } while ((cur = G_Find(cur, FOFS(classname), "info_player_deathmatch")) != NULL) { Cmd_SpawnOnStartByClass(gi.argv(1), cur->s.origin); } while ((cur = G_Find(cur, FOFS(classname), "info_player_coop")) != NULL) { Cmd_SpawnOnStartByClass(gi.argv(1), cur->s.origin); } while ((cur = G_Find(cur, FOFS(classname), "info_player_start")) != NULL) { Cmd_SpawnOnStartByClass(gi.argv(1), cur->s.origin); } } static void Cmd_ListEntities_f(edict_t *ent) { 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 (gi.argc() < 2) { gi.cprintf(ent, PRINT_HIGH, "Usage: listentities \n"); return; } /* What to print? */ qboolean all = false; qboolean ammo = false; qboolean items = false; qboolean keys = false; qboolean monsters = false; qboolean weapons = false; for (int i = 1; i < gi.argc(); i++) { const char *arg = gi.argv(i); if (Q_stricmp(arg, "all") == 0) { all = true; } else if (Q_stricmp(arg, "ammo") == 0) { ammo = true; } else if (Q_stricmp(arg, "items") == 0) { items = true; } else if (Q_stricmp(arg, "keys") == 0) { keys = true; } else if (Q_stricmp(arg, "monsters") == 0) { monsters = true; } else if (Q_stricmp(arg, "weapons") == 0) { weapons = true; } else { gi.cprintf(ent, PRINT_HIGH, "Usage: listentities \n"); } } /* Print what's requested. */ for (int i = 0; i < globals.num_edicts; i++) { edict_t *cur = &g_edicts[i]; qboolean print = false; /* Ensure that the entity is valid. */ if (!cur->classname) { continue; } if (all) { print = true; } else { if (ammo) { if (strncmp(cur->classname, "ammo_", 5) == 0) { print = true; } } if (items) { if (strncmp(cur->classname, "item_", 5) == 0) { print = true; } } if (keys) { if (strncmp(cur->classname, "key_", 4) == 0) { print = true; } } if (monsters) { if (strncmp(cur->classname, "monster_", 8) == 0) { print = true; } } if (weapons) { if (strncmp(cur->classname, "weapon_", 7) == 0) { print = true; } } } if (print) { /* We use dprintf() because cprintf() may flood the server... */ gi.dprintf("%s: %f %f %f\n", cur->classname, cur->s.origin[0], cur->s.origin[1], cur->s.origin[2]); } } } static int get_ammo_usage(gitem_t *weap) { if (!weap) { return 0; } /* handles grenades and tesla which only use 1 ammo per shot */ /* have to check this because they don't store their ammo usage in weap->quantity */ if (weap->flags & IT_AMMO) { return 1; } /* weapons store their ammo usage in the quantity field */ return weap->quantity; } static gitem_t * cycle_weapon(edict_t *ent) { gclient_t *cl; gitem_t *noammo_fallback; gitem_t *noweap_fallback; gitem_t *weap; gitem_t *ammo; int i; int start; int num_weaps; const char *weapname = NULL; if (!ent) { return NULL; } cl = ent->client; if (!cl) { return NULL; } num_weaps = gi.argc(); /* find where we want to start the search for the next eligible weapon */ if (cl->newweapon) { weapname = cl->newweapon->classname; } else if (cl->pers.weapon) { weapname = cl->pers.weapon->classname; } if (weapname) { for (i = 1; i < num_weaps; i++) { if (Q_stricmp(weapname, gi.argv(i)) == 0) { break; } } i++; if (i >= num_weaps) { i = 1; } } else { i = 1; } start = i; noammo_fallback = NULL; noweap_fallback = NULL; /* find the first eligible weapon in the list we can switch to */ do { weap = FindItemByClassname(gi.argv(i)); if (weap && weap != cl->pers.weapon && (weap->flags & IT_WEAPON) && weap->use) { if (cl->pers.inventory[ITEM_INDEX(weap)] > 0) { if (weap->ammo) { ammo = FindItem(weap->ammo); if (ammo) { if (cl->pers.inventory[ITEM_INDEX(ammo)] >= get_ammo_usage(weap)) { return weap; } if (!noammo_fallback) { noammo_fallback = weap; } } } else { return weap; } } else if (!noweap_fallback) { noweap_fallback = weap; } } i++; if (i >= num_weaps) { i = 1; } } while (i != start); /* if no weapon was found, the fallbacks will be used for printing the appropriate error message to the console */ if (noammo_fallback) { return noammo_fallback; } return noweap_fallback; } static void Cmd_CycleWeap_f(edict_t *ent) { gitem_t *weap; if (!ent) { return; } if (gi.argc() <= 1) { gi.cprintf(ent, PRINT_HIGH, "Usage: cycleweap classname1 classname2 .. classnameN\n"); return; } weap = cycle_weapon(ent); if (weap) { if (ent->client->pers.inventory[ITEM_INDEX(weap)] <= 0) { gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", weap->pickup_name); } else { weap->use(ent, weap); } } } static gitem_t * preferred_weapon(edict_t *ent) { gclient_t *cl; gitem_t *noammo_fallback; gitem_t *noweap_fallback; gitem_t *weap; gitem_t *ammo; int i; int num_weaps; if (!ent) { return NULL; } cl = ent->client; if (!cl) { return NULL; } num_weaps = gi.argc(); noammo_fallback = NULL; noweap_fallback = NULL; /* find the first eligible weapon in the list we can switch to */ for (i = 1; i < num_weaps; i++) { weap = FindItemByClassname(gi.argv(i)); if (weap && (weap->flags & IT_WEAPON) && weap->use) { if (cl->pers.inventory[ITEM_INDEX(weap)] > 0) { if (weap->ammo) { ammo = FindItem(weap->ammo); if (ammo) { if (cl->pers.inventory[ITEM_INDEX(ammo)] >= get_ammo_usage(weap)) { return weap; } if (!noammo_fallback) { noammo_fallback = weap; } } } else { return weap; } } else if (!noweap_fallback) { noweap_fallback = weap; } } } /* if no weapon was found, the fallbacks will be used for printing the appropriate error message to the console */ if (noammo_fallback) { return noammo_fallback; } return noweap_fallback; } static void Cmd_PrefWeap_f(edict_t *ent) { gitem_t *weap; if (!ent) { return; } if (gi.argc() <= 1) { gi.cprintf(ent, PRINT_HIGH, "Usage: prefweap classname1 classname2 .. classnameN\n"); return; } weap = preferred_weapon(ent); if (weap) { if (ent->client->pers.inventory[ITEM_INDEX(weap)] <= 0) { gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", weap->pickup_name); } else { weap->use(ent, weap); } } } 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 if (Q_stricmp(cmd, "teleport") == 0) { Cmd_Teleport_f(ent); } else if (Q_stricmp(cmd, "spawnentity") == 0) { Cmd_SpawnEntity_f(ent); } else if (Q_stricmp(cmd, "spawnonstart") == 0) { Cmd_SpawnOnStart_f(ent); } else if (Q_stricmp(cmd, "listentities") == 0) { Cmd_ListEntities_f(ent); } else if (Q_stricmp(cmd, "cycleweap") == 0) { Cmd_CycleWeap_f(ent); } else if (Q_stricmp(cmd, "prefweap") == 0) { Cmd_PrefWeap_f(ent); } else /* anything that doesn't match a command will be a chat */ { Cmd_Say_f(ent, false, true); } } yquake2-QUAKE2_8_40/src/game/g_combat.c000066400000000000000000000351761465112212000175440ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; } 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->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) { if (targ->enemy && targ->enemy->client) { targ->oldenemy = targ->enemy; } targ->enemy = attacker->enemy; if (!(targ->monsterinfo.aiflags & AI_DUCKED)) { FoundTarget(targ); } } } static void apply_knockback(edict_t *targ, vec3_t dir, float knockback, float scale) { vec3_t kvel; float mass; if (!knockback) { return; } mass = (targ->mass < 50) ? 50.0f : (float)targ->mass; VectorNormalize2 (dir, kvel); VectorScale (kvel, scale * (knockback / mass), kvel); VectorAdd (targ->velocity, kvel, targ->velocity); } 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 == SKILL_EASY) && (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; } /* 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) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP)) { apply_knockback (targ, dir, knockback, ((client && attacker == targ) ? 1600.0f : 500.0f)); } 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; /* 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 == SKILL_HARDPLUS) { 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_8_40/src/game/g_func.c000066400000000000000000001602721465112212000172260ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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->inuse) { /* 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 wait_and_change_think(edict_t* ent) { void (*afterwaitfunc)(edict_t *) = ent->moveinfo.endfunc; ent->moveinfo.endfunc = NULL; afterwaitfunc(ent); } /* * In coop mode, this waits for coop_elevator_delay seconds * before calling afterwaitfunc(ent); otherwise it just calls * afterwaitfunc(ent); */ static void wait_and_change(edict_t* ent, void (*afterwaitfunc)(edict_t *)) { float waittime = coop_elevator_delay->value; if (coop->value && waittime > 0.0f) { if(ent->nextthink == 0) { ent->moveinfo.endfunc = afterwaitfunc; ent->think = wait_and_change_think; ent->nextthink = level.time + waittime; } } else { afterwaitfunc(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) { wait_and_change(ent, plat_go_up); } 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; 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) { 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->inuse) { /* Hack for entitiy without their origin near the model */ VectorMA (other->absmin, 0.5, other->size, other->s.origin); BecomeExplosion1(other); } return; } T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH); if (self->spawnflags & DOOR_CRUSHER) { return; } /* if a door has a negative wait, it would never come back if blocked, so let it just squash the object to death real fast */ if (self->moveinfo.wait >= 0) { if (self->moveinfo.state == STATE_DOWN) { for (ent = self->teammaster; ent; ent = ent->teamchain) { door_go_up(ent, ent->activator); } } else { for (ent = self->teammaster; ent; ent = ent->teamchain) { door_go_down(ent); } } } } void door_killed(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker, int damage /* unused */, vec3_t point /* unused */) { edict_t *ent; if (!self || !attacker) { return; } for (ent = self->teammaster; ent; ent = ent->teamchain) { ent->health = ent->max_health; ent->takedamage = DAMAGE_NO; } door_use(self->teammaster, attacker, attacker); } void door_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */) { if (!self || !other) { return; } if (!other->client) { return; } if (level.time < self->touch_debounce_time) { return; } self->touch_debounce_time = level.time + 5.0; gi.centerprintf(other, "%s", self->message); gi.sound(other, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0); } void SP_func_door(edict_t *ent) { vec3_t abs_movedir; if (!ent) { return; } if (ent->sounds != 1) { ent->moveinfo.sound_start = gi.soundindex("doors/dr1_strt.wav"); ent->moveinfo.sound_middle = gi.soundindex("doors/dr1_mid.wav"); ent->moveinfo.sound_end = gi.soundindex("doors/dr1_end.wav"); } G_SetMovedir(ent->s.angles, ent->movedir); ent->movetype = MOVETYPE_PUSH; ent->solid = SOLID_BSP; gi.setmodel(ent, ent->model); ent->blocked = door_blocked; ent->use = door_use; if (!ent->speed) { ent->speed = 100; } if (deathmatch->value) { ent->speed *= 2; } if (!ent->accel) { ent->accel = ent->speed; } if (!ent->decel) { ent->decel = ent->speed; } if (!ent->wait) { ent->wait = 3; } if (!st.lip) { st.lip = 8; } if (!ent->dmg) { ent->dmg = 2; } /* calculate second position */ VectorCopy(ent->s.origin, ent->pos1); abs_movedir[0] = fabs(ent->movedir[0]); abs_movedir[1] = fabs(ent->movedir[1]); abs_movedir[2] = fabs(ent->movedir[2]); ent->moveinfo.distance = abs_movedir[0] * ent->size[0] + abs_movedir[1] * ent->size[1] + abs_movedir[2] * ent->size[2] - st.lip; VectorMA(ent->pos1, ent->moveinfo.distance, ent->movedir, ent->pos2); /* if it starts open, switch the positions */ if (ent->spawnflags & DOOR_START_OPEN) { VectorCopy(ent->pos2, ent->s.origin); VectorCopy(ent->pos1, ent->pos2); VectorCopy(ent->s.origin, ent->pos1); } ent->moveinfo.state = STATE_BOTTOM; if (ent->health) { ent->takedamage = DAMAGE_YES; ent->die = door_killed; ent->max_health = ent->health; } else if (ent->targetname && ent->message) { gi.soundindex("misc/talk.wav"); ent->touch = door_touch; } ent->moveinfo.speed = ent->speed; ent->moveinfo.accel = ent->accel; ent->moveinfo.decel = ent->decel; ent->moveinfo.wait = ent->wait; VectorCopy(ent->pos1, ent->moveinfo.start_origin); VectorCopy(ent->s.angles, ent->moveinfo.start_angles); VectorCopy(ent->pos2, ent->moveinfo.end_origin); VectorCopy(ent->s.angles, ent->moveinfo.end_angles); if (ent->spawnflags & 16) { ent->s.effects |= EF_ANIM_ALL; } if (ent->spawnflags & 64) { ent->s.effects |= EF_ANIM_ALLFAST; } /* to simplify logic elsewhere, make non-teamed doors into a team of one */ if (!ent->team) { ent->teammaster = ent; } gi.linkentity(ent); ent->nextthink = level.time + FRAMETIME; if (ent->health || ent->targetname) { ent->think = Think_CalcMoveSpeed; } else { ent->think = Think_SpawnDoorTrigger; } } /* * QUAKED func_door_rotating (0 .5 .8) ? START_OPEN REVERSE CRUSHER NOMONSTER ANIMATED TOGGLE X_AXIS Y_AXIS * * TOGGLE causes the door to wait in both the start and end states for a trigger event. * START_OPEN the door to moves to its destination when spawned, and operate in reverse. * It is used to temporarily or permanently close off an area when triggered * (not useful for touch or takedamage doors). * NOMONSTER monsters will not trigger this door * * You need to have an origin brush as part of this entity. The center of that brush will be * the point around which it is rotated. It will rotate around the Z axis by default. You can * check either the X_AXIS or Y_AXIS box to change that. * * "distance" is how many degrees the door will be rotated. * "speed" determines how fast the door moves; default value is 100. * * REVERSE will cause the door to rotate in the opposite direction. * * "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet * "angle" determines the opening direction * "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. * "health" if set, door must be shot open * "speed" movement speed (100 default) * "wait" wait before returning (3 default, -1 = never return) * "dmg" damage to inflict when blocked (2 default) * "sounds" * 1) silent * 2) light * 3) medium * 4) heavy */ void SP_func_door_rotating(edict_t *ent) { if (!ent) { return; } VectorClear(ent->s.angles); /* set the axis of rotation */ VectorClear(ent->movedir); if (ent->spawnflags & DOOR_X_AXIS) { ent->movedir[2] = 1.0; } else if (ent->spawnflags & DOOR_Y_AXIS) { ent->movedir[0] = 1.0; } else /* Z_AXIS */ { ent->movedir[1] = 1.0; } /* check for reverse rotation */ if (ent->spawnflags & DOOR_REVERSE) { VectorNegate(ent->movedir, ent->movedir); } if (!st.distance) { gi.dprintf("%s at %s with no distance set\n", ent->classname, vtos(ent->s.origin)); st.distance = 90; } VectorCopy(ent->s.angles, ent->pos1); VectorMA(ent->s.angles, st.distance, ent->movedir, ent->pos2); ent->moveinfo.distance = st.distance; ent->movetype = MOVETYPE_PUSH; ent->solid = SOLID_BSP; gi.setmodel(ent, ent->model); ent->blocked = door_blocked; ent->use = door_use; if (!ent->speed) { ent->speed = 100; } if (!ent->accel) { ent->accel = ent->speed; } if (!ent->decel) { ent->decel = ent->speed; } if (!ent->wait) { ent->wait = 3; } if (!ent->dmg) { ent->dmg = 2; } if (ent->sounds != 1) { ent->moveinfo.sound_start = gi.soundindex("doors/dr1_strt.wav"); ent->moveinfo.sound_middle = gi.soundindex("doors/dr1_mid.wav"); ent->moveinfo.sound_end = gi.soundindex("doors/dr1_end.wav"); } /* if it starts open, switch the positions */ if (ent->spawnflags & DOOR_START_OPEN) { VectorCopy(ent->pos2, ent->s.angles); VectorCopy(ent->pos1, ent->pos2); VectorCopy(ent->s.angles, ent->pos1); VectorNegate(ent->movedir, ent->movedir); } if (ent->health) { ent->takedamage = DAMAGE_YES; ent->die = door_killed; ent->max_health = ent->health; } if (ent->targetname && ent->message) { gi.soundindex("misc/talk.wav"); ent->touch = door_touch; } ent->moveinfo.state = STATE_BOTTOM; ent->moveinfo.speed = ent->speed; ent->moveinfo.accel = ent->accel; ent->moveinfo.decel = ent->decel; ent->moveinfo.wait = ent->wait; VectorCopy(ent->s.origin, ent->moveinfo.start_origin); VectorCopy(ent->pos1, ent->moveinfo.start_angles); VectorCopy(ent->s.origin, ent->moveinfo.end_origin); VectorCopy(ent->pos2, ent->moveinfo.end_angles); if (ent->spawnflags & 16) { ent->s.effects |= EF_ANIM_ALL; } /* to simplify logic elsewhere, make non-teamed doors into a team of one */ if (!ent->team) { ent->teammaster = ent; } gi.linkentity(ent); ent->nextthink = level.time + FRAMETIME; if (ent->health || ent->targetname) { ent->think = Think_CalcMoveSpeed; } else { ent->think = Think_SpawnDoorTrigger; } } /* ==================================================================== */ /* * QUAKED func_water (0 .5 .8) ? START_OPEN * * func_water is a moveable water brush. It must be targeted to operate. * Use a non-water texture at your own risk. * * START_OPEN causes the water to move to its destination when spawned * and operate in reverse. * * "angle" determines the opening direction (up or down only) * "speed" movement speed (25 default) * "wait" wait before returning (-1 default, -1 = TOGGLE) * "lip" lip remaining at end of move (0 default) * "sounds" (yes, these need to be changed) * 0) no sound * 1) water * 2) lava */ void SP_func_water(edict_t *self) { vec3_t abs_movedir; if (!self) { return; } G_SetMovedir(self->s.angles, self->movedir); self->movetype = MOVETYPE_PUSH; self->solid = SOLID_BSP; gi.setmodel(self, self->model); switch (self->sounds) { default: break; case 1: /* water */ 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->inuse) { /* 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->inuse) { /* 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_8_40/src/game/g_items.c000066400000000000000000001353341465112212000174150ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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); static gitem_armor_t jacketarmor_info = {25, 50, .30, .00, ARMOR_JACKET}; static gitem_armor_t combatarmor_info = {50, 100, .60, .30, ARMOR_COMBAT}; static gitem_armor_t bodyarmor_info = {100, 200, .80, .60, ARMOR_BODY}; static int jacket_armor_index; static int combat_armor_index; static 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 == SKILL_MEDIUM) && (quantity >= 2)) || ((skill->value >= SKILL_HARD) && (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); } } 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); } /* activate item instantly if appropriate */ /* moved down here so activation sounds override the pickup sound */ if (deathmatch->value) { if ((((int)dmflags->value & DF_INSTANT_ITEMS) && (ent->item->flags & IT_INSTANT_USE)) || ((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; } if (ent->item->use) { ent->item->use(other, ent->item); } } } } 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 (frandk() > 0.5) { dropped->s.angles[1] += frandk()*45; } else { dropped->s.angles[1] -= frandk()*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 && !(ent->spawnflags & ITEM_NO_TOUCH) && (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); } } /* ====================================================================== */ static const gitem_t gameitemlist[] = { { 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 | IT_INSTANT_USE, 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 | IT_INSTANT_USE, 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 | IT_INSTANT_USE, 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 | IT_INSTANT_USE, 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 | IT_INSTANT_USE, 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} }; gitem_t itemlist[MAX_ITEMS]; /* * 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) { memset(itemlist, 0, sizeof(itemlist)); memcpy(itemlist, gameitemlist, sizeof(gameitemlist)); game.num_items = sizeof(gameitemlist) / sizeof(gameitemlist[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_8_40/src/game/g_main.c000066400000000000000000000222641465112212000172150ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 *coop_pickup_weapons; cvar_t *coop_elevator_delay; 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 *g_footsteps; cvar_t *g_monsterfootsteps; cvar_t *g_fix_triggered; cvar_t *g_commanderbody_nogod; 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; cvar_t *aimfix; cvar_t *g_machinegun_norecoil; cvar_t *g_quick_weap; cvar_t *g_swap_speed; void G_RunFrame(void); /* =================================================================== */ void ShutdownGame(void) { gi.dprintf("==== ShutdownGame ====\n"); gi.FreeTags(TAG_LEVEL); gi.FreeTags(TAG_GAME); } /* * convert function declarations to correct one * (warning like from incompatible pointer type) * little bit better than cast function before set */ static void ReadLevel_f(char *filename) { ReadLevel(filename); } static void WriteLevel_f(char *filename) { WriteLevel(filename); } static void ReadGame_f(char *filename) { ReadGame(filename); } static void WriteGame_f(char *filename, qboolean autosave) { WriteGame(filename, autosave); } static void SpawnEntities_f(char *mapname, char *entities, char *spawnpoint) { SpawnEntities(mapname, entities, spawnpoint); } /* * 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_f; globals.WriteGame = WriteGame_f; globals.ReadGame = ReadGame_f; globals.WriteLevel = WriteLevel_f; globals.ReadLevel = ReadLevel_f; 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(const char *error, ...) { va_list argptr; char text[1024]; va_start(argptr, error); vsnprintf(text, sizeof(text), error, argptr); va_end(argptr); gi.error("%s", text); } void Com_Printf(const char *msg, ...) { va_list argptr; char text[1024]; va_start(argptr, msg); vsnprintf(text, sizeof(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->max_health) { ent->health = ent->max_health; } } debristhisframe = 0; gibsthisframe = 0; } /* * Advances the world by 0.1 seconds */ void G_RunFrame(void) { int i; edict_t *ent; level.framenum++; level.time = level.framenum * FRAMETIME; gibsthisframe = 0; debristhisframe = 0; /* 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_8_40/src/game/g_misc.c000066400000000000000000001465461465112212000172360ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 debristhisframe; int gibsthisframe; 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) { 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 (gibsthisframe >= MAX_GIBS) { return; } gib = G_SpawnOptional(); if (!gib) { return; } gibsthisframe++; 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->svflags = SVF_DEADMONSTER; gib->s.effects |= EF_GIB; gib->flags |= FL_NO_KNOCKBACK; gib->takedamage = DAMAGE_YES; gib->die = gib_die; gib->health = 250; 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; // The entity still has the monsters clipmaks. // Reset it to MASK_SHOT to be on the save side. // (MASK_SHOT is used by xatrix) self->clipmask = MASK_SHOT; 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; // The entity still has the monsters clipmaks. // Reset it to MASK_SHOT to be on the save side. // (MASK_SHOT is used by xatrix) self->clipmask = MASK_SHOT; 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 (debristhisframe >= MAX_DEBRIS) { return; } chunk = G_SpawnOptional(); if (!chunk) { return; } debristhisframe++; 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; chunk->health = 250; 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 */) { /* only squash thing we fall on top of */ if (!self || !other || !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 commander_body_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_BODY, gi.soundindex("tank/pain.wav"), 1, ATTN_NORM, 0); 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; } } 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->s.renderfx |= RF_FRAMELERP; if (g_commanderbody_nogod->value) { self->deadflag = DEAD_DEAD; self->svflags |= SVF_MONSTER | SVF_DEADMONSTER; self->die = commander_body_die; } else { self->flags = FL_GODMODE; } 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_8_40/src/game/g_monster.c000066400000000000000000000455241465112212000177640ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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] = 0; } } 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)) { if (self->s.frame != self->monsterinfo.nextframe) { self->s.frame = self->monsterinfo.nextframe; self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; } self->monsterinfo.nextframe = 0; } else { /* prevent nextframe from leaking into a future move */ self->monsterinfo.nextframe = 0; 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->spawnflags & 2) && !self->targetname) { if (g_fix_triggered->value) { self->spawnflags &= ~2; } gi.dprintf ("triggered %s at %s has no targetname\n", self->classname, vtos (self->s.origin)); } 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; } if (!self->viewheight) { self->viewheight = 25; } if (self->spawnflags & 2) { monster_triggered_start(self); } else { monster_start_go(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; } if (!self->viewheight) { self->viewheight = 25; } if (self->spawnflags & 2) { monster_triggered_start(self); } else { monster_start_go(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; } if (!self->viewheight) { self->viewheight = 10; } if (self->spawnflags & 2) { monster_triggered_start(self); } else { monster_start_go(self); } } void swimmonster_start(edict_t *self) { if (!self) { return; } self->flags |= FL_SWIM; self->think = swimmonster_start_go; monster_start(self); } yquake2-QUAKE2_8_40/src/game/g_phys.c000066400000000000000000000604041465112212000172520ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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) { trace_t trace; int mask; if (!ent) { return NULL; } /* dead bodies are supposed to not be solid so lets ensure they only collide with BSP during pushmoves */ if (ent->clipmask && !(ent->svflags & SVF_DEADMONSTER)) { 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) { 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); /* startsolid treats different-content volumes as continuous, like the bbox of a monster/player and the floor of an elevator. So do another trace that only collides with BSP so that we make a best effort to keep these entities inside non-solid space */ if (trace.startsolid && (mask & ~MASK_SOLID)) { trace = gi.trace (start, ent->mins, ent->maxs, end, ent, MASK_SOLID); } VectorCopy(trace.endpos, ent->s.origin); gi.linkentity(ent); /* Push slightly away from non-horizontal surfaces, prevent origin stuck in the plane which causes the entity to be rendered in full black. */ if (trace.plane.type != 2) { /* Limit the fix to gibs, debris and dead monsters. Everything else may break existing maps. Items may slide to unreachable locations, monsters may get stuck, etc. */ if (((strncmp(ent->classname, "monster_", 8) == 0) && ent->health < 1) || (strcmp(ent->classname, "debris") == 0) || (ent->s.effects & EF_GIB)) { VectorAdd(ent->s.origin, trace.plane.normal, ent->s.origin); } } 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; } pushed_t; static pushed_t pushed[MAX_EDICTS], *pushed_p; static 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); 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); /* 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); 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); /* entities are very often freed during thinking */ if (!ent->inuse) { return; } /* 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) { /* don't play splash sound for entities already in water on level start */ if (level.framenum > 3) { 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; vec3_t oldorig; trace_t tr; 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; } VectorCopy(ent->s.origin, oldorig); SV_FlyMove(ent, FRAMETIME, mask); /* Evil hack to work around dead parasites (and maybe other monster) falling through the worldmodel into the void. We copy the current origin (see above) and after the SV_FlyMove() was performend we checl if we're stuck in the world model. If yes we're undoing the move. */ if (!VectorCompare(ent->s.origin, oldorig)) { tr = gi.trace(ent->s.origin, ent->mins, ent->maxs, ent->s.origin, ent, mask); if (tr.startsolid) { VectorCopy(oldorig, ent->s.origin); } } 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_8_40/src/game/g_spawn.c000066400000000000000000000571511465112212000174240ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_makron(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); static 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_makron", SP_monster_makron}, {"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; if (!mapname || !entities || !spawnpoint) { return; } skill_level = floor(skill->value); if (skill_level < 0) { skill_level = 0; } if (skill_level > 3) { skill_level = 3; } if (skill->value != skill_level) { gi.cvar_forceset("skill", va("%f", skill_level)); } SaveClientData(); gi.FreeTags(TAG_LEVEL); memset(&level, 0, sizeof(level)); memset(g_edicts, 0, game.maxentities * sizeof(g_edicts[0])); Q_strlcpy(level.mapname, mapname, sizeof(level.mapname)); Q_strlcpy(game.spawnpoint, spawnpoint, sizeof(game.spawnpoint)); /* set client fields on player ents */ for (i = 0; i < game.maxclients; i++) { g_edicts[i + 1].client = game.clients + i; } ent = NULL; inhibit = 0; /* parse ents */ while (1) { /* parse the opening brace */ com_token = COM_Parse(&entities); if (!entities) { break; } if (com_token[0] != '{') { gi.error("ED_LoadFromFile: found %s when expecting {", com_token); } if (!ent) { ent = g_edicts; } else { ent = G_Spawn(); } entities = ED_ParseEdict(entities, ent); /* yet another map hack */ if (!Q_stricmp(level.mapname, "command") && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27")) { ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; } /* remove things (except the world) from different skill levels or deathmatch */ if (ent != g_edicts) { if (deathmatch->value) { if (ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH) { G_FreeEdict(ent); inhibit++; continue; } } else { if (((skill->value == SKILL_EASY) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == SKILL_MEDIUM) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (((skill->value == SKILL_HARD) || (skill->value == SKILL_HARDPLUS)) && (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(); } /* =================================================================== */ static 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 " ; static 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_8_40/src/game/g_svcmds.c000066400000000000000000000144521465112212000175700ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; static ipfilter_t ipfilters[MAX_IPFILTERS]; static int numipfilters; static qboolean StringToFilter(char *s, ipfilter_t *f) { char num[128]; int i, j; YQ2_ALIGNAS_TYPE(unsigned) byte b[4]; YQ2_ALIGNAS_TYPE(unsigned) 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; YQ2_ALIGNAS_TYPE(unsigned) 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; YQ2_ALIGNAS_TYPE(unsigned) 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]; YQ2_ALIGNAS_TYPE(unsigned) 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 = Q_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_8_40/src/game/g_target.c000066400000000000000000000566111465112212000175620ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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" #define TARGET_HELP_PRIMARY 1 #define TARGET_HELP_THINK_DELAY 0.3f /* * 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); } /* ========================================================== */ static void Target_Help_Apply(const char *msg, int is_primary) { char *curr; size_t sz; if (!msg) { return; } if (is_primary) { curr = game.helpmessage1; sz = sizeof (game.helpmessage1); } else { curr = game.helpmessage2; sz = sizeof (game.helpmessage2); } if (strcmp(curr, msg) == 0) { return; } Q_strlcpy(curr, msg, sz - 1); game.helpchanged++; } void Target_Help_Think (edict_t *ent) { if (!ent) { return; } Target_Help_Apply(ent->message, ent->spawnflags & TARGET_HELP_PRIMARY); ent->think = NULL; } void Use_Target_Help(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* unused */) { if (!ent) { return; } if (level.time > TARGET_HELP_THINK_DELAY) { Target_Help_Apply(ent->message, ent->spawnflags & TARGET_HELP_PRIMARY); } else { /* The game is still pre-loading so delay the help message a bit, otherwise its changes to game structure will leak past save loads */ ent->think = Target_Help_Think; ent->nextthink = TARGET_HELP_THINK_DELAY; } } /* * 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) { if (!self) { return; } self->activator = activator; if (!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) { 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 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_8_40/src/game/g_trigger.c000066400000000000000000000346531465112212000177410ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/g_turret.c000066400000000000000000000276501465112212000176220ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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, vec3_t point); 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) { 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, point); } 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_8_40/src/game/g_utils.c000066400000000000000000000274561465112212000174410ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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) { 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 (activator && (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; } static vec3_t VEC_UP = {0, -1, 0}; static vec3_t MOVEDIR_UP = {0, 0, 1}; static vec3_t VEC_DOWN = {0, -2, 0}; static 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. */ #define POLICY_DEFAULT 0 #define POLICY_DESPERATE 1 static edict_t * G_FindFreeEdict(int policy) { edict_t *e; for (e = g_edicts + game.maxclients + 1 ; e < &g_edicts[globals.num_edicts] ; e++) { /* the first couple seconds of server time can involve a lot of freeing and allocating, so relax the replacement policy */ if (!e->inuse && (policy == POLICY_DESPERATE || e->freetime < 2.0f || (level.time - e->freetime) > 0.5f)) { G_InitEdict (e); return e; } } return NULL; } edict_t * G_SpawnOptional(void) { edict_t *e = G_FindFreeEdict (POLICY_DEFAULT); if (e) { return e; } if (globals.num_edicts >= game.maxentities) { return G_FindFreeEdict (POLICY_DESPERATE); } e = &g_edicts[globals.num_edicts++]; G_InitEdict (e); return e; } edict_t * G_Spawn(void) { edict_t *e = G_SpawnOptional(); if (!e) gi.error ("ED_Alloc: no free edicts"); 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_8_40/src/game/g_weapon.c000066400000000000000000000612021465112212000175550ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 == SKILL_EASY) { 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) { T_Damage(other, self, self->owner, self->velocity, self->s.origin, (plane) ? plane->normal : 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; /* move it back a bit from walls so the effects aren't cut off */ if (!other->takedamage) { VectorNormalize(self->velocity); VectorMA(self->s.origin, -40.0f, 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.linkentity(self); 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_8_40/src/game/header/000077500000000000000000000000001465112212000170415ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/header/game.h000066400000000000000000000176111465112212000201310ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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, const char *fmt, ...); void (*dprintf)(const char *fmt, ...); void (*cprintf)(edict_t *ent, int printlevel, const char *fmt, ...); void (*centerprintf)(edict_t *ent, const 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); YQ2_ATTR_NORETURN_FUNCPTR void (*error)(const 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)(const char *var_name, char *value, int flags); cvar_t *(*cvar_set)(const char *var_name, char *value); cvar_t *(*cvar_forceset)(const 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_8_40/src/game/header/local.h000066400000000000000000000723141465112212000203130ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_COOP_TAKEN 0x00002000 /* Another client has already taken it */ #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; /* Maximum debris / gibs per frame */ #define MAX_GIBS 20 #define MAX_DEBRIS 20 /* 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 #define IT_INSTANT_USE 64 /* item is insta-used on pickup if dmflag is set */ /* 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 debristhisframe; extern int gibsthisframe; /* 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 /* Easier handling of AI skill levels */ #define SKILL_EASY 0 #define SKILL_MEDIUM 1 #define SKILL_HARD 2 #define SKILL_HARDPLUS 3 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 *coop_pickup_weapons; extern cvar_t *coop_elevator_delay; 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 *g_footsteps; extern cvar_t *g_monsterfootsteps; extern cvar_t *g_fix_triggered; extern cvar_t *g_commanderbody_nogod; 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; extern cvar_t *aimfix; extern cvar_t *g_machinegun_norecoil; extern cvar_t *g_quick_weap; extern cvar_t *g_swap_speed; #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; short save_ver; } field_t; extern field_t fields[]; extern gitem_t itemlist[]; /* player/client.c */ void ClientBegin(edict_t *ent); void ClientDisconnect(edict_t *ent); void ClientUserinfoChanged(edict_t *ent, char *userinfo); qboolean ClientConnect(edict_t *ent, char *userinfo); void ClientThink(edict_t *ent, usercmd_t *cmd); /* g_cmds.c */ void Cmd_Help_f(edict_t *ent); void ClientCommand(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_SpawnOptional(void); 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_spawn.c */ void ED_CallSpawn(edict_t *ent); /* 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); /* savegame */ void InitGame(void); void ReadLevel(const char *filename); void WriteLevel(const char *filename); void ReadGame(const char *filename); void WriteGame(const char *filename, qboolean autosave); void SpawnEntities(const char *mapname, char *entities, const char *spawnpoint); /* ============================================================================ */ /* 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; /* now also used by insane marines to store pain sound timeout */ float last_move_time; int health; int max_health; int gib_health; int deadflag; float 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; /* now also used for player death sound aggregation */ 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; }; /* * Uncomment for check that exported functions declarations are same as in * implementation. (-Wmissing-prototypes ) * */ #if 0 #include "../savegame/savegame.h" #include "../savegame/tables/gamefunc_decs.h" #endif #endif /* GAME_LOCAL_H */ yquake2-QUAKE2_8_40/src/game/monster/000077500000000000000000000000001465112212000173005ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/berserker/000077500000000000000000000000001465112212000212645ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/berserker/berserker.c000066400000000000000000000264321465112212000234230ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; static int sound_step; static int sound_step2; void berserk_footstep(edict_t *self) { if (!g_monsterfootsteps->value) return; // Lazy loading for savegame compatibility. if (sound_step == 0 || sound_step2 == 0) { sound_step = gi.soundindex("berserk/step1.wav"); sound_step2 = gi.soundindex("berserk/step2.wav"); } if (randk() % 2 == 0) { gi.sound(self, CHAN_BODY, sound_step, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_BODY, sound_step2, 1, ATTN_NORM, 0); } } 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); static 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; } static 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->enemy) { 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); } static mframe_t berserk_frames_walk[] = { {ai_walk, 9.1, NULL}, {ai_walk, 6.3, NULL}, {ai_walk, 4.9, NULL}, {ai_walk, 6.7, berserk_footstep}, {ai_walk, 6.0, NULL}, {ai_walk, 8.2, NULL}, {ai_walk, 7.2, NULL}, {ai_walk, 6.1, NULL}, {ai_walk, 4.9, berserk_footstep}, {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; } static mframe_t berserk_frames_run1[] = { {ai_run, 21, NULL}, {ai_run, 11, berserk_footstep}, {ai_run, 21, NULL}, {ai_run, 25, NULL}, {ai_run, 18, berserk_footstep}, {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); } static 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 */ } static mframe_t berserk_frames_attack_club[] = { {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, berserk_footstep}, {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... */ } static mframe_t berserk_frames_attack_strike[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, berserk_footstep}, {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, berserk_footstep}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 9.7, NULL}, {ai_move, 13.6, berserk_footstep} }; 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; } } static 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}; static 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 == SKILL_HARDPLUS) { 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); } static 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 }; static 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; } // Force recaching at next footstep to ensure // that the sound indices are correct. sound_step = 0; sound_step2 = 0; /* 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_8_40/src/game/monster/berserker/berserker.h000066400000000000000000000156411465112212000234300ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/boss2/000077500000000000000000000000001465112212000203305ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/boss2/boss2.c000066400000000000000000000401061465112212000215250ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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); } static 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 }; static 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 }; static 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 }; static 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}; static 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 */ static 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 }; static 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 }; static 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 }; static 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}; static 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 }; static 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_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_8_40/src/game/monster/boss2/boss2.h000066400000000000000000000126441465112212000215400ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/boss3/000077500000000000000000000000001465112212000203315ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/boss3/boss3.c000066400000000000000000000043331465112212000215310ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/boss3/boss31.c000066400000000000000000000433101465112212000216100ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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); static 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; } static 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 }; static 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 }; static 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 }; static 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; } } static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 == SKILL_HARDPLUS) { 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_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/jorg/tris.md2"); self->s.modelindex2 = gi.modelindex("models/monsters/boss3/rider/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_8_40/src/game/monster/boss3/boss31.h000066400000000000000000000132321465112212000216150ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/boss3/boss32.c000066400000000000000000000606371465112212000216240ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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); } } static 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; } static 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); } static 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_walk, 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; } } static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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); } static 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 }; static 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 }; static 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 == SKILL_HARDPLUS) { 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 */) { } 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; } /* detach from the makron if the legs are gone completely */ if (self->owner && (!self->owner->inuse || (self->owner->health <= self->owner->gib_health))) { self->owner = NULL; } /* if the makron is revived the torso was put back on him */ if (self->owner && self->owner->deadflag != DEAD_DEAD) { G_FreeEdict(self); return; } if (++self->s.frame >= FRAME_death320) { self->s.frame = FRAME_death301; } self->nextthink = level.time + FRAMETIME; } static void makron_torso_origin(edict_t *self, edict_t *torso) { vec3_t v; trace_t tr; AngleVectors(self->s.angles, v, NULL, NULL); VectorMA(self->s.origin, -84.0f, v, v); tr = gi.trace(self->s.origin, torso->mins, torso->maxs, v, self, MASK_SOLID); VectorCopy (tr.endpos, torso->s.origin); } void makron_torso_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, int damage /* unused */, vec3_t point /* unused */) { int n; if (self->health > self->gib_health) { return; } gi.sound(self, CHAN_VOICE, gi.soundindex( "misc/udeath.wav"), 1, ATTN_NORM, 0); 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); } G_FreeEdict(self); } void makron_torso(edict_t *self) { edict_t *torso; if (!self) { return; } torso = G_SpawnOptional(); if (!torso) { return; } VectorCopy(self->s.angles, torso->s.angles); VectorSet(torso->mins, -24, -24, 0); VectorSet(torso->maxs, 24, 24, 16); makron_torso_origin(self, torso); torso->gib_health = -800; torso->takedamage = DAMAGE_YES; torso->die = makron_torso_die; torso->deadflag = DEAD_DEAD; torso->owner = self; torso->movetype = MOVETYPE_TOSS; torso->solid = SOLID_BBOX; torso->svflags = SVF_MONSTER|SVF_DEADMONSTER; torso->clipmask = MASK_MONSTERSOLID; torso->s.frame = FRAME_death301; torso->s.modelindex = gi.modelindex("models/monsters/boss3/rider/tris.md2"); torso->think = makron_torso_think; torso->nextthink = level.time + 2 * FRAMETIME; torso->s.sound = gi.soundindex("makron/spine.wav"); gi.linkentity(torso); } void makron_dead(edict_t *self) { if (!self) { return; } VectorSet(self->mins, -48, -48, 0); VectorSet(self->maxs, 48, 48, 24); 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 */) { 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); 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; makron_torso(self); /* lower bbox since the torso is gone */ self->maxs[2] = 64; gi.linkentity (self); 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_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 = NULL; 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 *enemy; edict_t *oldenemy; if (!self) { return; } /* spawning can mess with enemy state so clear it temporarily */ enemy = self->enemy; self->enemy = NULL; oldenemy = self->oldenemy; self->oldenemy = NULL; SP_monster_makron(self); if (self->think) { self->think(self); } /* and re-link enemy state now that he's spawned */ if (enemy && enemy->inuse && enemy->deadflag != DEAD_DEAD) { self->enemy = enemy; } if (oldenemy && oldenemy->inuse && oldenemy->deadflag != DEAD_DEAD) { self->oldenemy = oldenemy; } if (!self->enemy) { self->enemy = self->oldenemy; self->oldenemy = NULL; } enemy = self->enemy; if (enemy) { FoundTarget(self); VectorCopy(self->pos1, self->monsterinfo.last_sighting); } if (enemy && visible(self, enemy)) { VectorSubtract(enemy->s.origin, self->s.origin, vec); self->s.angles[YAW] = vectoyaw(vec); VectorNormalize(vec); } else { AngleVectors(self->s.angles, vec, NULL, NULL); } VectorScale(vec, 400, self->velocity); /* the jump frames are fixed length so best to normalize the up speed */ self->velocity[2] = 200.0f * (sv_gravity->value / 800.0f); self->groundentity = NULL; self->s.origin[2] += 1; gi.linkentity(self); self->pain_debounce_time = level.time + 1; self->monsterinfo.currentmove = &makron_move_sight; } /* * 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->classname = "monster_makron"; ent->nextthink = level.time + 0.8; ent->think = MakronSpawn; ent->target = self->target; VectorCopy(self->s.origin, ent->s.origin); VectorCopy(self->s.angles, ent->s.angles); VectorCopy(self->monsterinfo.last_sighting, ent->pos1); ent->enemy = self->enemy; ent->oldenemy = self->oldenemy; } yquake2-QUAKE2_8_40/src/game/monster/boss3/boss32.h000066400000000000000000000330741465112212000216240ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/brain/000077500000000000000000000000001465112212000203735ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/brain/brain.c000066400000000000000000000374251465112212000216450ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; static int sound_step; static int sound_step2; void brain_footstep(edict_t *self) { if (!g_monsterfootsteps->value) return; // Lazy loading for savegame compatibility. if (sound_step == 0 || sound_step2 == 0) { sound_step = gi.soundindex("brain/step1.wav"); sound_step2 = gi.soundindex("brain/step2.wav"); } if (randk() % 2 == 0) { gi.sound(self, CHAN_BODY, sound_step, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_BODY, sound_step2, 1, ATTN_NORM, 0); } } 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); static 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; } static 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; } static mframe_t brain_frames_walk1[] = { {ai_walk, 7, NULL}, {ai_walk, 2, NULL}, {ai_walk, 3, NULL}, {ai_walk, 3, brain_footstep}, {ai_walk, 1, NULL}, {ai_walk, 0, NULL}, {ai_walk, 0, NULL}, {ai_walk, 9, NULL}, {ai_walk, -4, NULL}, {ai_walk, -1, brain_footstep}, {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; } static 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 }; static 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 }; static 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 }; static mframe_t brain_frames_pain1[] = { {ai_move, -6, NULL}, {ai_move, -2, NULL}, {ai_move, -6, brain_footstep}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 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, brain_footstep}, {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); } static mframe_t brain_frames_duck[] = { {ai_move, 0, NULL}, {ai_move, -2, brain_duck_down}, {ai_move, 17, brain_duck_hold}, {ai_move, -3, brain_footstep}, {ai_move, -1, brain_duck_up}, {ai_move, -5, NULL}, {ai_move, -6, NULL}, {ai_move, -6, brain_footstep} }; 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; FoundTarget(self); } self->monsterinfo.pausetime = level.time + eta + 0.5; self->monsterinfo.currentmove = &brain_move_duck; } static 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 }; static 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); } } static mframe_t brain_frames_attack1[] = { {ai_charge, 8, NULL}, {ai_charge, 3, NULL}, {ai_charge, 5, NULL}, {ai_charge, 0, brain_footstep}, {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, brain_footstep} }; 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 > SKILL_EASY)) { 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; } } static 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; } } static mframe_t brain_frames_run[] = { {ai_run, 9, NULL}, {ai_run, 2, NULL}, {ai_run, 3, NULL}, {ai_run, 3, brain_footstep}, {ai_run, 1, NULL}, {ai_run, 0, NULL}, {ai_run, 0, NULL}, {ai_run, 10, NULL}, {ai_run, -4, NULL}, {ai_run, -1, brain_footstep}, {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 == SKILL_HARDPLUS) { 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; } // Force recaching at next footstep to ensure // that the sound indices are correct. sound_step = 0; sound_step2 = 0; 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_8_40/src/game/monster/brain/brain.h000066400000000000000000000150611465112212000216420ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/chick/000077500000000000000000000000001465112212000203615ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/chick/chick.c000066400000000000000000000435051465112212000216150ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; static int sound_step; static int sound_step2; void chick_footstep(edict_t *self) { if (!g_monsterfootsteps->value) return; // Lazy loading for savegame compatibility. if (sound_step == 0 || sound_step2 == 0) { sound_step = gi.soundindex("bitch/step1.wav"); sound_step2 = gi.soundindex("bitch/step2.wav"); } if (randk() % 2 == 0) { gi.sound(self, CHAN_BODY, sound_step, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_BODY, sound_step2, 1, ATTN_NORM, 0); } } 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); } } static 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; } } static 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; } static 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 }; static mframe_t chick_frames_run[] = { {ai_run, 6, NULL}, {ai_run, 8, chick_footstep}, {ai_run, 13, NULL}, {ai_run, 5, NULL}, {ai_run, 7, NULL}, {ai_run, 4, NULL}, {ai_run, 11, chick_footstep}, {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 }; static mframe_t chick_frames_walk[] = { {ai_walk, 6, NULL}, {ai_walk, 8, chick_footstep}, {ai_walk, 13, NULL}, {ai_walk, 5, NULL}, {ai_walk, 7, NULL}, {ai_walk, 4, NULL}, {ai_walk, 11, chick_footstep}, {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; } } static 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 }; static 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 }; static 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 == SKILL_HARDPLUS) { 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); } static mframe_t chick_frames_death2[] = { {ai_move, -6, NULL}, {ai_move, 0, NULL}, {ai_move, -1, NULL}, {ai_move, -5, chick_footstep}, {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, chick_footstep}, {ai_move, 1, NULL}, {ai_move, 2, NULL}, {ai_move, 0, NULL}, {ai_move, 3, NULL}, {ai_move, 3, NULL}, {ai_move, 1, chick_footstep}, {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 }; static 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; if (randk() % 2 == 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); } static 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; FoundTarget(self); } 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); } static 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, chick_footstep}, {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}; static mframe_t chick_frames_attack1[] = { {ai_charge, 19, ChickRocket}, {ai_charge, -6, NULL}, {ai_charge, -5, chick_footstep}, {ai_charge, -2, NULL}, {ai_charge, -7, chick_footstep}, {ai_charge, 0, NULL}, {ai_charge, 1, NULL}, {ai_charge, 10, ChickReload}, {ai_charge, 4, NULL}, {ai_charge, 5, chick_footstep}, {ai_charge, 6, NULL}, {ai_charge, 6, NULL}, {ai_charge, 4, chick_footstep}, {ai_charge, 3, chick_rerocket} }; mmove_t chick_move_attack1 = { FRAME_attak114, FRAME_attak127, chick_frames_attack1, NULL }; static 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, chick_footstep} }; 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; } static 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 }; static mframe_t chick_frames_end_slash[] = { {ai_charge, -6, NULL}, {ai_charge, -1, NULL}, {ai_charge, -6, NULL}, {ai_charge, 0, chick_footstep} }; 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; } static mframe_t chick_frames_start_slash[] = { {ai_charge, 1, NULL}, {ai_charge, 8, NULL}, {ai_charge, 3, chick_footstep} }; 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; } // Force recaching at next footstep to ensure // that the sound indices are correct. sound_step = 0; sound_step2 = 0; 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_8_40/src/game/monster/chick/chick.h000066400000000000000000000205561465112212000216230ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/flipper/000077500000000000000000000000001465112212000207415ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/flipper/flipper.c000066400000000000000000000252241465112212000225530ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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); static 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; } static 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; } static 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 */ static 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; } static 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; } static 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 }; static 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); } static 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 == SKILL_HARDPLUS) { 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) { vec3_t p; trace_t tr; if (!self) { return; } /* original dead bbox was wrong - and make sure the bbox adjustment stays in solidity */ p[0] = self->s.origin[0]; p[1] = self->s.origin[1]; p[2] = self->s.origin[2] - 8; tr = gi.trace(self->s.origin, self->mins, self->maxs, p, self, self->clipmask); self->mins[2] = tr.endpos[2] - self->s.origin[2]; self->movetype = MOVETYPE_TOSS; self->svflags |= SVF_DEADMONSTER; self->nextthink = 0; gi.linkentity(self); } static 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_8_40/src/game/monster/flipper/flipper.h000066400000000000000000000121241465112212000225530ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/float/000077500000000000000000000000001465112212000204055ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/float/float.c000066400000000000000000000407331465112212000216650ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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); } static 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 }; static 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; } } static 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 }; static 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 }; static 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 }; static 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}; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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); if (range(self, self->enemy) == RANGE_MELEE && infront(self, self->enemy) && visible(self, self->enemy)) { 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 == SKILL_HARDPLUS) { 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_8_40/src/game/monster/float/float.h000066400000000000000000000165571465112212000217010ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/flyer/000077500000000000000000000000001465112212000204215ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/flyer/flyer.c000066400000000000000000000370201465112212000217100ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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); } static 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 }; static 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 }; static 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; } static 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 }; static 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; } static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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); } static 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); } static 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 }; static 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 }; static 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 == SKILL_HARDPLUS) { 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_8_40/src/game/monster/flyer/flyer.h000066400000000000000000000115351465112212000217200ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/gladiator/000077500000000000000000000000001465112212000212465ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/gladiator/gladiator.c000066400000000000000000000265751465112212000233770ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; static int sound_step; static int sound_step2; void gladiator_footstep(edict_t *self) { if (!g_monsterfootsteps->value) return; // Lazy loading for savegame compatibility. if (sound_step == 0 || sound_step2 == 0) { sound_step = gi.soundindex("gladiator/step1.wav"); sound_step2 = gi.soundindex("gladiator/step2.wav"); } if (randk() % 2 == 0) { gi.sound(self, CHAN_BODY, sound_step, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_BODY, sound_step2, 1, ATTN_NORM, 0); } } 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); } static 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; } static mframe_t gladiator_frames_walk[] = { {ai_walk, 15, NULL}, {ai_walk, 7, NULL}, {ai_walk, 6, NULL}, {ai_walk, 5, NULL}, {ai_walk, 2, gladiator_footstep}, {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, gladiator_footstep}, {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; } static mframe_t gladiator_frames_run[] = { {ai_run, 23, NULL}, {ai_run, 14, NULL}, {ai_run, 14, gladiator_footstep}, {ai_run, 21, NULL}, {ai_run, 12, NULL}, {ai_run, 13, gladiator_footstep} }; 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; } 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); } } static 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); } static 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 but not for stand-ground ones since players can abuse it by standing still inside this range */ if (!(self->monsterinfo.aiflags & AI_STAND_GROUND)) { 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; } static 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 }; static 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 == SKILL_HARDPLUS) { 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); } static 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; } // Force recaching at next footstep to ensure // that the sound indices are correct. sound_step = 0; sound_step2 = 0; 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_8_40/src/game/monster/gladiator/gladiator.h000066400000000000000000000061151465112212000233700ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/gunner/000077500000000000000000000000001465112212000205765ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/gunner/gunner.c000066400000000000000000000412111465112212000222370ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; static int sound_step; static int sound_step2; void gunner_footstep(edict_t *self) { if (!g_monsterfootsteps->value) return; // Lazy loading for savegame compatibility. if (sound_step == 0 || sound_step2 == 0) { sound_step = gi.soundindex("gunner/step1.wav"); sound_step2 = gi.soundindex("gunner/step2.wav"); } if (randk() % 2 == 0) { gi.sound(self, CHAN_BODY, sound_step, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_BODY, sound_step2, 1, ATTN_NORM, 0); } } 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); static 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->enemy) { return; } if (self->monsterinfo.aiflags & AI_STAND_GROUND) { return; } if (random() <= 0.05) { self->monsterinfo.currentmove = &gunner_move_fidget; } } static 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; } static mframe_t gunner_frames_walk[] = { {ai_walk, 0, gunner_footstep}, {ai_walk, 3, NULL}, {ai_walk, 4, NULL}, {ai_walk, 5, NULL}, {ai_walk, 7, NULL}, {ai_walk, 2, gunner_footstep}, {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, gunner_footstep} }; 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; } static mframe_t gunner_frames_run[] = { {ai_run, 26, NULL}, {ai_run, 9, gunner_footstep}, {ai_run, 9, NULL}, {ai_run, 9, NULL}, {ai_run, 15, NULL}, {ai_run, 10, gunner_footstep}, {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; } } static mframe_t gunner_frames_runandshoot[] = { {ai_run, 32, NULL}, {ai_run, 15, gunner_footstep}, {ai_run, 10, NULL}, {ai_run, 18, NULL}, {ai_run, 8, gunner_footstep}, {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; } static 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 }; static mframe_t gunner_frames_pain2[] = { {ai_move, -2, NULL}, {ai_move, 11, NULL}, {ai_move, 6, gunner_footstep}, {ai_move, 2, NULL}, {ai_move, -1, NULL}, {ai_move, -7, NULL}, {ai_move, -2, NULL}, {ai_move, -7, gunner_footstep} }; mmove_t gunner_move_pain2 = { FRAME_pain201, FRAME_pain208, gunner_frames_pain2, gunner_run }; static mframe_t gunner_frames_pain1[] = { {ai_move, 2, NULL}, {ai_move, 0, NULL}, {ai_move, -5, gunner_footstep}, {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, gunner_footstep}, {ai_move, 0, NULL}, {ai_move, -2, NULL}, {ai_move, -2, NULL}, {ai_move, 0, gunner_footstep}, {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 == SKILL_HARDPLUS) { 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); } static 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 >= SKILL_HARD) { 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); } static 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; FoundTarget(self); } 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); } static mframe_t gunner_frames_attack_chain[] = { {ai_charge, 0, gunner_opengun}, {ai_charge, 0, gunner_footstep}, {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 }; static 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 }; static 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, gunner_footstep} }; mmove_t gunner_move_endfire_chain = { FRAME_attak224, FRAME_attak230, gunner_frames_endfire_chain, gunner_run }; static 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; } // Force recaching at next footstep to ensure // that the sound indices are correct. sound_step = 0; sound_step2 = 0; 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_8_40/src/game/monster/gunner/gunner.h000066400000000000000000000142431465112212000222510ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/hover/000077500000000000000000000000001465112212000204235ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/hover/hover.c000066400000000000000000000362721465112212000217240ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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); static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static mframe_t hover_frames_land[] = { {ai_move, 0, NULL} }; mmove_t hover_move_land = { FRAME_land01, FRAME_land01, hover_frames_land, NULL }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 == SKILL_HARDPLUS) { 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_8_40/src/game/monster/hover/hover.h000066400000000000000000000142511465112212000217220ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/infantry/000077500000000000000000000000001465112212000211325ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/infantry/infantry.c000066400000000000000000000406331465112212000231360ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; static int sound_step; static int sound_step2; void infantry_footstep(edict_t *self) { if (!g_monsterfootsteps->value) return; // Lazy loading for savegame compatibility. if (sound_step == 0 || sound_step2 == 0) { sound_step = gi.soundindex("infantry/step1.wav"); sound_step2 = gi.soundindex("infantry/step2.wav"); } if (randk() % 2 == 0) { gi.sound(self, CHAN_BODY, sound_step, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_BODY, sound_step2, 1, ATTN_NORM, 0); } } static 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; } static 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, infantry_footstep}, {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, infantry_footstep}, {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); } static mframe_t infantry_frames_walk[] = { {ai_walk, 5, infantry_footstep}, {ai_walk, 4, NULL}, {ai_walk, 4, NULL}, {ai_walk, 5, NULL}, {ai_walk, 4, NULL}, {ai_walk, 5, NULL}, {ai_walk, 6, infantry_footstep}, {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; } static mframe_t infantry_frames_run[] = { {ai_run, 10, NULL}, {ai_run, 20, infantry_footstep}, {ai_run, 5, NULL}, {ai_run, 7, NULL}, {ai_run, 30, NULL}, {ai_run, 35, infantry_footstep}, {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; } } static mframe_t infantry_frames_pain1[] = { {ai_move, -3, NULL}, {ai_move, -2, NULL}, {ai_move, -1, NULL}, {ai_move, -2, NULL}, {ai_move, -1, infantry_footstep}, {ai_move, 1, NULL}, {ai_move, -1, NULL}, {ai_move, 1, NULL}, {ai_move, 6, NULL}, {ai_move, 2, infantry_footstep} }; mmove_t infantry_move_pain1 = { FRAME_pain101, FRAME_pain110, infantry_frames_pain1, infantry_run }; static mframe_t infantry_frames_pain2[] = { {ai_move, -3, NULL}, {ai_move, -3, NULL}, {ai_move, 0, NULL}, {ai_move, -1, NULL}, {ai_move, -2, infantry_footstep}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 2, NULL}, {ai_move, 5, NULL}, {ai_move, 2, infantry_footstep} }; 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) { 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 == SKILL_HARDPLUS) { return; /* no pain anims in nightmare */ } if (randk() % 2 == 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); } static mframe_t infantry_frames_death1[] = { {ai_move, -4, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -1, NULL}, {ai_move, -4, infantry_footstep}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, -1, infantry_footstep}, {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 */ static 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, infantry_footstep}, {ai_move, 1, infantry_footstep}, {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 }; static 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; self->s.skinnum = 1; /* switch to bloody skin */ 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); } static 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, infantry_footstep} }; 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; FoundTarget(self); } 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; } } static 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); } } static mframe_t infantry_frames_attack2[] = { {ai_charge, 3, NULL}, {ai_charge, 6, NULL}, {ai_charge, 0, infantry_swing}, {ai_charge, 8, infantry_footstep}, {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; } // Force recaching at next footstep to ensure // that the sound indices are correct. sound_step = 0; sound_step2 = 0; 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_8_40/src/game/monster/infantry/infantry.h000066400000000000000000000142261465112212000231420ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/insane/000077500000000000000000000000001465112212000205555ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/insane/insane.c000066400000000000000000000472501465112212000222060ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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" #define SPAWNFLAG_CRUSIFIED 8 static int sound_fist; static int sound_shake; static int sound_moan; static int sound_scream[8]; static int sound_step; static int sound_step2; static int sound_step3; static int sound_step4; void insane_footstep(edict_t *self) { if (!g_monsterfootsteps->value) return; // Lazy loading for savegame compatibility. if (sound_step == 0 || sound_step2 == 0 || sound_step3 == 0 || sound_step4 == 0) { sound_step = gi.soundindex("player/step1.wav"); sound_step2 = gi.soundindex("player/step2.wav"); sound_step3 = gi.soundindex("player/step3.wav"); sound_step4 = gi.soundindex("player/step4.wav"); } int i; i = randk() % 4; if (i == 0) { gi.sound(self, CHAN_BODY, sound_step, 0.7, ATTN_NORM, 0); } else if (i == 1) { gi.sound(self, CHAN_BODY, sound_step2, 0.7, ATTN_NORM, 0); } else if (i == 2) { gi.sound(self, CHAN_BODY, sound_step3, 0.7, ATTN_NORM, 0); } else if (i == 3) { gi.sound(self, CHAN_BODY, sound_step4, 0.7, ATTN_NORM, 0); } } 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; } /* suppress screaming so pain sound can play */ if (self->fly_sound_debounce_time > level.time) { return; } gi.sound(self, CHAN_VOICE, sound_moan, 1, ATTN_IDLE, 0); } void insane_scream(edict_t *self) { if (!self) { return; } /* suppress screaming so pain sound can play */ if (self->fly_sound_debounce_time > level.time) { 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); static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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, insane_footstep}, {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, insane_footstep} }; 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 }; static 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, insane_footstep}, /* 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, insane_footstep}, /* 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, insane_footstep}, /* 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, insane_footstep}, /* 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 }; static 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, insane_footstep}, {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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 (frandk() <= 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); /* suppress screaming and moaning for 1 second so pain sound plays */ self->fly_sound_debounce_time = level.time + 1; if (skill->value == SKILL_HARDPLUS) { return; /* no pain anims in nightmare */ } /* Don't go into pain frames if crucified. */ if (self->spawnflags & SPAWNFLAG_CRUSIFIED) { 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 & SPAWNFLAG_CRUSIFIED) /* 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 & SPAWNFLAG_CRUSIFIED) { 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 & SPAWNFLAG_CRUSIFIED) { 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; } // Force recaching at next footstep to ensure // that the sound indices are correct. sound_step = 0; sound_step2 = 0; sound_step3 = 0; sound_step4 = 0; 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 & SPAWNFLAG_CRUSIFIED) /* 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_8_40/src/game/monster/insane/insane.h000066400000000000000000000201311465112212000222000ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/medic/000077500000000000000000000000001465112212000203615ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/medic/medic.c000066400000000000000000000475231465112212000216210ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; static int sound_step; static int sound_step2; void medic_footstep(edict_t *self) { if (!g_monsterfootsteps->value) return; // Lazy loading for savegame compatibility. if (sound_step == 0 || sound_step2 == 0) { sound_step = gi.soundindex("medic/step1.wav"); sound_step2 = gi.soundindex("medic/step2.wav"); } if (randk() % 2 == 0) { gi.sound(self, CHAN_BODY, sound_step, 1, ATTN_NORM, 0); } else { gi.sound(self, CHAN_BODY, sound_step2, 1, ATTN_NORM, 0); } } 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); } static 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; } static mframe_t medic_frames_walk[] = { {ai_walk, 6.2, NULL}, {ai_walk, 18.1, medic_footstep}, {ai_walk, 1, NULL}, {ai_walk, 9, NULL}, {ai_walk, 10, NULL}, {ai_walk, 9, NULL}, {ai_walk, 11, NULL}, {ai_walk, 11.6, medic_footstep}, {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; } static mframe_t medic_frames_run[] = { {ai_run, 18, medic_footstep}, {ai_run, 22.5, NULL}, {ai_run, 25.4, NULL}, {ai_run, 23.4, NULL}, {ai_run, 24, medic_footstep}, {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; } } static 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 }; static mframe_t medic_frames_pain2[] = { {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, medic_footstep}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, NULL}, {ai_move, 0, medic_footstep} }; 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 == SKILL_HARDPLUS) { 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); } static 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); } static 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 (!self || !attacker) { return; } if (random() > 0.25) { return; } if (!self->enemy) { self->enemy = attacker; FoundTarget(self); } self->monsterinfo.currentmove = &medic_move_duck; } static 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; } } } static 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); } 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; } static 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, medic_footstep}, {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, medic_footstep}, {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; } // Force recaching at next footstep to ensure // that the sound indices are correct. sound_step = 0; sound_step2 = 0; 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_8_40/src/game/monster/medic/medic.h000066400000000000000000000153631465112212000216230ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/misc/000077500000000000000000000000001465112212000202335ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/misc/move.c000066400000000000000000000272451465112212000213570ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; } 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; } 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_8_40/src/game/monster/misc/player.h000066400000000000000000000136541465112212000217110ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/mutant/000077500000000000000000000000001465112212000206105ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/mutant/mutant.c000066400000000000000000000371471465112212000223000ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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); } static 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; } } static 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); static mframe_t mutant_frames_walk[] = { {ai_walk, 3, mutant_step}, {ai_walk, 1, NULL}, {ai_walk, 5, mutant_step}, {ai_walk, 10, NULL}, {ai_walk, 13, NULL}, {ai_walk, 10, NULL}, {ai_walk, 0, mutant_step}, {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; } static mframe_t mutant_frames_start_walk[] = { {ai_walk, 5, NULL}, {ai_walk, 5, NULL}, {ai_walk, -2, mutant_step}, {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; } static 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 == SKILL_HARDPLUS) && (random() < 0.5)) || (range(self, self->enemy) == RANGE_MELEE)) { self->monsterinfo.nextframe = FRAME_attack09; } } static 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; } } static 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; } static 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 }; static 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 }; static 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 == SKILL_HARDPLUS) { 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); } static 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 }; static 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_8_40/src/game/monster/mutant/mutant.h000066400000000000000000000113151465112212000222720ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/parasite/000077500000000000000000000000001465112212000211105ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/parasite/parasite.c000066400000000000000000000354531465112212000230760ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_VOICE, sound_sight, 1, ATTN_NORM, 0); } void parasite_tap(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_BODY, sound_tap, 1, ATTN_IDLE, 0); } void parasite_footstep(edict_t *self) { if (g_monsterfootsteps->value) { parasite_tap(self); } } void parasite_scratch(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_BODY, sound_scratch, 1, ATTN_IDLE, 0); } void parasite_search(edict_t *self) { if (!self) { return; } gi.sound(self, CHAN_VOICE, sound_search, 1, ATTN_IDLE, 0); } static 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 }; static 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 }; static 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; } static 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; } static mframe_t parasite_frames_run[] = { {ai_run, 30, NULL}, {ai_run, 30, NULL}, {ai_run, 22, parasite_footstep}, {ai_run, 19, parasite_footstep}, {ai_run, 24, NULL}, {ai_run, 28, parasite_footstep}, {ai_run, 25, NULL} }; mmove_t parasite_move_run = { FRAME_run03, FRAME_run09, parasite_frames_run, NULL }; static 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 }; static mframe_t parasite_frames_stop_run[] = { {ai_run, 20, NULL}, {ai_run, 20, NULL}, {ai_run, 12, parasite_footstep}, {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; } } static mframe_t parasite_frames_walk[] = { {ai_walk, 30, NULL}, {ai_walk, 30, NULL}, {ai_walk, 22, parasite_footstep}, {ai_walk, 19, NULL}, {ai_walk, 24, parasite_footstep}, {ai_walk, 28, parasite_footstep}, {ai_walk, 25, NULL} }; mmove_t parasite_move_walk = { FRAME_run03, FRAME_run09, parasite_frames_walk, parasite_walk }; static 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 }; static mframe_t parasite_frames_stop_walk[] = { {ai_walk, 20, NULL}, {ai_walk, 20, NULL}, {ai_walk, 12, parasite_footstep}, {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; } static 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 == SKILL_HARDPLUS) { 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); } static 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 }; static 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); } static 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->viewheight = 16; 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_8_40/src/game/monster/parasite/parasite.h000066400000000000000000000075231465112212000231000ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/soldier/000077500000000000000000000000001465112212000207415ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/soldier/soldier.c000066400000000000000000000747641465112212000225700ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; static int sound_step; static int sound_step2; static int sound_step3; static int sound_step4; void soldier_footstep(edict_t *self) { if (!g_monsterfootsteps->value) return; // Lazy loading for savegame compatibility. if (sound_step == 0 || sound_step2 == 0 || sound_step3 == 0 || sound_step4 == 0) { sound_step = gi.soundindex("player/step1.wav"); sound_step2 = gi.soundindex("player/step2.wav"); sound_step3 = gi.soundindex("player/step3.wav"); sound_step4 = gi.soundindex("player/step4.wav"); } int i; i = randk() % 4; if (i == 0) { gi.sound(self, CHAN_BODY, sound_step, 1, ATTN_NORM, 0); } else if (i == 1) { gi.sound(self, CHAN_BODY, sound_step2, 1, ATTN_NORM, 0); } else if (i == 2) { gi.sound(self, CHAN_BODY, sound_step3, 1, ATTN_NORM, 0); } else if (i == 3) { gi.sound(self, CHAN_BODY, sound_step4, 1, ATTN_NORM, 0); } } 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); static 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 }; static 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; } } static mframe_t soldier_frames_walk1[] = { {ai_walk, 3, NULL}, {ai_walk, 6, NULL}, {ai_walk, 2, NULL}, {ai_walk, 2, soldier_footstep}, {ai_walk, 2, NULL}, {ai_walk, 1, NULL}, {ai_walk, 6, NULL}, {ai_walk, 5, NULL}, {ai_walk, 3, soldier_footstep}, {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 }; static mframe_t soldier_frames_walk2[] = { {ai_walk, 4, soldier_footstep}, {ai_walk, 4, NULL}, {ai_walk, 9, NULL}, {ai_walk, 8, NULL}, {ai_walk, 5, soldier_footstep}, {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); static 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 }; static mframe_t soldier_frames_run[] = { {ai_run, 10, NULL}, {ai_run, 11, soldier_footstep}, {ai_run, 11, NULL}, {ai_run, 16, NULL}, {ai_run, 10, soldier_footstep}, {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; } } static 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 }; static 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 }; static mframe_t soldier_frames_pain3[] = { {ai_move, -8, NULL}, {ai_move, 10, NULL}, {ai_move, -4, soldier_footstep}, {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, soldier_footstep} }; mmove_t soldier_move_pain3 = { FRAME_pain301, FRAME_pain318, soldier_frames_pain3, soldier_run }; static 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 == SKILL_HARDPLUS) { 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 == SKILL_HARDPLUS) && (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 == SKILL_HARDPLUS) && (random() < 0.5)) || (range(self, self->enemy) == RANGE_MELEE)) { self->monsterinfo.nextframe = FRAME_attak102; } } static 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 == SKILL_HARDPLUS) && (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 == SKILL_HARDPLUS) && (random() < 0.5)) || (range(self, self->enemy) == RANGE_MELEE)) { self->monsterinfo.nextframe = FRAME_attak204; } } static 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; } } static 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); } static mframe_t soldier_frames_attack4[] = { {ai_charge, 0, NULL}, {ai_charge, 0, soldier_footstep}, {ai_charge, 0, soldier_fire4}, {ai_charge, 0, NULL}, {ai_charge, 0, NULL}, {ai_charge, 0, soldier_footstep} }; 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 == SKILL_HARDPLUS) { self->monsterinfo.nextframe = FRAME_runs03; } } static mframe_t soldier_frames_attack6[] = { {ai_charge, 10, NULL}, {ai_charge, 4, NULL}, {ai_charge, 12, soldier_footstep}, {ai_charge, 11, soldier_fire8}, {ai_charge, 13, NULL}, {ai_charge, 18, NULL}, {ai_charge, 15, soldier_footstep}, {ai_charge, 14, NULL}, {ai_charge, 11, NULL}, {ai_charge, 8, soldier_footstep}, {ai_charge, 11, NULL}, {ai_charge, 12, NULL}, {ai_charge, 12, soldier_footstep}, {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 > SKILL_EASY) && (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; } } static 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; FoundTarget(self); } if (skill->value == SKILL_EASY) { self->monsterinfo.currentmove = &soldier_move_duck; return; } self->monsterinfo.pausetime = level.time + eta + 0.3; r = random(); if (skill->value == SKILL_MEDIUM) { if (r > 0.33) { self->monsterinfo.currentmove = &soldier_move_duck; } else { self->monsterinfo.currentmove = &soldier_move_attack3; } return; } if (skill->value >= SKILL_HARD) { 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); } static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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; } // Force recaching at next footstep to ensure // that the sound indices are correct. sound_step = 0; sound_step2 = 0; sound_step3 = 0; sound_step4 = 0; 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; } self->health = 20; self->gib_health = -30; SP_monster_soldier_x(self); sound_pain_light = gi.soundindex("soldier/solpain2.wav"); sound_death_light = gi.soundindex("soldier/soldeth2.wav"); sound_step = gi.soundindex("player/step1.wav"); sound_step2 = gi.soundindex("player/step2.wav"); sound_step3 = gi.soundindex("player/step3.wav"); sound_step4 = gi.soundindex("player/step4.wav"); gi.modelindex("models/objects/laser/tris.md2"); gi.soundindex("misc/lasfly.wav"); gi.soundindex("soldier/solatck2.wav"); self->s.skinnum = 0; } /* * 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; } self->health = 30; self->gib_health = -30; SP_monster_soldier_x(self); sound_pain = gi.soundindex("soldier/solpain1.wav"); sound_death = gi.soundindex("soldier/soldeth1.wav"); sound_step = gi.soundindex("player/step1.wav"); sound_step2 = gi.soundindex("player/step2.wav"); sound_step3 = gi.soundindex("player/step3.wav"); sound_step4 = gi.soundindex("player/step4.wav"); gi.soundindex("soldier/solatck1.wav"); self->s.skinnum = 2; } /* * 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; } self->health = 40; self->gib_health = -30; SP_monster_soldier_x(self); sound_pain_ss = gi.soundindex("soldier/solpain3.wav"); sound_death_ss = gi.soundindex("soldier/soldeth3.wav"); sound_step = gi.soundindex("player/step1.wav"); sound_step2 = gi.soundindex("player/step2.wav"); sound_step3 = gi.soundindex("player/step3.wav"); sound_step4 = gi.soundindex("player/step4.wav"); gi.soundindex("soldier/solatck3.wav"); self->s.skinnum = 4; } yquake2-QUAKE2_8_40/src/game/monster/soldier/soldier.h000066400000000000000000000322621465112212000225600ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/supertank/000077500000000000000000000000001465112212000213145ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/supertank/supertank.c000066400000000000000000000423301465112212000234760ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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); static 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; } static 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 }; static 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; } } static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 }; static 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 >= SKILL_HARD) { if ((self->s.frame >= FRAME_attak2_1) && (self->s.frame <= FRAME_attak2_14)) { return; } } self->pain_debounce_time = level.time + 3; if (skill->value == SKILL_HARDPLUS) { 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_8_40/src/game/monster/supertank/supertank.h000066400000000000000000000171101465112212000235010ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/monster/tank/000077500000000000000000000000001465112212000202355ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/monster/tank/tank.c000066400000000000000000000516371465112212000213520ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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); } static 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); static 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 }; static 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 }; static 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); static 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 }; static 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 }; static 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; } } static 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 }; static 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 }; static 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 >= SKILL_HARD) { 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 == SKILL_HARDPLUS) { 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); } static 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 }; static 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 }; static 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 >= SKILL_HARD) { 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); } static 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 }; static 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 }; static 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 }; static 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 }; static 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 >= SKILL_HARD) { 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); } static 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); 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_8_40/src/game/monster/tank/tank.h000066400000000000000000000210261465112212000213440ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_8_40/src/game/player/000077500000000000000000000000001465112212000171055ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/player/client.c000066400000000000000000001403151465112212000205330ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; } } /* 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(edict_t *self) { /* 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 && 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) { 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 (sound is played at end of server frame) */ self->sounds = gi.soundindex("misc/udeath.wav"); 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; } } /* sound is played at end of server frame */ if (!self->sounds) { self->sounds = gi.soundindex(va("*death%i.wav", (randk() % 4) + 1)); } } } 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, ""); } /* ============================================================== */ static 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_8_40/src/game/player/hud.c000066400000000000000000000320521465112212000200330ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; edict_t *ent; if (!targ) { return; } if (level.intermissiontime) { return; /* already activated */ } game.autosaved = false; /* respawn any dead clients */ for (i = 0; i < maxclients->value; i++) { edict_t *client; 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++) { int n; edict_t *client; client = g_edicts + 1 + i; if (!client->inuse) { continue; } /* strip players of all keys between units */ for (n = 0; n < game.num_items; n++) { if (itemlist[n].flags & IT_KEY) { client->client->pers.inventory[n] = 0; } } client->client->pers.power_cubes = 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++) { edict_t *client; 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; int sorted[MAX_CLIENTS]; int sortedscores[MAX_CLIENTS]; int total; if (!ent) /* killer can be NULL */ { return; } /* sort the clients by score */ total = 0; for (i = 0; i < game.maxclients; i++) { int k, j, score; edict_t *cl_ent; 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++) { char *tag; int x, y, j; gclient_t *cl; edict_t *cl_ent; 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 == SKILL_EASY) { sk = "easy"; } else if (skill->value == SKILL_MEDIUM) { sk = "medium"; } else if (skill->value == SKILL_HARD) { 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 < -99) ? -99 : 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; if (!ent) { return; } for (i = 1; i <= maxclients->value; i++) { gclient_t *cl; 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_8_40/src/game/player/trail.c000066400000000000000000000060051465112212000203650ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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)) static edict_t *trail[TRAIL_LENGTH]; static int trail_head; static 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_8_40/src/game/player/view.c000066400000000000000000000676711465112212000202440ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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; static float xyspeed; static float bobmove; static int bobcycle; /* odd cycles are right foot going forward */ static 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; } /* death/gib sound is now aggregated and played here */ if (player->sounds) { gi.sound (player, CHAN_VOICE, player->sounds, 1, ATTN_NORM, 0); player->sounds = 0; } 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) && player->health > 0) { 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) && !(current_player->flags & FL_GODMODE)) { 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->health <= 0) { return; } if (g_footsteps->value == 1) { if (ent->groundentity && (xyspeed > 225)) { if ((int)(current_client->bobtime + bobmove) != bobcycle) { ent->s.event = EV_FOOTSTEP; } } } else if (g_footsteps->value == 2) { if (ent->groundentity) { if ((int)(current_client->bobtime + bobmove) != bobcycle) { ent->s.event = EV_FOOTSTEP; } } } else if (g_footsteps->value >= 3) { 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_8_40/src/game/player/weapon.c000066400000000000000000001117571465112212000205560ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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" #include #define PLAYER_NOISE_SELF 0 #define PLAYER_NOISE_IMPACT 1 #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(edict_t *ent, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result) { gclient_t *client = ent->client; float *point = ent->s.origin; 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); // Berserker: fix - now the projectile hits exactly where the scope is pointing. if (aimfix->value) { vec3_t start, end; VectorSet(start, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] + ent->viewheight); VectorMA(start, 8192, forward, end); trace_t tr = gi.trace(start, NULL, NULL, end, ent, MASK_SHOT); if (tr.fraction < 1) { VectorSubtract(tr.endpos, result, forward); VectorNormalize(forward); } } } /* * 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. */ static edict_t * PlayerNoise_Spawn(edict_t *who, int type) { edict_t *noise; if (!who) { return NULL; } noise = G_SpawnOptional(); if (!noise) { return NULL; } noise->classname = "player_noise"; noise->spawnflags = type; VectorSet (noise->mins, -8, -8, -8); VectorSet (noise->maxs, 8, 8, 8); noise->owner = who; noise->svflags = SVF_NOCLIENT; return noise; } static void PlayerNoise_Verify(edict_t *who) { edict_t *e; edict_t *n1; edict_t *n2; if (!who) { return; } n1 = who->mynoise; n2 = who->mynoise2; if (n1 && !n1->inuse) { n1 = NULL; } if (n2 && !n2->inuse) { n2 = NULL; } if (n1 && n2) { return; } for (e = g_edicts + 1 + game.maxclients; e < &g_edicts[globals.num_edicts]; e++) { if (!e->inuse || strcmp(e->classname, "player_noise") != 0) { continue; } if (e->owner && e->owner != who) { continue; } e->owner = who; if (!n2 && (e->spawnflags == PLAYER_NOISE_IMPACT || n1)) { n2 = e; } else { n1 = e; } if (n1 && n2) { break; } } if (!n1) { n1 = PlayerNoise_Spawn(who, PLAYER_NOISE_SELF); } if (!n2) { n2 = PlayerNoise_Spawn(who, PLAYER_NOISE_IMPACT); } who->mynoise = n1; who->mynoise2 = n2; } void PlayerNoise(edict_t *who, vec3_t where, int type) { edict_t *noise; if (!who || !who->client) { 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; } PlayerNoise_Verify(who); if ((type == PNOISE_SELF) || (type == PNOISE_WEAPON)) { if (level.framenum <= (level.sound_entity_framenum + 3)) { return; } if (!who->mynoise) { return; } noise = who->mynoise; level.sound_entity = noise; level.sound_entity_framenum = level.framenum; } else { if (level.framenum <= (level.sound2_entity_framenum + 3)) { return; } if (!who->mynoise2) { return; } 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)) && (!coop_pickup_weapons->value || (ent->flags & FL_COOP_TAKEN))) { 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; ent->flags |= FL_COOP_TAKEN; } } } 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]--; } /* * Client (player) animation for changing weapon */ static void Change_Weap_Animation(edict_t *ent) { if (!ent) { return; } 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; } } /* * 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; const unsigned short int change_speed = (g_swap_speed->value > 1)? (g_swap_speed->value < USHRT_MAX)? (unsigned short int)g_swap_speed->value : 1 : 1; 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 - change_speed + 1) { ChangeWeapon(ent); return; } else if ( (FRAME_DEACTIVATE_LAST - FRAME_DEACTIVATE_FIRST) >= (4 * change_speed) ) { unsigned short int remainder = FRAME_DEACTIVATE_LAST - ent->client->ps.gunframe; // "if (remainder == 4)" at change_speed == 1 if ( ( remainder <= (4 * change_speed) ) && ( remainder > (3 * change_speed) ) ) { Change_Weap_Animation(ent); } } ent->client->ps.gunframe += change_speed; return; } if (ent->client->weaponstate == WEAPON_ACTIVATING) { if (ent->client->ps.gunframe >= FRAME_ACTIVATE_LAST - change_speed + 1) { ent->client->weaponstate = WEAPON_READY; ent->client->ps.gunframe = FRAME_IDLE_FIRST; return; } ent->client->ps.gunframe += change_speed; 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 * change_speed) ) { Change_Weap_Animation(ent); } 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; gi.sound(ent, CHAN_ITEM, gi.soundindex( "items/damage3.wav"), 1, ATTN_NORM, 0); } VectorSet(offset, 8, 8, ent->viewheight - 8); AngleVectors(ent->client->v_angle, forward, right, NULL); P_ProjectSource(ent, 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, 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, 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, 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 || g_machinegun_norecoil->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, 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, 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, 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, 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); if (aimfix->value) { AngleVectors(v, 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, offset, forward, right, start); } 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); if (aimfix->value) { AngleVectors(v, 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, offset, forward, right, start); } 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, 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, 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_8_40/src/game/savegame/000077500000000000000000000000001465112212000174015ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/savegame/savegame.c000066400000000000000000000561651465112212000213520ustar00rootroot00000000000000/* * 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 "../../common/header/common.h" // YQ2ARCH #include "../header/local.h" #include "savegame.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-5" #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 /* ========================================================= */ /* * Prototypes for forward * declaration for all game * functions. */ #include "tables/gamefunc_decs.h" /* * List with function pointer * to each of the functions * prototyped above. */ static 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. */ static mmoveList_t mmoveList[] = { #include "tables/gamemmove_list.h" }; /* * Fields to be saved (used in g_spawn.c) */ field_t fields[] = { #include "tables/fields.h" }; /* * Level fields to * be saved */ static field_t levelfields[] = { #include "tables/levelfields.h" }; /* * Client fields to * be saved */ static 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); coop_pickup_weapons = gi.cvar("coop_pickup_weapons", "1", CVAR_ARCHIVE); coop_elevator_delay = gi.cvar("coop_elevator_delay", "1.0", CVAR_ARCHIVE); skill = gi.cvar("skill", "1", CVAR_LATCH); maxentities = gi.cvar("maxentities", "1024", CVAR_LATCH); g_footsteps = gi.cvar("g_footsteps", "1", CVAR_ARCHIVE); g_monsterfootsteps = gi.cvar("g_monsterfootsteps", "0", CVAR_ARCHIVE); g_fix_triggered = gi.cvar ("g_fix_triggered", "0", 0); g_commanderbody_nogod = gi.cvar("g_commanderbody_nogod", "0", CVAR_ARCHIVE); /* 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); /* others */ aimfix = gi.cvar("aimfix", "0", CVAR_ARCHIVE); g_machinegun_norecoil = gi.cvar("g_machinegun_norecoil", "0", CVAR_ARCHIVE); g_quick_weap = gi.cvar("g_quick_weap", "1", CVAR_ARCHIVE); g_swap_speed = gi.cvar("g_swap_speed", "1", 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, short save_ver) { field_t *field; fread(client, sizeof(*client), 1, f); for (field = clientfields; field->name; field++) { if (field->save_ver <= save_ver) { ReadField(f, field, (byte *)client); } } if (save_ver < 3) { InitClientResp(client); } } /* ========================================================= */ /* * Writes the game struct into * a file. This is called whenever * the game goes to a new level or * the user saves the game. The saved * information consists of: * - cross level data * - client states * - help computer info */ void WriteGame(const char *filename, qboolean autosave) { savegameHeader_t sv; FILE *f; int i; if (!autosave) { SaveClientData(); } f = Q_fopen(filename, "wb"); if (!f) { gi.error("Couldn't open %s", filename); } /* Savegame identification */ memset(&sv, 0, sizeof(sv)); Q_strlcpy(sv.ver, SAVEGAMEVER, sizeof(sv.ver) - 1); Q_strlcpy(sv.game, GAMEVERSION, sizeof(sv.game) - 1); Q_strlcpy(sv.os, YQ2OSTYPE, sizeof(sv.os) - 1); Q_strlcpy(sv.arch, YQ2ARCH, sizeof(sv.arch) - 1); fwrite(&sv, sizeof(sv), 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) { savegameHeader_t sv; FILE *f; int i; short save_ver = 0; gi.FreeTags(TAG_GAME); f = Q_fopen(filename, "rb"); if (!f) { gi.error("Couldn't open %s", filename); } /* Sanity checks */ fread(&sv, sizeof(sv), 1, f); static const struct { const char* verstr; int vernum; } version_mappings[] = { {"YQ2-1", 1}, {"YQ2-2", 2}, {"YQ2-3", 3}, {"YQ2-4", 4}, {"YQ2-5", 5}, }; for (i=0; i < sizeof(version_mappings)/sizeof(version_mappings[0]); ++i) { if (strcmp(version_mappings[i].verstr, sv.ver) == 0) { save_ver = version_mappings[i].vernum; break; } } if (save_ver == 0) // not found in mappings table { fclose(f); gi.error("Savegame from an incompatible version.\n"); } if (save_ver == 1) { if (strcmp(sv.game, GAMEVERSION) != 0) { fclose(f); gi.error("Savegame from another game.so.\n"); } else if (strcmp(sv.os, OSTYPE_1) != 0) { fclose(f); gi.error("Savegame from another os.\n"); } #ifdef _WIN32 /* Windows was forced to i386 */ if (strcmp(sv.arch, "i386") != 0) { fclose(f); gi.error("Savegame from another architecture.\n"); } #else if (strcmp(sv.arch, ARCH_1) != 0) { fclose(f); gi.error("Savegame from another architecture.\n"); } #endif } else // all newer savegame versions { if (strcmp(sv.game, GAMEVERSION) != 0) { fclose(f); gi.error("Savegame from another game.so.\n"); } else if (strcmp(sv.os, YQ2OSTYPE) != 0) { fclose(f); gi.error("Savegame from another os.\n"); } else if (strcmp(sv.arch, YQ2ARCH) != 0) { #if defined(_WIN32) && (defined(__i386__) || defined(_M_IX86)) // before savegame version "YQ2-4" (and after version 1), // the official Win32 binaries accidentally had the YQ2ARCH "AMD64" // instead of "i386" set due to a bug in the Makefile. // This quirk allows loading those savegames anyway if (save_ver >= 4 || strcmp(sv.arch, "AMD64") != 0) #endif { fclose(f); gi.error("Savegame from another architecture.\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], save_ver); } 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 = Q_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 = Q_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_8_40/src/game/savegame/savegame.h000066400000000000000000000025221465112212000213430ustar00rootroot00000000000000/* * 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. * */ #ifndef SAVEGAME_LOCAL_H #define SAVEGAME_LOCAL_H /* * 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; typedef struct { char ver[32]; char game[32]; char os[32]; char arch[32]; } savegameHeader_t; #endif /* SAVEGAME_LOCAL_H */ yquake2-QUAKE2_8_40/src/game/savegame/tables/000077500000000000000000000000001465112212000206535ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/game/savegame/tables/clientfields.h000066400000000000000000000024641465112212000234770ustar00rootroot00000000000000/* * 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}, {"resp.coop_respawn.weapon", CLOFS(resp.coop_respawn.weapon), F_ITEM, 0, 3}, {"resp.coop_respawn.lastweapon", CLOFS(resp.coop_respawn.lastweapon), F_ITEM, 0, 3}, {NULL, 0, F_INT, 0} yquake2-QUAKE2_8_40/src/game/savegame/tables/fields.h000066400000000000000000000112641465112212000222760ustar00rootroot00000000000000/* * 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_8_40/src/game/savegame/tables/gamefunc_decs.h000066400000000000000000001730761465112212000236250ustar00rootroot00000000000000/* * 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 , short save_ver ) ; 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 ( const char * s1 , const char * s2 ) ; extern int Q_strncasecmp ( const char * s1 , const char * s2 , int n ) ; extern char *Q_strcasestr ( const char *s1, const char *s2 ) ; 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 ( const 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 ( edict_t * ent , 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 ( edict_t * ent ) ; 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_footstep( 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 void medic_footstep( 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_footstep( 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_footstep( 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_footstep( 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_footstep( 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_footstep( 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_footstep( 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_torso_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_footstep( 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 Target_Help_Think ( edict_t * ent ); 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_die ( edict_t * self , edict_t * inflictor , edict_t * attacker , int damage , vec3_t point ) ; 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 ( const char * msg , ... ) ; extern void Sys_Error ( const 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 ) ; extern void wait_and_change_think(edict_t* ent); yquake2-QUAKE2_8_40/src/game/savegame/tables/gamefunc_list.h000066400000000000000000001337051465112212000236550ustar00rootroot00000000000000/* * 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_footstep", (byte *)soldier_footstep}, {"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_footstep", (byte *)medic_footstep}, {"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_footstep", (byte *)insane_footstep}, {"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_footstep", (byte *)infantry_footstep}, {"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_footstep", (byte *)gunner_footstep}, {"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_footstep", (byte *)gladiator_footstep}, {"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_footstep", (byte *)chick_footstep}, {"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_footstep", (byte *)brain_footstep}, {"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_torso_die", (byte *)makron_torso_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_footstep", (byte *)berserk_footstep}, {"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}, {"Target_Help_Think", (byte *)Target_Help_Think}, {"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_die", (byte *)commander_body_die}, {"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}, {"wait_and_change_think", (byte *)wait_and_change_think}, {0, 0} yquake2-QUAKE2_8_40/src/game/savegame/tables/gamemmove_decs.h000066400000000000000000000257171465112212000240130ustar00rootroot00000000000000/* * 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_8_40/src/game/savegame/tables/gamemmove_list.h000066400000000000000000000331411465112212000240360ustar00rootroot00000000000000/* * 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_8_40/src/game/savegame/tables/levelfields.h000066400000000000000000000024241465112212000233240ustar00rootroot00000000000000/* * 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_8_40/src/server/000077500000000000000000000000001465112212000162065ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/server/header/000077500000000000000000000000001465112212000174365ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/server/header/server.h000066400000000000000000000220001465112212000211070ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 /* MAX_TOKEN_CHARS was 128. YQ2 bumped it to 1024, since we * need to support some very long cvars like gl_nolerp_list. * Keep structs used in savegames at 128, otherwise older * savegames would be broken. */ #define MAX_SAVE_TOKEN_CHARS 128 #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 */ /* 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_SAVE_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 cvar_t *sv_downloadserver; /* Download server. */ 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, qboolean isautosave); 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, const char *fmt, ...); void SV_BroadcastPrintf(int level, const char *fmt, ...); void SV_BroadcastCommand(const 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); 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_8_40/src/server/sv_cmd.c000066400000000000000000000357521465112212000176410ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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, 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)); YQ2_COM_CHECK_OOM(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); } } // it's possible to start a map with the wrong case, e.g. "/map BASE1" // (even though the mapfile is maps/base1.bsp) // however, that will screw up the corresponding savegame (for going back to last map) // which will then be called baseq2/save/bla/BASE1.sav - because when going back to the // map from base2 it will look for .../base1.sav // so try to fix the mapname here // NOTE: does not properly handle all variations like base2$base1 and whatever else we forgot // but so far we haven't run into problems with that anyway char mapPath[MAX_QPATH]; { qboolean haveStar = (map[0] == '*'); snprintf(mapPath, sizeof(mapPath), "maps/%s.bsp", haveStar ? map+1 : map); fileHandle_t f = -1; if(FS_FOpenFile(mapPath, &f, false) >= 0) { const char* realMapPath = FS_GetFilenameForHandle(f); // now mapPath contains the fixed path Q_strlcpy(mapPath, realMapPath, sizeof(mapPath)); FS_FCloseFile(f); map = mapPath + 4; // skip "maps" if(haveStar) map[0] = '*'; // restore it (=> replace '/' by '*') else ++map; // skip '/' map[strlen(map)-4] = '\0'; // cut off ".bsp" } } /* start up the next map */ SV_Map(false, map, false, false); /* archive server state */ Q_strlcpy(svs.mapcmd, map, 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(); } /* * Lists available maps for user to load. */ void SV_ListMaps_f(void) { char **userMapNames; int nUserMaps = 0; int i; char* mapName; Com_Printf("\n"); if ((userMapNames = FS_ListFiles2("maps/*.bsp", &nUserMaps, 0, 0)) != NULL) { for (i = 0; i < nUserMaps - 1; i++) { if (strrchr(userMapNames[i], '/')) { mapName = strrchr(userMapNames[i], '/') + 1; } else { mapName = userMapNames[i]; } mapName = strtok(mapName, "."); Com_Printf("%s\n", mapName); } FS_FreeList(userMapNames, nUserMaps); } } /* * 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 = Q_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_Gamemode_f(void) { int none; char *arg; if (Cmd_Argc() != 2) { Com_Printf("gamemode dm, coop or sp\ngamemode ? to print current info\n"); return; } arg = Cmd_Argv(1); if (*arg == '?') { none = 1; if (Cvar_VariableValue("deathmatch")) { Com_Printf("dm "); none = 0; } if (Cvar_VariableValue("coop")) { Com_Printf("coop "); none = 0; } if (Cvar_VariableValue("singleplayer")) { Com_Printf("sp "); none = 0; } if (none) { Com_Printf("none\n"); } else { Com_Printf("\n"); } return; } if (strcmp(arg, "dm") == 0) { Cvar_Set("deathmatch", "1"); Cvar_Set("coop", "0"); Cvar_Set("singleplayer", "0"); } else if (strcmp(arg, "coop") == 0) { Cvar_Set("deathmatch", "0"); Cvar_Set("coop", "1"); Cvar_Set("singleplayer", "0"); } else if (strcmp(arg, "sp") == 0) { Cvar_Set("deathmatch", "0"); Cvar_Set("coop", "0"); Cvar_Set("singleplayer", "1"); } else { Com_Printf("unknown gamemode: '%s'\nspecify dm, coop or sp\n", arg); } } void SV_InitOperatorCommands(void) { Cmd_AddCommand("gamemode", SV_Gamemode_f); 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("listmaps", SV_ListMaps_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_8_40/src/server/sv_conless.c000066400000000000000000000217711465112212000205400ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 p=34", 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 protocol version 34.\n"); 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 */ if (sv_downloadserver->string[0]) { Netchan_OutOfBandPrint(NS_SERVER, adr, "client_connect dlserver=%s", sv_downloadserver->string); } else { 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_8_40/src/server/sv_entities.c000066400000000000000000000361541465112212000207170ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 #include "header/server.h" // DG: is casted to int32_t* in SV_FatPVS() so align accordingly static YQ2_ALIGNAS_TYPE(int32_t) 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) || /* added so weapon angle/offset update during pauseframes */ (ps->gunoffset[0] != ops->gunoffset[0]) || (ps->gunoffset[1] != ops->gunoffset[1]) || (ps->gunoffset[2] != ops->gunoffset[2]) || (ps->gunangles[0] != ops->gunangles[0]) || (ps->gunangles[1] != ops->gunangles[1]) || (ps->gunangles[2] != ops->gunangles[2])) { 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; // DG: used to be called "longs" and long was used which isn't really correct on 64bit int32_t numInt32s; 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"); } numInt32s = (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]), numInt32s << 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 < numInt32s; j++) { ((int32_t *)fatpvs)[j] |= ((int32_t *)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; 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; 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; } } 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_8_40/src/server/sv_game.c000066400000000000000000000212131465112212000177720ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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(const char *fmt, ...) { char msg[1024]; va_list argptr; va_start(argptr, fmt); vsnprintf(msg, sizeof(msg), fmt, argptr); va_end(argptr); Com_Printf("%s", msg); } /* * Print to a single client */ void PF_cprintf(edict_t *ent, int level, const 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); vsnprintf(msg, sizeof(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, const 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); vsnprintf(msg, sizeof(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 */ YQ2_ATTR_NORETURN_FUNCPTR void PF_error(const char *fmt, ...) { char msg[1024]; va_list argptr; va_start(argptr, fmt); vsnprintf(msg, sizeof(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); // cluster -1 means "not in a visible leaf" or something like that (void?) // so p1 and p2 probably don't "see" each other. // either way, we must avoid using a negative index into mask[]! if (cluster < 0 || (!(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); // cluster -1 means "not in a visible leaf" or something like that (void?) // so p1 and p2 probably don't "hear" each other. // either way, we must avoid using a negative index into mask[]! if (cluster < 0 || (!(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_8_40/src/server/sv_init.c000066400000000000000000000301661465112212000200330ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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" #define GAMEMODE_SP 0 #define GAMEMODE_COOP 1 #define GAMEMODE_DM 2 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(qboolean isautosave) { 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 = Q_fopen(name, "rb"); if (!f) { return; /* no savegame */ } fclose(f); SV_ClearWorld(); /* get configstrings and areaportals */ SV_ReadLevelFile(); if (!sv.loadgame || (sv.loadgame && isautosave)) { /* 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, qboolean isautosave) { 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(isautosave); /* set serverinfo variable */ Cvar_FullSet("mapname", sv.name, CVAR_SERVERINFO | CVAR_NOSET); Com_Printf("------------------------------------\n\n"); } /* * A brand new game has been started */ static void SV_ClearGamemodeCvar(char *name, char *msg, int flags) { Cvar_FullSet(name, "0", flags); strcat(msg, name); strcat(msg, " "); } static int SV_ChooseGamemode(void) { char msg[32], *choice; int gamemode; *msg = 0; if (Cvar_VariableValue("deathmatch")) { if (Cvar_VariableValue("coop")) { SV_ClearGamemodeCvar("coop", msg, CVAR_SERVERINFO | CVAR_LATCH); } if (Cvar_VariableValue("singleplayer")) { SV_ClearGamemodeCvar("singleplayer", msg, 0); } choice = "deathmatch"; gamemode = GAMEMODE_DM; } else if (Cvar_VariableValue("coop")) { if (Cvar_VariableValue("singleplayer")) { SV_ClearGamemodeCvar("singleplayer", msg, 0); } choice = "coop"; gamemode = GAMEMODE_COOP; } else { if (dedicated->value && !Cvar_VariableValue("singleplayer")) { Cvar_FullSet("deathmatch", "1", CVAR_SERVERINFO | CVAR_LATCH); choice = "deathmatch"; gamemode = GAMEMODE_DM; } else { Cvar_FullSet("singleplayer", "1", CVAR_SERVERINFO | CVAR_LATCH); choice = "singleplayer"; gamemode = GAMEMODE_SP; } } if (*msg) { Com_Printf("Gamemode ambiguity: Chose: %s, ignored: %s\n", choice, msg); } return gamemode; } void SV_InitGame(void) { int i, gamemode; 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; gamemode = SV_ChooseGamemode(); /* init clients */ if (gamemode == GAMEMODE_DM) { 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 (gamemode == GAMEMODE_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 */ if (dedicated->value) { if (gamemode == GAMEMODE_SP) { NET_Config(true); } else { NET_Config((maxclients->value > 1)); } } else { 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, qboolean isautosave) { 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 { // use next demo command if list of map commands as empty Cvar_Set("nextserver", (char*)Cvar_VariableString("nextdemo")); // and cleanup nextdemo Cvar_Set("nextdemo", ""); } /* 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, isautosave); } 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, isautosave); } 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, isautosave); } else { #ifndef DEDICATED_ONLY SCR_BeginLoadingPlaque(); /* for local system */ #endif SV_BroadcastCommand("changing\n"); SV_SendClientMessages(); SV_SpawnServer(level, spawnpoint, ss_game, attractloop, loadgame, isautosave); Cbuf_CopyToDefer(); } SV_BroadcastCommand("reconnect\n"); } yquake2-QUAKE2_8_40/src/server/sv_main.c000066400000000000000000000367101465112212000200150ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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 */ cvar_t *sv_entfile; /* External entity files. */ cvar_t *sv_downloadserver; /* Download server. */ 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 usec) { #ifndef DEDICATED_ONLY time_before_game = time_after_game = 0; #endif /* if server is not active, do nothing */ if (!svs.initialized) { return; } svs.realtime += usec / 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; } } /* * 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("singleplayer", "0", CVAR_SERVERINFO | CVAR_LATCH); Cvar_Get("deathmatch", "0", CVAR_SERVERINFO | CVAR_LATCH); Cvar_Get("coop", "0", CVAR_SERVERINFO | 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_downloadserver = Cvar_Get ("sv_downloadserver", "", 0); sv_noreload = Cvar_Get("sv_noreload", "0", 0); sv_airaccelerate = Cvar_Get("sv_airaccelerate", "0", CVAR_LATCH); public_server = Cvar_Get("public", "0", 0); sv_entfile = Cvar_Get("sv_entfile", "1", CVAR_ARCHIVE); 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 */ /* DG: we can't just use the maxclients cvar here for the number of clients, * because this is called by SV_Shutdown() and the shut down server might have * a different number of clients (e.g. 1 if it's single player), when maxclients * has already been set to a higher value for multiplayer (e.g. 4 for coop) * Luckily, svs.num_client_entities = maxclients->value * UPDATE_BACKUP * 64; * with the maxclients value from when the current server was started (see SV_InitGame()) * so we can just calculate the right number of clients from that */ int numClients = svs.num_client_entities / ( UPDATE_BACKUP * 64 ); for (i = 0, cl = svs.clients; i < numClients; i++, cl++) { if (cl->state >= cs_connected) { Netchan_Transmit(&cl->netchan, net_message.cursize, net_message.data); } } for (i = 0, cl = svs.clients; i < numClients; 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_8_40/src/server/sv_save.c000066400000000000000000000252321465112212000200240ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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); Sys_Remove(name); Com_sprintf(name, sizeof(name), "%s/save/%s/game.ssv", FS_Gamedir(), savename); Sys_Remove(name); Com_sprintf(name, sizeof(name), "%s/save/%s/*.sav", FS_Gamedir(), savename); s = Sys_FindFirst(name, 0, 0); while (s) { Sys_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) { Sys_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 = Q_fopen(src, "rb"); if (!f1) { return; } f2 = Q_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]; char workdir[MAX_OSPATH]; FILE *f; Com_DPrintf("SV_WriteLevelFile()\n"); Com_sprintf(name, sizeof(name), "%s/save/current/%s.sv2", FS_Gamedir(), sv.name); f = Q_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", FS_Gamedir()); Sys_GetWorkDir(workdir, sizeof(workdir)); Sys_Mkdir(name); if (!Sys_SetWorkDir(name)) { Com_Printf("Couldn't change to %s\n", name); Sys_SetWorkDir(workdir); return; } Com_sprintf(name, sizeof(name), "%s.sav", sv.name); ge->WriteLevel(name); Sys_SetWorkDir(workdir); } void SV_ReadLevelFile(void) { char name[MAX_OSPATH]; char workdir[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", FS_Gamedir()); Sys_GetWorkDir(workdir, sizeof(workdir)); if (!Sys_SetWorkDir(name)) { Com_Printf("Couldn't change to %s\n", name); Sys_SetWorkDir(workdir); return; } Com_sprintf(name, sizeof(name), "%s.sav", sv.name); ge->ReadLevel(name); Sys_SetWorkDir(workdir); } void SV_WriteServerFile(qboolean autosave) { FILE *f; cvar_t *var; char name[MAX_OSPATH], string[128]; char workdir[MAX_OSPATH]; 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 = Q_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", FS_Gamedir()); Sys_GetWorkDir(workdir, sizeof(workdir)); Sys_Mkdir(name); if (!Sys_SetWorkDir(name)) { Com_Printf("Couldn't change to %s\n", name); Sys_SetWorkDir(workdir); return; } ge->WriteGame("game.ssv", autosave); Sys_SetWorkDir(workdir); } void SV_ReadServerFile(void) { fileHandle_t f; char name[MAX_OSPATH], string[128]; char workdir[MAX_OSPATH]; char comment[32]; char mapcmd[MAX_SAVE_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", FS_Gamedir()); Sys_GetWorkDir(workdir, sizeof(workdir)); if (!Sys_SetWorkDir(name)) { Com_Printf("Couldn't change to %s\n", name); Sys_SetWorkDir(workdir); return; } ge->ReadGame("game.ssv"); /* While loading a savegame the global edict arrays is free()ed and newly malloc()ed to reset all entity states. When the game puts the first client into the server it sends it's entity state to us, so as long as there's only one client (the game is running in single player mode) everything's okay. But when there're more clients (the game is running in coop mode) the entity states if all clients >1 are dangeling. hack around that by reconnecting them here. */ cvar_t *coop = Cvar_Get("coop", "0", CVAR_LATCH); if (coop->value) { for (int i = 0; i < maxclients->value; i++) { edict_t *ent = EDICT_NUM(i + 1); svs.clients[i].edict = ent; } } Sys_SetWorkDir(workdir); } void SV_Loadgame_f(void) { char name[MAX_OSPATH]; FILE *f; char *dir; qboolean isautosave; 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 = Q_fopen(name, "rb"); if (!f) { Com_Printf("No such savegame: %s\n", name); return; } Com_Printf("Savegame: %s\n", Cmd_Argv(1)); if (strcmp(Cmd_Argv(1), "save0") == 0 || strcmp(Cmd_Argv(1), "current") == 0) { isautosave = true; } else { isautosave = false; } 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, isautosave); } 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_8_40/src/server/sv_send.c000066400000000000000000000311041465112212000200120ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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, const char *fmt, ...) { va_list argptr; char string[1024]; va_start(argptr, fmt); vsnprintf(string, sizeof(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, const char *fmt, ...) { va_list argptr; char string[2048]; client_t *cl; int i; va_start(argptr, fmt); vsnprintf(string, sizeof(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 (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(const char *fmt, ...) { va_list argptr; char string[1024]; if (!sv.state) { return; } va_start(argptr, fmt); vsnprintf(string, sizeof(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) { vec3_t origin; VectorCopy(client->edict->s.origin, origin); qboolean wereConnected = false; for(int i=0; i<2; ++i) { leafnum = CM_PointLeafnum(origin); cluster = CM_LeafCluster(leafnum); area2 = CM_LeafArea(leafnum); // cluster can be -1 if we're in the void (or sometimes just at a wall) // and using a negative index into mask[] would be invalid if (cluster >= 0 && CM_AreasConnected(area1, area2) && (mask[cluster >> 3] & (1 << (cluster & 7)))) { wereConnected = true; break; } // if the client is currently *not* in water, do *not* do a second check if((CM_PointContents(origin, 0) & MASK_WATER) == 0) { break; // wereConnected remains false } // if the client is half-submerged in opaque water so its origin // is below the water, but the head/camera is still above the water // and thus should be able to see/hear explosions or similar // that are above the water. // so try again at a slightly higher position origin[2] += 32.0f; // FIXME: OTOH, we have a similar problem if we're over water and shoot under water (near water level) => can't see explosion } if (!wereConnected) { continue; // don't send message to this client, continue with next client } } 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_8_40/src/server/sv_user.c000066400000000000000000000341141465112212000200430ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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_protected_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_protected_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_8_40/src/server/sv_world.c000066400000000000000000000325561465112212000202240ustar00rootroot00000000000000/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public 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) { /* decide which clipping hull to use, based on the size */ if (ent->solid == SOLID_BSP) { cmodel_t *model; /* 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; } } } } 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_8_40/src/win-wrapper/000077500000000000000000000000001465112212000171535ustar00rootroot00000000000000yquake2-QUAKE2_8_40/src/win-wrapper/wrapper.c000066400000000000000000000101251465112212000207760ustar00rootroot00000000000000/* * Just a trivial stupid wrapper quake2.exe that starts yquake2.exe. * It calls it with the whole path (assuming it's in same directory as this wrapper) * and passes the commandline * * This should allow us to rename the real executable to yquake2.exe (to hopefully * avoid trouble with whatever stupid thing interferes with mouse input once * console has been opened if the games executable is called quake2.exe) * while avoiding confusion for people upgrading their yq2 installation who still have * shortcuts (possibly in Steam) to quake2.exe that would otherwise launch an old version. * * Can be built with just * $ gcc -Wall -o quake2.exe wrapper.c * in our mingw build environment, will only depend on kernel32.dll and MSVCRT.DLL then, * which should be available on every Windows installation. * * (C) 2017 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. * * So you can do whatever you want with this code, including copying it * (or parts of it) into your own source. */ #include #include #include static const WCHAR WRAPPED_EXE[] = L"yquake2.exe"; // the struct and OnGetWindowByProcess taken from https://stackoverflow.com/a/17166455 typedef struct { DWORD pid; HWND hwnd; } WINDOWPROCESSINFO; static BOOL CALLBACK OnGetWindowByProcess(HWND hwnd, LPARAM lParam) { WINDOWPROCESSINFO *infoPtr = (WINDOWPROCESSINFO *)lParam; DWORD check = 0; BOOL br = TRUE; GetWindowThreadProcessId(hwnd, &check); if (check == infoPtr->pid) { infoPtr->hwnd = hwnd; br = FALSE; } return br; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WCHAR* cmdLine = GetCommandLineW(); WCHAR exePath[2048]; WCHAR* lastBackSlash = exePath; int maxLenSafe = sizeof(exePath)/sizeof(WCHAR) - wcslen(WRAPPED_EXE); // get full path to this executable.. DWORD len = GetModuleFileNameW(NULL, exePath, maxLenSafe); if(len <= 0 || len == maxLenSafe) { // an error occured, clear exe path exePath[0] = 0; } else { // .. cut off executable name (after last backslash in path) lastBackSlash = wcsrchr(exePath, L'\\'); if(lastBackSlash != NULL) { lastBackSlash[1] = 0; } else { // if there was no backslash, fall back to using only the wrapped exe name // (appended to empty exePath buffer) lastBackSlash = exePath; lastBackSlash[0] = 0; } } // .. append wrapped executable name to path .. // should be safe, because maxLenSafe subtracted WRAPPED_EXE's length // (and that's very conservative, we removed the original .exe name after all) wcscat(lastBackSlash, WRAPPED_EXE); // .. and start the wrapped executable { STARTUPINFOW si = {0}; PROCESS_INFORMATION pi = {0}; BOOL ret = FALSE; si.cb = sizeof(si); ret = CreateProcessW(exePath, cmdLine, NULL, // process security attributes NULL, // thread security attributes FALSE, // don't inherit handles 0, // no creation flag NULL, // environment block - no changes, use ours NULL, // don't change CWD &si, &pi); if(!ret) { fprintf(stderr, "Couldn't CreateProcess() (%ld).\n", GetLastError()); return 1; } Sleep(1000); // wait until yq2 should have created the window // send window to foreground, as shown in https://stackoverflow.com/a/17166455 // first we need to get its HWND WINDOWPROCESSINFO info; info.pid = GetProcessId(pi.hProcess); info.hwnd = 0; AllowSetForegroundWindow(info.pid); EnumWindows(OnGetWindowByProcess, (LPARAM)&info); if (info.hwnd != 0) { SetForegroundWindow(info.hwnd); SetActiveWindow(info.hwnd); } // wait for wrapped exe to exit WaitForSingleObject(pi.hProcess, INFINITE); // close the process and thread object handles CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } return 0; } yquake2-QUAKE2_8_40/stuff/000077500000000000000000000000001465112212000152405ustar00rootroot00000000000000yquake2-QUAKE2_8_40/stuff/cdripper.sh000077500000000000000000000010161465112212000174050ustar00rootroot00000000000000#!/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_8_40/stuff/cmake/000077500000000000000000000000001465112212000163205ustar00rootroot00000000000000yquake2-QUAKE2_8_40/stuff/cmake/modules/000077500000000000000000000000001465112212000177705ustar00rootroot00000000000000yquake2-QUAKE2_8_40/stuff/cmake/modules/FindSDL2.cmake000066400000000000000000000142671465112212000223110ustar00rootroot00000000000000 # 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.) # message("") SET(SDL2_SEARCH_PATHS ~/Library/Frameworks /Library/Frameworks /usr/local /usr /sw # Fink /opt/homebrew # Homebrew /opt/local # DarwinPorts /opt/csw # Blastwave /opt /boot/system/develop ${SDL2_PATH} ) FIND_PATH(SDL2_INCLUDE_DIR SDL.h HINTS $ENV{SDL2DIR} PATH_SUFFIXES include/SDL2 include headers/SDL2 PATHS ${SDL2_SEARCH_PATHS} ) if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(PATH_SUFFIXES lib64 lib/x64 lib) else() set(PATH_SUFFIXES lib/x86 lib) endif() FIND_LIBRARY(SDL2_LIBRARY_TEMP NAMES SDL2 HINTS $ENV{SDL2DIR} PATH_SUFFIXES ${PATH_SUFFIXES} 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 ${PATH_SUFFIXES} 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 link flag, -mwindows # It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -mwindows IF(MINGW) SET(MINGW32_LIBRARY mingw32 "-mwindows" 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) # message("") INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) yquake2-QUAKE2_8_40/stuff/mapfixes/000077500000000000000000000000001465112212000170545ustar00rootroot00000000000000yquake2-QUAKE2_8_40/stuff/mapfixes/baseq2/000077500000000000000000000000001465112212000202315ustar00rootroot00000000000000yquake2-QUAKE2_8_40/stuff/mapfixes/baseq2/base2.ent000066400000000000000000001500131465112212000217350ustar00rootroot00000000000000// FIXED ENTITY STRING (by BjossiAlfreds) // // 1. Fixed unused target_explosive (222) // // This explosion is by the ceiling fan near the start of the level. { "nextmap" "base3" "classname" "worldspawn" "message" "Installation" "sky" "unit1_" "sounds" "7" } { "origin" "112 -800 8" "targetname" "base3b" "classname" "info_player_coop" "angle" "0" } { "origin" "112 -752 8" "targetname" "base3b" "classname" "info_player_coop" "angle" "0" } { "origin" "112 -852 8" "targetname" "base3b" "angle" "0" "classname" "info_player_coop" } { "origin" "828 2232 -224" "targetname" "base1" "classname" "info_player_coop" "angle" "180" } { "origin" "876 2232 -224" "targetname" "base1" "classname" "info_player_coop" "angle" "180" } { "origin" "856 2356 -224" "targetname" "base1" "angle" "180" "classname" "info_player_coop" } { "origin" "804 -1768 44" "light" "80" "classname" "light" } { "origin" "544 -1888 24" "spawnflags" "769" "angle" "90" "classname" "monster_gunner" } { "origin" "336 -1792 24" "targetname" "t88" "angle" "90" "spawnflags" "1" "classname" "monster_gunner" } { "origin" "96 -96 8" "spawnflags" "1" "angle" "180" "classname" "monster_infantry" } { "origin" "-616 320 -128" "spawnflags" "769" "angle" "225" "classname" "monster_soldier_light" } { "origin" "424 1688 24" "spawnflags" "769" "angle" "90" "classname" "monster_soldier_light" } { "origin" "808 1792 32" "spawnflags" "769" "angle" "180" "classname" "monster_soldier_light" } { "origin" "32 1568 -168" "item" "ammo_bullets" "spawnflags" "769" "angle" "270" "classname" "monster_infantry" } { "origin" "480 1632 -168" "spawnflags" "769" "angle" "180" "classname" "monster_infantry" } { "origin" "-112 2144 -160" "spawnflags" "768" "angle" "90" "classname" "monster_soldier_light" } { "origin" "800 2528 32" "spawnflags" "768" "angle" "180" "classname" "monster_soldier_light" } { "light" "80" "classname" "light" "origin" "296 1192 -104" } { "classname" "light" "light" "80" "origin" "400 1184 -104" } { "classname" "func_timer" "wait" "90" "targetname" "t107" "target" "t108" "origin" "480 1384 64" "spawnflags" "2048" } { "model" "*1" "classname" "trigger_once" "spawnflags" "2048" "target" "t107" } { "classname" "target_speaker" "noise" "world/v_bas6.wav" "spawnflags" "2048" "attenuation" "-1" "origin" "504 1416 72" "targetname" "t108" } { "model" "*2" "target" "t106" "spawnflags" "2048" "classname" "trigger_once" } { "targetname" "t106" "origin" "-80 400 96" "target" "t105" "wait" "90" "classname" "func_timer" } { "origin" "-120 424 96" "delay" ".5" "targetname" "t102" "target" "t103" "classname" "trigger_relay" "spawnflags" "2048" } { "origin" "-120 448 96" "delay" ".3" "targetname" "t103" "target" "t104" "classname" "trigger_relay" "spawnflags" "2048" } { "origin" "-120 400 96" "targetname" "t105" "target" "t102" "spawnflags" "2048" "classname" "trigger_relay" } { "targetname" "t103" "spawnflags" "2048" "origin" "-144 424 96" "noise" "world/flyby1.wav" "attenuation" "-1" "classname" "target_speaker" } { "targetname" "t102" "spawnflags" "2048" "origin" "-144 400 96" "noise" "world/flyby1.wav" "attenuation" "-1" "classname" "target_speaker" } { "targetname" "t104" "spawnflags" "2048" "classname" "target_speaker" "attenuation" "-1" "noise" "world/flyby1.wav" "origin" "-144 448 96" } { "origin" "88 1896 -128" "targetname" "t101" "noise" "world/v_bas1.wav" "spawnflags" "2048" "attenuation" "-1" "classname" "target_speaker" } { "model" "*3" "target" "t101" "spawnflags" "2048" "classname" "trigger_once" } { "origin" "-80 1768 -120" "classname" "target_explosion" "targetname" "t20" "dmg" "1" } { "origin" "-72 1824 -120" "delay" ".2" "targetname" "t20" "classname" "target_explosion" "dmg" "1" } { "origin" "-16 1672 -120" "delay" ".4" "targetname" "t20" "dmg" "1" "classname" "target_explosion" } { "origin" "672 2320 -192" "targetname" "t100" "attenuation" "-1" "noise" "world/v_bas3.wav" "spawnflags" "2048" "classname" "target_speaker" } { "model" "*4" "target" "t100" "spawnflags" "2048" "classname" "trigger_once" } { "origin" "624 2264 -200" "targetname" "t97" "spawnflags" "0" "target" "t98" "classname" "trigger_relay" "delay" "1" } { "origin" "648 2264 -200" "spawnflags" "0" "targetname" "t76" "target" "t97" "classname" "trigger_relay" "delay" "1" } { "origin" "600 2264 -200" "targetname" "t98" "spawnflags" "0" "target" "t99" "delay" "3" "classname" "trigger_relay" } { "origin" "624 2240 -200" "targetname" "t98" "spawnflags" "2048" "classname" "target_speaker" "attenuation" "-1" "noise" "world/explod1.wav" } { "origin" "648 2240 -200" "targetname" "t97" "spawnflags" "2048" "classname" "target_speaker" "attenuation" "-1" "noise" "world/explod1.wav" } { "origin" "600 2240 -200" "targetname" "t99" "spawnflags" "2048" "noise" "world/explod1.wav" "attenuation" "-1" "classname" "target_speaker" } { "origin" "624 2360 -256" "classname" "misc_deadsoldier" "spawnflags" "2" } { "origin" "384 992 88" "target" "t96" "targetname" "t61" "delay" "4" "classname" "trigger_relay" } { "origin" "368 936 88" "attenuation" "-1" "spawnflags" "2048" "noise" "world/v_bas2.wav" "classname" "target_speaker" "targetname" "t96" } { "origin" "-112 360 96" "random" "15" "wait" "30" "spawnflags" "2049" "target" "t95" "classname" "func_timer" } { "spawnflags" "2048" "origin" "-144 360 96" "targetname" "t95" "noise" "world/flyby1.wav" "attenuation" "-1" "classname" "target_speaker" } { "classname" "monster_soldier_light" "angle" "270" "spawnflags" "257" "origin" "576 1776 24" } { "classname" "ammo_slugs" "spawnflags" "1792" "origin" "576 2656 16" } { "origin" "0 1976 136" "angles" "20 45 0" "classname" "info_player_intermission" } { "classname" "info_player_intermission" "angles" "20 215 0" "origin" "856 670 220" } { "model" "*5" "classname" "func_wall" "spawnflags" "1792" } { "classname" "ammo_shells" "spawnflags" "1536" "origin" "88 -1384 16" } { "model" "*6" "spawnflags" "1792" "classname" "func_wall" } { "classname" "trigger_always" "spawnflags" "1792" "target" "t67" "origin" "256 -856 72" } { "classname" "ammo_shells" "spawnflags" "1792" "origin" "-296 -352 0" } { "spawnflags" "1792" "classname" "item_health_small" "origin" "-96 -200 0" } { "spawnflags" "1792" "classname" "item_health_small" "origin" "-96 -160 0" } { "classname" "item_health_small" "spawnflags" "1792" "origin" "-96 -240 0" } { "classname" "ammo_rockets" "spawnflags" "1792" "origin" "-96 -416 0" } { "classname" "light" "light" "64" "origin" "-224 -36 44" } { "classname" "item_invulnerability" "spawnflags" "1792" "origin" "-224 -36 16" } { "model" "*7" "classname" "func_wall" "spawnflags" "2048" } { "model" "*8" "classname" "func_wall" "spawnflags" "2048" } { "classname" "weapon_rocketlauncher" "spawnflags" "1792" "origin" "704 -704 -136" } { "model" "*9" "classname" "func_wall" "spawnflags" "1792" } { "model" "*10" "spawnflags" "2048" "classname" "func_wall" } { "origin" "-224 -416 -4" "light" "80" "classname" "light" } { "origin" "-224 -352 44" "light" "100" "classname" "light" } { "origin" "-96 -304 44" "classname" "light" "light" "100" } { "origin" "-96 -416 44" "classname" "light" "light" "100" } { "origin" "-96 -160 44" "light" "100" "classname" "light" } { "origin" "412 -1856 16" "spawnflags" "1792" "classname" "item_armor_jacket" } { "origin" "304 -1856 16" "classname" "ammo_shells" "spawnflags" "1792" } { "origin" "344 -1856 16" "spawnflags" "1792" "classname" "ammo_shells" } { "origin" "80 -1576 16" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "80 -1624 16" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "608 1440 16" "classname" "ammo_grenades" "spawnflags" "1792" } { "origin" "608 1404 16" "classname" "ammo_grenades" "spawnflags" "1792" } { "origin" "-856 360 -136" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-856 264 -136" "spawnflags" "1792" "classname" "weapon_grenadelauncher" } { "origin" "500 -1056 16" "classname" "ammo_bullets" "spawnflags" "1792" } { "origin" "464 -1056 16" "classname" "ammo_bullets" "spawnflags" "1792" } { "origin" "536 -1056 16" "spawnflags" "1792" "classname" "ammo_bullets" } { "classname" "light" "light" "120" "origin" "840 2608 72" } { "classname" "light" "light" "120" "origin" "720 2608 72" } { "origin" "32 2444 -176" "classname" "weapon_shotgun" "spawnflags" "1792" } { "origin" "32 2480 -176" "classname" "ammo_shells" } { "origin" "704 -704 -112" "light" "80" "classname" "light" } { "model" "*11" "_minlight" ".1" "spawnflags" "1792" "classname" "func_wall" } { "spawnflags" "1536" "classname" "item_health" "origin" "-656 360 -140" } { "classname" "item_health" "origin" "-616 360 -140" } { "origin" "-616 240 -136" "spawnflags" "1792" "classname" "item_armor_combat" } { "model" "*12" "spawnflags" "2048" "classname" "func_wall" } { "origin" "-32 -32 8" "angle" "0" "classname" "info_player_deathmatch" } { "origin" "112 -1296 24" "angle" "0" "classname" "info_player_deathmatch" } { "origin" "-912 -72 -136" "angle" "0" "classname" "info_player_deathmatch" } { "origin" "96 1632 -168" "angle" "0" "classname" "info_player_deathmatch" } { "origin" "688 2520 -232" "angle" "180" "classname" "info_player_deathmatch" } { "origin" "360 1896 24" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "424 -224 8" "angle" "45" "classname" "info_player_deathmatch" } { "origin" "24 -320 8" "angle" "0" "classname" "info_player_deathmatch" } { "origin" "280 -1976 24" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "-224 2400 -168" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "-96 2600 24" "angle" "315" "classname" "info_player_deathmatch" } { "origin" "152 -960 0" "spawnflags" "1792" "classname" "weapon_chaingun" } { "origin" "864 2568 16" "spawnflags" "1792" "classname" "weapon_railgun" } { "spawnflags" "1024" "origin" "240 -1632 -72" "classname" "ammo_grenades" } { "classname" "weapon_shotgun" "origin" "-224 -344 -144" } { "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" "origin" "1012 -704 232" } { "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" "origin" "-4 -1792 248" } { "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" "origin" "-4 -1600 248" } { "spawnflags" "1" "classname" "target_speaker" "noise" "world/wind2.wav" "origin" "184 -1052 180" } { "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "-232 456 120" "volume" ".2" } { "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "296 456 120" "volume" ".4" } { "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" "origin" "736 456 120" "volume" ".4" } { "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" "origin" "480 184 120" "volume" ".4" } { "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" "origin" "0 168 120" "volume" ".2" } { "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "792 280 120" "volume" ".4" } { "attenuation" "-1" "classname" "target_speaker" "noise" "world/battle2.wav" "spawnflags" "2048" "targetname" "t93" "origin" "636 626 96" } { "wait" "19" "random" "8" "classname" "func_timer" "spawnflags" "1" "target" "t93" "origin" "644 594 96" } { "spawnflags" "1" "classname" "func_timer" "random" "4" "wait" "15" "target" "t91" "origin" "620 234 96" } { "attenuation" "-1" "spawnflags" "2048" "noise" "world/battle3.wav" "classname" "target_speaker" "targetname" "t91" "origin" "612 250 96" } { "spawnflags" "1" "classname" "func_timer" "random" "8" "wait" "19" "target" "t92" "origin" "660 458 96" } { "attenuation" "-1" "spawnflags" "2048" "noise" "world/explod2.wav" "classname" "target_speaker" "targetname" "t92" "volume" ".5" "origin" "652 490 32" } { "classname" "func_timer" "spawnflags" "1" "target" "t94" "random" "7" "wait" "16" "origin" "-42 474 62" } { "attenuation" "-1" "classname" "target_speaker" "noise" "world/battle1.wav" "spawnflags" "2048" "targetname" "t94" "origin" "-50 514 62" } { "classname" "item_armor_shard" "origin" "40 2128 -168" } { "classname" "item_armor_shard" "origin" "40 2088 -168" } { "classname" "item_armor_shard" "origin" "40 2168 -168" } { "spawnflags" "2048" "classname" "item_armor_shard" "origin" "840 2552 16" } { "spawnflags" "2048" "classname" "item_armor_shard" "origin" "872 2584 16" } { "spawnflags" "2048" "origin" "840 2640 24" "classname" "ammo_bullets" } { "spawnflags" "2048" "origin" "760 2640 24" "classname" "ammo_bullets" } { "classname" "ammo_grenades" "origin" "116 -994 0" } { "classname" "item_health_large" "origin" "28 -480 72" "spawnflags" "2048" } { "classname" "ammo_rockets" "origin" "272 -544 8" "spawnflags" "1792" } { "spawnflags" "1" "classname" "monster_gunner" "angle" "270" "target" "t89" "origin" "120 -2136 24" "item" "ammo_bullets" } { "classname" "path_corner" "targetname" "t89" "target" "t90" "origin" "120 -2272 8" } { "classname" "path_corner" "target" "t89" "targetname" "t90" "origin" "120 -2048 8" } { "model" "*13" "classname" "trigger_once" "target" "t88" } { "classname" "monster_soldier_ss" "angle" "0" "targetname" "t48" "spawnflags" "1" "origin" "544 -1296 24" } { "classname" "ammo_shells" "origin" "368 -1008 16" } { "classname" "ammo_shells" "origin" "368 -1048 16" } { "classname" "light" "light" "64" "origin" "76 -704 0" } { "origin" "216 -1824 -78" "classname" "ammo_shells" } { "origin" "160 -152 0" "classname" "ammo_shells" } { "origin" "160 -200 0" "classname" "ammo_shells" } { "classname" "target_speaker" "noise" "world/lite_on2.wav" "targetname" "t74" "attenuation" "-1" "origin" "648 -1024 112" "spawnflags" "2048" } { "origin" "236 2016 -160" "classname" "ammo_bullets" } { "classname" "ammo_shells" "origin" "32 2520 -176" } { "classname" "item_health_large" "origin" "288 2464 -176" } { "classname" "ammo_bullets" "origin" "258 -992 0" } { "origin" "712 440 168" "spawnflags" "1" "target" "t77" "classname" "target_crosslevel_target" } { "origin" "776 512 184" "targetname" "t77" "classname" "monster_flyer" "angle" "225" "spawnflags" "3" } { "origin" "744 568 96" "targetname" "t77" "classname" "monster_flyer" "angle" "225" "spawnflags" "3" } { "origin" "600 632 184" "targetname" "t77" "spawnflags" "3" "angle" "225" "classname" "monster_flyer" } { "classname" "target_help" "targetname" "t76" "origin" "808 2344 -208" "message" "Use sewer tunnels to gain\naccess to the Comm Center." } { "model" "*14" "classname" "trigger_once" "target" "t76" } { "origin" "16 -416 40" "light" "80" "classname" "light" } { "origin" "16 -416 88" "classname" "light" "light" "80" } { "origin" "16 -288 88" "light" "80" "classname" "light" } { "origin" "16 -288 40" "classname" "light" "light" "80" } { "classname" "target_secret" "targetname" "t75" "message" "You have found a secret area." "origin" "-168 -344 -88" } { "classname" "target_secret" "targetname" "t73" "message" "You have found a secret." "origin" "248 -1816 -56" } { "spawnflags" "2048" "targetname" "t67" "classname" "target_goal" "origin" "272 -752 48" } { "classname" "target_goal" "targetname" "t5" "origin" "-856 296 -64" } { "targetname" "t73" "origin" "-44 -2272 -72" "angle" "0" "classname" "monster_soldier_light" } { "origin" "624 -1040 96" "target" "t74" "delay" "3.5" "targetname" "t6" "classname" "trigger_relay" } { "origin" "848 2296 -72" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "model" "*15" "targetname" "block" "spawnflags" "2048" "classname" "func_wall" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "64 -2080 72" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "56 -2216 72" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "-24 -96 72" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" "origin" "352 -1880 104" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" "origin" "88 -1296 72" } { "origin" "416 -1456 0" "noise" "world/amb15.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "552 -1728 -56" "noise" "world/amb15.wav" "spawnflags" "1" "classname" "target_speaker" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" "origin" "768 -1384 72" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" "origin" "672 2608 96" } { "noise" "world/drip_amb.wav" "origin" "720 -888 -136" "classname" "target_speaker" "spawnflags" "1" } { "origin" "816 -696 -136" "classname" "target_speaker" "spawnflags" "1" "noise" "world/drip_amb.wav" } { "origin" "496 -736 -136" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "224 -2096 -56" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb15.wav" } { "origin" "360 -2272 -56" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb15.wav" } { "origin" "536 -2256 -56" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb15.wav" } { "origin" "552 -2016 -56" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb15.wav" } { "origin" "544 -1456 0" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb15.wav" } { "origin" "208 -1856 -56" "noise" "world/amb15.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "616 1352 64" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "352 -1992 72" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "392 1688 32" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb10.wav" } { "origin" "552 1888 32" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb10.wav" } { "origin" "104 1784 32" "noise" "world/amb10.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "208 2008 96" "noise" "world/amb6.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-40 2464 96" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "320 1184 64" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "288 2600 96" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "-240 2240 -144" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "-240 1984 -144" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "64 1960 -144" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "-40 2080 96" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "440 1880 -176" "noise" "world/spark2.wav" "spawnflags" "1" "classname" "target_speaker" } { "noise" "world/comp_hum2.wav" "origin" "416 1952 -104" "spawnflags" "1" "classname" "target_speaker" } { "targetname" "t20" "origin" "-128 1800 -104" "noise" "world/turbine1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "320 1176 -128" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum1.wav" } { "origin" "624 1360 -128" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum1.wav" } { "origin" "624 1176 -128" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" } { "origin" "480 1000 104" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" } { "origin" "24 1176 -128" "noise" "world/comp_hum3.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "720 -384 8" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "832 -144 8" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "392 -248 8" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "800 -536 8" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "160 -744 -88" "noise" "world/amb5.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "96 -240 -88" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb14.wav" } { "origin" "96 -416 -88" "noise" "world/amb14.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "32 -352 72" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-736 256 -152" "classname" "target_speaker" "spawnflags" "1" "noise" "world/water1.wav" } { "origin" "-568 96 -152" "classname" "target_speaker" "spawnflags" "1" "noise" "world/water1.wav" } { "origin" "-424 104 -152" "classname" "target_speaker" "spawnflags" "1" "noise" "world/water1.wav" } { "origin" "-360 288 -152" "classname" "target_speaker" "spawnflags" "1" "noise" "world/water1.wav" } { "origin" "-768 -56 -152" "noise" "world/water1.wav" "spawnflags" "1" "classname" "target_speaker" } { "noise" "world/drip_amb.wav" "origin" "24 -248 -152" "classname" "target_speaker" "spawnflags" "1" } { "noise" "world/drip_amb.wav" "origin" "-96 -304 -152" "classname" "target_speaker" "spawnflags" "1" } { "origin" "-192 -376 -144" "classname" "target_speaker" "spawnflags" "1" "noise" "world/drip_amb" } { "noise" "world/drip_amb.wav" "origin" "104 -416 -152" "classname" "target_speaker" "spawnflags" "1" } { "noise" "world/drip_amb.wav" "origin" "48 -808 -152" "classname" "target_speaker" "spawnflags" "1" } { "noise" "world/drip_amb.wav" "origin" "152 -672 -152" "classname" "target_speaker" "spawnflags" "1" } { "noise" "world/drip_amb.wav" "origin" "248 -672 -152" "classname" "target_speaker" "spawnflags" "1" } { "noise" "world/drip_amb.wav" "origin" "256 -864 -152" "classname" "target_speaker" "spawnflags" "1" } { "noise" "world/drip_amb.wav" "origin" "-160 -96 -152" "spawnflags" "1" "classname" "target_speaker" } { "origin" "272 -840 40" "killtarget" "block" "targetname" "t6" "classname" "trigger_relay" } { "origin" "544 1568 -176" "classname" "item_health" } { "origin" "600 1568 -176" "classname" "item_health" } { "origin" "64 -488 88" "light" "100" "classname" "light" } { "origin" "152 -8 8" "classname" "item_health_large" } { "origin" "-56 1696 -176" "classname" "item_health_large" } { "model" "*16" "spawnflags" "2048" "speed" "200" "wait" "-1" "angle" "-2" "classname" "func_door" "targetname" "t73" "_minlight" ".1" } { "origin" "-40 -1760 -72" "angle" "0" "classname" "monster_soldier_light" "targetname" "t73" } { "model" "*17" "spawnflags" "2048" "classname" "func_door" "angle" "-2" "wait" "-1" "speed" "200" "targetname" "t73" "_minlight" ".1" } { "light" "64" "classname" "light" "origin" "456 -1944 -84" } { "model" "*18" "targetname" "block" "classname" "func_wall" "spawnflags" "2048" } { "classname" "monster_soldier_light" "angle" "0" "targetname" "t73" "origin" "0 -2272 -72" } { "classname" "misc_deadsoldier" "spawnflags" "16" "angle" "0" "origin" "-240 -432 -160" } { "style" "1" "classname" "func_areaportal" "targetname" "t48" } { "origin" "288 -1384 24" "classname" "item_health_small" } { "origin" "168 -1408 0" "classname" "misc_deadsoldier" "spawnflags" "9" } { "origin" "256 -1344 0" "classname" "misc_deadsoldier" "spawnflags" "1" } { "origin" "160 -1304 0" "spawnflags" "2" "classname" "misc_deadsoldier" } { "origin" "936 -1280 24" "classname" "ammo_bullets" } { "origin" "936 -1328 24" "classname" "ammo_bullets" } { "origin" "440 352 -96" "light" "100" "classname" "light" } { "origin" "808 1832 24" "classname" "item_armor_jacket" } { "origin" "224 1696 24" "classname" "item_health" } { "origin" "160 1696 24" "classname" "item_health" } { "origin" "-48 2520 72" "classname" "light" "light" "120" } { "origin" "-48 2416 72" "light" "120" "classname" "light" } { "origin" "240 2608 72" "classname" "light" "light" "120" } { "origin" "344 2608 72" "light" "120" "classname" "light" } { "origin" "616 2608 72" "classname" "light" "light" "120" } { "origin" "840 2520 72" "light" "120" "classname" "light" } { "origin" "-40 2032 72" "classname" "light" "light" "120" } { "origin" "-40 2136 72" "light" "120" "classname" "light" } { "model" "*19" "classname" "trigger_once" "target" "t68" } { "origin" "-232 -344 -120" "light" "80" "classname" "light" } { "angle" "90" "origin" "448 352 -136" "spawnflags" "32" "classname" "misc_deadsoldier" } { "origin" "100 -540 -16" "classname" "misc_explobox" } { "model" "*20" "target" "t67" "classname" "trigger_once" } { "origin" "160 -2024 24" "classname" "ammo_shells" } { "origin" "424 -2024 24" "classname" "item_health_small" } { "origin" "424 -2072 24" "classname" "item_health_small" } { "origin" "560 -1776 -96" "spawnflags" "4" "classname" "misc_deadsoldier" } { "origin" "88 -1720 -96" "spawnflags" "4" "classname" "misc_deadsoldier" } { "origin" "304 -1824 -96" "spawnflags" "1" "classname" "misc_deadsoldier" } { "origin" "304 -1888 -72" "classname" "weapon_supershotgun" "target" "t73" } { "origin" "920 -1008 24" "classname" "item_health_small" } { "origin" "920 -1048 24" "classname" "item_health_small" } { "origin" "864 -616 8" "classname" "ammo_shells" } { "origin" "32 -416 0" "classname" "item_health" } { "origin" "32 -376 0" "classname" "item_health" } { "origin" "190 -992 0" "classname" "ammo_bullets" } { "origin" "224 -992 0" "classname" "ammo_bullets" } { "origin" "448 -704 32" "angle" "0" "classname" "monster_infantry" "targetname" "t68" } { "origin" "576 -1032 32" "angle" "45" "classname" "monster_soldier_light" "targetname" "t68" } { "style" "6" "light" "130" "origin" "-224 -352 -80" "classname" "light" "_color" "1.000000 0.976000 0.064000" } { "model" "*21" "mass" "800" "dmg" "1" "health" "20" "classname" "func_explosive" "target" "t75" } { "spawnflags" "1536" "origin" "-696 360 -140" "classname" "item_health" } { "origin" "608 1016 16" "classname" "item_health_small" } { "origin" "608 1056 16" "classname" "item_health_small" } { "origin" "608 976 16" "classname" "item_health_small" } { "origin" "608 1248 -168" "classname" "ammo_bullets" } { "origin" "608 1312 -168" "classname" "ammo_bullets" } { "spawnflags" "2048" "origin" "800 2640 24" "classname" "ammo_bullets" } { "light" "80" "classname" "light" "origin" "512 864 124" } { "classname" "light" "light" "80" "origin" "512 800 124" } { "classname" "light" "light" "80" "origin" "480 1008 128" } { "origin" "432 1056 128" "light" "80" "classname" "light" } { "classname" "light" "light" "80" "origin" "432 1056 48" } { "classname" "light" "light" "80" "origin" "544 1020 48" } { "classname" "light" "light" "120" "origin" "624 992 80" } { "origin" "192 1696 80" "classname" "light" "light" "80" } { "origin" "384 1696 80" "classname" "light" "light" "80" } { "origin" "768 1792 80" "light" "130" "classname" "light" } { "origin" "80 1472 80" "classname" "light" "light" "120" } { "origin" "80 1360 80" "light" "120" "classname" "light" } { "origin" "192 1624 -104" "classname" "light" "light" "100" } { "origin" "384 1624 -104" "light" "100" "classname" "light" } { "origin" "384 1696 -104" "light" "80" "classname" "light" } { "origin" "384 1496 -104" "classname" "light" "light" "100" } { "origin" "192 1688 -104" "classname" "light" "light" "80" } { "origin" "192 1496 -104" "light" "100" "classname" "light" } { "origin" "64 2432 -120" "classname" "light" "light" "120" } { "origin" "64 2176 -120" "light" "120" "classname" "light" } { "origin" "848 2360 -184" "classname" "light" "light" "100" } { "origin" "848 2232 -184" "light" "100" "classname" "light" } { "origin" "832 2292 -224" "angle" "180" "classname" "info_player_start" } { "light" "80" "classname" "light" "origin" "156 1964 -132" } { "classname" "light" "light" "80" "origin" "232 1964 -132" } { "origin" "-12 2028 8" "targetname" "t66" "spawnflags" "1" "classname" "point_combat" } { "origin" "408 1504 -176" "targetname" "t37" "classname" "point_combat" } { "origin" "-936 -72 -128" "angle" "45" "classname" "monster_soldier_light" } { "model" "*22" "target" "t65" "classname" "trigger_once" } { "origin" "-848 360 -128" "targetname" "t65" "angle" "315" "classname" "monster_soldier_light" } { "classname" "monster_soldier_light" "angle" "90" "target" "t60" "spawnflags" "1" "origin" "752 -568 16" } { "origin" "760 1792 32" "targetname" "t62" "spawnflags" "1" "angle" "180" "classname" "monster_soldier_light" } { "model" "*23" "spawnflags" "2048" "target" "t62" "classname" "trigger_once" } { "model" "*24" "spawnflags" "2048" "targetname" "t62" "speed" "200" "_minlight" ".2" "wait" "-1" "angle" "-2" "classname" "func_door" } { "origin" "552 1216 24" "classname" "item_health_small" } { "origin" "600 1216 24" "classname" "item_health_small" } { "origin" "-24 1896 16" "classname" "ammo_bullets" } { "origin" "32 1896 16" "classname" "ammo_bullets" } { "origin" "-96 2408 -168" "classname" "item_health_small" } { "origin" "-144 2408 -168" "classname" "item_health_small" } { "origin" "480 2112 -240" "classname" "item_health" } { "model" "*25" "target" "t61" "classname" "trigger_once" "spawnflags" "2048" } { "origin" "544 344 40" "dmg" "1" "targetname" "t61" "classname" "target_explosion" "delay" "1.5" } { "origin" "520 392 40" "dmg" "1" "delay" "1" "targetname" "t61" "classname" "target_explosion" } { "origin" "488 296 40" "dmg" "1" "delay" ".4" "targetname" "t61" "classname" "target_explosion" } { "origin" "400 336 56" "dmg" "1" "delay" ".2" "targetname" "t61" "classname" "target_explosion" } { "origin" "448 416 72" "dmg" "1" "delay" ".4" "targetname" "t61" "classname" "target_explosion" } { "origin" "424 464 40" "dmg" "1" "delay" ".8" "targetname" "t61" "classname" "target_explosion" } { "origin" "416 432 40" "dmg" "1" "delay" ".1" "targetname" "t61" "classname" "target_explosion" } { "origin" "544 448 40" "dmg" "1" "delay" "1.2" "targetname" "t61" "classname" "target_explosion" } { "classname" "info_player_start" "angle" "0" "targetname" "base3b" "origin" "8 -800 8" } { "item" "ammo_bullets" "classname" "monster_infantry" "angle" "90" "target" "t56" "spawnflags" "257" "origin" "808 -480 8" } { "classname" "path_corner" "targetname" "t57" "target" "t58" "origin" "752 -200 -8" } { "classname" "path_corner" "targetname" "t59" "target" "t60" "origin" "752 -200 -8" } { "classname" "path_corner" "target" "t57" "targetname" "t60" "origin" "752 -520 -8" } { "classname" "path_corner" "targetname" "t58" "target" "t59" "origin" "576 -200 -8" } { "classname" "path_corner" "targetname" "t55" "target" "t56" "origin" "808 -192 -8" } { "classname" "path_corner" "target" "t55" "targetname" "t56" "origin" "808 -448 -8" } { "model" "*26" "classname" "trigger_once" "target" "t52" } { "classname" "monster_soldier_light" "angle" "270" "targetname" "t52" "origin" "272 -288 24" "spawnflags" "1" } { "angle" "270" "classname" "monster_soldier_ss" "targetname" "t48" "origin" "312 -1328 24" "spawnflags" "768" } { "classname" "light" "light" "120" "origin" "96 -184 96" } { "classname" "func_group" } { "origin" "74 -704 76" "light" "80" "classname" "light" } { "origin" "76 -896 0" "light" "64" "classname" "light" } { "classname" "light" "light" "120" "origin" "88 -800 64" } { "origin" "8 -800 116" "classname" "light" "light" "64" } { "model" "*27" "_minlight" ".2" "angle" "-1" "classname" "func_door" } { "origin" "432 592 24" "targetname" "t45" "classname" "monster_soldier_light" "angle" "90" "spawnflags" "1" } { "model" "*28" "target" "t44" "classname" "trigger_once" } { "origin" "192 536 24" "targetname" "t44" "angle" "0" "classname" "monster_soldier_light" "spawnflags" "257" } { "origin" "88 544 24" "target" "t43" "angle" "90" "classname" "monster_soldier_light" "spawnflags" "1" } { "origin" "-288 600 -144" "target" "t43" "targetname" "t42" "classname" "path_corner" } { "origin" "88 600 8" "targetname" "t43" "target" "t42" "classname" "path_corner" } { "origin" "512 800 24" "target" "t45" "targetname" "t41" "angle" "90" "classname" "monster_infantry" "spawnflags" "1" } { "model" "*29" "target" "t41" "classname" "trigger_once" } { "origin" "592 976 8" "targetname" "t40" "target" "t39" "classname" "path_corner" } { "origin" "248 976 8" "target" "t40" "targetname" "t39" "classname" "path_corner" } { "item" "ammo_bullets" "spawnflags" "1" "origin" "304 976 24" "target" "t39" "angle" "180" "classname" "monster_infantry" } { "model" "*30" "target" "t38" "classname" "trigger_once" } { "spawnflags" "1" "origin" "304 1800 24" "targetname" "t38" "angle" "0" "classname" "monster_soldier_light" } { "origin" "496 1216 24" "target" "t36" "angle" "180" "classname" "monster_soldier_light" } { "origin" "144 1216 8" "target" "t36" "targetname" "t35" "classname" "path_corner" } { "origin" "440 1216 8" "targetname" "t36" "target" "t35" "classname" "path_corner" } { "spawnflags" "1" "origin" "96 1320 -160" "target" "t34" "angle" "270" "classname" "monster_soldier_ss" } { "origin" "96 1272 -176" "targetname" "t34" "target" "t33" "classname" "path_corner" } { "origin" "576 1224 -176" "target" "t33" "targetname" "t32" "classname" "path_corner" } { "origin" "48 1224 -176" "targetname" "t33" "target" "t32" "classname" "path_corner" } { "origin" "48 1456 -176" "target" "t33" "targetname" "t32" "classname" "path_corner" } { "spawnflags" "257" "origin" "32 1512 32" "targetname" "t30" "classname" "monster_soldier_light" } { "model" "*31" "target" "t30" "classname" "trigger_once" } { "spawnflags" "1" "target" "t37" "origin" "560 1504 -160" "targetname" "t31" "angle" "180" "classname" "monster_infantry" } { "origin" "464 1512 -104" "target" "t31" "targetname" "t30" "classname" "trigger_relay" } { "model" "*32" "target" "t30" "classname" "trigger_multiple" } { "origin" "320 1968 -152" "light" "80" "classname" "light" } { "origin" "288 1896 -112" "light" "150" "classname" "light" } { "origin" "400 1896 -112" "light" "150" "classname" "light" } { "model" "*33" "speed" "100" "classname" "func_door" "angle" "-1" "wait" "-1" "_minlight" ".2" "targetname" "t29" "spawnflags" "2048" } { "model" "*34" "classname" "func_button" "angle" "90" "wait" "-1" "target" "t29" "spawnflags" "2048" } { "classname" "ammo_bullets" "origin" "202 2016 -160" } { "classname" "weapon_machinegun" "origin" "162 2016 -160" } { "spawnflags" "1" "classname" "monster_soldier_ss" "angle" "270" "target" "t28" "origin" "384 1816 -160" } { "classname" "path_corner" "target" "t25" "targetname" "t26" "origin" "192 1768 -176" } { "classname" "path_corner" "targetname" "t27" "target" "t28" "origin" "384 1496 -176" } { "classname" "path_corner" "targetname" "t25" "target" "t27" "origin" "192 1488 -176" } { "classname" "path_corner" "targetname" "t25" "target" "t26" "origin" "40 1768 -176" } { "classname" "path_corner" "targetname" "t28" "target" "t26" "origin" "384 1768 -176" } { "spawnflags" "1" "classname" "monster_soldier_ss" "angle" "180" "target" "t23" "origin" "240 1912 -160" } { "classname" "path_corner" "targetname" "t23" "target" "t24" "origin" "128 1912 -176" } { "classname" "path_corner" "target" "t23" "targetname" "t24" "origin" "376 1912 -176" } { "target" "t66" "spawnflags" "257" "classname" "monster_soldier_light" "angle" "90" "targetname" "t21" "origin" "184 1792 32" } { "model" "*35" "classname" "trigger_multiple" "target" "t21" } { "spawnflags" "257" "classname" "monster_soldier_light" "angle" "180" "targetname" "t21" "origin" "816 2576 32" } { "model" "*36" "spawnflags" "2048" "classname" "trigger_once" "target" "t20" } { "spawnflags" "1" "classname" "monster_infantry" "angle" "180" "targetname" "t20" "origin" "-80 1896 -168" } { "spawnflags" "1" "classname" "monster_soldier_ss" "angle" "180" "targetname" "t19" "origin" "288 2528 -232" } { "spawnflags" "1" "classname" "monster_soldier_ss" "angle" "90" "targetname" "t19" "origin" "120 2088 -232" } { "angle" "135" "classname" "monster_soldier_ss" "target" "t18" "origin" "416 2208 -232" } { "spawnflags" "1" "classname" "monster_soldier_ss" "angle" "90" "targetname" "t18" "target" "t19" "origin" "352 2144 -232" } { "classname" "path_corner" "targetname" "t16" "target" "t17" "origin" "-200 2000 -176" } { "classname" "path_corner" "targetname" "t15" "target" "t16" "origin" "-160 2304 -176" } { "classname" "path_corner" "targetname" "t14" "target" "t15" "origin" "-64 2304 -176" } { "target" "t16" "classname" "path_corner" "targetname" "t17" "origin" "-200 2152 -176" } { "classname" "path_corner" "targetname" "t13" "target" "t14" "origin" "272 2304 -248" } { "classname" "path_corner" "targetname" "t12" "target" "t13" "origin" "384 2368 -248" } { "classname" "path_corner" "target" "t12" "origin" "440 2368 -248" "targetname" "t22" } { "origin" "-120 -760 72" "map" "base3$base2b" "targetname" "t11" "classname" "target_changelevel" } { "model" "*37" "target" "t11" "classname" "trigger_multiple" "angle" "180" } { "classname" "target_changelevel" "targetname" "t9" "origin" "-912 104 -232" "map" "base3$base2a" } { "model" "*38" "classname" "trigger_multiple" "target" "t9" } { "model" "*39" "target" "t8" "classname" "trigger_multiple" } { "origin" "856 2264 56" "map" "base1$base2" "targetname" "t8" "classname" "target_changelevel" } { "origin" "480 2448 -256" "classname" "misc_explobox" } { "origin" "544 2496 -256" "classname" "misc_explobox" } { "origin" "-248 544 -24" "light" "150" "classname" "light" } { "light" "64" "classname" "light" "origin" "248 -1992 -84" } { "light" "64" "classname" "light" "origin" "56 -2040 -84" } { "light" "64" "classname" "light" "origin" "376 -1800 -84" } { "origin" "56 -1848 -84" "classname" "light" "light" "64" } { "origin" "200 -1656 -84" "classname" "light" "light" "64" } { "origin" "200 -1496 -84" "classname" "light" "light" "64" } { "origin" "208 -1912 -84" "classname" "light" "light" "64" } { "origin" "376 -1912 -84" "classname" "light" "light" "64" } { "origin" "248 -2184 -84" "classname" "light" "light" "64" } { "origin" "56 -2184 -84" "classname" "light" "light" "64" } { "origin" "584 -2184 -84" "classname" "light" "light" "64" } { "origin" "584 -2104 -84" "classname" "light" "light" "64" } { "origin" "456 -2184 -84" "classname" "light" "light" "64" } { "origin" "456 -2104 -84" "classname" "light" "light" "64" } { "origin" "456 -1800 -84" "classname" "light" "light" "64" } { "origin" "56 -1672 -84" "light" "64" "classname" "light" } { "origin" "64 -2080 32" "classname" "light" "light" "80" } { "origin" "64 -2208 32" "light" "80" "classname" "light" } { "origin" "40 -2080 160" "classname" "light" "light" "80" } { "origin" "40 -2208 160" "light" "80" "classname" "light" } { "origin" "48 -2208 104" "classname" "light" "light" "80" } { "origin" "48 -2080 104" "light" "80" "classname" "light" } { "classname" "func_group" } { "classname" "func_group" } { "light" "150" "classname" "light" "origin" "352 -2288 56" } { "light" "150" "classname" "light" "origin" "144 -2288 56" } { "light" "150" "classname" "light" "origin" "160 -2064 56" } { "light" "200" "classname" "light" "origin" "352 -2080 56" } { "light" "120" "classname" "light" "origin" "352 -1968 64" } { "classname" "light" "origin" "-4 -1596 148" "light" "450" } { "classname" "light" "origin" "1012 -708 124" "light" "450" } { "origin" "280 -800 24" "classname" "light" "light" "80" } { "origin" "224 -800 24" "light" "120" "classname" "light" } { "origin" "560 -848 -88" "classname" "light" "light" "150" } { "origin" "832 -832 -88" "light" "150" "classname" "light" } { "origin" "832 -680 -136" "classname" "light" "light" "120" } { "origin" "560 -696 -136" "light" "120" "classname" "light" } { "style" "32" "targetname" "t74" "spawnflags" "1" "origin" "768 -1072 80" "light" "175" "classname" "light" } { "origin" "920 -616 168" "classname" "light" "light" "100" } { "origin" "920 -616 40" "light" "100" "classname" "light" } { "origin" "928 -800 40" "classname" "light" "light" "100" } { "origin" "928 -800 168" "light" "100" "classname" "light" } { "model" "*40" "origin" "450 -958 -18" "targetname" "t6" "distance" "90" "spawnflags" "65" "wait" "-1" "speed" "35" "classname" "func_door_rotating" } { "origin" "944 -1024 68" "light" "150" "classname" "light" } { "light" "450" "origin" "-4 -1788 148" "classname" "light" } { "classname" "func_group" } { "origin" "416 -1696 56" "classname" "light" "light" "150" } { "origin" "576 -1576 56" "classname" "light" "light" "150" } { "origin" "120 -1872 56" "classname" "light" "light" "150" } { "origin" "184 -1672 56" "classname" "light" "light" "150" } { "model" "*41" "_minlight" ".2" "target" "t7" "classname" "func_button" "angle" "0" } { "model" "*42" "spawnflags" "2048" "team" "1" "classname" "func_door" "angle" "270" "speed" "50" "lip" "32" } { "model" "*43" "spawnflags" "2048" "team" "1" "angle" "90" "classname" "func_door" "speed" "50" "lip" "32" } { "classname" "light" "light" "64" "origin" "904 2304 -216" } { "classname" "light" "light" "120" "origin" "712 2368 -184" } { "light" "120" "classname" "light" "origin" "712 2240 -184" } { "origin" "848 2296 -28" "classname" "light" "light" "150" } { "model" "*44" "_minlight" ".2" "targetname" "t7" "angle" "-1" "classname" "func_door" } { "classname" "light" "light" "150" "origin" "848 2360 -124" } { "light" "150" "classname" "light" "origin" "848 2232 -124" } { "origin" "704 2232 -160" "light" "200" "classname" "light" } { "classname" "func_group" } { "classname" "light" "light" "120" "origin" "832 -1296 80" } { "classname" "light" "light" "120" "origin" "696 -1296 80" } { "classname" "light" "light" "120" "origin" "608 -1296 80" } { "light" "120" "classname" "light" "origin" "480 -1296 80" } { "light" "120" "classname" "light" "origin" "768 -1216 80" } { "classname" "light" "light" "120" "origin" "344 -1296 80" } { "model" "*45" "_minlight" ".1" "classname" "func_door" "angle" "-1" "target" "t48" } { "classname" "func_group" } { "origin" "-888 92 -308" "light" "80" "classname" "light" } { "origin" "-888 96 -208" "light" "80" "classname" "light" } { "origin" "-888 92 20" "light" "80" "classname" "light" } { "origin" "-748 292 -208" "classname" "light" "light" "100" "_color" "0.666667 1.000000 0.986928" } { "origin" "-696 224 -208" "classname" "light" "light" "100" "_color" "0.666667 1.000000 0.986928" } { "origin" "-648 160 -208" "classname" "light" "light" "100" "_color" "0.666667 1.000000 0.986928" } { "origin" "-644 56 -208" "classname" "light" "light" "100" "_color" "0.666667 1.000000 0.986928" } { "origin" "-636 -44 -208" "classname" "light" "light" "100" "_color" "0.666667 1.000000 0.986928" } { "origin" "-860 -68 -204" "classname" "light" "light" "100" "_color" "0.666667 1.000000 0.986928" } { "origin" "-784 -60 -204" "classname" "light" "light" "100" "_color" "0.666667 1.000000 0.986928" } { "origin" "-728 -4 -204" "classname" "light" "light" "100" "_color" "0.666667 1.000000 0.986928" } { "origin" "-724 92 -204" "classname" "light" "light" "100" "_color" "0.666667 1.000000 0.986928" } { "origin" "-728 184 -204" "classname" "light" "light" "100" "_color" "0.000000 0.666667 1.000000" } { "origin" "-816 268 -208" "_color" "0.666667 1.000000 0.986928" "light" "100" "classname" "light" } { "origin" "-968 -72 -96" "classname" "light" "light" "120" } { "origin" "-608 288 -96" "light" "120" "classname" "light" } { "classname" "light" "light" "80" "origin" "-888 92 -404" } { "classname" "light" "light" "100" "origin" "-888 92 -108" } { "model" "*46" "spawnflags" "2048" "target" "t5" "angle" "180" "classname" "func_button" "wait" "-1" } { "origin" "-728 96 -40" "light" "150" "classname" "light" } { "origin" "-896 -64 -72" "classname" "light" "light" "120" } { "origin" "-876 264 -108" "classname" "light" "light" "80" } { "origin" "-888 92 124" "light" "64" "classname" "light" } { "model" "*47" "origin" "-888 180 -156" "_minlight" ".2" "targetname" "t5" "wait" "-1" "speed" "35" "spawnflags" "64" "distance" "-90" "classname" "func_door_rotating" } { "model" "*48" "origin" "-888 12 -156" "_minlight" ".2" "targetname" "t5" "distance" "90" "wait" "-1" "speed" "35" "spawnflags" "64" "classname" "func_door_rotating" } { "light" "200" "classname" "light" "origin" "448 -768 80" } { "light" "200" "classname" "light" "origin" "448 -640 80" } { "classname" "light" "light" "200" "origin" "448 -896 80" } { "classname" "func_group" } { "light" "150" "classname" "light" "origin" "168 -584 96" } { "light" "150" "classname" "light" "origin" "168 -888 96" } { "classname" "light" "light" "150" "origin" "168 -768 96" } { "classname" "func_group" } { "light" "80" "classname" "light" "origin" "-56 -96 96" } { "classname" "light" "light" "80" "origin" "-48 -96 24" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "light" "120" "classname" "light" "origin" "188 -1048 176" } { "light" "250" "classname" "light" "origin" "188 -1048 96" } { "classname" "light" "light" "120" "origin" "188 -1048 20" } { "model" "*49" "origin" "256 -800 -9" "targetname" "t67" "classname" "func_door_rotating" "wait" "-1" "distance" "90" "spawnflags" "144" "speed" "50" "target" "t6" "dmg" "1" } { "light" "300" "classname" "light" "origin" "416 -864 352" } { "light" "250" "classname" "light" "origin" "560 -912 112" } { "classname" "light" "light" "300" "origin" "416 -728 352" } { "style" "32" "targetname" "t74" "spawnflags" "1" "origin" "672 -1076 16" "classname" "light" "light" "120" } { "style" "32" "targetname" "t74" "spawnflags" "1" "origin" "864 -1076 16" "light" "120" "classname" "light" } { "model" "*50" "dmg" "10" "_minlight" ".2" "lip" "132" "classname" "func_plat" } { "origin" "8 1248 -28" "light" "64" "classname" "light" "_color" "0.643137 0.882353 1.000000" } { "origin" "12 1312 -168" "classname" "light" "light" "64" } { "origin" "12 1504 -168" "light" "64" "classname" "light" } { "origin" "632 1376 -28" "_color" "0.643137 0.882353 1.000000" "classname" "light" "light" "64" } { "origin" "632 1312 -28" "_color" "0.643137 0.882353 1.000000" "classname" "light" "light" "64" } { "origin" "632 1176 -28" "_color" "0.643137 0.882353 1.000000" "classname" "light" "light" "64" } { "origin" "544 1176 -28" "_color" "0.643137 0.882353 1.000000" "classname" "light" "light" "64" } { "origin" "416 1184 -36" "_color" "1.000000 0.000000 0.000000" "classname" "light" "light" "64" } { "origin" "352 1184 -36" "_color" "1.000000 0.000000 0.000000" "classname" "light" "light" "64" } { "origin" "288 1184 -36" "_color" "1.000000 0.000000 0.000000" "classname" "light" "light" "64" } { "origin" "160 1176 -28" "_color" "0.643137 0.882353 1.000000" "classname" "light" "light" "64" } { "origin" "96 1176 -28" "_color" "0.643137 0.882353 1.000000" "classname" "light" "light" "64" } { "origin" "8 1176 -28" "_color" "0.643137 0.882353 1.000000" "classname" "light" "light" "64" } { "origin" "8 1592 -28" "_color" "0.643137 0.882353 1.000000" "classname" "light" "light" "64" } { "origin" "632 1504 -28" "_color" "0.643137 0.882353 1.000000" "light" "64" "classname" "light" } { "origin" "-128 1792 16" "classname" "light" "light" "120" } { "origin" "-128 1792 128" "classname" "light" "light" "120" } { "origin" "-128 1792 -88" "light" "120" "classname" "light" } { "model" "*51" "origin" "-128 1792 -32" "classname" "func_rotating" "spawnflags" "1" "speed" "450" "_minlight" ".2" } { "model" "*52" "origin" "-128 1792 184" "_minlight" ".2" "speed" "450" "spawnflags" "2051" "classname" "func_rotating" } { "origin" "-80 1408 -96" "light" "150" "classname" "light" } { "origin" "480 1008 48" "light" "80" "classname" "light" } { "origin" "200 1880 -88" "classname" "light" "light" "150" } { "origin" "16 1832 -88" "classname" "light" "light" "150" } { "origin" "-176 1928 -88" "classname" "light" "light" "200" } { "origin" "-176 2112 -88" "classname" "light" "light" "200" } { "origin" "-168 2304 -88" "classname" "light" "light" "200" } { "origin" "416 1864 -88" "light" "150" "classname" "light" } { "origin" "224 1816 88" "classname" "light" "light" "150" } { "origin" "416 1808 88" "classname" "light" "light" "150" } { "origin" "576 1816 88" "classname" "light" "light" "150" } { "origin" "584 1664 88" "classname" "light" "light" "200" } { "origin" "576 1464 88" "classname" "light" "light" "200" } { "origin" "432 1288 176" "light" "200" "classname" "light" } { "origin" "432 1472 176" "classname" "light" "light" "150" } { "origin" "432 1472 -32" "classname" "light" "light" "150" } { "origin" "432 1288 -32" "light" "200" "classname" "light" } { "origin" "128 1296 -32" "light" "200" "classname" "light" } { "origin" "128 1472 -32" "light" "150" "classname" "light" } { "origin" "576 1280 88" "classname" "light" "light" "200" } { "origin" "128 1296 176" "classname" "light" "light" "200" } { "origin" "128 1472 176" "classname" "light" "light" "150" } { "origin" "528 2136 128" "light" "250" "classname" "light" } { "origin" "352 2160 128" "light" "250" "classname" "light" } { "origin" "304 2376 128" "light" "250" "classname" "light" } { "light" "250" "classname" "light" "origin" "504 2280 128" } { "light" "250" "classname" "light" "origin" "616 2360 128" } { "light" "200" "classname" "light" "origin" "456 2248 -160" } { "light" "200" "classname" "light" "origin" "248 2232 -160" } { "light" "200" "classname" "light" "origin" "248 2464 -160" } { "light" "200" "classname" "light" "origin" "440 2456 -160" } { "light" "200" "classname" "light" "origin" "576 2464 -160" } { "origin" "568 584 40" "classname" "light" } { "origin" "280 384 40" "classname" "light" } { "origin" "-8 408 40" "classname" "light" } { "origin" "-96 232 48" "classname" "light" } { "origin" "-104 240 304" "classname" "light" } { "origin" "-104 496 304" "classname" "light" } { "origin" "120 336 304" "classname" "light" } { "origin" "336 344 304" "classname" "light" } { "origin" "624 344 304" "classname" "light" } { "origin" "616 592 304" "classname" "light" } { "origin" "-48 632 440" "classname" "light" } { "origin" "768 744 440" "classname" "light" } { "origin" "648 344 40" "classname" "light" } { "origin" "848 2292 -224" "angle" "180" "classname" "info_player_start" "targetname" "base1" } { "classname" "func_group" } { "classname" "func_group" } { "origin" "-80 1408 148" "light" "80" "classname" "light" } { "classname" "light" "light" "150" "origin" "-80 1408 104" } { "origin" "608 -704 24" "light" "100" "classname" "light" } { "classname" "light" "light" "100" "origin" "608 -704 104" } { "origin" "608 -704 200" "light" "100" "classname" "light" } { "model" "*53" "message" "This door is opened elsewhere." "classname" "func_door" "angle" "-1" "_minlight" ".2" "targetname" "t6" "wait" "-1" } { "classname" "light" "light" "64" "origin" "-120 -800 116" } { "model" "*54" "target" "t4" "classname" "func_button" "angle" "0" } { "model" "*55" "spawnflags" "2048" "wait" "-1" "targetname" "t72" "classname" "func_door" "angle" "270" "speed" "50" "lip" "32" "_minlight" ".2" "team" "tits" } { "model" "*56" "spawnflags" "2048" "targetname" "t72" "wait" "-1" "angle" "90" "classname" "func_door" "speed" "50" "lip" "32" "_minlight" ".2" "team" "tits" } { "classname" "light" "light" "64" "origin" "825 -1728 40" } { "classname" "light" "light" "150" "origin" "632 -1744 56" } { "light" "150" "classname" "light" "origin" "576 -1872 56" } { "light" "150" "classname" "light" "origin" "592 -2272 56" } { "model" "*57" "classname" "func_door" "angle" "-2" "targetname" "t4" "_minlight" ".2" } { "origin" "576 -2096 56" "classname" "light" "light" "150" } { "light" "64" "classname" "light" "origin" "648 -1848 -84" } { "light" "64" "classname" "light" "origin" "648 -1928 -84" } { "light" "64" "classname" "light" "origin" "584 -2104 -84" } { "light" "64" "classname" "light" "origin" "584 -2184 -84" } { "classname" "light" "light" "80" "origin" "632 -1800 104" } { "light" "80" "classname" "light" "origin" "632 -1720 104" } { "classname" "light" "light" "80" "origin" "632 -1720 248" } { "light" "80" "classname" "light" "origin" "632 -1800 248" } { "classname" "light" "light" "80" "origin" "632 -1720 168" } { "light" "80" "classname" "light" "origin" "632 -1800 168" } { "classname" "light" "light" "150" "origin" "776 -1824 120" } { "light" "150" "classname" "light" "origin" "776 -1696 120" } { "spawnflags" "1" "origin" "800 -1836 40" "map" "eou1_.cin+*bunk1$start" "classname" "target_changelevel" "targetname" "t69" } { "classname" "misc_gib_head" "origin" "600 -1816 -80" } { "classname" "item_quad" "origin" "624 -2192 88" } { "classname" "light" "light" "130" "origin" "624 -1888 96" } { "light" "130" "classname" "light" "origin" "624 -1632 96" } { "classname" "trigger_relay" "targetname" "t4" "delay" "1" "target" "t69" "origin" "800 -1816 64" } { "model" "*58" "spawnflags" "2048" "message" "This is the end of the unit." "classname" "trigger_once" } { "model" "*59" "wait" "-1" "target" "t72" "angle" "0" "classname" "func_button" } { "origin" "744 -2272 -72" "targetname" "t73" "angle" "180" "classname" "monster_soldier_light" } { "model" "*60" "spawnflags" "2048" "_minlight" ".1" "targetname" "t73" "classname" "func_door" "angle" "-2" "wait" "-1" "speed" "200" } { "targetname" "t73" "classname" "monster_soldier_light" "angle" "180" "origin" "696 -2272 -72" } { "model" "*61" "spawnflags" "2048" "_minlight" ".1" "targetname" "t73" "classname" "func_door" "angle" "-2" "wait" "-1" "speed" "200" } { "targetname" "t73" "classname" "monster_soldier_light" "angle" "180" "origin" "696 -2016 -72" } { "spawnflags" "1" "classname" "target_speaker" "noise" "world/amb12.wav" "origin" "776 -1760 -56" } { "origin" "616 -1864 48" "targetname" "t72" "classname" "target_goal" }yquake2-QUAKE2_8_40/stuff/mapfixes/baseq2/base3.ent000066400000000000000000002136541465112212000217510ustar00rootroot00000000000000// FIXED ENTITY STRING (by BjossiAlfreds) // // 1. Fixed a messed-up explosive box model // // The offending box is func_explosive (1757). The model // is inline so it can't be fixed from the entity string, // but it is possible to reference one of the other 3 // and apply an offset (for inline models, the "origin" // field acts as such). Changed model *17 to *16 and // applied an offset of -160 -32 0 { "sky" "unit1_" "sounds" "2" "message" "Comm Center" "classname" "worldspawn" "spawnflags" "0" "nextmap" "bunk1" } { "origin" "2088 800 -300" "targetname" "t108" "spawnflags" "771" "angle" "180" "classname" "monster_gunner" } { "origin" "668 992 -1032" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "740 992 -1032" "spawnflags" "1792" "classname" "ammo_cells" } { "classname" "item_invulnerability" "spawnflags" "1792" "origin" "1912 1424 -544" } { "model" "*1" "targetname" "t44" "target" "t176" "classname" "trigger_once" } { "angle" "180" "spawnflags" "769" "classname" "monster_soldier_ss" "origin" "1696 32 -488" } { "classname" "monster_soldier_ss" "spawnflags" "769" "angle" "180" "origin" "1688 -96 -488" } { "classname" "monster_soldier_ss" "angle" "0" "spawnflags" "257" "target" "t174" "origin" "1464 -160 -480" } { "classname" "path_corner" "targetname" "t174" "target" "t175" "origin" "1496 -160 -496" } { "classname" "path_corner" "target" "t174" "targetname" "t175" "origin" "1632 -160 -496" } { "spawnflags" "768" "angle" "0" "classname" "monster_soldier_light" "origin" "608 96 -232" } { "classname" "monster_soldier_light" "angle" "270" "spawnflags" "768" "origin" "992 224 -232" } { "origin" "100 1708 -848" "targetname" "train" "classname" "info_player_coop" "angle" "270" } { "origin" "96 1768 -848" "targetname" "train" "classname" "info_player_coop" "angle" "270" } { "origin" "160 1768 -848" "targetname" "train" "angle" "270" "classname" "info_player_coop" } { "classname" "info_player_coop" "targetname" "base2b" "origin" "2224 904 -296" } { "classname" "info_player_coop" "targetname" "base2b" "origin" "2248 960 -296" } { "classname" "info_player_coop" "targetname" "base2b" "origin" "2256 848 -296" } { "angle" "180" "classname" "info_player_coop" "targetname" "base2a" "origin" "1576 1408 -800" } { "angle" "180" "classname" "info_player_coop" "targetname" "base2a" "origin" "1624 1408 -800" } { "classname" "info_player_coop" "angle" "180" "targetname" "base2a" "origin" "1528 1408 -800" } { "origin" "832 80 -420" "targetname" "t108" "spawnflags" "770" "angle" "0" "classname" "monster_gunner" } { "origin" "744 -576 -228" "targetname" "t108" "angle" "180" "spawnflags" "770" "classname" "monster_gunner" } { "origin" "64 -472 -228" "targetname" "t108" "spawnflags" "770" "angle" "180" "classname" "monster_gunner" } { "origin" "-40 -896 -280" "targetname" "t100" "spawnflags" "769" "angle" "180" "classname" "monster_parasite" } { "origin" "-680 -856 -272" "target" "t172" "spawnflags" "768" "angle" "0" "classname" "monster_gunner" } { "origin" "-448 -856 -288" "targetname" "t173" "target" "t172" "classname" "path_corner" } { "origin" "-648 -856 -288" "target" "t173" "targetname" "t172" "classname" "path_corner" } { "classname" "target_speaker" "targetname" "t171" "noise" "world/voice5.wav" "spawnflags" "2048" "attenuation" "-1" "origin" "784 -288 -208" } { "model" "*2" "classname" "trigger_once" "spawnflags" "2048" "target" "t171" } { "spawnflags" "2048" "classname" "target_speaker" "attenuation" "-1" "noise" "world/v_bas5.wav" "targetname" "t170" "origin" "1384 -264 -440" } { "model" "*3" "classname" "trigger_once" "target" "t170" } { "classname" "target_speaker" "noise" "world/v_bas4.wav" "attenuation" "-1" "targetname" "t169" "spawnflags" "2048" "origin" "1432 1464 -784" } { "model" "*4" "classname" "trigger_once" "spawnflags" "2048" "target" "t169" } { "classname" "trigger_relay" "delay" "1" "target" "t168" "spawnflags" "2048" "targetname" "t29" "origin" "1564 52 -292" } { "wait" "61" "classname" "func_timer" "target" "t167" "targetname" "t168" "spawnflags" "2048" "origin" "1564 76 -292" } { "noise" "world/flyby2.wav" "attenuation" "-1" "classname" "target_speaker" "targetname" "t167" "spawnflags" "2048" "origin" "1564 100 -292" } { "classname" "func_timer" "wait" "60" "target" "t166" "spawnflags" "2048" "targetname" "t29" "origin" "1536 76 -292" } { "classname" "target_speaker" "attenuation" "-1" "noise" "world/flyby1.wav" "targetname" "t166" "spawnflags" "2048" "origin" "1536 100 -292" } { "origin" "-152 -340 -164" "target" "t165" "targetname" "t101" "classname" "trigger_relay" } { "origin" "-136 -340 -164" "target" "t165" "targetname" "t101" "delay" "0.1" "classname" "trigger_relay" } { "style" "32" "origin" "-148 -320 -176" "targetname" "t165" "spawnflags" "1" "light" "100" "classname" "light" } { "origin" "-416 -312 -320" "light" "80" "classname" "light" } { "origin" "-424 -552 -224" "spawnflags" "1792" "target" "t164" "classname" "trigger_always" } { "origin" "-424 -504 -224" "targetname" "t164" "delay" "5" "target" "t145" "classname" "trigger_relay" } { "origin" "272 1736 -856" "target" "t163" "spawnflags" "1792" "angle" "225" "classname" "misc_teleporter" } { "origin" "-784 -912 -280" "spawnflags" "1792" "targetname" "t163" "angle" "45" "classname" "misc_teleporter_dest" } { "origin" "-512 -368 -200" "target" "t108" "spawnflags" "1792" "classname" "trigger_always" } { "targetname" "t176" "classname" "target_secret" "message" "You have found a secret level." "origin" "32 1712 -824" } { "classname" "info_player_deathmatch" "angle" "315" "origin" "296 1188 -808" } { "origin" "1616 24 -504" "classname" "item_armor_shard" } { "origin" "1616 -16 -504" "classname" "item_armor_shard" } { "origin" "1616 64 -504" "classname" "item_armor_shard" } { "origin" "1616 -96 -440" "spawnflags" "1792" "team" "lita" "classname" "item_adrenaline" } { "team" "lita" "origin" "1616 -96 -440" "spawnflags" "1792" "classname" "item_health_mega" } { "model" "*5" "spawnflags" "1792" "classname" "func_wall" } { "origin" "1372 1668 -944" "spawnflags" "1792" "classname" "item_adrenaline" } { "model" "*6" "classname" "func_wall" "spawnflags" "1792" } { "origin" "-120 -248 -232" "classname" "weapon_grenadelauncher" "spawnflags" "1792" } { "origin" "328 -832 -240" "classname" "ammo_shells" "spawnflags" "1792" } { "origin" "328 -752 -240" "spawnflags" "1792" "classname" "ammo_shells" } { "origin" "288 -792 -240" "spawnflags" "1792" "classname" "weapon_supershotgun" } { "origin" "800 -544 -240" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "800 -608 -240" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-8 1736 -864" "spawnflags" "1792" "classname" "ammo_rockets" } { "spawnflags" "1792" "origin" "136 1664 -864" "classname" "weapon_rocketlauncher" } { "spawnflags" "1792" "origin" "784 -1248 -680" "classname" "ammo_cells" } { "spawnflags" "1792" "origin" "704 -1248 -680" "classname" "ammo_cells" } { "origin" "744 -1208 -680" "spawnflags" "1792" "classname" "weapon_bfg" } { "origin" "-640 -704 -288" "classname" "ammo_bullets" "spawnflags" "1792" } { "origin" "-600 -744 -288" "classname" "ammo_bullets" "spawnflags" "1792" } { "origin" "-640 -744 -288" "classname" "ammo_bullets" "spawnflags" "1792" } { "origin" "-600 -704 -288" "spawnflags" "1792" "classname" "ammo_bullets" } { "origin" "-544 -376 -272" "spawnflags" "1792" "classname" "weapon_chaingun" } { "origin" "1232 -96 -488" "spawnflags" "1792" "classname" "weapon_machinegun" } { "origin" "1896 904 -472" "spawnflags" "1792" "classname" "weapon_hyperblaster" } { "origin" "1392 360 -472" "classname" "ammo_cells" "spawnflags" "1792" } { "origin" "1432 360 -472" "spawnflags" "1792" "classname" "ammo_cells" } { "origin" "96 612 -912" "spawnflags" "1792" "classname" "item_silencer" } { "origin" "-20 580 -608" "classname" "ammo_bullets" "spawnflags" "1792" } { "origin" "-20 616 -608" "classname" "ammo_bullets" "spawnflags" "1792" } { "origin" "-20 544 -608" "spawnflags" "1792" "classname" "ammo_bullets" } { "origin" "416 284 -912" "spawnflags" "1792" "classname" "item_armor_combat" } { "model" "*7" "spawnflags" "1792" "classname" "func_wall" } { "origin" "624 -160 -232" "angle" "45" "classname" "info_player_deathmatch" } { "classname" "info_player_deathmatch" "angle" "0" "origin" "24 1632 -856" } { "classname" "info_player_deathmatch" "angle" "90" "origin" "608 600 -808" } { "classname" "info_player_deathmatch" "angle" "180" "origin" "2120 592 -464" } { "classname" "info_player_deathmatch" "angle" "45" "origin" "520 -1088 -480" } { "classname" "info_player_deathmatch" "angle" "135" "origin" "-288 -928 -280" } { "classname" "info_player_deathmatch" "angle" "315" "origin" "-352 -1048 -280" } { "classname" "info_player_deathmatch" "angle" "90" "origin" "224 -864 -232" } { "classname" "info_player_deathmatch" "angle" "0" "origin" "152 -424 -488" } { "classname" "info_player_deathmatch" "angle" "90" "origin" "40 112 -800" } { "classname" "info_player_deathmatch" "angle" "270" "origin" "1168 1616 -808" } { "classname" "info_player_deathmatch" "angle" "90" "origin" "2208 744 -304" } { "classname" "info_player_deathmatch" "angle" "135" "origin" "1824 -224 -280" } { "classname" "info_player_intermission" "angles" "35 215 0" "origin" "1064 -480 -172" } { "classname" "trigger_always" "spawnflags" "1792" "target" "t138" "origin" "1528 328 -360" } { "spawnflags" "2048" "classname" "ammo_bullets" "origin" "256 1704 -864" } { "classname" "ammo_bullets" "spawnflags" "2048" "origin" "224 1744 -864" } { "classname" "item_armor_shard" "origin" "628 1184 -816" } { "classname" "item_armor_shard" "origin" "592 1184 -816" } { "classname" "item_armor_shard" "origin" "556 1184 -816" } { "origin" "-416 -312 -248" "light" "80" "classname" "light" } { "classname" "ammo_grenades" "origin" "1416 1672 -944" } { "classname" "misc_deadsoldier" "spawnflags" "1" "origin" "1440 1640 -960" } { "classname" "func_timer" "wait" "30" "random" "10" "spawnflags" "1" "target" "t162" "origin" "-88 -1248 -168" } { "noise" "world/battle1.wav" "classname" "target_speaker" "targetname" "t162" "origin" "-96 -1272 -168" } { "spawnflags" "1" "random" "3" "wait" "7" "classname" "func_timer" "target" "t161" "origin" "-176 -1224 -176" } { "classname" "target_speaker" "noise" "world/battle4.wav" "targetname" "t161" "origin" "-168 -1248 -176" } { "noise" "world/battle3.wav" "classname" "target_speaker" "targetname" "t159" "origin" "1304 -128 -440" "spawnflags" "2048" "attenuation" "-1" } { "noise" "world/battle4.wav" "classname" "target_speaker" "targetname" "t160" "origin" "1368 80 -440" "attenuation" "-1" "spawnflags" "2048" } { "noise" "world/battle1.wav" "classname" "target_speaker" "targetname" "t157" "origin" "1648 136 -440" "spawnflags" "2048" "attenuation" "-1" } { "classname" "target_speaker" "noise" "world/battle1.wav" "targetname" "t158" "origin" "1656 -160 -440" "attenuation" "-1" "spawnflags" "2048" } { "spawnflags" "1" "random" "6" "wait" "20" "classname" "func_timer" "target" "t159" "origin" "1304 -160 -440" } { "spawnflags" "1" "random" "10" "wait" "30" "classname" "func_timer" "target" "t158" "origin" "1664 -136 -440" } { "spawnflags" "1" "random" "6" "wait" "10" "classname" "func_timer" "target" "t157" "origin" "1632 160 -440" } { "classname" "func_timer" "wait" "7" "random" "3" "spawnflags" "1" "target" "t160" "origin" "1360 96 -440" } { "classname" "monster_soldier_ss" "angle" "270" "origin" "1374 -192 -480" } { "classname" "monster_soldier_ss" "angle" "90" "target" "t156" "spawnflags" "1" "origin" "1344 -100 -480" } { "classname" "path_corner" "targetname" "t155" "target" "t156" "origin" "1344 -128 -496" } { "classname" "path_corner" "target" "t155" "targetname" "t156" "origin" "1344 128 -496" } { "origin" "-288 -648 -176" "targetname" "t154" "noise" "world/brkglas.wav" "classname" "target_speaker" } { "origin" "-288 -696 -176" "target" "t154" "targetname" "t153" "classname" "trigger_relay" } { "origin" "-384 -736 -176" "targetname" "t153" "target" "t142" "classname" "trigger_relay" } { "origin" "-800 -824 -296" "targetname" "t151" "classname" "point_combat" } { "origin" "-800 -536 -280" "targetname" "t142" "target" "t151" "angle" "270" "classname" "monster_soldier" } { "origin" "1168 712 -808" "classname" "ammo_shells" } { "origin" "1144 678 -808" "classname" "ammo_shells" } { "origin" "992 -1096 -488" "classname" "ammo_bullets" } { "origin" "-40 168 -816" "classname" "ammo_rockets" "spawnflags" "1792" } { "origin" "1824 1052 -460" "classname" "ammo_grenades" } { "spawnflags" "1024" "origin" "-32 -216 -232" "classname" "ammo_grenades" } { "spawnflags" "1792" "origin" "-72 -216 -232" "classname" "ammo_grenades" } { "origin" "1152 160 -488" "classname" "item_health_large" } { "spawnflags" "1536" "origin" "1144 -136 -488" "classname" "ammo_bullets" } { "origin" "1144 -96 -488" "classname" "ammo_bullets" } { "classname" "misc_deadsoldier" "spawnflags" "2" "angle" "45" "origin" "260 1652 -880" } { "classname" "light" "light" "64" "origin" "2292 800 -312" } { "origin" "152 1668 -832" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb6.wav" } { "origin" "152 1568 -832" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb6.wav" } { "origin" "152 1464 -832" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb6.wav" } { "origin" "180 1724 -880" "angle" "315" "spawnflags" "1" "classname" "misc_deadsoldier" } { "classname" "func_group" } { "model" "*8" "wait" "-1" "target" "t135" "targetname" "lita" "angle" "90" "classname" "func_button" "spawnflags" "2048" } { "classname" "ammo_shells" "origin" "1512 648 -472" } { "spawnflags" "2048" "classname" "trigger_relay" "message" "This item must be activated to use it." "targetname" "t150" "origin" "680 696 -776" } { "spawnflags" "2048" "classname" "trigger_relay" "message" "This item must be activated to use it." "targetname" "t149" "origin" "1048 -720 -328" } { "spawnflags" "2048" "classname" "trigger_relay" "targetname" "t148" "message" "This item must be activated to use it." "origin" "408 568 -512" } { "origin" "1824 992 -452" "light" "64" "classname" "light" } { "classname" "target_speaker" "attenuation" "-1" "noise" "world/lite_on1.wav" "targetname" "t135" "origin" "664 224 -384" } { "classname" "target_speaker" "targetname" "t147" "spawnflags" "2048" "attenuation" "-1" "noise" "world/uplink2.wav" "origin" "-584 -360 -264" } { "classname" "ammo_shells" "origin" "-756 -692 -288" } { "classname" "ammo_shells" "origin" "104 -552 -232" } { "classname" "ammo_shells" "origin" "1568 -288 -488" } { "classname" "ammo_shells" "origin" "8 704 -816" } { "spawnflags" "2048" "classname" "item_adrenaline" "origin" "744 -1216 -680" } { "origin" "-664 -328 -88" "spawnflags" "1" "targetname" "t108" "classname" "target_crosslevel_trigger" } { "origin" "-616 -400 -256" "message" "Primary unit objective\ncompleted." "spawnflags" "1" "targetname" "t141" "classname" "target_help" } { "message" "Locate communication center." "origin" "1640 1416 -768" "targetname" "t146" "classname" "target_help" } { "model" "*9" "target" "t146" "classname" "trigger_once" } { "classname" "ammo_bullets" "origin" "1184 -96 -488" } { "spawnflags" "1536" "classname" "ammo_bullets" "origin" "1184 -136 -488" } { "origin" "1640 328 -440" "targetname" "t143" "classname" "target_goal" } { "origin" "-472 -368 -264" "targetname" "t108" "classname" "target_goal" } { "classname" "ammo_grenades" "origin" "1824 1012 -460" } { "classname" "light" "light" "64" "origin" "1824 1056 -452" } { "spawnflags" "2048" "classname" "target_speaker" "targetname" "t132" "attenuation" "-1" "origin" "-540 28 -168" "noise" "world/dish1.wav" } { "origin" "-296 -444 -232" "targetname" "t145" "target" "t144" "classname" "trigger_relay" } { "targetname" "t144" "noise" "world/l_hum1.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-284 -418 -254" } { "targetname" "t144" "origin" "-542 -420 -254" "classname" "target_speaker" "spawnflags" "1" "noise" "world/l_hum1.wav" } { "targetname" "t144" "origin" "-812 -416 -256" "noise" "world/l_hum1.wav" "spawnflags" "1" "classname" "target_speaker" } { "dmg" "100" "targetname" "t145" "origin" "-266 -400 -280" "classname" "target_laser" "spawnflags" "3" "angle" "180" } { "dmg" "100" "targetname" "t145" "origin" "-266 -400 -232" "angle" "180" "spawnflags" "3" "classname" "target_laser" } { "angle" "225" "classname" "monster_flyer" "spawnflags" "3" "targetname" "t108" "origin" "1728 184 -264" } { "angle" "225" "classname" "monster_flyer" "spawnflags" "3" "targetname" "t108" "origin" "1648 304 -328" } { "classname" "monster_flyer" "angle" "225" "spawnflags" "259" "targetname" "t108" "origin" "1704 256 -224" } { "origin" "2096 1072 -288" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" "origin" "1744 888 -424" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" "origin" "1376 -344 -448" } { "origin" "1672 360 -424" "targetname" "t143" "classname" "target_help" "message" "Locate unit exit\nand kill all resistance." } { "noise" "world/amb10.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-704 -496 -264" } { "noise" "world/comp_hum3.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-624 -584 -264" } { "noise" "world/amb10.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-696 -752 -264" } { "noise" "world/amb10.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "104 -1120 -216" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb10.wav" "origin" "56 -552 -216" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb11.wav" "origin" "-792 -600 -264" } { "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "208 -768 -216" } { "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "200 -392 -216" } { "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-152 -648 -216" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/spark2.wav" "origin" "-64 -248 -176" } { "origin" "-208 -896 -216" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "-440 -1144 -216" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "noise" "world/amb10.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "8 -1208 -216" } { "noise" "world/amb10.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "296 -880 -216" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb10.wav" "origin" "64 -840 -216" } { "origin" "760 -408 -168" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "752 -248 -168" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "-416 -240 -240" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum3.wav" "origin" "-544 -336 -240" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "1600 472 -448" } { "origin" "2080 744 -288" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "480 -784 -384" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb12.wav" } { "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-64 -248 -216" } { "origin" "-552 -1032 -216" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "spawnflags" "1" "origin" "272 1056 -760" "noise" "world/amb7.wav" "classname" "target_speaker" } { "spawnflags" "1" "origin" "992 1152 -760" "noise" "world/amb7.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/amb7.wav" "origin" "600 744 -760" "spawnflags" "1" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/spark2.wav" "origin" "1152 704 -736" } { "origin" "648 -160 -376" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb9.wav" } { "origin" "920 -168 -376" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb9.wav" } { "origin" "1024 -480 -376" "classname" "target_speaker" "spawnflags" "1" "noise" "world/bigpump2.wav" } { "origin" "312 -352 -456" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "1376 -576 -448" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/curnt2.wav" "origin" "672 1192 -912" } { "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "472 -344 -456" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" "origin" "1608 352 -448" } { "noise" "world/amb9.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "656 200 -376" } { "noise" "world/bigpump2.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1192 -960 -376" } { "noise" "world/amb15.wav" "classname" "target_speaker" "origin" "744 -1224 -584" } { "classname" "target_speaker" "noise" "world/amb15.wav" "origin" "736 -904 -680" } { "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "480 -568 -384" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb12.wav" "origin" "480 -952 -384" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "176 488 -616" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "328 352 -616" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "328 480 -616" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" "origin" "176 352 -616" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb6.wav" "origin" "64 1320 -832" } { "noise" "world/amb13.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-24 1672 -812" } { "spawnflags" "2048" "classname" "item_health_large" "origin" "-24 1736 -864" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "104 1144 -824" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "72 1224 -824" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "72 1224 -992" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "176 1112 -992" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "264 1096 -992" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "304 1176 -992" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "416 1136 -992" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "680 1160 -984" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "752 1032 -984" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "1128 712 -776" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "952 1144 -776" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "1168 1512 -776" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "1152 1248 -776" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "1064 1352 -776" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "1360 1680 -776" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "1392 1408 -800" } { "noise" "world/drip_amb.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "1648 1408 -776" } { "origin" "128 1384 -824" "spawnflags" "1" "classname" "target_speaker" "noise" "world/drip_amb.wav" } { "classname" "item_health" "origin" "-336 -608 -280" } { "classname" "item_health" "origin" "1120 1632 -816" } { "classname" "item_health" "origin" "1120 1584 -816" } { "origin" "32 480 -816" "classname" "item_health_large" } { "origin" "-612 -360 -264" "targetname" "t108" "classname" "trigger_relay" "delay" "7" "target" "t147" } { "model" "*10" "mass" "100" "targetname" "t100" "dmg" "1" "classname" "func_explosive" } { "origin" "808 -112 -236" "classname" "item_health_small" } { "origin" "808 -148 -236" "classname" "item_health_small" } { "origin" "1012 -64 -216" "classname" "light" "light" "80" } { "origin" "1012 -160 -216" "classname" "light" "light" "80" } { "origin" "928 -180 -216" "classname" "light" "light" "80" } { "origin" "608 -180 -216" "classname" "light" "light" "80" } { "origin" "628 244 -216" "classname" "light" "light" "80" } { "origin" "588 228 -216" "classname" "light" "light" "80" } { "origin" "588 96 -216" "classname" "light" "light" "80" } { "origin" "588 -32 -216" "classname" "light" "light" "80" } { "origin" "1012 60 -216" "light" "80" "classname" "light" } { "origin" "608 -160 -428" "classname" "item_health" } { "style" "33" "origin" "640 238 -412" "targetname" "t135" "light" "150" "classname" "light" } { "model" "*11" "classname" "trigger_once" "target" "t142" } { "classname" "target_help" "targetname" "t141" "message" "Use blue key-card to enter\nalien bunker installation." "origin" "-656 -400 -256" } { "classname" "trigger_relay" "targetname" "t108" "delay" "4" "target" "t141" "origin" "-656 -360 -264" } { "spawnflags" "2048" "classname" "key_blue_key" "origin" "-416 -312 -320" } { "origin" "2208 1048 -304" "classname" "item_health_small" } { "origin" "2208 1008 -304" "classname" "item_health_small" } { "origin" "2016 1056 -472" "classname" "ammo_bullets" } { "origin" "1976 1056 -472" "classname" "ammo_bullets" } { "origin" "928 160 -400" "delay" ".3" "targetname" "t135" "target" "t140" "classname" "trigger_relay" } { "origin" "1168 -1048 -488" "classname" "item_health" } { "origin" "1128 -1048 -488" "classname" "item_health" } { "origin" "1008 -1096 -448" "light" "80" "classname" "light" } { "origin" "344 -792 -200" "light" "80" "classname" "light" } { "spawnflags" "2048" "classname" "ammo_bullets" "origin" "368 -792 -240" } { "origin" "-72 -280 -232" "classname" "item_health_small" } { "origin" "-32 -280 -232" "classname" "item_health_small" } { "origin" "-336 -672 -280" "classname" "item_health_large" } { "origin" "-756 -728 -288" "classname" "ammo_shells" } { "origin" "112 1224 -840" "targetname" "t139" "classname" "target_secret" "message" "You have found a secret area." } { "model" "*12" "target" "t139" "classname" "trigger_once" } { "spawnflags" "1792" "origin" "288 1600 -864" "classname" "item_armor_jacket" } { "origin" "72 1304 -824" "light" "100" "classname" "light" } { "origin" "648 656 -816" "classname" "item_breather" "target" "t150" } { "origin" "464 648 -792" "classname" "light" "light" "80" } { "origin" "464 528 -792" "light" "80" "classname" "light" } { "classname" "item_health" "origin" "496 656 -816" } { "classname" "item_health" "origin" "496 704 -816" } { "model" "*13" "target" "t137" "classname" "trigger_multiple" "spawnflags" "2048" } { "origin" "1528 372 -364" "item" "key_blue_key" "target" "t138" "targetname" "t137" "classname" "trigger_key" } { "classname" "ammo_shells" "origin" "844 -160 -432" } { "classname" "point_combat" "targetname" "t136" "origin" "-192 -448 -248" } { "classname" "light" "light" "80" "origin" "464 544 -512" } { "classname" "item_quad" "origin" "464 544 -544" "target" "t148" } { "classname" "misc_deadsoldier" "spawnflags" "16" "angle" "90" "origin" "368 112 -624" } { "classname" "light" "light" "150" "origin" "744 -1216 -640" } { "model" "*14" "dmg" "50" "classname" "func_explosive" "targetname" "t135" "spawnflags" "2048" } { "model" "*15" "targetname" "t140" "dmg" "75" "classname" "func_explosive" "spawnflags" "2048" } { "model" "*16" "dmg" "75" "classname" "func_explosive" "targetname" "t135" "spawnflags" "2048" } { "model" "*16" // was *17 "origin" "-160 -32 0" // added this "targetname" "t140" "classname" "func_explosive" "dmg" "50" "spawnflags" "2048" } { "classname" "light" "light" "64" "origin" "648 -96 -372" } { "classname" "light" "light" "80" "origin" "1064 32 -324" } { "model" "*18" "classname" "trigger_once" "target" "lita" } { "origin" "996 -96 -372" "light" "64" "classname" "light" } { "origin" "992 192 -372" "light" "64" "classname" "light" } { "origin" "652 32 -372" "light" "64" "classname" "light" } { "origin" "720 -56 -372" "light" "64" "classname" "light" } { "origin" "616 -96 -424" "light" "64" "classname" "light" } { "classname" "light" "light" "150" "origin" "592 -536 -336" } { "classname" "light" "light" "150" "origin" "600 -576 -416" } { "light" "120" "classname" "light" "origin" "1504 -240 -440" } { "classname" "light" "light" "120" "origin" "1248 -240 -440" } { "origin" "992 -888 -440" "light" "130" "classname" "light" } { "origin" "904 -712 -440" "light" "130" "classname" "light" } { "origin" "832 -744 -440" "light" "130" "classname" "light" } { "origin" "632 -892 -572" "light" "150" "classname" "light" } { "origin" "740 -1000 -572" "classname" "light" "light" "150" } { "origin" "828 -892 -572" "classname" "light" "light" "150" } { "origin" "744 -752 -572" "classname" "light" "light" "150" } { "light" "80" "classname" "light" "origin" "120 464 -768" } { "origin" "120 552 -768" "classname" "light" "light" "80" } { "origin" "128 336 -768" "light" "80" "classname" "light" } { "light" "80" "classname" "light" "origin" "32 528 -768" } { "classname" "light" "light" "100" "origin" "40 400 -768" } { "light" "64" "classname" "light" "origin" "32 784 -736" } { "classname" "light" "light" "64" "origin" "-24 744 -736" } { "light" "80" "classname" "light" "origin" "176 704 -720" } { "light" "100" "classname" "light" "origin" "64 704 -704" } { "light" "100" "classname" "light" "origin" "64 424 -576" } { "light" "100" "classname" "light" "origin" "72 504 -624" } { "light" "100" "classname" "light" "origin" "64 584 -656" } { "classname" "light" "light" "180" "origin" "738 1092 -736" } { "light" "150" "classname" "light" "origin" "344 648 -512" } { "light" "100" "classname" "light" "origin" "392 696 -408" } { "light" "100" "classname" "light" "origin" "360 224 -728" } { "light" "100" "classname" "light" "origin" "240 216 -728" } { "light" "80" "classname" "light" "origin" "192 136 -728" } { "light" "100" "classname" "light" "origin" "40 144 -728" } { "light" "100" "classname" "light" "origin" "0 192 -728" } { "light" "100" "classname" "light" "origin" "0 416 -728" } { "light" "100" "classname" "light" "origin" "-48 416 -672" } { "light" "100" "classname" "light" "origin" "-48 160 -672" } { "light" "100" "classname" "light" "origin" "96 96 -672" } { "light" "100" "classname" "light" "origin" "256 96 -672" } { "light" "100" "classname" "light" "origin" "424 96 -672" } { "light" "100" "classname" "light" "origin" "496 160 -672" } { "light" "100" "classname" "light" "origin" "496 416 -672" } { "light" "100" "classname" "light" "origin" "496 672 -672" } { "light" "80" "classname" "light" "origin" "384 816 -672" } { "light" "80" "classname" "light" "origin" "256 816 -672" } { "light" "100" "classname" "light" "origin" "96 816 -672" } { "light" "100" "classname" "light" "origin" "-48 672 -672" } { "classname" "light" "light" "80" "origin" "456 256 -728" } { "origin" "64 640 -440" "classname" "light" "light" "250" } { "classname" "light" "light" "80" "origin" "904 664 -736" } { "classname" "light" "light" "200" "origin" "268 1056 -760" } { "light" "100" "classname" "light" "origin" "1152 1328 -776" } { "light" "100" "classname" "light" "origin" "1152 1472 -776" } { "light" "100" "classname" "light" "origin" "1152 1584 -776" } { "classname" "light" "light" "80" "origin" "1256 1600 -808" } { "light" "64" "classname" "light" "origin" "1472 1616 -840" } { "light" "64" "classname" "light" "origin" "1408 1616 -840" } { "light" "64" "classname" "light" "origin" "1408 1544 -840" } { "light" "64" "classname" "light" "origin" "1408 1472 -840" } { "light" "64" "classname" "light" "origin" "1408 1400 -840" } { "light" "64" "classname" "light" "origin" "1408 1328 -840" } { "light" "64" "classname" "light" "origin" "1336 1328 -840" } { "light" "64" "classname" "light" "origin" "1336 1392 -840" } { "light" "64" "classname" "light" "origin" "1336 1464 -840" } { "light" "64" "classname" "light" "origin" "1336 1536 -840" } { "light" "64" "classname" "light" "origin" "1336 1616 -840" } { "light" "64" "classname" "light" "origin" "1336 1680 -840" } { "classname" "light" "light" "64" "origin" "1464 1544 -840" } { "classname" "light" "light" "80" "origin" "-708 -288 -232" } { "light" "80" "classname" "light" "origin" "48 -1248 -168" } { "light" "80" "classname" "light" "origin" "48 -1248 -264" } { "light" "80" "classname" "light" "origin" "92 -1144 -228" } { "light" "80" "classname" "light" "origin" "56 -1180 -228" } { "light" "80" "classname" "light" "origin" "-164 -1092 -260" } { "origin" "100 -1204 -168" "classname" "light" "light" "80" } { "origin" "-276 -1052 -260" "light" "80" "classname" "light" } { "classname" "light" "light" "80" "origin" "-224 -1108 -260" } { "origin" "-224 -1108 -200" "light" "80" "classname" "light" } { "light" "80" "classname" "light" "origin" "-112 -1044 -260" } { "classname" "light" "light" "80" "origin" "-276 -1052 -200" } { "model" "*19" "target" "t153" "classname" "func_explosive" "health" "10" "mass" "100" "dmg" "1" } { "classname" "trigger_relay" "targetname" "t133" "target" "t134" "origin" "-436 -280 -212" "delay" "3" } { "classname" "trigger_relay" "target" "t133" "targetname" "t108" "origin" "-456 -280 -212" "delay" "3" } { "model" "*20" "classname" "func_door" "angle" "-1" "wait" "-1" "targetname" "t134" "_minlight" ".5" } { "model" "*21" "classname" "func_door" "angle" "90" "wait" "-1" "lip" "-2" "targetname" "t133" } { "classname" "trigger_relay" "target" "t132" "delay" "2" "targetname" "t108" "origin" "-536 -8 -168" } { "origin" "-756 -236 -232" "light" "80" "classname" "light" } { "origin" "-736 -336 -256" "light" "64" "classname" "light" } { "light" "64" "classname" "light" "origin" "-532 -269 -272" } { "classname" "light" "light" "64" "origin" "-556 -269 -272" } { "style" "34" "spawnflags" "1" "light" "120" "classname" "light" "targetname" "t131" "origin" "-480 -280 -64" } { "style" "34" "spawnflags" "1" "light" "120" "classname" "light" "targetname" "t131" "origin" "-608 -284 -64" } { "style" "34" "classname" "light" "light" "120" "targetname" "t131" "spawnflags" "1" "origin" "-412 -280 -64" } { "classname" "trigger_relay" "targetname" "t109" "delay" "1" "target" "t131" "origin" "-552 -292 -76" } { "classname" "light" "light" "80" "origin" "-752 -236 -160" } { "style" "35" "classname" "light" "light" "150" "targetname" "t108" "origin" "-488 -312 -224" "spawnflags" "1" } { "model" "*22" "targetname" "t22" "spawnflags" "1" "angle" "-2" "classname" "func_water" "lip" "32" "speed" "50" "wait" "240" } { "origin" "1016 -760 -368" "classname" "item_enviro" "target" "t149" } { "style" "1" "targetname" "t130" "classname" "func_areaportal" } { "model" "*23" "target" "t130" "_minlight" ".2" "angle" "-1" "classname" "func_door" } { "origin" "1688 1412 -784" "angle" "180" "classname" "info_player_start" } { "origin" "1108 32 -252" "classname" "misc_banner" "spawnflags" "2048" } { "origin" "-640 -24 -200" "light" "100" "classname" "light" } { "target" "t145" "origin" "-536 -472 -272" "spawnflags" "1" "angle" "90" "classname" "monster_gunner" "targetname" "t142" } { "classname" "light" "light" "150" "origin" "-640 -24 -296" } { "classname" "misc_satellite_dish" "angle" "0" "origin" "-640 -16 -236" "targetname" "t132" } { "origin" "496 608 -816" "classname" "item_health" } { "origin" "1696 944 -464" "classname" "item_health" } { "origin" "1696 896 -464" "classname" "item_health" } { "origin" "40 -1248 -288" "classname" "item_health" } { "origin" "-32 -1248 -288" "classname" "item_health" } { "classname" "light" "light" "150" "origin" "-520 -640 -200" } { "origin" "392 112 -608" "classname" "weapon_machinegun" } { "origin" "1280 1412 -704" "classname" "light" "light" "150" } { "origin" "1280 1604 -704" "classname" "light" "light" "150" } { "origin" "1288 1220 -704" "light" "150" "classname" "light" } { "origin" "1160 1404 -704" "classname" "light" "light" "150" } { "origin" "1160 1596 -704" "classname" "light" "light" "150" } { "origin" "1168 1212 -704" "light" "150" "classname" "light" } { "origin" "24 -552 -232" "classname" "item_health" } { "origin" "800 -160 -432" "classname" "ammo_shells" } { "origin" "456 776 -600" "classname" "item_health_small" } { "origin" "456 720 -600" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "328 -792 -240" "classname" "ammo_bullets" } { "origin" "320 -856 -232" "spawnflags" "1" "classname" "monster_soldier_light" "angle" "90" } { "origin" "224 -800 -232" "spawnflags" "1" "angle" "45" "classname" "monster_soldier_light" } { "model" "*24" "target" "t129" "classname" "trigger_once" } { "origin" "328 -680 -248" "targetname" "t128" "spawnflags" "1" "classname" "point_combat" } { "origin" "296 -480 -232" "spawnflags" "1" "targetname" "t129" "target" "t128" "angle" "270" "classname" "monster_soldier_light" } { "origin" "936 196 -376" "light" "80" "classname" "light" } { "origin" "1148 -576 -496" "target" "t126" "targetname" "t125" "classname" "point_combat" } { "origin" "1376 -576 -496" "target" "t125" "targetname" "t121" "classname" "point_combat" } { "origin" "1072 -752 -496" "spawnflags" "1" "targetname" "t126" "classname" "point_combat" } { "targetname" "t124" "spawnflags" "1" "target" "t121" "origin" "1376 -424 -480" "angle" "270" "classname" "monster_infantry" } { "model" "*25" "target" "t124" "classname" "trigger_once" } { "origin" "568 -472 -496" "spawnflags" "1" "targetname" "t120" "classname" "point_combat" } { "origin" "536 -576 -496" "target" "t120" "targetname" "t119" "classname" "point_combat" } { "origin" "400 688 -624" "classname" "misc_explobox" } { "origin" "96 304 -816" "targetname" "t118" "classname" "point_combat" } { "spawnflags" "1" "origin" "638 236 -440" "targetname" "t117" "classname" "point_combat" "pathtarget" "lita" } { "origin" "1880 800 -480" "spawnflags" "1" "targetname" "t116" "classname" "point_combat" } { "origin" "1984 576 -472" "targetname" "t114" "spawnflags" "1" "classname" "point_combat" } { "origin" "1904 632 -472" "targetname" "t115" "classname" "point_combat" } { "style" "36" "spawnflags" "1" "light" "200" "classname" "light" "targetname" "t112" "origin" "-756 96 -228" } { "style" "37" "spawnflags" "1" "light" "200" "classname" "light" "targetname" "t111" "origin" "-756 -32 -228" } { "style" "38" "classname" "light" "light" "200" "spawnflags" "1" "targetname" "t113" "origin" "-756 224 -228" } { "classname" "trigger_relay" "target" "t113" "delay" "3.4" "origin" "-788 224 -96" "targetname" "t108" } { "classname" "trigger_relay" "target" "t112" "delay" "3.2" "origin" "-792 96 -96" "targetname" "t108" } { "classname" "trigger_relay" "target" "t111" "delay" "3" "origin" "-788 -32 -96" "targetname" "t108" } { "style" "37" "spawnflags" "1" "origin" "-428 32 32" "classname" "light" "light" "200" "_color" "1.000000 0.019608 0.000000" "targetname" "t111" } { "style" "36" "spawnflags" "1" "origin" "-428 96 32" "classname" "light" "light" "200" "_color" "1.000000 0.019608 0.000000" "targetname" "t112" } { "style" "38" "spawnflags" "1" "origin" "-492 224 32" "classname" "light" "light" "200" "_color" "1.000000 0.019608 0.000000" "targetname" "t113" } { "style" "38" "spawnflags" "1" "origin" "-492 288 32" "classname" "light" "light" "200" "_color" "1.000000 0.019608 0.000000" "targetname" "t113" } { "style" "38" "spawnflags" "1" "origin" "-816 224 -96" "classname" "light" "light" "200" "_color" "1.000000 0.019608 0.000000" "targetname" "t113" } { "style" "36" "spawnflags" "1" "origin" "-816 96 -96" "classname" "light" "light" "200" "_color" "1.000000 0.019608 0.000000" "targetname" "t112" } { "style" "37" "spawnflags" "1" "origin" "-816 -32 -96" "classname" "light" "light" "200" "_color" "1.000000 0.019608 0.000000" "targetname" "t111" } { "origin" "-832 -32 -96" "classname" "light" "light" "64" "_color" "1.000000 0.019608 0.000000" } { "classname" "trigger_relay" "delay" "2" "targetname" "t108" "target" "t110" "origin" "-536 -176 -88" } { "style" "39" "light" "150" "classname" "light" "spawnflags" "1" "targetname" "t110" "origin" "-484 -164 -88" } { "style" "39" "light" "150" "classname" "light" "spawnflags" "1" "targetname" "t110" "origin" "-420 -164 -88" } { "style" "39" "classname" "light" "light" "150" "spawnflags" "1" "targetname" "t110" "origin" "-600 -168 -88" } { "style" "40" "spawnflags" "1" "light" "150" "classname" "light" "targetname" "t109" "origin" "-476 -272 -120" } { "style" "40" "spawnflags" "1" "light" "150" "classname" "light" "targetname" "t109" "origin" "-612 -264 -120" } { "style" "40" "classname" "light" "light" "150" "spawnflags" "1" "targetname" "t109" "origin" "-412 -272 -120" } { "classname" "trigger_relay" "targetname" "t108" "delay" "1" "target" "t109" "origin" "-420 -344 -144" } { "light" "64" "classname" "light" "origin" "-476 -164 -276" } { "style" "35" "spawnflags" "1" "light" "150" "classname" "light" "targetname" "t108" "origin" "-376 -184 -236" } { "style" "35" "spawnflags" "1" "light" "150" "classname" "light" "targetname" "t108" "origin" "-456 -192 -236" } { "style" "35" "classname" "light" "light" "150" "spawnflags" "1" "targetname" "t108" "origin" "-604 -316 -220" } { "light" "64" "classname" "light" "origin" "-412 32 32" "_color" "1.000000 0.019608 0.000000" } { "light" "64" "classname" "light" "origin" "-412 96 32" "_color" "1.000000 0.019608 0.000000" } { "light" "64" "classname" "light" "origin" "-476 224 32" "_color" "1.000000 0.019608 0.000000" } { "light" "64" "classname" "light" "origin" "-476 288 32" "_color" "1.000000 0.019608 0.000000" } { "light" "64" "classname" "light" "origin" "-832 224 -96" "_color" "1.000000 0.019608 0.000000" } { "light" "64" "classname" "light" "origin" "-832 96 -96" "_color" "1.000000 0.019608 0.000000" } { "style" "37" "light" "200" "classname" "light" "origin" "-428 -32 32" "spawnflags" "1" "_color" "1.000000 0.019608 0.000000" "targetname" "t111" } { "classname" "light" "light" "64" "origin" "-412 -32 32" "_color" "1.000000 0.019608 0.000000" } { "classname" "info_player_start" "angle" "180" "targetname" "base2b" "origin" "2368 896 -296" } { "target" "t116" "origin" "1752 792 -464" "spawnflags" "1" "targetname" "t107" "classname" "monster_soldier" "angle" "315" } { "origin" "1824 928 -464" "spawnflags" "1" "targetname" "t107" "angle" "270" "classname" "monster_soldier" } { "model" "*26" "target" "t107" "classname" "trigger_once" } { "model" "*27" "target" "t106" "classname" "trigger_once" } { "model" "*28" "target" "t105" "classname" "trigger_multiple" } { "spawnflags" "1" "origin" "1744 600 -464" "targetname" "t105" "angle" "180" "classname" "monster_infantry" } { "target" "t115" "origin" "1920 912 -464" "spawnflags" "1" "targetname" "t106" "classname" "monster_soldier_light" "angle" "270" } { "target" "t114" "origin" "1984 912 -464" "spawnflags" "1" "targetname" "t106" "angle" "270" "classname" "monster_soldier_light" } { "origin" "1952 800 -480" "target" "t104" "targetname" "t103" "classname" "point_combat" } { "origin" "1952 600 -480" "targetname" "t104" "classname" "point_combat" } { "origin" "1952 600 -464" "spawnflags" "1" "target" "t103" "angle" "180" "classname" "monster_soldier_light" } { "spawnflags" "1" "origin" "64 -508 -232" "angle" "90" "classname" "monster_infantry" } { "random" "0.2" "wait" "2" "origin" "-168 -328 -164" "targetname" "t100" "target" "t101" "classname" "func_timer" } { "origin" "-148 -312 -180" "targetname" "t101" "sounds" "1" "angle" "-2" "classname" "target_splash" } { "origin" "-192 -336 -200" "dmg" "1" "delay" ".3" "targetname" "t100" "classname" "target_explosion" } { "origin" "-144 -328 -200" "dmg" "1" "delay" ".3" "targetname" "t100" "classname" "target_explosion" } { "origin" "-216 -344 -160" "dmg" "1" "delay" ".7" "targetname" "t100" "classname" "target_explosion" } { "model" "*29" "spawnflags" "2048" "target" "t100" "classname" "trigger_once" } { "origin" "-56 -248 -140" "classname" "light" "light" "64" } { "origin" "-184 -248 -140" "light" "64" "classname" "light" } { "origin" "-192 -236 -232" "angle" "270" "classname" "monster_parasite" "target" "t136" } { "origin" "-372 -1156 -288" "target" "t98" "targetname" "t97" "classname" "path_corner" } { "origin" "-532 -1156 -288" "targetname" "t98" "classname" "path_corner" } { "model" "*30" "target" "t96" "classname" "trigger_once" } { "spawnflags" "1" "origin" "768 -376 -232" "target" "t94" "angle" "90" "classname" "monster_soldier_light" } { "origin" "768 -240 -248" "target" "t95" "targetname" "t94" "classname" "path_corner" } { "origin" "768 -424 -248" "targetname" "t95" "target" "t94" "classname" "path_corner" } { "spawnflags" "1" "origin" "632 -136 -232" "target" "t91" "angle" "90" "classname" "monster_soldier_light" } { "origin" "632 -88 -248" "target" "t92" "targetname" "t91" "classname" "path_corner" } { "origin" "632 208 -248" "target" "t91" "targetname" "t90" "classname" "path_corner" } { "origin" "832 208 -248" "targetname" "t93" "target" "t90" "classname" "path_corner" } { "origin" "648 192 -248" "target" "t93" "targetname" "t92" "classname" "path_corner" } { "spawnflags" "1793" "origin" "984 160 -232" "target" "t89" "angle" "180" "classname" "monster_soldier_light" } { "origin" "680 -104 -248" "target" "t86" "targetname" "t85" "classname" "path_corner" } { "origin" "784 -96 -248" "target" "t87" "targetname" "t86" "classname" "path_corner" } { "origin" "680 -96 -248" "target" "t88" "targetname" "t87" "classname" "path_corner" } { "origin" "680 152 -248" "target" "t89" "targetname" "t88" "classname" "path_corner" } { "origin" "944 160 -248" "targetname" "t89" "target" "t84" "classname" "path_corner" } { "origin" "672 160 -248" "target" "t85" "targetname" "t84" "classname" "path_corner" } { "model" "*31" "target" "t83" "classname" "trigger_once" } { "spawnflags" "1" "target" "t117" "origin" "640 -144 -424" "targetname" "t83" "angle" "90" "classname" "monster_infantry" "item" "ammo_bullets" } { "origin" "1016 -576 -480" "target" "t68" "angle" "0" "classname" "monster_infantry" } { "origin" "1072 -576 -496" "target" "t69" "targetname" "t68" "classname" "path_corner" } { "origin" "768 -576 -496" "targetname" "t69" "target" "t68" "classname" "path_corner" } { "target" "t119" "spawnflags" "1" "origin" "536 -1072 -480" "targetname" "t67" "angle" "90" "classname" "monster_soldier_light" } { "model" "*32" "target" "t67" "classname" "trigger_once" } { "spawnflags" "257" "origin" "176 672 -600" "target" "t66" "angle" "180" "classname" "monster_soldier_light" } { "origin" "208 672 -616" "target" "t66" "targetname" "t65" "classname" "path_corner" } { "origin" "24 672 -616" "targetname" "t66" "target" "t65" "classname" "path_corner" } { "spawnflags" "1" "origin" "448 256 -600" "target" "t64" "angle" "270" "classname" "monster_soldier_light" } { "origin" "448 152 -616" "target" "t63" "targetname" "t62" "classname" "path_corner" } { "origin" "104 152 -616" "target" "t62" "targetname" "t63" "classname" "path_corner" } { "targetname" "t64" "origin" "448 208 -616" "target" "t62" "classname" "path_corner" } { "origin" "448 376 -616" "target" "t62" "targetname" "t63" "classname" "path_corner" } { "classname" "misc_explobox" "origin" "436 596 -624" } { "model" "*33" "classname" "trigger_once" "target" "t61" } { "spawnflags" "1" "classname" "monster_infantry" "angle" "0" "targetname" "t61" "origin" "352 104 -808" } { "spawnflags" "1" "angle" "90" "origin" "416 288 -904" "classname" "monster_parasite" "targetname" "t29" } { "origin" "420 440 -836" "light" "80" "classname" "light" } { "classname" "light" "light" "80" "origin" "96 456 -836" } { "target" "t118" "spawnflags" "257" "classname" "monster_parasite" "origin" "96 616 -904" "angle" "270" } { "origin" "360 1008 -816" "target" "t59" "targetname" "t60" "classname" "path_corner" } { "origin" "576 1112 -816" "target" "t59" "targetname" "t60" "classname" "path_corner" } { "origin" "376 1112 -816" "target" "t60" "targetname" "t59" "classname" "path_corner" } { "origin" "432 1008 -800" "targetname" "t57" "target" "t59" "classname" "path_corner" } { "item" "ammo_bullets" "spawnflags" "1" "classname" "monster_infantry" "angle" "0" "target" "t25" "origin" "832 704 -800" } { "classname" "monster_soldier_light" "angle" "90" "targetname" "t46" "origin" "880 1224 -808" } { "spawnflags" "1" "classname" "monster_soldier_light" "target" "t54" "origin" "312 960 -800" } { "spawnflags" "1" "classname" "monster_soldier_light" "angle" "90" "target" "t57" "origin" "400 960 -800" } { "classname" "path_corner" "targetname" "t53" "target" "t54" "origin" "320 1152 -816" } { "classname" "path_corner" "target" "t52" "targetname" "t54" "origin" "296 1000 -816" } { "classname" "path_corner" "targetname" "t51" "target" "t53" "origin" "736 1168 -816" } { "classname" "path_corner" "target" "t51" "targetname" "t52" "origin" "304 1160 -816" } { "classname" "path_corner" "targetname" "t51" "target" "t53" "origin" "312 1000 -816" } { "model" "*34" "classname" "trigger_once" "target" "t46" } { "light" "180" "classname" "light" "origin" "896 704 -736" } { "classname" "light" "light" "120" "origin" "-8 1168 -824" } { "light" "150" "classname" "light" "origin" "128 1752 -760" } { "classname" "light" "light" "150" "origin" "136 1592 -760" } { "classname" "path_corner" "targetname" "t36" "target" "t37" "origin" "1296 1288 -928" } { "classname" "path_corner" "target" "t40" "targetname" "t41" "origin" "1416 1640 -928" } { "classname" "path_corner" "targetname" "t40" "target" "t41" "origin" "1416 1440 -928" } { "classname" "path_corner" "targetname" "t42" "target" "t43" "origin" "1448 1344 -928" } { "classname" "path_corner" "target" "t42" "targetname" "t43" "origin" "1448 1192 -928" } { "classname" "path_corner" "target" "t36" "targetname" "t37" "origin" "1392 1152 -928" } { "classname" "ammo_bullets" "origin" "600 864 -816" } { "classname" "ammo_bullets" "origin" "600 808 -816" } { "origin" "1168 744 -784" "light" "80" "classname" "light" } { "classname" "weapon_shotgun" "origin" "1168 752 -808" } { "light" "150" "classname" "light" "origin" "1416 1412 -704" } { "light" "150" "classname" "light" "origin" "1416 1604 -704" } { "classname" "light" "light" "150" "origin" "1424 1220 -704" } { "_color" "0.458824 0.823529 1.000000" "light" "80" "classname" "light" "origin" "1280 1128 -832" } { "_color" "0.458824 0.823529 1.000000" "light" "80" "classname" "light" "origin" "1152 1128 -832" } { "_color" "0.458824 0.823529 1.000000" "light" "80" "classname" "light" "origin" "1152 1736 -832" } { "_color" "0.458824 0.823529 1.000000" "light" "80" "classname" "light" "origin" "1280 1736 -832" } { "_color" "0.458824 0.823529 1.000000" "light" "80" "classname" "light" "origin" "1408 1736 -832" } { "classname" "light" "light" "80" "_color" "0.458824 0.823529 1.000000" "origin" "1408 1128 -832" } { "classname" "target_changelevel" "targetname" "t35" "map" "base2$base3b" "origin" "2496 864 -248" } { "model" "*35" "angle" "360" "classname" "trigger_multiple" "target" "t35" } { "origin" "-424 -832 -200" "light" "150" "classname" "light" } { "target" "t97" "origin" "-332 -1156 -272" "classname" "monster_soldier" "angle" "45" } { "spawnflags" "256" "origin" "-336 -1256 -272" "classname" "monster_soldier" "angle" "90" } { "targetname" "t96" "origin" "-80 -1056 -272" "angle" "0" "classname" "monster_soldier" } { "origin" "560 -368 -488" "target" "t32" "targetname" "t31" "classname" "path_corner" } { "origin" "208 -368 -488" "targetname" "t32" "target" "t31" "classname" "path_corner" } { "spawnflags" "1" "origin" "480 -360 -472" "target" "t31" "classname" "monster_infantry" } { "origin" "384 392 -736" "classname" "light" "light" "150" } { "origin" "384 512 -736" "classname" "light" "light" "150" } { "origin" "384 656 -736" "classname" "light" "light" "150" } { "origin" "384 768 -736" "classname" "light" "light" "150" } { "origin" "280 712 -736" "light" "150" "classname" "light" } { "model" "*36" "classname" "trigger_once" "target" "t29" } { "classname" "light" "light" "80" "origin" "1072 696 -784" } { "classname" "path_corner" "targetname" "t24" "target" "t25" "origin" "752 696 -816" } { "classname" "path_corner" "target" "t24" "targetname" "t25" "origin" "928 688 -816" } { "origin" "1012 1152 -760" "light" "200" "classname" "light" } { "origin" "316 480 -632" "light" "200" "classname" "light" } { "light" "64" "classname" "light" "origin" "880 1248 -720" } { "light" "64" "classname" "light" "origin" "880 1336 -720" } { "classname" "light" "light" "64" "origin" "1032 1336 -720" } { "classname" "light" "light" "200" "origin" "588 736 -760" } { "light" "64" "classname" "light" "origin" "644 1216 -908" } { "classname" "light" "light" "64" "origin" "712 1216 -908" } { "light" "75" "classname" "light" "origin" "48 1216 -1004" } { "origin" "48 1128 -1004" "classname" "light" "light" "75" } { "light" "85" "classname" "light" "origin" "136 1152 -1004" } { "light" "75" "classname" "light" "origin" "100 1224 -976" } { "origin" "48 1216 -920" "classname" "light" "light" "100" } { "origin" "128 1224 -920" "classname" "light" "light" "100" } { "origin" "136 1152 -920" "classname" "light" "light" "100" } { "origin" "224 1120 -992" "classname" "light" "light" "64" } { "origin" "292 1128 -1016" "classname" "light" "light" "64" } { "origin" "292 1192 -1016" "classname" "light" "light" "64" } { "origin" "356 1184 -1016" "classname" "light" "light" "64" } { "origin" "428 1120 -1016" "classname" "light" "light" "64" } { "origin" "428 1184 -1016" "classname" "light" "light" "64" } { "origin" "500 1176 -1016" "classname" "light" "light" "85" } { "light" "150" "classname" "light" "origin" "0 1672 -756" } { "light" "64" "classname" "light" "origin" "144 1504 -756" } { "style" "6" "classname" "light" "light" "170" "origin" "72 1176 -824" } { "origin" "240 1672 -756" "classname" "light" "light" "150" } { "origin" "144 1376 -756" "classname" "light" "light" "64" } { "origin" "16 1376 -756" "classname" "light" "light" "64" "style" "4" } { "origin" "1248 -576 -388" "classname" "light" "light" "64" } { "origin" "1376 -576 -388" "classname" "light" "light" "64" } { "origin" "1376 -448 -388" "classname" "light" "light" "64" } { "origin" "1376 -320 -388" "classname" "light" "light" "64" } { "model" "*37" "classname" "func_plat" "lip" "132" "_minlight" ".2" } { "classname" "light" "light" "150" "origin" "832 336 -152" } { "origin" "832 336 -352" "light" "150" "classname" "light" } { "origin" "736 224 -424" "classname" "light" "light" "64" } { "light" "100" "classname" "light" "origin" "1200 -576 -448" } { "light" "150" "classname" "light" "origin" "1088 -576 -416" } { "light" "80" "classname" "light" "origin" "288 -880 -160" } { "light" "80" "classname" "light" "origin" "280 -888 -208" } { "light" "80" "classname" "light" "origin" "224 -888 -208" } { "light" "150" "classname" "light" "origin" "548 -572 -132" } { "origin" "1048 -904 -352" "classname" "light" "light" "200" } { "light" "130" "classname" "light" "origin" "584 -1076 -240" } { "light" "130" "classname" "light" "origin" "584 -1076 -80" } { "classname" "ammo_bullets" "origin" "952 -1096 -488" } { "classname" "ammo_bullets" "origin" "1032 -1096 -488" } { "classname" "misc_explobox" "origin" "1040 -840 -512" } { "classname" "misc_explobox" "origin" "1120 -896 -512" } { "light" "130" "classname" "light" "origin" "904 -880 -440" } { "light" "130" "classname" "light" "origin" "848 -992 -440" } { "light" "130" "classname" "light" "origin" "760 -1040 -440" } { "light" "130" "classname" "light" "origin" "616 -1024 -440" } { "light" "130" "classname" "light" "origin" "552 -912 -440" } { "light" "130" "classname" "light" "origin" "552 -800 -440" } { "light" "130" "classname" "light" "origin" "616 -704 -440" } { "classname" "light" "light" "130" "origin" "1080 -1016 -440" } { "classname" "light" "light" "250" "origin" "1048 -912 -216" } { "light" "250" "classname" "light" "origin" "1048 -728 -216" } { "light" "150" "classname" "light" "origin" "1088 -1036 -352" } { "classname" "light" "light" "200" "origin" "1048 -576 -296" } { "light" "80" "classname" "light" "origin" "1120 -992 -16" } { "light" "80" "classname" "light" "origin" "1056 -800 -16" } { "light" "80" "classname" "light" "origin" "992 -736 -16" } { "light" "80" "classname" "light" "origin" "992 -608 -16" } { "classname" "light" "light" "80" "origin" "992 -992 -16" } { "model" "*38" "classname" "func_button" "angle" "90" "wait" "240" "target" "t22" } { "light" "80" "classname" "light" "origin" "828 -892 -652" } { "light" "80" "classname" "light" "origin" "740 -1000 -652" } { "light" "80" "classname" "light" "origin" "744 -752 -652" } { "classname" "light" "light" "80" "origin" "632 -892 -652" } { "model" "*39" "origin" "716 -1062 -652" "classname" "func_door_rotating" "_minlight" ".2" "spawnflags" "1" "wait" "240" "distance" "90" "targetname" "t22" "speed" "30" } { "origin" "1484 328 -284" "classname" "light" "light" "150" } { "origin" "1708 328 -284" "light" "150" "classname" "light" } { "origin" "992 -160 -96" "classname" "light" "light" "80" } { "origin" "864 -160 -96" "classname" "light" "light" "80" } { "origin" "992 -32 -96" "light" "64" "classname" "light" } { "light" "250" "classname" "light" "origin" "800 48 -160" } { "classname" "light" "light" "200" "origin" "800 48 -360" } { "light" "64" "classname" "light" "origin" "768 -256 -160" } { "light" "64" "classname" "light" "origin" "704 -96 -44" } { "origin" "616 32 -424" "light" "64" "classname" "light" } { "origin" "616 160 -424" "light" "64" "classname" "light" } { "light" "64" "classname" "light" "origin" "832 32 -44" } { "light" "64" "classname" "light" "origin" "704 32 -44" } { "light" "64" "classname" "light" "origin" "704 160 -44" } { "light" "64" "classname" "light" "origin" "832 160 -44" } { "classname" "light" "light" "64" "origin" "768 -384 -160" } { "origin" "2080 592 -416" "classname" "light" "light" "130" } { "origin" "1952 592 -416" "classname" "light" "light" "130" } { "origin" "1832 592 -416" "classname" "light" "light" "130" } { "origin" "1696 592 -416" "classname" "light" "light" "130" } { "origin" "1568 592 -416" "classname" "light" "light" "130" } { "origin" "1920 696 -416" "light" "130" "classname" "light" } { "origin" "1856 928 -392" "classname" "light" "light" "150" } { "origin" "1856 816 -392" "light" "150" "classname" "light" } { "origin" "2272 800 -84" "light" "64" "classname" "light" } { "origin" "2292 806 -236" "light" "80" "classname" "light" } { "origin" "2292 992 -312" "light" "64" "classname" "light" } { "origin" "2208 1076 -312" "light" "64" "classname" "light" } { "origin" "2208 720 -248" "classname" "light" "light" "64" } { "origin" "2208 720 -312" "light" "64" "classname" "light" } { "origin" "2080 720 -312" "classname" "light" "light" "64" } { "origin" "2080 1076 -312" "light" "64" "classname" "light" } { "origin" "2272 992 -84" "classname" "light" "light" "64" } { "origin" "2208 1076 -248" "light" "64" "classname" "light" } { "origin" "1696 592 -272" "classname" "light" "light" "64" } { "origin" "1952 592 -272" "classname" "light" "light" "64" } { "origin" "2080 592 -272" "classname" "light" "light" "64" } { "origin" "1568 592 -272" "light" "64" "classname" "light" } { "origin" "1504 592 -404" "classname" "light" "light" "120" } { "origin" "1684 800 -404" "classname" "light" "light" "120" } { "origin" "1684 992 -404" "classname" "light" "light" "120" } { "origin" "1952 1068 -404" "classname" "light" "light" "120" } { "origin" "2156 592 -404" "light" "120" "classname" "light" } { "classname" "func_group" } { "classname" "light" "light" "120" "origin" "2152 896 -248" } { "classname" "light" "light" "120" "origin" "2280 896 -248" } { "origin" "2360 896 -196" "classname" "light" "light" "64" } { "origin" "2488 896 -196" "light" "64" "classname" "light" } { "model" "*40" "_minlight" ".2" "angle" "-1" "classname" "func_door" } { "origin" "2024 896 -248" "light" "120" "classname" "light" } { "model" "*41" "_minlight" ".2" "spawnflags" "1" "classname" "func_plat" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "light" "light" "180" "origin" "520 1088 -736" } { "origin" "712 1024 -912" "classname" "light" "light" "150" "_color" "0.000000 0.666667 1.000000" } { "origin" "748 872 -1016" "classname" "light" "light" "64" } { "light" "64" "classname" "light" "origin" "972 888 -1016" } { "light" "64" "classname" "light" "origin" "916 816 -1016" } { "light" "64" "classname" "light" "origin" "988 792 -1016" } { "light" "64" "classname" "light" "origin" "980 728 -1016" } { "light" "64" "classname" "light" "origin" "960 600 -992" } { "light" "64" "classname" "light" "origin" "804 648 -1016" } { "light" "64" "classname" "light" "origin" "788 600 -1016" } { "light" "64" "classname" "light" "origin" "724 640 -1016" } { "light" "64" "classname" "light" "origin" "740 712 -1016" } { "light" "64" "classname" "light" "origin" "748 792 -1016" } { "light" "64" "classname" "light" "origin" "812 800 -1016" } { "light" "64" "classname" "light" "origin" "748 944 -1016" } { "light" "85" "classname" "light" "origin" "728 984 -992" } { "light" "85" "classname" "light" "origin" "660 1000 -1016" } { "light" "85" "classname" "light" "origin" "588 1024 -1016" } { "light" "85" "classname" "light" "origin" "500 1016 -1016" } { "light" "85" "classname" "light" "origin" "500 1104 -1016" } { "light" "100" "classname" "light" "origin" "48 1128 -920" } { "light" "85" "classname" "light" "origin" "564 1176 -1016" } { "light" "85" "classname" "light" "origin" "636 1176 -1016" } { "light" "85" "classname" "light" "origin" "700 1168 -1016" } { "light" "85" "classname" "light" "origin" "756 1168 -1016" } { "light" "85" "classname" "light" "origin" "828 1168 -1016" } { "light" "85" "classname" "light" "origin" "900 1168 -1016" } { "light" "85" "classname" "light" "origin" "964 1168 -1016" } { "light" "85" "classname" "light" "origin" "836 1088 -1016" } { "light" "85" "classname" "light" "origin" "912 1088 -992" } { "light" "85" "classname" "light" "origin" "1004 1088 -1016" } { "light" "64" "classname" "light" "origin" "988 1000 -1016" } { "light" "64" "classname" "light" "origin" "900 1000 -1016" } { "light" "85" "classname" "light" "origin" "804 1000 -1016" } { "classname" "light" "light" "64" "origin" "884 896 -1016" } { "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" "origin" "704 1152 -912" } { "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" "origin" "1432 1688 -912" } { "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" "origin" "1344 1600 -912" } { "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" "origin" "1448 1584 -912" } { "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" "origin" "1448 1488 -912" } { "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" "origin" "1336 1488 -912" } { "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" "origin" "1336 1376 -912" } { "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" "origin" "1432 1368 -912" } { "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" "origin" "1440 1248 -912" } { "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" "origin" "1336 1248 -912" } { "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" "origin" "1192 1152 -912" } { "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" "origin" "1336 1152 -912" } { "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" "origin" "1440 1160 -912" } { "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" "origin" "824 1024 -912" } { "origin" "952 704 -912" "classname" "light" "light" "150" "_color" "0.000000 0.666667 1.000000" } { "origin" "960 808 -912" "classname" "light" "light" "150" "_color" "0.000000 0.666667 1.000000" } { "origin" "864 808 -912" "classname" "light" "light" "150" "_color" "0.000000 0.666667 1.000000" } { "origin" "784 904 -908" "classname" "light" "light" "150" "_color" "0.000000 0.666667 1.000000" } { "origin" "1336 1704 -912" "classname" "light" "light" "150" "_color" "0.000000 0.666667 1.000000" } { "origin" "960 1152 -912" "classname" "light" "light" "150" "_color" "0.000000 0.666667 1.000000" } { "origin" "832 1152 -912" "classname" "light" "light" "150" "_color" "0.000000 0.666667 1.000000" } { "origin" "576 1144 -912" "classname" "light" "light" "150" "_color" "0.000000 0.666667 1.000000" } { "origin" "576 1024 -912" "classname" "light" "light" "150" "_color" "0.000000 0.666667 1.000000" } { "origin" "840 664 -912" "_color" "0.000000 0.666667 1.000000" "light" "150" "classname" "light" } { "origin" "1152 704 -688" "light" "130" "classname" "light" "style" "4" } { "light" "80" "classname" "light" "origin" "88 392 -360" } { "light" "80" "classname" "light" "origin" "80 256 -360" } { "light" "250" "classname" "light" "origin" "424 376 -440" } { "light" "250" "classname" "light" "origin" "416 216 -440" } { "light" "250" "classname" "light" "origin" "64 208 -440" } { "light" "250" "classname" "light" "origin" "64 432 -440" } { "light" "250" "classname" "light" "origin" "232 640 -440" } { "origin" "316 352 -632" "classname" "light" "light" "200" } { "origin" "196 352 -632" "classname" "light" "light" "200" } { "origin" "196 480 -632" "classname" "light" "light" "200" } { "origin" "308 1664 -824" "light" "200" "classname" "light" } { "light" "180" "classname" "light" "origin" "672 960 -768" } { "origin" "888 1072 -736" "classname" "light" "light" "180" } { "origin" "760 720 -736" "classname" "light" "light" "180" } { "origin" "626 1100 -768" "light" "100" "classname" "light" } { "origin" "424 984 -768" "light" "150" "classname" "light" } { "classname" "light" "light" "150" "origin" "1568 -248 -48" } { "classname" "light" "light" "150" "origin" "1440 -248 -48" } { "light" "150" "classname" "light" "origin" "1312 -248 -48" } { "light" "150" "classname" "light" "origin" "1440 -208 -296" } { "light" "150" "classname" "light" "origin" "1568 -208 -296" } { "light" "150" "classname" "light" "origin" "1712 -248 -48" } { "classname" "light" "light" "150" "origin" "1312 -208 -296" } { "origin" "1012 -64 -432" "classname" "light" "light" "64" } { "light" "64" "classname" "light" "origin" "1012 128 -432" } { "classname" "light" "light" "64" "origin" "856 -20 -372" } { "classname" "light" "light" "200" "origin" "720 128 -288" } { "origin" "-544 -1152 -184" "classname" "light" "light" "64" } { "origin" "-544 -1024 -184" "classname" "light" "light" "64" } { "origin" "-416 -1152 -184" "light" "64" "classname" "light" } { "origin" "-544 -928 -224" "light" "130" "classname" "light" } { "origin" "-448 -948 -288" "classname" "light" "light" "64" } { "origin" "-640 -948 -288" "light" "64" "classname" "light" } { "classname" "info_player_start" "angle" "180" "origin" "1720 1412 -688" "targetname" "base2a" } { "model" "*42" "target" "t102" "angle" "-1" "classname" "func_door" "_minlight" ".2" } { "origin" "-72 -376 -152" "classname" "light" "light" "100" } { "origin" "248 -376 -152" "classname" "light" "light" "100" } { "light" "150" "classname" "light" "origin" "-648 -712 -192" } { "origin" "-784 -728 -192" "classname" "light" "light" "150" } { "origin" "-784 -624 -192" "classname" "light" "light" "150" } { "origin" "-792 -504 -192" "classname" "light" "light" "150" } { "origin" "-544 -456 -144" "classname" "light" "light" "200" } { "light" "100" "classname" "light" "origin" "-416 -240 -144" } { "light" "100" "classname" "light" "origin" "-544 -232 -144" } { "light" "100" "classname" "light" "origin" "-672 -272 -144" } { "classname" "light" "light" "64" "origin" "-372 -1056 -288" } { "light" "64" "classname" "light" "origin" "-372 -1248 -288" } { "classname" "light" "light" "150" "origin" "-360 -416 -144" } { "light" "150" "classname" "light" "origin" "-712 -448 -144" } { "origin" "724 -580 -180" "classname" "light" "light" "150" } { "origin" "640 -1076 -412" "classname" "light" "light" "130" } { "origin" "924 -1056 -44" "classname" "light" "light" "80" } { "origin" "1088 -576 -132" "classname" "light" "light" "150" } { "origin" "844 -576 -132" "classname" "light" "light" "150" } { "origin" "248 72 -488" "light" "150" "classname" "light" } { "origin" "352 320 -360" "classname" "light" "light" "80" } { "origin" "160 192 -360" "classname" "light" "light" "80" } { "origin" "384 560 -408" "classname" "light" "light" "150" } { "origin" "416 744 -512" "classname" "light" "light" "150" } { "origin" "416 632 -440" "classname" "light" "light" "250" } { "origin" "160 160 -504" "classname" "light" "light" "175" } { "origin" "344 160 -504" "classname" "light" "light" "175" } { "origin" "288 -576 -152" "classname" "light" "light" "80" } { "origin" "472 -576 -152" "light" "80" "classname" "light" } { "origin" "808 -576 -416" "classname" "light" "light" "150" } { "origin" "952 -576 -416" "classname" "light" "light" "150" } { "origin" "1376 -248 -448" "classname" "light" "light" "100" } { "origin" "784 -536 -336" "light" "150" "classname" "light" } { "classname" "light" "light" "150" "origin" "256 -144 -400" } { "classname" "light" "light" "64" "origin" "1720 1404 -276" } { "light" "80" "classname" "light" "origin" "1732 1576 -508" } { "light" "120" "classname" "light" "origin" "1712 1248 -472" } { "classname" "light" "light" "150" "origin" "1880 1408 -440" } { "origin" "1720 1404 -508" "light" "100" "classname" "light" } { "origin" "1504 1412 -756" "light" "80" "classname" "light" } { "classname" "light" "light" "80" "origin" "1720 1404 -380" } { "classname" "light" "light" "80" "origin" "1720 1408 -608" } { "classname" "light" "light" "80" "origin" "1720 1404 -708" } { "classname" "light" "light" "80" "origin" "1720 1404 -804" } { "classname" "light" "light" "80" "origin" "1608 1404 -756" } { "classname" "light" "origin" "1224 24 -304" "light" "250" } { "origin" "1504 372 -472" "light" "64" "classname" "light" } { "origin" "1696 372 -472" "classname" "light" "light" "64" } { "model" "*43" "spawnflags" "16" "target" "t143" "targetname" "t138" "classname" "func_door" "angle" "-1" "wait" "-1" "_minlight" ".1" } { "origin" "1600 368 -408" "light" "150" "classname" "light" } { "model" "*44" "classname" "func_wall" } { "classname" "func_group" } { "model" "*45" "spawnflags" "2048" "lip" "16" "wait" "-1" "target" "t108" "angle" "90" "classname" "func_button" } { "origin" "-556 -317 -272" "light" "64" "classname" "light" } { "origin" "-532 -317 -272" "classname" "light" "light" "64" } { "classname" "light" "light" "150" "origin" "128 1872 -776" } { "origin" "128 1872 -976" "light" "150" "classname" "light" } { "model" "*46" "targetname" "t44" "_minlight" ".3" "angle" "-2" "classname" "func_door" } { "model" "*47" "target" "t44" "angle" "90" "classname" "func_button" } { "model" "*48" "target" "t45" "classname" "trigger_multiple" } { "origin" "160 1896 -944" "map" "train$base3c" "targetname" "t45" "classname" "target_changelevel" } { "origin" "128 1872 -848" "targetname" "train" "angle" "270" "classname" "info_player_start" } yquake2-QUAKE2_8_40/stuff/mapfixes/baseq2/biggun.ent000066400000000000000000001233641465112212000222250ustar00rootroot00000000000000// FIXED ENTITY STRING (by BjossiAlfreds) // // 1. Removed unusable entity target_actor (2016) // // 2. Renamed field "duration" to "count" for target_earthquake (1377) // // This makes the earthquake last 15 seconds instead of just 5. // If you want the earthquake length to go back to the way it was, // simply remove this field line (1384) entirely. // // 3. Set "ambush" spawnflag on monster_boss2. // // Targeted monsters must spawn in ambsuh mode, otherwise the AI messes // up. Additionally this may fix a corner case with the monster slipping // into the void while pushed out of it's compartment. { "message" "Big Gun" "nextmap" "hangar1" "classname" "worldspawn" "sky" "unit7_" "sounds" "10" "spawnflags" "1792" } { "origin" "1752 648 -240" "classname" "ammo_bullets" "spawnflags" "1024" } { "origin" "1792 624 -240" "classname" "ammo_bullets" "spawnflags" "1024" } { "origin" "1792 664 -240" "spawnflags" "1536" "classname" "ammo_bullets" } { "model" "*1" "classname" "trigger_push" "angle" "270" "speed" "50" } { "classname" "monster_flyer" "angle" "270" "targetname" "t132" "spawnflags" "258" "origin" "1440 480 48" } { "angle" "270" "classname" "monster_flyer" "targetname" "t132" "spawnflags" "258" "origin" "1440 528 48" } { "classname" "monster_flyer" "angle" "270" "targetname" "t132" "spawnflags" "258" "origin" "1440 432 48" } { "classname" "light" "light" "80" "origin" "1440 448 56" } { "model" "*2" "classname" "func_explosive" "dmg" "1" "targetname" "t132" } { "classname" "trigger_relay" "delay" "1.3" "target" "t134" "targetname" "t133" "origin" "1504 320 -184" } { "noise" "world/klaxon2.wav" "attenuation" "-1" "classname" "target_speaker" "targetname" "t134" "origin" "1504 344 -184" } { "classname" "target_speaker" "attenuation" "-1" "noise" "world/klaxon2.wav" "targetname" "t133" "origin" "1536 344 -184" } { "classname" "trigger_relay" "target" "t131" "targetname" "t133" "delay" "2.5" "origin" "1360 296 -184" } { "classname" "ammo_rockets" "origin" "2016 -24 -240" } { "classname" "item_armor_shard" "origin" "1424 360 -240" } { "classname" "item_armor_shard" "origin" "1384 360 -240" } { "classname" "item_armor_shard" "origin" "1408 320 -240" } { "classname" "item_health" "origin" "1248 232 -240" } { "classname" "item_health" "origin" "1248 192 -240" } { "classname" "ammo_bullets" "origin" "1416 -168 -240" } { "classname" "ammo_bullets" "origin" "1376 -168 -240" } { "classname" "ammo_bullets" "origin" "1376 -128 -240" } { "classname" "target_explosion" "delay" "1.8" "origin" "1352 128 -168" "dmg" "1" "targetname" "t131" } { "classname" "target_explosion" "delay" "1.6" "origin" "1344 48 -176" "dmg" "1" "targetname" "t131" } { "origin" "1360 88 -104" "delay" "2" "classname" "target_explosion" "dmg" "1" "targetname" "t131" } { "classname" "light" "light" "120" "origin" "1352 96 -368" } { "classname" "target_explosion" "delay" "1" "targetname" "t131" "origin" "1312 136 -208" } { "classname" "target_explosion" "delay" ".9" "targetname" "t131" "origin" "1408 176 -192" } { "classname" "target_explosion" "delay" ".8" "targetname" "t131" "origin" "1336 96 -240" } { "classname" "target_explosion" "targetname" "t131" "origin" "1376 16 -232" } { "classname" "target_explosion" "delay" ".6" "targetname" "t131" "origin" "1304 56 -240" } { "classname" "target_explosion" "delay" ".4" "targetname" "t131" "origin" "1408 80 -240" } { "classname" "target_explosion" "delay" ".2" "targetname" "t131" "origin" "1352 208 -232" } { "classname" "target_explosion" "delay" "1.4" "targetname" "t131" "origin" "1360 136 -152" } { "classname" "trigger_relay" "target" "t132" "targetname" "t131" "delay" "1.4" "origin" "1352 -24 -208" } { "model" "*3" "classname" "func_explosive" "dmg" "1" "targetname" "t132" } { "classname" "trigger_relay" "delay" "1.9" "target" "t130" "targetname" "t131" "origin" "1440 224 -208" } { "model" "*4" "classname" "trigger_once" "target" "t133" } { "model" "*5" "classname" "func_door" "angle" "-2" "wait" "-1" "speed" "300" "spawnflags" "1" "targetname" "t130" } { "origin" "1816 -1744 -128" "_color" "1.000000 0.500000 0.500000" "light" "200" "classname" "light" } { "origin" "1904 -1344 -312" "_color" "1.000000 0.500000 0.500000" "light" "200" "classname" "light" } { "origin" "1904 -1152 -312" "_color" "1.000000 0.500000 0.500000" "light" "200" "classname" "light" } { "origin" "1576 -1744 -128" "classname" "light" "light" "200" "_color" "1.000000 0.500000 0.500000" } { "origin" "1488 -1152 -312" "classname" "light" "light" "200" "_color" "1.000000 0.500000 0.500000" } { "origin" "1488 -1344 -312" "_color" "1.000000 0.500000 0.500000" "light" "200" "classname" "light" } { "model" "*6" "count" "2" "classname" "target_character" "team" "countdown" } { "model" "*7" "count" "1" "classname" "target_character" "team" "countdown" } { "model" "*8" "count" "2" "classname" "target_character" "team" "countdown" } { "model" "*9" "count" "1" "classname" "target_character" "team" "countdown" } { "model" "*10" "count" "1" "classname" "target_character" "team" "countdown" } { "model" "*11" "count" "2" "classname" "target_character" "team" "countdown" } { "model" "*12" "classname" "func_killbox" "targetname" "t117" } { "angle" "270" "classname" "info_player_deathmatch" "origin" "1696 1000 -232" } { "angle" "180" "classname" "info_player_deathmatch" "origin" "2144 288 -40" } { "angle" "180" "classname" "info_player_deathmatch" "origin" "1088 -896 -360" } { "angle" "180" "classname" "info_player_deathmatch" "origin" "2304 -896 -360" } { "angle" "90" "classname" "info_player_deathmatch" "origin" "2112 -1600 -232" } { "angle" "90" "classname" "info_player_deathmatch" "origin" "1280 -1600 -232" } { "classname" "info_player_deathmatch" "angle" "270" "origin" "2592 800 -376" } { "origin" "1600 -1576 -160" "delay" "10" "target" "t129" "targetname" "t41" "classname" "trigger_relay" } { "origin" "1592 -1648 -144" "targetname" "t129" "attenuation" "-1" "noise" "world/v_gun2.wav" "classname" "target_speaker" } { "origin" "1712 616 -248" "noise" "world/v_gun1.wav" "attenuation" "-1" "targetname" "t128" "classname" "target_speaker" } { "model" "*13" "target" "t128" "classname" "trigger_once" } { "origin" "1504 -1248 -328" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" } { "origin" "1456 -608 -232" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" } { "origin" "1712 864 -168" "noise" "world/amb23.wav" "spawnflags" "1" "classname" "target_speaker" } { "model" "*14" "wait" "-1" "team" "fps" "spawnflags" "8" "message" "This door is opened elsewhere." "classname" "func_door" "angle" "270" "target" "t96" "targetname" "t106" } { "model" "*15" "classname" "func_door" "spawnflags" "8" "target" "t96" "targetname" "t106" "team" "fps" "wait" "-1" "angle" "90" } { "model" "*16" "classname" "func_door" "angle" "180" "_minlight" "0.2" "targetname" "t94" "target" "t78" "spawnflags" "2048" "team" "fps4343" } { "model" "*17" "classname" "func_door" "angle" "0" "_minlight" "0.2" "targetname" "t94" "target" "t78" "spawnflags" "2048" "team" "fps4343" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" "origin" "1728 960 -168" } { "origin" "1544 96 -168" "spawnflags" "1" "noise" "world/amb21.wav" "classname" "target_speaker" } { "origin" "1696 -56 -168" "spawnflags" "1" "noise" "world/amb21.wav" "classname" "target_speaker" } { "origin" "1696 248 -168" "spawnflags" "1" "noise" "world/amb21.wav" "classname" "target_speaker" } { "noise" "world/comp_hum2.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1888 -1248 -328" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "1936 -608 -232" } { "model" "*18" "classname" "func_wall" "_minlight" "0.2" "spawnflags" "2050" "targetname" "t98" } { "classname" "ammo_cells" "spawnflags" "1792" "origin" "2760 840 -384" } { "classname" "ammo_cells" "spawnflags" "1792" "origin" "2688 840 -384" } { "classname" "weapon_bfg" "spawnflags" "1792" "origin" "2528 800 -384" } { "classname" "ammo_slugs" "spawnflags" "0" "origin" "1936 480 -48" } { "classname" "weapon_railgun" "spawnflags" "1792" "origin" "2128 -144 -48" } { "spawnflags" "1792" "classname" "ammo_rockets" "origin" "1552 -968 -368" } { "classname" "ammo_rockets" "spawnflags" "1792" "origin" "1840 -968 -368" } { "classname" "weapon_rocketlauncher" "spawnflags" "1792" "origin" "1696 -856 -368" } { "spawnflags" "1792" "classname" "ammo_shells" "origin" "1440 -1424 -368" } { "classname" "ammo_shells" "spawnflags" "1792" "origin" "1952 -1424 -368" } { "classname" "weapon_shotgun" "spawnflags" "1792" "origin" "1440 -1088 -368" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "1904 -1680 -176" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "1488 -1680 -176" } { "classname" "weapon_grenadelauncher" "spawnflags" "1792" "origin" "1696 -1728 -176" } { "classname" "trigger_always" "target" "t97" "spawnflags" "1792" "origin" "1784 -160 -232" } { "classname" "trigger_always" "target" "t93" "spawnflags" "1792" "origin" "1640 -128 -232" } { "classname" "trigger_always" "target" "t78" "origin" "1744 344 -224" } { "origin" "2784 704 -336" "classname" "light" "light" "110" "_color" "1.000000 0.000000 0.000000" } { "origin" "2880 704 -336" "_color" "1.000000 0.000000 0.000000" "light" "110" "classname" "light" } { "classname" "trigger_relay" "targetname" "t124" "target" "t126" "origin" "1040 760 184" "delay" "0.1" } { "classname" "trigger_relay" "targetname" "t124" "target" "t127" "origin" "984 784 184" "delay" "0.3" } { "classname" "trigger_relay" "targetname" "t124" "target" "t125" "origin" "1024 712 168" "delay" "0.2" } { "classname" "func_timer" "target" "t124" "origin" "984 720 168" "spawnflags" "1" "wait" "1.5" "random" "0.5" } { "classname" "trigger_relay" "targetname" "t120" "target" "t122" "origin" "1016 744 120" "delay" "0.4" } { "classname" "trigger_relay" "targetname" "t120" "target" "t123" "origin" "1056 712 120" "delay" "0.3" } { "classname" "trigger_relay" "targetname" "t120" "target" "t121" "delay" "0.1" "origin" "960 760 120" } { "classname" "func_timer" "target" "t120" "origin" "984 720 120" "spawnflags" "1" "wait" "1.5" "random" "0.5" } { "classname" "target_explosion" "targetname" "t122" "origin" "1056 792 120" } { "classname" "target_explosion" "targetname" "t123" "origin" "1096 712 120" } { "classname" "target_explosion" "targetname" "t125" "origin" "1088 704 184" } { "classname" "target_explosion" "targetname" "t126" "origin" "1088 832 184" } { "classname" "target_explosion" "targetname" "t127" "origin" "960 832 120" } { "classname" "target_explosion" "targetname" "t121" "origin" "936 792 120" } { "classname" "info_player_intermission" "angles" "0 45 0" "origin" "944 688 136" } { "origin" "1656 -1592 -168" "angle" "180" "classname" "monster_soldier_ss" } { "model" "*19" "target" "t119" "classname" "trigger_once" } { "model" "*20" "target" "t118" "classname" "trigger_once" } { "_color" "1.000000 1.000000 1.000000" "light" "100" "classname" "light" "origin" "1856 688 -168" } { "_color" "1.000000 1.000000 1.000000" "light" "100" "classname" "light" "origin" "1872 776 -168" } { "_color" "1.000000 1.000000 1.000000" "light" "100" "classname" "light" "origin" "1632 1024 -176" } { "_color" "1.000000 1.000000 1.000000" "light" "100" "classname" "light" "origin" "1632 896 -176" } { "origin" "1408 136 56" "light" "130" "classname" "light" } { "origin" "1792 56 56" "light" "130" "classname" "light" } { "origin" "1792 136 56" "classname" "light" "light" "130" } { "origin" "1792 256 56" "light" "130" "classname" "light" } { "origin" "1792 -64 56" "light" "130" "classname" "light" } { "origin" "1920 56 56" "light" "130" "classname" "light" } { "origin" "1920 136 56" "classname" "light" "light" "130" } { "origin" "1920 256 56" "light" "130" "classname" "light" } { "origin" "1920 -64 56" "light" "130" "classname" "light" } { "origin" "1408 56 56" "light" "130" "classname" "light" } { "origin" "1472 256 56" "light" "130" "classname" "light" } { "origin" "1472 -64 56" "light" "130" "classname" "light" } { "origin" "1600 56 56" "classname" "light" "light" "130" } { "origin" "1600 -64 56" "classname" "light" "light" "130" } { "origin" "1600 256 56" "classname" "light" "light" "130" } { "origin" "1600 136 56" "light" "130" "classname" "light" } { "targetname" "t117" "origin" "2496 576 -232" "classname" "target_explosion" "dmg" "10000" } { "targetname" "t117" "origin" "2496 832 -232" "classname" "target_explosion" "dmg" "10000" } { "targetname" "t117" "origin" "2496 256 -232" "classname" "target_explosion" "dmg" "10000" } { "targetname" "t117" "origin" "2240 96 -192" "classname" "target_explosion" "dmg" "10000" } { "targetname" "t117" "origin" "2144 176 24" "classname" "target_explosion" "dmg" "10000" } { "targetname" "t117" "dmg" "10000" "classname" "target_explosion" "origin" "1664 768 -144" } { "targetname" "t117" "origin" "1664 1096 -144" "classname" "target_explosion" "dmg" "10000" } { "targetname" "t117" "origin" "1696 96 24" "classname" "target_explosion" "dmg" "10000" } { "targetname" "t117" "dmg" "10000" "classname" "target_explosion" "origin" "2784 704 -352" } { "targetname" "t117" "dmg" "10000" "classname" "target_explosion" "origin" "1960 952 8" } { "targetname" "t117" "dmg" "10000" "classname" "target_explosion" "origin" "1408 -320 -144" } { "targetname" "t117" "origin" "2016 360 -200" "classname" "target_explosion" "dmg" "10000" } { "targetname" "t117" "origin" "1088 -896 -304" "classname" "target_explosion" "dmg" "10000" } { "targetname" "t117" "dmg" "10000" "classname" "target_explosion" "origin" "1984 -320 -144" } { "targetname" "t117" "origin" "1696 -888 -248" "classname" "target_explosion" "dmg" "10000" } { "targetname" "t117" "origin" "1440 -1248 -168" "classname" "target_explosion" "dmg" "10000" } { "targetname" "t117" "dmg" "10000" "classname" "target_explosion" "origin" "2304 -896 -304" } { "targetname" "t117" "origin" "1536 -1664 -80" "dmg" "10000" "classname" "target_explosion" } { "origin" "1952 -1248 -168" "targetname" "t117" "classname" "target_explosion" "dmg" "10000" } { "origin" "1856 -1664 -80" "targetname" "t117" "dmg" "10000" "classname" "target_explosion" } { "origin" "1696 -856 -160" "target" "t117" "delay" "10" "targetname" "t107" "classname" "trigger_relay" } { "model" "*21" "count" "1" "classname" "target_character" "team" "countdown" } { "model" "*22" "count" "2" "classname" "target_character" "team" "countdown" } { "model" "*23" "count" "1" "classname" "target_character" "team" "countdown" } { "model" "*24" "count" "2" "classname" "target_character" "team" "countdown" } { "model" "*25" "count" "1" "classname" "target_character" "team" "countdown" } { "model" "*26" "count" "2" "classname" "target_character" "team" "countdown" } { "model" "*27" "team" "countdown" "classname" "target_character" "count" "1" } { "model" "*28" "team" "countdown" "classname" "target_character" "count" "2" } { "classname" "item_health" "origin" "1632 -328 -240" } { "classname" "item_health" "origin" "1760 -328 -240" } { "classname" "monster_soldier_ss" "angle" "0" "spawnflags" "1" "origin" "1416 -328 -232" } { "classname" "monster_soldier_ss" "angle" "180" "spawnflags" "1" "origin" "1976 -328 -232" } { "model" "*29" "message" "This door is opened elsewhere." "target" "t95" "classname" "func_door" "angle" "-1" "targetname" "t92" "_minlight" "0.2" } { "classname" "ammo_shells" "origin" "2144 -224 -48" } { "classname" "ammo_shells" "origin" "2080 -224 -48" } { "classname" "item_health_large" "origin" "1856 424 -48" } { "_color" "1.000000 0.694118 0.392157" "light" "120" "classname" "light" "origin" "2000 336 -160" } { "_color" "1.000000 1.000000 1.000000" "light" "120" "classname" "light" "origin" "1728 576 88" } { "_color" "1.000000 1.000000 1.000000" "light" "120" "classname" "light" "origin" "1632 832 88" } { "classname" "item_health_small" "origin" "1600 616 -240" } { "classname" "item_health_small" "origin" "1600 576 -240" } { "classname" "item_health_small" "origin" "1600 536 -240" } { "classname" "item_health_small" "origin" "1600 656 -240" } { "classname" "ammo_rockets" "origin" "1856 680 -240" } { "classname" "item_armor_combat" "origin" "1912 760 -176" } { "classname" "item_health" "origin" "1592 864 -240" } { "classname" "item_health" "origin" "1888 1128 -240" } { "classname" "target_speaker" "noise" "world/amb21.wav" "spawnflags" "1" "origin" "1848 96 -168" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb20.wav" "origin" "1696 -776 -336" } { "targetname" "t119" "angle" "180" "classname" "monster_soldier_ss" "origin" "1928 -1152 -360" } { "targetname" "t119" "angle" "180" "classname" "monster_soldier_ss" "origin" "1928 -1344 -360" } { "targetname" "t118" "angle" "0" "classname" "monster_soldier_ss" "origin" "1464 -1344 -360" } { "targetname" "t118" "angle" "0" "classname" "monster_soldier_ss" "origin" "1464 -1152 -360" } { "classname" "monster_soldier_ss" "angle" "0" "origin" "1736 -1592 -168" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb18.wav" "origin" "1696 -1496 -120" } { "classname" "target_goal" "targetname" "t41" "origin" "1768 -1624 -40" } { "classname" "target_goal" "targetname" "t93" "origin" "1432 -72 -40" } { "classname" "target_explosion" "targetname" "t116" "origin" "1920 -672 -208" } { "classname" "target_explosion" "targetname" "t115" "origin" "1920 -544 -208" } { "random" "0.5" "wait" "2" "delay" "0.6" "classname" "func_timer" "targetname" "t114" "target" "t116" "origin" "1960 -656 -224" } { "random" "0.5" "wait" "2" "delay" "0.6" "classname" "func_timer" "targetname" "t114" "target" "t115" "origin" "1960 -568 -224" } { "classname" "func_timer" "target" "t113" "targetname" "t114" "delay" "0.6" "wait" "2" "random" "0.5" "origin" "1432 -560 -224" } { "classname" "func_timer" "target" "t112" "targetname" "t114" "delay" "0.6" "wait" "2" "random" "0.5" "origin" "1432 -648 -224" } { "model" "*30" "spawnflags" "4" "classname" "trigger_once" "target" "t114" "targetname" "t109" } { "classname" "target_explosion" "targetname" "t113" "origin" "1472 -544 -208" } { "classname" "target_explosion" "targetname" "t112" "origin" "1472 -672 -208" } { "classname" "target_help" "targetname" "t93" "message" "Lock down laser guard." "origin" "1464 336 -200" } { "classname" "target_help" "targetname" "t111" "message" "Eliminate all resistance." "origin" "1648 1072 -200" } { "classname" "target_help" "targetname" "t111" "spawnflags" "1" "message" "Destroy Big Gun." "origin" "1744 1072 -200" } { "model" "*31" "classname" "trigger_once" "target" "t111" } { "origin" "2144 96 -160" "classname" "light" "light" "120" "_color" "1.000000 0.694118 0.392157" } { "origin" "2016 96 -160" "classname" "light" "light" "120" "_color" "1.000000 0.694118 0.392157" } { "origin" "2048 384 -112" "classname" "light" "light" "110" "_color" "1.000000 0.694118 0.392157" } { "classname" "trigger_relay" "target" "t110" "targetname" "t108" "delay" "0.6" "origin" "1808 -944 16" } { "classname" "target_explosion" "targetname" "t110" "origin" "1896 -912 16" } { "model" "*32" "spawnflags" "4" "classname" "trigger_once" "targetname" "t41" "target" "t109" } { "model" "*33" "_minlight" "0.2" "spawnflags" "1" "classname" "func_object" "targetname" "t110" } { "classname" "light" "light" "120" "origin" "1376 -640 0" } { "classname" "light" "light" "120" "origin" "1376 -512 0" } { "classname" "light" "light" "120" "origin" "1376 -384 0" } { "classname" "light" "light" "120" "origin" "1760 -288 0" } { "classname" "light" "light" "120" "origin" "1632 -288 0" } { "classname" "light" "light" "120" "origin" "1504 -288 0" } { "classname" "light" "light" "120" "origin" "1376 -288 0" } { "classname" "light" "light" "120" "origin" "1888 -288 0" } { "classname" "light" "light" "120" "origin" "2016 -288 0" } { "classname" "light" "light" "120" "origin" "2016 -384 0" } { "classname" "light" "light" "120" "origin" "2016 -512 0" } { "classname" "light" "light" "120" "origin" "2016 -640 0" } { "classname" "light" "light" "120" "origin" "2016 -768 0" } { "classname" "light" "light" "120" "origin" "1984 -896 0" } { "light" "120" "classname" "light" "origin" "1536 -896 0" } { "light" "120" "classname" "light" "origin" "1408 -896 0" } { "light" "120" "classname" "light" "origin" "1376 -768 0" } { "light" "120" "classname" "light" "origin" "1856 -896 0" } { "classname" "light" "light" "120" "origin" "1696 -896 0" } { "model" "*34" "classname" "trigger_once" "target" "t109" "spawnflags" "4" "targetname" "t41" } { "classname" "trigger_relay" "target" "t108" "targetname" "t109" "origin" "1528 -872 -64" } { "classname" "target_explosion" "targetname" "t108" "origin" "1536 -904 16" } { "model" "*35" "classname" "func_object" "spawnflags" "1" "targetname" "t108" "_minlight" "0.2" } { "model" "*36" "classname" "trigger_once" "message" "Evacuate immediately." "targetname" "t41" "delay" "2" } { "classname" "trigger_relay" "targetname" "t41" "target" "t107" "delay" "12" "origin" "1720 -1576 -160" } { "classname" "target_earthquake" "speed" "100" "count" "15" "targetname" "t107" "origin" "1776 -1584 -160" } { "classname" "trigger_relay" "targetname" "t97" "target" "t106" "origin" "1744 -80 -184" } { "classname" "target_explosion" "targetname" "t104" "origin" "1592 160 -256" } { "classname" "target_explosion" "targetname" "t105" "origin" "1616 16 -256" } { "classname" "target_explosion" "targetname" "t102" "origin" "1776 176 -256" } { "classname" "target_explosion" "targetname" "t100" "origin" "1696 -24 -256" } { "classname" "target_explosion" "targetname" "t103" "origin" "1696 216 -256" } { "classname" "target_explosion" "targetname" "t101" "origin" "1768 16 -256" } { "classname" "func_timer" "targetname" "t99" "random" "2" "wait" "5" "delay" "0.5" "origin" "1672 80 -104" "target" "t105" "spawnflags" "2048" } { "classname" "func_timer" "targetname" "t99" "random" "2" "wait" "5" "delay" "1" "origin" "1720 80 -104" "target" "t101" "spawnflags" "2048" } { "classname" "func_timer" "targetname" "t99" "random" "2" "wait" "5" "delay" "0.5" "origin" "1720 112 -104" "target" "t102" "spawnflags" "2048" } { "classname" "func_timer" "targetname" "t99" "random" "2" "wait" "5" "delay" "1" "origin" "1672 112 -104" "target" "t104" "spawnflags" "2048" } { "classname" "func_timer" "targetname" "t99" "random" "2" "wait" "5" "origin" "1696 136 -104" "target" "t103" "spawnflags" "2048" } { "classname" "func_timer" "targetname" "t99" "random" "2" "wait" "5" "origin" "1696 56 -104" "target" "t100" "spawnflags" "2048" } { "classname" "trigger_relay" "target" "t99" "origin" "1696 96 -48" "targetname" "t106" } { "classname" "trigger_relay" "targetname" "t97" "killtarget" "mifs" "origin" "1640 -80 -184" } { "classname" "target_speaker" "noise" "world/10_0.wav" "spawnflags" "4" "origin" "1648 -1568 -160" "attenuation" "-1" "targetname" "t107" } { "classname" "trigger_relay" "targetname" "t97" "origin" "1672 -80 -184" "target" "t98" } { "classname" "trigger_relay" "targetname" "t97" "killtarget" "t76" "origin" "1696 -80 -184" } { "style" "1" "targetname" "t96" "classname" "func_areaportal" } { "classname" "func_group" } { "style" "2" "targetname" "t95" "classname" "func_areaportal" } { "model" "*37" "target" "t94" "classname" "trigger_multiple" "spawnflags" "2048" } { "model" "*38" "targetname" "t93" "target" "t92" "spawnflags" "4" "classname" "trigger_multiple" } { "classname" "light" "light" "120" "_color" "1.000000 0.694118 0.392157" "origin" "2048 384 32" } { "_color" "1.000000 0.694118 0.392157" "light" "120" "classname" "light" "origin" "2080 -32 32" } { "_color" "1.000000 0.694118 0.392157" "light" "120" "classname" "light" "origin" "2080 96 32" } { "_color" "1.000000 0.694118 0.392157" "light" "120" "classname" "light" "origin" "2080 224 32" } { "_color" "1.000000 0.694118 0.392157" "light" "120" "classname" "light" "origin" "2272 96 -160" } { "_color" "1.000000 0.694118 0.392157" "light" "120" "classname" "light" "origin" "1888 416 32" } { "classname" "light" "light" "120" "_color" "1.000000 0.694118 0.392157" "origin" "2016 -160 32" } { "model" "*39" "classname" "func_plat" "_minlight" "0.2" } { "deathtarget" "t93" "classname" "monster_boss2" "angle" "0" "origin" "1352 96 -360" "spawnflags" "1" "targetname" "t131" } { "classname" "func_timer" "target" "t90" "wait" "20" "spawnflags" "2049" "origin" "1560 264 -232" } { "classname" "trigger_relay" "target" "t89" "delay" "2" "origin" "1608 320 -232" "targetname" "t90" } { "classname" "trigger_relay" "target" "t89" "delay" "10" "origin" "1608 288 -232" "targetname" "t90" } { "classname" "path_corner" "targetname" "t88" "origin" "1696 96 -24" } { "classname" "target_laser" "target" "t88" "spawnflags" "66" "targetname" "t89" "origin" "1696 96 -240" } { "_color" "1.000000 0.501961 0.000000" "light" "80" "classname" "light" "origin" "1696 -472 -336" } { "_color" "1.000000 0.501961 0.000000" "light" "80" "classname" "light" "origin" "1696 -592 -336" } { "_color" "1.000000 0.501961 0.000000" "light" "80" "classname" "light" "origin" "1696 -688 -336" } { "classname" "trigger_relay" "targetname" "t86" "target" "t87" "delay" "0.1" "origin" "1704 -400 -336" } { "classname" "func_timer" "target" "t86" "spawnflags" "1" "delay" "0.2" "origin" "1696 -384 -336" } { "classname" "trigger_relay" "targetname" "t86" "target" "t87" "origin" "1688 -400 -336" } { "classname" "trigger_relay" "targetname" "t84" "target" "t85" "delay" "0.1" "origin" "1704 -528 -336" } { "classname" "func_timer" "target" "t84" "delay" "0.4" "origin" "1696 -512 -336" "spawnflags" "1" } { "classname" "trigger_relay" "targetname" "t84" "target" "t85" "origin" "1688 -528 -336" } { "classname" "trigger_relay" "target" "t82" "targetname" "t83" "origin" "1688 -648 -336" } { "classname" "trigger_relay" "target" "t82" "targetname" "t83" "delay" "0.1" "origin" "1704 -648 -336" } { "classname" "func_timer" "target" "t83" "spawnflags" "1" "delay" "0.6" "origin" "1696 -632 -336" } { "classname" "func_timer" "target" "t81" "spawnflags" "1" "wait" "1" "origin" "1696 -712 -336" "delay" "0.8" } { "classname" "trigger_relay" "target" "t80" "targetname" "t81" "origin" "1688 -728 -336" } { "classname" "trigger_relay" "target" "t80" "targetname" "t81" "delay" "0.1" "origin" "1704 -728 -336" } { "style" "32" "classname" "light" "_color" "1.000000 0.349020 0.035294" "targetname" "t82" "origin" "1696 -664 -336" "spawnflags" "1" } { "style" "33" "_color" "1.000000 0.349020 0.035294" "classname" "light" "targetname" "t85" "origin" "1696 -544 -336" "spawnflags" "1" } { "style" "34" "_color" "1.000000 0.349020 0.035294" "classname" "light" "targetname" "t80" "origin" "1696 -744 -336" "spawnflags" "1" } { "style" "35" "classname" "light" "_color" "1.000000 0.349020 0.035294" "targetname" "t87" "origin" "1696 -416 -336" "spawnflags" "1" } { "classname" "light" "light" "130" "_color" "1.000000 0.351064 0.037234" "origin" "1568 -360 -104" } { "_color" "1.000000 0.351064 0.037234" "light" "130" "classname" "light" "origin" "1696 -360 -104" } { "_color" "1.000000 0.351064 0.037234" "light" "130" "classname" "light" "origin" "1824 -360 -104" } { "classname" "light" "light" "130" "_color" "1.000000 0.351064 0.037234" "origin" "1696 -792 -104" } { "classname" "light" "light" "130" "_color" "1.000000 0.351064 0.037234" "origin" "1824 -792 -104" } { "classname" "light" "light" "130" "_color" "1.000000 0.351064 0.037234" "origin" "1944 -360 -104" } { "_color" "1.000000 0.351064 0.037234" "light" "130" "classname" "light" "origin" "1448 -792 -104" } { "classname" "light" "light" "130" "_color" "1.000000 0.351064 0.037234" "origin" "1448 -704 -104" } { "classname" "light" "light" "130" "_color" "1.000000 0.351064 0.037234" "origin" "1448 -576 -104" } { "classname" "light" "light" "130" "_color" "1.000000 0.351064 0.037234" "origin" "1448 -448 -104" } { "classname" "light" "light" "130" "_color" "1.000000 0.351064 0.037234" "origin" "1448 -360 -104" } { "_color" "1.000000 0.351064 0.037234" "light" "130" "classname" "light" "origin" "1944 -704 -104" } { "_color" "1.000000 0.351064 0.037234" "light" "130" "classname" "light" "origin" "1944 -576 -104" } { "_color" "1.000000 0.351064 0.037234" "light" "130" "classname" "light" "origin" "1944 -448 -104" } { "_color" "1.000000 0.351064 0.037234" "light" "130" "classname" "light" "origin" "1568 -792 -104" } { "classname" "light" "light" "130" "_color" "1.000000 0.351064 0.037234" "origin" "1944 -792 -104" } { "classname" "path_corner" "targetname" "t79" "origin" "1696 -352 -336" } { "classname" "target_laser" "origin" "1696 -920 -336" "target" "t79" "spawnflags" "2115" "dmg" "1000" } { "classname" "func_timer" "target" "t77" "spawnflags" "2049" "wait" "10" "origin" "1560 304 -232" "delay" "5" "targetname" "mifs" } { "origin" "1696 384 -192" "classname" "light" "light" "100" "_color" "1.000000 1.000000 1.000000" } { "style" "3" "classname" "func_areaportal" "targetname" "t78" } { "origin" "1632 640 -192" "classname" "light" "light" "100" "_color" "1.000000 1.000000 1.000000" } { "model" "*40" "_minlight" "0.2" "lip" "48" "speed" "40" "targetname" "t76" "spawnflags" "32" "wait" "-1" "angle" "-1" "classname" "func_door" } { "origin" "1696 96 -224" "targetname" "t77" "target" "t76" "classname" "trigger_relay" } { "model" "*41" "team" "llama" "lip" "32" "targetname" "t76" "spawnflags" "2088" "wait" "-1" "classname" "func_door" "angle" "270" } { "model" "*42" "team" "llama" "lip" "32" "targetname" "t76" "spawnflags" "2088" "wait" "-1" "classname" "func_door" "angle" "0" } { "model" "*43" "team" "llama" "lip" "32" "targetname" "t76" "spawnflags" "2050" "wait" "-1" "classname" "func_door" "angle" "180" } { "model" "*44" "team" "llama" "lip" "32" "targetname" "t76" "spawnflags" "2050" "wait" "-1" "angle" "90" "classname" "func_door" } { "origin" "1760 768 -192" "classname" "light" "light" "100" "_color" "1.000000 1.000000 1.000000" } { "origin" "1632 768 -192" "classname" "light" "light" "100" "_color" "1.000000 1.000000 1.000000" } { "_color" "1.000000 1.000000 1.000000" "light" "100" "classname" "light" "origin" "1760 640 -192" } { "_color" "1.000000 1.000000 1.000000" "light" "100" "classname" "light" "origin" "1760 896 -192" } { "origin" "1696 896 -176" "classname" "light" "light" "100" "_color" "1.000000 1.000000 1.000000" } { "origin" "1696 1024 -176" "classname" "light" "light" "100" "_color" "1.000000 1.000000 1.000000" } { "_color" "1.000000 1.000000 1.000000" "light" "100" "classname" "light" "origin" "1760 1024 -192" } { "origin" "1632 1128 -192" "classname" "light" "light" "100" "_color" "1.000000 1.000000 1.000000" } { "_color" "1.000000 1.000000 1.000000" "light" "100" "classname" "light" "origin" "1760 1128 -192" } { "origin" "1816 776 -168" "classname" "light" "light" "100" "_color" "1.000000 1.000000 1.000000" } { "origin" "1888 1128 -192" "classname" "light" "light" "100" "_color" "1.000000 1.000000 1.000000" } { "origin" "1760 1088 88" "classname" "light" "light" "120" "_color" "1.000000 1.000000 1.000000" } //{ //"origin" "1904 960 -248" //"targetname" "t73" //"spawnflags" "16" //"classname" "target_actor" //} { "origin" "1632 704 -96" "classname" "light" "light" "120" "_color" "1.000000 1.000000 1.000000" } { "origin" "1600 576 88" "classname" "light" "light" "120" "_color" "1.000000 1.000000 1.000000" } { "_color" "1.000000 1.000000 1.000000" "light" "120" "classname" "light" "origin" "1888 704 88" } { "_color" "1.000000 1.000000 1.000000" "light" "120" "classname" "light" "origin" "1760 832 88" } { "_color" "1.000000 1.000000 1.000000" "light" "120" "classname" "light" "origin" "1760 640 -96" } { "_color" "1.000000 1.000000 1.000000" "light" "120" "classname" "light" "origin" "1632 960 88" } { "_color" "1.000000 1.000000 1.000000" "light" "120" "classname" "light" "origin" "1760 960 88" } { "_color" "1.000000 1.000000 1.000000" "light" "100" "classname" "light" "origin" "1696 512 -192" } { "_color" "1.000000 1.000000 1.000000" "light" "120" "classname" "light" "origin" "1632 1088 88" } { "origin" "1664 816 -232" "classname" "info_player_coop" "angle" "180" } { "origin" "1792 768 -232" "classname" "info_player_coop" "angle" "270" } { "origin" "1624 728 -232" "classname" "info_player_coop" "angle" "180" } { "origin" "1744 816 -232" "angle" "180" "classname" "info_player_coop" } { "classname" "light" "origin" "1888 896 -128" "light" "130" "_color" "0.984127 1.000000 0.662698" } { "classname" "light" "origin" "2040 896 -128" "light" "130" "_color" "0.984127 1.000000 0.662698" } { "classname" "light" "origin" "2040 1024 -128" "light" "130" "_color" "0.984127 1.000000 0.662698" } { "delay" "1.8" "target" "t66" "targetname" "t61" "origin" "1952 880 -144" "classname" "trigger_relay" } { "origin" "1912 976 -184" "targetname" "t65" "noise" "world/explod2.wav" "classname" "target_speaker" } { "targetname" "t66" "origin" "1928 848 -184" "noise" "weapons/grenlx1a.wav" "classname" "target_speaker" } { "origin" "1968 912 -144" "delay" "0.6" "target" "t64" "targetname" "t61" "classname" "trigger_relay" } { "delay" "1.4" "origin" "2016 904 -144" "target" "t65" "targetname" "t61" "classname" "trigger_relay" } { "model" "*45" "targetname" "t65" "mass" "400" "classname" "func_explosive" } { "_color" "0.984127 1.000000 0.662698" "light" "130" "origin" "1888 1024 -128" "classname" "light" } { "origin" "1840 896 -256" "wait" "-1" "target" "t63" "targetname" "t62" "classname" "path_corner" } { "origin" "1840 896 384" "wait" "-1" "targetname" "t63" "target" "t62" "classname" "path_corner" } { "model" "*46" "target" "t61" "classname" "trigger_once" } { "wait" "-1" "origin" "1904 960 -272" "target" "t60" "targetname" "t59" "classname" "path_corner" } { "origin" "1904 960 384" "wait" "-1" "targetname" "t60" "target" "t59" "classname" "path_corner" } { "model" "*47" "_minlight" "0.2" "targetname" "t64" "speed" "600" "dmg" "1000" "spawnflags" "2" "target" "t63" "classname" "func_train" } { "model" "*48" "targetname" "t61" "mass" "800" "dmg" "1" "classname" "func_explosive" } { "model" "*49" "speed" "600" "targetname" "t61" "dmg" "1000" "spawnflags" "2" "_minlight" "0.2" "target" "t60" "classname" "func_train" } { "classname" "target_changelevel" "targetname" "t58" "map" "eou7_.cin+*hangar1$unitstart" "origin" "2888 696 -328" } { "model" "*50" "classname" "func_button" "angle" "0" "target" "t58" "wait" "-1" } { "model" "*51" "message" "Laser guard locked down." "targetname" "t41" "classname" "trigger_once" "target" "t97" } { "model" "*52" "wait" "-1" "angle" "90" "classname" "func_button" "target" "t41" "spawnflags" "2048" } { "classname" "path_corner" "targetname" "t3" "origin" "1696 -1072 -336" } { "classname" "target_laser" "target" "t3" "spawnflags" "67" "origin" "1696 -1200 -336" } { "model" "*53" "classname" "func_plat" } { "model" "*54" "classname" "func_plat" } { "light" "120" "classname" "light" "origin" "2112 -1600 32" } { "light" "120" "classname" "light" "origin" "2112 -1504 -128" } { "light" "150" "_color" "1.000000 1.000000 0.000000" "classname" "light" "origin" "2416 968 -288" } { "light" "150" "_color" "1.000000 1.000000 0.000000" "classname" "light" "origin" "2528 968 -288" } { "classname" "light" "_color" "1.000000 1.000000 0.000000" "light" "150" "origin" "2640 968 -288" } { "_color" "1.000000 0.000000 0.000000" "classname" "light" "origin" "2344 704 -336" } { "light" "120" "origin" "2560 896 -232" "classname" "light" } { "classname" "light" "origin" "2688 896 -232" "light" "120" } { "classname" "light" "origin" "2816 896 -232" "light" "120" } { "light" "120" "origin" "2560 512 -232" "classname" "light" } { "classname" "light" "origin" "2688 512 -232" "light" "120" } { "classname" "light" "origin" "2816 512 -232" "light" "120" } { "light" "120" "origin" "2400 704 -232" "classname" "light" } { "light" "120" "origin" "2400 608 -232" "classname" "light" } { "_color" "1.000000 1.000000 1.000000" "light" "120" "classname" "light" "origin" "2496 224 -40" } { "_color" "1.000000 1.000000 1.000000" "light" "120" "classname" "light" "origin" "2496 96 -40" } { "_color" "1.000000 1.000000 1.000000" "light" "120" "classname" "light" "origin" "2368 96 -40" } { "classname" "light" "origin" "2400 800 -232" "light" "120" } { "classname" "func_group" } { "classname" "func_group" } { "origin" "1280 -1600 32" "classname" "light" "light" "120" } { "origin" "1280 -1504 -128" "light" "120" "classname" "light" } { "model" "*55" "origin" "1696 -1664 -130" "classname" "func_rotating" "spawnflags" "1" "speed" "30" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1552 -968 -256" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1408 -960 -160" } { "origin" "2496 448 -296" "classname" "light" "light" "120" "_color" "1.000000 0.000000 0.000000" } { "origin" "1728 -1536 -88" "classname" "light" "light" "120" "_color" "1.000000 1.000000 1.000000" } { "origin" "1664 -1536 -88" "classname" "light" "light" "120" "_color" "1.000000 1.000000 1.000000" } { "origin" "1536 -1600 32" "classname" "light" "light" "120" "_color" "0.923729 1.000000 0.385593" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1728 -1600 32" } { "origin" "1728 -1728 32" "classname" "light" "light" "120" "_color" "0.923729 1.000000 0.385593" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1664 -1600 32" } { "origin" "1664 -1728 32" "classname" "light" "light" "120" "_color" "0.923729 1.000000 0.385593" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1856 -1728 32" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1536 -1728 32" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1664 -1344 32" } { "classname" "light" "_color" "1.000000 0.000000 0.000000" "origin" "1576 -1780 -136" } { "classname" "light" "_color" "1.000000 0.000000 0.000000" "origin" "1520 -1492 -328" } { "origin" "1760 704 88" "classname" "light" "light" "120" "_color" "1.000000 1.000000 1.000000" } { "origin" "2496 352 -40" "classname" "light" "light" "120" "_color" "1.000000 1.000000 1.000000" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1984 -960 -160" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "2144 -976 -160" } { "origin" "1840 -1040 -32" "classname" "light" "light" "120" "_color" "0.923729 1.000000 0.385593" } { "origin" "2144 -1040 -32" "classname" "light" "light" "120" "_color" "0.923729 1.000000 0.385593" } { "origin" "1248 -1040 -32" "classname" "light" "light" "120" "_color" "0.923729 1.000000 0.385593" } { "origin" "1248 -976 -160" "classname" "light" "light" "120" "_color" "0.923729 1.000000 0.385593" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1552 -1040 -32" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1088 -1024 -160" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1728 -1216 32" } { "origin" "1728 -1344 32" "classname" "light" "light" "120" "_color" "0.923729 1.000000 0.385593" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1664 -1216 32" } { "origin" "1856 -1600 32" "classname" "light" "light" "120" "_color" "0.923729 1.000000 0.385593" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1536 -1216 32" } { "origin" "1536 -1344 32" "classname" "light" "light" "120" "_color" "0.923729 1.000000 0.385593" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1856 -1344 32" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1088 -1408 -160" } { "_color" "1.000000 0.000000 0.000000" "light" "120" "classname" "light" "origin" "2112 -896 -272" } { "origin" "2176 -896 -272" "classname" "light" "light" "120" "_color" "1.000000 0.000000 0.000000" } { "_color" "1.000000 0.000000 0.000000" "light" "120" "classname" "light" "origin" "1280 -896 -272" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "2304 -896 -160" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1088 -896 -160" } { "origin" "1840 -968 -256" "classname" "light" "light" "120" "_color" "0.923729 1.000000 0.385593" } { "origin" "1088 -1152 -160" "classname" "light" "light" "120" "_color" "0.923729 1.000000 0.385593" } { "origin" "1088 -1280 -160" "classname" "light" "light" "120" "_color" "0.923729 1.000000 0.385593" } { "origin" "1856 -1216 32" "classname" "light" "light" "120" "_color" "0.923729 1.000000 0.385593" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "2304 -1024 -160" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "2304 -1152 -160" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "2304 -1280 -160" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "2304 -1408 -160" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1376 -1376 -112" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1376 -1120 -112" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1376 -1248 -112" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "2016 -1248 -112" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "2016 -1376 -112" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "2016 -1120 -112" } { "origin" "1872 -1494 -332" "classname" "light" "_color" "1.000000 0.000000 0.000000" } { "origin" "1816 -1780 -136" "_color" "1.000000 0.000000 0.000000" "classname" "light" } { "classname" "func_group" "spawnflags" "1" } { "targetname" "bstart" "origin" "1696 1128 -232" "angle" "270" "classname" "info_player_start" } { "_color" "0.923729 1.000000 0.385593" "light" "120" "classname" "light" "origin" "1696 -800 16" } { "origin" "1216 -896 -272" "classname" "light" "light" "120" "_color" "1.000000 0.000000 0.000000" } { "model" "*56" "count" "1" "classname" "target_character" "team" "countdown" } { "model" "*57" "count" "2" "classname" "target_character" "team" "countdown" } { "classname" "target_string" "origin" "1672 -1120 -240" "team" "countdown" "targetname" "t14" } { "targetname" "t107" "style" "0" "origin" "1720 -1120 -240" "classname" "func_clock" "target" "t14" "spawnflags" "14" "count" "10" "pathtarget" "boom" } yquake2-QUAKE2_8_40/stuff/mapfixes/baseq2/city1.ent000066400000000000000000003172321465112212000220020ustar00rootroot00000000000000// FIXED ENTITY STRING (by BjossiAlfreds) // // 1. Fixed 3x monster_hover (1162, 4068, 4274) never activating on Easy // // Was caused by spawnflags not being set to Medium+. Changed their // spawnflags from 2 to 258. { "spawnflags" "1" "classname" "worldspawn" "message" "Outer Courts" "sky" "unit9_" "sounds" "6" "nextmap" "city2" } { "classname" "misc_teleporter" "angle" "45" "target" "t304" "spawnflags" "1792" "origin" "-792 -1076 78" } { "classname" "misc_teleporter_dest" "angle" "270" "origin" "-784 -2464 1080" "targetname" "t304" "spawnflags" "1792" } { "origin" "24 -2632 456" "spawnflags" "2048" "classname" "item_adrenaline" } { "origin" "198 -2504 668" "classname" "light" "light" "64" "_color" "0.000000 1.000000 0.500000" } { "origin" "204 -2552 672" "spawnflags" "1792" "classname" "light" "light" "64" "_color" "0.000000 1.000000 0.500000" } { "origin" "254 -2488 668" "classname" "light" "light" "64" "_color" "0.000000 1.000000 0.500000" } { "targetname" "t91" "classname" "monster_hover" "spawnflags" "2" "origin" "-1651 -146 380" "item" "ammo_cells" } { "origin" "-1980 -1544 104" "targetname" "t1" "classname" "info_notnull" } { "targetname" "t74" "spawnflags" "1" "angle" "180" "origin" "-2632 -168 -280" "classname" "monster_soldier" "item" "ammo_shells" } { "origin" "-2948 -124 -232" "volume" "0.4" "spawnflags" "2048" "noise" "world/explod1.wav" "targetname" "t101" "classname" "target_speaker" } { "origin" "-3052 -116 -252" "targetname" "t101" "item" "ammo_bullets" "spawnflags" "1026" "classname" "monster_soldier_ss" } { "origin" "-3052 -164 -252" "targetname" "t101" "spawnflags" "1282" "classname" "monster_soldier_ss" } { "spawnflags" "2048" "delay" "0.5" "target" "t246" "origin" "-664 -2116 1024" "targetname" "t302" "classname" "trigger_relay" } { "spawnflags" "2048" "delay" "0.5" "target" "t64" "origin" "-664 -2140 1024" "targetname" "t302" "classname" "trigger_relay" } { "spawnflags" "2048" "delay" "0.5" "target" "t303" "origin" "-664 -2092 1024" "targetname" "t302" "classname" "trigger_relay" } { "spawnflags" "2048" "origin" "-684 -2164 1024" "target" "t302" "targetname" "t301" "classname" "trigger_relay" } { "spawnflags" "2048" "origin" "-692 -2192 1024" "target" "t301" "targetname" "t240" "classname" "trigger_relay" } { "origin" "-168 -2628 488" "target" "t301" "targetname" "t121" "classname" "trigger_relay" } { "target" "t301" "origin" "-540 -1880 248" "targetname" "t46" "classname" "trigger_relay" } { "origin" "184 -2464 736" "spawnflags" "2048" "targetname" "t300" "classname" "target_secret" } { "model" "*1" "message" "You have found a secret area." "spawnflags" "2048" "target" "t300" "classname" "trigger_once" } { "style" "32" "origin" "-2414 184 -264" "targetname" "red2" "_color" "1.000000 0.156863 0.125490" "spawnflags" "2048" "light" "120" "classname" "light" } { "classname" "info_player_start" "origin" "176 -2384 336" "angle" "180" } { "model" "*2" "classname" "trigger_multiple" "delay" "0.5" "target" "t297" } { "origin" "-2056 96 -168" "targetname" "city1XL" "angle" "270" "classname" "info_player_coop" } { "origin" "-2040 -56 -168" "targetname" "city1XL" "classname" "info_player_coop" "angle" "270" } { "origin" "-2000 96 -168" "targetname" "city1XL" "angle" "270" "classname" "info_player_coop" } { "origin" "-2096 128 72" "targetname" "city1XH" "classname" "info_player_coop" "angle" "270" } { "origin" "-1976 128 72" "targetname" "city1XH" "classname" "info_player_coop" "angle" "270" } { "origin" "-2032 -8 72" "targetname" "city1XH" "angle" "270" "classname" "info_player_coop" } { "origin" "216 -2464 328" "angle" "135" "targetname" "unitstart" "classname" "info_player_coop" } { "origin" "64 -2296 328" "angle" "180" "targetname" "unitstart" "classname" "info_player_coop" } { "origin" "152 -2240 328" "angle" "225" "targetname" "unitstart" "classname" "info_player_coop" } { "model" "*3" "classname" "trigger_multiple" "wait" "10" "target" "t297" } { "model" "*4" "classname" "trigger_multiple" "target" "t299" "targetname" "sewmsg" "spawnflags" "2048" "wait" "320" } { "classname" "trigger_relay" "killtarget" "sewmsg" "targetname" "t184" "origin" "-1280 624 464" "spawnflags" "2048" } { "classname" "target_speaker" "targetname" "t299" "attenuation" "-1" "noise" "world/v_bas2.wav" "origin" "-2056 -208 112" "spawnflags" "2048" } { "classname" "target_speaker" "targetname" "t298" "attenuation" "-1" "origin" "-72 -2328 376" "noise" "world/v_cit1.wav" "spawnflags" "2048" } { "model" "*5" "classname" "trigger_once" "target" "t298" "spawnflags" "2048" } { "classname" "target_speaker" "origin" "-288 -2352 512" "targetname" "t297" "noise" "world/flyby1.wav" "attenuation" "-1" "spawnflags" "2048" } { "model" "*6" "classname" "trigger_once" "target" "t297" "attenuation" "-1" "spawnflags" "2048" } { "classname" "point_combat" "origin" "-376 -2680 312" "target" "t292" "targetname" "t295" } { "classname" "point_combat" "origin" "-392 -2664 312" "target" "t291" "targetname" "t294" } { "classname" "point_combat" "origin" "-360 -2664 312" "target" "t293" "targetname" "t296" } { "classname" "point_combat" "origin" "-552 -2664 304" "targetname" "t293" } { "classname" "point_combat" "origin" "-584 -2664 304" "targetname" "t291" } { "classname" "point_combat" "origin" "-568 -2680 312" "targetname" "t292" } { "classname" "weapon_grenadelauncher" "spawnflags" "1792" "origin" "-784 -2576 1072" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "-784 -2528 1072" } { "classname" "info_player_deathmatch" "angle" "90" "origin" "-780 -2686 1080" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "100" "origin" "-1645 -155 241" } { "origin" "-2141 -126 -128" "noise" "world/amb24.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-2896 32 -272" "noise" "world/curnt2.wav" "spawnflags" "1" "classname" "target_speaker" } { "_color" "1.000000 0.976471 0.501961" "light" "150" "origin" "-1800 -144 72" "classname" "light" } { "origin" "-1732 16 8" "classname" "ammo_shells" } { "origin" "-2779 292 -397" "classname" "ammo_grenades" } { "origin" "-1648 -153 196" "targetname" "t290" "classname" "info_null" } { "style" "33" "targetname" "t290" "origin" "-1648 -152 456" "_color" "1.000000 0.976471 0.501961" "light" "100" "classname" "light" } { "origin" "-1384 608 456" "targetname" "t289" "volume" "0.7" "attenuation" "1" "noise" "world/brkglas.wav" "classname" "target_speaker" "spawnflags" "2048" } { "origin" "-32 -2502 656" "classname" "point_combat" "targetname" "t288" "target" "t296" } { "origin" "-32 -2492 656" "classname" "point_combat" "targetname" "t287" "target" "t295" } { "spawnflags" "2048" "origin" "174 -2486 640" "classname" "point_combat" "targetname" "t285" "target" "t288" } { "spawnflags" "2048" "origin" "172 -2490 640" "classname" "point_combat" "targetname" "t286" "target" "t287" } { "_color" "0.000000 1.000000 0.500000" "light" "64" "classname" "light" "origin" "174 -2488 680" } { "_color" "0.000000 1.000000 0.500000" "light" "64" "classname" "light" "origin" "206 -2448 668" } { "_color" "0.000000 1.000000 0.500000" "classname" "light" "light" "64" "origin" "234 -2600 664" } { "model" "*7" "target" "t289" "spawnflags" "2048" "_minlight" "0.8" "dmg" "1" "health" "25" "mass" "99" "classname" "func_explosive" } { "team" "hidey1" "spawnflags" "1792" "classname" "ammo_grenades" "origin" "578 -2656 480" } { "spawnflags" "0" "classname" "item_power_shield" "origin" "-420 -714 100" } { "_cone" "30" "target" "t290" "classname" "light" "light" "400" "_color" "1.000000 0.976471 0.501961" "origin" "-1648 -154 484" } { "classname" "weapon_grenadelauncher" "spawnflags" "1792" "origin" "-1728 -176 512" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "-1628 -188 524" } { "classname" "info_player_deathmatch" "angle" "0" "origin" "-1740 -132 516" } { "model" "*8" "classname" "func_wall" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "angle" "180" "origin" "-936 448 256" } { "model" "*9" "classname" "func_wall" "spawnflags" "1792" } { "model" "*10" "classname" "func_wall" "spawnflags" "1792" } { "origin" "-1808 336 614" "classname" "light" "light" "32" } { "classname" "misc_teleporter_dest" "angle" "270" "targetname" "valley" "origin" "592 -2440 456" "spawnflags" "1792" } { "classname" "misc_teleporter" "angle" "90" "spawnflags" "1792" "target" "valley" "origin" "-1780 -268 16" } { "classname" "ammo_cells" "spawnflags" "1792" "origin" "-2708 -100 -280" } { "classname" "ammo_rockets" "spawnflags" "512" "origin" "-2544 -100 -280" } { "classname" "misc_teleporter" "origin" "-3080 -136 -272" "angle" "0" "spawnflags" "1792" "target" "t284" } { "origin" "-1864 -1928 408" "spawnflags" "1792" "light" "120" "classname" "light" } { "origin" "-2032 -1928 408" "spawnflags" "1792" "light" "120" "classname" "light" } { "classname" "misc_teleporter_dest" "angle" "0" "origin" "-2232 -1928 376" "spawnflags" "1792" "targetname" "t284" } { "origin" "-1748 -1548 4" "angles" "-45 132 0" "classname" "info_player_intermission" } { "classname" "weapon_machinegun" "origin" "-2168 -1016 -56" "spawnflags" "1792" } { "classname" "ammo_bullets" "spawnflags" "1792" "origin" "-1896 -1056 -56" } { "spawnflags" "1792" "classname" "ammo_slugs" "origin" "-880 -2160 912" } { "spawnflags" "2048" "classname" "item_adrenaline" "origin" "-1152 788 420" } { "spawnflags" "2048" "classname" "item_invulnerability" "origin" "-952 -1718 588" } { "model" "*11" "classname" "func_wall" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "origin" "-892 -1580 590" } { "classname" "info_player_deathmatch" "angle" "225" "origin" "-28 -1904 112" "_minlight" "0.8" } { "spawnflags" "1792" "classname" "ammo_cells" "origin" "-672 -1048 112" } { "classname" "weapon_hyperblaster" "spawnflags" "1792" "origin" "-612 -1052 112" } { "classname" "weapon_hyperblaster" "spawnflags" "1792" "origin" "-2112 -236 -88" } { "classname" "misc_teleporter" "angle" "90" "_minlight" "0.1" "target" "t282" "origin" "-1812 192 548" } { "origin" "-1808 432 584" "light" "64" "classname" "light" } { "classname" "misc_teleporter_dest" "angle" "180" "_minlight" "0.1" "origin" "-1696 448 544" "targetname" "t283" } { "light" "56" "classname" "light" "origin" "-1808 504 584" } { "light" "32" "classname" "light" "origin" "-1816 192 614" } { "classname" "light" "light" "32" "origin" "-1696 448 612" } { "classname" "weapon_grenadelauncher" "spawnflags" "1792" "origin" "-1808 328 544" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "-1832 376 544" } { "spawnflags" "1792" "classname" "misc_teleporter_dest" "angle" "90" "origin" "-2536 48 472" "targetname" "t282" } { "model" "*12" "classname" "func_wall" "spawnflags" "1792" } { "model" "*13" "classname" "func_wall" "spawnflags" "1792" } { "angle" "90" "classname" "misc_teleporter" "spawnflags" "1792" "_minlight" "0.1" "origin" "-1836 -1342 -48" "target" "t283" } { "classname" "misc_teleporter" "angle" "90" "target" "t281" "spawnflags" "1792" "_minlight" "0.1" "origin" "-2236 -1334 -48" } { "classname" "misc_teleporter_dest" "angle" "270" "origin" "-912 -2008 1000" "targetname" "t281" "spawnflags" "1792" } { "origin" "184 -2496 328" "angle" "90" "classname" "info_player_deathmatch" } { "origin" "-2040 -1960 48" "classname" "ammo_grenades" "spawnflags" "2048" } { "classname" "ammo_bullets" "origin" "-984 -1816 544" } { "spawnflags" "2048" "origin" "8 -1992 104" "classname" "item_armor_combat" } { "classname" "ammo_grenades" "origin" "532 -2456 448" "spawnflags" "2048" } { "origin" "608 -2456 448" "classname" "ammo_shells" "spawnflags" "2048" } { "spawnflags" "0" "origin" "656 -2496 448" "classname" "ammo_slugs" } { "model" "*14" "classname" "trigger_once" "target" "t199" } { "model" "*15" "classname" "func_wall" "spawnflags" "2048" } { "spawnflags" "2048" "classname" "item_armor_combat" "origin" "-2496 -680 -56" } { "classname" "item_quad" "origin" "-2176 -1120 336" "team" "q2best" "spawnflags" "1792" } { "model" "*16" "classname" "func_wall" "spawnflags" "1792" } { "model" "*17" "classname" "func_wall" "spawnflags" "1792" } { "classname" "weapon_shotgun" "spawnflags" "1792" "origin" "-960 128 412" } { "classname" "info_player_deathmatch" "angle" "315" "origin" "-1452 768 256" } { "spawnflags" "2048" "classname" "ammo_rockets" "origin" "-1352 568 72" } { "classname" "item_armor_combat" "spawnflags" "1792" "origin" "-1676 -160 212" } { "spawnflags" "2048" "classname" "item_health" "origin" "-1860 -108 28" } { "classname" "ammo_bullets" "spawnflags" "2048" "origin" "-2488 160 -400" } { "classname" "ammo_bullets" "spawnflags" "1792" "origin" "-2564 156 -388" } { "classname" "ammo_bullets" "spawnflags" "1792" "origin" "-2748 152 -388" } { "spawnflags" "2048" "classname" "item_health_large" "origin" "-2008 -120 -176" } { "classname" "ammo_cells" "spawnflags" "1536" "origin" "-2116 -64 -164" } { "spawnflags" "2048" "origin" "-3096 -104 -280" "classname" "ammo_grenades" } { "classname" "weapon_shotgun" "spawnflags" "1792" "origin" "-2000 -144 104" } { "classname" "item_health_small" "origin" "-2060 -360 16" } { "classname" "item_health_small" "origin" "-2060 -304 24" } { "classname" "item_health_small" "origin" "-2056 -400 16" } { "classname" "ammo_cells" "spawnflags" "1792" "origin" "-2312 -1556 92" } { "classname" "info_player_deathmatch" "angle" "225" "origin" "-1380 -1556 104" } { "classname" "ammo_cells" "spawnflags" "1792" "origin" "188 -2592 656" } { "classname" "ammo_rockets" "spawnflags" "1792" "origin" "-116 -2440 656" } { "classname" "weapon_railgun" "spawnflags" "1792" "origin" "-904 -2148 1012" } { "spawnflags" "2048" "classname" "item_health_large" "origin" "278 -2591 656" } { "_color" "0.000000 1.000000 0.500000" "light" "64" "classname" "light" "spawnflags" "1792" "origin" "260 -2544 672" } { "classname" "item_invulnerability" "team" "qbest" "origin" "256 -2640 664" "spawnflags" "1792" } { "classname" "weapon_rocketlauncher" "spawnflags" "1792" "origin" "-232 -2344 512" } { "classname" "info_player_deathmatch" "angle" "45" "origin" "-842 -2694 712" } { "classname" "ammo_shells" "spawnflags" "1792" "origin" "168 -2424 336" } { "classname" "weapon_shotgun" "spawnflags" "1792" "origin" "160 -2192 320" } { "spawnflags" "2048" "classname" "key_red_key" "origin" "-2520 192 -240" } { "model" "*18" "classname" "func_wall" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "angle" "45" "origin" "-2220 -860 -56" } { "classname" "info_player_deathmatch" "angle" "270" "origin" "-1050 776 416" } { "spawnflags" "1792" "classname" "item_armor_body" "origin" "-1429 610 465" } { "classname" "trigger_always" "spawnflags" "1792" "target" "t20" "origin" "-1245 752 456" } { "model" "*19" "classname" "func_wall" "spawnflags" "1792" } { "model" "*20" "_minlight" "0.1" "spawnflags" "2048" "wait" "-1" "health" "5" "delay" "10" "lip" "64" "angle" "180" "classname" "func_door" } { "classname" "ammo_cells" "spawnflags" "1792" "origin" "-2800 212 8" } { "model" "*21" "classname" "func_wall" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "angle" "135" "origin" "-1992 -248 -168" } { "classname" "light" "_color" "1.000000 0.796078 0.003922" "light" "64" "origin" "-1808 520 612" } { "classname" "info_player_deathmatch" "angle" "270" "origin" "-1808 532 548" "_minlight" "0.1" } { "target" "pumpdoor" "classname" "trigger_always" "origin" "-1776 -128 -276" "spawnflags" "1792" } { "model" "*22" "classname" "func_wall" "spawnflags" "1792" } { "classname" "light" "light" "120" "spawnflags" "1792" "origin" "-2224 -1928 408" } { "model" "*23" "classname" "func_wall" "spawnflags" "2048" } { "model" "*24" "spawnflags" "1792" "message" "This door is opened elsewhere." "wait" "-1" "_minlight" "0.2" "team" "medicdoor" "angle" "90" "classname" "func_door" } { "model" "*25" "spawnflags" "1792" "message" "This door is opened elsewhere." "wait" "-1" "_minlight" "0.2" "team" "medicdoor" "angle" "270" "classname" "func_door" } { "model" "*26" "classname" "trigger_once" "target" "m1" "spawnflags" "2048" } { "classname" "trigger_always" "target" "t209" "spawnflags" "1792" "origin" "104 -2344 360" } { "model" "*27" "spawnflags" "2048" "classname" "func_wall" } { "classname" "light" "light" "175" "origin" "-608 -2648 368" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "175" "origin" "-704 -2656 368" "_color" "1.000000 0.976471 0.501961" } { "origin" "-2032 112 -74" "_color" "1.000000 0.920949 0.343874" "light" "90" "classname" "light" } { "origin" "-2032 180 -188" "targetname" "t278" "classname" "info_notnull" } { "origin" "-2032 32 -188" "targetname" "t277" "classname" "info_notnull" } { "classname" "light" "light" "90" "_color" "1.000000 0.920949 0.343874" "origin" "-2032 176 -74" } { "spawnflags" "1792" "classname" "weapon_railgun" "origin" "-2040 -1940 384" } { "classname" "info_player_deathmatch" "angle" "180" "origin" "-1864 -1928 376" } { "model" "*28" "classname" "func_wall" "spawnflags" "1792" } { "classname" "point_combat" "targetname" "t276" "origin" "-32 -2480 656" "target" "t294" } { "classname" "point_combat" "targetname" "t180" "target" "t276" "origin" "174 -2496 640" "spawnflags" "2048" } { "model" "*29" "classname" "trigger_monsterjump" "speed" "400" "height" "16" "angle" "180" "spawnflags" "2048" } { "model" "*30" "classname" "func_door" "angle" "270" "team" "medicdoor" "targetname" "medguy1" "_minlight" "0.1" "wait" "-1" "message" "This door is opened elsewhere." "spawnflags" "2048" } { "model" "*31" "classname" "func_door" "angle" "90" "team" "medicdoor" "targetname" "medguy1" "_minlight" "0.1" "wait" "-1" "message" "This door is opened elsewhere." "spawnflags" "2048" } { "classname" "point_combat" "targetname" "t273" "target" "t274" "origin" "-2080 -864 -72" "spawnflags" "2048" } { "classname" "point_combat" "targetname" "t274" "origin" "-2080 -904 -72" "spawnflags" "2048" } { "classname" "point_combat" "targetname" "t55" "target" "t273" "origin" "-1912 -864 -72" "spawnflags" "2048" } { "classname" "func_timer" "random" "2" "wait" "4" "origin" "-2600 256 464" "target" "t272" "targetname" "hv1" "spawnflags" "2048" } { "classname" "trigger_relay" "origin" "-2600 232 464" "target" "t271" "targetname" "t272" "spawnflags" "2048" } { "classname" "trigger_relay" "killtarget" "hv1" "origin" "-2600 208 464" "targetname" "t271" "delay" ".75" "spawnflags" "2048" } { "origin" "-2548 208 496" "classname" "monster_hover" "spawnflags" "258" "targetname" "t271" "deathtarget" "hv2" } { "origin" "-2032 -1112 112" "target" "t190" "delay" "1" "targetname" "t10" "classname" "trigger_relay" } { "origin" "-2584 0 472" "killtarget" "hv4" "targetname" "t267" "classname" "trigger_relay" "delay" ".75" "spawnflags" "2048" } { "targetname" "hv4" "target" "t266" "origin" "-2584 48 464" "wait" "4" "random" "2" "classname" "func_timer" "spawnflags" "2048" } { "target" "t267" "targetname" "t266" "origin" "-2584 24 472" "classname" "trigger_relay" "spawnflags" "2048" } { "target" "t264" "targetname" "hv2" "origin" "-2624 304 464" "wait" "4" "random" "2" "classname" "func_timer" "spawnflags" "2048" } { "target" "t265" "targetname" "t264" "origin" "-2624 280 464" "classname" "trigger_relay" "spawnflags" "2048" } { "targetname" "t265" "origin" "-2624 256 464" "killtarget" "hv2" "classname" "trigger_relay" "delay" ".75" "spawnflags" "2048" } { "origin" "-2576 208 464" "targetname" "hv3" "target" "t262" "wait" "4" "random" "2" "classname" "func_timer" "spawnflags" "2048" } { "origin" "-2576 184 464" "target" "t263" "targetname" "t262" "classname" "trigger_relay" "spawnflags" "2048" } { "origin" "-2576 160 464" "killtarget" "hv3" "targetname" "t263" "classname" "trigger_relay" "delay" ".75" "spawnflags" "2048" } { "targetname" "t135" "message" "Find Communications Laser\nData CD in Upper Palace." "origin" "-1264 720 480" "classname" "target_help" "spawnflags" "2048" } { "model" "*32" "angle" "-2" "classname" "trigger_push" "spawnflags" "2048" } { "model" "*33" "spawnflags" "2048" "classname" "func_wall" } { "origin" "-528 -1968 1032" "targetname" "t61" "target" "t260" "classname" "trigger_relay" "spawnflags" "2304" } { "origin" "-552 -1992 1032" "targetname" "t121" "target" "t260" "classname" "trigger_relay" "spawnflags" "2304" } { "model" "*34" "spawnflags" "2305" "target" "t242" "targetname" "t260" "classname" "trigger_counter" "count" "2" } { "origin" "-672 -2392 488" "target" "t259" "targetname" "t257" "classname" "path_corner" "spawnflags" "2048" } { "origin" "-536 -2608 488" "target" "t257" "targetname" "t256" "classname" "path_corner" "spawnflags" "2048" } { "origin" "-184 -2608 488" "targetname" "t259" "target" "t256" "classname" "path_corner" "spawnflags" "2048" } { "origin" "-624 -2600 1032" "target" "t255" "targetname" "t254" "classname" "path_corner" "spawnflags" "2048" } { "origin" "-680 -1904 1032" "target" "t254" "targetname" "t253" "classname" "path_corner" "spawnflags" "2048" } { "origin" "-576 -1976 1032" "targetname" "t255" "target" "t253" "classname" "path_corner" "spawnflags" "2048" } { "origin" "-840 -1800 248" "target" "t251" "targetname" "t250" "classname" "path_corner" "spawnflags" "2048" } { "origin" "-600 -1872 248" "target" "t252" "targetname" "t251" "classname" "path_corner" "spawnflags" "2048" } { "origin" "-848 -2192 248" "targetname" "t252" "target" "t250" "classname" "path_corner" "spawnflags" "2048" } { "classname" "ammo_shells" "origin" "-2544 -632 -56" } { "origin" "-1624 -1584 552" "spawnflags" "1" "targetname" "t40" "angle" "180" "classname" "monster_hover" } { "origin" "-1744 -1608 136" "target" "medguy1" "targetname" "t40" "classname" "trigger_relay" } { "origin" "-456 -712 120" "targetname" "t249" "classname" "target_secret" "spawnflags" "2048" } { "classname" "light" "light" "200" "origin" "-1888 -1264 1248" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "origin" "-1832 -1256 1248" "light" "200" "classname" "light" } { "classname" "light" "light" "200" "origin" "-1944 -1264 1248" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "origin" "-1992 -1264 1248" "light" "200" "classname" "light" } { "classname" "light" "light" "200" "origin" "-2048 -1264 1248" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "300" "origin" "-2136 -1304 1136" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2136 -1304 1088" "light" "300" "classname" "light" } { "classname" "light" "light" "200" "origin" "-2112 -1264 1248" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "origin" "-1848 -1264 1072" "light" "300" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2128 -1304 1208" "light" "300" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-1872 -1264 1208" "light" "200" "classname" "light" } { "classname" "item_health" "origin" "-96 -1944 104" } { "spawnflags" "1536" "classname" "item_health" "origin" "-144 -1936 104" } { "classname" "item_armor_jacket" "origin" "8 -1992 136" "spawnflags" "1792" } { "model" "*35" "classname" "trigger_push" "angle" "-2" "speed" "150" "spawnflags" "2048" } { "model" "*36" "spawnflags" "2048" "classname" "trigger_push" "angle" "0" "speed" "150" } { "classname" "ammo_bullets" "origin" "-2616 208 -24" } { "model" "*37" "angle" "-2" "speed" "100" "classname" "trigger_push" "spawnflags" "2048" } { "origin" "-888 -1912 536" "target" "t238" "targetname" "g2" "classname" "trigger_relay" } { "origin" "-240 -1360 88" "target" "t239" "targetname" "t238" "classname" "trigger_relay" "spawnflags" "2048" } { "model" "*38" "target" "t238" "classname" "trigger_once" "spawnflags" "2048" } { "targetname" "t237" "classname" "point_combat" "origin" "-532 -1760 88" "spawnflags" "2048" } { "targetname" "t236" "origin" "-508 -1648 96" "classname" "point_combat" "spawnflags" "2048" } { "targetname" "t235" "target" "t233" "origin" "-300 -1448 96" "classname" "point_combat" "spawnflags" "2048" } { "targetname" "t234" "target" "t232" "classname" "point_combat" "origin" "-332 -1448 96" "spawnflags" "2048" } { "target" "t236" "targetname" "t232" "origin" "-484 -1664 88" "classname" "point_combat" "spawnflags" "2048" } { "target" "t237" "targetname" "t233" "classname" "point_combat" "origin" "-364 -1656 88" } { "item" "item_armor_shard" "spawnflags" "1" "targetname" "t239" "angle" "225" "origin" "-336 -1344 104" "classname" "monster_soldier_ss" } { "spawnflags" "1" "targetname" "t239" "target" "t235" "angle" "225" "origin" "-320 -1280 104" "classname" "monster_soldier_ss" } { "item" "item_armor_shard" "spawnflags" "257" "targetname" "t239" "target" "t234" "angle" "270" "origin" "-368 -1264 104" "classname" "monster_soldier_ss" } { "spawnflags" "769" "targetname" "t239" "angle" "270" "origin" "-416 -1208 104" "classname" "monster_soldier_ss" } { "spawnflags" "1" "targetname" "t239" "angle" "225" "origin" "-280 -1344 104" "classname" "monster_soldier_ss" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/fan1.wav" "origin" "-1812 444 528" } { "origin" "-1928 -1264 128" "targetname" "t231" "target" "t181" "classname" "trigger_relay" "spawnflags" "2048" } { "origin" "-1912 -1240 120" "delay" "360" "targetname" "t231" "target" "t181" "classname" "trigger_relay" "spawnflags" "2048" } { "wait" "20" "random" "10" "classname" "func_timer" "origin" "-776 -2120 1000" "targetname" "t121" "target" "t227" "spawnflags" "2304" } { "classname" "trigger_relay" "origin" "-776 -2088 1000" "delay" "4" "targetname" "t227" "target" "t228" "spawnflags" "2304" } { "killtarget" "t121" "delay" ".5" "classname" "trigger_relay" "origin" "-776 -2056 1000" "targetname" "t228" "spawnflags" "2304" } { "target" "t225" "wait" "18" "random" "9" "classname" "func_timer" "origin" "-792 -2216 1000" "targetname" "t240" "spawnflags" "2048" } { "classname" "trigger_relay" "origin" "-792 -2184 1000" "delay" "4" "targetname" "t225" "target" "t226" "spawnflags" "2048" } { "killtarget" "t43" "delay" ".5" "classname" "trigger_relay" "origin" "-792 -2152 1000" "targetname" "t226" "spawnflags" "2048" } { "wait" "16" "origin" "-776 -2008 1000" "classname" "func_timer" "random" "8" "targetname" "t243" "target" "t229" "spawnflags" "2304" } { "delay" "10" "origin" "-776 -1976 1000" "classname" "trigger_relay" "targetname" "t229" "target" "t230" "spawnflags" "2304" } { "origin" "-776 -1944 1000" "classname" "trigger_relay" "delay" ".5" "killtarget" "t65" "targetname" "t230" "spawnflags" "2304" } { "killtarget" "t61" "delay" ".5" "classname" "trigger_relay" "origin" "-712 -2488 1000" "targetname" "t223" "spawnflags" "2048" } { "wait" "4" "random" "3" "classname" "func_timer" "origin" "-712 -2552 1000" "target" "t224" "targetname" "t243" "spawnflags" "2048" } { "classname" "trigger_relay" "targetname" "t222" "delay" ".5" "killtarget" "t46" "origin" "-712 -2584 1000" "spawnflags" "2048" } { "classname" "func_timer" "random" "3" "wait" "4" "targetname" "t46" "target" "t221" "origin" "-712 -2648 1000" "spawnflags" "2048" } { "classname" "trigger_relay" "origin" "-712 -2520 1000" "delay" "4" "target" "t223" "targetname" "t224" "spawnflags" "2048" } { "classname" "trigger_relay" "origin" "-712 -2616 1000" "targetname" "t221" "target" "t222" "spawnflags" "2048" } { "origin" "-2512 -352 32" "spawnflags" "1" "angle" "270" "classname" "monster_tank_commander" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-1632 -160 -184" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-1632 -160 -120" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-1632 -160 -56" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-1632 -160 8" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-1632 -160 72" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-1192 -40 72" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-1192 -104 72" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-1256 -168 72" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-1632 -160 -312" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-1576 -168 72" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-1512 -168 72" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-1448 -168 72" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-1384 -168 72" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-1320 -168 72" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-1192 24 72" } { "origin" "-2896 48 -272" "light" "120" "classname" "light" } { "attenuation" "-1" "volume" ".1" "noise" "World/battle5.wav" "classname" "target_speaker" "origin" "-2200 -1248 336" "targetname" "t219" "wait" "11" } { "wait" "5" "random" "1" "spawnflags" "1" "classname" "func_timer" "origin" "-2208 -1296 320" "target" "t219" } { "attenuation" "-1" "volume" ".35" "noise" "World/battle5.wav" "classname" "target_speaker" "origin" "-2232 -1248 336" "targetname" "t218" } { "wait" "15" "random" "5" "spawnflags" "1" "classname" "func_timer" "origin" "-2240 -1296 320" "target" "t218" } { "origin" "-2264 -1248 336" "classname" "target_speaker" "noise" "World/battle5.wav" "volume" ".2" "attenuation" "-1" "targetname" "t217" } { "origin" "-2272 -1296 320" "classname" "func_timer" "spawnflags" "1" "random" "4" "wait" "16" "target" "t217" } { "model" "*39" "classname" "trigger_once" "target" "t9" } { "spawnflags" "2048" "origin" "228 -2352 380" "message" "Find functioning\nData Spinner\nin Outer Courts." "classname" "target_help" "targetname" "t139" } { "origin" "-2880 208 104" "classname" "func_timer" "spawnflags" "1" "random" "3" "wait" "11" "target" "t220" } { "origin" "-2872 256 104" "classname" "target_speaker" "noise" "World/battle5.wav" "volume" "0.3" "attenuation" "1" "targetname" "t220" } { "attenuation" "1" "volume" "0.3" "noise" "World/battle5.wav" "classname" "target_speaker" "origin" "-2792 160 -136" "targetname" "t212" } { "wait" "12" "random" "4" "spawnflags" "1" "classname" "func_timer" "origin" "-2904 136 -136" "target" "t212" } { "attenuation" "1" "volume" "0.4" "noise" "World/battle5.wav" "classname" "target_speaker" "origin" "-2728 48 320" "targetname" "t211" } { "wait" "6" "random" "1" "spawnflags" "1" "classname" "func_timer" "origin" "-2736 0 320" "target" "t211" } { "attenuation" "1" "volume" ".5" "noise" "World/battle5.wav" "classname" "target_speaker" "origin" "-1712 -1536 416" "targetname" "t215" } { "wait" "16" "random" "2" "spawnflags" "1" "classname" "func_timer" "origin" "-1720 -1584 416" "target" "t215" } { "attenuation" "1" "volume" "1" "noise" "World/battle5.wav" "classname" "target_speaker" "origin" "-2016 -1608 328" "targetname" "t214" } { "wait" "6" "random" "3" "spawnflags" "1" "classname" "func_timer" "origin" "-2024 -1656 328" "target" "t214" } { "attenuation" "1" "volume" ".6" "noise" "World/battle5.wav" "classname" "target_speaker" "origin" "-2200 -1424 200" "targetname" "t213" } { "wait" "10" "random" "3" "spawnflags" "1" "classname" "func_timer" "origin" "-2208 -1472 200" "target" "t213" } { "model" "*40" "speed" "150" "angle" "-2" "classname" "trigger_push" } { "origin" "104 -2360 360" "target" "t209" "delay" "2" "targetname" "t139" "classname" "trigger_relay" "spawnflags" "2048" } { "model" "*41" "targetname" "t209" "wait" "-1" "_minlight" "0.1" "team" "dent2" "classname" "func_door" "angle" "270" "spawnflags" "4" } { "model" "*42" "targetname" "t209" "wait" "-1" "_minlight" "0.1" "team" "dent2" "classname" "func_door" "angle" "90" "spawnflags" "4" } { "model" "*43" "targetname" "t209" "wait" "-1" "_minlight" "0.1" "team" "dent2" "classname" "func_door" "angle" "-1" "spawnflags" "4" } { "origin" "-1816 444 292" "noise" "world/curnt3.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1812 444 528" "noise" "world/fan1.wav" "spawnflags" "1" "classname" "target_speaker" } { "spawnflags" "2048" "origin" "-2072 -1920 48" "classname" "item_health" } { "light" "150" "_style" "2" "origin" "24 -2664 528" "spawnflags" "1" "classname" "light" } { "model" "*44" "message" "You have found a secret area." "target" "t198" "classname" "trigger_once" "spawnflags" "2048" } { "targetname" "t198" "origin" "304 -2648 312" "classname" "target_secret" "spawnflags" "2048" } { "origin" "568 -2616 480" "classname" "target_speaker" "noise" "world/amb3.wav" "spawnflags" "1" "attenuation" "3" "volume" "0.4" } { "origin" "320 -2632 400" "classname" "target_speaker" "noise" "world/amb3.wav" "spawnflags" "1" "attenuation" "3" "volume" "0.4" } { "origin" "-8 -2336 400" "classname" "target_speaker" "noise" "world/amb3.wav" "spawnflags" "1" "attenuation" "3" "volume" "0.4" } { "origin" "144 -2336 400" "classname" "target_speaker" "noise" "world/amb3.wav" "spawnflags" "1" "attenuation" "3" "volume" "0.4" } { "origin" "-232 -2336 424" "classname" "target_speaker" "noise" "World/battle5.wav" "volume" ".4" "attenuation" "3" "targetname" "t192" } { "classname" "item_adrenaline" "origin" "-3096 -152 -280" "team" "adren" "spawnflags" "2048" } { "angle" "180" "classname" "monster_soldier" "targetname" "t58" "spawnflags" "1536" "origin" "-2896 -96 40" } { "classname" "monster_soldier" "angle" "180" "targetname" "t58" "spawnflags" "512" "origin" "-2544 -56 40" } { "classname" "item_health" "origin" "-2008 -1920 48" } { "spawnflags" "1792" "classname" "item_armor_jacket" "origin" "-2040 -1960 80" } { "model" "*45" "classname" "trigger_once" "target" "t10" "wait" "1" "spawnflags" "2048" } { "classname" "light" "light" "125" "origin" "-2040 -1928 40" } { "classname" "target_speaker" "noise" "world/lava1.wav" "spawnflags" "1" "origin" "-1080 -1920 80" } { "origin" "-736 -1832 712" "attenuation" "1" "volume" "1" "noise" "World/battle5.wav" "classname" "target_speaker" "targetname" "t195" } { "classname" "func_timer" "spawnflags" "1" "random" "3" "wait" "14" "origin" "-736 -1824 408" "target" "t195" } { "classname" "func_timer" "spawnflags" "1" "random" "2" "wait" "16" "origin" "-728 -2208 536" "target" "t196" } { "origin" "-728 -2168 536" "attenuation" "1" "volume" "1" "noise" "World/battle5.wav" "classname" "target_speaker" "targetname" "t196" "random" "4" } { "classname" "func_timer" "spawnflags" "1" "random" "3" "wait" "9" "origin" "-656 -2616 336" "targetname" "t197" } { "origin" "-656 -2576 336" "attenuation" "1" "volume" ".3" "noise" "World/battle5.wav" "classname" "target_speaker" "target" "t197" } { "classname" "target_speaker" "noise" "World/battle5.wav" "volume" "1" "attenuation" "1" "origin" "-352 -1696 680" "targetname" "t194" } { "origin" "-352 -1736 680" "wait" "7" "random" "2" "spawnflags" "1" "classname" "func_timer" "target" "t194" } { "origin" "-560 -2872 632" "attenuation" "1" "volume" "1" "noise" "World/battle5.wav" "classname" "target_speaker" "targetname" "t191" } { "origin" "-584 -2848 640" "wait" "6" "random" "2" "spawnflags" "1" "classname" "func_timer" "target" "t191" } { "attenuation" "1" "volume" ".6" "noise" "World/battle5.wav" "classname" "target_speaker" "origin" "-368 -2336 440" "targetname" "t192" } { "wait" "10" "random" "9" "spawnflags" "1" "classname" "func_timer" "origin" "-376 -2384 440" "target" "t192" } { "classname" "func_timer" "spawnflags" "1" "random" "4" "wait" "10" "origin" "-416 -1840 312" "target" "t193" } { "classname" "target_speaker" "noise" "World/battle5.wav" "volume" "1" "attenuation" "1" "origin" "-416 -1800 312" "targetname" "t193" } { "origin" "-2016 -1736 72" "targetname" "t10" "classname" "target_explosion" "spawnflags" "2048" } { "origin" "-2072 -1732 32" "delay" "2.5" "targetname" "t10" "classname" "target_explosion" "spawnflags" "2048" } { "origin" "-2004 -1628 288" "target" "t189" "delay" "3" "targetname" "t10" "classname" "trigger_relay" } { "origin" "-2044 -1628 288" "target" "t186" "delay" "2.5" "targetname" "t10" "classname" "trigger_relay" } { "delay" "1" "origin" "-2088 -1628 288" "target" "t185" "targetname" "t10" "classname" "trigger_relay" } { "origin" "-2036 -1932 132" "noise" "world/fan1.wav" "classname" "target_speaker" "spawnflags" "1" } { "model" "*46" "targetname" "t190" "mass" "500" "dmg" "0" "classname" "func_explosive" "spawnflags" "2048" } { "origin" "258 -2640 656" "classname" "item_health_large" "spawnflags" "3072" } { "origin" "262 -2539 656" "classname" "item_health_large" "spawnflags" "1792" } { "classname" "light" "light" "90" "origin" "-3016 -136 -232" } { "classname" "trigger_relay" "targetname" "t184" "target" "t135" "origin" "-1344 744 440" "spawnflags" "2048" } { "noise" "world/lava1.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-2194 -1302 -82" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/lava1.wav" "origin" "-1850 -1294 -80" } { "classname" "target_speaker" "noise" "world/lava1.wav" "spawnflags" "1" "origin" "-562 -874 56" } { "classname" "target_speaker" "noise" "world/pump2.wav" "spawnflags" "1" "origin" "-1312 600 448" } { "dmg" "0" "classname" "target_explosion" "targetname" "t101" "delay" ".5" "origin" "-2952 -144 -232" "spawnflags" "2048" } { "dmg" "0" "classname" "target_explosion" "targetname" "t101" "delay" "2" "origin" "-2952 -104 -240" "spawnflags" "2048" } { "classname" "target_explosion" "dmg" "0" "targetname" "t101" "delay" "3.25" "origin" "-2944 -160 -264" "spawnflags" "2048" } { "classname" "item_health_large" "origin" "-2544 -592 -56" } { "origin" "-2040 -1192 112" "classname" "target_speaker" "spawnflags" "2" "noise" "world/klaxon1.wav" "targetname" "t181" } { "classname" "ammo_shells" "origin" "-1852 436 316" } { "origin" "-1158 522 448" "spawnflags" "1" "noise" "world/pump2.wav" "classname" "target_speaker" } { "spawnflags" "1" "noise" "world/curnt1.wav" "classname" "target_speaker" "origin" "-1232 488 400" } { "spawnflags" "1" "noise" "world/bigpump.wav" "classname" "target_speaker" "origin" "-1232 480 24" } { "classname" "target_speaker" "noise" "world/l_hum1.wav" "spawnflags" "1" "origin" "-1432 608 448" } { "classname" "item_breather" "origin" "-2400 152 -288" "spawnflags" "2048" } { "model" "*47" "classname" "func_explosive" "dmg" "1" "mass" "700" "targetname" "t101" "spawnflags" "2048" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/fan1.wav" "origin" "-3024 -144 -216" } { "classname" "trigger_relay" "targetname" "t10" "target" "t182" "origin" "-1720 -1480 536" } { "model" "*48" "classname" "trigger_counter" "spawnflags" "1" "targetname" "t182" "count" "2" "target" "t183" } { "classname" "trigger_relay" "targetname" "medguy1" "target" "t182" "origin" "-1688 -1480 536" } { "noise" "world/klaxon1.wav" "spawnflags" "2050" "classname" "target_speaker" "origin" "-1432 -1576 152" "targetname" "t181" } { "noise" "world/klaxon1.wav" "spawnflags" "2050" "classname" "target_speaker" "origin" "-1800 -1384 88" "targetname" "t181" } { "noise" "world/klaxon1.wav" "spawnflags" "2050" "classname" "target_speaker" "origin" "-2232 -1384 88" "targetname" "t181" } { "noise" "world/klaxon1.wav" "spawnflags" "2" "classname" "target_speaker" "origin" "-2040 -952 88" } { "noise" "world/klaxon1.wav" "spawnflags" "2" "classname" "target_speaker" "origin" "-2040 -1192 112" "targetname" "t181" } { "classname" "target_speaker" "spawnflags" "2" "noise" "world/klaxon1.wav" "origin" "-1384 -1784 184" "targetname" "t181" } { "model" "*49" "target" "t231" "classname" "trigger_once" "spawnflags" "2048" } { "spawnflags" "256" "target" "monster_hover" "angle" "135" "classname" "target_spawner" "targetname" "t62" "origin" "-248 -2692 528" } { "spawnflags" "256" "classname" "target_spawner" "targetname" "t62" "angle" "135" "target" "monster_hover" "origin" "-324 -2744 528" } { "origin" "-580 -1056 68" "classname" "point_combat" "targetname" "t178" "target" "t179" } { "classname" "point_combat" "origin" "-600 -1032 64" "targetname" "t177" "target" "t178" "spawnflags" "2048" } { "classname" "point_combat" "origin" "-272 -1456 92" "targetname" "t179" "spawnflags" "2048" } { "model" "*50" "origin" "-964 -2000 72" "targetname" "drtrp1" "wait" "6" "team" "drtrp" "speed" "100" "distance" "-90" "angle" "-2" "spawnflags" "2124" "classname" "func_door_rotating" } { "origin" "24 -2664 500" "classname" "item_health_mega" "spawnflags" "1792" } { "classname" "item_health" "origin" "-1360 696 408" } { "origin" "-1200 -80 104" "noise" "world/curnt3.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1368 -168 104" "noise" "world/curnt3.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1584 -168 104" "noise" "world/curnt3.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1632 -152 0" "noise" "world/curnt1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1632 -152 -128" "noise" "world/curnt1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1632 -160 -296" "noise" "world/curnt1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1632 -160 -376" "noise" "world/bigpump.wav" "spawnflags" "1" "classname" "target_speaker" } { "noise" "world/drip_amb.wav" "spawnflags" "1" "origin" "-1832 -136 64" "classname" "target_speaker" } { "noise" "world/drip_amb.wav" "spawnflags" "1" "origin" "-1792 -200 -8" "classname" "target_speaker" } { "noise" "world/drip_amb.wav" "spawnflags" "1" "origin" "-1800 -160 -240" "classname" "target_speaker" } { "classname" "target_speaker" "origin" "-1760 -96 160" "spawnflags" "1" "noise" "world/drip_amb.wav" } { "classname" "target_speaker" "origin" "-1880 -168 -304" "spawnflags" "1" "noise" "world/curnt3.wav" } { "classname" "target_speaker" "origin" "-2064 -164 -304" "spawnflags" "1" "noise" "world/curnt3.wav" } { "classname" "target_speaker" "origin" "-2256 -160 -304" "spawnflags" "1" "noise" "world/curnt3.wav" } { "origin" "-2408 184 -240" "noise" "world/force1.wav" "targetname" "red2" "classname" "target_speaker" "spawnflags" "2049" } { "origin" "-2624 176 -280" "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" } { "origin" "-2768 184 -288" "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" } { "origin" "-2872 240 -288" "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" } { "origin" "-2264 136 -280" "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" } { "origin" "-2008 -80 -176" "classname" "item_health_large" } { "origin" "-2008 -196 -176" "classname" "item_health_large" "spawnflags" "1536" } { "origin" "-1360 736 408" "classname" "item_health" "spawnflags" "3072" } { "origin" "-1360 656 408" "classname" "item_health" "spawnflags" "2048" } { "origin" "-2448 160 -416" "angle" "135" "spawnflags" "1" "classname" "misc_deadsoldier" } { "origin" "-2400 -640 53" "_color" "1.000000 0.853755 0.268775" "light" "175" "classname" "light" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/curnt3.wav" "origin" "-1200 88 104" } { "classname" "target_goal" "targetname" "t135" "origin" "-1336 664 440" "spawnflags" "2048" } { "origin" "320 -2376 312" "targetname" "t174" "classname" "target_speaker" "noise" "world/spark1.wav" "attenuation" "1" "volume" "0.5" } { "origin" "328 -2312 312" "targetname" "t175" "volume" "0.5" "attenuation" "1" "noise" "world/spark2.wav" "classname" "target_speaker" } { "target" "t175" "classname" "func_timer" "random" "2.5" "wait" "5" "origin" "308 -2328 312" "spawnflags" "1" } { "targetname" "t148" "origin" "152 -2520 320" "volume" "0.2" "attenuation" "1" "noise" "world/spark5.wav" "classname" "target_speaker" } { "spawnflags" "4" "origin" "-1360 596 444" "targetname" "t135" "classname" "target_crosslevel_trigger" } { "targetname" "t172" "target" "red2" "origin" "-2396 176 -256" "classname" "trigger_relay" "spawnflags" "2048" } { "classname" "ammo_rockets" "origin" "660 -2608 448" "spawnflags" "2048" } { "origin" "-1488 104 344" "classname" "item_health" } { "classname" "ammo_cells" "origin" "-1208 408 4" } { "classname" "ammo_bullets" "origin" "-1240 300 4" } { "origin" "-952 -1718 620" "target" "t162" "classname" "weapon_machinegun" "spawnflags" "1792" } { "spawnflags" "1536" "origin" "-2448 -328 24" "classname" "ammo_bullets" } { "origin" "-2464 -288 24" "classname" "ammo_bullets" } { "classname" "item_health" "origin" "-1400 -1920 96" } { "classname" "ammo_bullets" "origin" "-1400 -1960 96" } { "target" "t242" "origin" "-336 -1816 104" "classname" "ammo_bullets" "spawnflags" "2048" } { "classname" "ammo_bullets" "origin" "-328 -2184 320" "spawnflags" "512" } { "classname" "item_health" "origin" "-288 -2144 320" "spawnflags" "2048" } { "classname" "ammo_bullets" "origin" "-912 -1640 584" } { "spawnflags" "2560" "origin" "-912 -1600 584" "classname" "ammo_bullets" } { "classname" "ammo_bullets" "origin" "-816 -2624 704" } { "spawnflags" "3584" "origin" "-312 -2816 488" "classname" "ammo_bullets" } { "classname" "ammo_bullets" "origin" "-280 -2540 320" "spawnflags" "2048" } { "spawnflags" "2048" "classname" "ammo_bullets" "origin" "-320 -2536 320" } { "classname" "ammo_shells" "origin" "660 -2656 480" "spawnflags" "1792" "team" "hidey1" } { "classname" "ammo_slugs" "origin" "660 -2532 480" "spawnflags" "1792" "team" "hidey2" } { "origin" "-1424 -1856 112" "angle" "270" "spawnflags" "258" "targetname" "t160" "classname" "monster_gladiator" } { "origin" "-336 -2304 320" "classname" "item_health" } { "target" "t160" "classname" "item_health" "origin" "420 -2584 320" "spawnflags" "2048" } { "target" "t160" "classname" "item_health" "origin" "460 -2584 320" } { "origin" "-224 -2380 580" "target" "big_secret" "targetname" "t159" "message" "A secret door has opened elsewhere." "classname" "trigger_relay" "spawnflags" "2048" } { "model" "*51" "wait" "-1" "target" "t159" "sounds" "3" "angle" "0" "classname" "func_button" "_minlight" "0.3" "spawnflags" "2048" } { "light" "125" "classname" "light" "origin" "360 -2616 504" } { "classname" "light" "light" "125" "origin" "296 -2616 504" } { "origin" "456 -2616 504" "light" "125" "classname" "light" } { "origin" "552 -2616 504" "classname" "light" "light" "125" } { "classname" "light" "light" "125" "origin" "392 -2616 440" } { "light" "125" "classname" "light" "origin" "456 -2616 440" } { "origin" "296 -2616 440" "classname" "light" "light" "125" } { "origin" "232 -2616 440" "light" "100" "classname" "light" } { "target" "t160" "origin" "648 -2420 448" "classname" "ammo_shells" "spawnflags" "2048" } { "origin" "546 -2510 480" "classname" "weapon_grenadelauncher" "spawnflags" "1792" "team" "hidey1" } { "target" "t160" "origin" "532 -2420 448" "classname" "ammo_grenades" "spawnflags" "2048" "team" "hidey2" } { "target" "t160" "origin" "660 -2568 448" "classname" "item_armor_body" "spawnflags" "2048" } { "origin" "632 -2616 436" "targetname" "t157" "classname" "info_null" } { "origin" "632 -2552 436" "targetname" "t156" "classname" "info_null" } { "origin" "600 -2520 436" "targetname" "t155" "classname" "info_null" } { "origin" "520 -2520 436" "targetname" "t154" "classname" "info_null" } { "origin" "632 -2680 436" "targetname" "t158" "classname" "info_null" } { "origin" "632 -2616 504" "target" "t157" "classname" "light" "light" "125" } { "origin" "632 -2552 504" "target" "t156" "classname" "light" "light" "150" } { "origin" "600 -2520 504" "target" "t155" "classname" "light" "light" "150" } { "origin" "520 -2520 504" "target" "t154" "classname" "light" "light" "150" } { "origin" "632 -2680 504" "target" "t158" "light" "150" "classname" "light" } { "model" "*52" "_minlight" "0.1" "targetname" "big_secret" "wait" "-1" "angle" "-2" "classname" "func_door" "spawnflags" "2048" } { "targetname" "t152" "classname" "target_splash" "angle" "270" "sounds" "1" "origin" "328 -2364 320" } { "targetname" "t174" "origin" "312 -2388 312" "target" "t152" "delay" ".5" "classname" "trigger_relay" } { "targetname" "t175" "origin" "340 -2388 312" "delay" ".2" "target" "t151" "classname" "trigger_relay" } { "targetname" "t175" "target" "t153" "origin" "296 -2304 312" "classname" "trigger_relay" } { "targetname" "t175" "origin" "356 -2312 312" "target" "t150" "classname" "trigger_relay" } { "targetname" "t151" "origin" "360 -2384 320" "classname" "target_splash" "angle" "225" "sounds" "1" } { "targetname" "t153" "origin" "336 -2308 328" "sounds" "1" "angle" "180" "classname" "target_splash" } { "targetname" "t175" "target" "t150" "origin" "364 -2336 352" "delay" ".2" "classname" "trigger_relay" } { "target" "t174" "spawnflags" "1" "origin" "308 -2352 312" "wait" "10" "random" "5" "classname" "func_timer" } { "style" "34" "light" "500" "targetname" "t150" "origin" "364 -2340 312" "spawnflags" "1" "classname" "light" } { "origin" "196 -2520 332" "Wait" "3" "random" ".5" "target" "t148" "classname" "func_timer" } { "style" "35" "targetname" "t148" "origin" "151 -2524 316" "_style" "1" "light" "200" "classname" "light" } { "origin" "-648 -840 80" "classname" "ammo_rockets" "spawnflags" "2048" } { "origin" "-568 -1068 80" "classname" "ammo_shells" "spawnflags" "0" } { "origin" "-328 -2144 320" "classname" "ammo_bullets" "spawnflags" "2560" } { "model" "*53" "spawnflags" "2048" "speed" "50" "angle" "135" "classname" "trigger_push" } { "model" "*54" "spawnflags" "2048" "angle" "180" "speed" "50" "classname" "trigger_push" } { "classname" "light" "light" "80" "origin" "-1352 -1552 88" } { "classname" "light" "light" "100" "origin" "-1464 -1584 184" } { "classname" "light" "light" "100" "origin" "-1528 -1584 184" } { "light" "100" "origin" "-1208 752 248" "classname" "light" "_color" "1.000000 0.933333 0.549020" } { "light" "100" "origin" "-1320 752 312" "classname" "light" "_color" "1.000000 0.933333 0.549020" } { "light" "100" "origin" "-1440 752 312" "classname" "light" "_color" "1.000000 0.933333 0.549020" } { "light" "100" "origin" "-1080 752 248" "_color" "1.000000 0.933333 0.549020" "classname" "light" } { "origin" "-2664 328 468" "spawnflags" "1792" "classname" "weapon_bfg" } { "origin" "-2533 336 473" "angle" "225" "classname" "info_player_deathmatch" } { "origin" "-2720 -352 32" "angle" "45" "classname" "info_player_deathmatch" } { "origin" "-1858 -601 -56" "angle" "225" "classname" "info_player_deathmatch" } { "angle" "270" "origin" "-2064 -24 72" "classname" "info_player_deathmatch" } { "model" "*55" "spawnflags" "2048" "speed" "125" "angle" "270" "classname" "trigger_push" } { "model" "*56" "spawnflags" "2048" "classname" "trigger_push" "angle" "0" "speed" "200" } { "model" "*57" "target" "t249" "classname" "trigger_once" "message" "You have found a secret area." "spawnflags" "2048" } { "classname" "target_explosion" "delay" ".1.25" "targetname" "t146" "origin" "-452 -784 74" "spawnflags" "2048" "dmg" "1" } { "classname" "target_explosion" "delay" "1" "targetname" "t146" "origin" "-484 -786 160" "spawnflags" "2048" "dmg" "1" } { "classname" "target_explosion" "delay" ".5" "targetname" "t146" "origin" "-456 -784 130" "spawnflags" "2048" "dmg" "1" } { "model" "*58" "classname" "func_explosive" "spawnflags" "2048" "dmg" "0" "mass" "800" "target" "t146" "health" "50" } { "delay" "10" "origin" "-1249 719 444" "classname" "trigger_relay" "targetname" "brdswit2" "target" "t143" "spawnflags" "2048" } { "model" "*59" "classname" "trigger_counter" "count" "2" "targetname" "brdswit" "spawnflags" "2049" "target" "t144" } { "targetname" "t242" "classname" "monster_gladiator" "angle" "180" "item" "ammo_slugs" "origin" "-34 -1930 112" "spawnflags" "770" } { "classname" "target_secret" "targetname" "t141" "origin" "-1320 758 438" "spawnflags" "2048" } { "model" "*60" "classname" "trigger_once" "target" "t141" "message" "You have found a secret area." "spawnflags" "2048" } { "model" "*61" "wait" "-1" "classname" "func_button" "angle" "0" "lip" "3" "target" "brdswit2" "spawnflags" "2048" } { "classname" "target_secret" "targetname" "t140" "origin" "-2320 160 -328" } { "model" "*62" "spawnflags" "2048" "classname" "trigger_once" "target" "t140" "message" "You have found a secret area." } { "classname" "target_help" "targetname" "t139" "message" "Neutralize Strogg Leader's\n Communication System." "origin" "160 -2260 336" "spawnflags" "2049" } { "model" "*63" "classname" "trigger_once" "target" "t139" "spawnflags" "2048" } { "origin" "184 -2540 680" "delay" "30" "classname" "trigger_relay" "targetname" "medguy1" "target" "t122" "spawnflags" "2048" } { "origin" "-280 -2580 320" "classname" "ammo_bullets" } { "targetname" "t138" "origin" "-2032 72 68" "classname" "info_null" } { "target" "t138" "classname" "light" "light" "400" "origin" "-2032 72 298" "_cone" "15" } { "light" "100" "classname" "light" "origin" "-2032 72 314" } { "targetname" "t136" "classname" "info_null" "origin" "-2032 16 68" } { "light" "100" "classname" "light" "origin" "-2032 184 314" } { "target" "t137" "classname" "light" "light" "400" "origin" "-2032 184 298" "_cone" "15" } { "targetname" "t137" "classname" "info_null" "origin" "-2032 184 68" } { "origin" "-2032 0 294" "classname" "light" "light" "100" } { "_cone" "15" "target" "t136" "origin" "-2032 0 278" "light" "400" "classname" "light" } { "classname" "light" "light" "175" "origin" "-832 -2608 400" "_color" "1.000000 0.976471 0.501961" } { "classname" "item_health" "origin" "-885 -2697 327" } { "classname" "ammo_bullets" "origin" "-600 -2344 336" "spawnflags" "2048" } { "spawnflags" "512" "origin" "-636 -2316 328" "classname" "ammo_bullets" } { "origin" "-1192 172 4" "classname" "ammo_shells" } { "origin" "-1252 356 -12" "spawnflags" "2057" "classname" "misc_deadsoldier" } { "origin" "-1352 568 108" "classname" "ammo_slugs" "spawnflags" "1792" } { "origin" "-1152 788 460" "classname" "ammo_cells" "spawnflags" "1792" } { "origin" "-1852 476 316" "classname" "ammo_shells" "spawnflags" "1024" } { "origin" "-1804 60 300" "classname" "item_health" "spawnflags" "3072" } { "origin" "-1608 -160 212" "classname" "item_health" "spawnflags" "2048" } { "origin" "-1784 0 -8" "angle" "90" "spawnflags" "2049" "classname" "misc_deadsoldier" } { "origin" "-1768 -252 60" "classname" "ammo_rockets" "spawnflags" "2048" } { "origin" "-1864 -220 28" "classname" "item_health" } { "origin" "-1860 -92 60" "classname" "weapon_supershotgun" "spawnflags" "1792" } { "_minlight" "0.2" "origin" "-2540 -680 -72" "angle" "45" "spawnflags" "2056" "classname" "misc_deadsoldier" } { "origin" "-2480 220 -288" "classname" "ammo_cells" "spawnflags" "2048" } { "origin" "-2520 220 -288" "classname" "ammo_grenades" "spawnflags" "2048" } { "origin" "-2516 184 -288" "classname" "ammo_slugs" "spawnflags" "2048" } { "origin" "-2480 148 -288" "classname" "ammo_rockets" "spawnflags" "0" } { "origin" "-2520 192 -231" "classname" "weapon_chaingun" "spawnflags" "1792" } { "origin" "-2520 144 -288" "classname" "item_health_large" "spawnflags" "0" } { "origin" "-2520 152 -240" "classname" "item_power_shield" "spawnflags" "2048" } { "target" "t172" "origin" "-2384 220 -296" "spawnflags" "2049" "classname" "target_crosslevel_target" } { "origin" "-2400 208 -288" "classname" "item_health_large" "spawnflags" "2048" } { "origin" "-2800 208 -24" "classname" "item_health" "spawnflags" "2048" } { "origin" "-2920 -136 40" "classname" "item_health" "spawnflags" "2048" } { "origin" "-2312 -1496 80" "classname" "ammo_shells" "spawnflags" "0" } { "origin" "-2272 -1512 80" "classname" "ammo_shells" "spawnflags" "2048" } { "origin" "-704 -832 72" "classname" "info_player_deathmatch" } { "spawnflags" "0" "origin" "-1624 -1624 112" "classname" "ammo_rockets" } { "model" "*64" "classname" "trigger_hurt" } { "origin" "-976 -2024 544" "classname" "item_health" } { "origin" "-1360 -1920 96" "classname" "ammo_bullets" } { "classname" "item_health" "origin" "-2016 -688 -64" "spawnflags" "2048" } { "origin" "-984 -1920 544" "classname" "item_armor_combat" "spawnflags" "1792" } { "origin" "-408 -1848 104" "classname" "item_health" } { "spawnflags" "2048" "classname" "ammo_bullets" "origin" "-600 -840 80" } { "classname" "item_health" "origin" "-528 -1148 80" "spawnflags" "2048" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2896 48 -384" "light" "70" "classname" "light" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2872 120 -384" "light" "80" "classname" "light" } { "style" "1" "classname" "func_areaportal" "targetname" "t124" } { "model" "*65" "team" "redportal3" "lip" "8" "wait" "6" "sounds" "4" "speed" "75" "angle" "270" "classname" "func_door" "target" "t124" "_minlight" "0.2" } { "model" "*66" "team" "redportal3" "lip" "8" "wait" "6" "sounds" "4" "speed" "75" "classname" "func_door" "angle" "90" "target" "t124" "_minlight" "0.2" } { "item" "ammo_cells" "origin" "269 -2460 665" "spawnflags" "257" "classname" "monster_medic" "targetname" "t123" "angle" "180" "target" "t286" } { "classname" "trigger_relay" "targetname" "medguy1" "delay" "15" "target" "t123" "origin" "184 -2516 680" "spawnflags" "2048" } { "classname" "monster_medic" "spawnflags" "769" "origin" "273 -2525 669" "targetname" "t122" "target" "t180" "angle" "180" } { "item" "ammo_cells" "origin" "-788 -2524 1084" "spawnflags" "2" "classname" "monster_hover" "targetname" "t223" } { "style" "2" "classname" "func_areaportal" "targetname" "t120" } { "style" "3" "classname" "func_areaportal" "targetname" "t119" } { "model" "*67" "_minlight" "0.2" "angle" "180" "classname" "func_door" "target" "t119" "speed" "75" "sounds" "4" "wait" "4" "lip" "24" "team" "redportal1" } { "model" "*68" "_minlight" "0.2" "classname" "func_door" "angle" "0" "speed" "75" "sounds" "4" "wait" "4" "lip" "24" "team" "redportal1" } { "model" "*69" "_minlight" "0.2" "classname" "func_door" "angle" "0" "speed" "75" "team" "big red" } { "model" "*70" "_minlight" "0.2" "team" "big red" "speed" "75" "angle" "180" "classname" "func_door" } { "classname" "point_combat" "targetname" "t117" "origin" "-2712 -96 24" "spawnflags" "2048" } { "model" "*71" "spawnflags" "2048" "classname" "trigger_once" "target" "t106" } { "model" "*72" "classname" "func_wall" "targetname" "t106" "spawnflags" "6" } { "light" "150" "origin" "-980 446 456" "_color" "0.862348 1.000000 0.085020" "classname" "light" } { "target" "bigbridge" "targetname" "brdswit2" "origin" "-1247 771 476" "classname" "trigger_relay" "delay" "3" "spawnflags" "2048" } { "model" "*73" "target" "t111" "classname" "trigger_once" "spawnflags" "2048" } { "light" "200" "_style" "10" "_color" "1.000000 1.000000 0.509804" "origin" "-1426 608 478" "classname" "light" } { "classname" "trigger_relay" "targetname" "brdswit2" "target" "brdswit" "origin" "-1271 711 444" "delay" "5" "spawnflags" "2048" } { "classname" "item_health" "origin" "-712 -1692 592" } { "origin" "-440 -1748 88" "targetname" "t108" "classname" "point_combat" "spawnflags" "2048" } { "origin" "-428 -1740 88" "target" "t108" "targetname" "t107" "classname" "point_combat" "pathtarget" "killme" "spawnflags" "2048" } { "targetname" "t178" "target" "t107" "origin" "-252 -1440 104" "classname" "point_combat" "spawnflags" "2048" } { "model" "*74" "targetname" "red2" "spawnflags" "2055" "classname" "func_wall" } { "origin" "-904 -2064 1000" "classname" "info_player_deathmatch" } { "origin" "-220 -2252 584" "classname" "info_player_deathmatch" } { "item" "ammo_cells" "origin" "209 -2472 657" "targetname" "medguy1" "spawnflags" "1" "classname" "monster_medic" "angle" "180" "target" "t285" } { "classname" "ammo_bullets" "origin" "-596 -2288 328" "spawnflags" "2048" } { "item" "item_armor_shard" "classname" "monster_soldier_ss" "angle" "270" "origin" "-888 -1544 608" "spawnflags" "257" "targetname" "m2" } { "origin" "-760 -1832 552" "classname" "trigger_relay" "killtarget" "g6" "targetname" "g6" "delay" ".4" "spawnflags" "2048" } { "model" "*75" "target" "t101" "classname" "trigger_once" "spawnflags" "2048" } { "origin" "-1382 188 340" "targetname" "t93" "classname" "point_combat" } { "origin" "-1248 120 344" "targetname" "t94" "classname" "point_combat" } { "spawnflags" "2049" "origin" "-1060 592 428" "targetname" "t98" "classname" "point_combat" } { "model" "*76" "target" "t91" "classname" "trigger_once" "spawnflags" "2048" } { "angle" "0" "item" "item_armor_shard" "origin" "-1615 -154 260" "spawnflags" "768" "targetname" "t91" "classname" "monster_hover" } { "origin" "-1122 415 431" "spawnflags" "257" "classname" "monster_soldier_ss" "targetname" "t111" } { "target" "t98" "origin" "-970 622 424" "spawnflags" "1" "classname" "monster_soldier_ss" "targetname" "t111" } { "item" "ammo_bullets" "target" "t93" "origin" "-1004 132 422" "spawnflags" "257" "classname" "monster_soldier_ss" "targetname" "t111" } { "target" "t94" "origin" "-1108 104 406" "spawnflags" "257" "classname" "monster_soldier_ss" "targetname" "t111" } { "item" "item_armor_shard" "origin" "-964 290 424" "angle" "270" "spawnflags" "1" "classname" "monster_soldier_ss" "targetname" "t111" } { "item" "item_armor_shard" "angle" "180" "origin" "-876 -1992 1016" "classname" "monster_hover" "spawnflags" "258" "targetname" "t230" } { "item" "ammo_rockets" "classname" "monster_tank_commander" "spawnflags" "257" "targetname" "t1" "origin" "-2024 -392 -8" "angle" "270" } { "spawnflags" "258" "targetname" "t1" "classname" "monster_hover" "origin" "-860 -1956 636" } { "item" "ammo_cells" "spawnflags" "258" "targetname" "t1" "classname" "monster_hover" "origin" "-812 -1876 620" } { "classname" "point_combat" "origin" "-2216 -1520 40" "targetname" "t77" "spawnflags" "2048" } { "classname" "point_combat" "origin" "-2128 -1512 24" "targetname" "t78" "spawnflags" "2048" } { "classname" "point_combat" "origin" "-1960 -1488 40" "targetname" "t79" "spawnflags" "2048" } { "classname" "point_combat" "origin" "-1840 -1584 40" "targetname" "t80" "spawnflags" "2048" } { "classname" "point_combat" "origin" "-1768 -1496 64" "targetname" "t81" "spawnflags" "2048" } { "classname" "point_combat" "origin" "-2288 -1512 56" "targetname" "t77" "spawnflags" "2048" } { "targetname" "t186" "item" "ammo_bullets" "origin" "-2036 -1908 380" "classname" "monster_soldier_ss" "target" "t79" "spawnflags" "2" } { "targetname" "t189" "origin" "-2036 -1948 380" "classname" "monster_soldier_ss" "target" "t78" "spawnflags" "258" } { "targetname" "t185" "classname" "monster_soldier_ss" "origin" "-2076 -1908 380" "target" "t77" "spawnflags" "258" } { "targetname" "t189" "item" "ammo_bullets" "classname" "monster_soldier_ss" "origin" "-1988 -1948 380" "target" "t80" "spawnflags" "514" } { "targetname" "t185" "classname" "monster_soldier_ss" "origin" "-1988 -1908 380" "target" "t81" "spawnflags" "770" } { "targetname" "t186" "item" "ammo_bullets" "angle" "180" "classname" "monster_soldier_ss" "origin" "-2076 -1948 380" "spawnflags" "2" } { "item" "item_armor_shard" "targetname" "t101" "classname" "monster_gladiator" "spawnflags" "770" "origin" "-3020 -136 -270" "angle" "0" } { "model" "*77" "classname" "trigger_once" "target" "t76" "spawnflags" "2048" } { "classname" "trigger_relay" "target" "t74" "delay" "0.3" "targetname" "t76" "origin" "-2728 -136 -296" } { "classname" "monster_soldier" "origin" "-2568 -144 -280" "targetname" "t74" "angle" "180" "spawnflags" "769" } { "item" "ammo_shells" "classname" "monster_soldier" "origin" "-2608 -120 -280" "targetname" "t76" "angle" "180" "spawnflags" "1" } { "item" "item_power_shield" "targetname" "t76" "classname" "monster_gladiator" "origin" "-2112 -240 -168" "angle" "270" "spawnflags" "1" } { "targetname" "t58" "spawnflags" "2" "angle" "180" "classname" "monster_hover" "origin" "-2534 84 484" "deathtarget" "hv4" } { "spawnflags" "258" "classname" "monster_hover" "origin" "-2536 264 504" "targetname" "t265" } { "angle" "270" "classname" "info_player_start" "targetname" "city1XH" "origin" "-2040 80 72" } { "classname" "info_player_start" "angle" "270" "targetname" "city1XL" "origin" "-2040 48 -168" } { "classname" "point_combat" "targetname" "t68" "origin" "-1368 -1864 88" "spawnflags" "2048" } { "classname" "point_combat" "targetname" "t67" "origin" "-1432 -1912 88" "spawnflags" "2048" } { "targetname" "t303" "deathtarget" "t121" "target" "t259" "classname" "monster_hover" "origin" "-160 -2600 504" "angle" "225" "spawnflags" "1" } { "target" "g6" "targetname" "g2" "origin" "-792 -1832 552" "classname" "trigger_relay" "delay" "0.1" "spawnflags" "2048" } { "target" "gate1" "origin" "-792 -1800 552" "classname" "trigger_relay" "targetname" "g6" "delay" "0.1" "spawnflags" "2048" } { "targetname" "g4" "target" "gate1" "classname" "trigger_relay" "origin" "-792 -1872 552" "spawnflags" "2048" } { "target" "gate1" "targetname" "g1" "origin" "-808 -1760 288" "classname" "trigger_relay" "spawnflags" "2048" } { "spawnflags" "2" "origin" "-1388 -1796 120" "targetname" "m1" "classname" "monster_soldier_ss" "target" "t67" } { "item" "ammo_bullets" "spawnflags" "258" "origin" "-1436 -1796 128" "angle" "270" "targetname" "m1" "classname" "monster_soldier_ss" "target" "t68" } { "item" "item_armor_shard" "targetname" "m2" "spawnflags" "257" "origin" "-936 -1568 596" "angle" "135" "classname" "monster_soldier_ss" } { "targetname" "g4" "killtarget" "gatetrig1" "classname" "trigger_relay" "origin" "-792 -1912 552" "spawnflags" "2048" } { "model" "*78" "lip" "12" "wait" "-1" "sounds" "3" "target" "g2" "angle" "-2" "classname" "func_button" "spawnflags" "2048" } { "model" "*79" "target" "g4" "targetname" "gatetrig2" "classname" "trigger_once" "spawnflags" "2048" } { "origin" "-800 -1728 288" "killtarget" "gatetrig2" "targetname" "g1" "classname" "trigger_relay" "spawnflags" "2048" } { "model" "*80" "targetname" "gatetrig1" "target" "g1" "classname" "trigger_once" } { "model" "*81" "_minlight" "0.1" "message" "ACCESS DENIED.\nThis gate is opened elsewhere." "sounds" "4" "lip" "64" "dmg" "25" "speed" "50" "targetname" "gate1" "spawnflags" "2093" "angle" "-1" "classname" "func_door" } { "classname" "light" "light" "70" "origin" "-1272 -1936 184" } { "item" "ammo_slugs" "angle" "180" "classname" "monster_gladiator" "targetname" "t58" "origin" "-2544 -120 32" "target" "t117" "spawnflags" "257" } { "classname" "ammo_bullets" "origin" "-264 -2792 488" "spawnflags" "2048" } { "classname" "item_health" "origin" "-856 -2616 704" } { "classname" "item_health" "origin" "-856 -2560 704" } { "model" "*82" "classname" "trigger_once" "spawnflags" "2308" "targetname" "t40" "target" "t62" } { "item" "item_armor_shard" "targetname" "t106" "angle" "270" "classname" "monster_soldier_ss" "origin" "-1248 792 422" "spawnflags" "257" } { "targetname" "t106" "classname" "monster_soldier_ss" "angle" "180" "origin" "-1320 604 430" "spawnflags" "1" } { "targetname" "bigbridge" "spawnflags" "1" "classname" "monster_gladiator" "origin" "-1015 366 436" "angle" "225" } { "deathtarget" "t121" "item" "ammo_cells" "classname" "monster_hover" "spawnflags" "2" "origin" "-900 -2160 1044" "targetname" "t226" } { "deathtarget" "t61" "classname" "monster_hover" "spawnflags" "258" "origin" "-788 -2628 1092" "targetname" "t222" } { "classname" "monster_hover" "spawnflags" "258" "origin" "-904 -2104 1016" "targetname" "t228" } { "targetname" "t242" "item" "ammo_slugs" "classname" "monster_gladiator" "spawnflags" "1" "origin" "-108 -1880 108" "deathtarget" "t243" } { "targetname" "t243" "classname" "monster_gladiator" "spawnflags" "257" "origin" "-704 -1048 102" "angle" "0" } { "model" "*83" "origin" "-964 -1840 72" "classname" "func_door_rotating" "spawnflags" "2124" "angle" "-2" "distance" "90" "speed" "100" "team" "drtrp" "targetname" "drtrp1" "wait" "6" } { "model" "*84" "classname" "func_plat" "speed" "75" "_minlight" "0.1" } { "model" "*85" "spawnflags" "2048" "classname" "trigger_multiple" "target" "t58" } { "classname" "path_corner" "target" "t54" "targetname" "t57" "origin" "-2192 -832 -56" } { "classname" "item_health" "origin" "-288 -2104 320" } { "model" "*86" "spawnflags" "4" "angle" "-1" "classname" "func_door" "team" "dent1" "_minlight" "0.1" } { "model" "*87" "spawnflags" "4" "angle" "270" "classname" "func_door" "team" "dent1" "_minlight" "0.1" } { "model" "*88" "spawnflags" "4" "angle" "90" "classname" "func_door" "team" "dent1" "_minlight" "0.1" "target" "t120" } { "classname" "light" "origin" "-128 -2288 344" "light" "150" "_color" "0.650980 1.000000 0.650980" } { "_color" "0.650980 1.000000 0.650980" "light" "150" "origin" "-128 -2400 344" "classname" "light" } { "classname" "light" "origin" "-128 -2400 400" "light" "150" "_color" "0.650980 1.000000 0.650980" } { "_color" "0.650980 1.000000 0.650980" "light" "150" "origin" "-128 -2288 400" "classname" "light" } { "origin" "-2000 -48 64" "classname" "item_health_large" "spawnflags" "2048" } { "origin" "-2504 -288 24" "classname" "ammo_bullets" } { "origin" "-2056 -688 -64" "classname" "item_health" } { "origin" "-2496 -680 -8" "classname" "item_armor_jacket" "spawnflags" "1792" } { "angle" "270" "origin" "-2664 312 520" "classname" "monster_hover" "targetname" "t58" "deathtarget" "hv1" "spawnflags" "257" } { "targetname" "t263" "origin" "-2536 144 488" "classname" "monster_hover" "spawnflags" "2" } { "targetname" "t267" "origin" "-2534 10 484" "classname" "monster_hover" "angle" "180" "spawnflags" "2" } { "origin" "-2768 312 512" "angle" "270" "classname" "monster_hover" "targetname" "t58" "deathtarget" "hv3" "spawnflags" "1" } { "model" "*89" "classname" "func_plat" "speed" "200" "lip" "0" "sounds" "2" "_minlight" "0.1" } { "deathtarget" "t171" "item" "ammo_bullets" "origin" "-2032 -120 72" "angle" "270" "classname" "monster_tank_commander" "spawnflags" "1" } { "angle" "45" "deathtarget" "t46" "target" "t251" "spawnflags" "257" "classname" "monster_hover" "origin" "-568 -1880 264" "targetname" "t246" } { "target" "t255" "deathtarget" "t240" "targetname" "t64" "origin" "-720 -2180 1040" "classname" "monster_hover" "spawnflags" "1" "angle" "45" } { "model" "*90" "target" "drtrp1" "delay" "0.1" "wait" "6" "classname" "trigger_multiple" "spawnflags" "2048" } { "target" "t242" "origin" "-336 -1856 104" "classname" "ammo_bullets" "spawnflags" "2048" } { "origin" "-560 -1108 80" "classname" "item_health" "spawnflags" "1024" } { "origin" "-488 -736 104" "classname" "item_health_large" "spawnflags" "0" } { "model" "*91" "spawnflags" "2048" "angle" "270" "speed" "75" "classname" "trigger_push" } { "origin" "-1608 -1640 88" "classname" "light" "light" "64" } { "origin" "-1592 -1640 88" "classname" "light" "light" "80" } { "origin" "-1576 -1640 88" "light" "64" "classname" "light" } { "origin" "-1608 -1528 88" "classname" "light" "light" "64" } { "origin" "-1592 -1528 88" "classname" "light" "light" "80" } { "origin" "-1576 -1528 88" "light" "64" "classname" "light" } { "origin" "-1544 -1528 88" "classname" "light" "light" "64" } { "origin" "-1528 -1528 88" "classname" "light" "light" "80" } { "origin" "-1512 -1528 88" "light" "64" "classname" "light" } { "origin" "-1480 -1528 88" "classname" "light" "light" "64" } { "origin" "-1464 -1528 88" "classname" "light" "light" "80" } { "origin" "-1448 -1528 88" "light" "64" "classname" "light" } { "origin" "-1416 -1528 88" "classname" "light" "light" "64" } { "origin" "-1400 -1528 88" "classname" "light" "light" "80" } { "origin" "-1384 -1528 88" "light" "64" "classname" "light" } { "origin" "-1352 -1600 88" "classname" "light" "light" "64" } { "origin" "-1352 -1568 88" "classname" "light" "light" "64" } { "origin" "-1352 -1536 88" "light" "64" "classname" "light" } { "origin" "-1352 -1664 88" "classname" "light" "light" "64" } { "origin" "-1352 -1632 88" "classname" "light" "light" "64" } { "origin" "-1352 -1616 88" "light" "80" "classname" "light" } { "origin" "-1352 -1696 88" "classname" "light" "light" "64" } { "origin" "-1352 -1680 88" "light" "80" "classname" "light" } { "origin" "-1352 -1744 88" "light" "64" "classname" "light" } { "origin" "-1352 -1840 88" "classname" "light" "light" "64" } { "origin" "-1352 -1824 88" "classname" "light" "light" "80" } { "origin" "-1352 -1808 88" "light" "64" "classname" "light" } { "origin" "-1464 -1888 88" "classname" "light" "light" "64" } { "origin" "-1464 -1872 88" "classname" "light" "light" "80" } { "origin" "-1464 -1856 88" "light" "64" "classname" "light" } { "origin" "-1464 -1744 88" "light" "64" "classname" "light" } { "origin" "-1512 -1640 88" "classname" "light" "light" "64" } { "origin" "-1528 -1640 88" "light" "80" "classname" "light" } { "origin" "-1544 -1640 88" "light" "64" "classname" "light" } { "origin" "-1464 -1952 88" "classname" "light" "light" "64" } { "origin" "-1464 -1936 88" "classname" "light" "light" "80" } { "origin" "-1464 -1920 88" "light" "64" "classname" "light" } { "origin" "-1464 -1792 88" "classname" "light" "light" "64" } { "origin" "-1464 -1808 88" "light" "80" "classname" "light" } { "origin" "-1464 -1824 88" "light" "64" "classname" "light" } { "origin" "-1440 -1976 88" "classname" "light" "light" "64" } { "origin" "-1424 -1976 88" "light" "80" "classname" "light" } { "origin" "-1408 -1976 88" "light" "64" "classname" "light" } { "origin" "-1280 -1976 88" "classname" "light" "light" "64" } { "origin" "-1296 -1976 88" "classname" "light" "light" "80" } { "origin" "-1312 -1976 88" "light" "64" "classname" "light" } { "origin" "-1344 -1976 88" "light" "64" "classname" "light" } { "origin" "-1360 -1976 88" "light" "80" "classname" "light" } { "origin" "-1376 -1976 88" "classname" "light" "light" "64" } { "origin" "-1280 -1872 88" "light" "64" "classname" "light" } { "origin" "-1296 -1872 88" "light" "80" "classname" "light" } { "origin" "-1312 -1872 88" "classname" "light" "light" "64" } { "origin" "-1464 -1680 88" "classname" "light" "light" "80" } { "origin" "-1464 -1696 88" "classname" "light" "light" "64" } { "origin" "-1464 -1664 88" "light" "64" "classname" "light" } { "model" "*92" "target" "t40" "classname" "trigger_multiple" } { "classname" "light" "light" "175" "origin" "-448 -2288 448" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "origin" "-472 -2344 360" "light" "90" "classname" "light" } { "_color" "1.000000 0.448000 0.208000" "classname" "light" "origin" "-496 -920 40" "light" "100" } { "_color" "1.000000 0.448000 0.208000" "classname" "light" "origin" "-544 -928 40" "light" "100" } { "_color" "1.000000 0.448000 0.208000" "classname" "light" "origin" "-536 -928 64" "light" "100" } { "light" "100" "origin" "-616 -928 64" "classname" "light" "_color" "1.000000 0.448000 0.208000" } { "light" "150" "origin" "-440 -712 40" "classname" "light" "_color" "1.000000 0.448000 0.208000" } { "_color" "1.000000 0.448000 0.208000" "classname" "light" "origin" "-472 -696 40" "light" "150" } { "light" "100" "origin" "-440 -712 88" "classname" "light" "_color" "1.000000 0.448000 0.208000" } { "light" "100" "origin" "-632 -888 40" "classname" "light" "_color" "1.000000 0.448000 0.208000" } { "light" "100" "origin" "-576 -872 64" "classname" "light" "_color" "1.000000 0.448000 0.208000" } { "_color" "1.000000 0.448000 0.208000" "classname" "light" "origin" "-568 -984 88" "light" "100" } { "_color" "1.000000 0.448000 0.208000" "classname" "light" "origin" "-440 -744 40" "light" "150" } { "_color" "1.000000 0.448000 0.208000" "classname" "light" "origin" "-440 -808 32" "light" "150" } { "_color" "1.000000 0.448000 0.208000" "classname" "light" "origin" "-624 -928 40" "light" "100" } { "classname" "light" "_color" "1.000000 0.448000 0.208000" "origin" "-552 -1056 120" "light" "150" } { "classname" "light" "light" "175" "origin" "-712 -2736 752" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "175" "origin" "-584 -2800 688" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "175" "origin" "-456 -2800 624" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "175" "origin" "-360 -2816 544" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "100" "origin" "-760 -2472 304" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "origin" "-784 -2480 352" "light" "150" "classname" "light" } { "classname" "light" "light" "150" "origin" "-720 -2576 384" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "175" "origin" "-880 -2480 352" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-800 -2368 336" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "origin" "-864 -2208 240" "light" "175" "classname" "light" } { "classname" "light" "light" "150" "origin" "-800 -2208 240" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "175" "origin" "-816 -2064 176" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "175" "origin" "-280 -2736 488" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "origin" "-800 -2648 784" "light" "100" "classname" "light" } { "origin" "-1656 -1584 168" "light" "100" "classname" "light" } { "origin" "-1592 -1584 168" "light" "100" "classname" "light" } { "origin" "-1400 -1584 184" "light" "100" "classname" "light" } { "origin" "-1400 -1664 184" "light" "100" "classname" "light" } { "origin" "-1400 -1792 184" "light" "100" "classname" "light" } { "origin" "-1400 -1856 184" "light" "100" "classname" "light" } { "origin" "-1400 -1920 184" "light" "100" "classname" "light" } { "origin" "-1336 -1920 184" "light" "100" "classname" "light" } { "origin" "-1272 -1904 184" "light" "100" "classname" "light" } { "angle" "270" "origin" "-1584 -1160 960" "targetname" "t10" "spawnflags" "258" "classname" "monster_hover" } { "item" "ammo_cells" "origin" "-2432 -1704 656" "targetname" "t10" "spawnflags" "2" "classname" "monster_hover" } { "angle" "270" "origin" "-2072 -944 1032" "targetname" "t10" "spawnflags" "2" "classname" "monster_hover" } { "model" "*93" "classname" "func_button" "angle" "-2" "target" "t20" "lip" "12" "sounds" "3" } { "origin" "-2848 -64 -168" "classname" "light" "light" "100" } { "origin" "-2832 -80 -168" "classname" "light" "light" "100" } { "light" "100" "classname" "light" "origin" "-2832 -192 -168" } { "light" "100" "classname" "light" "origin" "-2848 -208 -168" } { "spawnflags" "258" "origin" "-1992 -944 1032" "classname" "monster_hover" "targetname" "t183" } { "_color" "1.000000 0.976471 0.501961" "origin" "-318 -2066 448" "light" "175" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-446 -2066 512" "light" "175" "classname" "light" } { "classname" "light" "light" "200" "origin" "-512 -2512 512" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "200" "origin" "-512 -2192 512" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "200" "origin" "-512 -2384 512" "_color" "1.000000 0.976471 0.501961" } { "spawnflags" "80" "classname" "light" "light" "200" "origin" "-576 -2480 512" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "175" "origin" "-240 -2672 456" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "100" "origin" "-312 -2552 368" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "175" "origin" "-448 -2480 448" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "125" "origin" "-464 -2248 400" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "200" "origin" "-576 -2288 512" "_color" "1.000000 0.976471 0.501961" } { "_color" "0.000000 1.000000 1.000000" "origin" "-2960 232 -216" "light" "70" "classname" "light" } { "_color" "0.000000 1.000000 1.000000" "origin" "-2960 280 -216" "classname" "light" "light" "70" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "70" "origin" "-2904 232 -216" } { "light" "70" "classname" "light" "origin" "-2904 280 -216" } { "_color" "0.000000 1.000000 1.000000" "origin" "-2960 232 -280" "light" "70" "classname" "light" } { "_color" "0.000000 1.000000 1.000000" "origin" "-2960 280 -280" "classname" "light" "light" "70" } { "_color" "0.000000 1.000000 1.000000" "classname" "light" "light" "70" "origin" "-2904 232 -280" } { "_color" "0.000000 1.000000 1.000000" "light" "70" "classname" "light" "origin" "-2904 280 -280" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2960 232 -336" "light" "70" "classname" "light" } { "origin" "-2960 280 -336" "classname" "light" "light" "70" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-2904 232 -336" } { "_color" "0.615686 1.000000 0.894118" "light" "70" "classname" "light" "origin" "-2904 280 -336" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2968 224 -384" "light" "70" "classname" "light" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2968 272 -384" "classname" "light" "light" "70" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-2912 224 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "70" "classname" "light" "origin" "-2912 272 -384" } { "origin" "-2904 232 -152" "light" "70" "classname" "light" } { "origin" "-2904 280 -152" "classname" "light" "light" "70" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2872 184 -384" "light" "80" "classname" "light" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2872 232 -384" "classname" "light" "light" "80" } { "origin" "-1664 -1568 233" "_color" "1.000000 0.853755 0.268775" "light" "150" "classname" "light" } { "origin" "-2184 -752 53" "_color" "1.000000 0.853755 0.268775" "light" "150" "classname" "light" } { "classname" "light" "light" "150" "_color" "1.000000 0.853755 0.268775" "origin" "-2184 -720 53" } { "classname" "light" "light" "150" "_color" "1.000000 0.853755 0.268775" "origin" "-1664 -1600 233" } { "classname" "light" "light" "150" "_color" "1.000000 0.853755 0.268775" "origin" "-1880 -720 53" } { "classname" "light" "light" "150" "_color" "1.000000 0.853755 0.268775" "origin" "-2256 -640 53" } { "classname" "light" "light" "150" "_color" "1.000000 0.853755 0.268775" "origin" "-2288 -640 53" } { "classname" "light" "light" "125" "_color" "1.000000 0.853755 0.268775" "origin" "-2496 -632 21" } { "item" "ammo_rockets" "spawnflags" "257" "origin" "-1880 -650 -54" "angle" "270" "target" "t55" "targetname" "t9" "classname" "monster_tank_commander" } { "classname" "light" "light" "175" "_color" "1.000000 0.853755 0.268775" "origin" "-2440 -456 79" } { "classname" "light" "light" "175" "_color" "1.000000 0.853755 0.268775" "origin" "-2552 -456 79" } { "classname" "light" "light" "175" "_color" "1.000000 0.853755 0.268775" "origin" "-2496 -376 167" } { "classname" "light" "light" "150" "_color" "1.000000 0.853755 0.268775" "origin" "-2688 -240 167" } { "model" "*94" "classname" "trigger_once" "target" "bigbridge" "delay" "1" "spawnflags" "2048" } { "model" "*95" "spawnflags" "32" "classname" "func_door" "angle" "0" "speed" "35" "targetname" "bigbridge" "lip" "390" } { "model" "*96" "_minlight" "0.1" "wait" "-1" "classname" "func_door" "angle" "-1" "lip" "8" "speed" "75" "spawnflags" "8" "sounds" "2" "targetname" "t20" "delay" "10" } { "classname" "key_data_spinner" "origin" "-1428 608 448" "target" "t184" "spawnflags" "2048" } { "classname" "light" "light" "200" "origin" "-1024 792 440" } { "origin" "-2072 152 -184" "targetname" "exit_low" "classname" "target_changelevel" "map" "city2$city2NL" } { "model" "*97" "target" "exit_low" "classname" "trigger_multiple" "angle" "90" } { "origin" "-2072 104 88" "map" "city2$city2NH" "targetname" "exit_high" "classname" "target_changelevel" } { "model" "*98" "target" "exit_high" "classname" "trigger_multiple" "angle" "90" } { "light" "200" "classname" "light" "origin" "-2040 -376 120" } { "light" "200" "classname" "light" "origin" "-2040 -504 120" } { "light" "200" "classname" "light" "origin" "-2040 -632 120" } { "classname" "light" "light" "200" "origin" "-2024 -248 120" } { "light" "200" "classname" "light" "origin" "-1896 -832 32" } { "light" "200" "classname" "light" "origin" "-1952 -632 56" } { "light" "200" "classname" "light" "origin" "-2112 -632 32" } { "classname" "light" "light" "200" "origin" "-2160 -824 32" } { "_color" "1.000000 0.976471 0.501961" "origin" "-832 -1920 176" "light" "175" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-720 -2000 176" "light" "175" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-768 -2112 240" "light" "150" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-864 -2336 304" "light" "175" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-656 -2576 384" "light" "150" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-872 -2536 400" "light" "175" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-784 -2568 368" "light" "175" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-320 -2736 480" "light" "175" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-448 -2608 448" "light" "200" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-640 -2384 512" "light" "200" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "origin" "-1816 -144 288" "light" "100" } { "_color" "1.000000 0.709804 0.658824" "classname" "light" "origin" "-1785 -69 72" "light" "150" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "origin" "-1800 -144 168" "light" "150" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "origin" "-1800 -144 -272" "light" "200" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "origin" "-1800 -144 -56" "light" "150" } { "_color" "0.615686 1.000000 0.894118" "origin" "-1952 -168 -384" "classname" "light" "light" "90" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2016 -168 -384" "classname" "light" "light" "90" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2080 -168 -384" "classname" "light" "light" "90" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2144 -168 -384" "classname" "light" "light" "90" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2208 -168 -384" "classname" "light" "light" "90" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2272 -136 -384" "classname" "light" "light" "90" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2272 -72 -384" "classname" "light" "light" "90" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2272 -8 -384" "classname" "light" "light" "90" } { "_color" "0.615686 1.000000 0.894118" "light" "90" "classname" "light" "origin" "-1888 -168 -384" } { "origin" "-1636 -162 -434" "_style" "5" "light" "300" "_color" "0.000000 1.000000 0.000000" "classname" "light" } { "model" "*99" "origin" "-1718 -214 -327" "message" "The gate is blocked by debris." "speed" "60" "angle" "180" "targetname" "pumpdoor" "classname" "func_door_rotating" "distance" "90" "wait" "-1" } { "model" "*100" "spawnflags" "2048" "mass" "200" "health" "20" "target" "pumpdoor" "classname" "func_explosive" "dmg" "1" } { "item" "ammo_bullets" "spawnflags" "1" "targetname" "t9" "target" "t1" "classname" "monster_tank_commander" "angle" "0" "origin" "-2032 -1560 32" } { "light" "100" "classname" "light" "origin" "-2696 -192 -152" } { "light" "100" "classname" "light" "origin" "-2712 -176 -152" } { "origin" "-2712 -96 -152" "classname" "light" "light" "100" } { "origin" "-2696 -80 -152" "classname" "light" "light" "100" } { "_color" "1.000000 0.976471 0.501961" "origin" "-1832 -1400 312" "light" "200" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "300" "origin" "-1832 -1400 504" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "200" "origin" "-1832 -1400 696" } { "_color" "1.000000 0.576471 0.501961" "origin" "-2216 -1400 312" "light" "200" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "300" "origin" "-2216 -1400 504" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "200" "origin" "-2216 -1400 696" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2216 -1528 312" "light" "200" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "300" "origin" "-2216 -1528 504" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "300" "origin" "-2216 -1528 696" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2088 -1400 312" "classname" "light" "light" "200" } { "_color" "1.000000 0.576471 0.501961" "origin" "-1960 -1400 312" "classname" "light" "light" "200" } { "_color" "1.000000 0.976471 0.501961" "light" "300" "classname" "light" "origin" "-2088 -1400 504" } { "_color" "1.000000 0.976471 0.501961" "light" "300" "classname" "light" "origin" "-1960 -1400 504" } { "_color" "1.000000 0.976471 0.501961" "light" "200" "classname" "light" "origin" "-1960 -1400 696" } { "_color" "1.000000 0.976471 0.501961" "light" "200" "classname" "light" "origin" "-2088 -1400 696" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2168 -1264 1248" "light" "200" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2088 -1528 312" "classname" "light" "light" "200" } { "_color" "1.000000 0.976471 0.501961" "origin" "-1960 -1528 312" "classname" "light" "light" "200" } { "_color" "1.000000 0.976471 0.501961" "light" "300" "classname" "light" "origin" "-2088 -1528 504" } { "_color" "1.000000 0.976471 0.501961" "light" "300" "classname" "light" "origin" "-1960 -1528 504" } { "_color" "1.000000 0.976471 0.501961" "light" "300" "classname" "light" "origin" "-1960 -1528 696" } { "_color" "1.000000 0.976471 0.501961" "light" "300" "classname" "light" "origin" "-2088 -1528 696" } { "origin" "-2472 -1528 504" "light" "300" "classname" "light" "_color" "1.000000 0.576471 0.501961" } { "origin" "-2344 -1528 504" "light" "300" "classname" "light" "_color" "1.000000 0.976471 0.501961" } { "origin" "-2088 -1272 504" "classname" "light" "light" "200" "_color" "1.000000 0.976471 0.501961" } { "origin" "-1960 -1272 504" "classname" "light" "light" "200" "_color" "1.000000 0.976471 0.501961" } { "origin" "-1832 -1528 504" "classname" "light" "light" "300" "_color" "1.000000 0.976471 0.501961" } { "origin" "-1704 -1528 504" "classname" "light" "light" "300" "_color" "1.000000 0.976471 0.501961" } { "origin" "-1576 -1528 520" "classname" "light" "light" "300" "_color" "1.000000 0.976471 0.501961" } { "origin" "-2472 -1528 696" "light" "300" "classname" "light" "_color" "1.000000 0.976471 0.501961" } { "origin" "-2344 -1528 696" "light" "300" "classname" "light" "_color" "1.000000 0.976471 0.501961" } { "origin" "-2088 -1272 696" "classname" "light" "light" "300" "_color" "1.000000 0.976471 0.501961" } { "origin" "-1960 -1272 696" "classname" "light" "light" "300" "_color" "1.000000 0.976471 0.501961" } { "origin" "-1832 -1528 696" "classname" "light" "light" "300" "_color" "1.000000 0.976471 0.501961" } { "origin" "-1704 -1528 696" "classname" "light" "light" "300" "_color" "1.000000 0.976471 0.501961" } { "origin" "-1576 -1528 696" "classname" "light" "light" "300" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-1816 -1272 1008" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-2128 -1272 1008" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "200" "origin" "-1872 -1264 1144" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "300" "origin" "-1848 -1264 1024" "_color" "1.000000 0.976471 0.501961" } { "light" "300" "classname" "light" "origin" "-1576 -1528 376" "_color" "1.000000 0.576471 0.501961" } { "light" "200" "classname" "light" "origin" "-1704 -1528 312" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "200" "origin" "-2328 -1528 312" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-1832 -1528 312" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-1960 -1272 376" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-2088 -1272 344" "_color" "1.000000 0.976471 0.501961" } { "origin" "-1880 -752 53" "_color" "1.000000 0.853755 0.268775" "light" "150" "classname" "light" } { "origin" "-2688 -176 167" "_color" "1.000000 0.853755 0.268775" "light" "150" "classname" "light" } { "_color" "0.615686 1.000000 0.894118" "origin" "-1912 -72 -384" "light" "70" "classname" "light" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "70" "origin" "-1848 -72 -384" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2272 72 -384" "classname" "light" "light" "90" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2232 120 -384" "light" "70" "classname" "light" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2232 184 -384" "light" "70" "classname" "light" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2296 120 -384" "classname" "light" "light" "70" } { "_color" "0.615686 1.000000 0.894118" "origin" "-2296 184 -384" "classname" "light" "light" "70" } { "_color" "0.826772 0.539370 1.000000" "origin" "-2520 -104 56" "classname" "light" "light" "100" } { "_color" "1.000000 0.976471 0.501961" "light" "100" "classname" "light" "origin" "-2584 -104 56" } { "_color" "1.000000 0.976471 0.501961" "light" "100" "classname" "light" "origin" "-2648 -104 56" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2712 -104 56" "classname" "light" "light" "100" } { "_color" "1.000000 0.976471 0.501961" "light" "100" "classname" "light" "origin" "-2776 -104 56" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2840 -104 56" "classname" "light" "light" "100" } { "_color" "0.826772 0.539370 1.000000" "origin" "-2904 -104 56" "classname" "light" "light" "100" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2904 -40 56" "classname" "light" "light" "100" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2904 24 56" "classname" "light" "light" "100" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2904 88 56" "classname" "light" "light" "100" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2904 152 56" "classname" "light" "light" "64" } { "_color" "1.000000 0.976471 0.501961" "light" "64" "classname" "light" "origin" "-2584 -104 216" } { "_color" "1.000000 0.976471 0.501961" "light" "64" "classname" "light" "origin" "-2648 -104 216" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2712 -104 216" "classname" "light" "light" "64" } { "_color" "1.000000 0.976471 0.501961" "light" "64" "classname" "light" "origin" "-2776 -104 216" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2840 -104 216" "classname" "light" "light" "64" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2904 -104 216" "classname" "light" "light" "64" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2904 -40 216" "classname" "light" "light" "64" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2904 24 216" "classname" "light" "light" "64" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2904 88 188" "classname" "light" "light" "80" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2904 152 188" "classname" "light" "light" "100" } { "classname" "light" "light" "150" "origin" "-2708 136 -128" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-2836 136 -128" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-2708 104 8" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-2580 104 8" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-2900 104 8" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-2708 104 64" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-2580 104 64" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-2836 104 64" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-2900 104 64" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-2708 104 160" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-2580 104 160" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-2836 104 160" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "150" "origin" "-2900 104 160" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2580 168 -224" "light" "150" "classname" "light" } { "_color" "1.000000 0.620553 0.529644" "classname" "light" "light" "150" "origin" "-2324 168 -224" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2708 168 -224" "light" "150" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2836 168 -224" "light" "150" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2900 168 -224" "light" "150" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2580 104 224" "light" "150" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2708 104 224" "light" "150" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2836 104 224" "light" "150" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2900 104 196" "light" "150" "classname" "light" } { "_color" "1.000000 0.620553 0.529644" "origin" "-2580 104 288" "light" "150" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2708 104 288" "light" "150" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2708 104 352" "light" "150" "classname" "light" } { "_color" "1.000000 0.620553 0.529644" "origin" "-2900 104 352" "light" "150" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2636 104 504" "light" "200" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2708 104 448" "light" "200" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2796 104 448" "light" "200" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2580 104 512" "light" "300" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2708 104 512" "light" "300" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2836 104 512" "light" "300" "classname" "light" } { "origin" "-2584 -104 152" "classname" "light" "light" "80" "_color" "1.000000 0.976471 0.501961" } { "origin" "-2648 -104 152" "classname" "light" "light" "80" "_color" "1.000000 0.976471 0.501961" } { "light" "80" "classname" "light" "origin" "-2712 -104 152" "_color" "1.000000 0.976471 0.501961" } { "origin" "-2776 -104 152" "classname" "light" "light" "80" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "light" "150" "classname" "light" "origin" "-2712 32 504" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2648 32 504" "classname" "light" "light" "150" } { "light" "150" "classname" "light" "origin" "-2584 32 504" } { "light" "150" "classname" "light" "origin" "-2520 32 504" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2776 32 504" "classname" "light" "light" "150" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2840 32 504" "classname" "light" "light" "150" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2840 56 504" "classname" "light" "light" "150" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2840 120 504" "classname" "light" "light" "150" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2840 184 504" "classname" "light" "light" "150" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2840 248 504" "classname" "light" "light" "150" } { "_color" "1.000000 0.976471 0.501961" "origin" "-2872 312 504" "classname" "light" "light" "200" } { "classname" "light" "light" "125" "origin" "-2840 568 376" } { "classname" "light" "light" "125" "origin" "-2864 504 376" } { "light" "125" "classname" "light" "origin" "-2880 504 376" } { "classname" "light" "light" "125" "origin" "-2968 568 376" } { "light" "125" "classname" "light" "origin" "-3032 504 376" } { "light" "64" "classname" "light" "origin" "-2904 152 152" "_color" "1.000000 0.976471 0.501961" } { "light" "80" "classname" "light" "origin" "-2904 88 152" "_color" "1.000000 0.976471 0.501961" } { "light" "80" "classname" "light" "origin" "-2904 24 152" "_color" "1.000000 0.976471 0.501961" } { "light" "80" "classname" "light" "origin" "-2904 -40 152" "_color" "1.000000 0.976471 0.501961" } { "light" "80" "classname" "light" "origin" "-2904 -104 152" "_color" "1.000000 0.976471 0.501961" } { "light" "80" "classname" "light" "origin" "-2840 -104 152" "_color" "1.000000 0.976471 0.501961" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "70" "origin" "-2896 48 -352" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "70" "origin" "-2960 232 -152" } { "_color" "1.000000 0.976471 0.501961" "light" "70" "classname" "light" "origin" "-2960 280 -152" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-2808 120 -384" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-2808 184 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-2808 232 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-2360 184 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-2424 184 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-2488 184 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-2616 184 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-2552 184 -384" } { "light" "80" "classname" "light" "origin" "-2680 184 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-2744 184 -384" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "70" "origin" "-2296 232 -384" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-2360 232 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-2360 120 -384" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-2424 232 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-2424 120 -384" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-2488 232 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-2488 120 -384" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-2552 232 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-2552 120 -384" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-2616 232 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-2616 120 -384" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-2680 232 -384" } { "light" "80" "classname" "light" "origin" "-2680 120 -384" } { "_color" "0.615686 1.000000 0.894118" "classname" "light" "light" "80" "origin" "-2744 232 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "80" "classname" "light" "origin" "-2744 120 -384" } { "_color" "0.615686 1.000000 0.894118" "light" "70" "classname" "light" "origin" "-2232 232 -384" } { "_color" "1.000000 0.733333 0.733333" "origin" "-2144 96 136" "light" "100" "classname" "light" } { "classname" "light" "light" "100" "origin" "-2144 160 136" "_color" "1.000000 0.733333 0.733333" } { "classname" "light" "light" "100" "origin" "-1920 96 136" "_color" "1.000000 0.733333 0.733333" } { "_color" "1.000000 0.733333 0.733333" "origin" "-1920 160 136" "light" "100" "classname" "light" } { "_color" "1.000000 0.733333 0.733333" "classname" "light" "light" "150" "origin" "-1880 288 248" } { "model" "*101" "_minlight" "0.2" "lip" "8" "wait" "4" "speed" "75" "angle" "180" "classname" "func_door" "team" "b" } { "classname" "light" "light" "90" "_color" "1.000000 0.920949 0.343874" "origin" "-2032 32 -74" } { "target" "t278" "_cone" "20" "origin" "-2032 176 -78" "_color" "1.000000 0.920949 0.343874" "light" "200" "classname" "light" } { "_cone" "20" "target" "t277" "origin" "-2032 32 -78" "_color" "1.000000 0.920949 0.343874" "light" "200" "classname" "light" } { "model" "*102" "_minlight" "0.2" "team" "b" "lip" "8" "wait" "4" "sounds" "3" "speed" "75" "angle" "0" "classname" "func_door" } { "light" "100" "classname" "light" "origin" "-2464 -192 -152" } { "light" "100" "classname" "light" "origin" "-2448 -176 -152" } { "origin" "-2832 -24 -200" "classname" "light" "light" "100" } { "origin" "-2920 -208 -168" "classname" "light" "light" "100" } { "origin" "-2936 -192 -168" "classname" "light" "light" "100" } { "light" "100" "classname" "light" "origin" "-2936 -80 -168" } { "light" "100" "classname" "light" "origin" "-2920 -64 -168" } { "origin" "-2464 -80 -152" "classname" "light" "light" "100" } { "origin" "-2448 -96 -152" "classname" "light" "light" "100" } { "origin" "-1360 688 440" "light" "110" "classname" "light" } { "origin" "180 -2332 336" "classname" "info_player_start" "angle" "180" "targetname" "unitstart" } { "classname" "light" "light" "175" "origin" "-504 -1771 192" "_color" "1.000000 0.976471 0.501961" } { "light" "175" "classname" "light" "origin" "-624 -1889 192" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "175" "origin" "-382 -2130 512" "_color" "1.000000 0.976471 0.501961" } { "light" "175" "classname" "light" "origin" "-327 -1948 448" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "175" "origin" "-752 -1808 208" "_color" "1.000000 0.976471 0.501961" } { "light" "175" "classname" "light" "origin" "-744 -2288 448" "_color" "1.000000 0.976471 0.501961" }yquake2-QUAKE2_8_40/stuff/mapfixes/baseq2/city2.ent000066400000000000000000003166401465112212000220050ustar00rootroot00000000000000// FIXED ENTITY STRING (by BjossiAlfreds) // // 1. Fixed an unreachable monster_floater (3908) // // His targetname, t54, is never targeted. I changed it to t295 instead, which // is targeted by target_crosslevel_target (680). // That way he is triggered by picking up the Data CD in Upper Palace (city3). // // 2. Fixed unused target_splash (4090) // // This splash was in the level but had no targetname. Gave it what I // believe is the one intended by the level creator ("py1"). // // 3. Fixed inactive trigger_multiple (613) without a targetname // // Changed spawnflags from 2052 to 2048 to remove triggered flag. // // 4. Removed useless trigger_relay (3174, 3272) // // Set their spawnflags to 3840 so that they never spawn. { "nextmap" "city3" "sounds" "6" "sky" "unit9_" "spawnflags" "1792" "classname" "worldspawn" "message" "Lower Palace" } { "model" "*1" "classname" "func_wall" "spawnflags" "2048" "_minlight" "0.2" } { "model" "*2" "classname" "func_wall" "spawnflags" "1792" } { "origin" "216 -248 -680" "spawnflags" "1792" "classname" "ammo_shells" } { "origin" "216 -296 -680" "spawnflags" "1792" "classname" "ammo_shells" } { "origin" "-168 -440 -808" "spawnflags" "0" "classname" "item_pack" } { "origin" "40 -448 -808" "spawnflags" "1792" "classname" "item_armor_jacket" } { "origin" "-856 184 -568" "spawnflags" "1024" "classname" "ammo_bullets" } { "origin" "-856 232 -568" "spawnflags" "1792" "classname" "ammo_bullets" } { "origin" "-856 136 -568" "spawnflags" "1536" "classname" "ammo_bullets" } { "origin" "-688 24 -560" "spawnflags" "1792" "classname" "weapon_chaingun" } { "origin" "-720 -208 -592" "spawnflags" "1792" "classname" "item_armor_jacket" } { "origin" "360 -992 -928" "spawnflags" "1792" "classname" "ammo_cells" } { "origin" "360 -1040 -928" "spawnflags" "1792" "classname" "ammo_cells" } { "origin" "-480 -552 -936" "spawnflags" "1792" "classname" "weapon_shotgun" } { "origin" "-264 -880 -936" "classname" "weapon_hyperblaster" "spawnflags" "1792" } { "origin" "-208 -880 -936" "spawnflags" "1792" "classname" "ammo_cells" } { "classname" "ammo_grenades" "origin" "-70 -1014 -944" } { "origin" "-480 -1248 -688" "classname" "ammo_grenades" "spawnflags" "1792" } { "origin" "-424 -1248 -688" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-864 -1240 -760" "spawnflags" "1792" "classname" "weapon_grenadelauncher" } { "origin" "160 -152 -816" "classname" "ammo_bullets" "spawnflags" "2048" } { "origin" "-64 -144 -744" "spawnflags" "1792" "classname" "weapon_chaingun" } { "origin" "368 -1416 -944" "classname" "ammo_shells" } { "origin" "272 -1472 -944" "classname" "ammo_shells" } { "spawnflags" "1792" "origin" "360 -1472 -928" "classname" "weapon_shotgun" } { "classname" "item_health" "origin" "41 -1067 -944" "spawnflags" "2048" } { "origin" "376 -1184 -700" "classname" "ammo_cells" "spawnflags" "2048" } { "origin" "368 -1240 -696" "spawnflags" "1792" "classname" "weapon_chaingun" } { "classname" "ammo_bullets" "origin" "-286 394 -464" "spawnflags" "2048" } { "origin" "-352 360 -472" "spawnflags" "1792" "classname" "weapon_supershotgun" } { "classname" "target_speaker" "targetname" "t303" "noise" "world/spark7.wav" "attenuation" "3" "volume" "0.4" "origin" "168 -200 -728" } { "classname" "func_timer" "random" "1" "wait" "1.5" "target" "t303" "origin" "200 -216 -728" "spawnflags" "1" } { "light" "32" "classname" "light" "origin" "232 -328 -736" "_color" "1.000000 0.948819 0.755906" } { "light" "32" "classname" "light" "origin" "168 -328 -736" "_color" "1.000000 0.948819 0.755906" } { "light" "32" "classname" "light" "origin" "-280 -328 -736" "_color" "1.000000 0.948819 0.755906" } { "light" "32" "classname" "light" "origin" "-344 -328 -736" "_color" "1.000000 0.948819 0.755906" } { "light" "32" "classname" "light" "origin" "-344 -424 -736" "_color" "1.000000 0.948819 0.755906" } { "_color" "1.000000 0.948819 0.755906" "origin" "-280 -424 -736" "classname" "light" "light" "32" } { "origin" "-544 16 -640" "spawnflags" "1792" "light" "80" "classname" "light" } { "_color" "1.000000 0.875537 0.004292" "origin" "-896 -32 -380" "classname" "light" "light" "32" } { "light" "32" "classname" "light" "origin" "-896 416 -380" "_color" "1.000000 0.875537 0.004292" } { "_color" "1.000000 0.875537 0.004292" "origin" "-896 352 -380" "classname" "light" "light" "32" } { "light" "32" "classname" "light" "origin" "-896 224 -380" "_color" "1.000000 0.875537 0.004292" } { "_color" "1.000000 0.875537 0.004292" "origin" "-896 160 -380" "classname" "light" "light" "32" } { "light" "32" "classname" "light" "origin" "-728 -16 -380" "_color" "1.000000 0.875537 0.004292" } { "light" "150" "classname" "light" "origin" "-896 96 -400" "_color" "1.000000 0.875537 0.004292" } { "targetname" "t82" "classname" "trigger_relay" "origin" "-924 0 -564" "spawnflags" "2304" "killtarget" "guntrigger" } { "model" "*3" "wait" "3" "targetname" "t4" "speed" "100" "angle" "-2" "_minlight" "0.2" "classname" "func_door" "lip" "16" } { "origin" "-416 -288 -816" "classname" "item_health_large" } { "origin" "-416 -224 -816" "spawnflags" "2048" "classname" "item_health_large" } { "_color" "1.000000 0.948819 0.755906" "origin" "-376 -296 -736" "classname" "light" "light" "32" } { "style" "32" "_color" "1.000000 0.948819 0.755906" "origin" "168 -200 -736" "classname" "light" "light" "150" "targetname" "t303" "spawnflags" "1" } { "_color" "1.000000 0.948819 0.755906" "origin" "232 -200 -736" "classname" "light" "light" "32" } { "light" "32" "classname" "light" "origin" "-376 -216 -736" "_color" "1.000000 0.948819 0.755906" } { "_color" "1.000000 0.948819 0.755906" "origin" "-280 -184 -736" "classname" "light" "light" "100" } { "light" "32" "classname" "light" "origin" "-344 -184 -736" "_color" "1.000000 0.948819 0.755906" } { "_color" "1.000000 0.948819 0.755906" "origin" "-280 -40 -752" "classname" "light" "light" "32" } { "light" "32" "classname" "light" "origin" "-344 -40 -752" "_color" "1.000000 0.948819 0.755906" } { "classname" "monster_soldier_ss" "spawnflags" "1536" "angle" "180" "origin" "-289 -984 -924" } { "classname" "monster_soldier_ss" "spawnflags" "1536" "targetname" "t13" "angle" "135" "origin" "-185 -529 -928" } { "spawnflags" "1792" "classname" "weapon_supershotgun" "origin" "280 -552 -832" } { "origin" "282 -656 -832" "classname" "ammo_rockets" "spawnflags" "2048" } { "spawnflags" "1792" "classname" "light" "light" "100" "origin" "-864 532 -532" } { "spawnflags" "769" "angle" "270" "classname" "monster_floater" "origin" "-16 124 -334" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "-172 -452 -328" } { "classname" "item_armor_shard" "spawnflags" "1792" "origin" "-172 -500 -328" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "-168 -140 -328" } { "classname" "item_armor_shard" "spawnflags" "1792" "origin" "-168 -92 -328" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "180 -736 -574" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "224 -736 -574" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "268 -736 -574" } { "spawnflags" "2048" "classname" "item_health_large" "origin" "268 -752 -614" } { "classname" "ammo_shells" "spawnflags" "1024" "origin" "-20 -876 -620" } { "classname" "ammo_shells" "spawnflags" "1536" "origin" "-64 -876 -620" } { "classname" "misc_deadsoldier" "origin" "-120 -864 -640" "spawnflags" "2052" "angle" "45" } { "spawnflags" "1024" "classname" "ammo_grenades" "origin" "-664 -864 -920" } { "classname" "ammo_grenades" "spawnflags" "1536" "origin" "-552 -856 -688" } { "spawnflags" "1792" "classname" "weapon_machinegun" "origin" "160 -152 -800" } { "origin" "160 -192 -816" "classname" "ammo_bullets" } { "classname" "ammo_bullets" "origin" "160 -240 -816" "spawnflags" "3072" } { "spawnflags" "2048" "classname" "item_health" "origin" "576 56 -872" } { "spawnflags" "1792" "classname" "weapon_machinegun" "origin" "-408 -994 -696" } { "origin" "-24 -152 -712" "message" "Access Central Computer\nkeyboard to return\ndata spinner." "targetname" "t186" "classname" "target_help" } { "model" "*4" "wait" "3" "classname" "trigger_multiple" "target" "t301" } { "classname" "trigger_relay" "target" "t301" "targetname" "t46" "origin" "-480 -1432 -1064" } { "spawnflags" "3328" "speed" "1000" "origin" "-388 -1424 -1064" "angle" "180" "dmg" "20" "classname" "target_blaster" "targetname" "t46" } { "spawnflags" "2816" "speed" "1000" "origin" "-388 -1424 -1064" "angle" "180" "dmg" "25" "classname" "target_blaster" "targetname" "t46" } { "classname" "trigger_relay" "targetname" "t279" "killtarget" "reminder" "origin" "0 -808 -536" } { "model" "*5" "classname" "trigger_multiple" "target" "t300" "targetname" "reminder" } { "origin" "0 632 -88" "targetname" "city2XH" "angle" "225" "classname" "info_player_coop" } { "origin" "-128 632 -88" "targetname" "city2XH" "angle" "315" "classname" "info_player_coop" } { "origin" "-64 488 -88" "targetname" "city2XH" "angle" "270" "classname" "info_player_coop" } { "origin" "304 160 -880" "targetname" "city2XL" "angle" "0" "classname" "info_player_coop" } { "origin" "304 224 -880" "targetname" "city2XL" "angle" "0" "classname" "info_player_coop" } { "origin" "248 192 -888" "targetname" "city2XL" "angle" "0" "classname" "info_player_coop" } { "targetname" "city2NL" "angle" "90" "origin" "-96 -1808 -1064" "classname" "info_player_coop" } { "targetname" "city2NL" "angle" "90" "origin" "-64 -1944 -1056" "classname" "info_player_coop" } { "targetname" "city2NL" "classname" "info_player_coop" "origin" "-32 -1808 -1064" "angle" "90" } { "targetname" "city2NH" "angle" "90" "origin" "-120 -1702 -808" "classname" "info_player_coop" } { "targetname" "city2NH" "angle" "90" "origin" "-96 -1918 -816" "classname" "info_player_coop" } { "targetname" "city2NH" "angle" "90" "origin" "-64 -1862 -800" "classname" "info_player_start" } { "model" "*6" "classname" "trigger_multiple" "spawnflags" "4" "target" "t302" "targetname" "reminder" } { "classname" "target_speaker" "noise" "world/v_cit1.wav" "origin" "-64 -1696 -1008" "attenuation" "-1" "targetname" "t302" } { "model" "*7" "spawnflags" "2048" "classname" "trigger_multiple" "target" "t5" } { "classname" "target_speaker" "targetname" "t298" "origin" "-384 -400 -592" "spawnflags" "2048" "noise" "world/voice5.wav" "attenuation" "-1" "volume" "0.7" } { "classname" "func_timer" "target" "t298" "attenuation" "-1" "random" "34" "wait" "120" "targetname" "t89" "origin" "-384 -368 -592" "spawnflags" "2049" } { "classname" "target_speaker" "targetname" "t297" "origin" "-352 -400 -592" "spawnflags" "2048" "noise" "world/v_fac3.wav" "attenuation" "-1" "volume" "0.7" } { "delay" "3" "classname" "func_timer" "target" "t297" "attenuation" "-1" "random" "100" "wait" "400" "targetname" "t89" "origin" "-352 -368 -592" "spawnflags" "2049" } { "model" "*8" "classname" "trigger_multiple" "wait" "30" "target" "t296" "spawnflags" "2048" } { "classname" "target_speaker" "noise" "world/v_cit2.wav" "attenuation" "-1" "targetname" "t296" "spawnflags" "2052" "origin" "-24 -456 -544" } { "targetname" "t228" "origin" "-256 24 -752" "classname" "target_speaker" "spawnflags" "2050" "noise" "world/klaxon1.wav" } { "origin" "480 136 -896" "target" "t295" "spawnflags" "8" "classname" "target_crosslevel_target" } { "model" "*9" "targetname" "t295" "target" "t294" "spawnflags" "4" "classname" "trigger_once" } { "targetname" "t294" "origin" "520 -192 -816" "angle" "90" "classname" "target_spawner" "target" "monster_berserk" "spawnflags" "768" } { "targetname" "t294" "origin" "576 -176 -816" "angle" "90" "classname" "target_spawner" "target" "monster_berserk" "spawnflags" "256" } { "targetname" "t292" "deathtarget" "t57" "origin" "-94 -416 -416" "spawnflags" "3" "angle" "270" "classname" "monster_floater" } { "targetname" "t292" "classname" "monster_floater" "angle" "270" "spawnflags" "3" "origin" "-16 -416 -416" "deathtarget" "t57" } { "origin" "-904 264 -568" "targetname" "t292" "spawnflags" "259" "angle" "45" "classname" "monster_berserk" } { "origin" "-872 0 -568" "targetname" "t292" "spawnflags" "3" "angle" "45" "classname" "monster_berserk" } { "origin" "-56 -8 -376" "angle" "90" "targetname" "t292" "spawnflags" "3" "classname" "monster_tank_commander" } { "origin" "0 -200 -784" "targetname" "t293" "spawnflags" "1" "classname" "point_combat" } { "spawnflags" "2056" "origin" "32 -168 -672" "target" "t292" "classname" "target_crosslevel_target" } { "target" "t293" "origin" "32 -168 -768" "angle" "270" "targetname" "t292" "spawnflags" "3" "classname" "monster_tank_commander" } { "classname" "trigger_relay" "targetname" "t290" "target" "t291" "delay" "2" "origin" "480 -144 -816" } { "volume" "0.8" "classname" "target_speaker" "spawnflags" "4" "attenuation" "-1" "noise" "world/deactivated.wav" "origin" "484 -144 -832" "targetname" "yfld" } { "model" "*10" "classname" "func_wall" "spawnflags" "1792" } { "model" "*11" "classname" "func_button" "angle" "90" "sounds" "4" "_minlight" "0.8" "target" "redfieldbutton" "lip" "4" "wait" "-1" "spawnflags" "2048" } { "model" "*12" "classname" "func_button" "angle" "180" "sounds" "4" "wait" "-1" "spawnflags" "2048" "_minlight" "0.8" "target" "yfld" } { "model" "*13" "classname" "func_wall" "spawnflags" "2048" "_minlight" "0.2" "target" "t290" } { "targetname" "rfld" "killtarget" "redmsg" "origin" "-177 -814 -924" "classname" "trigger_relay" } { "classname" "monster_brain" "angle" "315" "targetname" "brainguy" "origin" "480 -156 -848" } { "classname" "monster_soldier_ss" "angle" "45" "origin" "504 -48 -856" "item" "ammo_bullets" "spawnflags" "256" } { "classname" "point_combat" "origin" "304 -792 -624" "spawnflags" "2048" "targetname" "t289" } { "origin" "-184 -778 -846" "targetname" "redfieldbutton" "killtarget" "redforfld" "classname" "trigger_relay" } { "targetname" "t148" "killtarget" "t182" "origin" "-32 -256 -768" "spawnflags" "2064" "classname" "target_crosslevel_trigger" } { "targetname" "t187" "classname" "trigger_relay" "origin" "-176 -192 -744" "killtarget" "t190" "spawnflags" "2048" } { "classname" "weapon_machinegun" "spawnflags" "1792" "origin" "-68 403 -66" } { "origin" "-280 -1024 -896" "light" "80" "classname" "light" } { "origin" "-152 -840 -848" "targetname" "redfieldbutton" "target" "t288" "delay" "2" "classname" "trigger_relay" } { "classname" "info_player_intermission" "angles" "20 68 0" "origin" "-120 -456 -528" } { "model" "*14" "spawnflags" "2048" "targetname" "redmsg" "message" "This force field is\ndeactivated elsewhere." "classname" "trigger_multiple" "wait" "3" } { "spawnflags" "1792" "origin" "-64 -240 -920" "target" "t286" "angle" "90" "classname" "misc_teleporter" } { "model" "*15" "wait" "-1" "classname" "func_door" "angle" "90" "sounds" "2" "speed" "50" "_minlight" "0.1" "spawnflags" "2048" "targetname" "spg1" } { "spawnflags" "1792" "targetname" "t286" "origin" "-288 -744 -696" "angle" "225" "classname" "misc_teleporter_dest" } { "origin" "112 -1424 -874" "_color" "1.000000 0.796078 0.003922" "light" "48" "classname" "light" } { "light" "48" "classname" "light" "origin" "-8 -1784 -590" "_color" "1.000000 0.796078 0.003922" } { "origin" "-120 -1784 -590" "classname" "light" "light" "48" "_color" "1.000000 0.796078 0.003922" } { "_color" "1.000000 0.796078 0.003922" "light" "48" "classname" "light" "origin" "-120 -1480 -590" } { "light" "100" "classname" "light" "origin" "-64 -1784 -590" } { "classname" "misc_teleporter_dest" "targetname" "t285" "spawnflags" "1792" "origin" "-144 -288 -328" "angle" "0" } { "model" "*16" "classname" "func_wall" "spawnflags" "1792" } { "model" "*17" "spawnflags" "1792" "classname" "func_wall" } { "classname" "misc_teleporter" "spawnflags" "1792" "origin" "-56 -1776 -920" "target" "t285" "angle" "180" } { "light" "64" "classname" "light" "origin" "128 -800 -880" "_color" "1.000000 0.875537 0.004292" } { "light" "64" "classname" "light" "origin" "192 -800 -880" "_color" "1.000000 0.875537 0.004292" } { "light" "80" "classname" "light" "origin" "256 -800 -880" "_color" "1.000000 0.875537 0.004292" } { "light" "80" "classname" "light" "origin" "320 -800 -880" "_color" "1.000000 0.875537 0.004292" } { "_color" "1.000000 0.875537 0.004292" "origin" "320 -1184 -880" "classname" "light" "light" "64" } { "_color" "1.000000 0.875537 0.004292" "origin" "320 -1120 -880" "classname" "light" "light" "64" } { "light" "80" "classname" "light" "origin" "320 -864 -880" "_color" "1.000000 0.875537 0.004292" } { "light" "64" "classname" "light" "origin" "320 -928 -880" "_color" "1.000000 0.875537 0.004292" } { "light" "64" "classname" "light" "origin" "320 -992 -880" "_color" "1.000000 0.875537 0.004292" } { "light" "64" "classname" "light" "origin" "320 -1056 -880" "_color" "1.000000 0.875537 0.004292" } { "light" "64" "classname" "light" "origin" "320 -1248 -880" "_color" "1.000000 0.875537 0.004292" } { "light" "150" "classname" "light" "origin" "320 -1120 -816" "_color" "1.000000 0.875537 0.004292" } { "classname" "misc_teleporter" "target" "t283" "origin" "0 480 -88" "spawnflags" "1792" } { "classname" "misc_teleporter_dest" "spawnflags" "1792" "origin" "-480 472 -472" "targetname" "t284" "angle" "315" } { "classname" "misc_teleporter_dest" "angle" "135" "spawnflags" "1792" "origin" "360 -1248 -696" "targetname" "t283" } { "classname" "misc_teleporter" "spawnflags" "1792" "origin" "-64 -1096 -760" "target" "t284" } { "spawnflags" "1792" "classname" "misc_teleporter_dest" "targetname" "t282" "origin" "-420 -1668 -920" "angle" "135" } { "classname" "misc_teleporter" "angle" "0" "target" "t282" "origin" "480 -144 -864" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "angle" "45" "origin" "-938 -34 -568" } { "model" "*18" "classname" "func_wall" "spawnflags" "1792" } { "spawnflags" "1792" "classname" "ammo_shells" "origin" "360 -656 -832" } { "classname" "item_health_large" "spawnflags" "2048" "origin" "320 -656 -816" } { "model" "*19" "classname" "func_wall" "spawnflags" "2048" } { "classname" "item_health_mega" "spawnflags" "1792" "origin" "-544 16 -672" } { "model" "*20" "classname" "func_wall" "spawnflags" "2048" } { "model" "*21" "classname" "func_door" "health" "5" "angle" "90" "spawnflags" "1792" } { "classname" "weapon_shotgun" "spawnflags" "1792" "origin" "-66 -426 -636" } { "classname" "item_armor_combat" "spawnflags" "1536" "origin" "-108 -846 -612" } { "classname" "item_armor_body" "spawnflags" "1792" "origin" "544 352 -826" } { "classname" "light" "light" "100" "_color" "1.000000 0.578740 0.003937" "origin" "544 352 -776" } { "model" "*22" "classname" "func_wall" "spawnflags" "2048" } { "model" "*23" "spawnflags" "1792" "angle" "-1" "health" "5" "classname" "func_door" } { "spawnflags" "2048" "classname" "ammo_shells" "origin" "632 -194 -872" } { "classname" "item_armor_jacket" "spawnflags" "1792" "origin" "70 -846 -938" } { "spawnflags" "2048" "origin" "-279 -1023 -944" "classname" "item_health" } { "spawnflags" "2048" "classname" "ammo_bullets" "origin" "-277 -1065 -944" } { "model" "*24" "classname" "func_wall" "spawnflags" "2048" } { "model" "*25" "lip" "8" "sounds" "2" "speed" "200" "wait" "3" "angle" "0" "classname" "func_door" "team" "traphider" "spawnflags" "1792" "health" "5" } { "model" "*26" "lip" "8" "classname" "func_door" "angle" "180" "wait" "3" "speed" "200" "sounds" "0" "team" "traphider" "spawnflags" "1792" "health" "5" } { "classname" "item_armor_combat" "spawnflags" "1792" "origin" "-898 520 -566" } { "model" "*27" "classname" "func_wall" "spawnflags" "2048" } { "model" "*28" "classname" "func_wall" "spawnflags" "1536" } { "model" "*29" "classname" "func_wall" "spawnflags" "1536" } { "classname" "ammo_grenades" "origin" "152 -32 -808" "spawnflags" "2048" } { "spawnflags" "2048" "classname" "ammo_bullets" "origin" "-328 -576 -944" } { "classname" "weapon_railgun" "spawnflags" "1792" "origin" "-64 -1216 -800" } { "classname" "ammo_rockets" "spawnflags" "1792" "origin" "-168 -1480 -968" } { "classname" "weapon_rocketlauncher" "spawnflags" "1792" "origin" "-56 -1416 -928" } { "classname" "ammo_bullets" "spawnflags" "1536" "origin" "-656 -728 -704" } { "classname" "item_health" "origin" "-408 -1038 -696" } { "classname" "weapon_hyperblaster" "origin" "-536 -728 -248" "spawnflags" "1792" } { "spawnflags" "2048" "classname" "ammo_bullets" "origin" "-48 -864 -352" } { "spawnflags" "2048" "origin" "-92 -864 -352" "classname" "ammo_rockets" } { "classname" "ammo_rockets" "origin" "-92 -864 -304" "spawnflags" "1792" "team" "ammo1" } { "spawnflags" "1792" "classname" "weapon_grenadelauncher" "origin" "-112 -8 -384" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "-16 -8 -384" } { "classname" "item_health" "spawnflags" "3584" "origin" "-140 468 -88" } { "classname" "item_health" "spawnflags" "3072" "origin" "12 468 -92" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb15.wav" "origin" "-48 688 120" } { "light" "80" "classname" "light" "origin" "-104 696 126" } { "light" "80" "classname" "light" "origin" "-24 696 126" } { "classname" "light" "light" "80" "origin" "-24 584 126" } { "classname" "light" "light" "80" "origin" "-104 584 126" } { "classname" "light" "light" "64" "_color" "1.000000 0.677165 0.003937" "origin" "-408 8 -330" } { "classname" "trigger_always" "target" "t14" "origin" "102 -636 -830" "spawnflags" "1792" } { "targetname" "t288" "spawnflags" "2052" "origin" "-167 -860 -849" "attenuation" "-1" "noise" "world/redforce.wav" "classname" "target_speaker" } { "classname" "item_quad" "team" "q2best" "origin" "56 -280 -336" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "angle" "135" "origin" "232 -456 -808" } { "classname" "info_player_deathmatch" "angle" "0" "origin" "-416 -224 -808" } { "classname" "info_player_deathmatch" "angle" "225" "origin" "224 48 -680" } { "classname" "info_player_deathmatch" "angle" "0" "origin" "-208 -1200 -936" } { "classname" "info_player_deathmatch" "angle" "225" "origin" "-296 -136 -664" } { "classname" "info_player_deathmatch" "angle" "225" "origin" "352 -744 -616" } { "classname" "info_player_deathmatch" "angle" "90" "origin" "24 -1728 -808" } { "spawnflags" "2052" "attenuation" "-1" "origin" "-72 -128 -696" "targetname" "spg2" "noise" "world/dataspin.wav" "classname" "target_speaker" } { "noise" "world/force1.wav" "origin" "320 -936 -568" "spawnflags" "2049" "targetname" "rfld" "classname" "target_speaker" } { "model" "*30" "spawnflags" "2048" "classname" "func_wall" } { "origin" "-316 -856 -460" "classname" "light" "light" "80" } { "light" "80" "classname" "light" "origin" "-316 -856 -396" } { "origin" "-316 -856 -588" "classname" "light" "light" "80" } { "light" "80" "classname" "light" "origin" "-316 -856 -524" } { "model" "*31" "target" "t281" "targetname" "rfld" "spawnflags" "2052" "classname" "trigger_once" } { "spawnflags" "2048" "origin" "320 -784 -624" "targetname" "t280" "classname" "point_combat" } { "model" "*32" "spawnflags" "2054" "targetname" "redforfld" "classname" "func_wall" } { "model" "*33" "spawnflags" "2048" "message" "This force field is\ndeactivated elsewhere." "targetname" "redmsg" "classname" "trigger_multiple" "wait" "3" } { "origin" "0 -784 -584" "target" "t279" "spawnflags" "4" "classname" "target_crosslevel_target" } { "attenuation" "2" "noise" "world/spark5.wav" "origin" "-280 -824 -264" "classname" "target_speaker" "targetname" "t108" } { "origin" "-424 -808 -600" "wait" "1" "random" ".9" "spawnflags" "1" "classname" "func_timer" "target" "t108" } { "classname" "target_speaker" "origin" "-616 -824 -600" "noise" "world/spark5.wav" "attenuation" "2" "targetname" "t194" } { "classname" "func_timer" "target" "t194" "spawnflags" "1" "random" ".9" "wait" "1" "origin" "-616 -808 -600" } { "classname" "func_timer" "spawnflags" "2049" "wait" "2" "target" "t16" "origin" "222 -1219 -881" } { "origin" "-64 -1528 -862" "_color" "1.000000 0.799213 0.003937" "light" "64" "classname" "light" } { "origin" "-64 -1464 -862" "classname" "light" "light" "64" "_color" "1.000000 0.799213 0.003937" } { "origin" "-64 -1592 -862" "classname" "light" "light" "64" "_color" "1.000000 0.799213 0.003937" } { "origin" "-64 -1400 -862" "_color" "1.000000 0.799213 0.003937" "light" "64" "classname" "light" } { "_color" "1.000000 0.875537 0.004292" "origin" "320 -1288 -808" "classname" "light" "light" "64" } { "_color" "1.000000 0.875537 0.004292" "origin" "64 -800 -880" "classname" "light" "light" "64" } { "origin" "322 -536 -774" "light" "80" "classname" "light" } { "origin" "322 -608 -774" "light" "80" "classname" "light" } { "origin" "-320 -800 -824" "classname" "light" "_color" "1.000000 0.384921 0.011905" "light" "64" } { "origin" "-312 -744 -824" "classname" "light" "_color" "1.000000 0.384921 0.011905" "light" "64" } { "origin" "-248 -744 -824" "classname" "light" "_color" "1.000000 0.384921 0.011905" "light" "64" } { "origin" "-168 -744 -824" "classname" "light" "_color" "1.000000 0.384921 0.011905" "light" "64" } { "origin" "-104 -744 -824" "classname" "light" "_color" "1.000000 0.384921 0.011905" "light" "64" } { "origin" "-320 -864 -824" "light" "64" "_color" "1.000000 0.384921 0.011905" "classname" "light" } { "origin" "-724 -848 -808" "classname" "light" "_color" "1.000000 0.250980 0.000000" "light" "64" } { "origin" "-722 -930 -806" "classname" "light" "_color" "1.000000 0.250980 0.000000" "light" "64" } { "origin" "-724 -1078 -816" "classname" "light" "_color" "1.000000 0.250980 0.000000" "light" "64" } { "origin" "-852 -1078 -816" "classname" "light" "_color" "1.000000 0.250980 0.000000" "light" "64" } { "origin" "-878 -996 -816" "classname" "light" "_color" "1.000000 0.250980 0.000000" "light" "64" } { "origin" "-878 -908 -816" "classname" "light" "_color" "1.000000 0.250980 0.000000" "light" "64" } { "origin" "-852 -778 -816" "classname" "light" "_color" "1.000000 0.250980 0.000000" "light" "64" } { "origin" "-724 -778 -816" "classname" "light" "_color" "1.000000 0.250980 0.000000" "light" "64" } { "origin" "-818 -894 -808" "light" "64" "_color" "1.000000 0.250980 0.000000" "classname" "light" } { "origin" "-448 -832 -546" "classname" "light" "light" "64" "_color" "1.000000 0.850980 0.000000" } { "origin" "-448 -896 -546" "classname" "light" "light" "64" "_color" "1.000000 0.850980 0.000000" } { "origin" "-448 -960 -546" "classname" "light" "light" "64" "_color" "1.000000 0.850980 0.000000" } { "origin" "-448 -1024 -546" "classname" "light" "light" "64" "_color" "1.000000 0.850980 0.000000" } { "origin" "-448 -768 -546" "_color" "1.000000 0.850980 0.000000" "light" "64" "classname" "light" } { "origin" "-544 288 -456" "classname" "light" "light" "64" } { "origin" "-544 488 -456" "light" "64" "classname" "light" } { "origin" "-272 488 -330" "classname" "light" "light" "64" } { "origin" "-272 384 -330" "classname" "light" "light" "64" } { "origin" "-272 200 -330" "classname" "light" "light" "64" } { "origin" "-272 280 -330" "classname" "light" "light" "64" } { "origin" "-432 280 -330" "classname" "light" "light" "64" } { "origin" "-376 120 -330" "classname" "light" "light" "64" } { "origin" "-248 120 -330" "classname" "light" "light" "64" } { "origin" "-432 488 -330" "light" "64" "classname" "light" } { "classname" "monster_floater" "angle" "0" "spawnflags" "257" "origin" "-310 -560 -630" "deathtarget" "t57" } { "spawnflags" "2048" "classname" "target_secret" "targetname" "py1" "message" "You have found a secret." "origin" "34 -1114 -618" } { "model" "*34" "classname" "trigger_once" "target" "t155" "spawnflags" "2048" } { "spawnflags" "2048" "classname" "target_help" "targetname" "t148" "origin" "-16 -312 -792" "message" "Shut down communication\nlaser in Upper Palace." } { "classname" "misc_explobox" "origin" "-36 -1484 -976" } { "_color" "1.000000 0.948819 0.755906" "origin" "-304 -68 -784" "classname" "light" "light" "70" } { "_color" "1.000000 0.948819 0.755906" "origin" "-304 -132 -784" "classname" "light" "light" "70" } { "_color" "1.000000 0.948819 0.755906" "origin" "-304 -196 -784" "classname" "light" "light" "70" } { "_color" "1.000000 0.948819 0.755906" "origin" "-304 -260 -784" "classname" "light" "light" "70" } { "_color" "1.000000 0.948819 0.755906" "origin" "-304 -324 -784" "classname" "light" "light" "70" } { "_color" "1.000000 0.948819 0.755906" "origin" "-304 -388 -784" "classname" "light" "light" "70" } { "light" "70" "classname" "light" "origin" "-304 -4 -784" "_color" "1.000000 0.948819 0.755906" } { "origin" "-260 -384 -824" "targetname" "t273" "classname" "info_null" } { "spawnflags" "2048" "targetname" "t265" "origin" "24 -360 -808" "classname" "point_combat" } { "spawnflags" "2048" "target" "t265" "origin" "168 -392 -808" "targetname" "t264" "classname" "point_combat" } { "spawnflags" "2048" "origin" "-72 -488 -456" "target" "t263" "targetname" "t262" "classname" "path_corner" } { "spawnflags" "2048" "origin" "-152 -280 -456" "target" "t262" "targetname" "t261" "classname" "path_corner" } { "spawnflags" "2048" "origin" "-56 -152 -456" "target" "t261" "targetname" "t260" "classname" "path_corner" } { "spawnflags" "2048" "origin" "40 -232 -456" "targetname" "t263" "target" "t260" "classname" "path_corner" } { "origin" "264 -808 -536" "classname" "light" "light" "80" "_color" "1.000000 0.875537 0.004292" } { "_color" "1.000000 0.875537 0.004292" "light" "80" "classname" "light" "origin" "212 -808 -536" } { "_color" "1.000000 0.875537 0.004292" "light" "80" "classname" "light" "origin" "264 -764 -536" } { "origin" "368 -792 -536" "classname" "light" "light" "80" "_color" "1.000000 0.875537 0.004292" } { "_color" "1.000000 0.875537 0.004292" "light" "80" "classname" "light" "origin" "212 -764 -536" } { "origin" "316 -836 -536" "classname" "light" "light" "80" "_color" "1.000000 0.875537 0.004292" } { "light" "100" "origin" "-184 -288 -616" "classname" "light" "_color" "1.000000 0.255906 0.003937" } { "light" "100" "origin" "-184 -288 -752" "classname" "light" "_color" "1.000000 0.255906 0.003937" } { "light" "80" "classname" "light" "origin" "-64 -392 -752" } { "light" "64" "classname" "light" "origin" "-64 -392 -792" } { "model" "*35" "_minlight" "0.3" "target" "t4" "classname" "func_button" "angle" "270" "lip" "3" } { "model" "*36" "wait" "5" "target" "t4" "delay" "1" "classname" "trigger_multiple" "spawnflags" "0" } { "origin" "-64 -360 -720" "classname" "light" "light" "80" } { "origin" "-64 -432 -816" "light" "64" "classname" "light" } { "origin" "-64 -348 -368" "light" "125" "classname" "light" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "85" "origin" "-136 -348 -304" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "125" "origin" "-88 -348 -256" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "125" "origin" "-40 -348 -256" "_color" "1.000000 0.976471 0.501961" } { "origin" "8 -348 -304" "light" "85" "classname" "light" "_color" "1.000000 0.976471 0.501961" } { "origin" "-40 -356 -368" "light" "125" "classname" "light" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "85" "origin" "8 -156 -304" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "125" "origin" "-40 -164 -368" } { "_color" "1.000000 0.658824 0.576471" "origin" "-136 -156 -304" "light" "85" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "125" "origin" "-64 -156 -368" } { "_color" "1.000000 0.976471 0.501961" "origin" "-40 -220 -256" "light" "125" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "85" "origin" "8 -220 -304" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "100" "origin" "-40 -228 -368" } { "_color" "1.000000 0.976471 0.501961" "origin" "-88 -220 -256" "light" "125" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-136 -220 -304" "light" "85" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "125" "origin" "-64 -220 -368" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "85" "origin" "8 -412 -304" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "125" "origin" "-40 -420 -368" } { "_color" "1.000000 0.976471 0.501961" "origin" "-136 -412 -304" "light" "85" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "125" "origin" "-64 -412 -368" } { "_color" "1.000000 0.976471 0.501961" "origin" "-40 -284 -256" "light" "125" "classname" "light" } { "_color" "1.000000 0.658824 0.576471" "classname" "light" "light" "85" "origin" "8 -284 -304" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "125" "origin" "-40 -292 -368" } { "_color" "1.000000 0.976471 0.501961" "origin" "-88 -284 -256" "light" "125" "classname" "light" } { "_color" "1.000000 0.658824 0.576471" "origin" "-136 -284 -304" "light" "85" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "125" "origin" "-64 -284 -368" } { "spawnflags" "2048" "target" "t253" "targetname" "t214" "origin" "-476 -1208 -600" "classname" "trigger_relay" } { "spawnflags" "2048" "target" "t252" "targetname" "t214" "classname" "trigger_relay" "origin" "-476 -1164 -600" } { "model" "*37" "spawnflags" "2048" "classname" "trigger_once" "target" "t251" } { "origin" "-277 -1065 -912" "classname" "ammo_shells" "spawnflags" "1792" } { "model" "*38" "classname" "trigger_once" "delay" "0.5" "target" "t250" } { "target" "t247" "origin" "48 -528 -528" "wait" "7" "random" "5" "spawnflags" "1" "classname" "func_timer" } { "targetname" "t247" "origin" "48 -492 -528" "volume" "0.12" "attenuation" "1" "noise" "world/battle5.wav" "classname" "target_speaker" } { "target" "t248" "origin" "32 -528 -528" "wait" "20" "random" "10" "spawnflags" "1" "classname" "func_timer" } { "targetname" "t248" "origin" "32 -492 -528" "volume" "0.2" "attenuation" "1" "noise" "world/battle5.wav" "classname" "target_speaker" } { "origin" "-4 -576 -528" "target" "t244" "wait" "12" "random" "10" "spawnflags" "1" "classname" "func_timer" } { "origin" "-4 -540 -528" "targetname" "t244" "volume" "0.35" "attenuation" "1" "noise" "world/battle5.wav" "classname" "target_speaker" } { "target" "t249" "origin" "16 -528 -528" "classname" "func_timer" "spawnflags" "1" "random" "12" "wait" "35" } { "targetname" "t249" "origin" "16 -492 -528" "classname" "target_speaker" "noise" "world/battle5.wav" "attenuation" "1" "volume" "0.35" } { "origin" "-76 -452 -420" "target" "t245" "classname" "func_timer" "spawnflags" "1" "random" "8" "wait" "16" } { "origin" "-76 -416 -420" "targetname" "t245" "classname" "target_speaker" "noise" "world/battle5.wav" "attenuation" "1" "volume" "0.6" } { "origin" "-128 -528 -528" "volume" "0.4" "attenuation" "1" "noise" "world/battle5.wav" "targetname" "t243" "classname" "target_speaker" } { "origin" "-128 -564 -528" "wait" "15" "random" "10" "spawnflags" "1" "target" "t243" "classname" "func_timer" } { "model" "*39" "classname" "func_wall" "spawnflags" "2048" } { "origin" "-64 -640 -388" "targetname" "t241" "classname" "info_notnull" } { "style" "33" "light" "175" "targetname" "t242" "_cone" "25" "origin" "-64 -640 -308" "target" "t241" "classname" "light" } { "spawnflags" "0" "target" "t242" "origin" "-64 -640 -368" "classname" "item_invulnerability" "team" "q2best" } { "origin" "-144 -744 -904" "delay" "1" "dmg" "1" "targetname" "rfld" "classname" "target_explosion" } { "classname" "weapon_shotgun" "origin" "-279 -1023 -912" "spawnflags" "1792" } { "model" "*40" "targetname" "guntrigger" "spawnflags" "2304" "target" "trap_gun" "delay" ".5" "wait" ".8" "classname" "trigger_multiple" } { "origin" "-108 -116 -684" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2" } { "origin" "-12 -116 -684" "noise" "world/comp_hum2" "spawnflags" "1" "classname" "target_speaker" } { "model" "*41" "_minlight" "0.5" "classname" "func_wall" } { "model" "*42" "spawnflags" "2048" "_minlight" "0.6" "targetname" "py1" "dmg" "1" "mass" "100" "classname" "func_explosive" } { "model" "*43" "target" "t240" "classname" "trigger_once" "spawnflags" "2048" } { "origin" "-832 -1024 -936" "targetname" "t239" "classname" "point_combat" "spawnflags" "2048" } { "origin" "-736 -816 -920" "targetname" "t240" "spawnflags" "1025" "angle" "180" "classname" "monster_soldier" } { "origin" "-608 -856 -920" "targetname" "t240" "spawnflags" "769" "angle" "180" "classname" "monster_soldier" } { "origin" "-840 -840 -920" "spawnflags" "1" "target" "t239" "targetname" "t238" "angle" "270" "classname" "monster_soldier" } { "angle" "270" "spawnflags" "257" "origin" "-288 -984 -936" "classname" "monster_berserk" } { "origin" "-56 -1424 -960" "classname" "monster_soldier_light" } { "classname" "item_health_small" "spawnflags" "1536" "origin" "-86 -1248 -936" } { "classname" "item_health_small" "spawnflags" "1024" "origin" "-126 -1248 -936" } { "classname" "item_health_small" "spawnflags" "0" "origin" "-166 -1248 -936" } { "classname" "item_health_small" "spawnflags" "0" "origin" "-206 -1248 -936" } { "light" "80" "classname" "light" "_color" "1.000000 0.920949 0.343874" "origin" "-92 -1678 -938" } { "spawnflags" "2048" "classname" "target_goal" "targetname" "t189" "origin" "56 -160 -752" } { "spawnflags" "2048" "classname" "target_goal" "targetname" "t187" "origin" "-184 -160 -752" } { "_color" "1.000000 0.875537 0.004292" "origin" "328 -1000 -560" "classname" "light" "light" "100" } { "_color" "1.000000 0.875537 0.004292" "origin" "328 -1056 -560" "classname" "light" "light" "100" } { "_color" "1.000000 0.875537 0.004292" "origin" "320 -1120 -536" "classname" "light" "light" "150" } { "model" "*44" "_minlight" "0.2" "speed" "35" "classname" "func_door" "spawnflags" "2049" "angle" "-2" "sounds" "1" "targetname" "spg2" "wait" "-1" } { "style" "1" "targetname" "t79" "classname" "func_areaportal" } { "origin" "-64 -1208 -936" "angle" "135" "spawnflags" "257" "classname" "monster_soldier" "targetname" "t251" } { "target" "t5" "origin" "-16 -1224 -928" "angle" "0" "spawnflags" "1" "classname" "monster_soldier" "targetname" "t251" } { "classname" "misc_explobox" "origin" "-108 -1516 -976" } { "origin" "-144 -744 -920" "volume" "1" "attenuation" "1" "noise" "world/explod2.wav" "targetname" "rfld" "classname" "target_speaker" } { "spawnflags" "2048" "target" "t228" "targetname" "t222" "classname" "trigger_relay" "killtarget" "t224" "origin" "288 200 -848" } { "spawnflags" "2048" "origin" "-8 608 -56" "target" "t228" "targetname" "t225" "classname" "trigger_relay" } { "targetname" "t228" "noise" "world/klaxon1.wav" "spawnflags" "2050" "classname" "target_speaker" "origin" "-64 600 32" } { "targetname" "t228" "noise" "world/klaxon1.wav" "spawnflags" "2050" "classname" "target_speaker" "origin" "-64 -24 -320" } { "targetname" "t228" "noise" "world/klaxon1.wav" "spawnflags" "2" "classname" "target_speaker" "origin" "-80 264 -424" } { "targetname" "t228" "noise" "world/klaxon1.wav" "spawnflags" "2050" "classname" "target_speaker" "origin" "-408 360 -424" } { "targetname" "t228" "noise" "world/klaxon1.wav" "spawnflags" "2050" "classname" "target_speaker" "origin" "-856 368 -512" } { "targetname" "t228" "noise" "world/klaxon1.wav" "spawnflags" "2050" "classname" "target_speaker" "origin" "-896 -8 -512" } { "targetname" "t228" "noise" "world/klaxon1.wav" "spawnflags" "2" "classname" "target_speaker" "origin" "-680 -184 -544" } { "targetname" "t228" "noise" "world/klaxon1.wav" "spawnflags" "2" "classname" "target_speaker" "origin" "-328 -192 -624" } { "targetname" "t228" "noise" "world/klaxon1.wav" "spawnflags" "2" "classname" "target_speaker" "origin" "-320 -488 -624" } { "targetname" "t228" "noise" "world/klaxon1.wav" "spawnflags" "2" "classname" "target_speaker" "origin" "-48 -464 -576" } { "targetname" "t228" "noise" "world/klaxon1.wav" "spawnflags" "2050" "classname" "target_speaker" "origin" "-64 -312 -728" } { "spawnflags" "2048" "origin" "-112 584 -72" "killtarget" "t222" "targetname" "t225" "classname" "trigger_relay" } { "spawnflags" "2048" "origin" "312 224 -848" "killtarget" "t224" "targetname" "t222" "classname" "trigger_relay" } { "origin" "0 576 -40" "target" "t224" "classname" "target_crosslevel_target" "spawnflags" "2056" "delay" "0.1" } { "model" "*45" "target" "t225" "targetname" "t224" "classname" "trigger_once" "spawnflags" "2052" } { "target" "t221" "classname" "target_crosslevel_target" "spawnflags" "2056" "delay" "0.1" "origin" "344 216 -840" } { "model" "*46" "target" "t222" "targetname" "t221" "spawnflags" "2052" "classname" "trigger_once" } { "targetname" "t228" "origin" "-64 216 -272" "classname" "target_speaker" "spawnflags" "2" "noise" "world/klaxon1.wav" } { "targetname" "t228" "origin" "208 -256 -792" "classname" "target_speaker" "spawnflags" "2" "noise" "world/klaxon1.wav" } { "targetname" "t228" "origin" "352 -96 -792" "classname" "target_speaker" "spawnflags" "2050" "noise" "world/klaxon1.wav" } { "targetname" "t228" "origin" "616 168 -840" "classname" "target_speaker" "spawnflags" "2050" "noise" "world/klaxon1.wav" } { "targetname" "t228" "origin" "-64 -160 -712" "noise" "world/klaxon1.wav" "spawnflags" "2050" "classname" "target_speaker" } { "model" "*47" "target" "t218" "targetname" "t214" "classname" "trigger_counter" "spawnflags" "2049" "count" "2" } { "targetname" "t218" "origin" "350 -1014 -640" "spawnflags" "257" "angle" "270" "classname" "monster_berserk" } { "origin" "-800 -1534 -848" "targetname" "t216" "target" "t215" "classname" "trigger_relay" "spawnflags" "2048" } { "origin" "-784 -1552 -848" "targetname" "t217" "target" "t215" "classname" "trigger_relay" "spawnflags" "2048" } { "origin" "-840 -1544 -848" "targetname" "t215" "classname" "monster_berserk" "spawnflags" "256" } { "model" "*48" "targetname" "t252" "target" "t216" "count" "4" "spawnflags" "2049" "classname" "trigger_counter" } { "spawnflags" "2048" "origin" "-392 -1212 -640" "target" "t214" "targetname" "t70" "classname" "trigger_relay" } { "style" "2" "classname" "func_areaportal" "targetname" "t213" } { "style" "3" "classname" "func_areaportal" "targetname" "t212" } { "style" "4" "classname" "func_areaportal" "targetname" "t211" } { "style" "5" "classname" "func_areaportal" "targetname" "t210" } { "style" "6" "classname" "func_areaportal" "targetname" "t209" } { "model" "*49" "classname" "func_door" "angle" "180" "team" "comp3" "_minlight" "0.2" } { "model" "*50" "classname" "func_door" "angle" "0" "team" "comp3" "_minlight" "0.2" "target" "t211" } { "model" "*51" "classname" "func_door" "angle" "270" "team" "comp1" "_minlight" "0.2" } { "model" "*52" "classname" "func_door" "angle" "90" "team" "comp1" "_minlight" "0.2" "target" "t210" } { "model" "*53" "team" "comp4" "angle" "0" "classname" "func_door" "_minlight" "0.2" "target" "t213" } { "model" "*54" "team" "comp4" "angle" "180" "classname" "func_door" "_minlight" "0.2" } { "model" "*55" "classname" "func_door" "angle" "90" "team" "comp2" "_minlight" "0.2" } { "model" "*56" "classname" "func_door" "angle" "270" "team" "comp2" "_minlight" "0.2" "target" "t209" } { "spawnflags" "2048" "targetname" "t206" "origin" "320 -1092 -696" "classname" "point_combat" } { "origin" "352 -1048 -664" "targetname" "t205" "target" "t146" "delay" "3" "classname" "trigger_relay" "spawnflags" "2048" } { "model" "*57" "spawnflags" "2048" "target" "t205" "classname" "trigger_once" } { "target" "t206" "targetname" "t205" "origin" "298 -1014 -644" "angle" "270" "spawnflags" "1" "classname" "monster_berserk" } { "model" "*58" "target" "t21" "classname" "trigger_once" "spawnflags" "2048" } { "spawnflags" "2048" "wait" "3" "origin" "-56 -1196 -808" "target" "t202" "targetname" "t201" "classname" "path_corner" } { "spawnflags" "2048" "wait" "3" "origin" "-56 -1252 -808" "target" "t199" "targetname" "t198" "classname" "path_corner" } { "spawnflags" "2048" "origin" "324 -1192 -712" "target" "t203" "targetname" "t202" "classname" "path_corner" } { "spawnflags" "2048" "origin" "324 -1248 -712" "target" "t200" "targetname" "t199" "classname" "path_corner" } { "spawnflags" "2048" "wait" "3" "origin" "-24 -1196 -808" "target" "t204" "targetname" "t203" "classname" "path_corner" } { "spawnflags" "2048" "wait" "3" "origin" "-24 -1252 -808" "target" "t197" "targetname" "t200" "classname" "path_corner" } { "spawnflags" "2048" "origin" "-380 -1192 -712" "targetname" "t204" "target" "t201" "classname" "path_corner" } { "spawnflags" "2048" "origin" "-380 -1248 -712" "target" "t198" "targetname" "t197" "classname" "path_corner" } { "origin" "-420 -1240 -676" "angle" "0" "target" "t197" "classname" "monster_soldier" } { "origin" "-420 -1196 -676" "angle" "0" "target" "t204" "classname" "monster_soldier" "spawnflags" "256" } { "target" "t214" "targetname" "t70" "origin" "-692 -1180 -760" "angle" "0" "classname" "monster_soldier_ss" "spawnflags" "1" } { "target" "t214" "targetname" "t70" "origin" "-692 -1244 -752" "angle" "0" "classname" "monster_soldier_ss" "spawnflags" "1" } { "target" "t214" "targetname" "t70" "spawnflags" "257" "classname" "monster_soldier_ss" "angle" "0" "origin" "-728 -1188 -772" } { "target" "t214" "targetname" "t70" "spawnflags" "1" "classname" "monster_soldier_ss" "angle" "0" "origin" "-728 -1240 -772" } { "origin" "-448 -928 -872" "targetname" "rfld" "classname" "target_speaker" "noise" "world/force2.wav" "spawnflags" "0" } { "origin" "-608 -1208 -1064" "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" } { "spawnflags" "769" "targetname" "rfld" "origin" "-452 -1480 -928" "angle" "270" "classname" "monster_tank_commander" } { "origin" "-136 -848 -928" "spawnflags" "1" "angle" "180" "classname" "monster_soldier" } { "origin" "-233 -761 -944" "classname" "item_health_large" } { "origin" "-336 -536 -944" "classname" "item_health" } { "origin" "-416 -568 -936" "classname" "ammo_shells" "spawnflags" "1792" } { "origin" "-296 -536 -944" "classname" "item_health" } { "origin" "232 -1400 -914" "_color" "1.000000 0.920949 0.343874" "classname" "light" "light" "100" } { "origin" "136 -1400 -914" "_color" "1.000000 0.920949 0.343874" "classname" "light" "light" "100" } { "origin" "24 -1400 -914" "_color" "1.000000 0.920949 0.343874" "classname" "light" "light" "100" } { "origin" "-64 -1400 -914" "_color" "1.000000 0.920949 0.343874" "classname" "light" "light" "100" } { "origin" "-64 -1464 -914" "_color" "1.000000 0.920949 0.343874" "classname" "light" "light" "150" } { "classname" "target_speaker" "noise" "world/klaxon1.wav" "attenuation" "3" "volume" "0.5" "origin" "-488 -1416 -1064" "spawnflags" "2048" "targetname" "t301" } { "origin" "-364 466 -464" "classname" "item_health" } { "origin" "-424 468 -464" "classname" "item_health" } { "origin" "-296 478 -464" "classname" "item_armor_jacket" "spawnflags" "2048" } { "spawnflags" "1792" "origin" "-286 394 -424" "classname" "ammo_shells" } { "origin" "-374 -800 -312" "targetname" "t181" "spawnflags" "2049" "noise" "world/force1.wav" "classname" "target_speaker" } { "origin" "220 -60 -752" "target" "t192" "targetname" "sd2" "classname" "trigger_relay" } { "origin" "238 -60 -752" "target" "t191" "delay" "8.5" "targetname" "sd2" "classname" "trigger_relay" } { "targetname" "t192" "origin" "198 12 -752" "attenuation" "1" "volume" "0.9" "noise" "world/lite_on1.wav" "classname" "target_speaker" } { "targetname" "t191" "volume" "0.9" "noise" "world/lite_out.wav" "origin" "198 -16 -750" "attenuation" "1" "classname" "target_speaker" } { "classname" "item_health" "origin" "632 -232 -872" } { "origin" "40 -1568 -816" "classname" "item_health" } { "spawnflags" "3584" "origin" "48 -1696 -816" "classname" "item_health" } { "origin" "-168 -1568 -816" "classname" "ammo_shells" } { "classname" "light" "light" "100" "origin" "-100 -92 -384" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "100" "origin" "-28 -92 -384" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "origin" "-100 -92 -448" "light" "100" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-28 -92 -448" "light" "100" "classname" "light" } { "classname" "light" "light" "100" "origin" "-100 -284 -384" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "origin" "-100 -284 -448" "light" "125" "classname" "light" } { "classname" "light" "light" "100" "origin" "-28 -284 -384" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "origin" "-28 -284 -448" "light" "100" "classname" "light" } { "origin" "-100 -476 -384" "light" "100" "classname" "light" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "100" "origin" "-100 -476 -448" } { "_color" "1.000000 0.976471 0.501961" "origin" "-28 -476 -448" "light" "100" "classname" "light" } { "classname" "light" "light" "100" "origin" "-28 -476 -384" "_color" "1.000000 0.976471 0.501961" } { "_color" "1.000000 0.976471 0.501961" "origin" "-28 -92 -320" "light" "125" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-28 -284 -320" "light" "100" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "100" "origin" "-100 -476 -320" } { "_color" "1.000000 0.976471 0.501961" "origin" "-28 -476 -320" "light" "100" "classname" "light" } { "light" "80" "classname" "light" "origin" "-316 -856 -332" } { "classname" "light" "light" "80" "origin" "-316 -856 -652" } { "classname" "info_player_deathmatch" "angle" "180" "origin" "-540 -1232 -1048" } { "classname" "item_adrenaline" "origin" "-328 -1368 -1056" "spawnflags" "2048" } { "classname" "item_armor_shard" "origin" "232 -336 -816" "spawnflags" "1792" } { "spawnflags" "2048" "classname" "target_goal" "targetname" "t148" "origin" "-16 -280 -788" } { "origin" "-152 -776 -888" "targetname" "redfieldbutton" "volume" "0.3" "attenuation" "3" "noise" "world/comp_hum1" "spawnflags" "2049" "classname" "target_speaker" } { "origin" "440 -200 -824" "targetname" "yfld" "noise" "world/l_hum1.wav" "spawnflags" "2049" "classname" "target_speaker" } { "origin" "232 -296 -816" "classname" "item_armor_shard" "spawnflags" "1792" } { "origin" "232 -384 -816" "classname" "item_armor_shard" "spawnflags" "1792" } { "classname" "ammo_shells" "origin" "-336 -368 -816" } { "spawnflags" "2048" "target" "t184" "targetname" "t187" "classname" "trigger_relay" "origin" "-144 -192 -744" } { "spawnflags" "3840" "targetname" "t187" "classname" "trigger_relay" "origin" "-136 -216 -744" } { "origin" "56 -200 -744" "target" "t184" "targetname" "t189" "classname" "trigger_relay" } { "spawnflags" "2048" "target" "t190" "origin" "24 -200 -744" "targetname" "t189" "classname" "trigger_relay" } { "spawnflags" "2048" "killtarget" "t188" "origin" "40 -232 -744" "targetname" "t189" "classname" "trigger_relay" } { "spawnflags" "2048" "target" "t188" "origin" "-160 -200 -744" "targetname" "t187" "classname" "trigger_relay" } { "spawnflags" "2048" "origin" "-160 -240 -728" "message" "Obtain the Data CD\nfrom the Upper Palace." "targetname" "t188" "classname" "target_help" } { "message" "Insert Data Spinner\nin next console." "targetname" "t190" "origin" "-16 -160 -608" "classname" "target_help" "spawnflags" "2048" } { "origin" "-88 -176 -752" "target" "spg1" "delay" "2" "targetname" "spg" "classname" "trigger_relay" "spawnflags" "2048" } { "origin" "-72 -168 -744" "target" "spg2" "delay" "3" "targetname" "spg" "classname" "trigger_relay" "spawnflags" "2048" } { "model" "*59" "targetname" "t186" "lip" "4" "sounds" "1" "angle" "90" "wait" "-1" "spawnflags" "2056" "classname" "func_door" } { "spawnflags" "2048" "targetname" "2nd message" "classname" "trigger_relay" "origin" "-4 -92 -724" "message" "Access keyboard\nto return Data Spinner." "delay" "3" } { "spawnflags" "2048" "target" "2nd message" "delay" "3" "message" "Data Spinner\n is now reprogrammed." "origin" "-24 -92 -700" "targetname" "t186" "classname" "trigger_relay" } { "model" "*60" "target" "t186" "targetname" "t184" "count" "2" "classname" "trigger_counter" "spawnflags" "2049" } { "origin" "-84 -224 -680" "target" "t185" "classname" "trigger_relay" "spawnflags" "3840" } { "origin" "-44 -224 -680" "target" "t185" "targetname" "t148" "classname" "trigger_relay" } { "origin" "-60 -132 -748" "targetname" "t148" "killtarget" "t182" "classname" "trigger_relay" "spawnflags" "2048" } { "spawnflags" "2048" "target" "t187" "message" "Data Spinner inserted for reprogramming." "item" "key_data_spinner" "origin" "-132 -156 -740" "targetname" "t182" "classname" "trigger_key" } { "spawnflags" "2048" "target" "t189" "message" "New program accepted." "item" "key_data_cd" "origin" "36 -176 -756" "targetname" "t183" "classname" "trigger_key" } { "model" "*61" "spawnflags" "2048" "target" "t182" "classname" "trigger_multiple" } { "model" "*62" "spawnflags" "2048" "target" "t183" "classname" "trigger_multiple" } { "model" "*63" "health" "5" "spawnflags" "3840" "_minlight" "0.1" "speed" "50" "sounds" "2" "angle" "90" "classname" "func_door" "wait" "3" } { "origin" "-874 -1170 -778" "classname" "item_health" } { "classname" "target_crosslevel_target" "target" "t181" "spawnflags" "2050" "origin" "-320 -784 -328" } { "classname" "target_crosslevel_trigger" "spawnflags" "2064" "targetname" "t148" "origin" "-8 -256 -768" } { "model" "*64" "classname" "trigger_once" "target" "t180" "spawnflags" "2048" } { "model" "*65" "classname" "trigger_once" "target" "t180" "spawnflags" "2048" } { "origin" "-834 -1170 -778" "classname" "item_health" } { "origin" "-848 -806 -926" "angle" "315" "classname" "info_player_deathmatch" } { "classname" "monster_tank_commander" "angle" "270" "spawnflags" "257" "item" "ammo_bullets" "origin" "-712 0 -584" } { "classname" "monster_tank_commander" "angle" "270" "item" "ammo_rockets" "spawnflags" "257" "origin" "216 24 -680" } { "classname" "light" "light" "80" "_color" "1.000000 1.000000 0.874510" "origin" "-64 504 120" } { "origin" "-64 504 112" "_color" "1.000000 1.000000 1.000000" "light" "300" "classname" "light" "_cone" "20" "target" "t164" } { "origin" "-64 504 -100" "classname" "info_notnull" "targetname" "t164" } { "classname" "light" "light" "80" "_color" "1.000000 1.000000 0.874510" "origin" "-64 584 120" } { "origin" "-64 584 112" "_color" "1.000000 1.000000 1.000000" "light" "300" "classname" "light" "_cone" "20" "target" "t163" } { "origin" "-64 584 -100" "classname" "info_notnull" "targetname" "t163" } { "classname" "light" "light" "80" "_color" "1.000000 1.000000 0.874510" "origin" "-64 696 120" } { "origin" "-64 696 112" "_color" "1.000000 1.000000 1.000000" "light" "300" "classname" "light" "_cone" "20" "target" "t162" } { "origin" "-64 696 -100" "classname" "info_notnull" "targetname" "t162" } { "origin" "-68 355 -82" "spawnflags" "1792" "classname" "ammo_bullets" } { "origin" "-84 -60 -384" "classname" "item_health_large" "spawnflags" "2048" } { "target" "t214" "targetname" "t70" "item" "ammo_bullets" "origin" "-792 -1188 -772" "angle" "0" "classname" "monster_soldier_ss" "spawnflags" "513" } { "target" "t214" "targetname" "t70" "origin" "-792 -1240 -772" "angle" "0" "classname" "monster_soldier_ss" "spawnflags" "257" } { "origin" "-64 -1432 -824" "targetname" "t160" "classname" "info_notnull" } { "origin" "-64 -1432 -612" "target" "t160" "light" "300" "classname" "light" } { "origin" "616 80 -872" "classname" "item_health" } { "origin" "496 72 -864" "classname" "ammo_bullets" "spawnflags" "1536" } { "origin" "-862 -1630 -856" "angle" "45" "classname" "info_player_deathmatch" } { "origin" "-132 -888 -936" "angle" "135" "classname" "info_player_deathmatch" } { "origin" "-204 -1194 -936" "classname" "item_health_small" "spawnflags" "2048" } { "origin" "-46 -1248 -936" "spawnflags" "1536" "classname" "item_health_small" } { "origin" "-62 -1810 -1064" "angle" "90" "classname" "info_player_deathmatch" } { "origin" "372 -632 -920" "angle" "180" "classname" "info_player_deathmatch" } { "origin" "14 -1026 -944" "classname" "item_health" } { "spawnflags" "1792" "origin" "-158 -1078 -928" "classname" "weapon_machinegun" } { "origin" "42 -1647 -958" "classname" "item_health_small" } { "origin" "-216 -1720 -936" "targetname" "t159" "dmg" "0" "classname" "target_explosion" } { "origin" "60 -1572 -992" "classname" "misc_explobox" } { "targetname" "t13" "item" "ammo_cells" "classname" "monster_brain" "angle" "270" "origin" "-175 -530 -930" "spawnflags" "257" } { "item" "ammo_cells" "classname" "monster_brain" "angle" "270" "origin" "-295 -770 -930" "spawnflags" "1" } { "origin" "-560 -1232 -1056" "classname" "ammo_bullets" "spawnflags" "3072" } { "origin" "-528 -1200 -1056" "classname" "item_health" "spawnflags" "2048" } { "origin" "-136 -816 -344" "angle" "180" "classname" "info_player_deathmatch" } { "origin" "-128 480 -88" "angle" "0" "classname" "info_player_deathmatch" } { "origin" "-288 472 -472" "angle" "225" "classname" "info_player_deathmatch" } { "origin" "-224 0 -664" "angle" "0" "classname" "info_player_deathmatch" } { "origin" "30 136 -392" "angle" "225" "classname" "info_player_deathmatch" } { "origin" "464 -240 -864" "angle" "45" "classname" "info_player_deathmatch" } { "origin" "546 222 -884" "angle" "270" "classname" "info_player_deathmatch" } { "spawnflags" "0" "classname" "ammo_rockets" "origin" "120 16 -808" } { "spawnflags" "2048" "classname" "item_armor_jacket" "origin" "168 16 -808" } { "origin" "-344 32 -816" "classname" "item_health" } { "origin" "-288 -416 -832" "angle" "90" "spawnflags" "2050" "classname" "misc_deadsoldier" } { "origin" "136 -736 -574" "classname" "item_armor_shard" "spawnflags" "1792" } { "spawnflags" "2048" "origin" "-464 -798 -328" "targetname" "t156" "classname" "target_secret" } { "model" "*66" "spawnflags" "2048" "message" "You have found a secret area." "target" "t156" "classname" "trigger_once" } { "model" "*67" "spawnflags" "2055" "classname" "func_wall" "targetname" "t181" } { "origin" "-60 -792 -368" "targetname" "t154" "classname" "point_combat" } { "targetname" "t155" "origin" "-64 -708 -362" "angle" "270" "target" "t154" "classname" "monster_soldier_light" "spawnflags" "1" } { "classname" "ammo_cells" "origin" "-452 -720 -702" } { "origin" "-408 -954 -640" "classname" "ammo_rockets" "spawnflags" "3584" } { "origin" "-504 -724 -352" "classname" "ammo_bullets" "spawnflags" "2048" } { "origin" "-508 -828 -352" "classname" "item_health" "spawnflags" "2048" } { "origin" "-472 -828 -352" "classname" "item_health" "spawnflags" "1024" } { "origin" "-508 -764 -352" "classname" "ammo_shells" "spawnflags" "0" } { "origin" "-508 -868 -352" "classname" "item_health_large" "spawnflags" "2048" } { "origin" "-472 -868 -352" "classname" "item_health" "spawnflags" "0" } { "origin" "-548 -868 -288" "classname" "ammo_rockets" "spawnflags" "2048" } { "origin" "-548 -796 -288" "classname" "ammo_cells" "spawnflags" "0" } { "origin" "-548 -760 -288" "classname" "ammo_slugs" "spawnflags" "2048" } { "origin" "-548 -724 -288" "classname" "ammo_rockets" "spawnflags" "2048" } { "origin" "-548 -760 -352" "classname" "ammo_grenades" "spawnflags" "2048" } { "origin" "-548 -832 -352" "classname" "ammo_bullets" "spawnflags" "2048" } { "origin" "-548 -868 -352" "classname" "ammo_bullets" "spawnflags" "2048" } { "origin" "-132 -864 -352" "classname" "ammo_rockets" "team" "ammo1" } { "origin" "-48 -864 -304" "classname" "ammo_bullets" "spawnflags" "1792" "team" "ammo1" } { "origin" "-48 -828 -352" "classname" "item_health" "spawnflags" "2048" } { "spawnflags" "2048" "origin" "-320 -1384 -1064" "targetname" "t153" "classname" "target_secret" } { "spawnflags" "2048" "origin" "-280 -1408 -1064" "target" "t153" "targetname" "t152" "message" "You have found a secret area." "classname" "trigger_relay" } { "spawnflags" "2048" "origin" "208 -32 -808" "targetname" "t149" "classname" "target_secret" } { "model" "*68" "spawnflags" "2048" "message" "You have found a secret area." "target" "t149" "classname" "trigger_once" } { "target" "t280" "targetname" "t281" "classname" "monster_berserk" "spawnflags" "769" "angle" "270" "origin" "220 -752 -612" } { "spawnflags" "1792" "classname" "ammo_bullets" "origin" "376 -1184 -652" } { "classname" "monster_berserk" "angle" "270" "origin" "16 -712 -612" "spawnflags" "1" } { "classname" "monster_berserk" "angle" "270" "origin" "-144 -712 -612" "spawnflags" "257" } { "classname" "misc_deadsoldier" "spawnflags" "2" "angle" "135" "origin" "-112 -1568 -832" } { "targetname" "city2NH" "angle" "90" "origin" "-64 -1862 -800" "classname" "info_player_start" } { "classname" "target_crosslevel_trigger" "spawnflags" "2049" "targetname" "rfld" "origin" "-116 -872 -809" } { "target" "t217" "spawnflags" "256" "origin" "-448 -1184 -936" "angle" "270" "classname" "monster_soldier_ss" } { "origin" "-480 -1248 -936" "angle" "225" "classname" "monster_soldier_ss" } { "killtarget" "redfieldbutton" "origin" "-155 -795 -822" "targetname" "rfld" "classname" "trigger_relay" "spawnflags" "2048" } { "model" "*69" "spawnflags" "2048" "targetname" "pylon" "classname" "func_wall" } { "origin" "-696 -1252 -776" "target" "t137" "targetname" "t136" "classname" "trigger_relay" } { "angle" "0" "spawnflags" "1" "origin" "-492 -1204 -680" "target" "t70" "classname" "monster_soldier_ss" "item" "ammo_bullets" } { "origin" "-64 -1864 -590" "classname" "light" "light" "100" } { "_cone" "15" "origin" "-64 -1864 -598" "target" "t133" "classname" "light" "light" "300" } { "origin" "-64 -1864 -808" "targetname" "t133" "classname" "info_notnull" } { "_color" "1.000000 0.796078 0.003922" "origin" "-8 -1480 -590" "classname" "light" "light" "48" } { "_cone" "15" "origin" "-64 -1784 -598" "target" "t134" "classname" "light" "light" "300" } { "origin" "-64 -1784 -808" "targetname" "t134" "classname" "info_notnull" } { "origin" "-64 -1976 -808" "targetname" "t132" "classname" "info_notnull" } { "origin" "-64 -1976 -590" "light" "100" "classname" "light" } { "_cone" "15" "origin" "-64 -1976 -598" "light" "300" "target" "t132" "classname" "light" } { "origin" "192 -216 -616" "angle" "270" "spawnflags" "259" "targetname" "t295" "classname" "monster_floater" } { "origin" "-16 -1104 -632" "targetname" "t57" "spawnflags" "3" "classname" "monster_floater" } { "spawnflags" "2048" "origin" "-288 64 -672" "classname" "item_health" } { "spawnflags" "2048" "origin" "-248 64 -672" "classname" "item_health" } { "origin" "-328 64 -664" "classname" "ammo_rockets" "spawnflags" "0" } { "origin" "632 -170 -824" "classname" "item_armor_jacket" "spawnflags" "1792" } { "origin" "592 -232 -872" "classname" "item_health" } { "spawnflags" "2048" "origin" "192 -48 -808" "classname" "item_health_mega" } { "spawnflags" "1792" "origin" "152 -32 -776" "classname" "weapon_rocketlauncher" } { "item" "ammo_bullets" "origin" "464 -8 -856" "angle" "45" "classname" "monster_soldier_ss" "deathtarget" "brainguy" } { "light" "64" "classname" "light" "origin" "-320 -804 -396" } { "light" "80" "classname" "light" "origin" "-316 -792 -524" } { "light" "64" "classname" "light" "origin" "-632 -832 -772" } { "light" "64" "classname" "light" "origin" "-632 -832 -836" } { "classname" "light" "light" "64" "origin" "-632 -832 -900" } { "classname" "info_null" "targetname" "t130" "origin" "-72 -832 -360" } { "style" "34" "classname" "light" "target" "t130" "light" "350" "_color" "1.000000 0.945098 0.643137" "origin" "-72 -832 -238" "spawnflags" "1" "targetname" "t155" "_cone" "25" } { "light" "100" "classname" "light" "origin" "-127 -798 -515" } { "light" "100" "classname" "light" "origin" "-81 -787 -532" } { "classname" "light" "light" "100" "origin" "-43 -793 -511" } { "origin" "-64 -1880 -982" "_color" "1.000000 0.920900 0.343800" "light" "90" "classname" "light" } { "classname" "light" "light" "200" "_color" "1.000000 0.920900 0.343800" "origin" "-64 -1880 -973" "target" "t129" "_cone" "20" } { "origin" "-64 -1872 -1080" "classname" "info_notnull" "targetname" "t129" } { "classname" "light" "light" "200" "_color" "1.000000 0.920900 0.343800" "origin" "-64 -2032 -976" "target" "t128" "_cone" "20" } { "classname" "info_notnull" "origin" "-64 -2032 -1080" "targetname" "t128" } { "classname" "light" "light" "90" "_color" "1.000000 0.920900 0.343800" "origin" "-64 -2032 -974" } { "classname" "info_notnull" "targetname" "t127" "origin" "-64 -1808 -1080" } { "target" "t264" "spawnflags" "1" "item" "item_armor_shard" "classname" "monster_brain" "angle" "270" "origin" "194 -222 -804" } { "spawnflags" "1" "item" "Ammo_cells" "classname" "monster_brain" "angle" "225" "origin" "238 -184 -804" } { "classname" "info_notnull" "targetname" "t126" "origin" "-64 -1096 -620" } { "classname" "trigger_relay" "delay" "0.1" "targetname" "py1" "angle" "45" "killtarget" "support_hose" "origin" "-68 -1174 -632" "target" "supporthose" } { "sounds" "2" "origin" "-64 -1096 -748" "angle" "-2" "classname" "target_splash" "targetname" "py1" } { "sounds" "2" "origin" "-64 -1096 -684" "classname" "target_splash" "angle" "225" "targetname" "py1" } { "sounds" "2" "origin" "-64 -1096 -638" "classname" "target_splash" "angle" "-1" "targetname" "py1" } { "model" "*70" "classname" "func_door" "angle" "0" "Team" "door1" "lip" "8" "speed" "50" "sounds" "4" } { "targetname" "rfld" "killtarget" "ran3" "origin" "-242 -826 -924" "classname" "trigger_relay" "spawnflags" "2048" } { "target" "t287" "targetname" "ran4" "random" ".8" "wait" ".95" "spawnflags" "2049" "classname" "func_timer" "origin" "-229 -901 -924" } { "targetname" "rfld" "classname" "trigger_relay" "origin" "-270 -820 -924" "killtarget" "ran2" "spawnflags" "2048" } { "targetname" "rfld" "classname" "trigger_relay" "origin" "-217 -826 -924" "killtarget" "ran4" } { "targetname" "rfld" "killtarget" "ran1" "origin" "-288 -787 -924" "classname" "trigger_relay" "spawnflags" "2048" } { "origin" "-262 -797 -924" "targetname" "ran2" "target" "t124" "random" ".8" "wait" "1" "spawnflags" "2049" "classname" "func_timer" } { "origin" "-238 -804 -924" "targetname" "ran3" "target" "t125" "classname" "func_timer" "spawnflags" "2049" "wait" "3" "random" "2.333333333" } { "origin" "-266 -764 -924" "targetname" "ran1" "target" "t118" "random" ".9" "wait" "4" "spawnflags" "2049" "classname" "func_timer" } { "targetname" "t121" "noise" "world/spark1.wav" "classname" "target_speaker" "origin" "-106 -812 -883" "spawnflags" "2048" } { "targetname" "t123" "noise" "world/spark5.wav" "classname" "target_speaker" "origin" "-110 -791 -896" "spawnflags" "2048" } { "targetname" "t122" "origin" "-102 -771 -896" "classname" "target_speaker" "noise" "world/spark1.wav" "spawnflags" "2048" } { "targetname" "t125" "origin" "-232 -771 -941" "target" "t123" "classname" "trigger_relay" } { "targetname" "t124" "origin" "-221 -761 -940" "target" "t122" "classname" "trigger_relay" } { "targetname" "t118" "origin" "-241 -763 -940" "target" "t121" "classname" "trigger_relay" "spawnflags" "2048" } { "origin" "-148 -835 -864" "_color" "1.000000 0.853175 0.011905" "light" "100" "classname" "light" } { "targetname" "rfld" "classname" "trigger_relay" "origin" "-177 -798 -924" "killtarget" "t119" } { "model" "*71" "_minlight" "0.9" "targetname" "rfld" "delay" ".25" "spawnflags" "2054" "classname" "func_wall" } { "model" "*72" "_minlight" "0.8" "targetname" "rfld" "spawnflags" "2049" "classname" "func_wall" } { "targetname" "t287" "origin" "-100 -826 -847" "target" "t119" "classname" "trigger_relay" "spawnflags" "2048" } { "style" "35" "light" "200" "targetname" "t119" "origin" "-142 -744 -896" "_color" "1.000000 0.391304 0.185771" "classname" "light" } { "targetname" "t122" "origin" "-154 -754 -938" "classname" "target_splash" "angle" "225" "sounds" "1" } { "targetname" "t121" "origin" "-130 -748 -924" "classname" "target_splash" "angle" "-1" "sounds" "1" } { "targetname" "t123" "origin" "-148 -754 -938" "sounds" "1" "angle" "315" "classname" "target_splash" "spawnflags" "2048" } { "model" "*73" "message" "Red force fields deactivated." "delay" ".5" "target" "rfld" "targetname" "redfieldbutton" "classname" "trigger_once" "spawnflags" "2048" } { "origin" "-256 254 -460" "angle" "180" "classname" "monster_tank_commander" "item" "ammo_bullets" "spawnflags" "1" } { "model" "*74" "spawnflags" "2048" "target" "t116" "classname" "trigger_once" } { "targetname" "t116" "origin" "-384 112 -440" "angle" "90" "classname" "monster_berserk" } { "origin" "-488 472 -488" "targetname" "t112" "classname" "point_combat" "spawnflags" "2048" } { "spawnflags" "256" "origin" "-368 352 -472" "classname" "monster_soldier_ss" "angle" "180" } { "item" "ammo_bullets" "origin" "-416 384 -472" "target" "t112" "classname" "monster_soldier_ss" "angle" "180" } { "origin" "-288 432 -472" "angle" "180" "classname" "monster_soldier_ss" } { "origin" "-316 -792 -588" "classname" "light" "light" "80" } { "origin" "-320 -804 -332" "classname" "light" "light" "64" } { "origin" "-316 -792 -652" "light" "80" "classname" "light" } { "origin" "-628 -884 -808" "_color" "1.000000 0.172549 0.023529" "light" "64" "classname" "light" } { "origin" "-464 -1072 -680" "angle" "90" "classname" "monster_berserk" "item" "item_armor_shard" "spawnflags" "1" "targetname" "t250" } { "style" "36" "targetname" "t194" "target" "t111" "_color" "1.000000 0.996078 0.768627" "classname" "light" "_cone" "20" "origin" "-632 -832 -600" "light" "450" "spawnflags" "1" } { "targetname" "t111" "classname" "info_notnull" "origin" "-633 -832 -936" } { "origin" "-281 -832 -712" "targetname" "t106" "classname" "info_notnull" } { "style" "37" "targetname" "t108" "spawnflags" "1" "light" "900" "origin" "-280 -832 -248" "_cone" "20" "target" "t106" "classname" "light" } { "model" "*75" "spawnflags" "2048" "classname" "func_wall" } { "model" "*76" "spawnflags" "2056" "lip" "8" "wait" "-1" "angle" "-1" "classname" "func_door" } { "model" "*77" "target" "t238" "lip" "8" "spawnflags" "8" "angle" "-1" "classname" "func_door" } { "model" "*78" "target" "sec2" "classname" "trigger_multiple" } { "model" "*79" "wait" "4" "lip" "64" "_minlight" "0.1" "targetname" "sec2" "speed" "100" "angle" "-1" "classname" "func_door" } { "target" "t102" "classname" "light" "_cone" "10" "light" "200" "origin" "0 -712 -486" } { "target" "t103" "light" "200" "_cone" "10" "classname" "light" "origin" "-48 -712 -486" } { "target" "t104" "light" "200" "_cone" "10" "classname" "light" "origin" "-80 -712 -486" } { "target" "t105" "light" "200" "_cone" "10" "classname" "light" "origin" "-128 -712 -486" } { "targetname" "t105" "classname" "info_notnull" "origin" "-128 -712 -636" } { "targetname" "t104" "classname" "info_notnull" "origin" "-80 -712 -636" } { "targetname" "t103" "classname" "info_notnull" "origin" "-48 -712 -636" } { "targetname" "t102" "classname" "info_notnull" "origin" "-4 -712 -636" } { "classname" "func_group" } { "model" "*80" "target" "t100" "classname" "trigger_once" "spawnflags" "2048" } { "model" "*81" "spawnflags" "2304" "target" "t82" "targetname" "t81" "classname" "trigger_once" } { "targetname" "t97" "origin" "-48 -632 -636" "classname" "info_null" } { "target" "t97" "origin" "-48 -632 -486" "classname" "light" "_cone" "10" "light" "200" } { "targetname" "t98" "origin" "-80 -632 -636" "classname" "info_null" } { "target" "t98" "origin" "-80 -632 -486" "classname" "light" "_cone" "10" "light" "200" } { "targetname" "t99" "origin" "-128 -632 -636" "classname" "info_null" } { "target" "t99" "origin" "-128 -632 -486" "classname" "light" "_cone" "10" "light" "200" } { "origin" "-4 -632 -636" "targetname" "t96" "classname" "info_null" } { "origin" "-4 -632 -486" "target" "t96" "light" "200" "_cone" "10" "classname" "light" } { "target" "t95" "classname" "light" "light" "300" "_cone" "20" "origin" "84 -384 -504" } { "targetname" "t95" "classname" "info_null" "origin" "84 -384 -664" } { "target" "t94" "_cone" "20" "light" "300" "classname" "light" "origin" "-216 -384 -504" } { "targetname" "t94" "classname" "info_null" "origin" "-216 -384 -664" } { "targetname" "t93" "classname" "info_null" "origin" "-216 -576 -664" } { "target" "t93" "_cone" "20" "light" "300" "classname" "light" "origin" "-216 -576 -504" } { "targetname" "t92" "classname" "info_null" "origin" "88 -576 -664" } { "target" "t92" "classname" "light" "light" "300" "_cone" "20" "origin" "88 -576 -504" } { "targetname" "t91" "origin" "-216 -192 -664" "classname" "info_null" } { "target" "t91" "origin" "-216 -192 -504" "classname" "light" "light" "300" "_cone" "20" } { "origin" "84 -192 -664" "targetname" "t90" "classname" "info_null" } { "origin" "84 -192 -504" "target" "t90" "_cone" "20" "light" "300" "classname" "light" } { "origin" "-320 -292 -668" "targetname" "t86" "target" "t85" "classname" "path_corner" } { "origin" "212 -592 -700" "targetname" "t84" "target" "t83" "classname" "path_corner" } { "killtarget" "trap_gun" "spawnflags" "2304" "origin" "-924 -16 -564" "targetname" "t82" "classname" "trigger_relay" } { "origin" "-936 456 -492" "target" "t1" "targetname" "t80" "classname" "trigger_relay" "spawnflags" "2048" } { "spawnflags" "2048" "targetname" "t100" "origin" "-408 120 -408" "message" "You have found a secret passage." "classname" "target_secret" } { "spawnflags" "2304" "targetname" "t82" "origin" "-928 32 -560" "message" "You have found a secret.\nGun trap is deactivated." "classname" "target_secret" } { "item" "item_armor_shard" "targetname" "t89" "origin" "195 -34 -673" "spawnflags" "1" "classname" "monster_berserk" } { "model" "*82" "spawnflags" "2048" "target" "t89" "classname" "trigger_once" } { "item" "item_armor_shard" "targetname" "t89" "origin" "-717 -77 -576" "spawnflags" "2" "angle" "270" "classname" "monster_berserk" } { "model" "*83" "target" "t81" "_minlight" "0.1" "classname" "func_button" "angle" "180" "health" "1" "lip" "4" "spawnflags" "2304" } { "_color" "1.000000 0.976471 0.501961" "origin" "-28 -156 -512" "light" "100" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-100 -156 -512" "light" "100" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-28 -284 -512" "light" "100" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-100 -284 -512" "light" "100" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "origin" "-28 -476 -512" "light" "100" "classname" "light" } { "_color" "1.000000 0.976471 0.501961" "classname" "light" "light" "100" "origin" "-100 -476 -512" } { "model" "*84" "spawnflags" "8" "target" "t79" "classname" "func_door" "angle" "180" "_minlight" "0.1" "team" "sdr1" "health" "80" } { "model" "*85" "angle" "0" "classname" "func_door" "team" "bigredx" "speed" "50" "sounds" "4" "wait" "4" "_minlight" "0.2" } { "model" "*86" "classname" "func_door" "angle" "180" "team" "bigredx" "speed" "50" "sounds" "4" "wait" "4" "_minlight" "0.2" } { "origin" "-924 532 -532" "light" "100" "classname" "light" "spawnflags" "1792" } { "model" "*87" "targetname" "t279" "target" "bigredoor" "spawnflags" "4" "classname" "trigger_once" } { "model" "*88" "spawnflags" "2080" "message" "This door is opened elsewhere." "sounds" "4" "_minlight" "0.3" "lip" "32" "team" "bigred" "targetname" "bigredoor" "speed" "25" "angle" "180" "classname" "func_door" } { "model" "*89" "spawnflags" "2080" "message" "This door is opened elsewhere." "sounds" "2" "_minlight" "0.3" "lip" "32" "team" "bigred" "speed" "25" "targetname" "bigredoor" "angle" "0" "classname" "func_door" } { "model" "*90" "spawnflags" "2080" "message" "This door is opened elsewhere." "sounds" "2" "_minlight" "0.3" "lip" "8" "targetname" "bigredoor" "speed" "25" "team" "bigred" "angle" "180" "classname" "func_door" } { "model" "*91" "spawnflags" "2080" "message" "This door is opened elsewhere." "sounds" "2" "_minlight" "0.3" "lip" "8" "targetname" "bigredoor" "team" "bigred" "speed" "25" "angle" "0" "classname" "func_door" } { "dmg" "200" "health" "50" "classname" "misc_explobox" "origin" "-36 -1824 -936" "spawnflags" "2048" } { "model" "*92" "spawnflags" "2048" "classname" "func_explosive" "target" "rly2" "health" "5" "dmg" "50" "mass" "100" "_minlight" "0.6" } { "model" "*93" "wait" "-1" "_minlight" "0.2" "angle" "180" "classname" "func_door" "spawnflags" "2048" "team" "Greendoor" "targetname" "yfld" "sounds" "3" "message" "This door is opened elsewhere." } { "model" "*94" "wait" "-1" "_minlight" "0.2" "classname" "func_door" "angle" "0" "spawnflags" "2048" "message" "This door is opened elsewhere." "team" "Greendoor" "targetname" "yfld" } { "classname" "info_notnull" "targetname" "t50" "origin" "-64 -224 -812" } { "style" "38" "targetname" "t185" "classname" "light" "light" "400" "target" "t50" "spawnflags" "1" "_color" "1.000000 0.894118 0.290196" "origin" "-64 -224 -680" "_cone" "25" } { "target" "t57" "item" "ammo_cells" "targetname" "t56" "origin" "-80 124 -334" "classname" "monster_floater" "angle" "270" "spawnflags" "1" } { "model" "*95" "sounds" "4" "lip" "3" "angle" "270" "classname" "func_button" "_minlight" "0.6" "target" "sd2" } { "target" "t152" "classname" "item_power_shield" "origin" "-288 -1368 -1056" "spawnflags" "0" } { "delay" "0" "classname" "trigger_relay" "targetname" "t46" "target" "t47" "origin" "-392 -1400 -1064" "spawnflags" "2048" } { "classname" "trigger_relay" "delay" "0.2" "targetname" "t46" "target" "t47" "origin" "-392 -1440 -1064" "spawnflags" "2048" } { "style" "39" "classname" "light" "light" "1000" "_color" "1.000000 0.873016 0.154762" "origin" "-393 -1424 -1064" "spawnflags" "2049" "targetname" "t47" } { "classname" "target_splash" "angle" "180" "sounds" "5" "origin" "-382 -1424 -1064" "targetname" "t46" } { "model" "*96" "classname" "trigger_multiple" "wait" "3" "delay" ".5" "target" "t46" "spawnflags" "2048" } { "classname" "target_blaster" "dmg" "15" "angle" "180" "origin" "-388 -1424 -1064" "speed" "1000" "targetname" "t46" "spawnflags" "3584" } { "classname" "point_combat" "targetname" "t44" "origin" "-472 -1000 -952" } { "classname" "monster_brain" "angle" "225" "origin" "-68 -118 -742" } { "dmg" "10000" "origin" "432 -218 -808" "classname" "target_laser" "angle" "90" "spawnflags" "2053" "targetname" "yfld" } { "sounds" "5" "dmg" "10000" "origin" "432 -218 -840" "classname" "target_laser" "spawnflags" "2053" "angle" "90" "targetname" "yfld" } { "model" "*97" "spawnflags" "2048" "target" "spg" "classname" "func_button" "angle" "90" "lip" "6" "speed" "50" "wait" "-1" "sounds" "5" } { "classname" "light" "light" "135" "origin" "-64 -232 -896" } { "target" "t148" "classname" "key_data_spinner" "origin" "-64 -224 -876" "spawnflags" "2048" } { "angle" "90" "classname" "target_splash" "targetname" "py1" "origin" "-64 -1096 -716" "sounds" "2" } { "angle" "315" "classname" "target_splash" "targetname" "py1" "origin" "-64 -1096 -658" "sounds" "2" } { "spawnflags" "2048" "origin" "-20 -1124 -612" "killtarget" "pylon" "classname" "trigger_relay" "targetname" "py1" } { "model" "*98" "spawnflags" "2048" "classname" "func_wall" "_minlight" "0.9" "targetname" "pylon" } { "classname" "light" "light" "200" "_color" "0.145098 0.466667 1.000000" "origin" "-64 -1095 -783" } { "origin" "-408 8 -376" "light" "100" "classname" "light" } { "model" "*99" "spawnflags" "8" "target" "t79" "_minlight" "0.9" "angle" "180" "team" "sdr1" "classname" "func_door" "health" "2" } { "model" "*100" "team" "condoor" "classname" "func_door" "angle" "90" "speed" "100" "sounds" "2" "lip" "8" "wait" "4" "_minlight" "0.1" } { "deathtarget" "t57" "target" "t261" "origin" "-72 -352 -416" "spawnflags" "1" "angle" "270" "classname" "monster_floater" } { "deathtarget" "t57" "origin" "210 -576 -630" "spawnflags" "1" "angle" "180" "classname" "monster_floater" } { "spawnflags" "1792" "origin" "41 -1067 -904" "classname" "ammo_slugs" } { "origin" "-161 -1701 -816" "classname" "ammo_grenades" "spawnflags" "2048" } { "origin" "43 -1501 -816" "classname" "ammo_rockets" } { "targetname" "t281" "spawnflags" "1" "origin" "124 -796 -616" "classname" "monster_tank_commander" "angle" "360" "item" "ammo_rockets" "target" "t289" } { "targetname" "t70" "spawnflags" "1" "origin" "-839 -1609 -860" "angle" "90" "classname" "monster_tank_commander" "item" "ammo_rockets" } { "model" "*101" "dmg" "25" "targetname" "f1" "mass" "300" "classname" "func_explosive" "spawnflags" "2048" } { "style" "40" "spawnflags" "2048" "_cone" "20" "classname" "light" "light" "250" "targetname" "pylon" "origin" "-64 -1096 -508" "target" "t126" } { "spawnflags" "1024" "classname" "item_health_small" "origin" "42 -1689 -934" } { "model" "*102" "spawnflags" "2048" "dmg" "20" "health" "200" "classname" "func_explosive" } { "spawnflags" "2048" "classname" "misc_explobox" "health" "15" "dmg" "55" "origin" "336 -764 -944" } { "spawnflags" "1" "classname" "monster_floater" "targetname" "t17" "origin" "-72 -654 -892" } { "targetname" "t16" "classname" "target_laser" "origin" "224 -1240 -956" "spawnflags" "2057" "angle" "-1" "dmg" "25" } { "targetname" "t16" "angle" "-1" "classname" "target_laser" "origin" "224 -1192 -956" "spawnflags" "2057" "dmg" "25" } { "targetname" "t16" "spawnflags" "2056" "angle" "90" "origin" "224 -1280 -920" "classname" "target_laser" "dmg" "45" } { "targetname" "t16" "spawnflags" "2056" "origin" "224 -1280 -872" "classname" "target_laser" "angle" "90" "dmg" "45" } { "spawnflags" "2048" "origin" "336 -902 -944" "dmg" "55" "health" "15" "classname" "misc_explobox" } { "target" "t15" "origin" "-464 -612 -930" "targetname" "t13" "classname" "trigger_relay" "spawnflags" "2048" } { "delay" ".5" "targetname" "t15" "origin" "96 -652 -872" "classname" "target_explosion" "spawnflags" "2048" } { "targetname" "t15" "delay" "1.25" "origin" "96 -652 -896" "classname" "target_explosion" "spawnflags" "2048" } { "origin" "96 -656 -1048" "light" "150" "classname" "light" } { "origin" "-488 -656 -936" "targetname" "t13" "delay" "0.5" "target" "f1" "classname" "trigger_relay" "spawnflags" "2048" } { "origin" "-472 -632 -944" "delay" "2" "target" "t14" "targetname" "t13" "classname" "trigger_relay" "spawnflags" "2048" } { "model" "*103" "target" "t13" "classname" "trigger_once" "spawnflags" "2048" } { "origin" "88 -656 -1056" "classname" "item_health_large" "spawnflags" "2048" } { "item" "ammo_cells" "angle" "180" "origin" "56 -648 -984" "targetname" "t14" "spawnflags" "2" "classname" "monster_floater" } { "origin" "120 -664 -984" "targetname" "t14" "spawnflags" "258" "classname" "monster_floater" } { "model" "*104" "lip" "16" "targetname" "t14" "classname" "func_door" "angle" "-2" "spawnflags" "1" "speed" "75" "wait" "-1" } { "origin" "-416 -624 -936" "targetname" "t12" "classname" "point_combat" "spawnflags" "2048" } { "origin" "-310 -852 -950" "targetname" "t11" "classname" "point_combat" "spawnflags" "2048" } { "origin" "-462 -674 -938" "targetname" "t10" "classname" "point_combat" "spawnflags" "2048" } { "spawnflags" "769" "origin" "-215 -802 -930" "angle" "270" "classname" "monster_brain" "target" "t11" } { "origin" "-72 -1804 -912" "classname" "item_health" "spawnflags" "2048" } { "origin" "-112 -1800 -904" "classname" "ammo_rockets" "spawnflags" "2048" } { "origin" "-48 -1752 -936" "targetname" "t8" "classname" "point_combat" "spawnflags" "1" } { "origin" "-80 -1752 -936" "targetname" "t9" "classname" "point_combat" "spawnflags" "1" "killtarget" "t9" } { "model" "*105" "classname" "trigger_multiple" "spawnflags" "2048" "wait" "15" "target" "t302" "targetname" "reminder" } { "spawnflags" "1" "targetname" "t28" "target" "t8" "origin" "-36 -1768 -920" "classname" "monster_soldier_ss" "angle" "90" } { "spawnflags" "1" "targetname" "t28" "target" "t9" "origin" "-76 -1768 -920" "angle" "90" "classname" "monster_soldier_ss" } { "target" "t17" "spawnflags" "1" "targetname" "t5" "origin" "36 -1748 -920" "classname" "monster_berserk" "angle" "90" } { "spawnflags" "768" "targetname" "t6" "origin" "-120 -1752 -920" "angle" "315" "classname" "monster_berserk" } { "model" "*106" "spawnflags" "2054" "classname" "func_wall" "targetname" "redforfld" } { "origin" "-56 -96 -760" "classname" "light" "light" "32" "_color" "0.215686 0.784314 1.000000" } { "origin" "-72 -96 -760" "classname" "light" "light" "32" "_color" "0.215686 0.784314 1.000000" } { "origin" "-104 -112 -760" "classname" "light" "light" "64" "_color" "1.000000 0.976471 0.501961" } { "origin" "-168 -112 -632" "classname" "light" "light" "32" "_color" "0.215686 0.784314 1.000000" } { "origin" "-160 -128 -712" "_color" "0.220472 0.590551 1.000000" "light" "64" "classname" "light" } { "origin" "40 -136 -772" "classname" "light" "light" "64" "_color" "0.220472 0.590551 1.000000" } { "origin" "-168 -136 -760" "classname" "light" "light" "64" "_color" "0.215686 0.784314 1.000000" } { "origin" "32 -112 -632" "_color" "0.215686 0.784314 1.000000" "light" "32" "classname" "light" } { "origin" "32 -136 -696" "classname" "light" "light" "64" "_color" "1.000000 0.976471 0.501961" } { "origin" "-24 -112 -760" "_color" "1.000000 0.976471 0.501961" "light" "64" "classname" "light" } { "model" "*107" "spawnflags" "2048" "health" "5" "_minlight" "0.6" "classname" "func_explosive" "target" "t3" "dmg" "50" } { "origin" "-107 -1331 -538" "classname" "trigger_relay" "targetname" "t3" "target" "cntr1" } { "spawnflags" "2048" "classname" "trigger_relay" "targetname" "rly2" "target" "cntr1" "origin" "-109 -1025 -526" } { "model" "*108" "spawnflags" "2048" "classname" "trigger_counter" "count" "2" "targetname" "cntr1" "target" "py1" } { "light" "150" "classname" "light" "origin" "-368 -648 -828" "_color" "1.000000 0.875537 0.004292" } { "spawnflags" "2048" "target" "pylon" "classname" "item_quad" "origin" "-64 -1096 -562" } { "classname" "light" "light" "100" "origin" "-100 -284 -576" "_color" "1.000000 0.976471 0.501961" } { "origin" "-152 -92 -368" "light" "125" "classname" "light" "_color" "1.000000 0.603922 0.533333" } { "classname" "light" "light" "85" "origin" "-136 -92 -304" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "100" "origin" "-100 -156 -576" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "100" "origin" "-28 -156 -576" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "100" "origin" "-28 -284 -576" "_color" "1.000000 0.976471 0.501961" } { "origin" "-40 -100 -368" "light" "125" "classname" "light" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "100" "origin" "-28 -476 -576" "_color" "1.000000 0.976471 0.501961" } { "origin" "36 -476 -368" "light" "125" "classname" "light" "_color" "1.000000 0.976471 0.501961" } { "classname" "light" "light" "125" "origin" "-164 -476 -368" "_color" "1.000000 0.603922 0.533333" } { "origin" "-100 -476 -576" "light" "100" "classname" "light" "_color" "1.000000 0.976471 0.501961" } { "origin" "8 -92 -304" "light" "85" "classname" "light" "_color" "1.000000 0.976471 0.501961" } { "classname" "trigger_relay" "target" "sdlight" "targetname" "sd2" "delay" "8.5" "origin" "246 -20 -752" } { "classname" "trigger_relay" "targetname" "sd2" "target" "sdlight" "origin" "228 24 -752" } { "model" "*109" "spawnflags" "2048" "sounds" "3" "classname" "func_button" "angle" "270" "target" "sd2" } { "model" "*110" "classname" "func_door" "angle" "-2" "speed" "75" "spawnflags" "12" "targetname" "sd2" "sounds" "4" "wait" "10" "_minlight" "0.1" } { "light" "175" "classname" "light" "origin" "544 -145 -744" } { "light" "100" "classname" "light" "origin" "544 -49 -744" } { "light" "100" "classname" "light" "origin" "544 23 -744" } { "light" "100" "classname" "light" "origin" "544 119 -744" } { "classname" "light" "light" "100" "origin" "544 231 -744" } { "classname" "light" "light" "100" "origin" "544 -241 -744" } { "light" "32" "classname" "light" "origin" "232 -424 -736" "_color" "1.000000 0.948819 0.755906" } { "_color" "1.000000 0.948819 0.755906" "origin" "168 -424 -736" "classname" "light" "light" "32" } { "model" "*111" "target" "t80" "_minlight" "0.6" "classname" "func_button" "angle" "90" "lip" "8" "speed" "50" "wait" "2" "health" "1" "spawnflags" "2304" } { "model" "*112" "classname" "func_door" "angle" "-2" "spawnflags" "2057" "wait" "4" "speed" "150" "targetname" "t1" } { "_color" "1.000000 0.875537 0.004292" "origin" "-448 -1312 -776" "classname" "light" "light" "150" } { "light" "150" "classname" "light" "origin" "-448 -1312 -824" "_color" "1.000000 0.875537 0.004292" } { "light" "150" "classname" "light" "origin" "-448 -1120 -824" "_color" "1.000000 0.875537 0.004292" } { "light" "150" "classname" "light" "origin" "-448 -1120 -776" "_color" "1.000000 0.875537 0.004292" } { "origin" "-376 -800 -822" "classname" "light" "light" "150" "_color" "1.000000 0.881423 0.217391" } { "origin" "-376 -864 -822" "_color" "1.000000 0.881423 0.217391" "light" "150" "classname" "light" } { "light" "100" "classname" "light" "_color" "1.000000 0.920949 0.343874" "origin" "-90 -1566 -938" } { "origin" "-28 -1566 -938" "_color" "1.000000 0.920949 0.343874" "classname" "light" "light" "100" } { "light" "80" "classname" "light" "_color" "1.000000 0.920949 0.343874" "origin" "-80 -1632 -938" } { "origin" "-48 -1632 -938" "_color" "1.000000 0.920949 0.343874" "classname" "light" "light" "80" } { "light" "80" "classname" "light" "_color" "1.000000 0.920949 0.343874" "origin" "-28 -1676 -938" } { "light" "120" "classname" "light" "_color" "1.000000 0.920949 0.343874" "origin" "-64 -1632 -874" } { "model" "*113" "team" "condoor" "_minlight" "0.1" "wait" "4" "lip" "8" "sounds" "2" "speed" "100" "angle" "270" "classname" "func_door" } { "model" "*114" "classname" "func_door" "angle" "-1" "team" "pal1" "_minlight" "0.2" } { "model" "*115" "classname" "func_door" "angle" "0" "team" "pal1" "sounds" "3" "_minlight" "0.1" "target" "t212" } { "model" "*116" "classname" "func_door" "angle" "180" "team" "pal1" "_minlight" "0.2" } { "classname" "info_player_start" "angle" "270" "targetname" "city2XH" "origin" "-65 592 -80" } { "classname" "target_changelevel" "targetname" "next_high" "map" "city3$city3NH" "origin" "-80 632 48" } { "model" "*117" "angle" "90" "classname" "trigger_multiple" "target" "next_high" } { "classname" "info_player_start" "angle" "0" "targetname" "city2XL" "origin" "356 176 -872" } { "classname" "target_changelevel" "targetname" "next_low" "map" "city3$city3NL" "origin" "224 144 -840" } { "model" "*118" "angle" "180" "classname" "trigger_multiple" "target" "next_low" } { "angle" "90" "origin" "-64 -1888 -1056" "classname" "info_player_start" "targetname" "city2NL" } { "classname" "target_changelevel" "targetname" "back_low" "map" "city1$city1XL" "origin" "-104 -1944 -1064" } { "model" "*119" "classname" "trigger_multiple" "target" "back_low" "angle" "270" } { "classname" "target_changelevel" "targetname" "back_high" "map" "city1$city1XH" "origin" "-24 -1944 -760" } { "model" "*120" "classname" "trigger_multiple" "target" "back_high" "angle" "270" } { "classname" "light" "light" "100" "origin" "16 -1888 -744" "_color" "1.000000 0.733333 0.733333" } { "_color" "1.000000 0.733333 0.733333" "origin" "16 -1952 -744" "light" "100" "classname" "light" } { "_color" "1.000000 0.255906 0.003937" "classname" "light" "origin" "-168 -1536 -728" "light" "100" } { "_color" "1.000000 0.255906 0.003937" "classname" "light" "origin" "56 -288 -616" "light" "100" } { "classname" "light" "_color" "1.000000 0.255906 0.003937" "origin" "-168 -1728 -728" "light" "100" } { "_color" "1.000000 0.255906 0.003937" "classname" "light" "origin" "56 -288 -752" "light" "100" } { "classname" "light" "_color" "1.000000 0.255906 0.003937" "origin" "-184 -488 -624" "light" "100" } { "item" "ammo_bullets" "spawnflags" "1" "targetname" "t13" "target" "t10" "angle" "270" "origin" "-474 -543 -928" "classname" "monster_soldier_ss" } { "_color" "1.000000 0.733333 0.733333" "origin" "416 -1184 -624" "light" "225" "classname" "light" } { "classname" "light" "light" "225" "origin" "416 -1248 -624" "_color" "1.000000 0.733333 0.733333" } { "_color" "1.000000 0.733333 0.733333" "origin" "-144 -1888 -744" "light" "100" "classname" "light" } { "classname" "light" "light" "100" "origin" "-144 -1952 -744" "_color" "1.000000 0.733333 0.733333" } { "_color" "1.000000 0.733333 0.733333" "origin" "-928 -1184 -704" "light" "225" "classname" "light" } { "angle" "270" "sounds" "1" "classname" "target_splash" "targetname" "gun3" "origin" "-880 488 -564" "spawnflags" "2048" } { "angle" "270" "sounds" "1" "classname" "target_splash" "targetname" "gun2" "origin" "-904 488 -564" "spawnflags" "2048" } { "angle" "270" "sounds" "1" "classname" "target_splash" "targetname" "gun1" "origin" "-928 488 -564" "spawnflags" "2048" } { "classname" "target_splash" "sounds" "1" "angle" "270" "targetname" "gun4" "origin" "-856 488 -564" "spawnflags" "2048" } { "light" "200" "classname" "light" "origin" "88 -384 -528" "_color" "1.000000 0.988189 0.724409" } { "light" "100" "classname" "light" "origin" "-216 -384 -528" "_color" "1.000000 0.988189 0.724409" } { "item" "ammo_bullets" "spawnflags" "1" "targetname" "t13" "target" "t12" "classname" "monster_soldier_ss" "origin" "-415 -535 -930" "angle" "270" } { "origin" "-132 -1702 -914" "_color" "1.000000 0.920949 0.343874" "classname" "light" "light" "64" } { "_color" "1.000000 0.875537 0.004292" "origin" "-896 32 -380" "classname" "light" "light" "32" } { "model" "*121" "spawnflags" "2048" "delay" ".25" "angle" "90" "speed" "500" "wait" ".25" "lip" "6" "classname" "func_door" "targetname" "gun3" "sounds" "2" } { "light" "150" "classname" "light" "origin" "-896 288 -416" "_color" "1.000000 0.875537 0.004292" } { "light" "100" "classname" "light" "origin" "88 -576 -528" "_color" "1.000000 0.988189 0.724409" } { "origin" "-64 -1808 -971" "_color" "1.000000 1.000000 1.000000" "light" "200" "classname" "light" "target" "t127" "_cone" "20" } { "origin" "-64 -1808 -975" "_color" "1.000000 1.000000 1.000000" "light" "90" "classname" "light" } { "light" "150" "classname" "light" "origin" "-4 -1232 -912" "_color" "1.000000 0.875537 0.004292" } { "light" "150" "classname" "light" "origin" "-132 -1232 -912" "_color" "1.000000 0.875537 0.004292" } { "model" "*122" "team" "B" "lip" "8" "wait" "4" "sounds" "3" "speed" "75" "angle" "180" "classname" "func_door" } { "model" "*123" "team" "B" "classname" "func_door" "angle" "0" "speed" "75" "wait" "4" "lip" "8" } { "classname" "light" "light" "100" "origin" "300 -592 -848" } { "_color" "1.000000 0.920949 0.343874" "light" "120" "classname" "light" "origin" "-64 -1824 -874" } { "light" "100" "classname" "light" "_color" "1.000000 0.920949 0.343874" "origin" "344 -1400 -914" } { "light" "150" "classname" "light" "_color" "1.000000 0.920949 0.343874" "origin" "-64 -1528 -914" } { "classname" "light" "light" "90" "_color" "1.000000 0.920900 0.343800" "origin" "-64 -1936 -982" } { "style" "41" "classname" "light" "light" "300" "origin" "200 0 -753" "spawnflags" "1" "targetname" "sdlight" } { "classname" "light" "_color" "1.000000 0.259843 0.220472" "origin" "-268 -1368 -1048" "light" "100" } { "model" "*124" "target" "t159" "classname" "func_explosive" "dmg" "15" "health" "50" "_minlight" "0.2" "spawnflags" "2048" } { "model" "*125" "Team" "door1" "classname" "func_door" "angle" "180" "speed" "50" "lip" "8" } { "classname" "light" "light" "150" "origin" "88 -1696 -648" "_color" "1.000000 0.733333 0.733333" } { "classname" "light" "light" "150" "origin" "88 -1760 -648" "_color" "1.000000 0.733333 0.733333" } { "classname" "info_player_coop" "origin" "-8 -1702 -808" "angle" "90" "targetname" "city2NH" } { "_color" "1.000000 0.733333 0.733333" "classname" "light" "light" "150" "origin" "-216 -1760 -648" } { "_color" "1.000000 0.733333 0.733333" "classname" "light" "light" "150" "origin" "-216 -1696 -648" } { "model" "*126" "spawnflags" "2048" "wait" "-1" "_minlight" "0.3" "classname" "func_button" "target" "droop_gate" "angle" "270" "lip" "3" "sounds" "4" } { "model" "*127" "spawnflags" "2048" "classname" "func_door" "angle" "-2" "sounds" "1" "speed" "75" "wait" "-1" "targetname" "droop_gate" } { "origin" "-552 -1216 -536" "classname" "light" "light" "150" "_color" "1.000000 0.875537 0.004292" } { "light" "150" "classname" "light" "origin" "-736 -1216 -624" "_color" "1.000000 0.875537 0.004292" } { "_color" "1.000000 0.875537 0.004292" "origin" "-832 -1312 -624" "classname" "light" "light" "150" } { "light" "150" "classname" "light" "origin" "-640 -1136 -952" "_color" "1.000000 0.875537 0.004292" } { "light" "150" "classname" "light" "origin" "-640 -1248 -952" "_color" "1.000000 0.875537 0.004292" } { "light" "150" "classname" "light" "origin" "-624 -1408 -952" "_color" "1.000000 0.875537 0.004292" } { "_color" "1.000000 0.875537 0.004292" "origin" "-640 -1360 -952" "classname" "light" "light" "150" } { "light" "150" "classname" "light" "origin" "-544 -1600 -776" "_color" "1.000000 0.875537 0.004292" } { "light" "150" "classname" "light" "origin" "-736 -1600 -712" "_color" "1.000000 0.875537 0.004292" } { "light" "150" "classname" "light" "origin" "-832 -1504 -696" "_color" "1.000000 0.875537 0.004292" } { "_color" "1.000000 0.875537 0.004292" "origin" "224 -1216 -536" "classname" "light" "light" "150" } { "light" "100" "classname" "light" "origin" "320 -1208 -584" "_color" "1.000000 0.875537 0.004292" } { "light" "64" "classname" "light" "origin" "320 -1336 -808" "_color" "1.000000 0.875537 0.004292" } { "_color" "1.000000 0.875537 0.004292" "light" "80" "classname" "light" "origin" "368 -836 -536" } { "_color" "1.000000 0.948819 0.755906" "origin" "-216 -16 -720" "classname" "light" "light" "100" } { "origin" "-64 152 -328" "light" "150" "classname" "light" } { "model" "*128" "classname" "func_door" "angle" "180" "speed" "75" "sounds" "3" "wait" "4" "lip" "8" "team" "C" } { "model" "*129" "lip" "8" "wait" "4" "speed" "75" "angle" "0" "classname" "func_door" "team" "C" } { "light" "100" "classname" "light" "origin" "128 -384 -736" "_color" "1.000000 0.948819 0.755906" } { "_cone" "25" "target" "t273" "_color" "1.000000 0.948819 0.755906" "origin" "-260 -384 -740" "classname" "light" "light" "200" } { "_color" "1.000000 0.988189 0.724409" "origin" "88 -192 -528" "classname" "light" "light" "200" } { "model" "*130" "lip" "8" "Team" "door4" "angle" "270" "classname" "func_door" } { "model" "*131" "lip" "8" "Team" "door4" "sounds" "3" "angle" "90" "classname" "func_door" } { "_color" "1.000000 0.988189 0.724409" "origin" "-216 -192 -528" "classname" "light" "light" "100" } { "light" "100" "classname" "light" "origin" "-216 -576 -528" "_color" "1.000000 0.988189 0.724409" } { "light" "100" "classname" "light" "origin" "-216 72 -720" "_color" "1.000000 0.948819 0.755906" } { "light" "200" "classname" "light" "origin" "200 0 -536" "_color" "1.000000 0.988189 0.724409" } { "light" "150" "classname" "light" "origin" "-416 -192 -536" "_color" "1.000000 0.875537 0.004292" } { "light" "150" "classname" "light" "origin" "-608 -192 -432" "_color" "1.000000 0.875537 0.004292" } { "_color" "1.000000 0.875537 0.004292" "origin" "-608 384 -368" "classname" "light" "light" "150" } { "_color" "1.000000 0.875537 0.004292" "origin" "-704 -96 -432" "classname" "light" "light" "150" } { "light" "150" "classname" "light" "origin" "-800 0 -400" "_color" "1.000000 0.875537 0.004292" } { "light" "150" "classname" "light" "origin" "-640 -1008 -952" "_color" "1.000000 0.875537 0.004292" } { "light" "150" "classname" "light" "origin" "-448 -1504 -776" "_color" "1.000000 0.875537 0.004292" } { "_color" "1.000000 0.875537 0.004292" "origin" "-178 -1216 -912" "classname" "light" "light" "150" } { "_color" "1.000000 0.875537 0.004292" "origin" "-124 -1016 -848" "classname" "light" "light" "150" } { "_color" "1.000000 0.875537 0.004292" "origin" "4 -1016 -848" "classname" "light" "light" "150" } { "_color" "1.000000 0.875537 0.004292" "origin" "320 -928 -816" "classname" "light" "light" "150" } { "_color" "1.000000 0.875537 0.004292" "origin" "88 -712 -828" "classname" "light" "light" "150" } { "_color" "1.000000 0.875537 0.004292" "origin" "8 -648 -828" "classname" "light" "light" "150" } { "_color" "1.000000 0.875537 0.004292" "origin" "-144 -648 -832" "classname" "light" "light" "150" } { "_color" "1.000000 0.875537 0.004292" "origin" "-448 -728 -828" "classname" "light" "light" "150" } { "model" "*132" "spawnflags" "2054" "classname" "func_wall" "targetname" "redforfld" } { "_color" "1.000000 0.875537 0.004292" "origin" "-448 -1504 -824" "classname" "light" "light" "150" } { "_color" "1.000000 0.875537 0.004292" "origin" "-448 -1504 -776" "classname" "light" "light" "150" } { "light" "150" "classname" "light" "origin" "-800 384 -416" "_color" "1.000000 0.875537 0.004292" } { "origin" "112 -1216 -640" "classname" "light" "light" "150" "_color" "1.000000 0.875537 0.004292" } { "origin" "-240 -1216 -624" "classname" "light" "light" "150" "_color" "1.000000 0.875537 0.004292" } { "origin" "-40 -784 -432" "classname" "light" "light" "200" "_color" "1.000000 0.875537 0.004292" } { "origin" "96 -800 -536" "classname" "light" "light" "100" "_color" "1.000000 0.875537 0.004292" } { "light" "200" "classname" "light" "origin" "-88 -784 -432" "_color" "1.000000 0.875537 0.004292" } { "_color" "1.000000 1.000000 0.901961" "light" "150" "classname" "light" "origin" "-64 -1432 -632" } { "light" "225" "classname" "light" "origin" "-352 -1216 -536" "_color" "1.000000 0.875537 0.004292" } { "origin" "-96 -976 -488" "light" "150" "classname" "light" "_color" "0.756863 0.807843 1.000000" } { "origin" "-32 -976 -488" "classname" "light" "light" "150" "_color" "0.756863 0.807843 1.000000" } { "light" "150" "classname" "light" "origin" "-64 -976 -560" "_color" "0.756863 0.807843 1.000000" } { "classname" "light" "light" "150" "origin" "-96 -976 -600" "_color" "0.756863 0.807843 1.000000" } { "spawnflags" "2048" "classname" "light" "light" "150" "origin" "-32 -976 -600" "_color" "0.756863 0.807843 1.000000" } { "light" "150" "classname" "light" "origin" "-64 -976 -640" "_color" "0.756863 0.807843 1.000000" } { "origin" "-96 -976 -680" "light" "150" "classname" "light" "_color" "0.756863 0.807843 1.000000" } { "origin" "-64 -976 -712" "classname" "light" "light" "150" "_color" "0.756863 0.807843 1.000000" } { "origin" "-32 -976 -680" "light" "150" "classname" "light" "_color" "0.756863 0.807843 1.000000" } { "origin" "-40 -1408 -544" "light" "150" "classname" "light" "_color" "0.756863 0.807843 1.000000" } { "origin" "-88 -1408 -544" "classname" "light" "light" "150" "_color" "0.756863 0.807843 1.000000" } { "origin" "-96 -1408 -600" "light" "200" "classname" "light" } { "classname" "light" "light" "150" "origin" "-96 -976 -744" "_color" "0.756863 0.807843 1.000000" } { "classname" "light" "light" "150" "origin" "-32 -976 -744" "_color" "0.756863 0.807843 1.000000" } { "classname" "light" "light" "150" "origin" "-32 -1408 -488" "_color" "0.756863 0.807843 1.000000" } { "light" "150" "classname" "light" "origin" "-96 -1408 -488" "_color" "0.756863 0.807843 1.000000" } { "_color" "0.615686 0.615686 1.000000" "classname" "light" "light" "200" "origin" "-64 256 80" } { "origin" "88 -1568 -648" "light" "150" "classname" "light" "_color" "1.000000 0.733333 0.733333" } { "origin" "88 -1504 -648" "light" "150" "classname" "light" "_color" "1.000000 0.733333 0.733333" } { "classname" "light" "light" "225" "origin" "-928 -1248 -704" "_color" "1.000000 0.733333 0.733333" } { "classname" "light" "light" "150" "origin" "-216 -1568 -648" "_color" "1.000000 0.733333 0.733333" } { "classname" "light" "light" "150" "origin" "-216 -1504 -648" "_color" "1.000000 0.733333 0.733333" } { "classname" "target_blaster" "angle" "270" "targetname" "gun4" "origin" "-856 488 -564" "dmg" "15" "spawnflags" "2048" } { "origin" "-928 488 -564" "angle" "270" "targetname" "gun1" "classname" "target_blaster" "dmg" "15" "spawnflags" "2048" } { "angle" "270" "origin" "-904 488 -564" "targetname" "gun2" "classname" "target_blaster" "dmg" "15" "spawnflags" "2048" } { "origin" "-880 488 -564" "angle" "270" "targetname" "gun3" "classname" "target_blaster" "dmg" "15" "spawnflags" "2048" } { "classname" "light" "light" "100" "origin" "-832 528 -580" } { "model" "*133" "classname" "func_door" "lip" "6" "wait" ".25" "speed" "500" "angle" "90" "delay" ".25" "targetname" "gun4" "spawnflags" "2048" "sounds" "2" } { "model" "*134" "delay" ".25" "angle" "90" "speed" "500" "wait" ".25" "lip" "6" "classname" "func_door" "targetname" "gun2" "spawnflags" "2048" "sounds" "2" } { "model" "*135" "delay" ".25" "angle" "90" "speed" "500" "wait" ".25" "lip" "6" "classname" "func_door" "targetname" "gun1" "spawnflags" "2048" "sounds" "2" } { "light" "100" "classname" "light" "origin" "-952 528 -580" } { "model" "*136" "team" "traphider" "sounds" "0" "speed" "200" "wait" "-1" "angle" "180" "classname" "func_door" "lip" "8" "targetname" "trap_gun" "spawnflags" "2048" } { "model" "*137" "team" "traphider" "classname" "func_door" "angle" "0" "targetname" "trap_gun" "wait" "-1" "speed" "200" "sounds" "2" "lip" "8" "spawnflags" "2048" } { "classname" "trigger_relay" "targetname" "trap_gun" "target" "gun1" "delay" ".2" "origin" "-776 400 -440" "spawnflags" "2304" } { "classname" "trigger_relay" "targetname" "trap_gun" "target" "gun2" "delay" ".4" "origin" "-752 400 -440" "spawnflags" "2304" } { "classname" "trigger_relay" "targetname" "trap_gun" "target" "gun3" "delay" ".6" "origin" "-728 400 -440" "spawnflags" "2304" } { "classname" "trigger_relay" "targetname" "trap_gun" "target" "gun4" "delay" ".8" "origin" "-704 400 -440" "spawnflags" "2304" } { "origin" "-64 408 96" "_color" "1.000000 1.000000 0.874510" "light" "80" "classname" "light" } { "_cone" "20" "classname" "light" "light" "300" "_color" "1.000000 1.000000 1.000000" "origin" "-64 408 88" "target" "t161" } { "classname" "info_notnull" "origin" "-65 408 -104" "targetname" "t161" }yquake2-QUAKE2_8_40/stuff/mapfixes/baseq2/city3.ent000066400000000000000000003257521465112212000220120ustar00rootroot00000000000000// FIXED ENTITY STRING (by BjossiAlfreds) // // 1. Fixed 2x monster_chick not activating (5016, 5023) // // Their targetname, t125, was never triggered. Set it to t257 instead, // which is activated by using the Data Spinner. Also set spawnflags of // one of them from 2051 to 3, as the 2048 flag is unnecessary for monsters. // // 2. Removed DM-only items from SP/co-op // // A Grenade Launcher and Grenades (99, 104) are located in a room // only accessible in DM. Set their spawnflags from 0 to 1792. { "nextmap" "boss1" "sky" "unit9_" "sounds" "4" "message" "Upper Palace" "spawnflags" "0" "classname" "worldspawn" } { "model" "*1" "classname" "func_wall" "spawnflags" "2048" "_minlight" "0.2" } { "model" "*2" "classname" "func_wall" "_minlight" "0.8" "spawnflags" "2048" } { "classname" "target_speaker" "spawnflags" "2052" "targetname" "t304" "noise" "world/x_alarm.wav" "volume" "1.0" "attenuation" "-1" "origin" "-1568 -416 -1016" } { "spawnflags" "1792" "classname" "misc_teleporter" "angle" "135" "target" "telly1" "origin" "368 -336 220" } { "spawnflags" "1792" "origin" "-408 -664 -88" "targetname" "telly1" "angle" "90" "classname" "misc_teleporter_dest" } { "origin" "-56 -520 224" "spawnflags" "2048" "classname" "item_power_shield" } { "spawnflags" "2560" "origin" "-208 -256 -408" "classname" "item_health" } { "origin" "128 64 -96" "spawnflags" "1792" "classname" "weapon_railgun" } { "origin" "-608 -128 256" "spawnflags" "1792" "classname" "weapon_machinegun" } { "origin" "-624 -448 224" "spawnflags" "1792" "classname" "ammo_rockets" } { "origin" "247 544 236" "classname" "item_health_small" "spawnflags" "1792" } { "origin" "295 544 236" "classname" "item_health_small" "spawnflags" "1792" } { "origin" "343 544 236" "classname" "item_health_small" "spawnflags" "1792" } { "origin" "199 544 252" "spawnflags" "1792" "classname" "item_health_small" } { "origin" "-414 436 242" "spawnflags" "1792" "classname" "ammo_grenades" } { "spawnflags" "1792" "origin" "-414 284 244" "classname" "weapon_grenadelauncher" } { "model" "*3" "classname" "func_wall" "spawnflags" "2048" } { "origin" "584 520 82" "angle" "-1" "spawnflags" "2057" "classname" "target_laser" "target" "com8" "targetname" "com7" } { "origin" "600 520 82" "angle" "-1" "spawnflags" "2057" "classname" "target_laser" "target" "com8" "targetname" "com7" } { "origin" "600 504 82" "angle" "-1" "spawnflags" "2057" "classname" "target_laser" "target" "com8" "targetname" "com7" } { "origin" "-120 -256 -408" "classname" "item_silencer" } { "classname" "monster_gunner" "angle" "0" "spawnflags" "769" "origin" "-168 -296 -400" } { "classname" "ammo_slugs" "origin" "-208 -256 -360" "spawnflags" "1792" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "48 -472 -104" } { "spawnflags" "1792" "origin" "8 -472 -104" "classname" "item_armor_shard" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "128 -472 -104" } { "spawnflags" "1792" "origin" "88 -472 -104" "classname" "item_armor_shard" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "208 -472 -104" } { "spawnflags" "1792" "origin" "168 -472 -104" "classname" "item_armor_shard" } { "spawnflags" "1792" "origin" "-32 -472 -104" "classname" "item_armor_shard" } { "origin" "312 -472 -104" "classname" "item_health_large" } { "classname" "info_player_deathmatch" "angle" "135" "origin" "184 -520 -552" } { "classname" "light" "light" "64" "origin" "-1651 -556 -1104" } { "origin" "-700 -470 -998" "spawnflags" "259" "classname" "monster_soldier_light" "angle" "0" "targetname" "pg1" } { "classname" "light" "light" "32" "origin" "488 1248 358" "_color" "1.000000 0.292490 0.193676" } { "deathtarget" "t243" "spawnflags" "257" "origin" "128 -468 -540" "angle" "180" "classname" "monster_soldier" "item" "ammo_shells" } { "classname" "monster_soldier_light" "angle" "90" "origin" "-606 -585 -87" "spawnflags" "768" } { "classname" "monster_soldier_light" "angle" "45" "origin" "-486 -441 -87" } { "light" "32" "classname" "light" "origin" "-24 744 55" } { "light" "32" "classname" "light" "origin" "-24 616 55" } { "light" "32" "classname" "light" "origin" "40 744 55" } { "light" "32" "classname" "light" "origin" "40 680 55" } { "light" "32" "classname" "light" "origin" "40 616 55" } { "origin" "-88 744 55" "classname" "light" "light" "32" } { "origin" "-88 680 55" "classname" "light" "light" "32" } { "origin" "-88 616 55" "classname" "light" "light" "32" } { "origin" "40 536 23" "classname" "light" "light" "48" } { "light" "32" "_color" "1.000000 0.774704 0.304348" "classname" "light" "origin" "-64 -128 326" } { "light" "32" "_color" "1.000000 0.774704 0.304348" "classname" "light" "origin" "0 -128 326" } { "light" "32" "_color" "1.000000 0.774704 0.304348" "classname" "light" "origin" "64 -128 326" } { "light" "32" "_color" "1.000000 0.774704 0.304348" "classname" "light" "origin" "128 -128 326" } { "light" "32" "_color" "1.000000 0.774704 0.304348" "classname" "light" "origin" "192 -128 326" } { "light" "150" "_color" "1.000000 0.774704 0.304348" "classname" "light" "origin" "256 -128 294" } { "style" "1" "classname" "func_areaportal" "targetname" "t303" } { "style" "2" "classname" "func_areaportal" "targetname" "t302" } { "style" "3" "classname" "func_areaportal" "targetname" "t301" } { "model" "*4" "classname" "func_door" "angle" "180" "team" "portal2" "lip" "16" "wait" "3" "_minlight" "0.2" "target" "t302" "spawnflags" "8" } { "model" "*5" "sounds" "3" "classname" "func_door" "angle" "0" "team" "portal2" "lip" "16" "wait" "3" "_minlight" "0.2" "target" "t302" "spawnflags" "8" } { "model" "*6" "wait" "3" "lip" "16" "team" "p2" "angle" "0" "classname" "func_door" "sounds" "3" "_minlight" "0.2" "target" "t301" "spawnflags" "8" } { "model" "*7" "wait" "3" "lip" "16" "team" "p2" "angle" "180" "classname" "func_door" "_minlight" "0.2" "target" "t301" "spawnflags" "8" } { "model" "*8" "classname" "func_door" "spawnflags" "1" "targetname" "left_elev" "angle" "-2" "sounds" "4" "speed" "150" "wait" "1.75" } { "model" "*9" "spawnflags" "2048" "classname" "func_wall" } { "model" "*10" "spawnflags" "1792" "classname" "func_wall" } { "classname" "trigger_relay" "targetname" "pg1" "killtarget" "yelmsg" "spawnflags" "2048" "origin" "948 1288 168" } { "light" "100" "_color" "0.602362 1.000000 0.555118" "classname" "light" "origin" "88 -568 -504" } { "light" "100" "_color" "0.602362 1.000000 0.555118" "classname" "light" "origin" "8 -568 -504" } { "light" "100" "_color" "0.602362 1.000000 0.555118" "classname" "light" "origin" "168 -568 -504" } { "light" "100" "_color" "0.602362 1.000000 0.555118" "classname" "light" "origin" "-72 -568 -504" } { "origin" "16 -304 -648" "noise" "world/pump1.wav" "spawnflags" "1" "classname" "target_speaker" } { "classname" "trigger_relay" "killtarget" "bluemsg" "targetname" "pg2" "origin" "176 496 268" } { "model" "*11" "message" "This force field is activated\nby the communication laser." "classname" "trigger_multiple" "targetname" "bluemsg" "wait" "3" "spawnflags" "2048" } { "model" "*12" "classname" "trigger_multiple" "targetname" "bluemsg" "message" "This force field is activated\nby the communication laser." "wait" "3" "spawnflags" "2048" } { "model" "*13" "classname" "trigger_multiple" "message" "Security pass required\nto deactivate yellow force fields." "targetname" "yelmsg" "spawnflags" "2048" "wait" "3" } { "model" "*14" "classname" "trigger_multiple" "targetname" "yelmsg" "message" "Security pass required\nto deactivate yellow force fields." "wait" "3" "spawnflags" "2048" } { "origin" "-64 -552 -560" "classname" "item_health_large" "spawnflags" "2048" } { "classname" "item_health_large" "origin" "-24 -552 -560" } { "classname" "trigger_relay" "targetname" "passmsg" "message" "Security pass grants\naccess to\nforce field control chamber." "origin" "-464 -240 -336" } { "model" "*15" "classname" "trigger_multiple" "target" "t281" } { "origin" "172 -168 -112" "classname" "item_pack" } { "origin" "168 -376 -648" "noise" "world/pump1.wav" "spawnflags" "1" "classname" "target_speaker" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb11.wav" "origin" "-40 80 -120" } { "targetname" "city3NH" "origin" "-500 -902 -84" "angle" "45" "classname" "info_player_coop" } { "targetname" "city3NH" "origin" "-400 -902 -84" "angle" "135" "classname" "info_player_coop" } { "targetname" "city3NH" "classname" "info_player_coop" "angle" "90" "origin" "-492 -662 -88" } { "targetname" "city3NL" "origin" "-112 -1188 -888" "angle" "180" "classname" "info_player_coop" } { "targetname" "city3NL" "origin" "-168 -1224 -896" "angle" "180" "classname" "info_player_coop" } { "targetname" "city3NL" "origin" "-224 -1216 -900" "angle" "180" "classname" "info_player_start" } { "model" "*16" "spawnflags" "2048" "classname" "func_wall" } { "model" "*17" "classname" "func_wall" "spawnflags" "3840" } { "origin" "176 680 -44" "light" "150" "spawnflags" "1792" "classname" "light" } { "origin" "46 670 242" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-586 -588 -88" "spawnflags" "1792" "classname" "weapon_shotgun" } { "origin" "-576 384 232" "spawnflags" "257" "classname" "monster_floater" } { "origin" "-288 368 232" "spawnflags" "257" "classname" "monster_floater" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "-72 -472 -104" } { "model" "*18" "classname" "func_wall" "spawnflags" "2048" } { "classname" "info_player_deathmatch" "angle" "225" "origin" "308 80 -96" } { "classname" "item_health" "origin" "172 -80 -112" } { "angle" "270" "classname" "monster_gunner" "targetname" "bob" "origin" "296 64 -92" "item" "ammo_grenades" } { "item" "item_armor_shard" "angle" "0" "classname" "monster_gunner" "targetname" "bob" "origin" "168 64 -80" } { "classname" "path_corner" "origin" "36 -448 -112" "target" "t294" "targetname" "t297" "spawnflags" "2048" } { "origin" "-112 -444 -112" "classname" "path_corner" "targetname" "t294" "target" "t295" "spawnflags" "2048" } { "origin" "-116 -448 -112" "classname" "path_corner" "targetname" "t296" "target" "t297" "spawnflags" "2048" } { "origin" "-128 -300 -108" "classname" "path_corner" "targetname" "t295" "target" "t296" "spawnflags" "2048" } { "spawnflags" "1" "angle" "0" "classname" "monster_soldier" "origin" "-124 -284 -96" "target" "t295" } { "classname" "path_corner" "origin" "276 -456 -112" "targetname" "t290" "target" "t291" "spawnflags" "2048" } { "origin" "136 -468 -112" "classname" "path_corner" "target" "t290" "targetname" "t293" "spawnflags" "2048" } { "classname" "path_corner" "origin" "300 -320 -108" "targetname" "t291" "target" "t292" "spawnflags" "2048" } { "classname" "path_corner" "origin" "280 -460 -112" "targetname" "t292" "target" "t293" "spawnflags" "2048" } { "item" "ammo_shells" "spawnflags" "1" "angle" "0" "classname" "monster_soldier" "origin" "-40 -288 -96" } { "style" "4" "targetname" "t289" "classname" "func_areaportal" } { "model" "*19" "spawnflags" "2048" "classname" "func_wall" } { "model" "*20" "target" "t289" "spawnflags" "1792" "lip" "8" "angle" "-1" "classname" "func_door" } { "model" "*21" "classname" "func_door" "angle" "-1" "lip" "86" "_minlight" "0.2" "sounds" "1" "speed" "100" "targetname" "t149" "wait" "2" } { "_color" "1.000000 0.940945 0.708661" "origin" "-608 -624 -56" "classname" "light" "light" "150" } { "_color" "1.000000 0.940945 0.708661" "origin" "-304 -624 -56" "light" "150" "classname" "light" } { "origin" "-280 -284 -644" "classname" "point_combat" "spawnflags" "2048" "targetname" "t287" } { "model" "*22" "classname" "func_wall" "spawnflags" "2048" } { "model" "*23" "classname" "func_wall" "spawnflags" "2048" } { "model" "*24" "classname" "func_wall" "spawnflags" "2048" } { "model" "*25" "classname" "func_wall" "spawnflags" "2048" } { "model" "*26" "classname" "func_button" "angle" "270" "lip" "3" "message" "Yellow Force Fields Deactivated." "target" "t147" "wait" "-1" "_minlight" "0.8" "spawnflags" "2048" } { "model" "*27" "classname" "func_wall" "spawnflags" "2048" } { "model" "*28" "wait" "0.5" "lip" "4" "sounds" "4" "spawnflags" "2048" "angle" "180" "_minlight" "0.8" "classname" "func_button" "target" "reddoor1" } { "model" "*29" "spawnflags" "2048" "_minlight" "0.1" "classname" "func_wall" } { "model" "*30" "classname" "func_wall" "_minlight" "0.1" "spawnflags" "2048" } { "model" "*31" "wait" "0.5" "classname" "func_button" "_minlight" "0.8" "angle" "180" "spawnflags" "2048" "sounds" "4" "lip" "4" "target" "reddoor2" } { "model" "*32" "spawnflags" "2048" "_minlight" "0.1" "classname" "func_wall" } { "model" "*33" "wait" "0.5" "target" "reddoor2" "lip" "4" "sounds" "4" "spawnflags" "2048" "angle" "180" "_minlight" "0.8" "classname" "func_button" } { "model" "*34" "wait" "0.5" "classname" "func_button" "_minlight" "0.8" "angle" "0" "spawnflags" "2048" "sounds" "4" "lip" "4" "target" "t173" } { "model" "*35" "classname" "func_wall" "_minlight" "0.1" "spawnflags" "2048" } { "targetname" "t286" "origin" "-452 -296 -644" "classname" "point_combat" } { "model" "*36" "targetname" "t281" "_minlight" "0.3" "delay" ".25" "wait" "4" "lip" "20" "speed" "225" "angle" "-1" "classname" "func_door" "sounds" "4" } { "model" "*37" "_minlight" "0.2" "spawnflags" "2048" "classname" "func_wall" } { "classname" "info_notnull" "targetname" "t285" "origin" "-336 36 -64" } { "style" "32" "classname" "light" "targetname" "com7" "_color" "0.070866 0.444882 1.000000" "origin" "592 512 80" } { "classname" "target_laser" "spawnflags" "2057" "angle" "-1" "targetname" "com7" "origin" "584 504 82" "target" "com8" } { "model" "*38" "classname" "func_wall" "spawnflags" "2048" } { "model" "*39" "origin" "592 512 320" "classname" "func_rotating" "spawnflags" "2049" "speed" "50" "targetname" "com2" } { "model" "*40" "classname" "func_wall" "spawnflags" "1792" } { "model" "*41" "classname" "func_wall" "spawnflags" "1792" } { "classname" "info_player_deathmatch" "angle" "135" "origin" "624 16 224" } { "model" "*42" "classname" "func_wall" "spawnflags" "2048" "_minlight" "0.1" } { "model" "*43" "spawnflags" "2048" "classname" "func_wall" } { "model" "*44" "classname" "func_wall" "_minlight" "0.1" "spawnflags" "1792" } { "model" "*45" "classname" "func_button" "angle" "90" "lip" "4" "_minlight" "0.6" "spawnflags" "1792" "target" "endplat" } { "model" "*46" "classname" "trigger_once" "target" "t275" } { "model" "*47" "classname" "func_wall" "spawnflags" "1792" } { "origin" "32 -368 -632" "angle" "90" "classname" "info_player_deathmatch" } { "classname" "weapon_hyperblaster" "spawnflags" "1792" "origin" "-448 68 408" } { "origin" "-456 696 112" "classname" "light" "light" "120" } { "model" "*48" "spawnflags" "1792" "classname" "func_wall" } { "origin" "-448 -1152 -728" "targetname" "farend" "angle" "0" "classname" "misc_teleporter_dest" } { "origin" "-488 -1128 -664" "light" "32" "classname" "light" } { "origin" "-472 -1192 -664" "light" "32" "classname" "light" } { "origin" "-488 -1176 -664" "light" "32" "classname" "light" } { "origin" "-472 -1112 -664" "light" "32" "classname" "light" } { "model" "*49" "spawnflags" "2048" "classname" "func_wall" } { "model" "*50" "spawnflags" "2048" "classname" "func_wall" } { "origin" "-376 -296 -760" "spawnflags" "1792" "classname" "item_power_shield" "team" "shield" } { "origin" "-376 -296 -760" "spawnflags" "1792" "team" "shield" "classname" "ammo_cells" } { "model" "*51" "spawnflags" "1792" "wait" "2" "angle" "0" "health" "2" "classname" "func_door" } { "model" "*52" "targetname" "t56" "dmg" "1" "classname" "func_explosive" } { "spawnflags" "1792" "origin" "-504 -172 -968" "classname" "ammo_bullets" } { "classname" "ammo_grenades" "origin" "-602 -164 -404" "spawnflags" "0" } { "origin" "-312 32 -408" "classname" "weapon_grenadelauncher" "spawnflags" "1792" } { "origin" "-312 -24 -408" "classname" "ammo_grenades" "spawnflags" "1792" } { "target" "bfgtpd" "origin" "-216 16 -400" "angle" "225" "spawnflags" "1792" "classname" "misc_teleporter" } { "model" "*53" "spawnflags" "1792" "classname" "func_wall" } { "model" "*54" "classname" "func_wall" "spawnflags" "2048" } { "origin" "696 896 80" "spawnflags" "1792" "classname" "weapon_shotgun" } { "targetname" "bfgtpd" "origin" "-616 440 -104" "spawnflags" "1792" "angle" "315" "classname" "misc_teleporter_dest" } { "origin" "912 1512 144" "light" "64" "classname" "light" } { "model" "*55" "spawnflags" "2048" "classname" "func_wall" } { "model" "*56" "classname" "func_door_secret" "spawnflags" "3" "wait" "5" "angle" "0" "_minlight" "0.1" } { "team" "green" "origin" "200 1496 312" "spawnflags" "1792" "classname" "item_power_shield" } { "origin" "336 1616 512" "classname" "item_health_large" "spawnflags" "3072" } { "target" "farend" "origin" "592 512 216" "spawnflags" "1792" "angle" "90" "classname" "misc_teleporter" } { "origin" "-628 244 -20" "angles" "-45 45 0" "classname" "info_player_intermission" } { "targetname" "t284" "classname" "misc_teleporter_dest" "angle" "90" "origin" "-328 88 408" "spawnflags" "1792" } { "model" "*57" "classname" "func_wall" "spawnflags" "2048" } { "target" "t284" "classname" "misc_teleporter" "spawnflags" "1792" "origin" "-48 -32 -632" } { "model" "*58" "classname" "func_wall" "spawnflags" "2048" } { "origin" "216 552 311" "classname" "light" "light" "32" } { "origin" "296 472 311" "classname" "light" "light" "32" } { "origin" "216 472 311" "classname" "light" "light" "32" } { "origin" "296 552 311" "light" "32" "classname" "light" } { "origin" "360 1384 264" "target" "t2" "spawnflags" "1792" "classname" "trigger_always" } { "model" "*59" "_minlight" "0.1" "spawnflags" "1792" "classname" "func_wall" } { "classname" "item_health" "spawnflags" "1792" "origin" "167 -169 215" } { "classname" "item_armor_combat" "spawnflags" "1792" "origin" "324 101 215" } { "spawnflags" "1792" "classname" "item_health" "origin" "119 -168 215" } { "classname" "ammo_bullets" "spawnflags" "1792" "origin" "617 167 224" } { "origin" "480 1216 320" "classname" "ammo_bullets" "spawnflags" "1792" } { "origin" "528 1216 320" "classname" "ammo_bullets" "spawnflags" "1792" } { "origin" "528 1216 272" "classname" "item_health" "spawnflags" "2048" } { "classname" "item_health_large" "origin" "-144 1280 240" } { "model" "*60" "angle" "-2" "classname" "func_door" "speed" "25" "sounds" "1" "spawnflags" "1824" "targetname" "t282" "_minlight" "0.3" } { "spawnflags" "1792" "classname" "weapon_railgun" "origin" "544 928 208" } { "classname" "item_armor_body" "spawnflags" "1792" "origin" "152 680 -80" } { "model" "*61" "classname" "func_wall" "spawnflags" "1792" } { "model" "*62" "spawnflags" "1792" "lip" "8" "classname" "func_door" "angle" "-1" "wait" "5" "speed" "200" "sounds" "0" "team" "door8" "targetname" "t283" "health" "5" } { "model" "*63" "classname" "func_wall" "spawnflags" "2048" } { "classname" "item_armor_jacket" "spawnflags" "1792" "origin" "-456 96 12" } { "classname" "item_armor_shard" "spawnflags" "1792" "origin" "660 440 176" } { "classname" "item_armor_shard" "spawnflags" "1792" "origin" "696 556 176" } { "classname" "item_armor_shard" "spawnflags" "1792" "origin" "644 612 176" } { "classname" "item_armor_shard" "spawnflags" "1792" "origin" "528 608 176" } { "classname" "item_armor_shard" "spawnflags" "1792" "origin" "508 516 208" } { "classname" "item_armor_shard" "spawnflags" "1792" "origin" "508 444 176" } { "classname" "item_armor_shard" "spawnflags" "1792" "origin" "592 392 176" } { "classname" "ammo_shells" "spawnflags" "1792" "origin" "-330 -592 -88" } { "light" "48" "classname" "light" "origin" "40 472 23" } { "light" "48" "classname" "light" "origin" "88 472 23" } { "light" "48" "classname" "light" "origin" "88 536 23" } { "light" "48" "classname" "light" "origin" "-8 536 23" } { "light" "48" "classname" "light" "origin" "-56 536 23" } { "classname" "light" "light" "48" "origin" "-8 472 23" } { "origin" "8 768 -112" "classname" "item_health" "spawnflags" "1792" } { "origin" "-32 768 -112" "classname" "ammo_cells" "spawnflags" "2048" } { "classname" "ammo_cells" "origin" "-456 584 160" "spawnflags" "2048" } { "spawnflags" "2048" "classname" "ammo_cells" "origin" "-336 664 160" } { "classname" "ammo_bullets" "origin" "-120 -400 216" } { "spawnflags" "2048" "origin" "-1256 -392 -1072" "classname" "item_armor_body" } { "classname" "item_health_small" "origin" "-720 -560 -1000" } { "classname" "item_health_small" "origin" "-800 -560 -1000" } { "classname" "item_health_small" "origin" "-760 -560 -1000" } { "classname" "item_health_small" "origin" "-680 -560 -1000" } { "origin" "-208 -464 -624" "classname" "item_health" "spawnflags" "2048" } { "spawnflags" "1792" "classname" "ammo_shells" "origin" "-184 -424 -624" } { "spawnflags" "2048" "classname" "ammo_slugs" "origin" "-312 -1252 -732" } { "classname" "weapon_supershotgun" "spawnflags" "1792" "origin" "-368 -1256 -728" } { "spawnflags" "1792" "classname" "weapon_machinegun" "origin" "-432 -144 -784" } { "spawnflags" "2048" "origin" "-504 -172 -984" "classname" "ammo_grenades" } { "classname" "ammo_rockets" "spawnflags" "1792" "origin" "-1376 -432 -1200" } { "origin" "-1376 -392 -1224" "classname" "item_adrenaline" "spawnflags" "2048" } { "spawnflags" "1792" "origin" "-602 -204 -364" "classname" "ammo_cells" } { "spawnflags" "1792" "classname" "weapon_hyperblaster" "origin" "-448 -148 -368" } { "spawnflags" "2048" "classname" "key_pass" "origin" "-448 -116 -344" "target" "passmsg" } { "classname" "ammo_bullets" "origin" "-320 -104 -408" } { "classname" "ammo_shells" "spawnflags" "1792" "origin" "0 -272 -408" } { "classname" "ammo_shells" "spawnflags" "1024" "origin" "48 -272 -408" } { "classname" "weapon_supershotgun" "spawnflags" "1792" "origin" "0 -298 -104" } { "classname" "ammo_slugs" "origin" "184 -536 216" } { "spawnflags" "2048" "origin" "212 -496 216" "classname" "item_invulnerability" } { "team" "ammo2" "classname" "ammo_rockets" "origin" "-716 -250 -598" "spawnflags" "1792" } { "team" "ammo2" "classname" "ammo_cells" "origin" "-715 -292 -598" "spawnflags" "1792" } { "classname" "ammo_bullets" "origin" "-716 -250 -638" "spawnflags" "2048" } { "classname" "ammo_bullets" "origin" "-715 -292 -638" "spawnflags" "2048" } { "spawnflags" "2048" "origin" "-296 28 -48" "classname" "ammo_slugs" } { "spawnflags" "2048" "origin" "-372 32 -48" "classname" "ammo_cells" } { "spawnflags" "2048" "origin" "-332 40 -48" "classname" "item_adrenaline" } { "classname" "weapon_machinegun" "origin" "-464 156 -110" "spawnflags" "1792" } { "spawnflags" "2048" "classname" "ammo_cells" "origin" "-280 512 -40" } { "spawnflags" "2048" "classname" "ammo_cells" "origin" "-232 512 -40" } { "classname" "weapon_bfg" "spawnflags" "1792" "origin" "912 1512 112" } { "spawnflags" "1792" "classname" "item_health_large" "origin" "872 1408 80" } { "classname" "info_player_deathmatch" "angle" "270" "origin" "992 1056 88" } { "classname" "info_player_deathmatch" "angle" "45" "origin" "-608 176 -104" } { "spawnflags" "2048" "classname" "item_quad" "origin" "-280 600 -200" } { "classname" "info_player_deathmatch" "angle" "135" "origin" "-16 -168 -112" } { "classname" "info_player_deathmatch" "angle" "45" "origin" "-1489 -368 -1119" } { "classname" "info_player_deathmatch" "angle" "225" "origin" "-320 -1024 -744" } { "classname" "info_player_deathmatch" "angle" "0" "origin" "-112 -352 224" } { "light" "48" "_color" "1.000000 0.796078 0.003922" "classname" "light" "origin" "592 248 342" } { "light" "48" "_color" "1.000000 0.796078 0.003922" "classname" "light" "origin" "592 208 342" } { "classname" "light" "_color" "1.000000 0.796078 0.003922" "light" "100" "origin" "592 160 334" } { "classname" "info_player_deathmatch" "angle" "270" "origin" "528 1000 200" } { "classname" "info_player_deathmatch" "angle" "225" "origin" "520 1272 280" } { "model" "*64" "classname" "func_wall" "spawnflags" "1792" "_color" "1.000000 0.796078 0.003922" } { "classname" "light" "light" "32" "origin" "-24 856 351" } { "light" "32" "classname" "light" "origin" "24 792 351" } { "classname" "light" "light" "32" "origin" "-24 792 351" } { "light" "32" "classname" "light" "origin" "24 728 351" } { "classname" "light" "light" "32" "origin" "-24 728 351" } { "light" "32" "classname" "light" "origin" "24 664 351" } { "classname" "light" "light" "32" "origin" "-24 664 351" } { "light" "32" "classname" "light" "origin" "24 600 351" } { "classname" "light" "light" "32" "origin" "-24 600 351" } { "light" "100" "classname" "light" "_color" "1.000000 0.796078 0.003922" "origin" "0 904 327" } { "classname" "light" "light" "32" "origin" "24 856 351" } { "light" "32" "classname" "light" "origin" "-24 472 311" } { "classname" "light" "light" "32" "origin" "-24 552 311" } { "light" "32" "classname" "light" "origin" "-104 472 311" } { "classname" "light" "light" "32" "origin" "-104 552 311" } { "light" "32" "classname" "light" "origin" "56 552 311" } { "classname" "light" "light" "32" "origin" "56 472 311" } { "classname" "info_player_deathmatch" "origin" "-344 624 160" } { "classname" "info_player_deathmatch" "angle" "90" "origin" "-240 40 -48" } { "classname" "info_player_deathmatch" "angle" "45" "origin" "-80 488 248" } { "light" "48" "classname" "light" "origin" "-408 -592 -609" } { "classname" "light" "light" "48" "origin" "-488 -592 -609" } { "light" "48" "classname" "light" "origin" "-392 -968 -633" } { "light" "48" "classname" "light" "origin" "-392 -904 -633" } { "light" "48" "classname" "light" "origin" "-504 -904 -633" } { "classname" "light" "light" "48" "origin" "-504 -968 -633" } { "team" "green" "spawnflags" "1792" "origin" "200 1496 288" "classname" "item_invulnerability" } { "origin" "120 1496 264" "target" "t282" "spawnflags" "1793" "wait" "5" "classname" "func_timer" } { "origin" "-448 -556 -1004" "angle" "135" "classname" "info_player_deathmatch" } { "origin" "-204 -368 -632" "angle" "90" "classname" "info_player_deathmatch" } { "origin" "-576 76 408" "angle" "90" "classname" "info_player_deathmatch" } { "origin" "36 -380 -400" "angle" "45" "classname" "info_player_deathmatch" } { "origin" "824 408 296" "angle" "135" "classname" "info_player_deathmatch" } { "model" "*65" "spawnflags" "1792" "classname" "func_wall" } { "origin" "520 772 88" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "-56 760 -104" "angle" "315" "classname" "info_player_deathmatch" } { "origin" "-448 -384 -88" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "-456 -432 216" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "0 312 -208" "angle" "225" "classname" "info_player_deathmatch" } { "origin" "-440 -1040 -848" "spawnflags" "1792" "team" "qbest" "classname" "item_quad" } { "_color" "0.003937 0.476378 1.000000" "origin" "-696 -272 -554" "light" "64" "classname" "light" } { "model" "*66" "spawnflags" "1792" "classname" "func_wall" } { "model" "*67" "target" "t281" "delay" "0.5" "classname" "trigger_multiple" "wait" "15" } { "model" "*68" "target" "t281" "classname" "trigger_multiple" } { "model" "*69" "spawnflags" "1792" "target" "t280" "wait" "1" "classname" "func_button" "angle" "-1" "lip" "4" "health" "5" } { "model" "*70" "targetname" "t280" "spawnflags" "1792" "classname" "func_door" "angle" "180" "wait" "5" "_minlight" "0.2" "sounds" "3" } { "model" "*71" "health" "5" "classname" "func_door" "angle" "-1" "wait" "5" "sounds" "2" "spawnflags" "1792" } { "origin" "600 664 312" "targetname" "t145" "killtarget" "block" "classname" "trigger_relay" } { "model" "*72" "targetname" "block" "spawnflags" "2048" "classname" "func_wall" } { "light" "64" "classname" "light" "origin" "432 1255 304" } { "spawnflags" "2048" "light" "48" "classname" "light" "origin" "480 1287 304" } { "origin" "528 1255 304" "classname" "light" "light" "64" } { "model" "*73" "spawnflags" "2048" "classname" "func_wall" } { "model" "*74" "spawnflags" "2048" "_minlight" "0.8" "classname" "func_wall" } { "model" "*75" "_minlight" "0.2" "spawnflags" "2048" "classname" "func_wall" } { "origin" "-812 -536 -1004" "target" "steps" "spawnflags" "1792" "classname" "trigger_always" } { "model" "*76" "spawnflags" "2048" "_minlight" "0.1" "classname" "func_wall" } { "origin" "-436 -1044 -848" "classname" "light" "light" "80" } { "origin" "-352 -1044 -848" "light" "80" "classname" "light" } { "model" "*77" "health" "5" "wait" "6" "sounds" "3" "spawnflags" "1792" "angle" "-2" "classname" "func_door" "_minlight" "0.1" } { "model" "*78" "health" "5" "_minlight" "0.1" "angle" "-1" "spawnflags" "1792" "classname" "func_door" } { "origin" "-424 -184 -376" "target" "t80" "spawnflags" "1792" "classname" "trigger_always" } { "targetname" "t162" "origin" "-447 -956 52" "noise" "world/klaxon1.wav" "spawnflags" "2050" "classname" "target_speaker" } { "origin" "-448 -344 -24" "noise" "world/amb14.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-560 32 280" "noise" "world/amb2.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-640 -464 248" "noise" "world/amb2.wav" "spawnflags" "1" "classname" "target_speaker" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb2.wav" "origin" "-560 32 -8" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb11.wav" "origin" "-448 -336 248" } { "noise" "World/amb15.wav" "targetname" "pg2" "classname" "target_speaker" "spawnflags" "2049" "origin" "664 504 232" } { "noise" "World/amb15.wav" "targetname" "pg2" "classname" "target_speaker" "spawnflags" "2049" "origin" "536 520 232" } { "noise" "World/amb15.wav" "targetname" "pg2" "classname" "target_speaker" "spawnflags" "2049" "origin" "600 568 232" } { "noise" "World/amb15.wav" "targetname" "pg2" "origin" "584 456 232" "spawnflags" "1" "classname" "target_speaker" } { "targetname" "t279" "origin" "-576 56 424" "classname" "target_explosion" "dmg" "1" "spawnflags" "2048" } { "wait" "5" "origin" "-352 56 424" "target" "t279" "spawnflags" "2049" "classname" "func_timer" } { "origin" "-328 56 424" "targetname" "t279" "dmg" "1" "classname" "target_explosion" "spawnflags" "2048" } { "origin" "-376 136 -120" "classname" "light" "light" "48" "_color" "1.000000 0.796078 0.003922" } { "origin" "-520 136 -120" "classname" "light" "light" "48" "_color" "1.000000 0.796078 0.003922" } { "origin" "-648 136 -120" "classname" "light" "light" "48" "_color" "1.000000 0.796078 0.003922" } { "origin" "-648 248 -120" "classname" "light" "light" "48" "_color" "1.000000 0.796078 0.003922" } { "origin" "-648 360 -120" "classname" "light" "light" "48" "_color" "1.000000 0.796078 0.003922" } { "origin" "-648 472 -120" "classname" "light" "light" "48" "_color" "1.000000 0.796078 0.003922" } { "origin" "-520 472 -120" "classname" "light" "light" "48" "_color" "1.000000 0.796078 0.003922" } { "origin" "-376 472 -120" "classname" "light" "light" "64" "_color" "1.000000 0.796078 0.003922" } { "origin" "-248 472 -120" "classname" "light" "light" "48" "_color" "1.000000 0.796078 0.003922" } { "origin" "-248 360 -120" "classname" "light" "light" "48" "_color" "1.000000 0.796078 0.003922" } { "origin" "-248 136 -120" "_color" "1.000000 0.796078 0.003922" "light" "48" "classname" "light" } { "origin" "-352 584 176" "spawnflags" "2048" "targetname" "t278" "classname" "target_secret" } { "model" "*79" "spawnflags" "2048" "message" "You have found a secret area." "target" "t278" "classname" "trigger_once" } { "origin" "592 848 240" "targetname" "pg2" "spawnflags" "2052" "attenuation" "-1" "noise" "world/comlas1.wav" "classname" "target_speaker" } { "origin" "544 1004 221" "light" "64" "classname" "light" } { "spawnflags" "2048" "origin" "520 956 221" "light" "200" "classname" "light" } { "classname" "target_speaker" "noise" "world/yelforce.wav" "attenuation" "-1" "spawnflags" "2052" "origin" "920 1344 160" "targetname" "t147" } { "classname" "light" "light" "200" "origin" "-336 40 40" "target" "t285" } { "model" "*80" "spawnflags" "2048" "target" "t226" "classname" "trigger_once" } { "spawnflags" "2048" "origin" "-326 -230 243" "targetname" "t277" "classname" "point_combat" } { "origin" "-127 -146 223" "target" "t277" "targetname" "t276" "spawnflags" "1" "angle" "180" "classname" "monster_gunner" } { "spawnflags" "257" "targetname" "t171" "angle" "270" "origin" "-103 -103 223" "classname" "monster_soldier_ss" } { "spawnflags" "769" "targetname" "t171" "angle" "180" "origin" "-149 -104 223" "classname" "monster_soldier_ss" } { "spawnflags" "1" "targetname" "t171" "angle" "270" "origin" "-77 -143 223" "classname" "monster_soldier_ss" } { "spawnflags" "257" "targetname" "gunguy" "origin" "592 280 224" "classname" "monster_gunner" } { "spawnflags" "2048" "origin" "664 600 168" "target" "t273" "targetname" "t272" "classname" "path_corner" } { "spawnflags" "2048" "origin" "528 616 168" "target" "t272" "targetname" "t271" "classname" "path_corner" } { "spawnflags" "2048" "origin" "496 512 184" "wait" "3" "target" "t271" "targetname" "t270" "classname" "path_corner" } { "spawnflags" "2048" "origin" "528 408 168" "target" "t270" "targetname" "t269" "classname" "path_corner" } { "spawnflags" "2048" "origin" "648 400 168" "target" "t269" "targetname" "t268" "classname" "path_corner" } { "spawnflags" "2048" "origin" "704 512 168" "wait" "3" "targetname" "t273" "target" "t268" "classname" "path_corner" } { "origin" "584 384 200" "target" "t269" "classname" "monster_gunner" } { "model" "*81" "spawnflags" "2048" "sounds" "3" "targetname" "t267" "_minlight" "0.2" "wait" "-1" "angle" "180" "classname" "func_door" } { "model" "*82" "spawnflags" "2048" "target" "t267" "health" "5" "lip" "4" "angle" "-1" "classname" "func_button" "wait" "-1" } { "classname" "item_health" "spawnflags" "1024" "origin" "-408 768 -216" } { "spawnflags" "1792" "classname" "weapon_rocketlauncher" "origin" "336 1616 496" } { "spawnflags" "1792" "classname" "weapon_machinegun" "origin" "432 1216 320" } { "spawnflags" "2048" "classname" "item_health" "origin" "480 1216 272" } { "light" "200" "classname" "light" "_color" "1.000000 0.937255 0.705882" "origin" "-644 -468 248" } { "classname" "light" "light" "200" "_color" "1.000000 0.937255 0.705882" "origin" "-244 -468 248" } { "model" "*83" "spawnflags" "2048" "classname" "trigger_multiple" "target" "148" } { "classname" "point_combat" "targetname" "t266" "origin" "-428 -296 -644" } { "target" "t286" "classname" "monster_soldier_ss" "spawnflags" "1" "origin" "-584 -288 -628" "angle" "0" "targetname" "t29" } { "spawnflags" "2048" "classname" "trigger_relay" "targetname" "t239" "target" "t265" "origin" "-176 -336 -648" } { "spawnflags" "2048" "classname" "point_combat" "targetname" "t264" "origin" "-186 -294 -648" "target" "t287" } { "model" "*84" "classname" "func_button" "angle" "0" "sounds" "4" "target" "left_elev" "delay" "1.5" } { "model" "*85" "_minlight" "0.3" "classname" "func_button" "angle" "0" "lip" "2" "target" "left_elev" "sounds" "4" } { "origin" "12 1244 240" "classname" "info_null" "targetname" "t263" } { "_color" "1.000000 0.388235 0.239216" "origin" "304 1240 344" "classname" "light" "light" "80" } { "_color" "1.000000 0.388235 0.239216" "origin" "176 1240 344" "classname" "light" "light" "80" } { "classname" "info_null" "targetname" "t259" "origin" "268 1244 268" } { "origin" "-64 1056 232" "noise" "world/lava1.wav" "spawnflags" "1" "classname" "target_speaker" } { "classname" "light" "_cone" "10" "origin" "140 1244 468" "target" "t261" "light" "250" } { "origin" "460 1244 468" "_cone" "25" "classname" "light" } { "origin" "396 1244 468" "_cone" "25" "classname" "light" } { "origin" "268 1244 468" "_cone" "10" "classname" "light" "target" "t259" "light" "250" } { "origin" "12 1244 468" "_cone" "10" "classname" "light" "target" "t263" "light" "250" } { "spawnflags" "2048" "origin" "480 1303 264" "classname" "light" "light" "48" } { "model" "*86" "classname" "trigger_once" "spawnflags" "2052" "target" "pg2" "targetname" "t257" } { "spawnflags" "2048" "classname" "target_help" "message" "Locate and use exit\n from Palace." "targetname" "pg2" "origin" "588 1000 200" } { "model" "*87" "spawnflags" "2048" "lip" "16" "wait" "-1" "angle" "90" "classname" "func_button" "target" "t1" } { "model" "*88" "classname" "func_wall" "spawnflags" "2055" "targetname" "t250" } { "spawnflags" "2048" "origin" "-416 -1064 -744" "classname" "trigger_relay" "targetname" "t32" "target" "BobnFred" } { "spawnflags" "2048" "classname" "point_combat" "targetname" "t253" "origin" "-352 -1280 -744" } { "spawnflags" "2048" "classname" "point_combat" "targetname" "t254" "origin" "-336 -1232 -744" } { "model" "*89" "targetname" "t80" "speed" "10" "spawnflags" "2" "classname" "func_train" "target" "t252" } { "origin" "-488 -152 -424" "targetname" "t252" "target" "t251" "classname" "path_corner" } { "wait" "-1" "origin" "-488 -168 -424" "target" "t252" "targetname" "t251" "classname" "path_corner" } { "model" "*90" "spawnflags" "2048" "targetname" "t250" "dmg" "10" "classname" "func_explosive" "_minlight" "0.1" } { "model" "*91" "spawnflags" "2048" "target" "t250" "health" "40" "dmg" "50" "classname" "func_explosive" "_minlight" "0.1" } { "spawnflags" "2048" "classname" "target_secret" "targetname" "t249" "origin" "-1379 -632 -1144" } { "model" "*92" "spawnflags" "2048" "classname" "trigger_once" "message" "You have found a secret area." "target" "t249" } { "targetname" "pg2" "origin" "-128 1256 312" "noise" "world/klaxon2.wav" "volume" "7" "spawnflags" "2050" "classname" "target_speaker" } { "targetname" "pg2" "origin" "-32 648 304" "classname" "target_speaker" "spawnflags" "2050" "volume" "7" "noise" "world/klaxon2.wav" } { "origin" "-432 240 128" "volume" ".7" "noise" "world/battle5.wav" "attenuation" "1" "targetname" "t248" "classname" "target_speaker" } { "origin" "-432 264 128" "random" "10" "wait" "20" "target" "t248" "classname" "func_timer" } { "origin" "-304 232 -96" "volume" ".4" "attenuation" "1" "noise" "world/battle5.wav" "targetname" "t247" "classname" "target_speaker" } { "origin" "-304 256 -96" "wait" "15" "random" "5" "target" "t247" "classname" "func_timer" } { "origin" "-440 704 -192" "targetname" "t246" "classname" "target_secret" "spawnflags" "2048" } { "model" "*93" "message" "You have found a secret area." "target" "t246" "classname" "trigger_once" "spawnflags" "2048" } { "model" "*94" "spawnflags" "1024" "classname" "func_wall" } { "origin" "-280 512 -8" "classname" "item_health" "spawnflags" "1792" } { "origin" "-232 512 -8" "classname" "item_health" "spawnflags" "1792" } { "origin" "-688 184 -40" "classname" "ammo_grenades" "spawnflags" "2048" } { "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" "origin" "720 384 72" } { "classname" "light" "light" "32" "_color" "0.284585 0.608696 1.000000" "origin" "648 456 72" } { "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" "origin" "672 432 72" } { "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" "origin" "696 408 72" } { "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" "origin" "464 384 72" } { "classname" "light" "light" "32" "_color" "0.284585 0.608696 1.000000" "origin" "536 456 72" } { "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" "origin" "512 432 72" } { "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" "origin" "488 408 72" } { "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" "origin" "648 568 72" } { "classname" "light" "light" "32" "_color" "0.284585 0.608696 1.000000" "origin" "720 640 72" } { "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" "origin" "696 616 72" } { "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" "origin" "672 592 72" } { "classname" "light" "light" "32" "_color" "0.284585 0.608696 1.000000" "origin" "592 696 72" } { "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" "origin" "592 664 72" } { "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" "origin" "592 632 72" } { "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" "origin" "592 600 72" } { "origin" "592 424 72" "classname" "light" "light" "32" "_color" "0.284585 0.608696 1.000000" } { "origin" "592 392 72" "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" } { "origin" "592 360 72" "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" } { "origin" "592 328 72" "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" } { "origin" "680 512 72" "classname" "light" "light" "32" "_color" "0.284585 0.608696 1.000000" } { "origin" "712 512 72" "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" } { "origin" "744 512 72" "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" } { "origin" "776 512 72" "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" } { "origin" "488 616 72" "classname" "light" "light" "32" "_color" "0.284585 0.608696 1.000000" } { "origin" "512 592 72" "classname" "light" "light" "32" "_color" "0.284585 0.608696 1.000000" } { "origin" "536 568 72" "classname" "light" "light" "32" "_color" "0.284585 0.608696 1.000000" } { "origin" "464 640 72" "_color" "0.284585 0.608696 1.000000" "light" "32" "classname" "light" } { "model" "*95" "spawnflags" "2048" "_minlight" ".6" "targetname" "t80" "sounds" "2" "wait" "-1" "angle" "-1" "classname" "func_door" } { "origin" "-448 -116 -388" "targetname" "t244" "classname" "info_notnull" } { "style" "33" "origin" "-448 -116 -300" "_cone" "30" "light" "200" "targetname" "t245" "target" "t244" "classname" "light" } { "origin" "-664 -296 -616" "delay" "5" "target" "t243" "targetname" "t49" "classname" "trigger_relay" } { "model" "*96" "spawnflags" "2304" "classname" "trigger_once" "target" "t242" "delay" "2" } { "model" "*97" "spawnflags" "2048" "target" "t239" "classname" "trigger_once" } { "item" "item_armor_shard" "origin" "-560 -216 -392" "targetname" "t239" "spawnflags" "1" "angle" "225" "classname" "monster_gunner" } { "spawnflags" "1" "angle" "180" "classname" "monster_gunner" "origin" "-42 278 -212" } { "classname" "monster_gunner" "angle" "90" "spawnflags" "257" "origin" "-428 286 -134" } { "classname" "target_secret" "targetname" "t236" "origin" "28 -500 220" "spawnflags" "2048" } { "model" "*98" "classname" "trigger_once" "target" "t236" "message" "You have found a secret area." "spawnflags" "2048" } { "classname" "info_notnull" "targetname" "t234" "origin" "212 -496 244" } { "style" "34" "classname" "light" "target" "t234" "light" "200" "_cone" "30" "targetname" "t235" "origin" "212 -496 284" } { "classname" "weapon_railgun" "origin" "212 -496 256" "spawnflags" "1792" "team" "tm1" } { "light" "150" "classname" "light" "origin" "148 -496 244" } { "classname" "light" "light" "64" "origin" "100 -504 228" } { "model" "*99" "sounds" "2" "team" "grate" "angle" "270" "classname" "func_door" "lip" "4" } { "model" "*100" "team" "grate" "angle" "90" "classname" "func_door" "lip" "4" } { "model" "*101" "spawnflags" "2048" "targetname" "t243" "classname" "func_explosive" "mass" "700" "dmg" "1" } { "target" "t51" "spawnflags" "1" "classname" "monster_chick" "angle" "0" "origin" "-200 -128 -616" "targetname" "t265" } { "targetname" "t239" "origin" "-180 -376 -632" "classname" "monster_gunner" "angle" "90" "spawnflags" "1" "target" "t264" } { "classname" "ammo_bullets" "origin" "-498 -220 -968" "spawnflags" "1792" } { "spawnflags" "2048" "classname" "item_adrenaline" "origin" "-440 -1040 -896" } { "classname" "point_combat" "targetname" "t26" "target" "t233" "origin" "-328 -1256 -872" } { "model" "*102" "targetname" "t29" "classname" "func_door" "angle" "-2" "spawnflags" "2048" "sounds" "3" "wait" "-1" "_minlight" "0.1" } { "classname" "item_health_small" "origin" "-460 -1284 -864" } { "classname" "item_health_small" "origin" "-508 -1284 -848" } { "classname" "item_health_small" "origin" "-548 -1284 -832" } { "classname" "item_health_small" "origin" "-588 -1284 -824" } { "model" "*103" "spawnflags" "2048" "target" "t121" "classname" "trigger_once" } { "targetname" "t121" "origin" "-256 704 -104" "angle" "270" "spawnflags" "1" "classname" "monster_soldier" } { "targetname" "t121" "origin" "-240 648 -104" "angle" "270" "spawnflags" "1" "classname" "monster_soldier" } { "spawnflags" "1792" "classname" "weapon_rocketlauncher" "origin" "-1376 -392 -1200" } { "_color" "0.567460 1.000000 0.250000" "light" "100" "classname" "light" "origin" "-1264 -376 -1224" } { "_color" "0.567460 1.000000 0.250000" "light" "100" "classname" "light" "origin" "-1176 -352 -1224" } { "classname" "light" "light" "100" "_color" "0.567460 1.000000 0.250000" "origin" "-1112 -352 -1224" } { "classname" "light" "light" "100" "_color" "0.567460 1.000000 0.250000" "origin" "-1336 -376 -1224" } { "classname" "target_laser" "angle" "-1" "spawnflags" "81" "origin" "-1183 -547 -1060" } { "classname" "item_armor_combat" "origin" "-1304 -392 -1072" "spawnflags" "1792" "team" "head" } { "noise" "world/lava1.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "200 1480 224" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/lava1.wav" "origin" "-220 1338 232" } { "spawnflags" "2048" "origin" "648 736 264" "targetname" "pg2" "classname" "target_goal" } { "spawnflags" "2048" "origin" "692 716 256" "targetname" "t162" "classname" "target_goal" } { "spawnflags" "1" "targetname" "pg2" "classname" "target_speaker" "origin" "588 480 80" "noise" "world/pump2.wav" } { "spawnflags" "2049" "targetname" "com2" "noise" "world/pump2.wav" "origin" "584 528 80" "classname" "target_speaker" } { "origin" "912 1440 120" "spawnflags" "1" "noise" "world/comp_hum2.wav" "classname" "target_speaker" } { "style" "5" "targetname" "t230" "classname" "func_areaportal" } { "origin" "-404 -176 -408" "classname" "item_health" } { "deathtarget" "gunguy" "origin" "496 48 224" "spawnflags" "257" "angle" "180" "classname" "monster_chick" } { "origin" "163 -273 -161" "classname" "target_speaker" "spawnflags" "1" "noise" "world/pump1.wav" } { "origin" "99 -273 95" "noise" "world/pump1.wav" "spawnflags" "1" "classname" "target_speaker" } { "model" "*104" "sounds" "3" "spawnflags" "8" "classname" "func_door" "angle" "270" "team" "portal2" "lip" "16" "wait" "4" "_minlight" "0.2" } { "model" "*105" "spawnflags" "8" "target" "t230" "classname" "func_door" "angle" "90" "team" "portal2" "lip" "16" "wait" "4" "_minlight" "0.2" } { "classname" "ammo_bullets" "origin" "848 448 80" } { "classname" "ammo_bullets" "origin" "816 384 80" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/pump1.wav" "origin" "-452 -732 -744" } { "origin" "-456 -1056 -752" "classname" "target_speaker" "spawnflags" "1" "noise" "world/pump2.wav" } { "origin" "-452 -484 -656" "noise" "world/pump1.wav" "spawnflags" "1" "classname" "target_speaker" } { "volume" "0.3" "attenuation" "1" "targetname" "t229" "noise" "world/battle4.wav" "origin" "-424 352 464" "classname" "target_speaker" } { "target" "t229" "random" "10" "wait" "60" "spawnflags" "1" "classname" "func_timer" "origin" "-416 280 496" } { "origin" "-448 152 496" "targetname" "t226" "target" "t228" "classname" "func_timer" "spawnflags" "0" "wait" "60" "random" "10" } { "attenuation" "1" "volume" "0.6" "targetname" "t228" "classname" "target_speaker" "origin" "-456 208 496" "noise" "world/battle4.wav" } { "origin" "-328 104 304" "target" "t227" "random" "10" "wait" "60" "spawnflags" "1" "classname" "func_timer" } { "noise" "world/battle4.wav" "targetname" "t227" "origin" "-336 48 304" "classname" "target_speaker" } { "targetname" "t225" "origin" "-552 328 560" "attenuation" "1" "volume" "1" "noise" "world/battle5.wav" "classname" "target_speaker" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb14.wav" "origin" "-447 -953 126" } { "origin" "-256 -464 248" "noise" "world/amb2.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-344 -224 16" "spawnflags" "2049" "targetname" "reddoor1" "noise" "world/force1.wav" "classname" "target_speaker" } { "origin" "-576 -224 16" "noise" "world/force1.wav" "spawnflags" "2049" "targetname" "reddoor2" "classname" "target_speaker" } { "origin" "256 -324 224" "noise" "world/force2.wav" "spawnflags" "2048" "classname" "target_speaker" "targetname" "pg1" "attenuation" "3" "volume" "0.3" } { "classname" "trigger_relay" "origin" "-536 -200 -632" "targetname" "t210" "delay" "1" "target" "t211" } { "classname" "target_speaker" "noise" "world/drip3.wav" "attenuation" "1" "origin" "-568 -184 -712" "targetname" "t211" } { "classname" "func_timer" "target" "t210" "spawnflags" "1" "wait" "2" "random" "1.5" "origin" "-504 -184 -632" } { "attenuation" "2" "classname" "target_speaker" "noise" "world/drip1.wav" "targetname" "t210" "volume" "0.6" "origin" "-536 -184 -664" } { "classname" "target_speaker" "origin" "-376 -152 -600" "spawnflags" "1" "noise" "world/drip_amb.wav" } { "origin" "-464 -236 -1008" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "noise" "world/pump1.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "216 -324 -648" } { "noise" "world/pump1.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "112 -324 -648" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/pump1.wav" "origin" "160 -480 -552" } { "noise" "world/amb7.wav" "classname" "target_speaker" "origin" "-368 -288 -316" } { "noise" "world/amb7.wav" "classname" "target_speaker" "origin" "-272 -136 -320" } { "classname" "target_speaker" "noise" "world/amb7.wav" "origin" "-584 -288 -344" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "-444 -176 -392" } { "classname" "target_speaker" "noise" "world/water1.wav" "spawnflags" "1" "origin" "-764 -512 -1020" } { "classname" "func_timer" "spawnflags" "1" "wait" "4" "random" "1.3" "target" "t207" "origin" "-536 -1096 -648" } { "volume" "0.4" "attenuation" "2" "noise" "world/l_hum2.wav" "classname" "target_speaker" "targetname" "t204" "origin" "-576 -1056 -648" } { "classname" "target_speaker" "noise" "world/spark1.wav" "attenuation" "2" "volume" "0.3" "targetname" "t205" "origin" "-576 -1056 -648" } { "classname" "trigger_relay" "target" "t205" "origin" "-468 -1056 -648" "targetname" "t206" "delay" "1.2" } { "classname" "target_splash" "sounds" "1" "angle" "180" "targetname" "t205" "origin" "-576 -1056 -648" } { "classname" "trigger_relay" "target" "t204" "targetname" "t200" "delay" "1" "origin" "-540 -1132 -648" } { "classname" "target_splash" "angle" "225" "sounds" "1" "targetname" "t204" "origin" "-576 -1056 -648" } { "origin" "-508 -1024 -648" "classname" "trigger_relay" "targetname" "t206" "target" "t202" } { "classname" "trigger_relay" "origin" "-536 -1064 -648" "target" "t206" "targetname" "t207" } { "classname" "trigger_relay" "origin" "-492 -1080 -648" "delay" "1.2" "targetname" "t206" "target" "t202" } { "style" "35" "spawnflags" "1" "origin" "-576 -1056 -648" "classname" "light" "light" "100" "targetname" "t202" } { "classname" "trigger_relay" "targetname" "t201" "target" "t199" "origin" "-560 -1104 -648" } { "classname" "trigger_relay" "targetname" "t201" "delay" "1" "target" "t199" "origin" "-592 -1112 -648" } { "classname" "trigger_relay" "targetname" "t200" "target" "t201" "origin" "-572 -1144 -648" } { "classname" "func_timer" "target" "t200" "origin" "-556 -1208 -648" "spawnflags" "1" "wait" "3" "random" "1.9" } { "classname" "func_timer" "target" "t198" "wait" "3" "random" "2.5" "origin" "-184 -1244 -812" "spawnflags" "1" } { "origin" "-216 -1268 -812" "noise" "world/drip1.wav" "classname" "target_speaker" "targetname" "t198" "attenuation" "1" "volume" "0.3" } { "classname" "func_timer" "origin" "-232 -1252 -812" "target" "t197" "wait" "5" "spawnflags" "1" } { "classname" "target_speaker" "noise" "world/drip_amb.wav" "origin" "-228 -1172 -812" "targetname" "t197" "attenuation" "2" "volume" "0.4" } { "classname" "target_speaker" "spawnflags" "2048" "noise" "world/force1.wav" "origin" "256 -324 240" "targetname" "pg1" } { "classname" "target_speaker" "spawnflags" "2049" "noise" "world/force1.wav" "origin" "912 1300 116" "targetname" "pg1" } { "classname" "target_speaker" "spawnflags" "2049" "noise" "world/force1.wav" "origin" "144 516 268" "targetname" "pg2" } { "noise" "world/force1.wav" "spawnflags" "2049" "classname" "target_speaker" "targetname" "pg1" "origin" "-488 -400 -988" } { "classname" "target_speaker" "spawnflags" "2049" "noise" "world/force1.wav" "targetname" "pg2" "origin" "-152 672 268" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/lava1.wav" "origin" "-396 676 -216" } { "style" "6" "classname" "func_areaportal" "targetname" "t196" } { "model" "*106" "classname" "func_door" "angle" "90" "sounds" "1" "team" "short_red" "_minlight" "0.2" } { "model" "*107" "classname" "func_door" "angle" "270" "sounds" "2" "team" "short_red" "target" "t196" "_minlight" "0.2" } { "classname" "info_player_coop" "angle" "180" "targetname" "city3NL" "origin" "-112 -1260 -888" } { "spawnflags" "2048" "classname" "target_speaker" "origin" "-343 -38 287" "targetname" "fldsnd1" "noise" "world/force3.wav" "attenuation" "3" "volume" "1" } { "classname" "target_speaker" "noise" "world/force1.wav" "spawnflags" "2049" "targetname" "pg1" "origin" "-336 -32 296" } { "origin" "-192 276 -160" "light" "64" "classname" "light" } { "origin" "-256 276 -144" "light" "56" "classname" "light" } { "origin" "-320 276 -128" "light" "56" "classname" "light" } { "origin" "-384 276 -112" "light" "56" "classname" "light" } { "origin" "-448 276 -112" "light" "56" "classname" "light" } { "origin" "-448 660 -80" "classname" "light" "light" "64" } { "origin" "-448 596 -80" "classname" "light" "light" "56" } { "origin" "-448 532 -80" "classname" "light" "light" "56" } { "origin" "-448 468 -80" "classname" "light" "light" "56" } { "origin" "-448 404 -80" "classname" "light" "light" "56" } { "origin" "-448 340 -96" "classname" "light" "light" "56" } { "origin" "-128 276 -160" "classname" "light" "light" "64" } { "origin" "-384 672 -80" "light" "64" "classname" "light" } { "origin" "-264 404 -112" "classname" "ammo_shells" } { "origin" "-264 456 -112" "classname" "ammo_shells" } { "origin" "-288 672 -124" "targetname" "t190" "classname" "info_notnull" } { "origin" "-288 672 19" "_color" "1.000000 0.889764 0.279528" "light" "400" "_cone" "20" "target" "t190" "classname" "light" } { "origin" "-224 672 -124" "targetname" "t191" "classname" "info_null" } { "origin" "-224 672 19" "_color" "1.000000 0.889764 0.279528" "light" "400" "_cone" "20" "target" "t191" "classname" "light" } { "origin" "-160 672 -124" "targetname" "t192" "classname" "info_null" } { "origin" "-160 672 19" "_color" "1.000000 0.889764 0.279528" "light" "400" "_cone" "20" "target" "t192" "classname" "light" } { "origin" "-352 672 -124" "targetname" "t189" "classname" "info_null" } { "origin" "-352 672 19" "_color" "1.000000 0.889764 0.279528" "light" "400" "_cone" "20" "target" "t189" "classname" "light" } { "spawnflags" "2048" "origin" "-392 -396 -92" "classname" "ammo_shells" } { "classname" "ammo_bullets" "origin" "-328 -472 208" } { "classname" "ammo_bullets" "origin" "-272 -448 208" } { "noise" "world/klaxon2.wav" "spawnflags" "2050" "classname" "target_speaker" "origin" "-448 -448 272" "targetname" "pg2" } { "origin" "-440 280 408" "classname" "target_speaker" "spawnflags" "2" "volume" "7" "noise" "world/klaxon2.wav" "targetname" "pg2" } { "targetname" "pg2" "origin" "-56 -344 272" "classname" "target_speaker" "spawnflags" "2" "volume" "7" "noise" "world/klaxon2.wav" } { "origin" "360 -152 360" "classname" "target_speaker" "spawnflags" "2" "volume" "7" "noise" "world/klaxon2.wav" } { "origin" "616 520 360" "classname" "target_speaker" "spawnflags" "2050" "volume" "7" "noise" "world/klaxon2.wav" } { "noise" "world/klaxon2.wav" "volume" "7" "spawnflags" "2050" "classname" "target_speaker" "targetname" "pg2" "origin" "536 588 304" } { "noise" "world/klaxon2.wav" "volume" "7" "spawnflags" "2" "classname" "target_speaker" "origin" "-448 -464 -24" "targetname" "pg2" } { "classname" "target_speaker" "spawnflags" "2050" "volume" "7" "noise" "world/klaxon2.wav" "targetname" "pg2" "origin" "264 1464 312" } { "classname" "light" "origin" "-184 216 -40" "_cone" "20" "target" "t186" "light" "325" } { "classname" "info_notnull" "origin" "-176 216 120" "targetname" "t186" } { "classname" "info_notnull" "origin" "-176 144 120" "targetname" "t187" } { "light" "325" "classname" "light" "origin" "-184 144 -48" "_cone" "20" "target" "t187" } { "classname" "light" "origin" "-184 72 -48" "_cone" "20" "target" "t188" "light" "325" } { "classname" "info_notnull" "origin" "-176 72 120" "targetname" "t188" } { "origin" "-184 328 -40" "classname" "light" "_cone" "20" "target" "t185" "light" "325" } { "origin" "-176 328 120" "classname" "info_notnull" "targetname" "t185" } { "origin" "-176 400 120" "classname" "info_notnull" "targetname" "t178" } { "light" "325" "origin" "-184 400 -48" "classname" "light" "target" "t178" "_cone" "20" } { "origin" "-184 472 -48" "classname" "light" "target" "t177" "_cone" "20" "light" "325" } { "origin" "-176 472 120" "classname" "info_notnull" "targetname" "t177" } { "origin" "-720 216 120" "classname" "info_notnull" "targetname" "t181" } { "origin" "-720 144 120" "classname" "info_notnull" "targetname" "t180" } { "origin" "-720 72 120" "classname" "info_notnull" "targetname" "t179" } { "origin" "-712 216 -48" "classname" "light" "_cone" "20" "target" "t181" "light" "325" } { "light" "325" "origin" "-712 144 -48" "classname" "light" "_cone" "20" "target" "t180" } { "origin" "-712 72 -48" "classname" "light" "_cone" "20" "target" "t179" "light" "325" } { "classname" "info_notnull" "origin" "-720 400 120" "targetname" "t183" } { "classname" "info_notnull" "origin" "-720 328 120" "targetname" "t182" } { "classname" "info_notnull" "origin" "-720 472 120" "targetname" "t184" } { "light" "325" "classname" "light" "origin" "-712 400 -48" "_cone" "20" "target" "t183" } { "classname" "light" "origin" "-712 472 -48" "_cone" "20" "target" "t184" "light" "325" } { "classname" "light" "origin" "-712 328 -48" "_cone" "20" "target" "t182" "light" "325" } { "style" "7" "classname" "func_areaportal" "targetname" "t176" } { "origin" "-328 -284 -16" "targetname" "t173" "target" "reddoor1" "classname" "trigger_relay" } { "spawnflags" "2048" "origin" "-328 -292 -48" "target" "t174" "targetname" "t173" "classname" "trigger_relay" } { "origin" "-48 -64 -116" "spawnflags" "2" "targetname" "t174" "classname" "monster_soldier_light" "angle" "180" } { "origin" "-20 -128 -116" "spawnflags" "2" "targetname" "t174" "classname" "monster_soldier_light" "angle" "180" } { "origin" "-100 -108 -100" "spawnflags" "2" "targetname" "t174" "angle" "180" "classname" "monster_soldier_light" } { "model" "*108" "sounds" "4" "team" "paldoor1" "angle" "270" "classname" "func_door" "target" "t176" "_minlight" "0.2" "spawnflags" "8" } { "model" "*109" "team" "paldoor1" "angle" "90" "classname" "func_door" "_minlight" "0.2" "spawnflags" "8" } { "model" "*110" "classname" "func_door" "angle" "270" "team" "comexit" "sounds" "3" "target" "t175" "_minlight" "0.2" } { "model" "*111" "classname" "func_door" "angle" "90" "team" "comexit" "_minlight" "0.2" } { "style" "8" "classname" "func_areaportal" "targetname" "t175" } { "light" "100" "classname" "light" "origin" "536 440 104" } { "light" "100" "classname" "light" "origin" "680 440 104" } { "light" "100" "classname" "light" "origin" "664 600 104" } { "light" "100" "classname" "light" "origin" "600 632 104" } { "light" "100" "classname" "light" "origin" "584 408 104" } { "light" "100" "classname" "light" "origin" "488 520 104" } { "light" "100" "classname" "light" "origin" "728 520 104" } { "classname" "light" "light" "100" "origin" "520 600 104" } { "spawnflags" "2048" "classname" "target_explosion" "dmg" "0" "targetname" "t56" "origin" "-384 -304 -892" } { "light" "125" "_color" "1.000000 0.774704 0.304348" "classname" "light" "origin" "280 -320 294" } { "classname" "info_null" "targetname" "t169" "origin" "104 -440 -572" } { "classname" "light" "origin" "104 -440 -488" "target" "t169" "_cone" "20" } { "classname" "info_null" "targetname" "t168" "origin" "216 -440 -572" } { "classname" "light" "target" "t168" "origin" "216 -440 -488" "_cone" "20" } { "spawnflags" "2048" "classname" "target_secret" "message" "You have found a secret." "targetname" "t167" "origin" "-376 -328 -808" } { "light" "80" "classname" "light" "origin" "-440 -264 -600" } { "classname" "light" "light" "80" "origin" "-376 -264 -600" } { "light" "80" "classname" "light" "origin" "-568 -264 -600" } { "classname" "light" "light" "80" "origin" "-504 -264 -600" } { "classname" "light" "light" "80" "origin" "-312 -264 -600" } { "origin" "-336 -1216 -696" "light" "60" "classname" "light" } { "origin" "-336 -1152 -696" "light" "60" "classname" "light" } { "origin" "-336 -1088 -696" "light" "60" "classname" "light" } { "origin" "-384 -1040 -680" "light" "64" "classname" "light" } { "origin" "-448 -1040 -696" "light" "64" "classname" "light" } { "origin" "-512 -1040 -712" "light" "64" "classname" "light" } { "origin" "-560 -1064 -728" "light" "64" "classname" "light" } { "origin" "-560 -1096 -744" "light" "64" "classname" "light" } { "origin" "-568 -1144 -760" "light" "64" "classname" "light" } { "origin" "-568 -1200 -776" "light" "64" "classname" "light" } { "origin" "-568 -1248 -792" "light" "64" "classname" "light" } { "origin" "-520 -1264 -808" "light" "64" "classname" "light" } { "origin" "-472 -1264 -824" "light" "64" "classname" "light" } { "origin" "-424 -1264 -840" "light" "64" "classname" "light" } { "origin" "-376 -1264 -856" "light" "64" "classname" "light" } { "light" "80" "classname" "light" "origin" "-328 -1264 -856" } { "light" "90" "classname" "light" "origin" "-232 -1216 -856" } { "light" "90" "classname" "light" "origin" "-168 -1216 -856" } { "light" "90" "classname" "light" "origin" "-104 -1232 -856" } { "light" "90" "classname" "light" "origin" "-40 -1248 -856" } { "classname" "light" "light" "60" "origin" "-336 -1264 -696" } { "spawnflags" "2048" "origin" "700 752 272" "message" "Reprogram Data Spinner\nat Central Computer." "targetname" "t162" "classname" "target_help" } { "spawnflags" "2048" "origin" "-4 -264 -632" "angle" "90" "classname" "misc_insane" } { "origin" "336 64 232" "target" "t142" "classname" "monster_chick" } { "model" "*112" "target" "t145" "targetname" "t156" "spawnflags" "2052" "classname" "trigger_once" } { "origin" "632 680 256" "targetname" "t162" "spawnflags" "2056" "classname" "target_crosslevel_trigger" } { "spawnflags" "2048" "origin" "528 956 256" "delay" "2" "targetname" "t163" "item" "key_data_spinner" "classname" "trigger_key" "target" "t257" } { "targetname" "t161" "origin" "592 664 192" "classname" "info_null" } { "light" "150" "target" "t161" "origin" "592 664 284" "classname" "light" } { "spawnflags" "2048" "target" "t162" "origin" "592 664 196" "classname" "key_data_cd" } { "origin" "848 1312 80" "classname" "ammo_cells" "spawnflags" "2048" } { "origin" "856 1352 80" "classname" "ammo_cells" "spawnflags" "2048" } { "origin" "912 1392 80" "classname" "item_adrenaline" "spawnflags" "2048" } { "spawnflags" "0" "origin" "848 1272 80" "classname" "ammo_cells" } { "model" "*113" "spawnflags" "2048" "target" "t145" "classname" "trigger_once" } { "spawnflags" "2048" "classname" "item_health" "origin" "-72 736 -112" } { "spawnflags" "2048" "origin" "-72 768 -112" "classname" "ammo_cells" } { "classname" "item_health" "origin" "-152 536 -216" } { "classname" "item_health" "origin" "-184 576 -216" } { "model" "*114" "classname" "func_wall" "spawnflags" "2816" } { "model" "*115" "classname" "func_wall" "spawnflags" "1536" } { "model" "*116" "classname" "func_wall" "spawnflags" "1280" } { "model" "*117" "classname" "func_wall" "spawnflags" "3584" } { "origin" "140 1244 264" "classname" "info_null" "targetname" "t261" } { "classname" "light" "_cone" "25" "origin" "524 1244 468" } { "origin" "944 1308 168" "spawnflags" "2050" "targetname" "pg1" "classname" "target_crosslevel_trigger" } { "origin" "624 602 280" "spawnflags" "2076" "target" "t156" "classname" "target_crosslevel_target" } { "classname" "light" "light" "80" "_color" "1.000000 1.000000 0.874510" "origin" "-448 -840 120" } { "origin" "-448 -840 112" "_color" "1.000000 1.000000 1.000000" "light" "300" "classname" "light" "_cone" "20" "target" "t154" } { "origin" "-448 -840 -100" "classname" "info_notnull" "targetname" "t154" } { "classname" "light" "light" "80" "_color" "1.000000 1.000000 0.874510" "origin" "-448 -756 120" } { "origin" "-448 -756 112" "_color" "1.000000 1.000000 1.000000" "light" "300" "classname" "light" "_cone" "20" "target" "t155" } { "origin" "-448 -756 -100" "classname" "info_notnull" "targetname" "t155" } { "origin" "-456 620 160" "classname" "item_armor_combat" } { "target" "t153" "_color" "1.000000 0.255144 0.004115" "origin" "-216 600 -152" "light" "200" "classname" "light" } { "targetname" "t153" "origin" "-216 600 -232" "classname" "info_notnull" } { "target" "t151" "classname" "light" "light" "200" "origin" "-152 600 -152" "_color" "1.000000 0.255144 0.004115" } { "targetname" "t151" "classname" "info_notnull" "origin" "-152 600 -232" } { "target" "t152" "classname" "light" "light" "200" "origin" "-344 600 -152" "_color" "1.000000 0.255144 0.004115" } { "targetname" "t152" "classname" "info_notnull" "origin" "-344 600 -228" } { "origin" "-280 600 -232" "targetname" "t150" "classname" "info_notnull" } { "_color" "1.000000 0.255144 0.004115" "origin" "-280 600 -152" "light" "200" "target" "t150" "classname" "light" } { "model" "*118" "target" "t149" "classname" "trigger_multiple" } { "origin" "-280 600 -168" "classname" "item_health_mega" "spawnflags" "1792" } { "spawnflags" "1792" "origin" "-456 584 184" "classname" "ammo_shells" } { "spawnflags" "0" "origin" "-392 704 160" "classname" "ammo_slugs" } { "origin" "-352 704 160" "classname" "item_health_large" } { "origin" "-512 -124 -984" "classname" "item_health" } { "origin" "-568 -152 -1000" "spawnflags" "2064" "classname" "misc_deadsoldier" } { "origin" "-312 -1252 -692" "classname" "item_bandolier" "spawnflags" "1792" } { "origin" "-308 -1292 -732" "classname" "item_health" } { "origin" "-588 -1224 -812" "classname" "item_health_small" } { "origin" "-412 -1284 -880" "classname" "item_health_small" } { "origin" "-592 -1156 -800" "classname" "item_health_small" } { "model" "*119" "target" "pg1" "targetname" "t147" "classname" "trigger_once" "spawnflags" "2048" "message" "Yellow force fields deactivated." } { "classname" "target_speaker" "targetname" "com5" "noise" "world/electro.wav" "origin" "592 512 360" } { "spawnflags" "2048" "classname" "target_speaker" "targetname" "com7" "origin" "592 520 360" "noise" "world/spark2.wav" } { "spawnflags" "2048" "classname" "target_speaker" "targetname" "com6" "noise" "world/spark2.wav" "origin" "592 504 360" } { "classname" "target_speaker" "targetname" "com3" "noise" "world/spark1.wav" "origin" "592 512 360" } { "classname" "target_speaker" "targetname" "com4" "noise" "world/electro.wav" "origin" "592 512 360" } { "spawnflags" "2048" "origin" "584 504 360" "classname" "target_explosion" "targetname" "com2" } { "spawnflags" "2048" "origin" "600 512 376" "classname" "target_explosion" "targetname" "com7" } { "classname" "target_explosion" "origin" "600 512 312" "targetname" "com5" } { "spawnflags" "2048" "classname" "trigger_relay" "target" "com7" "origin" "680 384 328" "targetname" "pg2" "delay" "3.5" } { "spawnflags" "2048" "classname" "trigger_relay" "target" "com6" "origin" "648 384 328" "targetname" "pg2" "delay" "3.25" } { "spawnflags" "2048" "classname" "trigger_relay" "target" "com5" "origin" "616 384 328" "targetname" "pg2" "delay" "3" } { "spawnflags" "2048" "classname" "trigger_relay" "target" "com4" "origin" "584 384 328" "targetname" "pg2" "delay" "2.75" } { "spawnflags" "2048" "classname" "trigger_relay" "target" "com3" "origin" "552 384 328" "targetname" "pg2" "delay" "2.5" } { "spawnflags" "2048" "classname" "trigger_relay" "target" "com2" "origin" "520 384 328" "targetname" "pg2" "delay" "2" } { "spawnflags" "2048" "classname" "trigger_relay" "target" "com1" "origin" "488 384 328" "targetname" "pg2" "delay" "1" } { "classname" "trigger_relay" "target" "com8" "origin" "712 384 328" "targetname" "pg2" "delay" "4.25" } { "model" "*120" "classname" "func_explosive" "targetname" "com8" "dmg" "50" "mass" "300" } { "attenuation" "3" "volume" "3" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb12.wav" "targetname" "com2" "origin" "608 512 336" } { "model" "*121" "spawnflags" "2048" "target" "t163" "classname" "trigger_once" "message" "Warning. Communication laser shut down." } { "classname" "light" "light" "64" "origin" "544 916 221" } { "model" "*122" "sounds" "4" "spawnflags" "2089" "classname" "func_door" "angle" "-2" "lip" "128" "targetname" "t145" "wait" "-1" "_minlight" "0.1" } { "model" "*123" "spawnflags" "2048" "classname" "func_wall" } { "classname" "path_corner" "origin" "328 -160 216" "targetname" "t143" "target" "t144" } { "spawnflags" "2048" "classname" "path_corner" "targetname" "t139" "target" "t140" "origin" "328 -160 216" } { "spawnflags" "2048" "classname" "path_corner" "targetname" "t140" "target" "t141" "origin" "328 40 216" } { "spawnflags" "2048" "classname" "path_corner" "targetname" "t141" "target" "t142" "origin" "376 40 216" } { "spawnflags" "2048" "classname" "path_corner" "targetname" "t142" "origin" "328 40 216" "target" "t143" } { "spawnflags" "2048" "classname" "path_corner" "target" "t139" "origin" "28 -136 216" "targetname" "t144" } { "classname" "item_health" "origin" "840 600 80" } { "classname" "item_health" "origin" "816 640 80" } { "classname" "item_health" "origin" "656 752 80" } { "classname" "target_secret" "targetname" "t138" "message" "You have found a secret area." "origin" "-312 -8 -56" } { "model" "*124" "classname" "trigger_once" "target" "t138" } { "light" "200" "classname" "light" "origin" "-544 392 392" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-440 360 392" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-440 408 392" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-336 392 392" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-336 168 392" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-440 200 392" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-544 200 392" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-544 360 520" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-544 408 520" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-440 360 520" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-440 408 520" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-336 360 520" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-336 408 520" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-336 168 520" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-440 168 520" "_color" "1.000000 0.976471 0.501961" } { "light" "200" "classname" "light" "origin" "-544 168 520" "_color" "1.000000 0.976471 0.501961" } { "classname" "item_health" "origin" "-456 96 -56" "spawnflags" "2048" } { "classname" "monster_tank_commander" "angle" "270" "origin" "4 964 244" "targetname" "pg2" "spawnflags" "1" } { "model" "*125" "spawnflags" "2048" "classname" "trigger_once" "target" "t134" } { "origin" "120 1612 324" "classname" "monster_floater" "spawnflags" "769" "item" "ammo_cells" } { "origin" "240 1424 384" "classname" "monster_floater" "spawnflags" "1" "item" "ammo_cells" } { "angle" "180" "classname" "monster_chick" "spawnflags" "1" "origin" "139 1242 284" "targetname" "t134" } { "classname" "ammo_bullets" "origin" "-372 32 -16" "spawnflags" "1792" } { "classname" "weapon_chaingun" "origin" "-332 40 -16" "spawnflags" "1792" } { "classname" "ammo_bullets" "origin" "-296 28 -16" "spawnflags" "1792" } { "classname" "misc_deadsoldier" "spawnflags" "2049" "angle" "45" "origin" "-444 -408 -112" } { "origin" "-448 520 228" "classname" "info_null" "targetname" "t124" } { "origin" "-448 520 375" "_cone" "20" "classname" "light" "target" "t124" } { "origin" "-448 520 383" "light" "150" "classname" "light" } { "classname" "monster_soldier" "targetname" "t121" "spawnflags" "257" "angle" "270" "origin" "-200 704 -104" } { "classname" "monster_soldier" "targetname" "t121" "spawnflags" "769" "angle" "270" "origin" "-312 680 -104" } { "classname" "monster_soldier" "targetname" "t121" "spawnflags" "1" "angle" "270" "origin" "-184 648 -104" } { "spawnflags" "2048" "classname" "trigger_relay" "origin" "-464 232 -128" "targetname" "t226" "target" "t121" } { "model" "*126" "spawnflags" "2048" "classname" "trigger_once" "target" "t119" } { "classname" "monster_floater" "origin" "-1124 -344 -1072" "spawnflags" "258" "targetname" "t119" } { "angle" "0" "classname" "monster_soldier_light" "spawnflags" "3" "targetname" "pg1" "origin" "-700 -518 -998" } { "angle" "0" "classname" "monster_gunner" "spawnflags" "259" "targetname" "pg1" "origin" "-804 -480 -998" } { "angle" "45" "classname" "monster_soldier_light" "spawnflags" "3" "targetname" "pg1" "origin" "-620 -528 -998" } { "classname" "target_secret" "targetname" "t109" "message" "You have found a secret area." "origin" "-1246 -392 -1086" } { "model" "*127" "classname" "trigger_once" "target" "t109" } { "spawnflags" "1792" "classname" "item_health" "origin" "48 768 -112" } { "classname" "misc_deadsoldier" "angle" "315" "spawnflags" "2064" "origin" "-111 759 -128" } { "spawnflags" "2048" "classname" "path_corner" "targetname" "t104" "target" "t105" "wait" "1" "origin" "-576 168 -120" } { "spawnflags" "2048" "classname" "path_corner" "targetname" "t105" "target" "t106" "origin" "-592 168 -120" } { "spawnflags" "2048" "classname" "path_corner" "targetname" "t103" "target" "t104" "origin" "-576 400 -120" } { "spawnflags" "2048" "classname" "path_corner" "target" "t103" "targetname" "t106" "wait" "1" "origin" "-592 400 -120" } { "classname" "monster_chick" "target" "t103" "item" "ammo_rockets" "origin" "-560 416 -104" "spawnflags" "1" } { "classname" "monster_soldier_ss" "origin" "-632 316 -104" "angle" "0" "spawnflags" "1" } { "classname" "monster_soldier_ss" "origin" "-632 252 -104" "angle" "0" "item" "ammo_shells" "spawnflags" "1" } { "classname" "monster_soldier_ss" "origin" "-632 192 -104" "spawnflags" "769" } { "classname" "monster_soldier_ss" "origin" "-632 380 -104" "angle" "0" "item" "ammo_shells" "spawnflags" "257" } { "origin" "-476 32 468" "classname" "info_null" "targetname" "t102" } { "origin" "-448 32 480" "classname" "info_null" "targetname" "t101" } { "classname" "info_null" "origin" "-420 32 468" "targetname" "t100" } { "_cone" "5" "light" "200" "classname" "light" "origin" "-448 40 388" "target" "t101" } { "_cone" "5" "light" "200" "classname" "light" "target" "t100" "origin" "-464 40 388" } { "_cone" "5" "classname" "light" "light" "200" "origin" "-432 40 388" "target" "t102" } { "classname" "info_notnull" "targetname" "t99" "origin" "-448 520 -124" } { "classname" "light" "light" "150" "origin" "-448 520 63" } { "classname" "light" "target" "t99" "_cone" "20" "origin" "-448 520 55" } { "classname" "monster_chick" "angle" "180" "origin" "-196 704 256" "spawnflags" "3" "targetname" "t257" } { "classname" "monster_chick" "angle" "180" "origin" "-192 632 256" "spawnflags" "3" "targetname" "t257" } { "classname" "monster_tank_commander" "angle" "90" "origin" "-452 616 256" "item" "ammo_rockets" "spawnflags" "1" } { "classname" "func_group" } { "origin" "-208 -464 -584" "classname" "weapon_shotgun" "spawnflags" "1792" } { "origin" "-1183 -299 -1060" "spawnflags" "81" "angle" "-1" "classname" "target_laser" } { "deathtarget" "t171" "targetname" "t275" "origin" "-380 -316 232" "spawnflags" "1" "angle" "270" "classname" "monster_chick" "item" "ammo_rockets" } { "targetname" "t275" "origin" "-268 -336 232" "spawnflags" "257" "angle" "270" "classname" "monster_chick" "item" "item_armor_shard" } { "targetname" "t90" "classname" "point_combat" "origin" "-454 -576 -732" } { "origin" "296 -304 -96" "classname" "monster_soldier" "angle" "0" "spawnflags" "257" "item" "item_armor_shard" "target" "t291" "deathtarget" "bob" } { "origin" "-1119 -579 -1060" "spawnflags" "81" "angle" "-1" "classname" "target_laser" } { "origin" "-318 -593 -87" "angle" "90" "classname" "monster_soldier_light" "spawnflags" "768" } { "origin" "-384 -307 -56" "angle" "135" "classname" "monster_soldier_light" "spawnflags" "256" } { "spawnflags" "2048" "origin" "-16 -152 -640" "classname" "misc_explobox" } { "spawnflags" "2048" "origin" "40 -232 -656" "classname" "misc_explobox" } { "spawnflags" "2048" "origin" "-424 -288 -1008" "target" "t84" "targetname" "t83" "classname" "path_corner" } { "origin" "-448 -168 -1016" "target" "t83" "targetname" "t82" "classname" "path_corner" } { "origin" "-472 -344 -1016" "targetname" "t84" "target" "t82" "classname" "path_corner" } { "item" "ammo_cells" "origin" "-412 -340 -984" "classname" "monster_soldier_light" "spawnflags" "2304" "angle" "180" "targetname" "t242" } { "origin" "-496 -352 -984" "target" "t84" "angle" "180" "spawnflags" "1" "classname" "monster_soldier_light" } { "targetname" "148" "classname" "trigger_key" "spawnflags" "2052" "message" "Security pass approved. \nYou may enter force field control." "target" "t81" "origin" "744 744 72" "item" "key_pass" } { "classname" "light" "light" "64" "origin" "96 676 16" } { "spawnflags" "2048" "classname" "ammo_grenades" "origin" "-602 -204 -404" } { "spawnflags" "2048" "classname" "trigger_relay" "delay" "1" "targetname" "t56" "origin" "-497 -325 -1008" "target" "t76" } { "classname" "info_notnull" "targetname" "t75" "origin" "-376 -296 -772" } { "classname" "light" "origin" "-376 -296 -880" "target" "t75" "light" "150" "_cone" "20" } { "spawnflags" "2048" "classname" "item_enviro" "origin" "-376 -296 -760" } { "model" "*128" "spawnflags" "2048" "classname" "func_explosive" "dmg" "100" "health" "1" "mass" "500" "_minlight" "0.2" "target" "t167" } { "classname" "monster_floater" "spawnflags" "1" "origin" "-464 -224 -376" "target" "t80" } { "classname" "light" "origin" "872 1424 183" "target" "t64" "light" "175" "_cone" "15" } { "classname" "info_null" "origin" "872 1424 68" "targetname" "t64" } { "classname" "light" "origin" "952 1424 183" "target" "t63" "light" "175" "_cone" "15" } { "classname" "info_null" "origin" "952 1424 68" "targetname" "t63" } { "classname" "light" "origin" "952 1344 183" "target" "t65" "light" "175" "_cone" "15" } { "classname" "info_null" "origin" "952 1344 68" "targetname" "t65" } { "classname" "info_null" "origin" "872 1344 68" "targetname" "t66" } { "classname" "light" "origin" "872 1344 183" "target" "t66" "light" "175" "_cone" "15" } { "model" "*129" "sounds" "3" "classname" "func_door" "angle" "0" "team" "p1" "lip" "16" "wait" "3" "_minlight" "0.2" "target" "t303" } { "model" "*130" "classname" "func_door" "angle" "180" "team" "p1" "lip" "16" "wait" "3" "_minlight" "0.2" "target" "t303" } { "classname" "info_null" "origin" "-392 -216 -628" "targetname" "t60" } { "classname" "light" "origin" "-392 -216 -568" "target" "t60" } { "classname" "light" "target" "t59" "origin" "-320 -216 -568" } { "classname" "info_null" "targetname" "t59" "origin" "-320 -216 -644" } { "classname" "light" "origin" "-504 -216 -568" "target" "t61" } { "classname" "info_null" "origin" "-504 -216 -620" "targetname" "t61" } { "classname" "info_null" "targetname" "t58" "origin" "-264 -216 -644" } { "classname" "light" "target" "t58" "origin" "-264 -216 -568" } { "style" "9" "targetname" "t57" "classname" "func_areaportal" } { "model" "*131" "sounds" "3" "wait" "4" "lip" "16" "team" "portal1" "angle" "180" "classname" "func_door" "_minlight" "0.2" "spawnflags" "8" } { "model" "*132" "target" "t57" "wait" "4" "lip" "16" "team" "portal1" "angle" "0" "classname" "func_door" "_minlight" "0.2" "spawnflags" "8" } { "model" "*133" "spawnflags" "2048" "target" "t56" "delay" "5" "classname" "trigger_once" } { "model" "*134" "angle" "-2" "spawnflags" "1" "classname" "trigger_push" "speed" "25" } { "spawnflags" "2" "origin" "-376 -290 -849" "classname" "monster_floater" "targetname" "t76" } { "classname" "monster_soldier_ss" "spawnflags" "257" "angle" "270" "origin" "-480 -672 -728" "targetname" "t11" "item" "ammo_bullets" } { "classname" "monster_soldier_ss" "spawnflags" "769" "angle" "270" "origin" "-416 -664 -728" "targetname" "t11" } { "item" "item_armor_shard" "angle" "180" "classname" "monster_soldier" "origin" "180 -488 -540" "spawnflags" "1" "deathtarget" "t243" } { "item" "ammo_shells" "classname" "monster_soldier" "angle" "180" "origin" "156 -536 -540" "spawnflags" "1" "deathtarget" "t243" } { "item" "ammo_shells" "classname" "monster_soldier" "angle" "180" "origin" "100 -524 -540" "spawnflags" "769" "deathtarget" "t243" } { "spawnflags" "2048" "classname" "point_combat" "targetname" "t51" "origin" "-198 -262 -644" } { "target" "t266" "targetname" "t243" "item" "ammo_rockets" "classname" "monster_chick" "spawnflags" "2" "angle" "90" "origin" "-672 -272 -624" } { "style" "36" "targetname" "t45" "spawnflags" "0" "_color" "1.000000 0.316206 0.142292" "classname" "light" "light" "175" "origin" "-132 -368 232" } { "model" "*135" "health" "5" "lip" "4" "classname" "func_door" "angle" "0" "team" "2grate" } { "model" "*136" "health" "5" "lip" "4" "classname" "func_door" "angle" "180" "team" "2grate" "sounds" "2" } { "style" "36" "origin" "-128 -272 232" "light" "175" "classname" "light" "targetname" "t45" "_color" "1.000000 0.316206 0.142292" "spawnflags" "0" } { "model" "*137" "spawnflags" "2048" "targetname" "t81" "sounds" "2" "wait" "-1" "angle" "-1" "classname" "func_door" } { "origin" "432 544 126" "target" "t36" "classname" "light" "light" "100" } { "origin" "432 544 70" "targetname" "t36" "classname" "info_null" } { "origin" "432 480 70" "targetname" "t35" "classname" "info_null" } { "origin" "432 480 126" "light" "100" "target" "t35" "classname" "light" } { "origin" "-480 -744 -728" "angle" "270" "targetname" "t11" "spawnflags" "1" "classname" "monster_soldier_ss" } { "origin" "-416 -736 -728" "angle" "270" "targetname" "t11" "spawnflags" "1" "classname" "monster_soldier_ss" } { "spawnflags" "2048" "targetname" "t22" "target" "t34" "origin" "-576 -1264 -832" "classname" "point_combat" } { "classname" "info_notnull" "targetname" "t33" "origin" "-448 -952 -100" } { "origin" "-448 -952 120" "_color" "1.000000 1.000000 0.874510" "light" "80" "classname" "light" } { "spawnflags" "2048" "classname" "trigger_relay" "targetname" "t11" "target" "t32" "origin" "-344 -1112 -744" } { "origin" "-368 -1020 -760" "spawnflags" "2048" "classname" "point_combat" "targetname" "t30" "wait" "10" } { "classname" "point_combat" "spawnflags" "2048" "origin" "-320 -1040 -760" "targetname" "t31" "wait" "10" } { "classname" "monster_soldier_ss" "angle" "180" "origin" "-312 -1020 -742" "spawnflags" "0" "target" "t30" "targetname" "t32" } { "model" "*138" "spawnflags" "2048" "classname" "trigger_once" "target" "t29" } { "target" "t90" "classname" "point_combat" "targetname" "t25" "origin" "-454 -734 -712" } { "spawnflags" "2048" "classname" "path_corner" "target" "t25" "targetname" "t28" "origin" "-452 -1024 -736" } { "classname" "point_combat" "targetname" "t27" "target" "t28" "origin" "-560 -1032 -720" } { "spawnflags" "2048" "classname" "point_combat" "target" "t27" "origin" "-552 -1264 -776" "targetname" "t233" } { "item" "ammo_cells" "classname" "monster_floater" "target" "t26" "targetname" "t29" "angle" "180" "spawnflags" "1" "origin" "-324 -1072 -864" } { "classname" "monster_soldier_ss" "angle" "180" "origin" "-360 -1184 -728" "spawnflags" "1" "item" "ammo_bullets" "target" "t253" "targetname" "BobnFred" } { "spawnflags" "769" "origin" "-364 -1144 -728" "angle" "180" "classname" "monster_soldier_ss" "targetname" "t11" "target" "t31" } { "classname" "monster_soldier_ss" "angle" "180" "origin" "-312 -1152 -728" "spawnflags" "257" "target" "t254" "Targetname" "BobnFred" "targetname" "BobnFred" } { "model" "*139" "spawnflags" "2048" "classname" "trigger_once" "target" "t23" } { "pathtarget" "BobnFred" "wait" "1" "targetname" "t34" "classname" "point_combat" "origin" "-592 -1176 -804" } { "spawnflags" "2048" "origin" "592 514 3972" "targetname" "jimmy" "classname" "info_notnull" } { "model" "*140" "spawnflags" "2048" "target" "t11" "classname" "trigger_once" } { "origin" "-560 -1160 -792" "classname" "monster_soldier_ss" "target" "t22" "targetname" "t23" "spawnflags" "1" "angle" "270" "wait" ".5" "delay" ".25" } { "origin" "-312 -1107 -728" "targetname" "t11" "angle" "90" "classname" "monster_chick" "spawnflags" "257" } { "target" "jimmy" "origin" "586 516 352" "targetname" "com5" "classname" "target_laser" "spawnflags" "2057" } { "target" "jimmy" "origin" "596 516 352" "targetname" "com4" "classname" "target_laser" "spawnflags" "2057" } { "target" "jimmy" "origin" "596 508 352" "targetname" "com3" "classname" "target_laser" "spawnflags" "2057" } { "target" "jimmy" "origin" "592 512 350" "targetname" "com7" "classname" "target_laser" "spawnflags" "2057" } { "target" "jimmy" "origin" "586 508 352" "targetname" "com6" "spawnflags" "2057" "classname" "target_laser" } { "model" "*141" "classname" "func_wall" "spawnflags" "2055" "targetname" "pg1" } { "model" "*142" "spawnflags" "2055" "classname" "func_wall" "targetname" "pg2" } { "light" "150" "classname" "light" "origin" "-456 696 160" } { "item" "item_silencer" "angle" "45" "classname" "monster_soldier_light" "origin" "-507 -311 -72" "spawnflags" "256" } { "classname" "monster_soldier_light" "angle" "135" "origin" "-413 -463 -87" } { "model" "*143" "spawnflags" "2055" "classname" "func_wall" "targetname" "reddoor2" } { "model" "*144" "classname" "func_wall" "spawnflags" "2055" "targetname" "reddoor1" } { "origin" "-48 168 -154" "_color" "1.000000 0.830709 0.263780" "light" "100" "classname" "light" } { "origin" "-40 96 -122" "_color" "1.000000 0.830709 0.263780" "light" "100" "classname" "light" } { "origin" "-40 16 -90" "_color" "1.000000 0.830709 0.263780" "light" "100" "classname" "light" } { "origin" "-40 -56 -74" "_color" "1.000000 0.830709 0.263780" "light" "100" "classname" "light" } { "origin" "-40 -128 -58" "_color" "1.000000 0.830709 0.263780" "light" "100" "classname" "light" } { "classname" "light" "light" "100" "_color" "1.000000 0.830709 0.263780" "origin" "-56 280 -154" } { "model" "*145" "classname" "func_wall" "spawnflags" "2055" "targetname" "pg2" } { "light" "100" "_color" "0.602362 1.000000 0.555118" "classname" "light" "origin" "-168 -328 -568" } { "light" "100" "_color" "0.602362 1.000000 0.555118" "classname" "light" "origin" "-168 -216 -568" } { "origin" "-392 -104 -568" "light" "100" "_color" "0.602362 1.000000 0.555118" "classname" "light" } { "classname" "trigger_relay" "targetname" "endplat" "target" "city3X" "delay" "0.3" "origin" "192 1472 344" } { "_color" "1.000000 1.000000 0.850980" "light" "150" "classname" "light" "origin" "168 -320 -424" } { "_color" "1.000000 1.000000 0.850980" "light" "150" "classname" "light" "origin" "168 -320 -296" } { "_color" "1.000000 1.000000 0.850980" "light" "150" "classname" "light" "origin" "168 -320 -104" } { "_color" "1.000000 1.000000 0.850980" "light" "150" "classname" "light" "origin" "168 -320 24" } { "_color" "1.000000 1.000000 0.850980" "light" "150" "classname" "light" "origin" "168 -320 152" } { "_color" "1.000000 1.000000 0.850980" "light" "150" "classname" "light" "origin" "168 -320 280" } { "classname" "light" "light" "150" "_color" "1.000000 1.000000 0.850980" "origin" "168 -320 -600" } { "model" "*146" "lip" "4" "spawnflags" "2048" "classname" "func_button" "angle" "45" "sounds" "2" "target" "endsecret" "wait" "-1" } { "model" "*147" "spawnflags" "2048" "_minlight" "0.1" "classname" "func_door" "angle" "90" "targetname" "endsecret" "wait" "-1" "speed" "50" "sounds" "4" } { "model" "*148" "spawnflags" "2048" "classname" "func_explosive" "health" "25" "dmg" "25" } { "classname" "light" "light" "100" "_style" "6" "origin" "-1268 -396 -1044" } { "model" "*149" "spawnflags" "2048" "sounds" "4" "lip" "4" "angle" "180" "target" "steps" "_minlight" "0.9" "classname" "func_button" "wait" "-1" } { "map" "city2$city2XH" "classname" "target_changelevel" "targetname" "back_high" "target" "city2XH" "origin" "-392 -920 -40" } { "model" "*150" "angle" "270" "classname" "trigger_multiple" "target" "back_high" } { "classname" "target_changelevel" "targetname" "back_low" "map" "city2$city2XL" "origin" "-72 -1176 -856" } { "model" "*151" "angle" "360" "classname" "trigger_multiple" "target" "back_low" } { "classname" "target_changelevel" "map" "*boss1$bosstart" "origin" "128 1392 460" "targetname" "city3X" "spawnflags" "1" } { "light" "150" "origin" "-424 -584 -992" "_color" "0.071429 0.444444 1.000000" "classname" "light" } { "light" "150" "origin" "-424 -440 -992" "_color" "0.071429 0.444444 1.000000" "classname" "light" } { "origin" "-48 -16 -26" "_color" "1.000000 0.830709 0.263780" "light" "150" "classname" "light" } { "origin" "-48 176 -98" "_color" "1.000000 0.830709 0.263780" "light" "150" "classname" "light" } { "origin" "-160 272 -130" "classname" "light" "light" "150" "_color" "1.000000 0.830709 0.263780" } { "origin" "-240 -128 14" "classname" "light" "light" "150" "_color" "1.000000 0.830709 0.263780" } { "origin" "-208 272 -130" "_color" "1.000000 0.830709 0.263780" "light" "150" "classname" "light" } { "model" "*152" "classname" "func_button" "lip" "6" "angle" "0" "health" "1" "target" "blastshield" "delay" "0" "wait" "1" "sounds" "4" "_minlight" "0.8" "spawnflags" "2048" } { "model" "*153" "classname" "func_door" "spawnflags" "2049" "targetname" "blastshield" "lip" "-2" "speed" "100" "sounds" "2" "wait" "3" "delay" "0" "angle" "-2" } { "model" "*154" "classname" "func_door" "spawnflags" "2049" "lip" "24" "dmg" "0" "targetname" "endplat" "angle" "-2" } { "light" "100" "classname" "light" "origin" "364 1592 364" } { "light" "100" "classname" "light" "origin" "364 1636 364" } { "light" "100" "classname" "light" "origin" "308 1636 364" } { "classname" "light" "light" "100" "origin" "308 1592 364" } { "model" "*155" "classname" "func_button" "angle" "45" "_minlight" "0.2" "lip" "2" "sounds" "4" "target" "endplat" "spawnflags" "2048" } { "model" "*156" "classname" "func_wall" "_minlight" "0.2" "spawnflags" "2048" } { "light" "80" "classname" "light" "origin" "424 1244 344" "_color" "1.000000 0.388235 0.239216" } { "_color" "1.000000 0.388235 0.239216" "origin" "336 1616 572" "classname" "light" "light" "100" } { "_color" "1.000000 0.937255 0.705882" "light" "150" "classname" "light" "origin" "-568 24 284" } { "classname" "light" "light" "100" "origin" "-568 -128 256" } { "model" "*157" "team" "exit1" "angle" "90" "classname" "func_door" } { "model" "*158" "classname" "func_door" "angle" "270" "team" "exit1" "sounds" "2" } { "model" "*159" "sounds" "2" "wait" "2" "classname" "func_plat" "_minlight" "0.2" } { "origin" "-488 -616 -600" "classname" "light" "light" "100" } { "origin" "-504 -632 -600" "classname" "light" "light" "100" } { "light" "100" "classname" "light" "origin" "-504 -864 -600" } { "light" "100" "classname" "light" "origin" "-488 -880 -600" } { "origin" "-408 -880 -600" "classname" "light" "light" "100" } { "origin" "-392 -864 -600" "classname" "light" "light" "100" } { "targetname" "gun3" "origin" "112 664 -104" "angle" "180" "sounds" "1" "classname" "target_splash" } { "origin" "-440 408 328" "classname" "light" "light" "100" "_color" "1.000000 0.976471 0.501961" } { "origin" "-440 360 328" "classname" "light" "light" "100" "_color" "1.000000 0.976471 0.501961" } { "origin" "-336 392 328" "classname" "light" "light" "100" "_color" "1.000000 0.976471 0.501961" } { "origin" "-544 392 328" "classname" "light" "light" "100" "_color" "1.000000 0.976471 0.501961" } { "origin" "-336 168 328" "classname" "light" "light" "100" "_color" "1.000000 0.976471 0.501961" } { "origin" "-544 200 328" "classname" "light" "light" "100" "_color" "1.000000 0.976471 0.501961" } { "origin" "-440 200 328" "classname" "light" "light" "100" "_color" "1.000000 0.976471 0.501961" } { "origin" "-376 -232 -952" "light" "120" "classname" "light" "_color" "1.000000 0.892430 0.294821" } { "origin" "-504 -232 -952" "light" "125" "classname" "light" "_color" "1.000000 0.892430 0.294821" } { "origin" "-408 -232 -952" "light" "120" "classname" "light" "_color" "1.000000 0.892430 0.294821" } { "origin" "-296 -232 -952" "light" "120" "classname" "light" "_color" "1.000000 0.892430 0.294821" } { "_color" "1.000000 0.968627 0.725490" "light" "80" "classname" "light" "origin" "-408 -840 120" } { "classname" "light" "light" "80" "_color" "1.000000 0.968627 0.725490" "origin" "-488 -840 120" } { "_color" "1.000000 0.968627 0.725490" "light" "80" "classname" "light" "origin" "-488 -952 120" } { "classname" "light" "light" "80" "_color" "1.000000 0.968627 0.725490" "origin" "-408 -952 120" } { "_cone" "20" "classname" "light" "light" "300" "_color" "1.000000 1.000000 1.000000" "origin" "-448 -952 112" "target" "t33" } { "model" "*160" "angle" "-2" "targetname" "step2" "classname" "func_door" "lip" "26" "wait" "-1" "sounds" "1" } { "model" "*161" "angle" "-2" "targetname" "step3" "classname" "func_door" "lip" "42" "wait" "-1" "sounds" "1" } { "model" "*162" "angle" "-2" "targetname" "step4" "classname" "func_door" "lip" "58" "wait" "-1" "sounds" "1" } { "model" "*163" "angle" "-2" "targetname" "step5" "classname" "func_door" "lip" "74" "wait" "-1" "sounds" "1" } { "model" "*164" "angle" "-2" "targetname" "step6" "classname" "func_door" "lip" "90" "wait" "-1" "sounds" "1" } { "model" "*165" "angle" "-2" "targetname" "step7" "classname" "func_door" "lip" "106" "wait" "-1" "sounds" "1" } { "model" "*166" "sounds" "3" "angle" "-2" "wait" "-1" "targetname" "step1" "lip" "10" "classname" "func_door" } { "classname" "light" "_color" "0.071429 0.444444 1.000000" "origin" "-962 -506 -992" "light" "100" } { "classname" "light" "_color" "0.071429 0.444444 1.000000" "origin" "-832 -461 -994" "light" "150" } { "origin" "-568 -128 -40" "light" "100" "classname" "light" } { "model" "*167" "spawnflags" "2055" "classname" "func_wall" "targetname" "pg1" } { "light" "150" "origin" "-1000 -456 -992" "_color" "0.071429 0.444444 1.000000" "classname" "light" } { "light" "100" "origin" "-836 -510 -992" "_color" "0.071429 0.444444 1.000000" "classname" "light" } { "origin" "360 -320 294" "classname" "light" "_color" "1.000000 0.774704 0.304348" "light" "125" } { "origin" "216 -440 -488" "classname" "light" "_color" "1.000000 0.976471 0.792157" "light" "64" } { "origin" "232 -504 -504" "classname" "light" "_color" "0.602362 1.000000 0.555118" "light" "100" } { "origin" "-136 -504 -504" "classname" "light" "_color" "0.602362 1.000000 0.555118" "light" "100" } { "origin" "-136 -264 -504" "classname" "light" "_color" "1.000000 1.000000 0.949020" "light" "100" } { "origin" "-120 -216 -544" "classname" "light" "_color" "0.602362 1.000000 0.555118" "light" "100" } { "light" "100" "_color" "0.602362 1.000000 0.555118" "classname" "light" "origin" "-136 -216 -568" } { "light" "64" "_color" "1.000000 0.976471 0.792157" "classname" "light" "origin" "104 -440 -488" } { "light" "100" "_color" "0.602362 1.000000 0.555118" "classname" "light" "origin" "-40 -216 -544" } { "light" "100" "_color" "0.602362 1.000000 0.555118" "classname" "light" "origin" "40 -216 -544" } { "origin" "-232 -328 -568" "classname" "light" "_color" "0.602362 1.000000 0.555118" "light" "100" } { "origin" "8 -104 -568" "classname" "light" "_color" "0.602362 1.000000 0.555118" "light" "100" } { "origin" "-112 -104 -568" "classname" "light" "_color" "0.602362 1.000000 0.555118" "light" "100" } { "origin" "-232 -104 -568" "classname" "light" "_color" "0.602362 1.000000 0.555118" "light" "100" } { "origin" "-232 -216 -568" "classname" "light" "_color" "0.602362 1.000000 0.555118" "light" "100" } { "origin" "-264 -216 -568" "classname" "light" "_color" "0.602362 1.000000 0.555118" "light" "110" } { "classname" "trigger_relay" "targetname" "steps" "target" "step2" "delay" "0.2" "origin" "-960 -532 -1004" } { "classname" "trigger_relay" "targetname" "steps" "target" "step3" "delay" "0.4" "origin" "-940 -532 -1004" } { "classname" "trigger_relay" "targetname" "steps" "target" "step4" "delay" "0.6" "origin" "-916 -532 -1004" } { "classname" "trigger_relay" "targetname" "steps" "target" "step5" "delay" "0.8" "origin" "-892 -532 -1004" } { "classname" "trigger_relay" "targetname" "steps" "target" "step6" "delay" "1.0" "origin" "-868 -532 -1004" } { "classname" "trigger_relay" "targetname" "steps" "target" "step7" "delay" "1.2" "origin" "-844 -532 -1004" } { "classname" "trigger_relay" "targetname" "steps" "target" "step1" "origin" "-984 -532 -1004" } { "origin" "-128 -128 294" "classname" "light" "_color" "1.000000 0.774704 0.304348" "light" "175" } { "origin" "-504 -216 -568" "light" "110" "_color" "0.602362 1.000000 0.555118" "classname" "light" } { "origin" "-448 -834 -84" "angle" "90" "classname" "info_player_start" "targetname" "city3NH" } { "light" "110" "classname" "light" "origin" "-336 -1272 -648" } { "light" "110" "classname" "light" "origin" "-352 -1256 -648" } { "light" "110" "classname" "light" "origin" "-320 -1256 -648" } { "light" "110" "classname" "light" "origin" "-336 -1240 -648" } { "light" "110" "classname" "light" "origin" "-336 -1072 -648" } { "light" "110" "classname" "light" "origin" "-352 -1056 -648" } { "light" "110" "classname" "light" "origin" "-320 -1056 -648" } { "light" "110" "classname" "light" "origin" "-336 -1040 -648" } { "style" "37" "light" "200" "classname" "light" "origin" "-576 -1056 -648" "spawnflags" "1" "targetname" "t199" } { "light" "100" "classname" "light" "origin" "-408 -616 -600" } { "light" "100" "classname" "light" "origin" "-392 -632 -600" } { "classname" "light" "light" "120" "origin" "-544 -1256 -648" } { "classname" "light" "light" "120" "origin" "-560 -1240 -648" } { "classname" "light" "light" "120" "origin" "-576 -1256 -648" } { "classname" "light" "light" "120" "origin" "-560 -1272 -648" "wait" ".5" } { "model" "*168" "spawnflags" "2055" "classname" "func_wall" "targetname" "pg1" } { "light" "150" "_color" "1.000000 0.774704 0.304348" "classname" "light" "origin" "232 -320 294" } { "light" "150" "_color" "1.000000 0.774704 0.304348" "classname" "light" "origin" "344 -168 294" } { "_color" "0.071429 0.444444 1.000000" "classname" "light" "origin" "-568 -440 -1000" "light" "150" } { "classname" "light" "_color" "0.071429 0.444444 1.000000" "origin" "-568 -584 -1000" "light" "150" } { "classname" "light" "_color" "0.071429 0.444444 1.000000" "origin" "-996 -564 -992" "light" "150" } { "classname" "light" "_color" "0.602362 1.000000 0.555118" "light" "100" "origin" "-264 -104 -568" } { "light" "100" "_color" "0.602362 1.000000 0.555118" "classname" "light" "origin" "-600 -104 -568" } { "light" "110" "_color" "0.602362 1.000000 0.555118" "classname" "light" "origin" "-320 -216 -568" } { "classname" "light" "_color" "0.602362 1.000000 0.555118" "light" "110" "origin" "-392 -216 -568" } { "light" "100" "_color" "0.602362 1.000000 0.555118" "classname" "light" "origin" "56 -184 -568" } { "classname" "light" "_color" "0.602362 1.000000 0.555118" "light" "100" "origin" "-504 -104 -568" } { "origin" "-328 24 296" "classname" "light" "light" "100" } { "origin" "-568 -40 104" "classname" "light" "light" "150" } { "_color" "1.000000 0.937255 0.705882" "origin" "-568 24 -16" "classname" "light" "light" "150" } { "origin" "-328 -136 -24" "light" "150" "classname" "light" } { "light" "100" "classname" "light" "origin" "-328 24 -24" } { "origin" "-328 -72 104" "light" "150" "classname" "light" } { "light" "100" "classname" "light" "origin" "-328 24 104" } { "model" "*169" "team" "big_red" "sounds" "4" "classname" "func_door" "angle" "0" "speed" "50" "lip" "8" } { "model" "*170" "team" "big_red" "lip" "8" "speed" "50" "angle" "180" "classname" "func_door" } { "model" "*171" "origin" "372 1536 280" "team" "grandexit" "targetname" "t2" "classname" "func_door_rotating" "speed" "33" "distance" "95" "wait" "-1" "angle" "-2" "spawnflags" "68" } { "classname" "light" "origin" "-200 1424 380" "light" "200" } { "classname" "light" "origin" "-136 1216 376" "light" "200" } { "light" "80" "classname" "light" "origin" "48 1240 344" "_color" "1.000000 0.388235 0.239216" } { "light" "150" "classname" "light" "origin" "152 728 -128" } { "model" "*172" "delay" ".25" "angle" "0" "speed" "500" "wait" ".25" "lip" "6" "classname" "func_door" "targetname" "gun1" "spawnflags" "2048" "sounds" "2" } { "model" "*173" "delay" ".25" "angle" "0" "speed" "500" "wait" ".25" "lip" "6" "classname" "func_door" "targetname" "gun2" "spawnflags" "2048" "sounds" "2" } { "model" "*174" "classname" "func_door" "lip" "6" "wait" ".25" "speed" "500" "angle" "0" "delay" ".25" "targetname" "gun4" "spawnflags" "2048" "sounds" "2" } { "classname" "light" "light" "150" "origin" "152 616 -128" } { "origin" "112 664 -104" "angle" "180" "targetname" "gun3" "classname" "target_blaster" "dmg" "15" } { "angle" "180" "origin" "112 688 -104" "targetname" "gun2" "classname" "target_blaster" "dmg" "15" } { "origin" "112 712 -104" "angle" "180" "targetname" "gun1" "classname" "target_blaster" "dmg" "15" } { "classname" "target_blaster" "angle" "180" "targetname" "gun4" "origin" "112 640 -104" "dmg" "15" } { "model" "*175" "spawnflags" "2048" "delay" ".25" "angle" "0" "speed" "500" "wait" ".25" "lip" "6" "classname" "func_door" "targetname" "gun3" "sounds" "2" } { "classname" "target_splash" "sounds" "1" "angle" "270" "targetname" "gun4" "origin" "112 640 -104" } { "angle" "180" "sounds" "1" "classname" "target_splash" "targetname" "gun1" "origin" "112 712 -104" } { "angle" "180" "sounds" "1" "classname" "target_splash" "targetname" "gun2" "origin" "112 688 -104" } { "classname" "trigger_relay" "targetname" "trap_gun" "target" "gun4" "delay" ".8" "origin" "40 680 24" "spawnflags" "2048" } { "classname" "trigger_relay" "targetname" "trap_gun" "target" "gun3" "delay" ".6" "origin" "40 704 24" "spawnflags" "2048" } { "classname" "trigger_relay" "targetname" "trap_gun" "target" "gun2" "delay" ".4" "origin" "40 728 24" "spawnflags" "2048" } { "classname" "trigger_relay" "targetname" "trap_gun" "target" "gun1" "delay" ".2" "origin" "40 752 24" "spawnflags" "2048" } { "model" "*176" "classname" "trigger_multiple" "wait" ".8" "delay" ".5" "target" "trap_gun" "spawnflags" "2048" } { "model" "*177" "team" "door8" "sounds" "0" "speed" "200" "wait" "-1" "angle" "-1" "classname" "func_door" "lip" "8" "targetname" "trap_gun" "spawnflags" "2048" } { "_color" "0.501961 0.501961 1.000000" "light" "200" "classname" "light" "origin" "696 272 295" } { "origin" "472 263 295" "classname" "light" "light" "200" "_color" "0.501961 0.501961 1.000000" } { "origin" "353 381 295" "_color" "0.501961 0.501961 1.000000" "light" "200" "classname" "light" } { "origin" "426 759 295" "classname" "light" "light" "200" "_color" "0.501961 0.501961 1.000000" } { "_color" "0.501961 0.501961 1.000000" "light" "200" "classname" "light" "origin" "533 801 295" } { "_color" "0.501961 0.501961 1.000000" "light" "200" "classname" "light" "origin" "484 780 295" } { "origin" "824 360 295" "classname" "light" "light" "200" "_color" "0.501961 0.501961 1.000000" } { "origin" "872 453 295" "classname" "light" "light" "200" "_color" "0.501961 0.501961 1.000000" } { "_color" "0.501961 0.501961 1.000000" "light" "200" "classname" "light" "origin" "852 408 295" } { "_color" "0.501961 0.501961 1.000000" "light" "200" "classname" "light" "origin" "878 569 295" } { "origin" "836 670 295" "classname" "light" "light" "200" "_color" "0.501961 0.501961 1.000000" } { "origin" "857 619 295" "classname" "light" "light" "200" "_color" "0.501961 0.501961 1.000000" } { "origin" "754 756 295" "classname" "light" "light" "200" "_color" "0.501961 0.501961 1.000000" } { "_color" "0.501961 0.501961 1.000000" "light" "200" "classname" "light" "origin" "648 797 295" } { "_color" "0.501961 0.501961 1.000000" "light" "200" "classname" "light" "origin" "700 779 295" } { "model" "*178" "spawnflags" "2048" "classname" "func_button" "angle" "180" "wait" "-1" "lip" "16" } { "classname" "trigger_relay" "targetname" "t1" "delay" "1" "target" "t2" "origin" "128 1404 268" } { "origin" "-1651 -532 -1104" "classname" "light" "light" "64" } { "origin" "-1635 -588 -1056" "light" "64" "classname" "light" } { "model" "*179" "_minlight" "0.2" "classname" "func_wall" "spawnflags" "2048" } { "model" "*180" "_minlight" "0.2" "spawnflags" "2048" "classname" "func_button" "angle" "180" "target" "a108" "wait" "5" "lip" "16" } { "classname" "light" "light" "64" "origin" "-1603 -556 -1104" } { "light" "64" "classname" "light" "origin" "-1603 -532 -1104" } { "spawnflags" "2048" "classname" "item_ancient_head" "origin" "-1632 -415 -1076" "target" "t304" } { "classname" "light" "origin" "-1464 -596 -1088" "light" "175" } { "style" "38" "_cone" "20" "origin" "-1616 -416 -1088" "targetname" "a6" "light" "300" "target" "a5" "classname" "light" "_color" "0.124498 1.000000 0.000000" "spawnflags" "2048" } { "origin" "-1632 -416 -1036" "targetname" "a5" "classname" "info_notnull" "spawnflags" "2048" } { "light" "76" "origin" "-1584 -392 -1120" "classname" "light" } { "light" "76" "origin" "-1584 -440 -1120" "classname" "light" } { "light" "76" "origin" "-1584 -416 -1120" "classname" "light" } { "model" "*181" "spawnflags" "2048" "targetname" "a_water" "sounds" "1" "speed" "10" "angle" "-2" "classname" "func_water" } { "model" "*182" "targetname" "a134" "spawnflags" "2055" "classname" "func_wall" } { "spawnflags" "2048" "origin" "-1524 -488 -1048" "target" "a_water" "targetname" "a108" "classname" "trigger_relay" } { "targetname" "a134" "origin" "-1632 -416 -1080" "noise" "world/force1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1556 -424 -1058" "volume" ".8" "attenuation" "2" "noise" "world/brkglas.wav" "targetname" "a132" "classname" "target_speaker" "spawnflags" "2048" } { "model" "*183" "spawnflags" "2048" "target" "a132" "health" "10" "mass" "200" "dmg" "0" "classname" "func_explosive" } { "origin" "-1592 -440 -1068" "attenuation" "1" "volume" "0.6" "noise" "world/bubbl4.wav" "targetname" "a133" "classname" "target_speaker" "spawnflags" "2048" } { "targetname" "a_water" "origin" "-1592 -408 -1064" "noise" "world/bubl2.wav" "spawnflags" "2049" "classname" "target_speaker" } { "targetname" "a_water" "origin" "-1528 -392 -1064" "wait" "5" "random" "3" "spawnflags" "2049" "target" "a133" "classname" "func_timer" } { "spawnflags" "2048" "origin" "-1592 -504 -1080" "target" "a134" "targetname" "a108" "item" "key_red_key" "classname" "trigger_key" } { "spawnflags" "2048" "origin" "-1560 -392 -1064" "targetname" "a132" "killtarget" "a_water" "classname" "trigger_relay" } { "origin" "-1628 -620 -1096" "classname" "item_enviro" "team" "head" } { "classname" "target_speaker" "origin" "-416 -400 -600" "spawnflags" "2048" "noise" "world/v_fac3.wav" "attenuation" "-1" "volume" "0.7" "targetname" "t300" } { "classname" "target_speaker" "targetname" "t298" "origin" "-448 -400 -600" "spawnflags" "2048" "noise" "world/v_cit2.wav" "attenuation" "-1" "volume" "0.7" } { "classname" "target_speaker" "targetname" "t299" "origin" "-472 -400 -600" "spawnflags" "2048" "noise" "world/voice7.wav" "attenuation" "-1" "volume" "0.7" } { "classname" "func_timer" "target" "t299" "attenuation" "-1" "random" "60" "wait" "180" "targetname" "t89" "origin" "-472 -368 -600" "spawnflags" "2048" } { "classname" "func_timer" "target" "t298" "attenuation" "-1" "random" "20" "wait" "200" "targetname" "t89" "origin" "-448 -368 -600" "spawnflags" "2048" } { "classname" "func_timer" "attenuation" "-1" "random" "100" "wait" "400" "origin" "-416 -368 -600" "spawnflags" "2048" "target" "t300" } { "classname" "target_spawner" "origin" "-1612 -428 -1068" "target" "item_adrenaline" "spawnflags" "2048" "targetname" "t306" } { "origin" "-1612 -428 -1068" "classname" "target_spawner" "target" "item_adrenaline" "spawnflags" "2048" "targetname" "t304" } { "origin" "-1612 -404 -1068" "classname" "target_spawner" "target" "item_adrenaline" "spawnflags" "2048" "targetname" "t305" } { "classname" "target_spawner" "origin" "-1612 -404 -1068" "target" "item_adrenaline" "spawnflags" "2048" "targetname" "t307" } { "delay" ".05" "origin" "-1592 -456 -1080" "classname" "trigger_relay" "targetname" "t304" "spawnflags" "2048" "target" "t305" } { "delay" ".1" "origin" "-1584 -424 -1080" "classname" "trigger_relay" "targetname" "t304" "spawnflags" "2048" "target" "t306" } { "delay" ".15" "origin" "-1588 -396 -1080" "classname" "trigger_relay" "targetname" "t304" "spawnflags" "2048" "target" "t307" }yquake2-QUAKE2_8_40/stuff/mapfixes/baseq2/cool1.ent000066400000000000000000002164771465112212000217770ustar00rootroot00000000000000// FIXED ENTITY STRING (by BjossiAlfreds) // // 1. Fixed monster_gladiator (810) in box being inaccessible on Medium // // This gladiator spawns on Medium and above but the trigger_once (784) // that blows up the box is only spawned on Hard and above, making the // monster count off by 1 on Medium. I changed the gladiator's spawnflags // from 259 to 771 to fix this inconsistency. { "sky" "unit6_" "classname" "worldspawn" "message" "Cooling Facility" "sounds" "5" "nextmap" "waste1" } { "origin" "-192 -1704 -112" "light" "150" "classname" "light" } { "model" "*1" "spawnflags" "1792" "classname" "func_wall" } { "origin" "404 -624 16" "spawnflags" "4" "target" "t188" "classname" "target_crosslevel_target" } { "model" "*2" "targetname" "t188" "classname" "func_explosive" } { "origin" "720 -932 68" "classname" "light" "light" "120" } { "origin" "732 -820 68" "classname" "light" "light" "120" } { "origin" "892 -940 68" "light" "80" "classname" "light" } { "model" "*3" "message" "Design is law." "classname" "trigger_once" "spawnflags" "2048" } { "origin" "812 -896 68" "light" "120" "classname" "light" } { "model" "*4" "dmg" "10" "health" "10" "classname" "func_explosive" } { "model" "*5" "team" "td1" "_minlight" ".18" "angle" "180" "classname" "func_door" } { "model" "*6" "classname" "trigger_once" "killtarget" "prevent" "spawnflags" "2048" } { "model" "*7" "classname" "func_wall" "targetname" "prevent" "spawnflags" "2048" } { "targetname" "cool1b" "origin" "-1496 -1308 24" "classname" "info_player_coop" "angle" "90" } { "targetname" "cool1b" "origin" "-1652 -1208 24" "classname" "info_player_coop" "angle" "45" } { "targetname" "cool1b" "origin" "-1388 -1308 24" "angle" "90" "classname" "info_player_coop" } { "origin" "-960 828 -388" "classname" "light" "light" "120" "_color" "1.000000 0.931193 0.064220" } { "origin" "-1144 844 -388" "classname" "light" "light" "120" "_color" "1.000000 0.931193 0.064220" } { "origin" "-1124 988 -388" "classname" "light" "light" "120" "_color" "1.000000 0.931193 0.064220" } { "origin" "-980 988 -388" "classname" "light" "light" "120" "_color" "1.000000 0.931193 0.064220" } { "origin" "-960 616 -388" "_color" "1.000000 0.931193 0.064220" "light" "120" "classname" "light" } { "classname" "item_armor_shard" "origin" "-1328 -1752 -728" "spawnflags" "2048" } { "model" "*8" "spawnflags" "2048" "classname" "func_wall" } { "origin" "408 -264 24" "angle" "225" "classname" "info_player_deathmatch" } { "origin" "-2024 -128 -296" "target" "t186" "spawnflags" "1792" "classname" "misc_teleporter" } { "origin" "-2432 -544 -960" "targetname" "t186" "spawnflags" "1792" "angle" "270" "classname" "misc_teleporter_dest" } { "targetname" "t187" "origin" "-408 -80 24" "spawnflags" "1792" "angle" "0" "classname" "misc_teleporter_dest" } { "model" "*9" "spawnflags" "2048" "classname" "func_wall" } { "target" "t187" "origin" "-24 552 -360" "classname" "misc_teleporter" } { "origin" "-80 888 -160" "spawnflags" "1792" "classname" "item_health_large" } { "classname" "ammo_rockets" "spawnflags" "1792" "origin" "336 -1568 -452" } { "origin" "260 -1344 -216" "classname" "item_armor_shard" "spawnflags" "1792" } { "origin" "220 -1344 -216" "classname" "item_armor_shard" "spawnflags" "1792" } { "origin" "300 -1344 -216" "spawnflags" "1792" "classname" "item_armor_shard" } { "origin" "312 -1500 -216" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "92 -1676 -452" "classname" "item_health_small" "spawnflags" "1792" } { "origin" "92 -1636 -452" "classname" "item_health_small" "spawnflags" "1792" } { "origin" "92 -1596 -452" "classname" "item_health_small" "spawnflags" "1792" } { "origin" "92 -1716 -452" "spawnflags" "1792" "classname" "item_health_small" } { "origin" "-448 -1576 -256" "classname" "ammo_cells" "spawnflags" "1792" } { "origin" "-492 -1464 -244" "spawnflags" "1792" "classname" "ammo_cells" } { "origin" "-416 -1516 -216" "target" "t1" "spawnflags" "1792" "classname" "trigger_always" } { "origin" "-440 -1496 -228" "target" "t80" "spawnflags" "1792" "classname" "trigger_always" } { "origin" "88 -1240 -216" "spawnflags" "2048" "classname" "ammo_shells" } { "origin" "64 -1364 -216" "classname" "ammo_bullets" "spawnflags" "2048" } { "origin" "80 -1320 -216" "spawnflags" "2048" "classname" "ammo_bullets" } { "origin" "-156 -1288 -456" "classname" "item_health" } { "origin" "-196 -1288 -456" "classname" "item_health" } { "model" "*10" "spawnflags" "2048" "classname" "func_wall" } { "model" "*11" "spawnflags" "2048" "classname" "func_wall" } { "origin" "136 -1332 -228" "spawnflags" "1792" "target" "t185" "classname" "trigger_always" } { "model" "*12" "wait" "-1" "targetname" "t185" "target" "t182" "angle" "-1" "spawnflags" "8" "classname" "func_door" } { "origin" "-148 -1588 -460" "target" "t184" "spawnflags" "1792" "classname" "trigger_always" } { "model" "*13" "wait" "-1" "targetname" "t184" "target" "t183" "spawnflags" "8" "angle" "-1" "classname" "func_door" } { "model" "*14" "spawnflags" "1792" "classname" "func_wall" } { "model" "*15" "spawnflags" "1792" "classname" "func_wall" } { "style" "1" "targetname" "t183" "classname" "func_areaportal" } { "style" "2" "targetname" "t182" "classname" "func_areaportal" } { "model" "*16" "spawnflags" "1792" "classname" "func_wall" } { "target" "t181" "targetname" "t180" "classname" "path_corner" "origin" "-472 -1620 -448" } { "classname" "weapon_shotgun" "spawnflags" "1792" "origin" "-332 -1636 -396" } { "classname" "item_armor_jacket" "spawnflags" "1792" "origin" "-1648 -456 -288" } { "model" "*17" "classname" "func_wall" "spawnflags" "2048" } { "spawnflags" "1792" "classname" "ammo_shells" "origin" "-2232 -588 -960" } { "classname" "ammo_shells" "spawnflags" "1792" "origin" "-2272 -588 -960" } { "classname" "ammo_bullets" "spawnflags" "1792" "origin" "-1716 104 -840" } { "classname" "weapon_machinegun" "spawnflags" "1792" "origin" "-1752 104 -840" } { "classname" "ammo_rockets" "spawnflags" "1792" "origin" "-1836 -136 -392" } { "spawnflags" "1792" "classname" "ammo_bullets" "origin" "-1640 -1044 32" } { "classname" "weapon_machinegun" "spawnflags" "1792" "origin" "-1640 -1008 32" } { "model" "*18" "classname" "func_wall" "spawnflags" "1792" } { "classname" "ammo_cells" "spawnflags" "1792" "origin" "-76 -764 -216" } { "origin" "-520 1208 -88" "classname" "light" "light" "250" } { "origin" "-676 1164 -88" "classname" "light" "light" "250" } { "origin" "-812 840 -156" "classname" "item_armor_shard" "spawnflags" "1792" } { "origin" "-812 1136 -156" "spawnflags" "1792" "classname" "item_armor_shard" } { "origin" "-368 -808 -212" "spawnflags" "1792" "classname" "ammo_bullets" } { "origin" "-312 -1564 -448" "spawnflags" "1792" "classname" "ammo_shells" } { "origin" "-640 -1568 -340" "_color" "1.000000 1.000000 0.000000" "light" "120" "classname" "light" } { "origin" "-604 -1664 -340" "classname" "light" "light" "120" "_color" "1.000000 1.000000 0.000000" } { "origin" "-576 -1568 -340" "_color" "1.000000 1.000000 0.000000" "light" "120" "classname" "light" } { "origin" "-876 -1740 -648" "classname" "light" "light" "120" } { "origin" "-808 -1740 -648" "light" "120" "classname" "light" } { "model" "*19" "target" "t179" "spawnflags" "2048" "classname" "trigger_once" } { "model" "*20" "origin" "-500 -1572 -704" "targetname" "t179" "distance" "180" "wait" "-1" "target" "t33" "spawnflags" "2120" "classname" "func_door_rotating" } { "model" "*21" "spawnflags" "2048" "classname" "func_wall" } { "spawnflags" "1792" "classname" "ammo_grenades" "origin" "-2272 -548 -956" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "-2232 -548 -956" } { "spawnflags" "1792" "classname" "item_health_small" "origin" "-2464 -1032 -952" } { "classname" "item_health_small" "spawnflags" "1792" "origin" "-2464 -1072 -952" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "-1924 -648 -928" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "-1924 -608 -928" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "-1924 -568 -928" } { "classname" "item_armor_shard" "spawnflags" "1792" "origin" "-1924 -688 -928" } { "classname" "item_armor_body" "spawnflags" "1792" "origin" "-1504 -1316 -868" } { "classname" "weapon_rocketlauncher" "spawnflags" "1792" "origin" "-1728 -700 -728" } { "spawnflags" "1792" "classname" "item_health" "origin" "-948 -1500 -732" } { "classname" "item_health" "spawnflags" "1792" "origin" "-988 -1500 -732" } { "classname" "weapon_supershotgun" "spawnflags" "1792" "origin" "-1056 -1988 -724" } { "spawnflags" "2048" "classname" "ammo_grenades" "origin" "-984 -2016 -788" } { "classname" "ammo_grenades" "spawnflags" "2048" "origin" "-944 -2016 -788" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "-1240 -2020 -728" } { "classname" "item_armor_shard" "spawnflags" "1792" "origin" "-1240 -1984 -728" } { "classname" "item_armor_shard" "spawnflags" "1792" "origin" "-1240 -1948 -728" } { "spawnflags" "1792" "classname" "item_health_small" "origin" "-888 -1984 -728" } { "spawnflags" "1792" "classname" "item_health_small" "origin" "-888 -1948 -728" } { "classname" "item_health_small" "spawnflags" "1792" "origin" "-888 -2020 -728" } { "classname" "item_armor_combat" "spawnflags" "1792" "origin" "-604 -1572 -424" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "-736 -1320 -272" } { "classname" "weapon_grenadelauncher" "spawnflags" "1792" "origin" "-776 -1344 -272" } { "origin" "-1712 52 -1252" "spawnflags" "1792" "classname" "item_health_mega" } { "origin" "816 -532 12" "classname" "ammo_shells" "spawnflags" "1792" } { "origin" "816 -568 12" "spawnflags" "1792" "classname" "ammo_shells" } { "origin" "816 -760 16" "spawnflags" "1792" "classname" "weapon_supershotgun" } { "origin" "884 -616 20" "spawnflags" "1792" "team" "pow" "classname" "item_quad" } { "origin" "884 -616 12" "spawnflags" "1792" "team" "pow" "classname" "item_invulnerability" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "340 -224 16" } { "classname" "ammo_bullets" "spawnflags" "1792" "origin" "-56 -592 24" } { "classname" "weapon_chaingun" "spawnflags" "1792" "origin" "-56 -552 24" } { "classname" "weapon_supershotgun" "spawnflags" "1792" "origin" "-220 1152 -136" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "-384 1256 -160" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "-344 1256 -160" } { "classname" "ammo_shells" "spawnflags" "1792" "origin" "-1104 640 -280" } { "classname" "ammo_cells" "origin" "-984 544 -432" } { "classname" "weapon_bfg" "spawnflags" "1792" "origin" "-1144 800 -424" } { "origin" "-1144 672 -280" "spawnflags" "1792" "classname" "weapon_supershotgun" } { "origin" "-1832 -16 -264" "classname" "ammo_bullets" "spawnflags" "1792" } { "origin" "-1760 -16 -264" "spawnflags" "1792" "classname" "ammo_bullets" } { "model" "*22" "classname" "func_wall" "spawnflags" "1792" } { "classname" "trigger_always" "spawnflags" "1792" "target" "t33" "origin" "-744 -1656 -648" } { "classname" "trigger_always" "spawnflags" "1792" "target" "t173" "origin" "-1464 -168 -304" } { "origin" "-96 680 -152" "angle" "135" "classname" "info_player_deathmatch" } { "origin" "-1088 -456 -296" "angle" "90" "classname" "info_player_deathmatch" } { "origin" "-416 336 -848" "angle" "180" "classname" "info_player_deathmatch" } { "origin" "-968 -972 -724" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "-1768 -1496 -724" "angle" "45" "classname" "info_player_deathmatch" } { "origin" "-160 -1336 -452" "angle" "225" "classname" "info_player_deathmatch" } { "origin" "-104 -616 -208" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "320 -296 24" "angle" "180" "classname" "info_player_deathmatch" } { "origin" "-424 1208 -152" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "-1840 -480 -296" "angle" "90" "classname" "info_player_deathmatch" } { "origin" "-1224 -1376 24" "angle" "135" "classname" "info_player_deathmatch" } { "origin" "-608 -1296 -688" "spawnflags" "2816" "classname" "item_health" } { "origin" "-608 -1376 -632" "spawnflags" "2816" "classname" "item_health_large" } { "origin" "-1440 -1528 80" "light" "130" "classname" "light" } { "model" "*23" "target" "t178" "targetname" "t177" "spawnflags" "2820" "classname" "trigger_once" } { "origin" "-272 232 40" "target" "t177" "targetname" "t176" "classname" "trigger_relay" } { "origin" "-248 192 40" "spawnflags" "256" "target" "t176" "targetname" "t133" "classname" "trigger_relay" } { "origin" "-304 752 -64" "spawnflags" "256" "targetname" "t175" "classname" "target_explosion" } { "origin" "-384 832 -136" "targetname" "t176" "spawnflags" "771" "angle" "315" "classname" "monster_gladiator" } { "model" "*24" "spawnflags" "2048" "targetname" "t178" "target" "t175" "mass" "400" "dmg" "5" "classname" "func_explosive" } { "model" "*25" "target" "t174" "classname" "trigger_once" "spawnflags" "2048" } { "classname" "light" "light" "100" "origin" "-2056 -88 -206" } { "classname" "light" "light" "130" "origin" "-2024 -128 -296" } { "classname" "light" "light" "100" "origin" "-1440 -280 -296" } { "classname" "target_goal" "targetname" "t173" "origin" "-2012 -104 -296" "spawnflags" "2048" } { "model" "*26" "target" "t173" "speed" "150" "wait" "-1" "angle" "180" "classname" "func_button" "spawnflags" "2048" } { "style" "3" "targetname" "t172" "classname" "func_areaportal" } { "model" "*27" "_minlight" ".2" "angle" "270" "lip" "132" "targetname" "t173" "target" "t172" "classname" "func_door" "speed" "40" "wait" "-1" } { "origin" "-416 -1344 -548" "targetname" "t157" "noise" "world/explod1.wav" "classname" "target_speaker" } { "spawnflags" "2048" "origin" "-780 -1324 -272" "classname" "item_health" } { "spawnflags" "2048" "origin" "-780 -1364 -272" "classname" "item_health" } { "model" "*28" "target" "t144" "targetname" "ho1" "spawnflags" "2052" "classname" "trigger_once" } { "classname" "info_player_intermission" "angles" "23 310 0" "origin" "-1784 56 104" } { "origin" "-1872 -552 -296" "classname" "ammo_shells" } { "origin" "-1872 -512 -296" "classname" "ammo_shells" } { "spawnflags" "0" "origin" "152 -1092 20" "classname" "item_armor_shard" } { "spawnflags" "0" "origin" "152 -1048 20" "classname" "item_armor_shard" } { "spawnflags" "2048" "origin" "-364 -600 -216" "classname" "item_health_large" } { "origin" "-1936 -208 -1208" "classname" "light" "light" "120" } { "origin" "-1872 -184 -1208" "classname" "light" "light" "120" } { "origin" "-1936 -216 -1152" "light" "120" "classname" "light" } { "spawnflags" "2048" "origin" "-1408 -1720 -720" "classname" "ammo_slugs" } { "origin" "48 -1376 -232" "angle" "45" "spawnflags" "2050" "classname" "misc_deadsoldier" } { "origin" "-604 -1572 -596" "targetname" "t33" "classname" "target_speaker" "noise" "world/pump1.wav" "spawnflags" "2" } { "origin" "-604 -1572 -532" "targetname" "t33" "spawnflags" "2" "noise" "world/pump1.wav" "classname" "target_speaker" } { "origin" "-604 -1572 -560" "targetname" "t33" "spawnflags" "2" "noise" "world/pump3.wav" "classname" "target_speaker" } { "spawnflags" "2048" "origin" "-1640 -948 28" "classname" "item_health_large" } { "spawnflags" "2048" "origin" "-1240 -948 28" "classname" "item_health_large" } { "spawnflags" "2048" "origin" "-424 -64 24" "classname" "ammo_bullets" } { "spawnflags" "2048" "origin" "-64 -656 20" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "-64 -696 20" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "-72 -756 -212" "classname" "ammo_grenades" } { "light" "140" "origin" "-272 -1740 -436" "classname" "light" } { "spawnflags" "2048" "origin" "-1304 -1704 -728" "classname" "item_armor_shard" } { "origin" "-1392 -1700 -744" "angle" "270" "spawnflags" "2056" "classname" "misc_deadsoldier" } { "spawnflags" "2048" "classname" "ammo_shells" "origin" "-416 -1820 -716" } { "spawnflags" "2048" "origin" "-416 -1856 -716" "classname" "item_health" } { "light" "140" "classname" "light" "origin" "-1648 -1248 -1084" } { "light" "140" "classname" "light" "origin" "-1500 -1260 -1084" } { "light" "140" "classname" "light" "origin" "-1244 -1260 -1084" } { "light" "140" "classname" "light" "origin" "-1184 -1144 -1084" } { "light" "140" "classname" "light" "origin" "-1188 -896 -1084" } { "light" "140" "classname" "light" "origin" "-1248 -776 -1084" } { "light" "140" "classname" "light" "origin" "-1416 -776 -1084" } { "light" "140" "classname" "light" "origin" "-1568 -776 -1084" } { "light" "140" "classname" "light" "origin" "-1620 -852 -1084" } { "light" "140" "classname" "light" "origin" "-1644 -992 -1084" } { "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" "origin" "-220 960 -172" } { "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" "origin" "-220 724 -172" } { "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" "origin" "-220 544 -124" } { "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" "origin" "-220 296 -8" } { "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" "origin" "-180 84 -8" } { "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" "origin" "76 84 -8" } { "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" "origin" "120 -120 -8" } { "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" "origin" "-52 -200 -8" } { "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" "origin" "-296 -200 -8" } { "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" "origin" "-844 208 -876" } { "classname" "misc_deadsoldier" "spawnflags" "2048" "angle" "45" "origin" "-324 -776 -236" } { "model" "*29" "speed" "200" "wait" "-1" "target" "t1" "angle" "270" "classname" "func_button" "spawnflags" "2048" } { "style" "4" "classname" "func_areaportal" "targetname" "t109" } { "style" "5" "classname" "func_areaportal" "targetname" "t171" } { "model" "*30" "_minlight" ".18" "classname" "func_door" "angle" "-1" "spawnflags" "8" "target" "t171" } { "spawnflags" "2048" "classname" "item_health" "origin" "-812 840 -156" } { "spawnflags" "2048" "classname" "item_health" "origin" "-812 1136 -156" } { "origin" "-960 524 -408" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" } { "target" "t169" "wait" "10" "random" "8" "spawnflags" "1" "classname" "func_timer" "origin" "-960 628 -356" } { "target" "t170" "wait" "16" "random" "5" "origin" "-1144 824 -368" "classname" "func_timer" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-428 604 -352" } { "targetname" "t170" "classname" "target_speaker" "noise" "world/drip_amb.wav" "spawnflags" "1" "origin" "-1152 892 -368" } { "classname" "target_speaker" "noise" "world/drip_amb.wav" "spawnflags" "1" "origin" "-960 992 -408" } { "classname" "target_speaker" "noise" "world/water1.wav" "spawnflags" "1" "origin" "-1024 988 -336" } { "targetname" "kill1" "classname" "target_speaker" "noise" "world/water1.wav" "spawnflags" "1" "origin" "-912 988 -336" } { "volume" ".5" "classname" "target_speaker" "noise" "world/amb10.wav" "spawnflags" "1" "origin" "-1036 988 -176" } { "classname" "target_speaker" "noise" "world/drip_amb.wav" "spawnflags" "1" "origin" "116 84 48" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "284 -128 48" } { "origin" "-220 272 48" "spawnflags" "1" "noise" "world/drip_amb.wav" "classname" "target_speaker" } { "origin" "332 -184 116" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/water1.wav" "spawnflags" "1" "origin" "332 -448 -16" } { "classname" "target_speaker" "noise" "world/water1.wav" "spawnflags" "1" "origin" "52 -448 -16" } { "origin" "352 -704 -16" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "origin" "188 -704 -16" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "origin" "188 -736 32" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" } { "origin" "88 -636 116" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "origin" "-376 -168 116" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "origin" "-224 -428 116" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/water1.wav" "spawnflags" "1" "origin" "-148 -436 -16" } { "origin" "-224 -636 116" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-52 -128 48" } { "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "68 -1120 116" } { "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "-220 -916 -28" } { "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "-164 -1116 -28" } { "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "92 -1116 -28" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "92 -1116 -168" } { "origin" "88 -428 116" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "148 -1112 -168" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "88 -1344 -168" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "-120 -1344 -168" } { "origin" "-104 -1116 -168" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-340 -1344 -168" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-272 -1752 -376" "spawnflags" "1" "noise" "world/amb10.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "-896 -1740 -676" } { "killtarget" "kill1" "origin" "-324 -1724 -424" "target" "t168" "targetname" "t1" "classname" "trigger_relay" "spawnflags" "2048" } { "targetname" "t168" "origin" "-708 -1712 -376" "spawnflags" "2" "noise" "world/amb12.wav" "classname" "target_speaker" } { "targetname" "t168" "origin" "-496 -1716 -376" "spawnflags" "2" "noise" "world/amb12.wav" "classname" "target_speaker" } { "targetname" "t168" "classname" "target_speaker" "noise" "world/amb12.wav" "spawnflags" "2" "origin" "-496 -1432 -376" } { "targetname" "t168" "origin" "-712 -1432 -376" "spawnflags" "2" "noise" "world/amb12.wav" "classname" "target_speaker" } { "targetname" "t168" "classname" "target_speaker" "noise" "world/force1.wav" "spawnflags" "2" "origin" "-712 -1432 -456" } { "targetname" "t168" "classname" "target_speaker" "noise" "world/force1.wav" "spawnflags" "2" "origin" "-708 -1712 -456" } { "targetname" "t168" "classname" "target_speaker" "noise" "world/force1.wav" "spawnflags" "2" "origin" "-496 -1716 -456" } { "classname" "target_speaker" "noise" "world/amb10.wav" "spawnflags" "1" "origin" "-28 -1344 -220" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-272 -1752 -400" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-256 -1252 -400" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-400 -1256 -688" } { "origin" "-704 -1904 -688" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" } { "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "origin" "-604 -1792 -656" } { "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "origin" "-604 -1816 -656" } { "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" "origin" "-512 -1904 -688" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-816 -1264 -704" } { "targetname" "t168" "origin" "-496 -1432 -456" "spawnflags" "2" "noise" "world/force1.wav" "classname" "target_speaker" } { "origin" "-340 -1344 -676" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-1176 -1948 -676" "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" } { "origin" "-1196 -1720 -676" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-940 -1948 -676" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-1056 -2100 -744" "classname" "target_speaker" "noise" "world/water1.wav" "spawnflags" "1" } { "origin" "-1176 -2100 -744" "classname" "target_speaker" "noise" "world/water1.wav" "spawnflags" "1" } { "origin" "-1064 -1932 -688" "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" } { "origin" "-604 -1704 -656" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" } { "origin" "-1392 -1680 -676" "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" } { "origin" "-1388 -1520 -676" "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" } { "origin" "-1080 -1108 -676" "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" } { "origin" "-1276 -1344 -676" "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" } { "origin" "-1716 -1176 -676" "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" } { "origin" "-932 -2100 -744" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "origin" "-1680 -1296 -816" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-1132 -728 -816" "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" } { "origin" "-1424 -720 -816" "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" } { "origin" "-1680 -720 -816" "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" } { "origin" "-1716 -816 -676" "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" } { "origin" "-1132 -1296 -816" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/drip_amb.wav" "origin" "-2284 -1032 -928" } { "origin" "-2472 -688 -956" "target" "t166" "spawnflags" "1" "classname" "func_timer" "random" "4" "wait" "12" } { "origin" "-2472 -824 -956" "target" "t165" "spawnflags" "1" "classname" "func_timer" "random" "4" "wait" "9" } { "origin" "-2472 -544 -956" "target" "t167" "spawnflags" "1" "wait" "11" "random" "8" "classname" "func_timer" } { "targetname" "t166" "classname" "target_speaker" "spawnflags" "0" "noise" "world/steam1.wav" "origin" "-2500 -688 -956" } { "targetname" "t165" "classname" "target_speaker" "spawnflags" "0" "noise" "world/steam1.wav" "origin" "-2500 -824 -956" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb14.wav" "origin" "-2332 -724 -912" } { "targetname" "t167" "origin" "-2500 -544 -956" "noise" "world/steam1.wav" "spawnflags" "0" "classname" "target_speaker" } { "origin" "-1868 -704 -912" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-2368 -1140 -912" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-2028 -996 -980" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-2056 -684 -928" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-2056 -400 -928" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1980 208 -800" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-2172 -96 -1120" "target" "t163" "classname" "func_timer" "spawnflags" "1" "random" "4" "wait" "13" } { "origin" "-2172 -224 -1120" "target" "t162" "classname" "func_timer" "spawnflags" "1" "random" "6" "wait" "15" } { "origin" "-2172 32 -1120" "target" "t164" "wait" "12" "random" "7" "spawnflags" "1" "classname" "func_timer" } { "targetname" "t163" "origin" "-2192 -96 -1120" "noise" "world/steam1.wav" "spawnflags" "0" "classname" "target_speaker" } { "targetname" "t162" "origin" "-2192 -224 -1120" "noise" "world/steam1.wav" "spawnflags" "0" "classname" "target_speaker" } { "origin" "-2052 -252 -1128" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-2064 -196 -1164" "target" "t161" "classname" "func_timer" "wait" "12" "random" "3" "spawnflags" "1" } { "targetname" "t161" "classname" "target_speaker" "spawnflags" "0" "noise" "world/drip2.wav" "origin" "-2040 -196 -1164" } { "origin" "-1948 -192 -1116" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "targetname" "t160" "classname" "target_speaker" "spawnflags" "0" "noise" "world/drip1.wav" "origin" "-2044 -8 -1116" } { "targetname" "t164" "classname" "target_speaker" "spawnflags" "0" "noise" "world/steam1.wav" "origin" "-2192 32 -1120" } { "origin" "-2068 -8 -1116" "target" "t160" "spawnflags" "1" "wait" "8" "random" "4" "classname" "func_timer" } { "origin" "-1724 108 -1204" "target" "t159" "spawnflags" "1" "random" "3" "wait" "12" "classname" "func_timer" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb14.wav" "origin" "-2332 -1012 -912" } { "targetname" "t159" "origin" "-1700 108 -1204" "noise" "world/drip2.wav" "spawnflags" "0" "classname" "target_speaker" } { "origin" "-896 208 -800" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1192 208 -800" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1532 208 -800" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1564 -16 -628" "noise" "world/amb15.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1568 -192 -488" "noise" "world/amb15.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1300 -192 -488" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb15.wav" } { "origin" "-1296 -16 -628" "noise" "world/amb15.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-504 212 -636" "noise" "world/pump3.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-356 352 -800" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "-2072 236 -800" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "-504 212 -796" "classname" "target_speaker" "spawnflags" "1" "noise" "world/pump3.wav" } { "origin" "-356 96 -800" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "volume" ".2" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "-220 704 -44" } { "volume" ".2" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "-256 984 -44" } { "volume" ".2" "origin" "20 1056 -140" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "volume" ".2" "origin" "-220 1220 -44" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "volume" ".2" "origin" "-432 984 -44" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "origin" "-608 984 -44" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "origin" "-788 984 -116" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "volume" ".5" "origin" "-836 984 -176" "spawnflags" "1" "noise" "world/amb10.wav" "classname" "target_speaker" } { "targetname" "t169" "origin" "-960 608 -356" "spawnflags" "0" "noise" "world/drip1.wav" "classname" "target_speaker" } { "volume" ".5" "origin" "-1144 804 -176" "spawnflags" "1" "noise" "world/amb10.wav" "classname" "target_speaker" } { "volume" ".5" "origin" "-1292 688 -176" "spawnflags" "1" "noise" "world/amb10.wav" "classname" "target_speaker" } { "volume" ".5" "origin" "-1444 632 -176" "spawnflags" "1" "noise" "world/amb10.wav" "classname" "target_speaker" } { "volume" ".5" "origin" "-1444 460 -176" "spawnflags" "1" "noise" "world/amb10.wav" "classname" "target_speaker" } { "origin" "-1444 336 -144" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "volume" ".2" "origin" "-1444 196 -256" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "volume" ".2" "origin" "-1116 48 -256" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "volume" ".2" "origin" "-1828 48 -256" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "volume" ".2" "origin" "-1672 -320 -256" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "volume" ".2" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "-24 832 -140" } { "volume" ".2" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "-1440 -216 -172" } { "volume" ".2" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "-1440 -412 -172" } { "volume" ".2" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "-1440 -608 -172" } { "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-1444 -812 -172" } { "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-1444 -932 -92" } { "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-1444 -932 224" } { "classname" "target_speaker" "noise" "world/pump1.wav" "spawnflags" "1" "origin" "-1340 -1040 168" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-1712 -992 88" } { "classname" "target_speaker" "noise" "world/pump1.wav" "spawnflags" "1" "origin" "-1548 -1304 168" } { "classname" "target_speaker" "noise" "world/water1.wav" "spawnflags" "1" "origin" "-1548 -1304 24" } { "origin" "-1548 -1040 24" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/water1.wav" "spawnflags" "1" "origin" "-1340 -1040 24" } { "origin" "-1340 -1304 24" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/pump1.wav" "spawnflags" "1" "origin" "-1548 -1040 168" } { "origin" "-1176 -992 88" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/pump1.wav" "spawnflags" "1" "origin" "-1340 -1304 168" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-1444 -1600 128" } { "volume" ".2" "origin" "-1216 -320 -256" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "classname" "item_health_small" "origin" "-872 1016 -420" "spawnflags" "2048" } { "classname" "item_health_small" "origin" "-872 952 -420" "spawnflags" "2048" } { "spawnflags" "0" "classname" "item_health" "origin" "420 -64 24" } { "spawnflags" "0" "classname" "item_health" "origin" "420 -100 24" } { "classname" "misc_deadsoldier" "spawnflags" "2056" "angle" "180" "origin" "-296 -1360 -752" } { "spawnflags" "2048" "classname" "item_quad" "origin" "-276 -1376 -724" } { "spawnflags" "0" "classname" "item_armor_shard" "origin" "152 -1180 20" } { "classname" "misc_deadsoldier" "angle" "45" "spawnflags" "2050" "origin" "-236 -1304 -476" } { "style" "6" "classname" "func_areaportal" "targetname" "t157" } { "classname" "target_secret" "message" "You have found a secret." "targetname" "t158" "origin" "-352 -1328 -712" } { "model" "*31" "classname" "trigger_once" "target" "t158" } { "spawnflags" "0" "classname" "item_health" "origin" "-416 -1892 -716" } { "delay" ".3" "dmg" "10" "classname" "target_explosion" "targetname" "t157" "origin" "-392 -1318 -648" } { "classname" "target_explosion" "dmg" "10" "delay" ".6" "targetname" "t157" "origin" "-400 -1374 -696" } { "model" "*32" "targetname" "exp69" "classname" "func_explosive" "mass" "100" "dmg" "10" "target" "t157" } { "spawnflags" "2816" "classname" "weapon_rocketlauncher" "origin" "-696 -1816 -728" } { "classname" "misc_deadsoldier" "spawnflags" "2816" "angle" "135" "origin" "-720 -1840 -744" } { "model" "*33" "spawnflags" "4" "classname" "func_train" "target" "t27" } { "model" "*34" "spawnflags" "4" "classname" "func_train" "target" "t31" } { "spawnflags" "0" "classname" "ammo_rockets" "origin" "-1776 -720 -728" } { "spawnflags" "0" "classname" "ammo_rockets" "origin" "-1776 -680 -728" } { "spawnflags" "2048" "origin" "-1536 -1312 -864" "targetname" "t155" "message" "You have found a secret." "classname" "target_secret" } { "spawnflags" "2048" "origin" "-2464 -1112 -952" "classname" "item_health_large" } { "spawnflags" "2048" "classname" "ammo_shells" "origin" "-112 1132 -148" } { "spawnflags" "0" "origin" "-76 1132 -148" "classname" "ammo_shells" } { "spawnflags" "2048" "message" "You have found a secret." "classname" "target_secret" "targetname" "t154" "origin" "-1712 -48 -1232" } { "spawnflags" "2048" "classname" "target_help" "targetname" "t36" "origin" "-176 -1656 -184" "message" "Coolant systems drained.\nReturn to the Reactor.\n" } { "spawnflags" "2048" "classname" "weapon_railgun" "origin" "-288 -1320 -720" } { "spawnflags" "2048" "classname" "target_goal" "targetname" "t36" "origin" "-176 -1656 -120" } { "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" "origin" "-552 208 -872" } { "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" "origin" "-844 208 -872" } { "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" "origin" "-1040 208 -872" } { "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" "origin" "-1276 208 -872" } { "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" "origin" "-1504 208 -872" } { "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" "origin" "-1752 208 -872" } { "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" "origin" "-1952 208 -872" } { "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" "origin" "-2056 124 -872" } { "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" "origin" "-2056 -36 -892" } { "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" "origin" "-2056 -196 -960" } { "origin" "-2056 -392 -976" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "origin" "-2052 -592 -976" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "origin" "-2052 -752 -976" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "origin" "-2256 -752 -976" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "origin" "-2312 -924 -976" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "origin" "-2140 -992 -980" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "origin" "-1940 -992 -1056" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "origin" "-1760 -992 -1116" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/water1.wav" "spawnflags" "1" "origin" "-1544 -776 -1116" } { "origin" "-1280 -776 -1116" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "origin" "-1280 -996 -1116" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/water1.wav" "spawnflags" "1" "origin" "-1544 -996 -1116" } { "origin" "-1544 -1220 -1116" "spawnflags" "1" "noise" "world/water1.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/water1.wav" "spawnflags" "1" "origin" "-1280 -1220 -1116" } { "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" "origin" "-1544 -1220 -1120" } { "origin" "-1544 -996 -1120" "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" } { "origin" "-1544 -776 -1120" "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" "origin" "-1280 -776 -1120" } { "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" "origin" "-1280 -996 -1120" } { "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" "origin" "-1760 -992 -1120" } { "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" "origin" "-1940 -992 -1060" } { "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" "origin" "-2140 -992 -984" } { "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" "origin" "-2312 -924 -980" } { "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" "origin" "-2256 -752 -980" } { "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" "origin" "-2052 -752 -980" } { "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" "origin" "-2052 -592 -980" } { "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" "origin" "-2056 -392 -980" } { "origin" "-2056 -196 -964" "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" } { "origin" "-2056 -36 -896" "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" } { "origin" "-2056 124 -876" "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" } { "origin" "-1952 208 -876" "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" } { "origin" "-1752 208 -876" "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" } { "origin" "-1504 208 -876" "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" } { "origin" "-1276 208 -876" "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" } { "origin" "-1040 208 -876" "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" } { "origin" "-220 1232 -172" "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" } { "origin" "-552 208 -876" "classname" "target_speaker" "noise" "world/curnt2.wav" "spawnflags" "1" } { "origin" "-1280 -1220 -1120" "spawnflags" "1" "noise" "world/curnt2.wav" "classname" "target_speaker" } { "classname" "light" "light" "140" "origin" "-664 208 -856" } { "classname" "light" "light" "140" "origin" "-816 208 -856" } { "classname" "light" "light" "140" "origin" "-960 208 -856" } { "classname" "light" "light" "140" "origin" "-1104 208 -856" } { "classname" "light" "light" "140" "origin" "-1268 208 -856" } { "classname" "light" "light" "140" "origin" "-1428 208 -856" } { "classname" "light" "light" "140" "origin" "-1608 208 -856" } { "classname" "light" "light" "140" "origin" "-1780 208 -856" } { "classname" "light" "light" "140" "origin" "-1912 208 -856" } { "classname" "light" "light" "140" "origin" "-2052 208 -856" } { "classname" "light" "light" "140" "origin" "-2052 52 -860" } { "classname" "light" "light" "140" "origin" "-2052 -96 -900" } { "classname" "light" "light" "140" "origin" "-2052 -260 -940" } { "classname" "light" "light" "140" "origin" "-2052 -376 -964" } { "classname" "light" "light" "140" "origin" "-2052 -516 -964" } { "light" "140" "classname" "light" "origin" "-1792 -992 -1028" } { "light" "140" "classname" "light" "origin" "-1920 -992 -992" } { "light" "140" "classname" "light" "origin" "-2052 -992 -964" } { "light" "140" "classname" "light" "origin" "-2176 -992 -964" } { "light" "140" "classname" "light" "origin" "-2312 -992 -964" } { "light" "140" "classname" "light" "origin" "-2312 -860 -964" } { "light" "140" "classname" "light" "origin" "-2312 -736 -964" } { "light" "140" "classname" "light" "origin" "-2176 -736 -964" } { "light" "140" "classname" "light" "origin" "-2056 -736 -964" } { "origin" "-2052 -624 -964" "light" "140" "classname" "light" } { "origin" "-1648 -1132 -1084" "classname" "light" "light" "140" } { "origin" "-528 208 -856" "light" "140" "classname" "light" } { "origin" "416 -600 12" "classname" "light" "light" "150" } { "origin" "296 -600 12" "classname" "light" "light" "150" } { "origin" "184 -600 12" "classname" "light" "light" "150" } { "origin" "52 -600 12" "classname" "light" "light" "150" } { "origin" "416 -440 12" "classname" "light" "light" "150" } { "origin" "296 -440 12" "classname" "light" "light" "150" } { "origin" "184 -440 12" "classname" "light" "light" "150" } { "origin" "52 -440 12" "classname" "light" "light" "150" } { "light" "150" "classname" "light" "origin" "416 -752 12" } { "light" "150" "classname" "light" "origin" "296 -752 12" } { "light" "150" "classname" "light" "origin" "184 -752 12" } { "light" "150" "classname" "light" "origin" "52 -752 12" } { "light" "150" "classname" "light" "origin" "-84 -440 12" } { "light" "150" "classname" "light" "origin" "-200 -440 12" } { "light" "140" "classname" "light" "origin" "-436 -204 12" } { "light" "140" "classname" "light" "origin" "-288 -200 12" } { "light" "140" "classname" "light" "origin" "-156 -204 12" } { "light" "140" "classname" "light" "origin" "-28 -204 12" } { "light" "140" "classname" "light" "origin" "120 -204 12" } { "light" "140" "classname" "light" "origin" "120 -84 12" } { "light" "140" "classname" "light" "origin" "120 20 12" } { "light" "140" "classname" "light" "origin" "48 84 12" } { "light" "140" "classname" "light" "origin" "-80 84 12" } { "light" "140" "classname" "light" "origin" "-216 84 12" } { "spawnflags" "2048" "classname" "weapon_hyperblaster" "origin" "-2288 -1100 -968" } { "classname" "misc_deadsoldier" "spawnflags" "2048" "angle" "180" "origin" "-2268 -1116 -984" } { "model" "*35" "spawnflags" "2048" "classname" "trigger_once" "target" "t154" } { "spawnflags" "2048" "classname" "item_adrenaline" "origin" "-1712 52 -1252" } { "spawnflags" "0" "origin" "-1712 112 -1248" "classname" "ammo_grenades" } { "spawnflags" "0" "origin" "-2284 -632 -944" "classname" "misc_explobox" "mass" "80" "health" "25" "dmg" "40" } { "spawnflags" "2048" "origin" "-2452 -880 -944" "classname" "misc_explobox" "mass" "80" "health" "25" "dmg" "40" } { "classname" "func_group" } { "spawnflags" "2048" "origin" "-600 32 -864" "classname" "ammo_rockets" } { "spawnflags" "2048" "classname" "item_health" "origin" "-76 656 -160" } { "spawnflags" "2048" "classname" "item_health" "origin" "-40 656 -160" } { "origin" "28 1088 -144" "classname" "light" "light" "140" } { "origin" "-220 1104 -116" "classname" "light" "light" "140" } { "light" "96" "classname" "light" "origin" "-1096 24 -816" } { "classname" "light" "light" "96" "origin" "-1096 80 -848" } { "light" "96" "classname" "light" "origin" "-1848 80 -848" } { "classname" "light" "light" "96" "origin" "-1848 24 -816" } { "light" "120" "classname" "light" "origin" "-1672 -1248 440" } { "classname" "light" "light" "120" "origin" "-1672 -1160 440" } { "classname" "light" "light" "120" "origin" "-1672 -1072 440" } { "light" "120" "classname" "light" "origin" "-1216 -1152 440" } { "light" "120" "classname" "light" "origin" "-1216 -1064 440" } { "classname" "light" "light" "120" "origin" "-1216 -1240 440" } { "light" "96" "classname" "light" "origin" "-1392 -1116 44" } { "classname" "light" "light" "96" "origin" "-1240 -1052 40" } { "classname" "light" "light" "130" "origin" "-1440 -1296 80" } { "classname" "light" "light" "130" "origin" "-1440 -1368 80" } { "classname" "light" "light" "130" "origin" "-1440 -1424 80" } { "light" "200" "classname" "light" "origin" "-1444 -1252 -60" } { "light" "200" "classname" "light" "origin" "-1444 -1132 -60" } { "classname" "light" "light" "200" "origin" "-1444 -1392 -60" } { "origin" "-1340 -1116 -60" "classname" "light" "light" "200" } { "classname" "light" "light" "200" "origin" "-1340 -1248 -60" } { "origin" "-1340 -1420 -60" "light" "200" "classname" "light" } { "origin" "-1552 -1216 -60" "light" "200" "classname" "light" } { "origin" "-1288 -1256 -60" "classname" "light" "light" "200" } { "origin" "-1288 -1112 -60" "classname" "light" "light" "200" } { "origin" "-1288 -968 -60" "classname" "light" "light" "200" } { "origin" "-1712 -992 136" "light" "120" "classname" "light" } { "classname" "light" "light" "120" "origin" "-1176 -992 136" } { "origin" "-1440 -1196 80" "light" "130" "classname" "light" } { "light" "200" "classname" "light" "origin" "-1552 -1084 -60" } { "light" "200" "classname" "light" "origin" "-1288 -1372 -60" } { "light" "200" "classname" "light" "origin" "-1608 -1004 -60" } { "light" "200" "classname" "light" "origin" "-1556 -1148 -60" } { "light" "200" "classname" "light" "origin" "-1556 -1280 -60" } { "classname" "light" "light" "200" "origin" "-1552 -1388 -60" } { "spawnflags" "2048" "classname" "ammo_rockets" "origin" "-1384 -1064 16" } { "spawnflags" "2048" "classname" "ammo_shells" "origin" "-1224 -1108 16" } { "spawnflags" "0" "classname" "item_health_small" "origin" "-1792 48 -304" } { "spawnflags" "0" "classname" "item_health_small" "origin" "-1828 48 -304" } { "spawnflags" "0" "classname" "item_health_small" "origin" "-1756 48 -304" } { "classname" "misc_deadsoldier" "spawnflags" "2048" "angle" "315" "origin" "-1784 -36 -288" } { "angle" "45" "spawnflags" "2048" "classname" "misc_deadsoldier" "origin" "-1240 -1052 0" } { "classname" "misc_deadsoldier" "spawnflags" "2048" "angle" "90" "origin" "-1392 -1116 0" } { "spawnflags" "2048" "classname" "target_help" "message" "Activate cooling pump to\nlower coolant around\nreactor." "targetname" "t153" "origin" "-1484 -1508 24" } { "model" "*36" "classname" "trigger_once" "target" "t153" } { "spawnflags" "2048" "origin" "-1664 -1348 20" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "-1664 -1384 20" "classname" "item_health_small" } { "origin" "-152 -1536 -200" "targetname" "t36" "spawnflags" "2" "classname" "target_crosslevel_trigger" } { "model" "*37" "target" "t180" "dmg" "5" "spawnflags" "4" "classname" "func_train" "speed" "80" "targetname" "t1" } { "model" "*38" "dmg" "5" "spawnflags" "4" "classname" "func_train" "target" "t76" "targetname" "t80" "speed" "70" } { "model" "*39" "spawnflags" "4" "classname" "func_train" "target" "t28" } { "model" "*40" "spawnflags" "4" "classname" "func_train" "target" "t29" } { "spawnflags" "2048" "dmg" "40" "health" "25" "mass" "80" "classname" "misc_explobox" "origin" "-2164 -620 -944" } { "spawnflags" "2048" "classname" "item_health" "origin" "-1768 44 -1248" } { "spawnflags" "2048" "classname" "item_health" "origin" "-1656 48 -1248" } { "spawnflags" "0" "classname" "ammo_bullets" "origin" "-1672 112 -1248" } { "spawnflags" "0" "classname" "ammo_bullets" "origin" "-1752 112 -1248" } { "spawnflags" "0" "classname" "item_armor_shard" "origin" "-368 80 -864" } { "spawnflags" "0" "classname" "item_health_small" "origin" "-368 124 -864" } { "spawnflags" "0" "classname" "item_armor_shard" "origin" "-368 168 -864" } { "spawnflags" "0" "classname" "item_health_small" "origin" "-368 212 -864" } { "spawnflags" "0" "classname" "item_armor_shard" "origin" "-368 256 -864" } { "spawnflags" "0" "classname" "item_health_small" "origin" "-368 300 -864" } { "spawnflags" "0" "classname" "item_armor_shard" "origin" "-368 344 -864" } { "classname" "ammo_slugs" "origin" "-1124 792 -428" "spawnflags" "2048" } { "classname" "monster_chick" "angle" "90" "spawnflags" "769" "target" "t117" "origin" "-216 -1732 -420" } { "classname" "light" "light" "120" "origin" "-396 212 -516" } { "model" "*41" "classname" "trigger_once" "target" "t148" "spawnflags" "2048" } { "classname" "monster_chick" "target" "t116" "spawnflags" "769" "angle" "270" "origin" "-312 -1328 -416" "item" "ammo_rockets" } { "classname" "path_corner" "target" "t145" "targetname" "t146" "origin" "-1024 -1456 -712" } { "classname" "path_corner" "targetname" "t145" "target" "t146" "origin" "-1368 -1456 -712" } { "targetname" "t174" "classname" "monster_berserk" "angle" "180" "spawnflags" "770" "target" "t146" "origin" "-992 -1456 -696" } { "classname" "monster_berserk" "targetname" "t144" "spawnflags" "257" "angle" "180" "origin" "-1152 992 -264" } { "model" "*42" "classname" "trigger_once" "target" "t143" "spawnflags" "2048" } { "classname" "monster_berserk" "angle" "225" "spawnflags" "1" "targetname" "t144" "origin" "-1144 760 -272" } { "classname" "path_corner" "target" "t141" "targetname" "t142" "origin" "96 -1116 64" } { "classname" "path_corner" "targetname" "t141" "target" "t142" "origin" "-260 -1116 64" } { "classname" "monster_hover" "spawnflags" "1" "target" "t142" "angle" "180" "origin" "128 -1116 80" } { "classname" "monster_chick" "angle" "225" "spawnflags" "769" "origin" "-1920 -524 -940" } { "spawnflags" "2048" "classname" "item_health_small" "origin" "-1664 -1312 20" } { "spawnflags" "0" "classname" "weapon_chaingun" "origin" "-1796 -52 -264" } { "spawnflags" "1" "classname" "point_combat" "targetname" "t137" "origin" "-1248 -416 -304" } { "spawnflags" "1" "classname" "point_combat" "targetname" "t138" "origin" "-1648 -416 -304" } { "spawnflags" "0" "classname" "ammo_grenades" "origin" "-600 392 -864" } { "spawnflags" "0" "classname" "item_health" "origin" "-1112 -360 -400" } { "spawnflags" "0" "classname" "item_health" "origin" "-1112 -320 -400" } { "model" "*43" "spawnflags" "2052" "targetname" "ho1" "classname" "trigger_once" "target" "t133" } { "spawnflags" "3" "angle" "270" "classname" "monster_hover" "targetname" "t133" "origin" "-408 1048 320" } { "classname" "monster_hover" "angle" "270" "spawnflags" "771" "targetname" "t133" "origin" "-168 1152 80" } { "classname" "point_combat" "targetname" "t130" "origin" "208 -424 256" } { "classname" "point_combat" "targetname" "t129" "spawnflags" "1" "origin" "0 -312 280" } { "classname" "point_combat" "spawnflags" "0" "targetname" "t131" "origin" "132 -668 256" } { "classname" "monster_hover" "target" "t129" "spawnflags" "3" "angle" "225" "origin" "260 -164 348" "targetname" "t132" } { "classname" "monster_hover" "angle" "225" "spawnflags" "771" "target" "t130" "origin" "368 -196 348" "targetname" "t132" "item" "ammo_cells" } { "classname" "monster_hover" "angle" "180" "spawnflags" "771" "target" "t131" "targetname" "t132" "origin" "384 -672 300" } { "model" "*44" "spawnflags" "2048" "classname" "trigger_once" "target" "t132" } { "spawnflags" "2048" "classname" "misc_explobox" "origin" "-52 -560 8" } { "spawnflags" "2048" "classname" "misc_explobox" "origin" "-72 -616 8" } { "spawnflags" "2048" "origin" "-380 -100 8" "classname" "misc_explobox" } { "spawnflags" "2048" "classname" "misc_explobox" "origin" "-100 -620 -192" } { "classname" "func_group" } { "classname" "func_group" } { "spawnflags" "0" "classname" "item_armor_shard" "origin" "152 -1136 20" } { "classname" "point_combat" "targetname" "t128" "spawnflags" "1" "origin" "396 -284 8" } { "classname" "monster_gunner" "angle" "270" "target" "t128" "spawnflags" "1" "origin" "360 -160 24" } { "classname" "point_combat" "targetname" "t127" "spawnflags" "1" "origin" "256 -172 8" } { "spawnflags" "0" "classname" "ammo_bullets" "origin" "-76 -804 -212" } { "spawnflags" "2048" "classname" "ammo_shells" "origin" "-368 -808 -212" } { "spawnflags" "0" "classname" "ammo_bullets" "origin" "-328 -808 -212" } { "classname" "path_corner" "targetname" "t126" "target" "t121" "origin" "-220 -920 -200" } { "classname" "path_corner" "targetname" "t125" "target" "t126" "origin" "-220 -1120 -200" } { "classname" "path_corner" "targetname" "t124" "target" "t125" "origin" "36 -1120 -200" } { "classname" "path_corner" "targetname" "t123" "target" "t124" "origin" "36 -1120 -200" } { "classname" "path_corner" "targetname" "t122" "target" "t123" "origin" "-220 -1120 -200" } { "classname" "path_corner" "targetname" "t121" "target" "t122" "origin" "-220 -920 -200" } { "classname" "path_corner" "targetname" "t120" "target" "t121" "origin" "-220 -792 -200" } { "item" "ammo_slugs" "classname" "monster_gladiator" "angle" "270" "target" "t120" "spawnflags" "1" "origin" "-220 -740 -184" } { "origin" "-100 812 276" "light" "150" "classname" "light" "_color" "1.000000 0.972549 0.043137" } { "_color" "1.000000 0.972549 0.043137" "classname" "light" "light" "150" "origin" "-48 864 276" } { "origin" "-44 1112 276" "light" "150" "classname" "light" "_color" "1.000000 0.972549 0.043137" } { "_color" "1.000000 0.972549 0.043137" "classname" "light" "light" "150" "origin" "-96 1164 276" } { "origin" "-352 1164 276" "light" "150" "classname" "light" "_color" "1.000000 0.972549 0.043137" } { "_color" "1.000000 0.972549 0.043137" "classname" "light" "light" "150" "origin" "-404 1112 276" } { "origin" "-352 812 276" "light" "150" "classname" "light" "_color" "1.000000 0.972549 0.043137" } { "origin" "-368 1128 276" "light" "150" "classname" "light" "_color" "1.000000 0.972549 0.043137" } { "classname" "path_corner" "target" "t117" "targetname" "t118" "origin" "-216 -1372 -436" } { "classname" "path_corner" "targetname" "t117" "target" "t118" "origin" "-216 -1736 -436" } { "classname" "path_corner" "targetname" "t115" "target" "t116" "origin" "-312 -1536 -436" } { "classname" "path_corner" "target" "t115" "targetname" "t116" "origin" "-312 -1344 -436" } { "model" "*45" "classname" "trigger_once" "target" "t114" "spawnflags" "2048" } { "classname" "monster_chick" "angle" "270" "targetname" "t114" "origin" "-796 -1328 -720" } { "classname" "point_combat" "targetname" "t112" "spawnflags" "1" "origin" "-448 -1596 -724" } { "item" "ammo_bullets" "classname" "monster_gunner" "angle" "135" "target" "t112" "origin" "-456 -1856 -708" "targetname" "t114" "spawnflags" "1" } { "spawnflags" "2048" "classname" "item_armor_body" "origin" "-252 -1344 -724" } { "spawnflags" "1" "classname" "monster_gunner" "angle" "270" "origin" "-960 -1752 -712" } { "item" "ammo_bullets" "classname" "monster_gunner" "angle" "90" "spawnflags" "1" "target" "t59" "targetname" "t109" "origin" "-1176 -1888 -708" } { "spawnflags" "2048" "target" "t155" "classname" "item_health_mega" "origin" "-1504 -1316 -880" } { "classname" "path_corner" "target" "t107" "targetname" "t108" "origin" "-964 -984 -728" } { "classname" "path_corner" "targetname" "t107" "target" "t108" "origin" "-1052 -1436 -728" } { "targetname" "t174" "classname" "monster_berserk" "angle" "270" "target" "t108" "origin" "-1012 -1088 -712" "spawnflags" "3" } { "classname" "path_corner" "target" "t105" "targetname" "t106" "origin" "-1708 -1316 -732" } { "classname" "path_corner" "targetname" "t104" "target" "t105" "origin" "-1744 -1448 -732" } { "classname" "path_corner" "target" "t104" "targetname" "t105" "origin" "-1740 -1320 -732" } { "classname" "path_corner" "targetname" "t105" "target" "t104" "origin" "-1448 -1448 -732" } { "targetname" "t174" "classname" "monster_berserk" "target" "t106" "spawnflags" "3" "origin" "-1740 -1280 -716" } { "classname" "path_corner" "target" "t99" "targetname" "t100" "origin" "-1948 -860 -956" } { "classname" "path_corner" "targetname" "t99" "target" "t100" "origin" "-2200 -860 -956" } { "item" "ammo_rockets" "classname" "monster_chick" "target" "t100" "spawnflags" "0" "angle" "180" "origin" "-1920 -860 -940" } { "model" "*46" "classname" "trigger_once" "target" "t98" "spawnflags" "2048" } { "item" "ammo_cells" "classname" "monster_floater" "spawnflags" "769" "angle" "90" "targetname" "t98" "origin" "-1816 -344 -544" } { "angle" "90" "spawnflags" "3" "classname" "monster_hover" "targetname" "t96" "origin" "-1248 -420 -64" } { "angle" "90" "spawnflags" "771" "classname" "monster_hover" "targetname" "t96" "origin" "-1040 -264 28" } { "classname" "monster_hover" "spawnflags" "3" "angle" "90" "targetname" "t96" "origin" "-1764 -340 104" "item" "ammo_cells" } { "classname" "point_combat" "spawnflags" "1" "targetname" "t97" "origin" "16 1132 -160" } { "classname" "monster_gunner" "spawnflags" "769" "angle" "90" "target" "t97" "origin" "16 812 -144" "targetname" "t96" } { "model" "*47" "classname" "trigger_once" "target" "t96" "spawnflags" "2048" } { "classname" "point_combat" "spawnflags" "0" "targetname" "t95" "origin" "-472 760 -160" } { "item" "ammo_bullets" "classname" "monster_gunner" "angle" "180" "spawnflags" "1" "target" "t95" "targetname" "t96" "origin" "-328 736 -136" } { "angle" "90" "classname" "monster_gunner" "origin" "-1248 -448 -288" "target" "t137" "spawnflags" "1" "targetname" "t148" "item" "ammo_grenades" } { "angle" "180" "classname" "monster_floater" "origin" "-864 244 -820" "spawnflags" "1" "targetname" "t98" } { "item" "ammo_cells" "classname" "monster_floater" "angle" "180" "origin" "-888 176 -820" "spawnflags" "1" "targetname" "t98" } { "spawnflags" "0" "classname" "item_health" "origin" "-404 -792 24" } { "spawnflags" "2048" "classname" "item_health" "origin" "-404 -752 24" } { "classname" "path_corner" "targetname" "t85" "target" "t86" "origin" "-2432 -784 -936" } { "classname" "path_corner" "targetname" "t84" "target" "t85" "origin" "-2432 -584 -936" } { "classname" "path_corner" "targetname" "t83" "target" "t84" "origin" "-2212 -584 -936" } { "classname" "path_corner" "targetname" "t87" "target" "t88" "origin" "-2416 -600 -936" } { "classname" "path_corner" "targetname" "t88" "target" "t83" "origin" "-2220 -600 -936" } { "classname" "path_corner" "targetname" "t86" "target" "t87" "origin" "-2416 -800 -936" } { "spawnflags" "1" "classname" "monster_chick" "target" "t83" "origin" "-2184 -584 -920" } { "classname" "point_combat" "origin" "-44 832 -144" "targetname" "t82" "spawnflags" "1" } { "spawnflags" "2048" "classname" "misc_explobox" "origin" "-56 -776 8" } { "spawnflags" "2048" "origin" "-328 -88 8" "classname" "misc_explobox" } { "spawnflags" "2048" "origin" "-340 -700 -192" "classname" "misc_explobox" } { "origin" "-148 -1752 -440" "classname" "light" "light" "72" } { "light" "72" "classname" "light" "origin" "-148 -1716 -412" } { "origin" "-148 -1680 -440" "classname" "light" "light" "72" } { "origin" "-480 -1572 -708" "light" "130" "classname" "light" } { "origin" "-224 -1700 -408" "target" "t80" "targetname" "t1" "classname" "trigger_relay" "delay" ".3" "spawnflags" "2048" } { "origin" "-248 -1588 -408" "target" "exp69" "targetname" "t1" "delay" ".6" "classname" "trigger_relay" "spawnflags" "2048" } { "origin" "-536 -1508 -440" "target" "t76" "targetname" "t77" "classname" "path_corner" } { "wait" ".5" "origin" "-536 -1508 -508" "target" "t77" "targetname" "t76" "classname" "path_corner" } { "target" "t180" "targetname" "t181" "wait" ".5" "origin" "-472 -1620 -508" "classname" "path_corner" } { "wait" ".5" "origin" "-688 -1652 -812" "target" "t73" "targetname" "t72" "classname" "path_corner" } { "wait" ".5" "origin" "-688 -1652 -644" "target" "t72" "targetname" "t73" "classname" "path_corner" } { "model" "*48" "dmg" "15" "spawnflags" "4" "targetname" "t33" "speed" "150" "target" "t72" "classname" "func_train" } { "_color" "1.000000 0.976471 0.023529" "light" "64" "classname" "light" "origin" "-2216 -96 -1120" } { "_color" "1.000000 0.976471 0.023529" "light" "64" "classname" "light" "origin" "-2216 32 -1120" } { "_color" "0.087379 1.000000 0.043689" "light" "64" "classname" "light" "origin" "-2224 32 -1120" } { "spawnflags" "0" "origin" "-2332 -560 -944" "classname" "misc_explobox" "mass" "80" "health" "25" "dmg" "40" } { "spawnflags" "2048" "origin" "-1928 -776 -944" "dmg" "40" "health" "25" "mass" "80" "classname" "misc_explobox" } { "_color" "1.000000 0.976471 0.023529" "light" "64" "classname" "light" "origin" "-2540 -688 -952" } { "_color" "1.000000 0.976471 0.023529" "light" "64" "classname" "light" "origin" "-2540 -544 -952" } { "_color" "0.087379 1.000000 0.043689" "light" "64" "classname" "light" "origin" "-2548 -544 -952" } { "origin" "-2548 -688 -952" "_color" "0.087379 1.000000 0.043689" "light" "64" "classname" "light" } { "origin" "-2548 -824 -952" "_color" "0.087379 1.000000 0.043689" "light" "64" "classname" "light" } { "origin" "-2224 -96 -1120" "_color" "0.087379 1.000000 0.043689" "light" "64" "classname" "light" } { "origin" "-2540 -824 -952" "classname" "light" "light" "64" "_color" "1.000000 0.976471 0.023529" } { "origin" "-2216 -224 -1120" "classname" "light" "light" "64" "_color" "1.000000 0.976471 0.023529" } { "origin" "-2224 -224 -1120" "_color" "0.087379 1.000000 0.043689" "light" "64" "classname" "light" } { "_color" "1.000000 0.972549 0.043137" "classname" "light" "light" "150" "origin" "-404 864 276" } { "_color" "1.000000 0.972549 0.043137" "classname" "light" "light" "150" "origin" "-80 1128 276" } { "_color" "1.000000 0.972549 0.043137" "classname" "light" "light" "150" "origin" "-80 848 276" } { "classname" "light" "light" "160" "origin" "36 732 120" } { "model" "*49" "target" "t71" "classname" "trigger_multiple" "angle" "360" } { "origin" "-376 592 -416" "targetname" "t71" "map" "power2$cool1a" "classname" "target_changelevel" } { "model" "*50" "target" "t70" "classname" "trigger_multiple" "angle" "270" } { "origin" "-1432 -1672 80" "targetname" "t70" "map" "power2$cool1" "classname" "target_changelevel" } { "spawnflags" "2048" "classname" "misc_explobox" "origin" "-1768 -240 -416" } { "spawnflags" "2048" "classname" "misc_explobox" "origin" "-1800 -168 -416" } { "spawnflags" "2048" "classname" "misc_explobox" "origin" "-1824 -320 -416" } { "origin" "-1176 -1728 -728" "target" "t61" "targetname" "t60" "classname" "path_corner" } { "origin" "-1176 -1728 -728" "target" "t59" "targetname" "t58" "classname" "path_corner" } { "origin" "-1176 -1864 -728" "target" "t60" "targetname" "t59" "classname" "path_corner" } { "origin" "-1392 -1728 -728" "targetname" "t61" "target" "t58" "classname" "path_corner" } { "origin" "-1032 -1992 -712" "target" "t57" "classname" "monster_gunner" "spawnflags" "1" } { "origin" "-944 -1984 -728" "target" "t57" "targetname" "t56" "classname" "path_corner" } { "origin" "-1176 -1992 -728" "targetname" "t57" "target" "t56" "classname" "path_corner" } { "origin" "320 -160 24" "classname" "monster_gunner" "spawnflags" "1" "target" "t127" "angle" "180" "item" "ammo_bullets" } { "origin" "320 -296 24" "target" "t52" "classname" "monster_gladiator" "spawnflags" "1" } { "origin" "-400 -328 8" "targetname" "t53" "target" "t52" "classname" "path_corner" } { "origin" "256 -328 8" "target" "t53" "targetname" "t52" "classname" "path_corner" } { "targetname" "t96" "origin" "-24 936 -144" "classname" "monster_gunner" "angle" "270" "spawnflags" "1" "target" "t82" } { "spawnflags" "1" "deathtarget" "ho1" "item" "ammo_slugs" "origin" "-440 1192 -136" "target" "t48" "classname" "monster_gladiator" } { "origin" "-528 1144 -152" "classname" "path_corner" "targetname" "t92" "target" "t93" } { "origin" "-384 932 -152" "classname" "path_corner" "targetname" "t94" "target" "t91" } { "origin" "-384 1144 -152" "classname" "path_corner" "targetname" "t91" "target" "t92" } { "origin" "-384 1208 -152" "classname" "path_corner" "target" "t91" "targetname" "t48" } { "origin" "-536 840 -152" "classname" "path_corner" "targetname" "t93" "target" "t94" } { "origin" "-1648 -456 -288" "angle" "90" "classname" "monster_gunner" "target" "t138" "spawnflags" "1" "targetname" "t148" "item" "ammo_bullets" } { "origin" "-1264 -24 -384" "target" "t38" "classname" "monster_gladiator" "spawnflags" "1" } { "origin" "-1168 -40 -400" "target" "t38" "targetname" "t37" "classname" "path_corner" } { "origin" "-1652 -40 -400" "targetname" "t38" "target" "t37" "classname" "path_corner" } { "model" "*51" "spawnflags" "2048" "target" "t36" "targetname" "t35" "classname" "trigger_counter" } { "model" "*52" "targetname" "t1" "target" "t35" "classname" "trigger_once" "spawnflags" "2048" } { "model" "*53" "targetname" "t33" "target" "t35" "classname" "trigger_once" "spawnflags" "2048" } { "model" "*54" "lip" "-128" "classname" "func_water" "spawnflags" "0" "targetname" "t1" "angle" "-2" } { "model" "*55" "targetname" "t36" "classname" "func_water" "spawnflags" "2048" "angle" "-2" } { "origin" "-448 -1388 -624" "classname" "light" "light" "80" } { "origin" "-448 -1296 -624" "classname" "light" "light" "80" } { "origin" "-448 -1340 -572" "light" "80" "classname" "light" } { "origin" "-1544 -64 -552" "light" "130" "classname" "light" } { "origin" "-1168 -256 -496" "light" "130" "classname" "light" } { "origin" "-1344 -208 -496" "light" "130" "classname" "light" } { "origin" "-1440 -432 -240" "classname" "light" "light" "100" } { "origin" "-2056 -168 -206" "light" "100" "classname" "light" } { "origin" "-1444 -528 -176" "light" "120" "classname" "light" } { "classname" "light" "light" "125" "origin" "-1384 -1336 -684" } { "classname" "light" "light" "100" "origin" "-1384 -1336 -824" } { "origin" "-1384 -1336 -1216" "classname" "light" "light" "80" } { "origin" "-1384 -1336 -1048" "light" "100" "classname" "light" } { "origin" "-1384 -1336 -912" "light" "100" "classname" "light" } { "origin" "-1076 -1000 -684" "light" "125" "classname" "light" } { "origin" "-1076 -1000 -1048" "classname" "light" "light" "100" } { "origin" "-1076 -1000 -912" "classname" "light" "light" "100" } { "origin" "-1076 -1000 -824" "classname" "light" "light" "100" } { "origin" "-1076 -1000 -1216" "light" "80" "classname" "light" } { "classname" "light" "light" "110" "origin" "-1384 -1520 -664" } { "classname" "light" "light" "150" "origin" "-1440 -1688 80" } { "model" "*56" "angle" "270" "classname" "func_door" "sounds" "4" "speed" "70" "team" "cl1" } { "model" "*57" "angle" "90" "classname" "func_door" "sounds" "4" "team" "cl1" } { "model" "*58" "classname" "func_plat" } { "light" "150" "origin" "-168 -1344 -192" "classname" "light" } { "target" "t26" "targetname" "t25" "origin" "-1464 -1366 -1336" "classname" "path_corner" } { "target" "t27" "targetname" "t26" "origin" "-1464 -1366 -776" "classname" "path_corner" } { "target" "t28" "targetname" "t27" "origin" "-1464 -1488 -776" "classname" "path_corner" } { "target" "t25" "targetname" "t28" "origin" "-1464 -1488 -1336" "classname" "path_corner" } { "classname" "light" "light" "250" "origin" "-676 812 -88" } { "light" "250" "classname" "light" "origin" "-520 768 -88" } { "origin" "-368 848 276" "light" "150" "classname" "light" "_color" "1.000000 0.972549 0.043137" } { "origin" "36 1244 120" "classname" "light" "light" "160" } { "origin" "-620 988 176" "classname" "light" "light" "80" } { "origin" "-484 732 120" "classname" "light" "light" "160" } { "origin" "-484 1244 120" "classname" "light" "light" "160" } { "light" "80" "classname" "light" "origin" "-564 988 216" } { "classname" "light" "light" "100" "origin" "-1444 -704 -176" } { "light" "140" "classname" "light" "origin" "-1448 -936 -40" } { "light" "120" "classname" "light" "origin" "-1444 -936 -176" } { "light" "120" "classname" "light" "origin" "-1444 -808 -176" } { "classname" "light" "light" "130" "origin" "-1344 -56 -552" } { "origin" "-1448 -936 152" "classname" "light" "light" "150" } { "origin" "-1448 -936 296" "classname" "light" "light" "100" } { "classname" "path_corner" "origin" "-1008 -1080 -1336" "target" "t29" "targetname" "t32" } { "classname" "path_corner" "origin" "-1008 -1080 -776" "targetname" "t31" "target" "t32" } { "classname" "path_corner" "origin" "-1130 -1080 -776" "targetname" "t30" "target" "t31" } { "classname" "path_corner" "origin" "-1130 -1080 -1336" "targetname" "t29" "target" "t30" } { "classname" "light" "light" "130" "origin" "-1728 -280 -496" } { "light" "130" "classname" "light" "origin" "-1560 -208 -496" } { "light" "140" "classname" "light" "origin" "-224 1252 -116" } { "light" "140" "classname" "light" "origin" "28 880 -144" } { "light" "140" "classname" "light" "origin" "-220 928 -116" } { "light" "140" "classname" "light" "origin" "-220 752 -116" } { "light" "140" "classname" "light" "origin" "-220 576 -116" } { "light" "140" "classname" "light" "origin" "-220 440 -48" } { "origin" "-220 272 12" "classname" "light" "light" "140" } { "model" "*59" "_minlight" ".18" "sounds" "4" "classname" "func_door" "angle" "-1" "target" "t109" "spawnflags" "0" } { "light" "80" "classname" "light" "origin" "-1060 -2168 -816" } { "classname" "light" "light" "80" "origin" "-248 -1304 -700" } { "light" "80" "classname" "light" "origin" "-248 -1380 -700" } { "origin" "-496 604 -356" "classname" "light" "light" "120" } { "light" "120" "classname" "light" "origin" "-368 604 -356" } { "model" "*60" "angle" "0" "classname" "func_door" "team" "td1" "_minlight" ".18" } { "origin" "-1448 -1520 24" "angle" "90" "classname" "info_player_start" "targetname" "cool1b" } { "model" "*61" "spawnflags" "2048" "_minlight" ".18" "targetname" "t36" "classname" "func_door" "angle" "270" "wait" "-1" "sounds" "4" } { "model" "*62" "spawnflags" "2048" "_minlight" ".18" "targetname" "t36" "classname" "func_door" "angle" "90" "wait" "-1" "sounds" "4" } { "classname" "info_player_start" "angle" "180" "targetname" "power2a" "origin" "-516 600 -480" } { "classname" "light" "light" "100" "origin" "-320 604 -412" } { "origin" "-608 604 -412" "light" "100" "classname" "light" } { "origin" "-552 604 -412" "light" "100" "classname" "light" }yquake2-QUAKE2_8_40/stuff/mapfixes/baseq2/hangar1.ent000066400000000000000000001345711465112212000222750ustar00rootroot00000000000000{ "nextmap" "hangar2" "message" "Outer Hangar" "classname" "worldspawn" "angle" "180" "sounds" "2" "sky" "unit8_" } { "origin" "1488 -584 1576" "targetname" "t86" "message" "You have found a secret level." "classname" "target_secret" } { "model" "*1" "target" "t86" "targetname" "t82" "classname" "trigger_once" } { "classname" "target_speaker" "targetname" "t85" "noise" "misc/tele_up.wav" "origin" "1512 -560 1424" } { "model" "*2" "classname" "trigger_multiple" "wait" "5" "target" "t85" } { "classname" "target_help" "targetname" "t84" "message" "Resume previous mission." "origin" "1656 -376 1344" } { "classname" "trigger_relay" "targetname" "t83" "target" "t84" "delay" "2" "origin" "1616 -360 1328" } { "classname" "target_crosslevel_target" "spawnflags" "32" "target" "t83" "origin" "1616 -384 1328" } { "origin" "80 444 1536" "light" "80" "classname" "light" } { "origin" "-928 -552 1112" "angle" "270" "classname" "item_breather" } { "angle" "0" "classname" "info_player_coop" "targetname" "lab" "origin" "-128 448 1400" } { "angle" "0" "classname" "info_player_coop" "targetname" "lab" "origin" "-80 448 1400" } { "classname" "info_player_coop" "angle" "0" "targetname" "lab" "origin" "-184 448 1400" } { "angle" "180" "classname" "info_player_coop" "targetname" "hangar2" "origin" "1208 -88 1408" } { "angle" "180" "classname" "info_player_coop" "targetname" "hangar2" "origin" "1208 -40 1408" } { "classname" "info_player_coop" "angle" "180" "targetname" "hangar2" "origin" "1256 -56 1408" } { "angle" "270" "classname" "info_player_coop" "targetname" "space" "origin" "1416 -328 1152" } { "angle" "270" "classname" "info_player_coop" "targetname" "space" "origin" "1592 -336 1152" } { "classname" "info_player_coop" "angle" "270" "targetname" "space" "origin" "1352 -360 1152" } { "angle" "0" "classname" "info_player_coop" "targetname" "unitstart" "origin" "-1072 -600 1432" } { "angle" "0" "classname" "info_player_coop" "targetname" "unitstart" "origin" "-1016 -600 1432" } { "classname" "info_player_coop" "angle" "0" "targetname" "unitstart" "origin" "-1128 -600 1432" } { "origin" "1528 -560 1576" "map" "space$hangar1" "spawnflags" "2048" "targetname" "t82" "classname" "target_changelevel" } { "model" "*3" "spawnflags" "2048" "target" "t82" "classname" "trigger_multiple" } { "origin" "1464 -560 1176" "classname" "target_speaker" "spawnflags" "1" "noise" "world/tele1.wav" } { "origin" "1512 -536 1176" "classname" "target_speaker" "spawnflags" "1" "noise" "world/tele1.wav" } { "origin" "1512 -472 1176" "classname" "target_speaker" "spawnflags" "1" "noise" "world/tele1.wav" } { "origin" "1560 -560 1176" "noise" "world/tele1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1144 -584 1416" "classname" "light" } { "origin" "1504 -308 1144" "targetname" "t81" "spawnflags" "1792" "angle" "270" "classname" "misc_teleporter_dest" } { "origin" "1056 -288 1384" "target" "t81" "spawnflags" "1792" "angle" "315" "classname" "misc_teleporter" } { "model" "*4" "spawnflags" "2048" "classname" "func_wall" } { "classname" "misc_teleporter_dest" "angle" "135" "spawnflags" "1792" "targetname" "t80" "origin" "504 272 1384" } { "classname" "misc_teleporter" "spawnflags" "1792" "target" "t80" "origin" "-464 -664 1120" } { "classname" "misc_teleporter_dest" "spawnflags" "1792" "angle" "90" "targetname" "t79" "origin" "-1216 -920 1432" } { "classname" "misc_teleporter" "spawnflags" "1792" "target" "t79" "origin" "1504 -1232 1160" } { "origin" "-300 -920 1444" "angles" "25 130 0" "classname" "info_player_intermission" } { "origin" "-896 -416 1424" "classname" "item_health_small" } { "classname" "ammo_cells" "origin" "-728 -992 1428" } { "classname" "item_health_large" "origin" "-260 -676 1424" } { "noise" "world/bigpump.wav" "origin" "-728 -536 1136" "classname" "target_speaker" } { "classname" "target_speaker" "origin" "-880 -528 1136" "noise" "world/bigpump.wav" } { "classname" "target_speaker" "origin" "-728 -432 1136" "noise" "world/bigpump.wav" } { "noise" "world/bigpump.wav" "origin" "-616 -488 1024" "classname" "target_speaker" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum1.wav" "origin" "-992 -784 1136" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum3.wav" "origin" "-928 -752 1136" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum3.wav" "origin" "-1184 -784 1136" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "-1232 -408 1176" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "-1112 -424 1120" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "-1024 -464 1120" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "-952 -416 1464" } { "origin" "-694 -982 1408" "classname" "misc_deadsoldier" } { "origin" "-728 -952 1428" "classname" "ammo_cells" } { "origin" "-1184 -416 1112" "classname" "ammo_bullets" } { "origin" "-1144 -416 1112" "classname" "ammo_bullets" } { "model" "*5" "target" "t76" "classname" "trigger_once" } { "origin" "-728 -712 1432" "spawnflags" "1536" "classname" "item_health" } { "origin" "-728 -672 1432" "spawnflags" "1536" "classname" "item_health" } { "origin" "-728 -752 1432" "spawnflags" "1792" "classname" "item_health" } { "origin" "-424 -1000 904" "target" "t5" "spawnflags" "1792" "classname" "trigger_always" } { "origin" "-352 -1128 1128" "angle" "90" "classname" "info_player_deathmatch" } { "origin" "-344 -864 1128" "spawnflags" "1792" "classname" "weapon_chaingun" } { "spawnflags" "1792" "origin" "-256 -864 1128" "classname" "ammo_bullets" } { "spawnflags" "1792" "origin" "-296 -864 1128" "classname" "ammo_bullets" } { "origin" "-48 -48 1424" "spawnflags" "1792" "classname" "ammo_rockets" } { "origin" "-88 -80 1424" "spawnflags" "1792" "classname" "ammo_rockets" } { "origin" "168 304 1376" "spawnflags" "1792" "classname" "weapon_machinegun" } { "origin" "120 320 1376" "classname" "ammo_bullets" } { "origin" "120 280 1376" "classname" "ammo_bullets" } { "origin" "824 16 1376" "classname" "ammo_grenades" } { "origin" "824 -24 1376" "spawnflags" "1536" "classname" "ammo_grenades" } { "origin" "864 -32 1376" "spawnflags" "1792" "classname" "weapon_grenadelauncher" } { "origin" "1120 -576 1424" "classname" "ammo_cells" "spawnflags" "1536" } { "origin" "1120 -616 1424" "spawnflags" "1792" "classname" "ammo_cells" } { "origin" "1352 -984 1120" "spawnflags" "256" "classname" "item_armor_combat" } { "origin" "1576 -1144 1152" "classname" "ammo_cells" "spawnflags" "0" } { "origin" "1576 -1104 1152" "spawnflags" "1536" "classname" "ammo_cells" } { "origin" "1416 -1120 1152" "spawnflags" "1792" "classname" "weapon_hyperblaster" } { "origin" "-1048 -664 1112" "spawnflags" "1792" "classname" "weapon_supershotgun" } { "origin" "-902 -864 1424" "classname" "weapon_rocketlauncher" } { "origin" "-896 -912 1408" "spawnflags" "2" "classname" "misc_deadsoldier" } { "origin" "-1248 -416 1120" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "-704 -1032 1432" "angle" "90" "classname" "info_player_deathmatch" } { "origin" "-216 -728 1432" "angle" "90" "classname" "info_player_deathmatch" } { "origin" "-224 -568 1512" "target" "t70" "spawnflags" "1792" "classname" "trigger_always" } { "origin" "1720 -552 1144" "angle" "225" "classname" "info_player_deathmatch" } { "origin" "880 -368 1432" "angle" "135" "classname" "info_player_deathmatch" } { "origin" "984 -600 1432" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "408 488 1488" "target" "t26" "spawnflags" "1792" "classname" "trigger_always" } { "origin" "992 -64 1384" "angle" "180" "classname" "info_player_deathmatch" } { "origin" "496 568 1384" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "-1248 -416 1432" "angle" "315" "classname" "info_player_deathmatch" } { "classname" "ammo_rockets" "origin" "-728 -360 1424" } { "classname" "ammo_shells" "origin" "-936 -936 1424" } { "classname" "ammo_shells" "origin" "-936 -896 1424" } { "classname" "item_health_large" "origin" "-1184 -808 1424" } { "classname" "item_health_large" "origin" "-1184 -864 1424" } { "classname" "target_secret" "targetname" "t75" "message" "You have found a secret." "origin" "-920 -416 1160" } { "classname" "item_adrenaline" "origin" "-928 -416 1112" "target" "t75" } { "classname" "item_pack" "origin" "-1120 -536 1176" } { "classname" "ammo_shells" "origin" "-912 -744 1112" } { "classname" "ammo_shells" "origin" "-952 -744 1112" } { "classname" "item_health_large" "origin" "-1248 -800 1112" } { "spawnflags" "1" "angle" "270" "classname" "monster_infantry" "item" "ammo_bullets" "origin" "-1056 -472 1120" } { "targetname" "t77" "classname" "monster_infantry" "angle" "270" "spawnflags" "1" "item" "ammo_bullets" "origin" "-1248 -472 1120" } { "classname" "monster_infantry" "spawnflags" "1" "target" "t73" "origin" "-1008 -712 1120" } { "classname" "path_corner" "targetname" "t73" "target" "t74" "origin" "-1176 -712 1104" } { "classname" "path_corner" "target" "t73" "targetname" "t74" "origin" "-960 -712 1104" } { "target" "t77" "targetname" "t76" "classname" "monster_gunner" "angle" "270" "spawnflags" "1" "origin" "-1176 -536 1120" } { "model" "*6" "classname" "trigger_counter" "spawnflags" "1" "target" "t71" "targetname" "t72" } { "model" "*7" "classname" "trigger_push" "angle" "-1" "speed" "50" } { "spawnflags" "2" "angle" "180" "classname" "monster_flyer" "origin" "-152 -1024 1656" "targetname" "t78" } { "classname" "monster_flyer" "angle" "180" "spawnflags" "2" "targetname" "t71" "origin" "-152 -1024 1720" "target" "t78" } { "classname" "target_explosion" "targetname" "t70" "delay" ".3" "origin" "-256 -616 1464" } { "classname" "target_explosion" "targetname" "t70" "origin" "-272 -624 1504" } { "classname" "target_explosion" "targetname" "t70" "delay" ".5" "origin" "-216 -624 1504" } { "classname" "target_explosion" "targetname" "t70" "delay" ".1" "origin" "-200 -632 1432" } { "model" "*8" "classname" "trigger_counter" "count" "5" "targetname" "t69" "spawnflags" "1" "target" "t70" } { "item" "ammo_bullets" "angle" "180" "classname" "monster_infantry" "spawnflags" "1" "target" "t69" "origin" "320 48 1432" } { "angle" "225" "classname" "monster_infantry" "spawnflags" "1" "target" "t69" "origin" "64 -8 1432" } { "item" "ammo_bullets" "angle" "225" "classname" "monster_infantry" "spawnflags" "1" "target" "t69" "origin" "-64 -96 1432" } { "angle" "225" "classname" "monster_infantry" "spawnflags" "1" "target" "t69" "origin" "-216 -312 1432" } { "classname" "monster_infantry" "angle" "180" "spawnflags" "1" "target" "t69" "origin" "560 -16 1432" } { "light" "80" "classname" "light" "origin" "-248 -688 1488" } { "classname" "light" "light" "80" "origin" "-192 -736 1488" } { "classname" "monster_medic" "angle" "90" "origin" "-216 -720 1432" "targetname" "t70" } { "model" "*9" "classname" "func_explosive" "dmg" "1" "targetname" "t70" "mass" "100" } { "classname" "item_armor_shard" "origin" "840 -224 1424" } { "classname" "item_armor_shard" "origin" "816 -184 1424" } { "classname" "item_armor_shard" "origin" "792 -144 1424" } { "classname" "item_armor_shard" "origin" "760 -112 1424" } { "classname" "item_armor_shard" "origin" "856 -264 1424" } { "light" "80" "classname" "light" "origin" "184 0 1480" } { "light" "80" "classname" "light" "origin" "-56 -144 1480" } { "light" "80" "classname" "light" "origin" "-184 -368 1480" } { "light" "80" "classname" "light" "origin" "-208 -512 1480" } { "classname" "light" "light" "80" "origin" "320 8 1480" } { "light" "150" "classname" "light" "origin" "320 464 1464" } { "classname" "light" "light" "150" "origin" "320 320 1464" } { "classname" "path_corner" "targetname" "t67" "target" "t68" "origin" "160 448 1368" } { "classname" "path_corner" "targetname" "t68" "target" "t67" "origin" "456 440 1368" } { "classname" "monster_tank" "spawnflags" "1" "target" "t51" "origin" "1528 -328 1136" } { "classname" "path_corner" "targetname" "t64" "target" "t65" "origin" "1384 -360 1128" } { "classname" "path_corner" "targetname" "t63" "target" "t64" "origin" "1328 -424 1128" } { "classname" "path_corner" "targetname" "t62" "target" "t63" "origin" "1288 -504 1128" } { "classname" "path_corner" "targetname" "t61" "target" "t62" "origin" "1296 -576 1128" } { "classname" "path_corner" "targetname" "t60" "target" "t61" "origin" "1312 -656 1128" } { "classname" "path_corner" "targetname" "t59" "target" "t60" "origin" "1368 -720 1128" } { "classname" "path_corner" "targetname" "t58" "target" "t59" "origin" "1456 -744 1144" } { "classname" "path_corner" "targetname" "t57" "target" "t58" "origin" "1544 -752 1144" } { "classname" "path_corner" "targetname" "t56" "target" "t57" "origin" "1624 -712 1128" } { "classname" "path_corner" "targetname" "t55" "target" "t56" "origin" "1688 -656 1128" } { "classname" "path_corner" "targetname" "t54" "target" "t55" "origin" "1720 -560 1128" } { "classname" "path_corner" "targetname" "t53" "target" "t54" "origin" "1712 -472 1128" } { "classname" "path_corner" "targetname" "t52" "target" "t53" "origin" "1664 -400 1128" } { "classname" "path_corner" "targetname" "t51" "target" "t52" "origin" "1600 -352 1128" } { "classname" "path_corner" "target" "t51" "targetname" "t66" "origin" "1504 -328 1128" } { "classname" "path_corner" "targetname" "t65" "target" "t66" "origin" "1448 -336 1128" } { "classname" "target_goal" "targetname" "t45" "origin" "488 448 1488" } { "spawnflags" "2048" "classname" "target_secret" "targetname" "t50" "origin" "952 -984 1176" "message" "You found a secret area!" } { "model" "*10" "classname" "trigger_once" "target" "t50" } { "classname" "item_armor_shard" "origin" "-776 -412 1112" } { "classname" "item_armor_shard" "origin" "-812 -412 1112" } { "classname" "item_armor_shard" "origin" "-740 -412 1112" } { "classname" "misc_deadsoldier" "spawnflags" "2" "origin" "-758 -574 1096" } { "classname" "ammo_bullets" "origin" "-724 -572 1112" } { "classname" "ammo_bullets" "origin" "-724 -536 1112" } { "classname" "item_armor_combat" "origin" "-760 -540 1112" } { "classname" "light" "light" "100" "origin" "-816 -480 1200" } { "classname" "monster_hover" "angle" "90" "spawnflags" "1" "origin" "-536 -1096 1744" "target" "t72" } { "classname" "monster_gunner" "angle" "90" "target" "t48" "origin" "-704 -912 1432" "item" "ammo_grenades" } { "classname" "path_corner" "target" "t48" "targetname" "t49" "origin" "-704 -576 1416" } { "classname" "path_corner" "targetname" "t48" "target" "t49" "origin" "-704 -872 1416" } { "origin" "-1152 -704 1288" "classname" "light" "light" "150" } { "origin" "-960 -512 1288" "classname" "light" "light" "150" } { "origin" "-1152 -512 1288" "classname" "light" "light" "150" } { "origin" "-960 -704 1288" "light" "150" "classname" "light" } { "origin" "-1400 -704 1328" "classname" "light" "light" "64" } { "origin" "-1400 -704 1360" "light" "64" "classname" "light" } { "origin" "-1400 -704 1456" "classname" "light" "light" "64" } { "origin" "-1400 -704 1488" "light" "64" "classname" "light" } { "origin" "-1400 -704 1232" "classname" "light" "light" "64" } { "origin" "-1400 -704 1200" "light" "64" "classname" "light" } { "origin" "-1304 -704 1312" "classname" "light" "light" "80" } { "origin" "-1304 -704 1184" "classname" "light" "light" "80" } { "origin" "-1304 -704 1120" "classname" "light" "light" "80" } { "origin" "-1304 -704 1472" "light" "80" "classname" "light" } { "origin" "-952 -704 1176" "classname" "light" "light" "150" } { "origin" "-1152 -704 1176" "classname" "light" "light" "150" } { "origin" "-1064 -568 1200" "light" "150" "classname" "light" } { "_color" "1.000000 1.000000 0.000000" "light" "120" "classname" "light" "origin" "-768 -516 1464" } { "classname" "light" "light" "120" "_color" "1.000000 1.000000 0.000000" "origin" "-832 -516 1468" } { "origin" "-1184 -808 1164" "classname" "light" "light" "80" } { "origin" "-1056 -808 1164" "classname" "light" "light" "80" } { "origin" "-992 -808 1164" "classname" "light" "light" "80" } { "origin" "-1184 -808 1236" "light" "80" "classname" "light" } { "origin" "-1088 -392 1204" "classname" "light" "light" "80" } { "origin" "-1220 -396 1204" "light" "80" "classname" "light" } { "light" "120" "origin" "-1248 -576 1240" "classname" "light" } { "classname" "light" "origin" "-960 -416 1240" "light" "120" } { "light" "120" "origin" "-1088 -416 1240" "classname" "light" } { "light" "120" "origin" "-1216 -416 1240" "classname" "light" } { "classname" "light" "origin" "-1216 -800 1240" "light" "120" } { "classname" "light" "origin" "-1248 -448 1240" "light" "120" } { "classname" "light" "origin" "-1248 -704 1240" "light" "120" } { "classname" "light" "origin" "-1248 -576 1576" "light" "120" } { "model" "*11" "lip" "8" "_minlight" ".1" "spawnflags" "1" "classname" "func_plat" } { "light" "120" "origin" "-1248 -704 1576" "classname" "light" } { "style" "1" "targetname" "t47" "classname" "func_areaportal" } { "model" "*12" "target" "t47" "angle" "-1" "classname" "func_door" } { "origin" "-816 -640 1144" "classname" "light" "light" "120" "_color" "1.000000 1.000000 0.000000" } { "origin" "-880 -640 1148" "_color" "1.000000 1.000000 0.000000" "light" "120" "classname" "light" } { "style" "2" "targetname" "t46" "classname" "func_areaportal" } { "model" "*13" "_minlight" ".2" "target" "t46" "angle" "-1" "classname" "func_door" } { "origin" "-1088 -536 1432" "angle" "0" "classname" "info_player_start" } { "classname" "target_help" "targetname" "t45" "message" "Move to main hangar bay\ninstallation." "origin" "544 432 1480" } { "classname" "trigger_relay" "targetname" "t26" "killtarget" "message" "origin" "536 472 1488" "target" "t45" } { "model" "*14" "classname" "trigger_multiple" "message" "Tank commander's head needed at scanner." "wait" "5" "targetname" "message" } { "classname" "target_help" "targetname" "t44" "message" "Find a way into the main\nhangar bay installation." "origin" "-1032 -464 1512" } { "classname" "target_help" "targetname" "t44" "message" "Close main hangar bay door.\nDestroy blackhole generator." "spawnflags" "1" "origin" "-1088 -464 1512" } { "model" "*15" "classname" "trigger_once" "target" "t44" } { "spawnflags" "2048" "origin" "1000 -288 1376" "classname" "item_armor_shard" } { "spawnflags" "2048" "origin" "1032 -288 1376" "classname" "item_armor_shard" } { "spawnflags" "2048" "origin" "968 -288 1376" "classname" "item_armor_shard" } { "origin" "1064 -288 1360" "spawnflags" "2" "angle" "135" "classname" "misc_deadsoldier" } { "origin" "-320 480 1448" "map" "lab$lstart" "targetname" "t43" "classname" "target_changelevel" } { "model" "*16" "angle" "180" "target" "t43" "classname" "trigger_multiple" } { "classname" "info_player_start" "angle" "90" "targetname" "space" "origin" "1510 -420 1160" } { "origin" "1136 -64 1408" "angle" "180" "classname" "info_player_start" "targetname" "hangar2" } { "origin" "1344 -8 1472" "map" "hangar2$hangar1" "targetname" "t41" "classname" "target_changelevel" } { "model" "*17" "angle" "360" "target" "t41" "classname" "trigger_multiple" } { "_color" "0.000000 0.500000 1.000000" "light" "180" "origin" "384 -992 968" "classname" "light" } { "_color" "0.000000 0.500000 1.000000" "light" "180" "origin" "256 -992 968" "classname" "light" } { "_color" "0.000000 0.500000 1.000000" "light" "180" "origin" "128 -992 968" "classname" "light" } { "_color" "0.000000 0.500000 1.000000" "light" "180" "origin" "0 -992 968" "classname" "light" } { "_color" "0.000000 0.500000 1.000000" "light" "180" "origin" "-128 -992 968" "classname" "light" } { "origin" "120 240 1376" "classname" "item_health_small" } { "origin" "160 240 1376" "classname" "item_health_small" } { "origin" "480 240 1376" "classname" "item_health_small" "spawnflags" "2048" } { "origin" "520 240 1376" "classname" "item_health_small" "spawnflags" "2048" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1704 -624 1224" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1720 -496 1224" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1664 -392 1224" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1576 -328 1224" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1456 -328 1224" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1352 -392 1224" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1296 -488 1224" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1312 -608 1224" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1376 -704 1224" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1504 -744 1224" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" "origin" "1624 -712 1224" } { "classname" "item_health_large" "origin" "896 -636 1424" } { "classname" "light" "origin" "1048 -452 1416" } { "classname" "item_health_small" "origin" "120 624 1376" } { "classname" "item_health_small" "origin" "160 624 1376" } { "classname" "item_health_small" "origin" "480 624 1376" } { "classname" "item_health_small" "origin" "520 624 1376" } { "classname" "item_health_small" "origin" "-856 -416 1424" } { "classname" "monster_tank" "angle" "270" "spawnflags" "1" "item" "ammo_rockets" "origin" "264 456 1376" "target" "t67" } { "spawnflags" "1" "angle" "180" "classname" "monster_infantry" "origin" "992 8 1384" //stuck monster at the steps "1008 8 1384" } { "classname" "monster_infantry" "angle" "180" "spawnflags" "1" "origin" "992 -136 1384" //stuck monster at the steps "1008 -136 1384" } { "classname" "item_health_large" "origin" "688 96 1384" } { "model" "*18" "classname" "func_explosive" "dmg" "50" } { "classname" "light" "style" "10" "light" "120" "origin" "844 -488 1416" } { "classname" "ammo_bullets" "origin" "864 -600 1424" } { "classname" "ammo_shells" "origin" "864 -560 1424" } { "classname" "misc_deadsoldier" "angle" "90" "spawnflags" "2" "origin" "896 -488 1408" } { "classname" "item_health_small" "origin" "1488 -1160 1152" } { "classname" "item_health_small" "origin" "1464 -1088 1152" } { "spawnflags" "2048" "classname" "ammo_shells" "origin" "1512 -1120 1152" } { "classname" "misc_deadsoldier" "spawnflags" "8" "angle" "135" "origin" "1536 -1160 1136" } { "spawnflags" "1" "angle" "180" "classname" "monster_hover" "item" "ammo_cells" "origin" "1652 -924 1832" } { "classname" "monster_hover" "angle" "180" "spawnflags" "1" "origin" "1664 -1088 1832" } { "classname" "path_corner" "targetname" "t39" "target" "t40" "origin" "928 -920 992" } { "classname" "path_corner" "target" "t39" "targetname" "t40" "origin" "752 -976 912" } { "classname" "path_corner" "targetname" "t37" "target" "t38" "origin" "688 -1136 912" } { "classname" "path_corner" "target" "t37" "targetname" "t38" "origin" "712 -928 912" } { "classname" "monster_flipper" "target" "t37" "origin" "688 -1096 928" } { "classname" "monster_flipper" "target" "t39" "origin" "760 -984 928" } { "classname" "path_corner" "targetname" "t35" "target" "t36" "origin" "-376 -336 1032" } { "classname" "path_corner" "target" "t35" "targetname" "t36" "origin" "-600 -472 1032" } { "classname" "path_corner" "targetname" "t33" "target" "t34" "origin" "-376 -752 1032" } { "classname" "path_corner" "target" "t33" "targetname" "t34" "origin" "-376 -520 1032" } { "classname" "path_corner" "targetname" "t31" "target" "t32" "origin" "-584 -512 1064" } { "classname" "path_corner" "target" "t31" "targetname" "t32" "origin" "-600 -744 1064" } { "classname" "path_corner" "targetname" "t29" "target" "t30" "origin" "-464 -800 1032" } { "classname" "path_corner" "target" "t29" "targetname" "t30" "origin" "-520 -1128 1032" } { "classname" "path_corner" "targetname" "t27" "target" "t28" "origin" "-440 -1256 1032" } { "classname" "path_corner" "target" "t27" "targetname" "t28" "origin" "-680 -1120 1032" } { "classname" "monster_flipper" "origin" "-464 -768 1048" "target" "t29" } { "classname" "monster_flipper" "origin" "-368 -624 1048" "target" "t34" } { "classname" "monster_flipper" "origin" "-600 -584 1048" "target" "t32" } { "classname" "monster_flipper" "origin" "-408 -320 1048" "target" "t35" } { "classname" "monster_flipper" "target" "t27" "origin" "-496 -1176 1048" } { "spawnflags" "1" "angle" "90" "classname" "monster_hover" "origin" "-432 -1192 1952" "target" "t72" } { "origin" "936 -184 1416" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" } { "origin" "1016 -64 1416" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" } { "origin" "856 -72 1416" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" } { "origin" "760 32 1416" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" } { "origin" "880 56 1416" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" } { "origin" "1032 40 1416" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" } { "origin" "1048 160 1416" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" } { "origin" "904 168 1416" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" } { "origin" "824 136 1416" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" } { "origin" "784 296 1416" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" } { "origin" "784 408 1416" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" } { "origin" "688 448 1416" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" } { "origin" "272 64 1480" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" } { "origin" "-1176 -416 1464" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" } { "origin" "-1056 -416 1464" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" } { "origin" "-1048 -784 1136" "noise" "world/comp_hum1.wav" "spawnflags" "1" "classname" "target_speaker" } { "noise" "world/wind2.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-360 -984 1632" } { "noise" "world/wind2.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-528 -944 1632" } { "noise" "world/wind2.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-672 -936 1632" } { "noise" "world/wind2.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-496 -832 1632" } { "noise" "world/wind2.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-360 -776 1632" } { "noise" "world/wind2.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-680 -680 1632" } { "noise" "world/wind2.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-640 -832 1632" } { "noise" "world/wind2.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-536 -688 1632" } { "noise" "world/wind2.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-384 -640 1632" } { "noise" "world/wind2.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-416 -496 1632" } { "noise" "world/wind2.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-544 -344 1632" } { "noise" "world/wind2.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-656 -512 1632" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/wind2.wav" "origin" "-480 -1192 1632" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "848 -280 1480" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "808 -208 1480" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "736 -128 1480" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "648 -40 1480" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "544 8 1480" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "440 48 1480" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "320 184 1480" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1032 -240 1416" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "168 40 1480" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "56 -8 1480" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-48 -88 1480" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-136 -176 1480" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-200 -280 1480" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-248 -384 1480" } { "noise" "world/amb4.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-256 -512 1480" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb4.wav" "origin" "880 -368 1480" } { "origin" "212 264 1388" "_color" "0.215686 0.607843 1.000000" "light" "100" "classname" "light" } { "origin" "96 224 1496" "_color" "0.215686 0.607843 1.000000" "light" "100" "classname" "light" } { "origin" "544 352 1496" "classname" "light" "light" "100" "_color" "0.215686 0.607843 1.000000" } { "origin" "544 544 1496" "classname" "light" "light" "100" "_color" "0.215686 0.607843 1.000000" } { "origin" "544 672 1496" "classname" "light" "light" "100" "_color" "0.215686 0.607843 1.000000" } { "origin" "96 672 1496" "classname" "light" "light" "100" "_color" "0.215686 0.607843 1.000000" } { "origin" "96 544 1496" "classname" "light" "light" "100" "_color" "0.215686 0.607843 1.000000" } { "origin" "428 264 1388" "classname" "light" "light" "100" "_color" "0.215686 0.607843 1.000000" } { "origin" "96 352 1496" "classname" "light" "light" "100" "_color" "0.215686 0.607843 1.000000" } { "origin" "544 224 1496" "_color" "0.215686 0.607843 1.000000" "light" "100" "classname" "light" } { "origin" "324 576 1584" "light" "40" "_color" "0.003937 0.653543 1.000000" "classname" "light" } { "origin" "324 576 1560" "light" "40" "_color" "0.003937 0.653543 1.000000" "classname" "light" } { "origin" "324 576 1536" "light" "40" "_color" "0.003937 0.653543 1.000000" "classname" "light" } { "origin" "324 576 1512" "light" "40" "_color" "0.003937 0.653543 1.000000" "classname" "light" } { "origin" "324 576 1488" "light" "40" "_color" "0.003937 0.653543 1.000000" "classname" "light" } { "origin" "320 568 1424" "light" "100" "_color" "0.003937 0.653543 1.000000" "classname" "light" } { "origin" "320 576 1424" "light" "100" "_color" "0.003937 0.653543 1.000000" "classname" "light" "style" "10" } { "origin" "520 488 1440" "targetname" "t26" "noise" "world/force1.wav" "spawnflags" "1" "classname" "target_speaker" } { "model" "*19" "classname" "func_wall" "spawnflags" "6" "targetname" "t26" } { "classname" "trigger_key" "item" "key_commander_head" "targetname" "t25" "target" "t26" "origin" "408 520 1488" } { "model" "*20" "classname" "func_button" "angle" "90" "target" "t22" "spawnflags" "2048" } { "classname" "target_speaker" "noise" "world/scan1.wav" "targetname" "t23" "origin" "320 552 1424" } { "classname" "light" "_color" "0.003937 0.653543 1.000000" "light" "40" "origin" "324 576 1608" } { "model" "*21" "target" "t23" "targetname" "t22" "classname" "trigger_multiple" } { "targetname" "t23" "origin" "384 520 1488" "delay" "1.2" "classname" "trigger_relay" "target" "t25" } { "targetname" "t23" "origin" "328 552 1512" "delay" "0.9" "target" "t20" "classname" "trigger_relay" } { "targetname" "t23" "origin" "360 552 1512" "delay" "1" "target" "t20" "classname" "trigger_relay" } { "targetname" "t23" "origin" "328 552 1536" "delay" "0.7" "target" "t19" "classname" "trigger_relay" } { "targetname" "t23" "origin" "360 552 1536" "delay" "0.8" "target" "t19" "classname" "trigger_relay" } { "targetname" "t23" "origin" "328 552 1560" "delay" "0.5" "target" "t18" "classname" "trigger_relay" } { "targetname" "t23" "origin" "360 552 1560" "delay" "0.6" "target" "t18" "classname" "trigger_relay" } { "targetname" "t23" "origin" "328 552 1584" "delay" "0.3" "target" "t17" "classname" "trigger_relay" } { "targetname" "t23" "origin" "360 552 1584" "delay" "0.4" "target" "t17" "classname" "trigger_relay" } { "targetname" "t23" "origin" "328 552 1608" "delay" "0.1" "target" "t16" "classname" "trigger_relay" } { "targetname" "t23" "origin" "360 552 1608" "delay" "0.2" "target" "t16" "classname" "trigger_relay" } { "targetname" "t23" "origin" "360 552 1488" "delay" "1.2" "target" "t21" "classname" "trigger_relay" } { "targetname" "t23" "origin" "328 552 1488" "delay" "1.1" "target" "t21" "classname" "trigger_relay" } { "origin" "224 608 1496" "noise" "world/amb15.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "224 584 1432" "noise" "world/amb15.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "432 608 1528" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb15.wav" } { "origin" "320 576 1456" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb15.wav" } { "origin" "408 584 1432" "noise" "world/amb15.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "304 580 1584" "targetname" "t11" "classname" "path_corner" } { "origin" "304 580 1560" "targetname" "t12" "classname" "path_corner" } { "origin" "304 580 1536" "targetname" "t13" "classname" "path_corner" } { "origin" "304 580 1512" "targetname" "t14" "classname" "path_corner" } { "origin" "304 580 1488" "targetname" "t15" "classname" "path_corner" } { "origin" "304 580 1608" "targetname" "t10" "classname" "path_corner" } { "targetname" "t17" "origin" "344 580 1584" "target" "t11" "classname" "target_laser" "spawnflags" "16" } { "targetname" "t18" "origin" "344 580 1560" "target" "t12" "classname" "target_laser" "spawnflags" "16" } { "targetname" "t19" "origin" "344 580 1536" "target" "t13" "classname" "target_laser" "spawnflags" "16" } { "targetname" "t20" "origin" "344 580 1512" "target" "t14" "classname" "target_laser" "spawnflags" "16" } { "targetname" "t21" "origin" "344 580 1488" "target" "t15" "classname" "target_laser" "spawnflags" "16" } { "targetname" "t16" "origin" "344 580 1608" "target" "t10" "spawnflags" "16" "classname" "target_laser" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" "origin" "960 248 1416" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" "origin" "648 160 1416" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" "origin" "736 320 1416" } { "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "656 496 1416" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" "origin" "656 400 1416" } { "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1008 -312 1416" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" "origin" "832 320 1416" } { "style" "3" "classname" "func_areaportal" "targetname" "t9" } { "model" "*22" "classname" "func_door" "angle" "-2" "target" "t9" "health" "10" "spawnflags" "0" } { "classname" "target_speaker" "origin" "784 -1032 912" "spawnflags" "1" "noise" "world/bubl2.wav" } { "classname" "target_speaker" "origin" "720 -1136 912" "spawnflags" "1" "noise" "world/bubl2.wav" } { "classname" "target_speaker" "origin" "776 -912 912" "spawnflags" "1" "noise" "world/bubl2.wav" } { "classname" "target_speaker" "origin" "648 -1000 912" "spawnflags" "1" "noise" "world/bubl2.wav" } { "classname" "target_speaker" "origin" "528 -1000 912" "spawnflags" "1" "noise" "world/bubl2.wav" } { "classname" "target_speaker" "origin" "400 -1000 912" "spawnflags" "1" "noise" "world/bubl2.wav" } { "classname" "target_speaker" "origin" "256 -1000 912" "spawnflags" "1" "noise" "world/bubl2.wav" } { "classname" "target_speaker" "origin" "112 -1000 912" "spawnflags" "1" "noise" "world/bubl2.wav" } { "classname" "target_speaker" "origin" "-40 -1000 912" "spawnflags" "1" "noise" "world/bubl2.wav" } { "classname" "target_speaker" "origin" "-240 -1000 912" "spawnflags" "1" "noise" "world/bubl2.wav" } { "classname" "target_speaker" "origin" "-448 -1040 912" "spawnflags" "1" "noise" "world/bubl2.wav" } { "classname" "target_speaker" "origin" "-456 -1048 1048" "spawnflags" "1" "noise" "world/bubl2.wav" } { "noise" "world/bubl2.wav" "spawnflags" "1" "origin" "-712 -1064 1048" "classname" "target_speaker" } { "noise" "world/bubl2.wav" "spawnflags" "1" "origin" "-576 -1080 1048" "classname" "target_speaker" } { "noise" "world/bubl2.wav" "spawnflags" "1" "origin" "896 -896 1032" "classname" "target_speaker" } { "noise" "world/bubl2.wav" "spawnflags" "1" "origin" "-456 -880 1048" "classname" "target_speaker" } { "noise" "world/bubl2.wav" "spawnflags" "1" "origin" "-360 -744 1048" "classname" "target_speaker" } { "noise" "world/bubl2.wav" "spawnflags" "1" "origin" "-504 -768 1048" "classname" "target_speaker" } { "noise" "world/bubl2.wav" "spawnflags" "1" "origin" "-608 -664 1048" "classname" "target_speaker" } { "noise" "world/bubl2.wav" "spawnflags" "1" "origin" "-344 -648 1048" "classname" "target_speaker" } { "noise" "world/bubl2.wav" "spawnflags" "1" "origin" "-368 -512 1048" "classname" "target_speaker" } { "noise" "world/bubl2.wav" "spawnflags" "1" "origin" "-512 -536 1048" "classname" "target_speaker" } { "noise" "world/bubl2.wav" "spawnflags" "1" "origin" "-680 -520 1048" "classname" "target_speaker" } { "noise" "world/bubl2.wav" "spawnflags" "1" "origin" "-616 -344 1048" "classname" "target_speaker" } { "noise" "world/bubl2.wav" "spawnflags" "1" "origin" "-400 -312 1048" "classname" "target_speaker" } { "noise" "world/bubl2.wav" "spawnflags" "1" "origin" "-512 -408 1048" "classname" "target_speaker" } { "classname" "target_speaker" "origin" "-560 -1248 1048" "spawnflags" "1" "noise" "world/bubl2.wav" } { "noise" "world/fan1.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "-392 -976 944" "targetname" "t5" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/fan1.wav" "targetname" "t5" "origin" "-352 -976 944" } { "model" "*23" "classname" "func_door" "angle" "-1" "target" "t8" } { "style" "4" "classname" "func_areaportal" "targetname" "t8" } { "classname" "light" "_color" "1.000000 0.780877 0.015936" "light" "195" "origin" "808 -200 1648" } { "classname" "light" "_color" "1.000000 0.780877 0.015936" "light" "195" "origin" "728 -104 1648" } { "classname" "light" "_color" "1.000000 0.780877 0.015936" "light" "195" "origin" "616 -24 1648" } { "classname" "light" "_color" "1.000000 0.780877 0.015936" "light" "195" "origin" "472 40 1648" } { "classname" "light" "_color" "1.000000 0.780877 0.015936" "light" "195" "origin" "320 64 1648" } { "origin" "168 40 1648" "classname" "light" "_color" "1.000000 0.780877 0.015936" "light" "195" } { "origin" "24 -24 1648" "classname" "light" "_color" "1.000000 0.780877 0.015936" "light" "195" } { "origin" "-88 -104 1648" "classname" "light" "_color" "1.000000 0.780877 0.015936" "light" "195" } { "origin" "-168 -200 1648" "classname" "light" "_color" "1.000000 0.780877 0.015936" "light" "195" } { "origin" "-216 -328 1648" "classname" "light" "_color" "1.000000 0.780877 0.015936" "light" "195" } { "origin" "-248 -456 1648" "classname" "light" "_color" "1.000000 0.780877 0.015936" "light" "195" } { "origin" "-248 -568 1648" "light" "195" "classname" "light" "_color" "1.000000 0.780877 0.015936" } { "origin" "856 -328 1648" "light" "195" "_color" "1.000000 0.780877 0.015936" "classname" "light" } { "style" "5" "targetname" "t7" "classname" "func_areaportal" } { "origin" "-512 -304 1124" "angle" "0" "spawnflags" "32" "classname" "misc_deadsoldier" } { "model" "*24" "classname" "trigger_hurt" "spawnflags" "4" "dmg" "1000" } { "model" "*25" "classname" "trigger_push" "angle" "270" } { "model" "*26" "_minlight" ".2" "angle" "-1" "classname" "func_door" } { "light" "120" "classname" "light" "origin" "992 -160 1440" } { "light" "120" "classname" "light" "origin" "928 -32 1440" } { "light" "120" "classname" "light" "origin" "800 96 1440" } { "light" "120" "classname" "light" "origin" "920 40 1440" } { "classname" "light" "light" "120" "origin" "784 216 1440" } { "model" "*27" "target" "t7" "sounds" "3" "speed" "120" "angle" "-1" "classname" "func_door" "_minlight" ".2" } { "origin" "320 192 1536" "light" "120" "classname" "light" } { "light" "120" "classname" "light" "origin" "1504 -896 1248" } { "dmg" "1" "classname" "target_explosion" "targetname" "t5" "origin" "-388 -1004 920" } { "dmg" "1" "classname" "target_explosion" "targetname" "t5" "delay" ".2" "origin" "-396 -1112 920" } { "dmg" "1" "classname" "target_explosion" "targetname" "t5" "delay" ".4" "origin" "-396 -1056 920" } { "dmg" "1" "classname" "target_explosion" "targetname" "t5" "origin" "-412 -1084 904" } { "classname" "target_explosion" "dmg" "1" "targetname" "t5" "delay" ".6" "origin" "-372 -1020 952" } { "classname" "trigger_relay" "targetname" "t5" "target" "t6" "origin" "-432 -960 904" "killtarget" "t6" } { "model" "*28" "classname" "trigger_hurt" "spawnflags" "6" "dmg" "1000" "targetname" "t5" } { "model" "*29" "classname" "trigger_push" "angle" "180" "speed" "50" "targetname" "t6" } { "model" "*30" "classname" "func_explosive" "dmg" "20" "health" "20" "target" "t5" } { "model" "*31" "target" "t1" "classname" "func_button" "angle" "-2" "wait" "-1" } { "light" "240" "origin" "1240 -1264 1280" "classname" "light" } { "light" "240" "origin" "1408 -1280 1280" "classname" "light" } { "light" "240" "origin" "1696 -896 1336" "classname" "light" } { "light" "240" "origin" "1648 -896 1280" "classname" "light" } { "light" "240" "origin" "1376 -928 1280" "classname" "light" } { "light" "240" "origin" "1272 -872 1280" "classname" "light" } { "light" "240" "origin" "1112 -888 1280" "classname" "light" } { "light" "240" "origin" "1000 -960 1280" "classname" "light" } { "light" "240" "origin" "960 -1192 1280" "classname" "light" } { "light" "240" "origin" "800 -1160 1280" "classname" "light" } { "light" "240" "origin" "880 -928 1280" "classname" "light" } { "light" "240" "origin" "640 -1152 1280" "classname" "light" } { "light" "240" "origin" "-448 -1088 1280" "classname" "light" } { "classname" "light" "origin" "512 -992 968" "light" "180" "_color" "0.000000 0.500000 1.000000" } { "classname" "light" "origin" "-288 -992 936" "light" "180" "_color" "0.000000 0.500000 1.000000" } { "classname" "light" "origin" "-456 -992 960" "light" "180" "_color" "0.000000 0.500000 1.000000" } { "model" "*32" "origin" "-380 -992 928" "classname" "func_rotating" "spawnflags" "2053" "speed" "600" "targetname" "t5" } { "model" "*33" "lip" "64" "wait" "-1" "spawnflags" "1" "targetname" "t1" "angle" "180" "classname" "func_door" "_minlight" ".2" } { "classname" "light" "origin" "-640 -1088 1280" "light" "240" } { "classname" "light" "origin" "1088 -1216 1280" "light" "240" } { "classname" "light" "origin" "-352 -992 1280" "light" "240" } { "classname" "light" "origin" "-608 -744 1280" "light" "240" } { "classname" "light" "origin" "-320 -768 1280" "light" "240" } { "classname" "light" "origin" "-512 -640 1280" "light" "240" } { "classname" "light" "origin" "-640 -512 1280" "light" "240" } { "classname" "light" "origin" "-512 -384 1280" "light" "240" } { "classname" "light" "origin" "-960 -416 1576" "light" "120" } { "light" "120" "origin" "-1152 -416 1576" "classname" "light" } { "light" "120" "origin" "-1056 -416 1576" "classname" "light" } { "classname" "light" "origin" "-1248 -832 1576" "light" "120" } { "classname" "light" "origin" "-1344 -704 1576" "light" "120" } { "light" "120" "origin" "-960 -800 1240" "classname" "light" } { "classname" "light" "origin" "-864 -832 1576" "light" "120" } { "classname" "light" "origin" "-864 -704 1576" "light" "120" } { "light" "120" "origin" "-864 -576 1576" "classname" "light" } { "targetname" "unitstart" "classname" "info_player_start" "angle" "45" "origin" "-1056 -536 1432" } { "light" "94" "classname" "light" "origin" "-64 448 1500" } { "origin" "-128 448 1500" "classname" "light" "light" "94" } { "light" "106" "classname" "light" "origin" "-176 536 1604" } { "classname" "light" "light" "80" "origin" "-176 508 1532" } { "origin" "-240 508 1532" "classname" "light" "light" "65" } { "light" "106" "classname" "light" "origin" "-304 536 1604" } { "light" "106" "classname" "light" "origin" "-240 536 1604" } { "light" "65" "classname" "light" "origin" "-240 388 1532" } { "light" "80" "classname" "light" "origin" "-176 388 1532" } { "light" "106" "classname" "light" "origin" "-176 360 1604" } { "classname" "light" "light" "106" "origin" "-240 360 1604" } { "light" "106" "classname" "light" "origin" "-304 360 1604" } { "targetname" "lab" "classname" "info_player_start" "angle" "0" "origin" "-16 448 1400" } { "model" "*34" "targetname" "t26" "spawnflags" "32" "team" "doorent" "classname" "func_door" "angle" "-1" } { "model" "*35" "spawnflags" "32" "team" "doorent" "classname" "func_door" "angle" "90" } { "model" "*36" "spawnflags" "32" "team" "doorent" "classname" "func_door" "angle" "270" } { "origin" "1552 -312 1440" "targetname" "rings1" "delay" "1.2" "classname" "trigger_relay" "target" "ring7" } { "origin" "1408 -312 1440" "targetname" "rings1" "classname" "trigger_relay" "target" "ring1" } { "origin" "1432 -312 1440" "targetname" "rings1" "delay" "0.2" "classname" "trigger_relay" "target" "ring2" } { "origin" "1456 -312 1440" "targetname" "rings1" "delay" "0.4" "classname" "trigger_relay" "target" "ring3" } { "origin" "1480 -312 1440" "targetname" "rings1" "delay" "0.6" "classname" "trigger_relay" "target" "ring4" } { "origin" "1504 -312 1440" "targetname" "rings1" "delay" "0.8" "classname" "trigger_relay" "target" "ring5" } { "origin" "1528 -312 1440" "targetname" "rings1" "delay" "1" "classname" "trigger_relay" "target" "ring6" } { "model" "*37" "targetname" "tellift" "_minlight" "0.2" "angle" "-1" "classname" "func_door" "speed" "200" } { "model" "*38" "target" "rings1" "wait" "8" "classname" "trigger_multiple" } { "origin" "1576 -312 1440" "delay" "3" "targetname" "rings1" "classname" "trigger_relay" "target" "tellift" } { "model" "*39" "speed" "40" "team" "telepteam" "classname" "func_door" "lip" "-23" "targetname" "ring7" "angle" "-2" "_minlight" "2" "spawnflags" "2048" } { "model" "*40" "speed" "40" "team" "telepteam" "classname" "func_door" "lip" "-185" "targetname" "ring1" "angle" "-2" "_minlight" "2" "spawnflags" "2048" } { "model" "*41" "speed" "40" "team" "telepteam" "classname" "func_door" "lip" "-158" "targetname" "ring2" "angle" "-2" "_minlight" "2" "spawnflags" "2048" } { "model" "*42" "speed" "40" "team" "telepteam" "classname" "func_door" "lip" "-131" "targetname" "ring3" "angle" "-2" "_minlight" "2" "spawnflags" "2048" } { "model" "*43" "speed" "40" "team" "telepteam" "classname" "func_door" "lip" "-104" "targetname" "ring4" "angle" "-2" "_minlight" "2" "spawnflags" "2048" } { "model" "*44" "speed" "40" "team" "telepteam" "classname" "func_door" "lip" "-77" "targetname" "ring5" "angle" "-2" "_minlight" "2" "spawnflags" "2048" } { "model" "*45" "speed" "40" "team" "telepteam" "classname" "func_door" "lip" "-50" "targetname" "ring6" "angle" "-2" "_minlight" "2" "spawnflags" "2048" } yquake2-QUAKE2_8_40/stuff/mapfixes/baseq2/jail5.ent000066400000000000000000001600511465112212000217500ustar00rootroot00000000000000// FIXED ENTITY STRING (by BjossiAlfreds) // // 1. Fixed 2x monster_infantry stuck in wall (b#1) { "nextmap" "security" "message" "Guard House" "classname" "worldspawn" "_wad" "gfx/e1u3.wad" "sounds" "8" "sky" "unit3_" } { "classname" "light" "light" "120" "origin" "104 1184 -184" } { "origin" "160 1392 -144" "light" "100" "classname" "light" } { "origin" "352 1416 -144" "light" "100" "classname" "light" } { "origin" "192 1144 -232" "light" "120" "classname" "light" } { "origin" "64 1048 -184" "classname" "light" "light" "120" } { "origin" "200 1224 -144" "classname" "light" "light" "100" } { "origin" "-24 1184 -184" "classname" "light" "light" "120" } { "origin" "-104 1256 -184" "classname" "light" "light" "120" } { "origin" "56 1280 -184" "classname" "light" "light" "120" } { "origin" "152 1016 -192" "light" "120" "classname" "light" } { "angle" "90" "classname" "info_player_coop" "targetname" "jail3" "origin" "96 -392 24" } { "angle" "90" "classname" "info_player_coop" "targetname" "jail3" "origin" "160 -392 24" } { "classname" "info_player_coop" "angle" "90" "targetname" "jail3" "origin" "48 -392 24" } { "model" "*1" "classname" "func_button" "angle" "90" "_minlight" ".2" "target" "t20" "spawnflags" "2048" } { "origin" "-896 1660 -216" "message" "You have found a secret area." "targetname" "t77" "classname" "target_secret" } { "model" "*2" "classname" "func_wall" } { "origin" "192 568 -64" "spawnflags" "1792" "target" "t18" "classname" "trigger_always" } { "origin" "-96 -488 232" "angle" "90" "targetname" "t104" "classname" "misc_teleporter_dest" "spawnflags" "1792" } { "origin" "544 416 -104" "angle" "180" "target" "t104" "classname" "misc_teleporter" "spawnflags" "1792" } { "origin" "544 224 -104" "angle" "0" "spawnflags" "1792" "targetname" "t103" "classname" "misc_teleporter_dest" } { "origin" "288 -416 232" "angle" "270" "spawnflags" "1792" "target" "t103" "classname" "misc_teleporter" } { "origin" "-1256 88 112" "classname" "item_health" } { "origin" "-1216 108 112" "spawnflags" "1792" "classname" "item_armor_body" } { "origin" "-1256 196 96" "spawnflags" "2" "classname" "misc_deadsoldier" } { "origin" "-1220 220 116" "classname" "item_pack" } { "origin" "-1216 192 172" "classname" "light" "light" "80" } { "origin" "-1216 128 172" "light" "80" "classname" "light" } { "origin" "-1108 218 180" "message" "You have found a secret area." "targetname" "t102" "classname" "target_secret" } { "model" "*3" "target" "t102" "classname" "trigger_once" } { "origin" "-1106 190 136" "targetname" "t101" "classname" "target_explosion" "dmg" "1" "delay" ".2" } { "origin" "-1110 134 118" "targetname" "t101" "delay" ".2" "dmg" "1" "classname" "target_explosion" } { "model" "*4" "target" "t101" "health" "30" "dmg" "1" "classname" "func_explosive" } { "origin" "-296 856 80" "spawnflags" "1792" "classname" "ammo_shells" } { "origin" "-240 808 80" "spawnflags" "1792" "classname" "weapon_shotgun" } { "origin" "-950 2088 -320" "spawnflags" "1792" "classname" "weapon_bfg" } { "origin" "600 576 16" "classname" "ammo_bullets" "spawnflags" "1536" } { "origin" "600 536 16" "spawnflags" "1536" "classname" "ammo_bullets" } { "origin" "672 584 16" "spawnflags" "1792" "classname" "weapon_machinegun" } { "origin" "928 912 -128" "spawnflags" "1792" "classname" "weapon_grenadelauncher" } { "origin" "544 2976 -136" "spawnflags" "1792" "classname" "ammo_cells" } { "origin" "480 2976 -136" "spawnflags" "1792" "classname" "ammo_cells" } { "origin" "480 3040 -128" "spawnflags" "1792" "classname" "weapon_hyperblaster" } { "origin" "1120 2456 -224" "classname" "ammo_shells" } { "origin" "1056 2464 -160" "spawnflags" "0" "classname" "weapon_supershotgun" } { "origin" "-828 1860 -216" "angles" "20 135 0" "classname" "info_player_intermission" } { "origin" "800 3352 -128" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "1408 3488 -256" "angle" "180" "classname" "info_player_deathmatch" } { "origin" "1416 2312 -216" "angle" "135" "classname" "info_player_deathmatch" } { "origin" "1312 800 -120" "angle" "135" "classname" "info_player_deathmatch" } { "origin" "328 760 -104" "angle" "180" "classname" "info_player_deathmatch" } { "origin" "736 224 -104" "angle" "180" "classname" "info_player_deathmatch" } { "origin" "-408 1728 -184" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "32 1120 -104" "angle" "225" "classname" "info_player_deathmatch" } { "origin" "-1072 32 120" "angle" "90" "classname" "info_player_deathmatch" } { "origin" "224 288 232" "angle" "180" "classname" "info_player_deathmatch" } { "origin" "32 -344 24" "angle" "90" "classname" "info_player_deathmatch" } { "classname" "ammo_cells" "origin" "1296 1264 -128" } { "classname" "ammo_cells" "origin" "1258 1240 -128" } { "classname" "item_power_shield" "origin" "1196 2386 -336" } { "classname" "light" "light" "80" "origin" "-32 288 -88" } { "classname" "trigger_relay" "targetname" "t80" "target" "t100" "delay" ".4" "origin" "88 544 -104" } { "classname" "target_secret" "targetname" "t99" "message" "You have found a secret area." "origin" "-416 1000 32" } { "model" "*5" "classname" "trigger_once" "target" "t99" } { "noise" "world/amb13.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "640 216 192" } { "classname" "target_speaker" "noise" "world/amb13.wav" "spawnflags" "1" "origin" "640 416 192" } { "noise" "world/amb8.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "264 584 -24" } { "classname" "target_speaker" "noise" "world/amb8.wav" "spawnflags" "1" "origin" "48 592 -40" } { "noise" "world/wind2" "classname" "target_speaker" "spawnflags" "1" "origin" "-200 1088 192" } { "noise" "world/wind2.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "-408 1112 192" } { "noise" "world/wind2.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "-376 640 192" } { "noise" "world/wind2.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "-144 640 192" } { "noise" "world/wind2.wav" "classname" "target_speaker" "spawnflags" "1" "origin" "96 656 192" } { "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "40 984 192" } { "noise" "world/water1.wav" "origin" "104 1232 -200" "spawnflags" "1" "classname" "target_speaker" } { "noise" "world/water1.wav" "origin" "-80 1232 -200" "spawnflags" "1" "classname" "target_speaker" } { "classname" "target_speaker" "spawnflags" "1" "origin" "136 1072 -200" "noise" "world/water1.wav" } { "classname" "item_health_small" "origin" "1360 2376 -224" } { "classname" "item_health_small" "origin" "1360 2336 -224" } { "classname" "item_health_small" "origin" "1320 2360 -224" } { "spawnflags" "1" "angle" "135" "classname" "monster_soldier_ss" "origin" "1096 2072 -216" } { "classname" "item_armor_combat" "origin" "1088 904 -128" } { "classname" "ammo_bullets" "origin" "976 3552 -264" } { "origin" "230 538 -310" "angle" "270" "classname" "misc_deadsoldier" "spawnflags" "32" } { "model" "*6" "dmg" "25" "classname" "trigger_hurt" } { "model" "*7" "classname" "trigger_hurt" "dmg" "25" } { "model" "*8" "dmg" "25" "classname" "trigger_hurt" } { "spawnflags" "3" "angle" "315" "classname" "monster_flyer" "origin" "-296 1248 208" "targetname" "t75" } { "spawnflags" "3" "classname" "monster_flyer" "origin" "-344 1200 280" "targetname" "t75" } { "classname" "monster_flyer" "spawnflags" "3" "origin" "-224 1248 280" "targetname" "t75" "angle" "315" } { "classname" "monster_mutant" "angle" "270" "targetname" "t97" "origin" "1088 832 24" } { "light" "120" "classname" "light" "origin" "1272 1048 -48" } { "classname" "light" "light" "120" "origin" "1264 944 -48" } { "origin" "1016 3552 -264" "classname" "ammo_bullets" } { "origin" "936 3552 -264" "classname" "ammo_bullets" } { "origin" "1080 2312 -144" "noise" "world/klaxon1.wav" "targetname" "t94" "classname" "target_speaker" } { "origin" "80 1118 -248" "classname" "item_health_mega" } { "origin" "120 1100 -264" "spawnflags" "2" "angle" "0" "classname" "misc_deadsoldier" } { "origin" "108 1154 -264" "spawnflags" "1" "angle" "180" "classname" "misc_deadsoldier" } { "origin" "144 -128 24" "classname" "monster_soldier_light" "angle" "90" } { "classname" "target_crosslevel_trigger" "targetname" "t75" "spawnflags" "4" "origin" "528 3112 -72" } { "model" "*9" "targetname" "message" "target" "t93" "classname" "trigger_multiple" "wait" "30" "angle" "90" } { "style" "1" "classname" "func_areaportal" "targetname" "t92" } { "origin" "672 2448 -208" "classname" "item_armor_shard" "angle" "45" } { "origin" "640 2400 -208" "classname" "item_armor_shard" "angle" "45" } { "origin" "592 2432 -208" "classname" "item_armor_shard" "angle" "45" } { "origin" "528 2416 -208" "classname" "item_armor_shard" "angle" "45" } { "origin" "704 2416 -208" "angle" "45" "classname" "item_armor_shard" } { "target" "t91" "targetname" "t90" "spawnflags" "1" "classname" "monster_soldier" "angle" "90" "origin" "956 3180 -176" } { "target" "t90" "targetname" "t89" "spawnflags" "1" "classname" "monster_soldier" "angle" "90" "origin" "984 3280 -192" } { "target" "t89" "targetname" "t88" "spawnflags" "1" "origin" "936 3344 -224" "classname" "monster_soldier" "angle" "90" } { "item" "ammo_bullets" "targetname" "t91" "spawnflags" "1" "origin" "796 3196 -124" "angle" "0" "classname" "monster_soldier" } { "model" "*10" "target" "t88" "classname" "trigger_once" } { "model" "*11" "target" "t87" "classname" "trigger_once" } { "origin" "1408 3320 -272" "targetname" "t86" "classname" "point_combat" } { "origin" "1128 3384 -272" "targetname" "t84" "classname" "point_combat" } { "origin" "1440 3384 -272" "targetname" "t85" "classname" "point_combat" } { "origin" "1152 3328 -272" "targetname" "t83" "classname" "point_combat" } { "origin" "1296 3432 -256" "targetname" "t87" "target" "t85" "classname" "monster_soldier" "angle" "0" "spawnflags" "1" } { "origin" "1304 3368 -256" "targetname" "t87" "target" "t86" "angle" "0" "classname" "monster_soldier" "spawnflags" "1" } { "origin" "1224 3368 -256" "targetname" "t87" "target" "t83" "classname" "monster_soldier" "angle" "180" "spawnflags" "1" } { "origin" "1256 3432 -256" "targetname" "t87" "target" "t84" "angle" "0" "classname" "monster_soldier" "spawnflags" "1" } { "origin" "1440 3208 -272" "classname" "item_health" } { "origin" "1440 3168 -272" "classname" "item_health" } { "origin" "1456 3488 -224" "classname" "light" "light" "80" } { "origin" "1456 3432 -224" "light" "80" "classname" "light" } { "model" "*12" "target" "t94" "classname" "trigger_once" "spawnflags" "4" "targetname" "t75" } { "targetname" "t94" "angle" "270" "classname" "monster_flyer" "spawnflags" "2" "origin" "1304 3168 80" } { "targetname" "t94" "angle" "270" "classname" "monster_flyer" "spawnflags" "2" "origin" "1256 3168 80" } { "targetname" "t94" "classname" "monster_flyer" "angle" "270" "spawnflags" "2" "origin" "1280 3112 80" } { "model" "*13" "classname" "trigger_push" "angle" "-1" "speed" "30" } { "model" "*14" "target" "t82" "classname" "trigger_once" } { "spawnflags" "1" "targetname" "t82" "origin" "536 288 -96" "classname" "monster_soldier_light" "angle" "180" } { "spawnflags" "1" "origin" "496 336 -96" "targetname" "t82" "classname" "monster_soldier_light" "angle" "180" } { "spawnflags" "1" "origin" "456 288 -96" "targetname" "t80" "classname" "monster_soldier_light" "angle" "0" } { "spawnflags" "1" "origin" "608 416 -96" "targetname" "t82" "angle" "180" "classname" "monster_soldier_light" } { "origin" "152 608 -112" "delay" ".6" "target" "t81" "targetname" "t80" "classname" "trigger_relay" } { "origin" "-372 996 -104" "item" "ammo_bullets" "targetname" "t79" "classname" "monster_soldier" } { "origin" "-440 996 -104" "item" "ammo_bullets" "targetname" "t79" "classname" "monster_soldier" } { "origin" "-376 1088 -64" "targetname" "t78" "delay" ".5" "classname" "target_explosion" } { "origin" "-432 1072 -32" "targetname" "t78" "delay" ".8" "classname" "target_explosion" } { "model" "*15" "dmg" "1" "targetname" "t79" "target" "t78" "mass" "800" "classname" "func_explosive" } { "origin" "-528 1304 -208" "classname" "ammo_cells" } { "origin" "-568 1304 -208" "classname" "ammo_cells" } { "spawnflags" "2048" "origin" "-952 2088 -320" "classname" "item_health_large" } { "origin" "-936 2008 -408" "spawnflags" "32" "angle" "0" "classname" "misc_deadsoldier" } { "origin" "-584 1656 -224" "angle" "90" "spawnflags" "1" "classname" "misc_deadsoldier" } { "target" "t79" "origin" "-520 1656 -200" "angle" "180" "classname" "monster_mutant" } { "origin" "-568 1504 -224" "classname" "misc_explobox" } { "origin" "-512 1568 -224" "classname" "misc_explobox" } { "model" "*16" "target" "t77" "classname" "trigger_once" } { "origin" "1148 2464 -368" "spawnflags" "2" "classname" "misc_deadsoldier" } { "origin" "1196 2464 -336" "classname" "item_health" } { "origin" "1196 2424 -336" "classname" "item_health" } { "origin" "1088 2432 -344" "targetname" "t76" "classname" "monster_mutant" } { "light" "150" "classname" "light" "origin" "632 936 32" } { "light" "150" "classname" "light" "origin" "632 1080 32" } { "classname" "light" "light" "150" "origin" "640 808 72" } { "light" "100" "classname" "light" "origin" "736 320 -96" } { "classname" "light" "light" "100" "origin" "736 320 -32" } { "origin" "-48 -416 48" "classname" "light" "light" "80" } { "origin" "240 -416 48" "classname" "light" "light" "80" } { "origin" "240 -160 48" "classname" "light" "light" "80" } { "origin" "-48 -152 48" "light" "80" "classname" "light" } { "origin" "520 3064 -72" "targetname" "t75" "classname" "target_goal" } { "message" "Pyramid key acquired.\nReturn to security complex." "origin" "504 2992 -56" "targetname" "t75" "classname" "target_help" } { "origin" "488 3088 -72" "killtarget" "message" "targetname" "t75" "classname" "trigger_relay" } { "targetname" "t93" "origin" "176 -320 136" "message" "Locate pyramid key\n then return to the\nsecurity complex." "classname" "target_help" } { "spawnflags" "1" "angle" "270" "classname" "monster_soldier_ss" "origin" "752 2072 -216" } { "spawnflags" "2048" "origin" "-1120 2000 -320" "classname" "weapon_grenadelauncher" } { "origin" "-1152 1928 -240" "target" "t73" "random" "0.2" "wait" "2" "spawnflags" "1" "classname" "func_timer" } { "origin" "-1172 1928 -240" "targetname" "t73" "sounds" "1" "angle" "0" "classname" "target_splash" } { "classname" "light" "light" "120" "origin" "-916 1884 -240" } { "light" "210" "classname" "light" "origin" "296 456 -368" "_color" "1.000000 0.309804 0.207843" } { "light" "210" "classname" "light" "origin" "24 456 -368" "_color" "1.000000 0.309804 0.207843" } { "light" "210" "classname" "light" "origin" "24 632 -368" "_color" "1.000000 0.309804 0.207843" } { "light" "84" "classname" "light" "origin" "32 224 -304" "_color" "1.000000 0.309804 0.207843" } { "origin" "800 848 64" "noise" "world/comp_hum2.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "816 896 64" "noise" "world/comp_hum2.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "688 920 64" "noise" "world/comp_hum2.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "688 1000 64" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" } { "origin" "800 1000 64" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" } { "origin" "688 848 64" "noise" "world/comp_hum2.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "640 568 24" "targetname" "t71" "classname" "monster_soldier_ss" "angle" "90" "spawnflags" "1" } { "origin" "832 600 24" "targetname" "t71" "spawnflags" "1" "angle" "90" "classname" "monster_soldier_ss" } { "model" "*17" "target" "t71" "classname" "trigger_once" } { "targetname" "t69" "origin" "736 224 -124" "classname" "info_null" } { "targetname" "t68" "origin" "544 224 -124" "classname" "info_null" } { "targetname" "t67" "origin" "544 416 -124" "classname" "info_null" } { "targetname" "t66" "origin" "736 416 -124" "classname" "info_null" } { "light" "64" "classname" "light" "origin" "736 224 240" "_color" "0.176471 0.588235 1.000000" } { "origin" "544 224 240" "classname" "light" "light" "64" "_color" "0.176471 0.588235 1.000000" } { "classname" "light" "light" "64" "origin" "544 416 240" "_color" "0.176471 0.588235 1.000000" } { "light" "64" "classname" "light" "origin" "736 416 240" "_color" "0.176471 0.588235 1.000000" } { "noise" "world/amb7.wav" "spawnflags" "1" "origin" "832 416 72" "classname" "target_speaker" } { "origin" "1368 2640 -40" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" } { "origin" "1240 2712 -112" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" } { "origin" "1248 2528 -112" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" } { "origin" "1224 2352 -112" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" } { "origin" "1000 2368 -40" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" } { "origin" "1040 2568 -112" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" } { "origin" "1056 2776 -40" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" } { "origin" "856 2456 -40" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" } { "origin" "888 2840 -40" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" } { "origin" "592 2640 -40" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" } { "origin" "600 2496 -40" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" } { "origin" "600 2784 -40" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" } { "origin" "872 2648 -40" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" } { "origin" "1368 2440 -40" "noise" "world/wind2.wav" "classname" "target_speaker" "spawnflags" "1" } { "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "480 2936 -64" } { "origin" "576 3224 -72" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb14.wav" } { "origin" "648 3296 -72" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb14.wav" } { "origin" "496 3152 -72" "noise" "world/amb14.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1280 3224 -184" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" } { "origin" "1280 3360 -200" "noise" "world/comp_hum1.wav" "spawnflags" "1" "classname" "target_speaker" } { "model" "*18" "speed" "150" "angle" "-1" "classname" "trigger_push" } { "origin" "1528 2728 -104" "item" "ammo_cells" "classname" "monster_flyer" "angle" "180" "spawnflags" "2" "target" "t20" } { "origin" "1536 2784 -104" "classname" "monster_flyer" "angle" "180" "spawnflags" "2" "target" "t20" } { "origin" "1528 2664 -104" "spawnflags" "2" "angle" "180" "classname" "monster_flyer" "target" "t20" } { "origin" "1200 2812 -216" // b#1: 2816 -> 2812 "classname" "monster_infantry" "angle" "270" "spawnflags" "1" } { "origin" "1360 2812 -216" // b#1: 2816 -> 2812 "spawnflags" "1" "angle" "270" "classname" "monster_infantry" } { "origin" "952 2040 -216" "classname" "monster_soldier_ss" "angle" "180" "spawnflags" "1" "targetname" "t98" } { "origin" "824 2072 -216" "item" "ammo_bullets" "spawnflags" "1" "angle" "270" "classname" "monster_soldier_ss" "target" "t98" } { "origin" "1056 1752 -216" "item" "ammo_shells" "spawnflags" "1" "angle" "270" "classname" "monster_soldier" } { "origin" "1112 1752 -216" "spawnflags" "1" "angle" "270" "classname" "monster_soldier" } { "origin" "-680 1496 -112" "light" "64" "classname" "light" "_color" "1.000000 0.338583 0.220472" "style" "10" } { "origin" "-680 1624 -112" "light" "64" "classname" "light" "_color" "1.000000 0.338583 0.220472" "style" "10" } { "spawnflags" "1" "origin" "-928 1904 -240" "random" "0.2" "wait" "2" "target" "t64" "classname" "func_timer" } { "origin" "-928 1872 -240" "targetname" "t64" "sounds" "1" "angle" "90" "classname" "target_splash" } { "origin" "-920 1808 -408" "targetname" "t54" "sounds" "5" "angle" "-1" "classname" "target_splash" } { "origin" "-920 1808 -360" "target" "t54" "random" "0.2" "wait" "2" "spawnflags" "1" "classname" "func_timer" } { "origin" "-1144 1816 -408" "targetname" "t56" "sounds" "5" "angle" "-1" "classname" "target_splash" } { "origin" "-1144 1816 -360" "target" "t56" "random" "0.2" "wait" "2" "spawnflags" "1" "classname" "func_timer" } { "origin" "-1184 1744 -408" "targetname" "t55" "sounds" "5" "angle" "-1" "classname" "target_splash" } { "origin" "-1184 1744 -360" "target" "t55" "random" "0.2" "wait" "2" "spawnflags" "1" "classname" "func_timer" } { "origin" "-1168 1880 -408" "targetname" "t57" "sounds" "5" "angle" "-1" "classname" "target_splash" } { "origin" "-1168 1880 -360" "target" "t57" "random" "0.2" "wait" "2" "spawnflags" "1" "classname" "func_timer" } { "origin" "-1112 2208 -408" "targetname" "t58" "sounds" "5" "angle" "-1" "classname" "target_splash" } { "origin" "-1112 2208 -360" "target" "t58" "random" "0.2" "wait" "2" "spawnflags" "1" "classname" "func_timer" } { "origin" "-1128 2136 -408" "targetname" "t59" "sounds" "5" "angle" "-1" "classname" "target_splash" } { "origin" "-1128 2136 -360" "target" "t59" "random" "0.2" "wait" "2" "spawnflags" "1" "classname" "func_timer" } { "origin" "-1024 2144 -408" "targetname" "t60" "sounds" "5" "angle" "-1" "classname" "target_splash" } { "origin" "-1024 2144 -360" "target" "t60" "random" "0.2" "wait" "2" "spawnflags" "1" "classname" "func_timer" } { "origin" "-920 1992 -408" "targetname" "t62" "sounds" "5" "angle" "-1" "classname" "target_splash" } { "origin" "-920 1992 -360" "target" "t62" "random" "0.2" "wait" "2" "spawnflags" "1" "classname" "func_timer" } { "origin" "-968 1952 -360" "target" "t61" "classname" "func_timer" "spawnflags" "1" "wait" "2" "random" "0.2" } { "origin" "-968 1952 -408" "targetname" "t61" "classname" "target_splash" "angle" "-1" "sounds" "5" } { "origin" "-872 1712 -360" "target" "t53" "classname" "func_timer" "spawnflags" "1" "wait" "2" "random" "0.2" } { "origin" "-872 1712 -408" "targetname" "t53" "classname" "target_splash" "angle" "-1" "sounds" "5" } { "origin" "-896 1904 -360" "target" "t63" "random" "0.2" "wait" "2" "spawnflags" "1" "classname" "func_timer" } { "origin" "-896 1904 -408" "targetname" "t63" "sounds" "5" "angle" "-1" "classname" "target_splash" } { "origin" "-1088 1952 -336" "angle" "90" "spawnflags" "2" "classname" "misc_deadsoldier" } { "origin" "-904 1760 -408" "classname" "target_speaker" "spawnflags" "1" "noise" "world/lava1.wav" } { "origin" "-928 1928 -408" "classname" "target_speaker" "spawnflags" "1" "noise" "world/lava1.wav" } { "origin" "-912 2032 -408" "classname" "target_speaker" "spawnflags" "1" "noise" "world/lava1.wav" } { "origin" "-1056 2192 -408" "classname" "target_speaker" "spawnflags" "1" "noise" "world/lava1.wav" } { "origin" "-1152 2176 -408" "noise" "world/lava1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1168 2080 -408" "classname" "target_speaker" "spawnflags" "1" "noise" "world/lava1.wav" } { "origin" "-1152 1768 -408" "noise" "world/lava1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-360 1560 -128" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "-360 1752 -128" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-920 1832 -240" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "-912 1888 -240" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1072 1000 -136" "classname" "light" "_color" "0.666667 1.000000 0.666667" "light" "50" } { "origin" "1104 1000 -136" "light" "50" "_color" "0.666667 1.000000 0.666667" "classname" "light" } { "origin" "1128 944 -136" "classname" "light" "_color" "0.666667 1.000000 0.666667" "light" "50" } { "origin" "1128 976 -136" "light" "50" "_color" "0.666667 1.000000 0.666667" "classname" "light" } { "origin" "1048 944 -136" "classname" "light" "_color" "0.666667 1.000000 0.666667" "light" "50" } { "origin" "1048 976 -136" "light" "50" "_color" "0.666667 1.000000 0.666667" "classname" "light" } { "origin" "1104 920 -136" "classname" "light" "_color" "0.666667 1.000000 0.666667" "light" "50" } { "origin" "1072 920 -136" "light" "50" "_color" "0.666667 1.000000 0.666667" "classname" "light" } { "origin" "640 536 96" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "832 600 96" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1280 1288 -48" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "896 1288 -48" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "1000 1720 -320" } { "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "864 1728 -320" } { "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "768 1816 -320" } { "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "768 1960 -320" } { "origin" "824 2040 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "944 2056 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1072 2056 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1096 2184 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1120 2344 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1112 2512 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1016 2424 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "928 2344 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "936 2520 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "904 2416 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "768 2416 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "648 2424 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "528 2416 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "528 2544 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1184 1776 -320" "classname" "target_speaker" "spawnflags" "1" "noise" "world/drip_amb.wav" } { "origin" "528 2688 -320" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" "origin" "1056 2984 -128" } { "origin" "544 2856 -128" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1056 3440 -192" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "768 3376 -64" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "origin" "472 2984 -112" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" } { "origin" "960 3096 -64" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "targetname" "t72" "origin" "-784 416 356" "classname" "info_null" } { "origin" "-736 464 356" "targetname" "t52" "classname" "info_null" } { "origin" "-800 656 356" "targetname" "t51" "classname" "info_null" } { "origin" "-1024 432 356" "targetname" "t50" "classname" "info_null" } { "origin" "-1136 288 356" "targetname" "t49" "classname" "info_null" } { "origin" "-1136 96 356" "targetname" "t48" "classname" "info_null" } { "origin" "-1056 -48 356" "targetname" "t47" "classname" "info_null" } { "origin" "-864 -48 356" "targetname" "t46" "classname" "info_null" } { "origin" "-672 -48 356" "targetname" "t45" "classname" "info_null" } { "origin" "-608 240 356" "targetname" "t44" "classname" "info_null" } { "origin" "-736 240 356" "targetname" "t42" "classname" "info_null" } { "origin" "-784 280 356" "targetname" "t43" "classname" "info_null" } { "origin" "-736 464 224" "classname" "light" "light" "64" "_color" "0.243137 0.619608 1.000000" } { "_color" "0.243137 0.619608 1.000000" "light" "64" "classname" "light" "origin" "-784 416 224" } { "origin" "-784 280 224" "classname" "light" "light" "64" "_color" "0.243137 0.619608 1.000000" } { "origin" "-800 656 224" "classname" "light" "light" "64" "_color" "0.243137 0.619608 1.000000" } { "origin" "-1024 432 224" "classname" "light" "light" "64" "_color" "0.243137 0.619608 1.000000" } { "origin" "-1136 288 224" "classname" "light" "light" "64" "_color" "0.243137 0.619608 1.000000" } { "origin" "-1136 96 224" "classname" "light" "light" "64" "_color" "0.243137 0.619608 1.000000" } { "origin" "-1056 -48 224" "classname" "light" "light" "64" "_color" "0.243137 0.619608 1.000000" } { "origin" "-864 -48 224" "classname" "light" "light" "64" "_color" "0.243137 0.619608 1.000000" } { "origin" "-672 -48 224" "light" "64" "classname" "light" "_color" "0.243137 0.619608 1.000000" } { "light" "64" "classname" "light" "origin" "-608 240 224" "_color" "0.243137 0.619608 1.000000" } { "origin" "-736 240 224" "classname" "light" "light" "64" "_color" "0.243137 0.619608 1.000000" } { "_cone" "15" "target" "t72" "origin" "-784 416 232" "classname" "light" "light" "400" "_color" "0.243137 0.619608 1.000000" } { "origin" "-48 -32 64" "targetname" "t3" "classname" "target_speaker" "spawnflags" "2049" "noise" "world/force1.wav" } { "origin" "-48 -288 64" "targetname" "t2" "noise" "world/force1.wav" "spawnflags" "2049" "classname" "target_speaker" } { "origin" "240 -288 64" "targetname" "t5" "classname" "target_speaker" "spawnflags" "2049" "noise" "world/force1.wav" } { "origin" "240 -32 64" "targetname" "t4" "noise" "world/force1.wav" "spawnflags" "2049" "classname" "target_speaker" } { "classname" "misc_deadsoldier" "spawnflags" "8" "origin" "104 488 -384" } { "classname" "misc_deadsoldier" "spawnflags" "2" "origin" "216 592 -384" } { "origin" "92 352 -376" "target" "t41" "targetname" "t40" "classname" "path_corner" } { "origin" "96 244 -376" "target" "t40" "targetname" "t41" "classname" "path_corner" } { "origin" "92 284 -360" "target" "t40" "spawnflags" "4" "classname" "misc_insane" } { "origin" "160 220 -368" "classname" "ammo_grenades" } { "origin" "160 352 -368" "classname" "ammo_bullets" } { "origin" "160 316 -368" "classname" "ammo_bullets" } { "target" "t80" "classname" "weapon_hyperblaster" "origin" "160 544 -120" } { "model" "*19" "classname" "trigger_once" "target" "t18" } { "spawnflags" "32" "classname" "misc_deadsoldier" "angle" "135" "origin" "258 598 -330" } { "classname" "misc_deadsoldier" "spawnflags" "32" "angle" "315" "origin" "68 580 -264" } { "model" "*20" "dmg" "25" "classname" "trigger_hurt" } { "model" "*21" "dmg" "25" "classname" "trigger_hurt" } { "model" "*22" "dmg" "25" "classname" "trigger_hurt" } { "model" "*23" "classname" "trigger_hurt" "dmg" "25" } { "_color" "1.000000 0.309804 0.207843" "origin" "296 632 -368" "classname" "light" "light" "210" } { "origin" "-32 224 -160" "classname" "light" "light" "64" } { "origin" "32 416 16" "classname" "light" "light" "64" } { "origin" "904 952 16" "classname" "ammo_shells" } { "origin" "880 984 0" "spawnflags" "2" "classname" "misc_deadsoldier" } { "origin" "880 912 -144" "classname" "misc_explobox" } { "origin" "848 944 -144" "classname" "misc_explobox" } { "origin" "1312 992 -120" "classname" "item_adrenaline" } { "model" "*24" "health" "25" "dmg" "100" "mass" "800" "classname" "func_explosive" } { "origin" "-1096 256 112" "classname" "ammo_shells" } { "classname" "ammo_shells" "origin" "-1096 292 112" } { "origin" "-672 624 8" "spawnflags" "1" "classname" "monster_soldier_ss" "angle" "180" "targetname" "t31" } { "classname" "item_armor_shard" "origin" "-68 360 224" } { "classname" "item_armor_shard" "origin" "-104 360 224" } { "classname" "item_armor_shard" "origin" "-32 360 224" } { "classname" "item_health_large" "origin" "408 -240 16" } { "classname" "ammo_grenades" "origin" "864 832 -128" } { "classname" "ammo_grenades" "origin" "864 792 -128" } { "classname" "monster_mutant" "angle" "0" "target" "t35" "origin" "1072 1144 -120" } { "classname" "path_corner" "targetname" "t37" "target" "t38" "origin" "1040 832 -136" } { "classname" "path_corner" "targetname" "t36" "target" "t37" "origin" "1240 840 -136" } { "classname" "path_corner" "targetname" "t34" "target" "t35" "origin" "952 1144 -136" } { "classname" "path_corner" "targetname" "t35" "target" "t36" "origin" "1208 1104 -136" } { "classname" "path_corner" "target" "t34" "targetname" "t38" "origin" "928 912 -136" } { "classname" "item_health_large" "origin" "744 672 16" } { "classname" "ammo_shells" "origin" "376 584 80" } { "classname" "ammo_shells" "origin" "336 584 80" } { "model" "*25" "classname" "trigger_once" "target" "t97" } { "model" "*26" "classname" "trigger_once" "target" "t97" } { "model" "*27" "classname" "trigger_once" "target" "t32" } { "model" "*28" "classname" "trigger_once" "target" "t32" } { "classname" "monster_mutant" "angle" "270" "spawnflags" "1" "targetname" "t32" "origin" "824 784 32" } { "spawnflags" "2048" "classname" "ammo_cells" "origin" "-296 902 80" } { "spawnflags" "2048" "classname" "ammo_cells" "origin" "-296 936 80" } { "classname" "ammo_cells" "origin" "-262 918 80" } { "classname" "item_health" "origin" "-608 680 0" } { "classname" "item_health" "origin" "-656 680 0" } { "classname" "ammo_bullets" "origin" "-736 544 224" } { "classname" "ammo_bullets" "origin" "-736 584 224" } { "classname" "ammo_bullets" "origin" "-736 624 224" } { "classname" "ammo_bullets" "origin" "-736 504 224" } { "model" "*29" "classname" "trigger_once" "target" "t31" } { "angle" "180" "classname" "monster_soldier_ss" "spawnflags" "1" "targetname" "t31" "origin" "-616 496 8" } { "classname" "monster_soldier_ss" "angle" "180" "spawnflags" "1" "targetname" "t31" "origin" "-728 560 8" } { "model" "*30" "classname" "trigger_once" "target" "t30" } { "classname" "monster_soldier_ss" "origin" "-541 -44 232" "targetname" "t30" "angle" "90" "spawnflags" "1" } { "classname" "monster_soldier_ss" "origin" "-597 168 232" "targetname" "t30" "angle" "270" "spawnflags" "1" } { "classname" "monster_infantry" "angle" "0" "spawnflags" "1" "targetname" "t29" "origin" "-472 32 232" } { "classname" "ammo_bullets" "origin" "72 -24 16" } { "origin" "-1008 240 96" "classname" "misc_explobox" } { "origin" "-1008 312 96" "classname" "misc_explobox" } { "origin" "-608 -24 224" "classname" "ammo_bullets" } { "origin" "-576 -24 224" "classname" "ammo_bullets" } { "origin" "2 -480 224" "classname" "item_health_small" } { "origin" "-32 -480 224" "classname" "item_health_small" } { "origin" "36 -480 224" "classname" "item_health_small" } { "origin" "224 96 16" "classname" "ammo_grenades" } { "origin" "224 56 16" "classname" "ammo_grenades" } { "origin" "224 344 228" "classname" "item_health" } { "origin" "96 -48 0" "spawnflags" "1" "classname" "misc_deadsoldier" } { "origin" "-8 -160 0" "spawnflags" "4" "classname" "misc_deadsoldier" } { "origin" "-200 -288 24" "spawnflags" "4" "classname" "misc_insane" "item" "ammo_shells" } { "angle" "90" "origin" "-120 -288 0" "classname" "misc_deadsoldier" } { "origin" "344 -288 24" "spawnflags" "32" "angle" "180" "classname" "misc_insane" "item" "ammo_cells" } { "origin" "-216 -88 8" "target" "t28" "targetname" "t27" "classname" "path_corner" } { "origin" "-224 16 8" "target" "t27" "targetname" "t26" "classname" "path_corner" } { "origin" "-128 -32 8" "targetname" "t28" "target" "t26" "classname" "path_corner" } { "origin" "-176 -32 24" "target" "t28" "classname" "misc_insane" } { "origin" "-484 1028 80" "classname" "item_health_small" } { "origin" "-484 988 80" "classname" "item_health_small" } { "origin" "-484 1064 80" "classname" "item_health_small" } { "origin" "-404 752 0" "classname" "item_health_small" } { "origin" "-404 720 0" "classname" "item_health_small" } { "origin" "224 616 64" "classname" "misc_deadsoldier" "spawnflags" "1" } { "origin" "232 744 64" "angle" "180" "spawnflags" "17" "classname" "misc_deadsoldier" } { "origin" "-440 1192 64" "spawnflags" "2" "classname" "misc_deadsoldier" } { "origin" "-408 1160 80" "target" "t24" "classname" "item_armor_combat" } { "model" "*31" "target" "t17" "classname" "trigger_multiple" } { "origin" "-160 728 80" "classname" "item_health" } { "origin" "-112 728 80" "classname" "item_health" } { "origin" "-416 992 0" "classname" "item_adrenaline" } { "model" "*32" "classname" "trigger_push" "angle" "-1" "speed" "35" } { "model" "*33" "speed" "35" "angle" "-1" "classname" "trigger_push" } { "targetname" "t25" "origin" "464 1456 112" "spawnflags" "2" "classname" "monster_flyer" "angle" "180" } { "targetname" "t25" "origin" "352 1544 104" "spawnflags" "2" "classname" "monster_flyer" } { "targetname" "t25" "origin" "216 1544 104" "spawnflags" "2" "classname" "monster_flyer" } { "targetname" "t25" "origin" "464 1328 112" "spawnflags" "2" "angle" "180" "classname" "monster_flyer" } { "model" "*34" "_minlight" "0.5" "classname" "func_door" "angle" "-2" "target" "t92" } { "model" "*35" "angle" "270" "classname" "trigger_monsterjump" } { "target" "t25" "targetname" "t24" "origin" "-416 1428 164" "angle" "270" "classname" "monster_mutant" "spawnflags" "2" } { "origin" "-408 1436 220" "light" "80" "classname" "light" } { "model" "*36" "targetname" "t24" "mass" "800" "dmg" "1" "classname" "func_explosive" } { "model" "*37" "target" "t17" "angle" "90" "classname" "func_button" } { "model" "*38" "classname" "func_wall" "spawnflags" "2054" "targetname" "t20" } { "classname" "point_combat" "spawnflags" "1" "targetname" "t19" "origin" "796 2920 -148" } { "classname" "monster_tank" "angle" "315" "target" "t19" "origin" "748 2976 -132" } { "spawnflags" "2048" "target" "t75" "classname" "key_pyramid" "origin" "480 3040 -132" } { "origin" "336 624 16" "classname" "light" "light" "64" } { "model" "*39" "targetname" "t81" "classname" "func_explosive" "dmg" "1" } { "model" "*40" "targetname" "t18" "dmg" "1" "classname" "func_explosive" } { "model" "*41" "origin" "28 564 -144" "wait" "-1" "classname" "func_door_rotating" "spawnflags" "128" "distance" "90" "speed" "300" "targetname" "t100" } { "model" "*42" "origin" "292 524 -144" "wait" "-1" "targetname" "t18" "speed" "300" "distance" "-90" "spawnflags" "128" "classname" "func_door_rotating" } { "model" "*43" "origin" "-450 772 -24" "speed" "50" "classname" "func_door_rotating" "distance" "-26" "spawnflags" "64" "_minlight" ".1" "targetname" "t17" } { "classname" "path_corner" "targetname" "t12" "target" "t13" "origin" "-1064 312 104" } { "classname" "path_corner" "target" "t12" "targetname" "t13" "origin" "-1064 96 104" } { "classname" "point_combat" "targetname" "t11" "spawnflags" "1" "origin" "-1040 56 104" } { "classname" "monster_infantry" "target" "t12" "spawnflags" "1" "origin" "-1064 352 120" "item" "ammo_bullets" } { "classname" "monster_infantry" "target" "t11" "spawnflags" "1" "origin" "-1088 8 120" } { "classname" "monster_infantry" "angle" "90" "targetname" "t10" "origin" "-352 168 232" "spawnflags" "1" "target" "t29" "item" "ammo_bullets" } { "model" "*44" "classname" "trigger_once" "target" "t10" } { "classname" "monster_gunner" "angle" "90" "origin" "16 232 240" "targetname" "t39" } { "classname" "item_health" "origin" "264 344 228" } { "classname" "monster_soldier_light" "angle" "90" "spawnflags" "0" "origin" "96 -96 32" "target" "t39" } { "model" "*45" "targetname" "t3" "classname" "func_wall" "spawnflags" "2055" } { "model" "*46" "targetname" "t4" "classname" "func_wall" "spawnflags" "2055" } { "model" "*47" "targetname" "t5" "classname" "func_wall" "spawnflags" "2055" } { "model" "*48" "targetname" "t2" "spawnflags" "2055" "classname" "func_wall" } { "model" "*49" "classname" "func_explosive" "health" "25" "dmg" "1" } { "map" "jail3$jail5" "classname" "target_changelevel" "targetname" "t1" "origin" "120 -632 72" } { "model" "*50" "angle" "270" "classname" "trigger_multiple" "target" "t1" } { "origin" "1016 1584 -192" "light" "150" "classname" "light" } { "classname" "light" "light" "150" "origin" "1176 1584 -192" } { "classname" "light" "light" "100" "origin" "1040 1560 -8" } { "classname" "light" "light" "150" "origin" "1056 1736 -192" } { "origin" "560 3236 -72" "classname" "light" "light" "80" } { "origin" "488 3164 -72" "classname" "light" "light" "80" } { "origin" "636 3308 -72" "light" "80" "classname" "light" } { "origin" "1168 3400 -104" "light" "120" "classname" "light" } { "model" "*51" "angle" "-2" "classname" "func_door" "target" "t33" } { "light" "120" "classname" "light" "origin" "1056 2992 -132" } { "light" "130" "classname" "light" "origin" "-420 1288 -72" } { "light" "64" "classname" "light" "origin" "-356 1232 -112" } { "origin" "484 2928 -68" "classname" "light" "light" "120" } { "origin" "964 3088 -68" "classname" "light" "light" "120" } { "light" "120" "classname" "light" "origin" "1060 3432 -196" } { "origin" "1012 3224 -184" "classname" "light" "light" "64" } { "origin" "1012 3176 -184" "light" "64" "classname" "light" } { "origin" "1128 3360 -216" "classname" "light" "light" "80" } { "origin" "1136 3224 -216" "classname" "light" "light" "80" } { "origin" "1424 3216 -216" "classname" "light" "light" "80" } { "origin" "1280 3144 -216" "classname" "light" "light" "80" } { "origin" "1280 3232 -184" "classname" "light" "light" "80" } { "origin" "1120 2336 -280" "classname" "light" "light" "64" } { "origin" "1120 2528 -280" "classname" "light" "light" "64" } { "origin" "928 2528 -280" "classname" "light" "light" "64" } { "origin" "768 2416 -280" "classname" "light" "light" "64" } { "origin" "528 2416 -280" "classname" "light" "light" "64" } { "origin" "528 2544 -280" "classname" "light" "light" "64" } { "origin" "528 2688 -280" "classname" "light" "light" "64" } { "origin" "928 2336 -280" "light" "64" "classname" "light" } { "light" "150" "classname" "light" "origin" "1236 1468 -40" } { "light" "150" "classname" "light" "origin" "1084 1452 -40" } { "origin" "948 1468 -40" "classname" "light" "light" "150" } { "origin" "1084 1308 -40" "classname" "light" "light" "150" } { "origin" "64 760 -72" "classname" "light" "light" "150" } { "light" "64" "classname" "light" "origin" "-16 796 -112" } { "origin" "144 796 -112" "classname" "light" "light" "64" } { "origin" "-420 1432 -72" "classname" "light" "light" "130" } { "light" "64" "classname" "light" "origin" "-356 1072 -112" } { "light" "150" "classname" "light" "origin" "64 864 -72" } { "light" "64" "classname" "light" "origin" "-16 464 16" } { "light" "64" "classname" "light" "origin" "336 464 16" } { "light" "64" "classname" "light" "origin" "288 672 16" } { "light" "64" "classname" "light" "origin" "288 416 16" } { "_color" "1.000000 0.309804 0.207843" "light" "84" "classname" "light" "origin" "160 224 -304" } { "light" "64" "classname" "light" "origin" "32 672 16" } { "target" "t68" "_color" "0.176471 0.588235 1.000000" "light" "450" "classname" "light" "origin" "544 224 232" } { "_color" "0.176471 0.588235 1.000000" "origin" "1032 672 8" "classname" "light" "light" "80" } { "_color" "0.176471 0.588235 1.000000" "origin" "1144 512 8" "classname" "light" "light" "80" } { "_color" "0.176471 0.588235 1.000000" "origin" "1032 512 8" "classname" "light" "light" "80" } { "_color" "0.176471 0.588235 1.000000" "origin" "1032 376 8" "classname" "light" "light" "80" } { "_color" "0.176471 0.588235 1.000000" "origin" "1144 264 8" "classname" "light" "light" "80" } { "_color" "0.176471 0.588235 1.000000" "origin" "952 376 8" "classname" "light" "light" "80" } { "_color" "0.176471 0.588235 1.000000" "origin" "952 264 8" "classname" "light" "light" "80" } { "_color" "0.176471 0.588235 1.000000" "origin" "1144 672 8" "light" "80" "classname" "light" } { "light" "120" "classname" "light" "origin" "896 1296 -52" } { "target" "t66" "_color" "0.176471 0.588235 1.000000" "origin" "736 416 232" "classname" "light" "light" "450" } { "target" "t69" "_color" "0.176471 0.588235 1.000000" "origin" "736 224 232" "classname" "light" "light" "450" } { "origin" "-16 624 16" "classname" "light" "light" "64" } { "target" "t67" "_color" "0.176471 0.588235 1.000000" "origin" "544 416 232" "light" "450" "classname" "light" } { "model" "*52" "spawnflags" "1" "classname" "func_plat" } { "origin" "544 2864 -132" "classname" "light" "light" "120" } { "classname" "light" "light" "80" "origin" "704 848 188" } { "light" "80" "classname" "light" "origin" "768 848 188" } { "origin" "832 424 76" "classname" "light" "light" "64" } { "origin" "1088 2096 -160" "light" "150" "classname" "light" } { "origin" "896 2056 -192" "light" "150" "classname" "light" } { "origin" "784 1936 -192" "light" "150" "classname" "light" } { "origin" "792 1760 -192" "light" "150" "classname" "light" } { "origin" "792 1760 -336" "classname" "light" "light" "150" } { "origin" "784 1936 -336" "classname" "light" "light" "150" } { "origin" "896 2056 -336" "classname" "light" "light" "150" } { "origin" "1088 2096 -304" "classname" "light" "light" "150" } { "origin" "1152 1576 -8" "light" "100" "classname" "light" } { "_color" "1.000000 0.694118 0.290196" "origin" "352 736 228" "classname" "light" "light" "80" } { "_color" "1.000000 0.694118 0.290196" "origin" "352 608 228" "classname" "light" "light" "80" } { "origin" "1280 1296 -52" "classname" "light" "light" "120" } { "light" "120" "classname" "light" "origin" "772 3376 -68" } { "light" "150" "origin" "1200 1728 -176" "classname" "light" } { "light" "150" "classname" "light" "origin" "1088 832 80" } { "light" "150" "classname" "light" "origin" "992 960 200" } { "light" "64" "classname" "light" "origin" "784 960 12" } { "light" "64" "classname" "light" "origin" "736 1200 96" } { "light" "64" "classname" "light" "origin" "672 1200 96" } { "light" "64" "classname" "light" "origin" "608 1200 96" } { "classname" "light" "light" "64" "origin" "784 896 12" } { "light" "150" "classname" "light" "origin" "1280 2904 -144" } { "light" "120" "classname" "light" "origin" "832 592 100" } { "origin" "976 960 -52" "classname" "light" "light" "80" } { "light" "80" "classname" "light" "origin" "864 960 -52" } { "light" "64" "classname" "light" "origin" "1088 832 -52" } { "_color" "1.000000 0.696356 0.291498" "light" "80" "classname" "light" "origin" "1344 2944 -84" } { "classname" "light" "light" "120" "origin" "640 528 100" } { "origin" "1184 960 200" "classname" "light" "light" "150" } { "origin" "960 960 80" "classname" "light" "light" "150" } { "origin" "800 1080 204" "classname" "light" "light" "80" } { "origin" "768 1004 188" "classname" "light" "light" "80" } { "origin" "1280 3284 -196" "classname" "light" "light" "80" } { "origin" "800 1032 204" "light" "80" "classname" "light" } { "origin" "768 928 188" "light" "80" "classname" "light" } { "origin" "704 928 188" "classname" "light" "light" "80" } { "origin" "704 1004 188" "classname" "light" "light" "80" } { "origin" "1280 3348 -196" "light" "80" "classname" "light" } { "classname" "light" "light" "120" "origin" "-912 1836 -244" } { "light" "150" "classname" "light" "origin" "-528 1720 -96" } { "classname" "light" "light" "150" "origin" "-528 1520 -96" } { "origin" "-364 1624 -134" "light" "120" "classname" "light" } { "origin" "-1156 2216 100" "classname" "light" "light" "250" } { "origin" "-1164 2052 100" "light" "250" "classname" "light" } { "origin" "-1176 1928 -240" "light" "120" "classname" "light" } { "light" "80" "classname" "light" "origin" "-448 1624 112" } { "classname" "light" "light" "80" "origin" "-448 1448 112" } { "origin" "264 -288 112" "classname" "light" "light" "80" } { "origin" "-104 1104 -72" "classname" "light" "light" "150" } { "origin" "8 992 -72" "classname" "light" "light" "150" } { "origin" "-248 1144 -72" "light" "150" "classname" "light" } { "origin" "160 1312 -8" "classname" "light" "light" "200" } { "origin" "192 1112 -8" "classname" "light" "light" "200" } { "origin" "8 1344 -8" "light" "200" "classname" "light" } { "_color" "1.000000 0.750000 0.289683" "light" "80" "classname" "light" "origin" "-640 512 164" } { "_color" "1.000000 0.750000 0.289683" "origin" "-640 640 164" "classname" "light" "light" "80" } { "_color" "1.000000 0.696356 0.291498" "light" "80" "classname" "light" "origin" "1216 2944 -84" } { "light" "80" "classname" "light" "origin" "-264 288 312" } { "light" "64" "classname" "light" "origin" "-992 -88 408" } { "light" "64" "classname" "light" "origin" "-760 -96 408" } { "light" "64" "classname" "light" "origin" "-616 -104 408" } { "light" "64" "classname" "light" "origin" "-544 -104 408" } { "light" "64" "classname" "light" "origin" "-480 -40 408" } { "light" "64" "classname" "light" "origin" "-488 104 408" } { "light" "64" "classname" "light" "origin" "-528 272 408" } { "light" "64" "classname" "light" "origin" "-696 376 408" } { "light" "64" "classname" "light" "origin" "-768 232 408" } { "origin" "-904 40 168" "light" "120" "classname" "light" } { "origin" "-536 112 240" "classname" "light" "light" "64" } { "_cone" "15" "target" "t44" "_color" "0.243137 0.619608 1.000000" "origin" "-608 240 232" "classname" "light" "light" "400" } { "light" "48" "classname" "light" "origin" "-916 508 40" } { "classname" "light" "light" "48" "origin" "-864 500 40" } { "classname" "light" "light" "48" "origin" "-868 456 56" } { "light" "48" "classname" "light" "origin" "-928 456 56" } { "classname" "light" "light" "48" "origin" "-868 424 72" } { "light" "48" "classname" "light" "origin" "-928 424 72" } { "classname" "light" "light" "48" "origin" "-868 392 88" } { "light" "48" "classname" "light" "origin" "-928 392 88" } { "classname" "light" "light" "48" "origin" "-860 228 104" } { "light" "48" "classname" "light" "origin" "-920 212 104" } { "classname" "light" "light" "48" "origin" "-848 192 120" } { "light" "48" "classname" "light" "origin" "-888 156 120" } { "classname" "light" "light" "48" "origin" "-816 164 136" } { "light" "48" "classname" "light" "origin" "-832 100 136" } { "light" "48" "classname" "light" "origin" "-776 96 152" } { "classname" "light" "light" "48" "origin" "-776 160 152" } { "light" "48" "classname" "light" "origin" "-744 96 168" } { "classname" "light" "light" "48" "origin" "-744 160 168" } { "light" "48" "classname" "light" "origin" "-712 96 184" } { "classname" "light" "light" "48" "origin" "-712 160 184" } { "light" "48" "classname" "light" "origin" "-828 524 24" } { "classname" "light" "light" "48" "origin" "-868 564 24" } { "_cone" "15" "target" "t46" "_color" "0.243137 0.619608 1.000000" "light" "400" "classname" "light" "origin" "-864 -48 232" } { "_cone" "15" "target" "t47" "_color" "0.243137 0.619608 1.000000" "light" "400" "classname" "light" "origin" "-1056 -48 232" } { "_cone" "15" "target" "t48" "_color" "0.243137 0.619608 1.000000" "light" "400" "classname" "light" "origin" "-1136 96 232" } { "_cone" "15" "target" "t49" "_color" "0.243137 0.619608 1.000000" "light" "400" "classname" "light" "origin" "-1136 288 232" } { "_cone" "15" "target" "t50" "_color" "0.243137 0.619608 1.000000" "light" "400" "classname" "light" "origin" "-1024 432 232" } { "_cone" "15" "target" "t51" "_color" "0.243137 0.619608 1.000000" "light" "400" "classname" "light" "origin" "-800 656 232" } { "_cone" "15" "target" "t52" "_color" "0.243137 0.619608 1.000000" "light" "400" "classname" "light" "origin" "-736 464 232" } { "_cone" "15" "target" "t43" "_color" "0.243137 0.619608 1.000000" "light" "400" "classname" "light" "origin" "-784 280 232" } { "_cone" "15" "target" "t42" "_color" "0.243137 0.619608 1.000000" "light" "400" "classname" "light" "origin" "-736 240 232" } { "light" "64" "classname" "light" "origin" "-536 -48 240" } { "_cone" "15" "target" "t45" "_color" "0.243137 0.619608 1.000000" "classname" "light" "light" "400" "origin" "-672 -48 232" } { "origin" "-352 32 312" "classname" "light" "light" "80" } { "origin" "-352 160 312" "classname" "light" "light" "80" } { "origin" "-352 288 312" "classname" "light" "light" "80" } { "origin" "864 832 -52" "classname" "light" "light" "80" } { "origin" "-672 32 352" "classname" "light" "light" "48" } { "origin" "-800 32 352" "classname" "light" "light" "48" } { "origin" "-928 32 352" "classname" "light" "light" "48" } { "origin" "-928 160 352" "classname" "light" "light" "48" } { "origin" "-992 288 352" "classname" "light" "light" "48" } { "origin" "-1120 416 352" "classname" "light" "light" "48" } { "origin" "-984 416 352" "classname" "light" "light" "48" } { "origin" "-736 544 352" "classname" "light" "light" "48" } { "origin" "-864 480 352" "classname" "light" "light" "48" } { "origin" "-864 352 352" "classname" "light" "light" "48" } { "origin" "-672 160 352" "light" "48" "classname" "light" } { "classname" "light" "light" "64" "origin" "-800 480 408" } { "classname" "light" "light" "64" "origin" "-856 624 408" } { "classname" "light" "light" "64" "origin" "-944 544 408" } { "classname" "light" "light" "64" "origin" "-928 440 408" } { "classname" "light" "light" "64" "origin" "-1184 360 408" } { "classname" "light" "light" "64" "origin" "-1176 192 408" } { "classname" "light" "light" "64" "origin" "-1064 224 408" } { "origin" "-1064 352 408" "classname" "light" "light" "64" } { "origin" "-832 128 408" "classname" "light" "light" "64" } { "origin" "-936 352 408" "classname" "light" "light" "64" } { "origin" "-864 288 408" "classname" "light" "light" "64" } { "origin" "-768 320 408" "classname" "light" "light" "64" } { "origin" "-1160 -88 408" "classname" "light" "light" "64" } { "origin" "-688 232 408" "classname" "light" "light" "64" } { "origin" "-576 64 408" "classname" "light" "light" "64" } { "origin" "-1168 32 408" "classname" "light" "light" "64" } { "origin" "-1056 32 408" "classname" "light" "light" "64" } { "origin" "-992 96 408" "classname" "light" "light" "64" } { "light" "120" "classname" "light" "origin" "-1040 296 168" } { "light" "150" "classname" "light" "origin" "-904 272 200" } { "light" "150" "classname" "light" "origin" "-888 480 168" } { "light" "150" "classname" "light" "origin" "-808 560 168" } { "classname" "light" "light" "120" "origin" "-1048 136 168" } { "style" "10" "_color" "1.000000 0.338583 0.220472" "classname" "light" "light" "64" "origin" "-824 1496 -112" } { "light" "48" "classname" "light" "origin" "160 -96 320" } { "light" "48" "classname" "light" "origin" "224 -224 320" } { "light" "48" "classname" "light" "origin" "32 -224 320" } { "light" "48" "classname" "light" "origin" "32 -96 320" } { "light" "48" "classname" "light" "origin" "96 32 320" } { "classname" "light" "light" "48" "origin" "224 -416 248" } { "light" "80" "classname" "light" "origin" "-72 -288 112" } { "light" "80" "classname" "light" "origin" "264 -32 112" } { "classname" "light" "light" "80" "origin" "-72 -32 112" } { "classname" "light" "light" "64" "origin" "96 288 368" } { "classname" "light" "light" "64" "origin" "-32 224 368" } { "classname" "light" "light" "64" "origin" "-32 24 368" } { "classname" "light" "light" "64" "origin" "-32 -160 368" } { "classname" "light" "light" "64" "origin" "-32 -288 368" } { "classname" "light" "light" "64" "origin" "-32 -416 368" } { "classname" "light" "light" "64" "origin" "96 -352 368" } { "classname" "light" "light" "64" "origin" "160 -288 368" } { "classname" "light" "light" "64" "origin" "224 -152 368" } { "classname" "light" "light" "64" "origin" "104 -160 368" } { "light" "64" "classname" "light" "origin" "224 224 368" } { "light" "64" "classname" "light" "origin" "160 32 368" } { "classname" "light" "light" "64" "origin" "96 -32 368" } { "light" "200" "classname" "light" "origin" "96 -136 112" } { "light" "200" "classname" "light" "origin" "96 16 112" } { "classname" "light" "light" "200" "origin" "96 -368 112" } { "light" "64" "classname" "light" "origin" "96 160 148" } { "classname" "light" "light" "64" "origin" "96 160 72" } { "classname" "light" "light" "64" "origin" "96 160 236" } { "model" "*53" "classname" "func_plat" "spawnflags" "1" } { "targetname" "jail3" "classname" "info_player_start" "angle" "90" "origin" "96 -504 24" } { "model" "*54" "target" "t3" "sounds" "3" "lip" "-8" "angle" "180" "classname" "func_button" } { "model" "*55" "target" "t2" "classname" "func_button" "angle" "180" "lip" "-8" "sounds" "3" } { "model" "*56" "target" "t5" "sounds" "3" "lip" "-8" "angle" "0" "classname" "func_button" } { "light" "80" "classname" "light" "origin" "-448 32 312" } { "light" "80" "classname" "light" "origin" "-176 288 312" } { "model" "*57" "target" "t4" "classname" "func_button" "angle" "0" "lip" "-8" "sounds" "3" } { "model" "*58" "sounds" "4" "classname" "func_door" "angle" "-2" "_minlight" ".3" } { "origin" "160 -628 8" "classname" "light" "light" "80" } { "light" "80" "classname" "light" "origin" "160 -508 8" } { "origin" "32 -508 8" "classname" "light" "light" "80" } { "light" "80" "classname" "light" "origin" "32 -628 8" } { "origin" "96 -564 128" "classname" "light" "light" "150" } yquake2-QUAKE2_8_40/stuff/mapfixes/baseq2/lab.ent000066400000000000000000004253731465112212000215150ustar00rootroot00000000000000// FIXED ENTITY STRING (by BjossiAlfreds) // // 1. Fixed inaccessible monster_medic on easy (b#1) // // He spawns on all difficulties but the trigger_once that releases him is // medium+. Changed his spawnflags from 0 to 256 (not-easy). // // 2. Fixed 2x inaccessible monster_parasite on easy/medium (b#2) // // These guys spawn on all difficulties but the trigger_once that releases // them only spawns on hard+. // Changed their spawnflags to 768 (not-easy + not-medium). // // 3. Added missing targetnames to info_player_coop (b#3) // // 4. Activated 4x target_speaker (b#4) // // These are ambient sounds so should be spawnflags 1. // // 5. Fixed insane marine screams in DM (b#5) // // In DM, a numbr of func_timer and target_speaker play // random insane marine screams. I fixed some broken // targeting and added 1792 spawnflags to the DM-only // speaker entities. // // 6. Removed thud sound when triggered monsters are activated (b#6) // // 7. Removed unused entities + cleanup (b#7) // // 8. Fixed glitchy medic (b#8) // // This medic has a combat target but is not // set to ambush. The AI was not designed for // combat and sound targets co-operation so I // set him to ambush mode and gave him // a targetname so he is angered by the trigger // // 9. Fixed no-sound console button at the start // // 10. Removed leftover forcefield disable button from deathmatch mode { "message" "Research Lab" "classname" "worldspawn" "sounds" "6" "angle" "90" "nextmap" "command" } { "classname" "point_combat" "targetname" "t383" "origin" "-456 -216 0" } { "classname" "point_combat" "targetname" "t382" "target" "t383" "origin" "-424 -216 0" } { "spawnflags" "2048" "classname" "item_health" "origin" "-1624 636 8" } { "classname" "item_health" "spawnflags" "2048" "origin" "-1660 636 8" } { "model" "*1" "spawnflags" "1792" "classname" "func_wall" } { "light" "100" "classname" "light" "origin" "-936 832 -400" } { "classname" "light" "light" "100" "origin" "-936 736 -400" } { "origin" "-2096 -128 -400" "light" "100" "classname" "light" } { "origin" "-2000 -128 -400" "classname" "light" "light" "100" } { "origin" "-936 832 48" "classname" "light" "light" "100" } { "origin" "-936 736 48" "light" "100" "classname" "light" } { "origin" "-2000 -128 48" "classname" "light" "light" "100" } { "origin" "-2096 -128 48" "light" "100" "classname" "light" } { "origin" "-1644 1632 -412" "target" "makeitstop2" "targetname" "killmenow" "delay" "2" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this } { "model" "*2" "targetname" "makeitstop" "spawnflags" "3840" // b#7: 4 -> 3840 "classname" "trigger_once" } { "model" "*3" "spawnflags" "1" "mass" "100" "targetname" "makeitstop2" "classname" "func_explosive" } { "origin" "-1656 1608 -412" "target" "makeitstop" "delay" "1" "targetname" "killmenow" "classname" "trigger_relay" "spawnflags" "3840" // b#7: now unnecessary trigger } { "origin" "-1676 1632 -412" "killtarget" "dead" "targetname" "killmenow" "classname" "trigger_relay" "spawnflags" "3840" // b#7: added this } { "model" "*4" "targetname" "dead" "classname" "func_wall" } { "model" "*5" "message" "A.H.D.S.S.I.B.H.\nbjjc" "classname" "trigger_once" } { "targetname" "t190" "classname" "target_speaker" "noise" "world/klaxon1.wav" "spawnflags" "2050" // b#7: 2 -> 2050 "origin" "-320 1216 72" } { "targetname" "t190" "classname" "target_speaker" "noise" "world/klaxon1.wav" "spawnflags" "2050" // b#7: 2 -> 2050 "origin" "-320 976 72" } { "targetname" "t190" "classname" "target_speaker" "noise" "world/klaxon1.wav" "spawnflags" "2050" // b#7: 2 -> 2050 "origin" "-368 776 72" } { "targetname" "t190" "classname" "target_speaker" "noise" "world/klaxon1.wav" "spawnflags" "2050" // b#7: 2 -> 2050 "origin" "-640 760 72" } { "targetname" "t190" "classname" "target_speaker" "noise" "world/klaxon1.wav" "spawnflags" "2050" // b#7: 2 -> 2050 "origin" "-640 464 72" } { "targetname" "t190" "classname" "target_speaker" "noise" "world/klaxon1.wav" "spawnflags" "2050" // b#7: 2 -> 2050 "origin" "-640 256 72" } { "classname" "point_combat" "targetname" "t379" "origin" "-2848 888 -424" } { "model" "*6" "target" "t339" "pathtarget" "e1" "classname" "trigger_multiple" "spawnflags" "2048" } { "model" "*7" "target" "t341" "pathtarget" "e4" "classname" "trigger_multiple" "spawnflags" "2048" } { "origin" "-1604 1120 -416" "target" "t378" "targetname" "t343" "classname" "trigger_relay" "spawnflags" "2048" } { "model" "*8" "targetname" "t378" "spawnflags" "2051" "classname" "func_wall" } { "model" "*9" "_minlight" ".2" "wait" "-1" "targetname" "t375" "lip" "-80" "angle" "-1" "classname" "func_door" } { "model" "*10" "targetname" "t296" "wait" "-1" "_minlight" ".2" "speed" "120" "lip" "-126" "angle" "-1" "classname" "func_door" } { "origin" "-1528 1632 -400" "spawnflags" "2048" "targetname" "t32" "classname" "target_goal" } { "origin" "-1776 248 8" "targetname" "t259" "classname" "target_goal" } { "model" "*11" "classname" "func_button" "spawnflags" "2048" // b#10: this was missing "wait" "-1" "angle" "90" "target" "t229" "_minlight" ".3" } { "model" "*12" "classname" "func_button" "angle" "90" "_minlight" ".3" "target" "t228" "wait" "-1" "spawnflags" "2048" } { "origin" "-1440 -608 48" "targetname" "t373" "noise" "world/brkglas.wav" "classname" "target_speaker" "spawnflags" "2048" // b#7: added this } { "model" "*13" "target" "t373" "targetname" "t328" "mass" "150" "classname" "func_explosive" } { "classname" "info_player_coop" "targetname" "lstart" // b#3: added this "angle" "90" "origin" "92 436 16" } { "classname" "info_player_coop" "targetname" "lstart" // b#3: added this "angle" "90" "origin" "36 436 16" } { "origin" "64 368 16" "angle" "90" "classname" "info_player_coop" "targetname" "lstart" // b#3: added this } { "origin" "-2092 144 8" "spawnflags" "1536" "classname" "ammo_slugs" } { "origin" "-824 1284 -472" "spawnflags" "2048" "classname" "ammo_shells" } { "model" "*14" "classname" "trigger_counter" "count" "20" "spawnflags" "2049" // b#7: 1 -> 2049 "targetname" "marduk" "target" "killmenow" "killtarget" "dead" // b#7: added this } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t371" "target" "t372" "origin" "-140 464 8" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t370" "target" "t371" "origin" "-140 636 8" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t369" "target" "t370" "origin" "-112 632 8" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t368" "target" "t369" "origin" "-112 432 8" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t367" "target" "t368" "origin" "-272 432 8" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t366" "target" "t367" "origin" "-324 376 8" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t365" "target" "t366" "origin" "-324 508 8" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t372" "target" "t365" "origin" "-276 464 8" } { "classname" "misc_insane" "spawnflags" "4" "target" "t365" "origin" "-308 520 24" } { "classname" "misc_deadsoldier" "spawnflags" "4" "angle" "135" "origin" "-428 492 16" } { "classname" "misc_insane" "angle" "360" "spawnflags" "48" "origin" "-412 424 40" } { "_color" "1.000000 0.027451 0.027451" "light" "96" "classname" "light" "origin" "-416 488 120" } { "classname" "light" "light" "96" "_color" "1.000000 0.027451 0.027451" "origin" "-416 408 120" } { "spawnflags" "2048" "classname" "item_health_large" "origin" "-466 1784 -456" } { "classname" "item_health_large" "spawnflags" "2048" "origin" "-430 1784 -456" } { "spawnflags" "2048" "classname" "ammo_cells" "origin" "-732 1768 -456" } { "classname" "ammo_cells" "spawnflags" "2048" "origin" "-732 1732 -456" } { "light" "140" "classname" "light" "origin" "-2792 1164 -384" } { "origin" "-2792 1224 -384" "classname" "light" "light" "140" } { "origin" "-2792 1292 -384" "classname" "light" "light" "140" } { "light" "140" "classname" "light" "origin" "-2792 1292 -332" } { "light" "140" "classname" "light" "origin" "-2792 1224 -332" } { "spawnflags" "2048" "classname" "ammo_bullets" "origin" "-1388 192 8" } { "classname" "ammo_bullets" "spawnflags" "2048" "origin" "-1388 156 8" } { "classname" "ammo_shells" "origin" "-800 128 8" } { "classname" "ammo_grenades" "origin" "-760 128 8" } { "spawnflags" "2048" "classname" "item_health" "origin" "76 -404 48" } { "classname" "item_health" "spawnflags" "3072" "origin" "40 -404 48" } { "origin" "-1696 1896 -416" "classname" "light" "light" "150" } { "origin" "-1696 1928 -392" "classname" "light" "light" "150" } { "model" "*15" "classname" "func_wall" "spawnflags" "2051" "targetname" "t267" } { "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" "origin" "-536 976 -336" } { "model" "*16" "spawnflags" "2048" "classname" "func_wall" } { "model" "*17" "spawnflags" "1792" "classname" "func_wall" } { "origin" "-1548 400 -104" "classname" "ammo_shells" "spawnflags" "1792" } { "origin" "-1548 360 -104" "spawnflags" "1792" "classname" "ammo_bullets" } { "origin" "-1218 -780 24" "angles" "0 135 0" "classname" "info_player_intermission" } { "origin" "-952 1512 -488" "classname" "ammo_bullets" "spawnflags" "1536" } { "origin" "-952 1548 -488" "spawnflags" "1536" "classname" "ammo_bullets" } { "origin" "-732 1772 -456" "spawnflags" "1792" "classname" "ammo_shells" } { "origin" "-732 1732 -456" "spawnflags" "1792" "classname" "weapon_supershotgun" } { "origin" "-1300 1036 -488" "classname" "item_health" "spawnflags" "1792" } { "origin" "-780 1036 -488" "spawnflags" "1792" "classname" "item_health" } { "origin" "-1696 1916 -456" "classname" "ammo_cells" "spawnflags" "1792" } { "origin" "-1440 1908 -456" "spawnflags" "1792" "classname" "ammo_cells" } { "origin" "-1568 1620 -460" "spawnflags" "1792" "classname" "weapon_grenadelauncher" } { "origin" "-2080 1904 -432" "classname" "ammo_slugs" "spawnflags" "1792" } { "origin" "-2040 1904 -432" "spawnflags" "1792" "classname" "ammo_slugs" } { "origin" "-3240 1360 -328" "classname" "item_health_small" "spawnflags" "1792" } { "origin" "-3240 1276 -328" "classname" "item_health_small" "spawnflags" "1792" } { "origin" "-3240 1188 -328" "classname" "item_health_small" "spawnflags" "1792" } { "origin" "-3240 1092 -328" "classname" "item_health_small" "spawnflags" "1792" } { "classname" "item_armor_shard" "spawnflags" "1792" "origin" "-1816 628 8" } { "origin" "-1816 592 8" "spawnflags" "1792" "classname" "item_armor_shard" } { "origin" "-2008 484 72" "spawnflags" "1792" "classname" "weapon_bfg" } { "origin" "-2000 524 84" "target" "t364" "spawnflags" "1792" "classname" "trigger_always" } { "origin" "-1924 516 100" "target" "t364" "targetname" "t190" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this } { "model" "*18" "spawnflags" "1792" "classname" "func_wall" } { "origin" "-872 784 16" "target" "t363" "spawnflags" "1792" "angle" "180" "classname" "misc_teleporter" } { "origin" "-872 784 -444" "targetname" "t363" "spawnflags" "1792" "angle" "360" "classname" "misc_teleporter_dest" } { "origin" "-1612 792 16" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-1648 792 16" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-1716 956 -456" "classname" "item_health" "spawnflags" "1792" } { "origin" "-1716 920 -456" "spawnflags" "1792" "classname" "item_health" } { "origin" "-2312 716 -440" "classname" "item_health_small" "spawnflags" "1792" } { "origin" "-2312 612 -440" "spawnflags" "1792" "classname" "item_health_small" } { "origin" "-3152 1328 -416" "classname" "ammo_grenades" "spawnflags" "1792" } { "origin" "-3152 1288 -416" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-3044 1212 -420" "spawnflags" "1792" "classname" "item_armor_combat" } { "origin" "-2792 1164 -416" "classname" "ammo_bullets" "spawnflags" "1792" } { "origin" "-2828 1164 -416" "spawnflags" "1792" "classname" "ammo_bullets" } { "origin" "-2988 852 -416" "classname" "item_health" "spawnflags" "1792" } { "origin" "-2988 892 -416" "spawnflags" "1792" "classname" "item_health" } { "origin" "-2700 912 -416" "spawnflags" "1792" "classname" "ammo_shells" } { "origin" "-2740 912 -416" "spawnflags" "1792" "classname" "weapon_supershotgun" } { "origin" "-2084 260 8" "spawnflags" "1792" "classname" "weapon_machinegun" } { "origin" "-1496 336 -96" "team" "doh1" "spawnflags" "1792" "classname" "item_health_mega" } { "origin" "-1496 336 -96" "team" "doh1" "spawnflags" "1792" "classname" "weapon_railgun" } { "origin" "-1214 360 20" "classname" "item_health_small" "spawnflags" "1792" } { "origin" "-1090 360 20" "spawnflags" "1792" "classname" "item_health_small" } { "origin" "-1344 -428 40" "spawnflags" "1792" "classname" "weapon_supershotgun" } { "origin" "-1000 -492 8" "spawnflags" "1792" "classname" "item_health_small" } { "origin" "-1000 -536 8" "spawnflags" "1792" "classname" "item_health_small" } { "origin" "-1000 -448 8" "spawnflags" "1792" "classname" "item_health_small" } { "origin" "-384 -480 40" "spawnflags" "1792" "classname" "weapon_machinegun" } { "origin" "-384 -544 48" "classname" "item_health" } { "origin" "0 -480 40" "spawnflags" "1792" "classname" "item_armor_combat" } { "origin" "64 -88 40" "classname" "ammo_grenades" "spawnflags" "1792" } { "origin" "24 -88 40" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-668 44 8" "classname" "ammo_bullets" "spawnflags" "1792" } { "origin" "-612 44 8" "spawnflags" "1792" "classname" "ammo_bullets" } { "origin" "-640 84 8" "spawnflags" "1792" "classname" "weapon_chaingun" } { "model" "*19" "spawnflags" "1792" "classname" "func_wall" } { "origin" "-320 1372 40" "classname" "item_armor_shard" "spawnflags" "1792" } { "origin" "-356 1372 40" "classname" "item_armor_shard" "spawnflags" "1792" } { "origin" "-284 1372 40" "spawnflags" "1792" "classname" "item_armor_shard" } { "origin" "-128 780 8" "spawnflags" "1792" "team" "doh" "classname" "item_quad" } { "origin" "-128 780 8" "team" "doh" "spawnflags" "1792" "classname" "weapon_railgun" } { "origin" "272 840 -24" "spawnflags" "1792" "classname" "ammo_shells" } { "origin" "224 828 -24" "spawnflags" "1792" "classname" "weapon_supershotgun" } { "model" "*20" "_minlight" ".2" "spawnflags" "1792" "classname" "func_wall" } { "angle" "90" "targetname" "t362" "origin" "64 672 16" "classname" "misc_teleporter_dest" "spawnflags" "1792" } { "spawnflags" "1792" "targetname" "t361" "origin" "-1448 -432 48" "angle" "315" "classname" "misc_teleporter_dest" } { "angle" "90" "origin" "-448 1760 -448" "target" "t359" "classname" "misc_teleporter" "spawnflags" "1792" } { "origin" "-928 88 16" "target" "t243" "spawnflags" "1792" "classname" "trigger_always" } { "spawnflags" "1792" "origin" "-656 480 64" "target" "t256" "classname" "trigger_always" } { "origin" "-640 104 64" "target" "t240" "spawnflags" "1792" "classname" "trigger_always" } { "origin" "64 1104 24" "target" "t255" "spawnflags" "1792" "classname" "trigger_always" } { "light" "100" "classname" "light" "origin" "-1760 424 140" } { "classname" "light" "light" "100" "origin" "-1760 552 140" } { "classname" "trigger_always" "spawnflags" "1792" "target" "t211" "origin" "-976 -656 48" } { "spawnflags" "1792" "target" "t362" "classname" "misc_teleporter" "origin" "-1248 256 -96" } { "spawnflags" "1792" "targetname" "t360" "classname" "misc_teleporter_dest" "angle" "90" "origin" "-1792 184 -96" } { "spawnflags" "1792" "target" "t358" "classname" "misc_teleporter" "origin" "-1696 1392 -440" } { "spawnflags" "1792" "targetname" "t359" "classname" "misc_teleporter_dest" "angle" "90" "origin" "-3240 992 -320" } { "spawnflags" "1792" "target" "t360" "classname" "misc_teleporter" "origin" "-2656 1416 -320" } { "angle" "315" "classname" "info_player_deathmatch" "origin" "-920 1696 -448" } { "target" "t375" "classname" "trigger_always" "spawnflags" "1792" "origin" "-3032 1166 -400" } { "angle" "270" "classname" "info_player_deathmatch" "origin" "-1216 1736 -480" } { "angle" "180" "classname" "info_player_deathmatch" "origin" "-2800 1304 -408" } { "angle" "90" "classname" "info_player_deathmatch" "origin" "-2840 736 -408" } { "classname" "info_player_deathmatch" "angle" "90" "origin" "-2712 536 -408" } { "classname" "info_player_deathmatch" "origin" "-1624 600 16" } { "classname" "info_player_deathmatch" "angle" "45" "origin" "-1320 -808 16" } { "classname" "info_player_deathmatch" "angle" "45" "origin" "-544 -480 48" } { "spawnflags" "1792" "classname" "trigger_always" "target" "t292" "origin" "16 -144 72" } { "classname" "trigger_always" "spawnflags" "1792" "target" "t296" "origin" "-544 -480 72" } { "angle" "270" "classname" "info_player_deathmatch" "origin" "-168 96 16" } { "targetname" "t358" "classname" "misc_teleporter_dest" "angle" "90" "spawnflags" "1792" "origin" "-128 656 16" } { "target" "t361" "classname" "misc_teleporter" "spawnflags" "1792" "origin" "-128 912 16" } { "angle" "180" "classname" "info_player_deathmatch" "origin" "352 784 -16" } { "origin" "-2968 1408 -320" "target" "t354" "classname" "misc_insane" "spawnflags" "4" "angle" "90" } { "origin" "-3116 1408 -320" "target" "t353" "classname" "misc_insane" "spawnflags" "4" "angle" "90" } { "origin" "-3236 1352 -320" "target" "t352" "classname" "misc_insane" "spawnflags" "4" "angle" "90" } { "origin" "-3236 1240 -320" "target" "t351" "classname" "misc_insane" "spawnflags" "4" "angle" "90" } { "origin" "-3236 1076 -320" "target" "t350" "classname" "misc_insane" "spawnflags" "4" "angle" "90" } { "origin" "-2800 1408 -320" "target" "t355" "angle" "90" "spawnflags" "4" "classname" "misc_insane" } { "origin" "-2752 1408 -336" "target" "t356" "targetname" "t355" "classname" "path_corner" "spawnflags" "2048" // b#7: 0 -> 2048 } { "origin" "-2908 1408 -336" "target" "t355" "targetname" "t354" "classname" "path_corner" "spawnflags" "2048" // b#7: 0 -> 2048 } { "origin" "-3072 1408 -336" "target" "t354" "targetname" "t353" "classname" "path_corner" "spawnflags" "2048" // b#7: 0 -> 2048 } { "origin" "-3236 1408 -336" "target" "t353" "targetname" "t352" "classname" "path_corner" "spawnflags" "2048" // b#7: 0 -> 2048 } { "origin" "-3236 1280 -336" "target" "t352" "targetname" "t351" "classname" "path_corner" "spawnflags" "2048" // b#7: 0 -> 2048 } { "origin" "-3236 1152 -336" "target" "t351" "targetname" "t350" "classname" "path_corner" "spawnflags" "2048" // b#7: 0 -> 2048 } { "targetname" "t357" "spawnflags" "2049" // b#7: 1 -> 2049 "origin" "-3236 988 -336" "target" "t350" "classname" "path_corner" } { "target" "t357" "origin" "-2648 1408 -336" "targetname" "t356" "spawnflags" "2048" // b#7: 0 -> 2048 "classname" "path_corner" } { "origin" "-2848 1408 -300" "classname" "light" "light" "200" "_color" "1.000000 0.012658 0.012658" } { "origin" "-2976 1408 -300" "classname" "light" "light" "200" "_color" "1.000000 0.012658 0.012658" } { "origin" "-3104 1408 -300" "classname" "light" "light" "200" "_color" "1.000000 0.012658 0.012658" } { "origin" "-3236 1408 -300" "classname" "light" "light" "200" "_color" "1.000000 0.012658 0.012658" } { "origin" "-3236 1280 -300" "classname" "light" "light" "200" "_color" "1.000000 0.012658 0.012658" } { "origin" "-3236 1152 -300" "classname" "light" "light" "200" "_color" "1.000000 0.012658 0.012658" } { "origin" "-3236 1020 -300" "classname" "light" "light" "200" "_color" "1.000000 0.012658 0.012658" } { "origin" "-2720 1408 -300" "_color" "1.000000 0.012658 0.012658" "light" "200" "classname" "light" } { "origin" "-3048 992 -408" "classname" "monster_parasite" "angle" "360" } { "origin" "-2864 1008 -408" "angle" "270" "classname" "monster_parasite" } { "origin" "-636 672 -8" "dmg" "40" "targetname" "t258" "classname" "target_explosion" "spawnflags" "2048" // b#7: added this } { "origin" "-640 352 8" "dmg" "40" "targetname" "t257" "classname" "target_explosion" "spawnflags" "2048" // b#7: added this } { "classname" "point_combat" "targetname" "t348" "origin" "-156 1352 8" } { "classname" "point_combat" "targetname" "t347" "origin" "-200 1352 8" } { "classname" "point_combat" "targetname" "t349" "origin" "-112 1352 8" } { "classname" "point_combat" "targetname" "t345" "origin" "36 936 0" } { "classname" "point_combat" "targetname" "t346" "origin" "92 936 0" } { "model" "*21" "wait" "2" "classname" "func_button" "angle" "360" "target" "t343" "pathtarget" "e6" } { "model" "*22" "wait" "2" "classname" "func_button" "angle" "180" "target" "t343" "pathtarget" "e5" } { "model" "*23" "spawnflags" "2048" "classname" "func_button" "angle" "270" "target" "t341" "pathtarget" "e4" } { "model" "*24" "spawnflags" "2048" "classname" "func_button" "angle" "90" "target" "t341" "pathtarget" "e3" } { "model" "*25" "classname" "func_button" "target" "t339" "pathtarget" "e1" "angle" "360" } { "model" "*26" "classname" "func_button" "angle" "180" "target" "t339" "pathtarget" "e2" } { "model" "*27" "speed" "150" "classname" "func_train" "targetname" "t344" "target" "e5" } { "wait" "-1" "origin" "-956 704 -480" "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "e4" } { "wait" "-1" "origin" "-956 704 -32" "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "e3" } { "spawnflags" "2048" "origin" "-868 780 -432" "classname" "trigger_elevator" "targetname" "t341" "target" "t342" } { "model" "*28" "spawnflags" "2048" "speed" "150" "classname" "func_train" "targetname" "t342" "target" "e4" } { "wait" "-1" "origin" "-2128 -140 -480" "classname" "path_corner" "targetname" "e1" } { "origin" "-2044 -52 -432" "classname" "trigger_elevator" "targetname" "t339" "target" "t340" } { "wait" "-1" "origin" "-2128 -140 -32" "classname" "path_corner" "targetname" "e2" } { "light" "120" "classname" "light" "origin" "-2048 144 24" } { "light" "120" "classname" "light" "origin" "-2004 224 24" } { "light" "120" "classname" "light" "origin" "-1904 224 24" } { "light" "120" "classname" "light" "origin" "-1820 224 24" } { "classname" "path_corner" "origin" "-1744 1048 -480" "wait" "-1" "targetname" "e5" } { "classname" "path_corner" "origin" "-1744 1048 -32" "wait" "-1" "targetname" "e6" } { "classname" "trigger_elevator" "origin" "-1668 1124 -432" "targetname" "t343" "target" "t344" } { "model" "*29" "classname" "func_train" "speed" "150" "targetname" "t340" "target" "e2" } { "noise" "world/klaxon1.wav" "spawnflags" "2050" // b#7: 2 -> 2050 "classname" "target_speaker" "targetname" "t190" "origin" "-2352 344 -404" } { "noise" "world/klaxon1.wav" "spawnflags" "2050" // b#7: 2 -> 2050 "classname" "target_speaker" "targetname" "t190" "origin" "-2412 668 -404" } { "noise" "world/klaxon1.wav" "spawnflags" "2050" // b#7: 2 -> 2050 "classname" "target_speaker" "targetname" "t190" "origin" "-1924 660 -404" } { "classname" "target_speaker" "spawnflags" "2050" // b#7: 2 -> 2050 "noise" "world/klaxon1.wav" "targetname" "t190" "origin" "-2408 1100 -404" } { "classname" "point_combat" "spawnflags" "1" "targetname" "t338" "origin" "128 824 16" } { "classname" "monster_chick" "angle" "90" "target" "t338" "spawnflags" "2" "targetname" "t190" "origin" "128 768 -7" // b#6: 32 -> -7 } { "classname" "point_combat" "spawnflags" "1" "targetname" "t337" "origin" "20 692 16" } { "classname" "monster_chick" "angle" "90" "target" "t337" "spawnflags" "2" "targetname" "t190" "origin" "20 644 -7" // b#6: 32 -> -7 } { "classname" "point_combat" "targetname" "t336" "origin" "-640 212 4" } { "classname" "trigger_relay" "spawnflags" "3840" // b#7: added this "targetname" "t198" "origin" "-1928 560 24" } { "classname" "trigger_relay" "spawnflags" "3840" // b#7: added this "target" "t190" "targetname" "t198" "origin" "-1924 536 100" } { "model" "*30" "spawnflags" "2048" "classname" "trigger_once" "target" "t190" // b#7: t198 -> t190 } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t334" "target" "t335" "origin" "-2428 468 -436" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "target" "t333" "targetname" "t335" "origin" "-2404 656 -436" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t333" "target" "t334" "origin" "-2436 836 -436" } { "classname" "target_explosion" "spawnflags" "2048" // b#7: added this "targetname" "t332" "origin" "-2428 616 -340" } { "model" "*31" "spawnflags" "2816" // b#7: 768 -> 2816 "classname" "trigger_once" "target" "t331" } { "model" "*32" "classname" "func_explosive" "targetname" "t331" "target" "t332" } { "angle" "90" "origin" "-2408 536 -272" "classname" "monster_parasite" "spawnflags" "768" // b#2: 0 -> 768 } { "classname" "monster_parasite" "spawnflags" "768" // b#2: 0 -> 768 "origin" "-2408 588 -272" "angle" "90" } { "light" "140" "classname" "light" "origin" "-832 1560 -456" } { "origin" "-832 1688 -410" "light" "120" "classname" "light" } { "model" "*33" "targetname" "t308" "classname" "func_explosive" "mass" "100" } { "classname" "func_group" } { "model" "*34" "classname" "func_wall" "spawnflags" "16" "_minlight" ".18" } { "model" "*35" "classname" "func_wall" "spawnflags" "16" "_minlight" ".18" } { "model" "*36" "spawnflags" "2048" "_minlight" ".18" "lip" "16" "wait" "-1" "angle" "90" "classname" "func_button" "target" "t42" } { "classname" "light" "light" "160" "origin" "-528 -480 -96" } { "classname" "path_corner" "targetname" "t324" "target" "t330" "origin" "-1788 176 -112" "spawnflags" "2049" // b#7: 1 -> 2049 } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this "delay" "1.5" "targetname" "t328" "target" "t329" "origin" "-1424 -684 116" } { "classname" "light" "light" "170" "origin" "-1440 -608 -20" } { "classname" "light" "light" "140" "origin" "-1224 -428 88" } { "model" "*37" "classname" "func_door" "angle" "-1" "wait" "-1" "speed" "50" "targetname" "t329" "_minlight" ".18" } { "model" "*38" "classname" "func_button" "angle" "360" "wait" "-1" "lip" "16" "_minlight" ".18" "spawnflags" "2048" "target" "t58" } { "model" "*39" "classname" "func_wall" "spawnflags" "16" } { "origin" "-1500 384 -64" "_color" "1.000000 0.015686 0.015686" "light" "160" "classname" "light" } { "origin" "-1332 384 -64" "_color" "1.000000 0.015686 0.015686" "light" "160" "classname" "light" } { "classname" "light" "light" "160" "_color" "1.000000 0.015686 0.015686" "origin" "-1500 264 -64" } { "classname" "misc_insane" "angle" "270" "spawnflags" "4" "target" "t327" "origin" "-1248 376 -96" } { "origin" "-1248 384 -112" "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t326" "target" "t327" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "origin" "-1248 240 -112" "targetname" "t327" "target" "t324" } { "origin" "-1652 292 -64" "_color" "1.000000 0.015686 0.015686" "light" "160" "classname" "light" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t325" "target" "t321" "origin" "-1648 288 -112" } { "spawnflags" "4" "classname" "misc_insane" "target" "t322" "origin" "-1496 328 -96" } { "classname" "misc_insane" "spawnflags" "4" "target" "t323" "origin" "-1428 384 -96" } { "classname" "misc_insane" "spawnflags" "4" "target" "t321" "origin" "-1548 288 -96" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "origin" "-1496 384 -112" "targetname" "t322" "target" "t323" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "origin" "-1496 288 -112" "targetname" "t321" "target" "t322" } { "classname" "path_corner" "origin" "-1792 288 -112" "spawnflags" "2048" // b#7: 0 -> 2048 "target" "t325" "targetname" "t330" } { "classname" "path_corner" "spawnflags" "2048" // b#7: 0 -> 2048 "origin" "-1332 384 -112" "targetname" "t323" "target" "t326" } { "classname" "misc_insane" "spawnflags" "4" "angle" "360" "origin" "-1680 288 -96" "target" "t325" } { "origin" "-1244 336 -64" "_color" "1.000000 0.015686 0.015686" "light" "160" "classname" "light" } { "classname" "light" "light" "160" "_color" "1.000000 0.015686 0.015686" "origin" "-1764 292 -64" } { "classname" "point_combat" "targetname" "t319" "origin" "-1108 536 0" } { "classname" "point_combat" "targetname" "t320" "origin" "-1196 536 0" } { "classname" "point_combat" "targetname" "t318" "origin" "-428 -480 36" "target" "t382" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t316" "target" "t317" "origin" "-660 -216 12" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t317" "target" "t316" "origin" "-228 -216 12" } { "classname" "misc_insane" "angle" "360" "target" "t316" "origin" "-688 -216 28" "deathtarget" "marduk" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t314" "target" "t304" "origin" "-2676 776 -416" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t304" "target" "t311" "origin" "-2356 776 -416" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t313" "target" "t314" "origin" "-2356 776 -416" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t311" "target" "t312" "origin" "-2356 1064 -416" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t312" "target" "t313" "origin" "-2356 1064 -416" } { "model" "*40" "classname" "func_explosive" "mass" "100" "targetname" "t184" } { "model" "*41" "classname" "trigger_once" "spawnflags" "2048" // b#7: added this "target" "t310" } { "spawnflags" "3" "angle" "180" "classname" "monster_brain" "origin" "-2108 1184 -428" "targetname" "t310" } { "spawnflags" "3" "angle" "180" "classname" "monster_brain" "origin" "-2108 1236 -428" "targetname" "t310" } { "classname" "monster_brain" "angle" "180" "spawnflags" "3" "origin" "-2032 1212 -428" "targetname" "t310" } { "target" "t375" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this "targetname" "t308" "delay" "1.2" "origin" "-2928 1020 -412" } { "model" "*42" "classname" "trigger_once" "spawnflags" "2308" // b#7: 260 -> 2308 "targetname" "t267" "target" "t308" } { "classname" "monster_medic" "spawnflags" "257" // b#1: 0 -> 256, b#8: 256 -> 257 "angle" "315" "origin" "-3044 1212 -488" "target" "t379" "targetname" "t375" // b#8: make a trigger anger him } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t303" "target" "t304" "origin" "-2664 776 -416" } { "classname" "misc_insane" "spawnflags" "4" "angle" "360" "target" "t303" "origin" "-2704 776 -400" "deathtarget" "marduk" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t301" "target" "t302" "origin" "-2408 1176 -448" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t302" "target" "t301" "origin" "-2408 344 -448" } { "classname" "misc_insane" "angle" "270" "spawnflags" "32" "target" "t301" "origin" "-2408 1208 -432" "deathtarget" "marduk" } { "classname" "monster_brain" "spawnflags" "1" "angle" "360" "origin" "-2296 344 -432" } { "angle" "360" "classname" "monster_parasite" "spawnflags" "2" "targetname" "t299" "origin" "-1504 528 28" "target" "t319" } { "classname" "monster_parasite" "angle" "360" "spawnflags" "2" "targetname" "t299" "origin" "-1568 528 28" "target" "t320" } { "origin" "-1616 596 16" "spawnflags" "1" "classname" "point_combat" "targetname" "t298" } { "origin" "-1152 596 20" "spawnflags" "1" "classname" "monster_medic" "target" "t298" "angle" "270" } { "model" "*43" "classname" "trigger_once" "spawnflags" "2048" // b#7: added this "target" "t299" } { "model" "*44" "target" "t238" "spawnflags" "2052" // b#7: added this "classname" "trigger_once" "targetname" "t328" } { "classname" "func_group" } { "origin" "-1424 -448 48" // b#6: 72 -> 48 "targetname" "t211" "classname" "monster_brain" "angle" "270" "spawnflags" "259" } { "origin" "-1240 -592 16" // b#6: 32 -> 16 "targetname" "t211" "classname" "monster_brain" "angle" "270" "spawnflags" "3" } { "origin" "-1296 -656 16" // b#6: 48 -> 16 "targetname" "t211" "spawnflags" "3" "angle" "360" "classname" "monster_brain" } { "model" "*45" "targetname" "t297" "classname" "func_explosive" } { "targetname" "t328" "target" "t297" "origin" "-544 -376 56" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this } { "origin" "-560 -416 56" "target" "t296" "delay" ".5" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this "targetname" "t297" } { "origin" "-1028 -376 16" // b#6: 32 -> 16 "targetname" "t67" "spawnflags" "3" "angle" "90" "classname" "monster_parasite" } { "origin" "-972 -408 16" // b#6: 24 -> 16 "targetname" "t67" "spawnflags" "3" "angle" "90" "classname" "monster_parasite" } { "angle" "45" "origin" "-528 -480 -48" "classname" "monster_medic" "target" "t318" "targetname" "t297" } { "origin" "-352 40 8" "target" "t292" "delay" "3.3" "targetname" "t289" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this } { "origin" "-352 64 24" "target" "t291" "delay" "3.2" "targetname" "t289" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this } { "origin" "-352 96 24" "target" "t290" "delay" "3" "targetname" "t289" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this } { "model" "*46" "spawnflags" "2048" "target" "t289" "classname" "trigger_once" } { "targetname" "t291" "origin" "0 -480 -64" "spawnflags" "2" "angle" "135" "classname" "monster_chick" } { "origin" "0 -480 -88" "classname" "light" "light" "120" } { "origin" "16 -144 -88" "light" "120" "classname" "light" } { "model" "*47" "targetname" "t292" "_minlight" ".18" "target" "t287" "classname" "func_train" } { "origin" "-48 -528 -96" "target" "t288" "targetname" "t287" "classname" "path_corner" } { "origin" "-48 -528 16" "targetname" "t288" "classname" "path_corner" } { "model" "*48" "targetname" "t290" "mass" "50" "classname" "func_explosive" } { "model" "*49" "targetname" "t290" "classname" "func_explosive" "mass" "50" } { "origin" "-32 -192 -104" "target" "t286" "targetname" "t285" "classname" "path_corner" } { "origin" "-32 -192 16" "targetname" "t286" "classname" "path_corner" } { "model" "*50" "targetname" "t292" "target" "t285" "speed" "120" "_minlight" ".18" "classname" "func_train" } { "item" "ammo_rockets" "targetname" "t291" "origin" "16 -144 -95" // b#6: -56 -> -95 "angle" "225" "spawnflags" "2" "classname" "monster_chick" } { "origin" "-504 64 -8" // b#6: 16 -> -8 "target" "t284" "classname" "monster_chick" "spawnflags" "2" "angle" "135" "targetname" "t315" } { "origin" "-616 120 0" "targetname" "t284" "classname" "point_combat" "spawnflags" "1" } { "origin" "-664 152 0" "targetname" "t283" "spawnflags" "1" "classname" "point_combat" } { "origin" "-764 64 -8" // b#6: 16 -> -8 "target" "t283" "angle" "45" "spawnflags" "2" "classname" "monster_chick" "targetname" "t315" } { "model" "*51" "classname" "trigger_once" "spawnflags" "2048" // b#7: added this "target" "t315" } { "item" "ammo_rockets" "origin" "-732 816 24" "classname" "monster_chick" "angle" "360" "spawnflags" "257" } { "origin" "-640 676 24" "spawnflags" "257" "angle" "45" "classname" "monster_chick" } { "origin" "-296 1368 0" "target" "t280" "targetname" "t281" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "target" "t281" "targetname" "t280" "origin" "80 1368 0" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "target" "t280" "origin" "60 1364 16" "angle" "180" "spawnflags" "4" "classname" "misc_insane" "deathtarget" "marduk" } { "model" "*52" "target" "t221" "spawnflags" "2048" // b#7: 0 -> 2048 "classname" "trigger_once" } { "origin" "92 1188 20" "targetname" "t255" "angle" "270" "classname" "monster_parasite" "target" "t346" } { "origin" "36 1188 20" "targetname" "t255" "angle" "270" "classname" "monster_parasite" "target" "t345" } { "origin" "64 1296 20" "targetname" "t255" "angle" "270" "spawnflags" "768" "classname" "monster_parasite" } { "origin" "388 816 36" "target" "t279" "targetname" "t278" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this "message" "Access granted to\nResearch Lab." // b#9: moved here from button "delay" "0.6" // b#9: added this } { "origin" "24 -344 88" "spawnflags" "20" "classname" "misc_insane" "deathtarget" "marduk" } { "model" "*53" "classname" "func_button" "angle" "270" "wait" "-1" "lip" "16" "_minlight" ".18" "target" "t64" "spawnflags" "2048" } { "model" "*54" "classname" "func_wall" "spawnflags" "16" } { "model" "*55" "target" "t62" "_minlight" ".18" "lip" "16" "wait" "-1" "angle" "360" "classname" "func_button" "spawnflags" "2048" } { "origin" "-1692 692 -320" "targetname" "t117" "classname" "target_speaker" "spawnflags" "2048" // b#7: added this "noise" "world/brkglas.wav" } { "origin" "-2032 696 -320" "targetname" "t115" "noise" "world/brkglas.wav" "classname" "target_speaker" "spawnflags" "2048" // b#7: added this } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "-440 -216 128" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "-224 -208 128" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "-224 -40 128" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "-344 64 128" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "-536 64 128" } { "style" "32" "origin" "-304 -488 184" "targetname" "t64" "classname" "light" "spawnflags" "2049" // b#7: 1 -> 2049 "light" "300" "_color" "1.000000 0.016949 0.016949" } { "style" "32" "origin" "-304 -552 184" "targetname" "t64" "_color" "1.000000 0.016949 0.016949" "light" "300" "spawnflags" "2049" // b#7: 1 -> 2049 "classname" "light" } { "style" "33" "origin" "8 -344 184" "targetname" "t62" "classname" "light" "spawnflags" "2049" // b#7: 1 -> 2049 "light" "300" "_color" "1.000000 0.016949 0.016949" } { "style" "33" "origin" "72 -344 184" "targetname" "t62" "_color" "1.000000 0.016949 0.016949" "light" "300" "spawnflags" "2049" // b#7: 1 -> 2049 "classname" "light" } { "origin" "-1568 1608 -392" "classname" "target_speaker" "noise" "world/amb23.wav" "spawnflags" "1" // b#4: added this } { "origin" "-1816 1616 -392" "classname" "target_speaker" "noise" "world/amb23.wav" "spawnflags" "1" // b#4: added this } { "origin" "-1296 1608 -392" "noise" "world/amb23.wav" "classname" "target_speaker" "spawnflags" "1" // b#4: added this } { "origin" "-2768 1288 -312" "noise" "world/comp_hum2.wav" "spawnflags" "1" "classname" "target_speaker" } { "classname" "target_goal" "spawnflags" "2048" "targetname" "t267" "origin" "-2824 1240 -368" } { "classname" "key_blue_key" "origin" "-1440 -608 -24" } { "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this "targetname" "t58" "delay" "5.5" "killtarget" "t276" "origin" "-1380 -748 80" } { "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this "targetname" "t58" "delay" "5.5" "killtarget" "t277" "origin" "-1384 -728 80" } { "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this "targetname" "t58" "delay" ".5" "target" "t277" "origin" "-1408 -704 80" } { "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this "targetname" "t58" "delay" "5" "target" "t276" "origin" "-1416 -680 80" } { "model" "*56" "classname" "trigger_hurt" "spawnflags" "2051" // b#7: 3 -> 2051 "dmg" "500" "targetname" "t276" } { "style" "34" "_color" "1.000000 0.003922 0.003922" "light" "300" "classname" "light" "spawnflags" "1" "targetname" "t277" "origin" "-1312 -268 184" } { "style" "34" "classname" "light" "light" "300" "_color" "1.000000 0.003922 0.003922" "spawnflags" "1" "targetname" "t277" "origin" "-1384 -268 184" } { "style" "34" "_color" "1.000000 0.003922 0.003922" "light" "300" "classname" "light" "spawnflags" "1" "targetname" "t277" "origin" "-1384 -368 184" } { "style" "34" "classname" "light" "light" "300" "_color" "1.000000 0.003922 0.003922" "spawnflags" "1" "targetname" "t277" "origin" "-1312 -368 184" } { "classname" "misc_insane" "spawnflags" "20" "angle" "270" "origin" "-1320 -328 88" "deathtarget" "marduk" } { "classname" "trigger_always" "spawnflags" "1792" "target" "t275" "origin" "-1796 204 92" } { "classname" "trigger_key" "targetname" "t274" "target" "t275" "item" "key_blue_key" "spawnflags" "2048" "origin" "-1804 248 92" } { "model" "*57" "classname" "trigger_multiple" "target" "t274" "spawnflags" "2048" } { "origin" "-3068 1228 -424" "target" "t273" "targetname" "t272" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "-2840 1224 -424" "target" "t272" "targetname" "t271" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "-2848 1304 -424" "target" "t271" "targetname" "t270" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "-3108 1304 -424" "target" "t270" "targetname" "t269" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "-3104 1064 -424" "target" "t269" "targetname" "t268" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "-2968 1108 -424" "target" "t268" "targetname" "t273" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "-3104 1024 -408" "target" "t268" "spawnflags" "4" "classname" "misc_insane" "deathtarget" "marduk" } { "model" "*58" "spawnflags" "2048" "target" "t267" "wait" "-1" "speed" "130" "message" "Maintenence bridge\nactivated." "angle" "360" "classname" "func_button" } { "origin" "-1500 416 108" "classname" "target_speaker" "noise" "world/comp_hum2.wav" "spawnflags" "1" } { "spawnflags" "1" "noise" "world/comp_hum2.wav" "classname" "target_speaker" "origin" "-1500 516 108" } { "origin" "-1592 516 108" "classname" "target_speaker" "noise" "world/comp_hum2.wav" "spawnflags" "1" } { "spawnflags" "1" "noise" "world/comp_hum2.wav" "classname" "target_speaker" "origin" "-1592 416 108" } { "origin" "-1592 416 156" "classname" "target_speaker" "noise" "world/comp_hum3.wav" "spawnflags" "1" } { "spawnflags" "1" "noise" "world/comp_hum3.wav" "classname" "target_speaker" "origin" "-1592 516 156" } { "origin" "-1500 516 156" "classname" "target_speaker" "noise" "world/comp_hum3.wav" "spawnflags" "1" } { "origin" "-1412 160 76" "classname" "target_speaker" "noise" "world/comp_hum2.wav" "spawnflags" "1" } { "origin" "-3112 1152 -384" "spawnflags" "1" "noise" "world/amb23.wav" "classname" "target_speaker" } { "origin" "-3012 1060 -384" "spawnflags" "1" "noise" "world/amb23.wav" "classname" "target_speaker" } { "origin" "-2916 964 -384" "spawnflags" "1" "noise" "world/amb23.wav" "classname" "target_speaker" } { "origin" "-2752 828 -396" "classname" "light" "light" "100" } { "origin" "-2688 780 -396" "classname" "light" "light" "100" } { "origin" "-2792 1164 -332" "classname" "light" "light" "140" } { "origin" "-2808 1208 -264" "classname" "light" "light" "130" } { "origin" "-3008 1096 -264" "classname" "light" "light" "130" } { "origin" "-2824 1008 -264" "classname" "light" "light" "130" } { "origin" "-2960 848 -264" "classname" "light" "light" "130" } { "origin" "-2832 720 -264" "classname" "light" "light" "130" } { "origin" "-2696 896 -264" "classname" "light" "light" "130" } { "origin" "-2856 1304 -312" "noise" "world/comp_hum2.wav" "spawnflags" "1" "classname" "target_speaker" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "-2856 1192 -312" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum1.wav" "origin" "-2856 1304 -376" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "-2768 1200 -312" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "-2812 732 -312" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" "origin" "-2572 780 -280" } { "model" "*59" "target" "t184" "classname" "trigger_multiple" "spawnflags" "1792" } { "light" "150" "classname" "light" "origin" "-1440 1896 -432" } { "origin" "-1356 -324 104" "target" "t266" "random" "6" "wait" "7" "classname" "func_timer" "spawnflags" "1793" } { "origin" "-1356 -344 104" "targetname" "t266" "classname" "target_speaker" "noise" "insane/insane7.wav" "spawnflags" "1792" // added this } { "origin" "-196 60 104" "classname" "func_timer" "spawnflags" "1793" // b#5: 1 -> 1793 "target" "_fix1" // b#5: added this "random" "5.5" // "wait" "8" // } { "origin" "-196 40 104" "targetname" "_fix1" // b#5: added this "classname" "target_speaker" "noise" "insane/insane7.wav" "spawnflags" "1792" // b#5: added this } { "origin" "24 -308 104" "wait" "10" "random" "8" "target" "t261" "spawnflags" "1793" "classname" "func_timer" } { "origin" "24 -328 104" "targetname" "t261" "classname" "target_speaker" "noise" "insane/insane5.wav" "spawnflags" "1792" // b#5: added this } { "origin" "-4 -308 104" "random" "7.5" "wait" "9" "target" "t262" "spawnflags" "1793" "classname" "func_timer" } { "origin" "-4 -328 104" "targetname" "t262" "noise" "insane/insane7.wav" "classname" "target_speaker" "spawnflags" "1792" // b#5: added this } { "origin" "-320 -476 104" "targetname" "t264" "classname" "target_speaker" "noise" "insane/insane7.wav" "spawnflags" "1792" // b#5: added this } { "origin" "-292 -476 104" "targetname" "t263" "noise" "insane/insane5.wav" "classname" "target_speaker" "spawnflags" "1792" // b#5: added this } { "origin" "-320 -456 104" "target" "t264" "wait" "11" "random" "8" "classname" "func_timer" "spawnflags" "1793" } { "origin" "-168 40 104" "classname" "target_speaker" "spawnflags" "3840" // b#7: added this "noise" "insane/insane5.wav" } { "origin" "-1336 -344 104" "targetname" "t265" "noise" "insane/insane5.wav" "classname" "target_speaker" "spawnflags" "1792" // b#5: added this } { "origin" "-292 -456 104" "target" "t263" "wait" "9.5" "random" "6" "classname" "func_timer" "spawnflags" "1793" } { "origin" "-1336 -324 104" "target" "t265" "random" "6.5" "wait" "9.5" "spawnflags" "1793" "classname" "func_timer" } { "model" "*60" "spawnflags" "1792" "classname" "func_wall" } { "model" "*61" "classname" "func_wall" "spawnflags" "1792" } { "model" "*62" "spawnflags" "1792" "classname" "func_wall" } { "origin" "-1496 1672 -400" "target" "t32" "spawnflags" "3840" // b#7: 1792 -> 3840 "classname" "trigger_always" } { "model" "*63" "spawnflags" "2048" "target" "t201" "classname" "trigger_once" } { "origin" "-640 624 72" "spawnflags" "2050" // b#7: 2 -> 2050 "noise" "world/klaxon1.wav" "classname" "target_speaker" "targetname" "t190" } { "origin" "-152 1336 72" "spawnflags" "2050" // b#7: 2 -> 2050 "noise" "world/klaxon1.wav" "classname" "target_speaker" "targetname" "t190" } { "origin" "-792 64 72" "spawnflags" "2050" // b#7: 2 -> 2050 "noise" "world/klaxon1.wav" "classname" "target_speaker" "targetname" "t190" } { "origin" "-1128 88 72" "spawnflags" "2050" // b#7: 2 -> 2050 "noise" "world/klaxon1.wav" "classname" "target_speaker" "targetname" "t190" } { "origin" "-1128 376 72" "spawnflags" "2050" // b#7: 2 -> 2050 "noise" "world/klaxon1.wav" "classname" "target_speaker" "targetname" "t190" } { "origin" "-1312 576 72" "spawnflags" "2050" // b#7: 2 -> 2050 "noise" "world/klaxon1.wav" "classname" "target_speaker" "targetname" "t190" } { "origin" "-1600 536 72" "spawnflags" "2050" // b#7: 2 -> 2050 "noise" "world/klaxon1.wav" "classname" "target_speaker" "targetname" "t190" } { "classname" "target_speaker" "noise" "world/klaxon1.wav" "spawnflags" "2050" // b#7: 2 -> 2050 "origin" "-1792 536 72" "targetname" "t190" } { "origin" "-1600 344 72" "spawnflags" "2050" // b#7: 2 -> 2050 "noise" "world/klaxon1.wav" "classname" "target_speaker" "targetname" "t190" } { "spawnflags" "2048" "classname" "target_help" "targetname" "t190" "message" "Return to Hangar.\nUse Commander's head to\ngain further access." "origin" "-1896 460 40" } { "light" "140" "classname" "light" "origin" "-640 352 -112" } { "classname" "light" "light" "160" "origin" "-640 672 -112" } { "style" "1" "classname" "func_areaportal" "targetname" "t259" } { "classname" "target_speaker" "noise" "world/klaxon1.wav" "spawnflags" "2050" // b#7: 2 -> 2050 "targetname" "t190" "origin" "-1880 624 72" } { "style" "2" "classname" "func_areaportal" "targetname" "t258" } { "style" "3" "classname" "func_areaportal" "targetname" "t257" } { "model" "*64" "classname" "trigger_once" "spawnflags" "2052" "target" "t256" "targetname" "t190" } { "spawnflags" "1" "angle" "90" "classname" "monster_chick" "origin" "-640 352 -84" "item" "ammo_rockets" } { "classname" "monster_chick" "angle" "270" "spawnflags" "1" "origin" "-640 672 -76" } { "model" "*65" "classname" "func_door" "angle" "-1" "spawnflags" "8" "wait" "-1" "lip" "-104" "targetname" "t256" "speed" "200" } { "model" "*66" "classname" "func_explosive" "dmg" "5" "targetname" "t256" "target" "t258" } { "model" "*67" "classname" "func_door" "angle" "-1" "spawnflags" "8" "lip" "-104" "wait" "-1" "targetname" "t256" "speed" "200" } { "model" "*68" "classname" "func_explosive" "targetname" "t256" "dmg" "5" "target" "t257" } { "style" "4" "classname" "func_areaportal" "targetname" "t255" } { "style" "5" "classname" "func_areaportal" "targetname" "t57" } { "origin" "-112 860 64" "wait" "10" "random" "6" "spawnflags" "1" "classname" "func_timer" "target" "t247" } { "origin" "-56 840 64" "noise" "insane/insane5.wav" "classname" "target_speaker" "targetname" "t247" } { "spawnflags" "1" "wait" "8" "random" "5" "classname" "func_timer" "target" "t246" "origin" "-116 720 64" } { "classname" "func_timer" "wait" "8.5" "random" "5" "spawnflags" "1" "target" "t245" "origin" "-144 836 64" } { "noise" "insane/insane7.wav" "classname" "target_speaker" "targetname" "t245" "origin" "-72 784 64" } { "noise" "insane/insane7.wav" "classname" "target_speaker" "targetname" "t246" "origin" "-56 704 64" } { "style" "6" "classname" "func_areaportal" "targetname" "t243" } { "model" "*69" "classname" "func_door" "angle" "-1" "speed" "120" "_minlight" ".2" "dmg" "5" "target" "t243" "spawnflags" "2048" } { "light" "100" "classname" "light" "origin" "-28 1344 24" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t241" "target" "t242" "origin" "-96 904 0" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t242" "target" "t241" "origin" "-96 760 0" } { "classname" "misc_insane" "angle" "270" "spawnflags" "32" "target" "t241" "origin" "-96 932 16" } { "spawnflags" "2048" "classname" "item_armor_combat" "origin" "-128 1516 16" } { "spawnflags" "2048" "classname" "ammo_bullets" "origin" "-2700 616 -412" } { "spawnflags" "2048" "classname" "ammo_shells" "origin" "-2648 692 -412" } { "classname" "target_speaker" "spawnflags" "2048" // b#7: added this "noise" "world/lite_out.wav" "targetname" "t32" "origin" "-1568 1640 -400" "attenuation" "-1" } { "origin" "-640 72 -112" "light" "200" "classname" "light" } { "model" "*70" "target" "t240" "classname" "trigger_once" "spawnflags" "2048" // b#7: added this } { "origin" "-640 64 -88" "angle" "90" "classname" "monster_medic" "target" "t336" } { "model" "*71" "targetname" "t240" "classname" "func_explosive" } { "model" "*72" "targetname" "t240" "spawnflags" "8" "lip" "-104" "angle" "-1" "classname" "func_door" "wait" "-1" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "angle" "360" "classname" "monster_parasite" "origin" "-752 76 208" } { "classname" "monster_parasite" "angle" "360" "origin" "-792 52 208" } { "model" "*73" "classname" "func_explosive" "mass" "75" "dmg" "5" "targetname" "t238" } { "spawnflags" "2048" "classname" "item_health" "origin" "-176 1516 8" } { "spawnflags" "2048" "classname" "item_health" "origin" "-80 1516 8" } { "classname" "monster_parasite" "angle" "270" "spawnflags" "0" "origin" "-128 1480 16" "targetname" "t221" "target" "t348" } { "spawnflags" "0" "classname" "monster_parasite" "angle" "270" "origin" "-176 1480 16" "targetname" "t221" "target" "t347" } { "classname" "monster_parasite" "angle" "270" "origin" "-80 1480 16" "targetname" "t221" "target" "t349" "spawnflags" "768" } { "model" "*74" "classname" "func_explosive" "mass" "50" "dmg" "20" "targetname" "t221" } { "spawnflags" "0" "classname" "item_health_large" "origin" "392 896 8" } { "spawnflags" "0" "classname" "item_health_large" "origin" "352 896 8" } { "spawnflags" "2048" "origin" "-1910 546 28" "targetname" "t190" "classname" "target_goal" } { "message" "You have found a secret." "spawnflags" "2048" "classname" "target_secret" "targetname" "t234" "origin" "-1724 1380 -448" } { "origin" "-800 1656 -456" "spawnflags" "1" "noise" "world/amb23.wav" "classname" "target_speaker" "volume" "1" "attenuation" "3" } { "spawnflags" "1" "noise" "world/comp_hum3.wav" "classname" "target_speaker" "origin" "-1500 416 156" } { "origin" "-1372 -304 200" "classname" "target_speaker" "noise" "world/amb10.wav" "spawnflags" "1" } { "origin" "-304 -528 200" "classname" "target_speaker" "noise" "world/amb10.wav" "spawnflags" "1" } { "origin" "56 -344 200" "classname" "target_speaker" "noise" "world/amb10.wav" "spawnflags" "1" } { "origin" "-1316 -304 200" "spawnflags" "1" "noise" "world/amb10.wav" "classname" "target_speaker" } { "spawnflags" "1" "noise" "world/comp_hum2.wav" "classname" "target_speaker" "origin" "-216 -504 92" "volume" ".7" } { "spawnflags" "1" "noise" "world/comp_hum2.wav" "classname" "target_speaker" "origin" "20 -256 92" "volume" ".7" } { "spawnflags" "1" "noise" "world/comp_hum1.wav" "classname" "target_speaker" "origin" "20 -256 104" } { "model" "*75" "spawnflags" "2048" "classname" "trigger_once" "targetname" "t57" "target" "t233" } { "origin" "-2572 556 -280" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum2.wav" } { "origin" "-2572 552 -280" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum1.wav" } { "origin" "-2572 776 -280" "classname" "target_speaker" "spawnflags" "1" "noise" "world/comp_hum1.wav" } { "origin" "-2856 1192 -376" "noise" "world/comp_hum1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1664 992 -388" "noise" "world/force1.wav" "targetname" "for1" "spawnflags" "2049" "classname" "target_speaker" } { "origin" "-748 784 96" "targetname" "for1" "spawnflags" "2049" "noise" "world/force1.wav" "classname" "target_speaker" } { "classname" "target_speaker" "origin" "-546 1704 -320" "noise" "world/amb10.wav" "spawnflags" "1" } { "classname" "target_speaker" "origin" "-650 1704 -320" "noise" "world/amb10.wav" "spawnflags" "1" } { "origin" "-468 812 -412" "noise" "world/amb22.wav" "spawnflags" "1" "volume" ".7" "classname" "target_speaker" } { "origin" "-464 984 -396" "noise" "world/amb22.wav" "spawnflags" "1" "volume" ".7" "classname" "target_speaker" } { "origin" "-464 1176 -408" "noise" "world/amb15.wav" "spawnflags" "1" "volume" ".5" "classname" "target_speaker" } { "origin" "-464 1392 -456" "noise" "world/amb22.wav" "spawnflags" "1" "volume" ".7" "classname" "target_speaker" } { "origin" "-1248 1496 -456" "noise" "world/amb15.wav" "spawnflags" "1" "volume" ".6" "classname" "target_speaker" } { "origin" "-2004 672 100" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" } { "origin" "-1816 844 140" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" } { "origin" "-1664 948 140" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" } { "origin" "-2036 372 -408" "noise" "world/amb22.wav" "spawnflags" "1" "volume" "1" "classname" "target_speaker" } { "origin" "-2268 380 -408" "noise" "world/amb22.wav" "spawnflags" "1" "volume" "1" "classname" "target_speaker" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "-2048 120 160" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "-1908 224 160" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "-1800 224 196" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "-1424 344 72" } { "origin" "-1000 -552 120" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" } { "origin" "-1000 -324 120" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" } { "origin" "-844 -216 120" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" } { "origin" "-696 -216 44" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" "origin" "-1000 -684 44" } { "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" "origin" "-304 -408 188" } { "volume" ".5" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" "origin" "-1828 1688 -372" } { "volume" ".5" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" "origin" "-1564 1692 -372" } { "volume" ".5" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" "origin" "-1312 1688 -372" } { "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" "origin" "-1344 -384 72" } { "origin" "-1472 168 76" "classname" "target_speaker" "noise" "world/amb10.wav" "spawnflags" "1" } { "origin" "-1408 292 92" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" } { "origin" "-1152 572 52" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" } { "origin" "-1152 328 192" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" } { "origin" "-948 64 192" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" } { "origin" "-756 64 128" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" } { "origin" "-584 -216 128" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" } { "origin" "-640 556 144" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" } { "origin" "-624 796 144" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" } { "spawnflags" "2048" "origin" "-1424 1928 -344" "killtarget" "tr2" "target" "t31" "targetname" "t229" "classname" "trigger_relay" } { "origin" "-1712 1924 -328" "target" "t31" "killtarget" "tr1" "targetname" "t228" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this } { "spawnflags" "1" "targetname" "tr2" "origin" "-1668 1812 -392" "noise" "world/amb4.wav" "classname" "target_speaker" } { "spawnflags" "1" "targetname" "tr1" "origin" "-1624 1712 -392" "noise" "world/amb10.wav" "classname" "target_speaker" } { "spawnflags" "1" "targetname" "tr2" "origin" "-1564 1684 -392" "noise" "world/amb4.wav" "classname" "target_speaker" } { "spawnflags" "1" "targetname" "tr1" "origin" "-1512 1712 -392" "noise" "world/amb10.wav" "classname" "target_speaker" } { "spawnflags" "1" "targetname" "tr2" "origin" "-1464 1812 -392" "classname" "target_speaker" "noise" "world/amb4.wav" } { "spawnflags" "1" "targetname" "tr2" "origin" "-1724 1812 -392" "classname" "target_speaker" "noise" "world/amb4.wav" } { "spawnflags" "1" "targetname" "tr1" "origin" "-1776 1712 -392" "noise" "world/amb10.wav" "classname" "target_speaker" } { "spawnflags" "1" "targetname" "tr2" "origin" "-1828 1684 -392" "noise" "world/amb4.wav" "classname" "target_speaker" } { "spawnflags" "1" "origin" "-1888 1712 -392" "targetname" "tr1" "noise" "world/amb10.wav" "classname" "target_speaker" } { "spawnflags" "1" "targetname" "tr2" "origin" "-1932 1812 -392" "noise" "world/amb4.wav" "classname" "target_speaker" } { "spawnflags" "1" "targetname" "tr1" "origin" "-1260 1720 -392" "classname" "target_speaker" "noise" "world/amb10.wav" } { "spawnflags" "1" "targetname" "tr2" "origin" "-1312 1692 -392" "classname" "target_speaker" "noise" "world/amb4.wav" } { "spawnflags" "1" "targetname" "tr1" "origin" "-1372 1720 -392" "classname" "target_speaker" "noise" "world/amb10.wav" } { "spawnflags" "1" "targetname" "tr2" "origin" "-1416 1820 -392" "classname" "target_speaker" "noise" "world/amb4.wav" } { "spawnflags" "1" "targetname" "tr2" "origin" "-1212 1820 -392" "noise" "world/amb4.wav" "classname" "target_speaker" } { "classname" "target_speaker" "volume" "1" "spawnflags" "1" "noise" "world/amb22.wav" "origin" "-2228 1216 -408" } { "classname" "target_speaker" "volume" "1" "spawnflags" "1" "noise" "world/amb22.wav" "origin" "-2408 1040 -408" } { "classname" "target_speaker" "volume" "1" "spawnflags" "1" "noise" "world/amb22.wav" "origin" "-2040 132 -408" } { "classname" "target_speaker" "volume" "1" "spawnflags" "1" "noise" "world/amb22.wav" "origin" "-2408 568 -408" } { "classname" "target_speaker" "volume" "1" "spawnflags" "1" "noise" "world/amb22.wav" "origin" "-2404 748 -408" } { "classname" "target_speaker" "volume" "1" "spawnflags" "1" "noise" "world/amb22.wav" "origin" "-2260 664 -408" } { "classname" "target_speaker" "volume" ".7" "spawnflags" "1" "noise" "world/amb22.wav" "origin" "-2104 664 -408" } { "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" "origin" "-2988 1260 -384" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "-380 784 144" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "-320 968 144" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "-320 1160 132" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "-284 1344 132" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "-72 1344 132" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "64 1236 132" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "64 1040 8" } { "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "origin" "64 896 144" } { "spawnflags" "1" "noise" "world/comp_hum1.wav" "classname" "target_speaker" "origin" "336 836 8" } { "spawnflags" "1" "noise" "world/comp_hum2.wav" "classname" "target_speaker" "origin" "404 836 8" } { "spawnflags" "1" "noise" "world/comp_hum2.wav" "classname" "target_speaker" "origin" "404 732 8" } { "origin" "336 732 8" "classname" "target_speaker" "noise" "world/comp_hum1.wav" "spawnflags" "1" } { "origin" "408 784 52" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" } { "origin" "-368 -504 116" "targetname" "t65" "spawnflags" "2050" // b#7: 2 -> 2050 "noise" "world/l_hum1.wav" "attenuation" "3" "volume" "1" "classname" "target_speaker" } { "origin" "24 -408 104" "targetname" "t63" "volume" "1" "attenuation" "3" "spawnflags" "2050" // b#7: 2 -> 2050 "noise" "world/l_hum1.wav" "classname" "target_speaker" } { "targetname" "t25" "volume" ".8" "origin" "-1388 -332 100" "attenuation" "2" "spawnflags" "2048" // b#7: 0 -> 2048 "noise" "world/spark1.wav" "classname" "target_speaker" } { "origin" "-1388 -332 100" "spawnflags" "1" "noise" "world/turbine1.wav" "classname" "target_speaker" } { "origin" "-1484 -544 144" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" } { "origin" "-1484 -620 144" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" } { "origin" "-1440 -744 88" "spawnflags" "1" "noise" "world/comp_hum2.wav" "classname" "target_speaker" } { "origin" "-1352 -444 188" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" } { "origin" "-1484 -472 144" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" } { "origin" "-216 -504 104" "classname" "target_speaker" "noise" "world/comp_hum1.wav" "spawnflags" "1" } { "volume" ".7" "origin" "-1440 -744 92" "classname" "target_speaker" "noise" "world/comp_hum1.wav" "spawnflags" "1" } { "origin" "-64 -348 188" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" } { "origin" "-2096 424 128" "targetname" "mac2" "spawnflags" "2048" // b#7: 0 -> 2048 "classname" "target_speaker" "volume" ".8" "attenuation" "2" "noise" "world/turbine1.wav" } { "origin" "-1880 424 72" "targetname" "mac1" "noise" "world/turbine1.wav" "attenuation" "2" "volume" ".8" "classname" "target_speaker" "spawnflags" "2048" // b#7: added this } { "origin" "-2144 424 144" "targetname" "mac2" "volume" ".8" "attenuation" "2" "noise" "world/spark2.wav" "classname" "target_speaker" "spawnflags" "2048" // b#7: added this } { "volume" ".2" "origin" "-592 1648 -372" "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" } { "origin" "-1424 528 72" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" } { "origin" "-2448 664 -384" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" } { "origin" "-1704 664 -408" "noise" "world/amb22.wav" "spawnflags" "1" "volume" ".7" "classname" "target_speaker" } { "origin" "-2032 1340 -408" "noise" "world/amb22.wav" "spawnflags" "1" "volume" "1" "classname" "target_speaker" } { "origin" "-1568 1552 -456" "noise" "world/amb15.wav" "spawnflags" "1" "volume" ".6" "classname" "target_speaker" } { "classname" "target_speaker" "volume" ".5" "spawnflags" "1" "noise" "world/amb22.wav" "origin" "-1664 960 -408" } { "classname" "target_speaker" "volume" ".7" "spawnflags" "1" "noise" "world/amb22.wav" "origin" "-664 784 -412" } { "classname" "target_speaker" "volume" ".7" "spawnflags" "1" "noise" "world/amb22.wav" "origin" "-1248 1312 -456" } { "classname" "target_speaker" "volume" ".7" "spawnflags" "1" "noise" "world/amb22.wav" "origin" "-1248 1184 -456" } { "classname" "target_speaker" "volume" ".7" "spawnflags" "1" "noise" "world/amb22.wav" "origin" "-1112 1088 -456" } { "classname" "target_speaker" "volume" ".7" "spawnflags" "1" "noise" "world/amb22.wav" "origin" "-928 1088 -456" } { "classname" "target_speaker" "volume" ".7" "spawnflags" "1" // b#4: 2048 -> 1 "noise" "world/amb22.wav" "origin" "-832 1232 -456" } { "classname" "target_speaker" "volume" "1" "spawnflags" "1" "noise" "world/amb15.wav" "origin" "-832 1432 -456" } { "classname" "target_speaker" "volume" ".6" "spawnflags" "1" "noise" "world/amb22.wav" "origin" "-648 1608 -456" } { "model" "*76" "targetname" "t226" "spawnflags" "2051" "classname" "func_wall" } { "model" "*77" "targetname" "t225" "spawnflags" "2051" "classname" "func_wall" } { "model" "*78" "targetname" "t225" "spawnflags" "2071" "classname" "func_wall" } { "origin" "-752 1628 -432" "target" "t226" "delay" "7" "targetname" "t42" "classname" "trigger_relay" "spawnflags" "2048" } { "origin" "-776 1636 -432" "target" "t225" "delay" "6.5" "targetname" "t42" "classname" "trigger_relay" "spawnflags" "2048" } { "origin" "-800 1496 -456" "spawnflags" "1" "noise" "world/amb22.wav" "classname" "target_speaker" "volume" "1" "attenuation" "3" } { "origin" "-496 1496 -456" "classname" "target_speaker" "noise" "world/amb22.wav" "spawnflags" "1" "attenuation" "3" "volume" "1" } { "model" "*79" "targetname" "t226" "classname" "func_wall" "spawnflags" "2071" } { "spawnflags" "2048" // b#7: 0 -> 2048 "noise" "world/l_hum1.wav" "classname" "target_speaker" "targetname" "t44" "origin" "-546 1760 -370" "volume" "1" "attenuation" "3" } { "spawnflags" "2048" // b#7: 0 -> 2048 "noise" "world/l_hum1.wav" "classname" "target_speaker" "targetname" "t43" "origin" "-650 1760 -370" "volume" "1" "attenuation" "3" } { "volume" "1" "attenuation" "3" "spawnflags" "1" "noise" "world/amb23.wav" "classname" "target_speaker" "origin" "-496 1656 -456" } { "attenuation" "3" "volume" "1" "classname" "target_speaker" "noise" "world/comp_hum2.wav" "spawnflags" "1" "origin" "-836 1720 -432" } { "light" "140" "classname" "light" "origin" "-546 1704 -452" } { "light" "140" "classname" "light" "origin" "-650 1704 -452" } { "classname" "light" "light" "140" "origin" "-490 1704 -452" } { "origin" "-1624 960 16" "classname" "ammo_bullets" "spawnflags" "2048" } { "spawnflags" "2048" "origin" "-868 1248 -472" "classname" "weapon_supershotgun" } { "spawnflags" "2048" "targetname" "t40" "classname" "target_spawner" "angle" "225" "origin" "-840 1204 -428" "target" "misc_gib_leg" } { "spawnflags" "2048" "targetname" "t40" "classname" "target_spawner" "angle" "225" "origin" "-820 1232 -428" "target" "misc_gib_leg" } { "spawnflags" "2048" "targetname" "t40" "classname" "target_spawner" "angle" "225" "origin" "-896 1108 -428" "target" "misc_gib_arm" } { "spawnflags" "2048" "targetname" "t40" "target" "misc_gib_head" "origin" "-824 1264 -428" "angle" "225" "classname" "target_spawner" } { "spawnflags" "2048" "targetname" "t40" "origin" "-928 1076 -428" "angle" "135" "target" "misc_gib_arm" "classname" "target_spawner" } { "origin" "-824 1316 -504" "classname" "misc_deadsoldier" "spawnflags" "2050" "angle" "45" } { "origin" "-868 1264 -504" "angle" "90" "spawnflags" "2049" "classname" "misc_deadsoldier" } { "origin" "-1712 1280 -440" "classname" "ammo_slugs" } { "light" "120" "classname" "light" "origin" "-288 64 24" } { "light" "120" "classname" "light" "origin" "-384 64 24" } { "light" "120" "classname" "light" "origin" "-544 64 24" } { "origin" "-470 944 -420" "targetname" "t46" "spawnflags" "769" "classname" "monster_parasite" } { "spawnflags" "2048" "origin" "-2028 1904 -432" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "-2064 1904 -432" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "-2728 520 -416" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "-2728 808 -416" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "-2304 704 -440" "classname" "ammo_grenades" } { "origin" "-2328 688 -456" "spawnflags" "2048" "angle" "45" "classname" "misc_deadsoldier" } { "origin" "-1640 976 0" "classname" "misc_deadsoldier" "angle" "270" "spawnflags" "2050" } { "spawnflags" "0" "origin" "-1468 -828 40" "classname" "ammo_shells" } { "origin" "-1000 -216 0" "target" "t214" "targetname" "t213" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "-856 -216 0" "target" "t213" "targetname" "t212" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "-1000 -464 0" "target" "t213" "targetname" "t214" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "-824 -216 16" "target" "t212" "spawnflags" "4" "angle" "180" "classname" "misc_insane" "deathtarget" "marduk" } { "spawnflags" "0" "origin" "-512 132 12" "classname" "item_health" } { "spawnflags" "0" "origin" "-476 132 12" "classname" "item_health" } { "spawnflags" "2048" "origin" "-328 1204 16" "classname" "weapon_grenadelauncher" } { "origin" "-1764 192 -8" "angle" "225" "spawnflags" "2049" "classname" "misc_deadsoldier" } { "style" "7" "targetname" "t211" "classname" "func_areaportal" } { "model" "*80" "target" "t211" "dmg" "5" "spawnflags" "2052" "_minlight" ".18" "speed" "120" "angle" "-1" "classname" "func_door" } { "origin" "-2008 472 32" "light" "200" "classname" "light" } { "origin" "-2106 422 108" "light" "86" "classname" "light" } { "classname" "light" "light" "86" "origin" "-2058 422 120" } { "origin" "-1886 428 92" "light" "86" "classname" "light" } { "model" "*81" "targetname" "t364" "_minlight" ".18" "sounds" "1" "speed" "10" "classname" "func_train" "target" "t196" } { "spawnflags" "2048" "angle" "135" "origin" "-1896 510 28" "target" "t2" "classname" "key_commander_head" } { "origin" "-1508 576 24" "classname" "light" "light" "120" } { "origin" "-1408 576 24" "classname" "light" "light" "120" } { "origin" "-1472 512 24" "classname" "light" "light" "120" } { "origin" "-1472 416 24" "classname" "light" "light" "120" } { "origin" "-1472 320 24" "classname" "light" "light" "120" } { "light" "120" "classname" "light" "origin" "-1728 224 24" } { "light" "120" "classname" "light" "origin" "-1632 224 24" } { "light" "120" "classname" "light" "origin" "-1564 224 24" } { "light" "120" "classname" "light" "origin" "-1472 224 24" } { "light" "120" "classname" "light" "origin" "-1664 320 24" } { "light" "120" "classname" "light" "origin" "-1664 416 24" } { "light" "120" "classname" "light" "origin" "-1664 512 24" } { "light" "120" "classname" "light" "origin" "-1600 576 24" } { "light" "120" "classname" "light" "origin" "-1312 576 24" } { "light" "120" "classname" "light" "origin" "-1216 576 24" } { "light" "120" "classname" "light" "origin" "-1152 544 24" } { "light" "120" "classname" "light" "origin" "-1152 448 24" } { "light" "120" "classname" "light" "origin" "-1152 352 24" } { "light" "120" "classname" "light" "origin" "-1152 256 24" } { "light" "120" "classname" "light" "origin" "-1152 160 24" } { "light" "120" "classname" "light" "origin" "-1120 64 24" } { "light" "120" "classname" "light" "origin" "-1024 64 24" } { "light" "120" "classname" "light" "origin" "-924 64 24" } { "light" "120" "classname" "light" "origin" "-824 64 24" } { "origin" "-2048 64 24" "classname" "light" "light" "120" } { "origin" "-732 64 24" "classname" "light" "light" "120" } { "origin" "-192 64 24" "classname" "light" "light" "120" } { "origin" "-640 64 24" "classname" "light" "light" "120" } { "origin" "-640 160 24" "classname" "light" "light" "120" } { "origin" "-640 240 24" "classname" "light" "light" "120" } { "origin" "-600 416 24" "classname" "light" "light" "120" } { "origin" "-640 448 24" "classname" "light" "light" "120" } { "origin" "-640 544 24" "classname" "light" "light" "120" } { "origin" "-640 640 24" "classname" "light" "light" "120" } { "origin" "-640 720 24" "classname" "light" "light" "120" } { "origin" "-704 784 24" "classname" "light" "light" "120" } { "origin" "-604 784 24" "classname" "light" "light" "120" } { "origin" "-512 784 24" "classname" "light" "light" "120" } { "origin" "440 872 116" "classname" "light" "light" "125" } { "origin" "440 784 116" "classname" "light" "light" "125" } { "origin" "-416 784 24" "classname" "light" "light" "120" } { "origin" "-320 780 24" "classname" "light" "light" "120" } { "origin" "-320 864 24" "classname" "light" "light" "120" } { "origin" "-320 960 24" "classname" "light" "light" "120" } { "origin" "-320 1060 24" "classname" "light" "light" "100" } { "origin" "-320 1152 24" "classname" "light" "light" "100" } { "origin" "-320 1252 24" "classname" "light" "light" "100" } { "classname" "light" "light" "94" "origin" "-320 1216 132" } { "origin" "-320 1344 24" "classname" "light" "light" "120" } { "origin" "-220 1344 24" "classname" "light" "light" "100" } { "origin" "-128 1344 24" "classname" "light" "light" "100" } { "origin" "64 1344 24" "classname" "light" "light" "100" } { "origin" "64 1248 24" "classname" "light" "light" "100" } { "origin" "64 1152 24" "classname" "light" "light" "100" } { "origin" "64 1060 24" "classname" "light" "light" "100" } { "origin" "64 924 24" "classname" "light" "light" "120" } { "origin" "64 832 24" "classname" "light" "light" "120" } { "origin" "64 736 24" "classname" "light" "light" "120" } { "origin" "64 640 24" "classname" "light" "light" "120" } { "origin" "64 640 104" "classname" "light" "light" "130" } { "origin" "-2204 344 -424" "classname" "light" "light" "80" } { "origin" "-2252 664 -380" "classname" "light" "light" "130" } { "light" "82" "classname" "light" "origin" "-2672 592 -300" } { "light" "82" "classname" "light" "origin" "-2672 736 -300" } { "light" "82" "classname" "light" "origin" "-2616 736 -300" } { "origin" "-1664 836 -436" "classname" "light" "light" "80" } { "origin" "-1664 748 -436" "classname" "light" "light" "80" } { "origin" "-1664 664 -436" "classname" "light" "light" "80" } { "origin" "-1772 664 -436" "classname" "light" "light" "80" } { "origin" "-1892 664 -436" "classname" "light" "light" "80" } { "origin" "-1984 664 -436" "classname" "light" "light" "80" } { "origin" "-2088 664 -436" "classname" "light" "light" "80" } { "origin" "-2184 664 -436" "classname" "light" "light" "80" } { "origin" "-2312 664 -424" "classname" "light" "light" "80" } { "origin" "-2408 664 -424" "classname" "light" "light" "80" } { "origin" "-2408 564 -424" "classname" "light" "light" "80" } { "light" "80" "classname" "light" "origin" "-2052 180 -424" } { "light" "80" "classname" "light" "origin" "-2052 264 -424" } { "light" "80" "classname" "light" "origin" "-2052 344 -424" } { "light" "80" "classname" "light" "origin" "-2128 344 -424" } { "light" "100" "classname" "light" "origin" "-2308 344 -424" } { "light" "80" "classname" "light" "origin" "-2408 344 -424" } { "light" "80" "classname" "light" "origin" "-2408 460 -424" } { "light" "80" "classname" "light" "origin" "-1664 924 -436" } { "light" "100" "classname" "light" "origin" "-2512 564 -412" } { "light" "100" "classname" "light" "origin" "-2584 564 -396" } { "light" "100" "classname" "light" "origin" "-2688 564 -396" } { "light" "100" "classname" "light" "origin" "-2688 664 -396" } { "light" "100" "classname" "light" "origin" "-2800 872 -396" } { "light" "100" "classname" "light" "origin" "-2584 780 -396" } { "light" "100" "classname" "light" "origin" "-2512 780 -412" } { "light" "80" "classname" "light" "origin" "-2408 784 -424" } { "light" "80" "classname" "light" "origin" "-2408 880 -424" } { "light" "80" "classname" "light" "origin" "-2408 968 -424" } { "light" "80" "classname" "light" "origin" "-2408 1064 -412" } { "light" "80" "classname" "light" "origin" "-2408 1136 -424" } { "light" "80" "classname" "light" "origin" "-2408 1216 -424" } { "light" "80" "classname" "light" "origin" "-2304 1216 -424" } { "light" "120" "classname" "light" "origin" "-2204 1216 -424" } { "light" "80" "classname" "light" "origin" "-2116 1216 -424" } { "light" "100" "classname" "light" "origin" "-2032 1216 -424" } { "light" "80" "classname" "light" "origin" "-2032 1292 -424" } { "light" "80" "classname" "light" "origin" "-2032 1372 -424" } { "light" "120" "classname" "light" "origin" "-2032 1480 -412" } { "light" "120" "classname" "light" "origin" "-1824 1600 -440" } { "origin" "-1248 1536 -456" "classname" "light" "light" "120" } { "light" "120" "classname" "light" "origin" "-1728 1600 -456" } { "light" "120" "classname" "light" "origin" "-1600 1600 -456" } { "light" "120" "classname" "light" "origin" "-1472 1600 -456" } { "light" "120" "classname" "light" "origin" "-1344 1600 -456" } { "light" "120" "classname" "light" "origin" "-1248 1600 -456" } { "light" "120" "classname" "light" "origin" "-1248 1452 -456" } { "light" "100" "classname" "light" "origin" "-1280 1300 -456" } { "light" "100" "classname" "light" "origin" "-1280 1220 -456" } { "light" "100" "classname" "light" "origin" "-1280 1152 -456" } { "light" "100" "classname" "light" "origin" "-1280 1060 -456" } { "light" "100" "classname" "light" "origin" "-1152 1060 -456" } { "light" "100" "classname" "light" "origin" "-1040 1060 -456" } { "light" "100" "classname" "light" "origin" "-928 1060 -456" } { "light" "100" "classname" "light" "origin" "-800 1060 -456" } { "light" "100" "classname" "light" "origin" "-800 1160 -456" } { "light" "100" "classname" "light" "origin" "-800 1248 -456" } { "light" "100" "classname" "light" "origin" "-800 1328 -456" } { "light" "140" "classname" "light" "origin" "-832 1452 -456" } { "origin" "-464 1472 -456" "classname" "light" "light" "140" } { "origin" "-464 1560 -456" "classname" "light" "light" "140" } { "origin" "-588 1560 -456" "classname" "light" "light" "140" } { "origin" "-708 1560 -456" "classname" "light" "light" "140" } { "origin" "-832 1656 -456" "classname" "light" "light" "140" } { "origin" "-2052 96 -404" "classname" "light" "light" "120" } { "origin" "-832 1416 -388" "classname" "light" "light" "120" } { "classname" "light" "light" "96" "_color" "1.000000 0.000000 0.000000" "origin" "-960 1680 -288" } { "origin" "-884 1464 -288" "_color" "1.000000 0.000000 0.000000" "light" "96" "classname" "light" } { "origin" "-920 1516 -288" "_color" "1.000000 0.000000 0.000000" "light" "96" "classname" "light" } { "origin" "-960 1584 -288" "_color" "1.000000 0.000000 0.000000" "light" "96" "classname" "light" } { "origin" "-776 1480 -288" "_color" "1.000000 0.000000 0.000000" "light" "125" "classname" "light" } { "origin" "-744 1512 -288" "_color" "1.000000 0.000000 0.000000" "light" "125" "classname" "light" } { "light" "120" "classname" "light" "origin" "-464 1392 -364" } { "light" "120" "classname" "light" "origin" "-464 1488 -364" } { "light" "120" "classname" "light" "origin" "-832 1488 -388" } { "light" "140" "classname" "light" "origin" "-464 1372 -456" } { "light" "190" "classname" "light" "origin" "-1696 424 116" } { "light" "190" "classname" "light" "origin" "-1696 548 116" } { "classname" "path_corner" "targetname" "t196" "origin" "-2064 384 -132" "target" "t197" } { "classname" "path_corner" "origin" "-2064 384 -88" "targetname" "t197" } { "classname" "misc_deadsoldier" "spawnflags" "2048" "angle" "135" "origin" "-2048 232 -8" } { "spawnflags" "2048" "classname" "ammo_shells" "origin" "-2084 260 16" } { "spawnflags" "3" "angle" "0" "classname" "monster_parasite" "origin" "-1940 1588 -424" "targetname" "t195" } { "spawnflags" "3" "angle" "0" "classname" "monster_parasite" "origin" "-1940 1636 -424" "targetname" "t195" } { "classname" "monster_parasite" "angle" "0" "spawnflags" "771" "origin" "-2016 1612 -412" "targetname" "t195" } { "model" "*82" "spawnflags" "2048" "classname" "trigger_once" "target" "t195" } { "origin" "-2692 816 -288" "classname" "light" "light" "90" } { "light" "90" "classname" "light" "origin" "-2648 816 -288" } { "light" "90" "classname" "light" "origin" "-2692 512 -288" } { "light" "90" "classname" "light" "origin" "-2736 816 -288" } { "origin" "-1716 716 -312" "light" "80" "classname" "light" } { "model" "*83" "classname" "func_explosive" "target" "t184" "health" "110" "mass" "200" } { "spawnflags" "0" "classname" "item_health_small" "origin" "-2200 592 16" } { "spawnflags" "0" "classname" "item_health_small" "origin" "-2200 556 16" } { "spawnflags" "0" "classname" "item_health_small" "origin" "-2200 628 16" } { "angle" "90" "classname" "misc_insane" "spawnflags" "2068" "origin" "-304 -504 104" "deathtarget" "marduk" } { "style" "8" "targetname" "t188" "classname" "func_areaportal" } { "model" "*84" "target" "t188" "speed" "120" "spawnflags" "4" "_minlight" ".18" "angle" "-1" "classname" "func_door" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-1176 1160 -384" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-1176 1248 -384" } { "origin" "-1568 1428 -384" "angle" "180" "spawnflags" "2049" "classname" "misc_deadsoldier" } { "origin" "-1680 1280 -440" "classname" "item_quad" } { "model" "*85" "speed" "120" "wait" "-1" "targetname" "t184" "angle" "-1" "classname" "func_door" "target" "t234" } { "origin" "-1696 1284 -432" "light" "150" "classname" "light" } { "model" "*86" "spawnflags" "2048" "angle" "90" "classname" "trigger_monsterjump" } { "origin" "-1444 -264 96" "classname" "light" "light" "140" } { "spawnflags" "2048" "origin" "-1444 -272 40" "classname" "item_health" } { "spawnflags" "2048" "origin" "-1444 -308 40" "classname" "item_health" } { "item" "ammo_shells" "origin" "-904 64 20" "spawnflags" "48" "angle" "360" "classname" "misc_insane" "deathtarget" "marduk" } { "origin" "-2168 656 144" "classname" "light" "light" "110" } { "classname" "light" "light" "110" "origin" "-1848 656 144" } { "origin" "-1448 -800 96" "classname" "light" "light" "80" } { "origin" "-1444 -696 88" "light" "80" "classname" "light" } { "origin" "-140 -512 100" "classname" "light" "light" "82" } { "origin" "44 -176 100" "light" "82" "classname" "light" } { "classname" "light" "light" "130" "origin" "-640 276 112" } { "origin" "-592 408 12" "targetname" "t173" "target" "t172" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "-688 408 12" "target" "t173" "targetname" "t172" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "468 896 40" "classname" "light" "light" "64" } { "origin" "468 676 40" "light" "64" "classname" "light" } { "origin" "-1696 1956 -320" "classname" "light" "light" "130" } { "origin" "-1440 1956 -320" "light" "130" "classname" "light" } { "origin" "-1896 510 60" "light" "120" "classname" "light" } { "classname" "light" "light" "84" "origin" "-800 -216 132" } { "classname" "light" "light" "110" "origin" "-896 64 220" } { "origin" "-1152 320 220" "light" "110" "classname" "light" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t138" "target" "t139" "origin" "-2048 656 -456" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t139" "origin" "-1656 664 -456" "target" "t335" } { "classname" "misc_insane" "angle" "0" "spawnflags" "16" "origin" "-88 728 16" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t135" "target" "t136" "origin" "-156 696 0" } { "classname" "path_corner" "spawnflags" "2048" // b#7: added this "targetname" "t136" "target" "t135" "origin" "-156 912 0" } { "classname" "misc_insane" "angle" "90" "target" "t135" "origin" "-156 672 16" } { "light" "120" "classname" "light" "origin" "-216 768 212" "_color" "0.989189 0.989189 1.000000" } { "light" "120" "classname" "light" "origin" "-336 680 212" "_color" "0.989189 0.989189 1.000000" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "light" "120" "classname" "light" "origin" "-128 880 52" } { "classname" "light" "light" "120" "origin" "-128 688 52" } { "origin" "64 504 228" "classname" "light" "light" "110" } { "origin" "64 440 228" "classname" "light" "light" "110" } { "origin" "64 384 228" "classname" "light" "light" "110" } { "origin" "64 344 180" "classname" "light" "light" "110" } { "origin" "64 320 132" "classname" "light" "light" "110" } { "origin" "-1712 648 -432" "target" "t134" "targetname" "t28" "classname" "trigger_relay" "spawnflags" "3840" // b#7: added this } { "classname" "light" "light" "120" "origin" "-1216 512 204" } { "light" "150" "classname" "light" "origin" "-1444 -292 76" } { "classname" "light" "light" "160" "origin" "-1248 -290 96" } { "classname" "light" "light" "130" "origin" "-396 64 100" } { "light" "100" "classname" "light" "origin" "-612 -496 208" } { "spawnflags" "32" "target" "t173" "classname" "misc_insane" "angle" "90" "origin" "-640 476 28" "deathtarget" "marduk" } { "targetname" "t46" "classname" "monster_parasite" "angle" "90" "spawnflags" "1" "origin" "-496 980 -420" } { "targetname" "t46" "classname" "monster_parasite" "angle" "90" "spawnflags" "1" "origin" "-444 980 -420" } { "classname" "target_explosion" "spawnflags" "2048" // b#7: added this "dmg" "120" "targetname" "t118" "origin" "-1884 664 -228" } { "classname" "target_explosion" "spawnflags" "2048" // b#7: added this "dmg" "50" "targetname" "t117" "origin" "-1664 660 -228" } { "classname" "target_explosion" "spawnflags" "2048" // b#7: added this "dmg" "120" "targetname" "t116" "origin" "-1664 868 -228" } { "spawnflags" "20" "angle" "180" "classname" "misc_insane" "origin" "-1664 660 -232" "deathtarget" "marduk" } { "spawnflags" "16" "angle" "90" "classname" "misc_insane" "origin" "-1884 664 -232" "deathtarget" "marduk" } { "classname" "misc_insane" "angle" "270" "spawnflags" "4" "origin" "-1664 868 -232" "deathtarget" "marduk" } { "classname" "misc_insane" "spawnflags" "4" "angle" "180" "origin" "-2084 660 -236" "target" "t138" "deathtarget" "marduk" } { "classname" "misc_deadsoldier" "spawnflags" "2050" "angle" "90" "origin" "-2672 552 -432" } { "light" "96" "classname" "light" "origin" "-440 1076 -332" } { "light" "96" "classname" "light" "origin" "-440 1016 -332" } { "light" "96" "classname" "light" "origin" "-440 956 -332" } { "light" "96" "classname" "light" "origin" "-440 904 -332" } { "light" "96" "classname" "light" "origin" "-440 844 -332" } { "light" "96" "classname" "light" "origin" "-448 784 -332" } { "spawnflags" "2048" "classname" "weapon_hyperblaster" "origin" "-1248 -320 48" } { "spawnflags" "2048" "classname" "item_armor_body" //"target" "t97" // never used "origin" "-1248 -272 48" } { "classname" "misc_deadsoldier" "spawnflags" "2052" "origin" "-352 1216 -8" "angle" "45" } { "origin" "116 704 12" "target" "t77" "targetname" "t78" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "68 872 20" "target" "t78" "targetname" "t77" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "64 644 28" "target" "t77" "angle" "90" "classname" "misc_insane" "item" "ammo_slugs" "deathtarget" "marduk" } { "spawnflags" "2048" "origin" "64 680 92" "message" "Locate Repair facility.\nSteal Commander's head." "classname" "target_help" "targetname" "t233" } { "origin" "-1488 -544 144" "classname" "light" "light" "96" } { "origin" "-1488 -620 144" "classname" "light" "light" "96" } { "origin" "-1488 -472 144" "light" "96" "classname" "light" } { "origin" "32 -232 92" "classname" "light" "light" "150" } { "origin" "-200 -504 92" "light" "150" "classname" "light" } { "origin" "-1436 -744 92" "light" "150" "classname" "light" } { "delay" "1.5" "origin" "-1592 1660 -344" "message" "Forcefields deactivated." "targetname" "t32" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this } { "style" "9" "targetname" "t67" "classname" "func_areaportal" } { "model" "*87" "spawnflags" "2048" "killtarget" "for1" "targetname" "t31" "target" "t32" "classname" "trigger_counter" } { "spawnflags" "2048" "origin" "-1416 -744 72" "killtarget" "p2" "targetname" "t58" "classname" "trigger_relay" } { "spawnflags" "2048" "origin" "-140 -472 168" "target" "t66" "targetname" "t65" "classname" "trigger_relay" } { "origin" "-20 -160 168" "target" "t66" "targetname" "t63" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this } { "spawnflags" "2048" "origin" "-168 -472 168" "target" "t65" "targetname" "t64" "classname" "trigger_relay" } { "origin" "4 -160 168" "target" "t63" "targetname" "t62" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this } { "model" "*88" "spawnflags" "4" "target" "t67" "_minlight" ".18" "angle" "-1" "classname" "func_door" "speed" "120" } { "origin" "-1696 624 -436" "target" "t55" "targetname" "t54" "delay" ".6" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this } { "origin" "-1656 624 -436" "target" "t54" "targetname" "t28" "delay" ".6" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this } { "origin" "-1732 624 -436" "target" "t56" "targetname" "t55" "delay" ".6" "classname" "trigger_relay" "spawnflags" "2048" // b#7: added this } { "light" "82" "classname" "light" "origin" "-2616 592 -300" } { "light" "90" "classname" "light" "origin" "-2736 712 -288" } { "origin" "-2736 512 -288" "classname" "light" "light" "90" } { "origin" "-2736 568 -288" "classname" "light" "light" "90" } { "origin" "-2736 616 -288" "classname" "light" "light" "90" } { "origin" "-2736 664 -288" "classname" "light" "light" "90" } { "origin" "-2684 664 -300" "classname" "light" "light" "82" } { "origin" "-2736 760 -288" "classname" "light" "light" "90" } { "origin" "-2648 512 -288" "classname" "light" "light" "90" } { "origin" "-2664 776 -188" "classname" "light" "light" "92" } { "origin" "-2688 732 -188" "classname" "light" "light" "92" } { "origin" "-2688 664 -188" "classname" "light" "light" "92" } { "origin" "-2688 604 -188" "classname" "light" "light" "92" } { "origin" "-2672 552 -188" "classname" "light" "light" "92" } { "origin" "-2408 624 -332" "classname" "light" "light" "92" } { "classname" "light" "light" "130" "_color" "1.000000 0.000000 0.000000" "origin" "-2592 776 -240" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-2480 784 -336" } { "angle" "90" "origin" "-1368 -320 56" "classname" "misc_deadsoldier" "spawnflags" "2049" } { "origin" "24 -408 166" "angle" "-2" "targetname" "t51" "sounds" "1" "classname" "target_splash" "spawnflags" "3840" // b#7: never used } { "origin" "-368 -504 166" "angle" "-2" "targetname" "t52" "sounds" "1" "classname" "target_splash" "spawnflags" "3840" // b#7: never used } { "dmg" "7" "origin" "-368 -504 168" "targetname" "t65" "spawnflags" "2050" // b#7: 2 -> 2050 "target" "t50" "classname" "target_laser" } { "model" "*89" "spawnflags" "2049" // b#7: 1 -> 2049 "targetname" "t50" "target" "t48" "classname" "func_train" } { "origin" "-304 -452 48" "target" "t49" "targetname" "t48" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "-304 -570 48" "target" "t48" "targetname" "t49" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "light" "74" "classname" "light" "origin" "-1688 1848 -328" } { "light" "74" "classname" "light" "origin" "-1688 1800 -328" } { "light" "74" "classname" "light" "origin" "-1676 1756 -328" } { "light" "74" "classname" "light" "origin" "-1652 1728 -328" } { "light" "74" "classname" "light" "origin" "-1620 1704 -328" } { "light" "74" "classname" "light" "origin" "-1568 1700 -328" } { "light" "74" "classname" "light" "origin" "-1508 1704 -328" } { "light" "74" "classname" "light" "origin" "-1476 1728 -328" } { "light" "74" "classname" "light" "origin" "-1440 1800 -328" } { "light" "74" "classname" "light" "origin" "-1440 1848 -328" } { "light" "74" "classname" "light" "origin" "-1452 1756 -328" } { "origin" "-1948 1900 -328" "classname" "light" "light" "74" } { "light" "74" "classname" "light" "origin" "-1948 1848 -328" } { "light" "74" "classname" "light" "origin" "-1948 1800 -328" } { "light" "74" "classname" "light" "origin" "-1936 1756 -328" } { "light" "74" "classname" "light" "origin" "-1912 1728 -328" } { "light" "74" "classname" "light" "origin" "-1880 1704 -328" } { "light" "74" "classname" "light" "origin" "-1828 1700 -328" } { "light" "74" "classname" "light" "origin" "-1768 1704 -328" } { "light" "74" "classname" "light" "origin" "-1736 1728 -328" } { "light" "74" "classname" "light" "origin" "-1700 1800 -328" } { "light" "74" "classname" "light" "origin" "-1700 1848 -328" } { "light" "74" "classname" "light" "origin" "-1712 1756 -328" } { "_color" "1.000000 0.000000 0.000000" "origin" "-1948 1900 -268" "classname" "light" "light" "120" } { "_color" "1.000000 0.000000 0.000000" "light" "95" "classname" "light" "origin" "-1948 1800 -268" } { "_color" "1.000000 0.000000 0.000000" "light" "95" "classname" "light" "origin" "-1912 1728 -268" } { "_color" "1.000000 0.000000 0.000000" "light" "95" "classname" "light" "origin" "-1828 1700 -268" } { "_color" "1.000000 0.000000 0.000000" "light" "95" "classname" "light" "origin" "-1736 1728 -268" } { "_color" "1.000000 0.000000 0.000000" "light" "95" "classname" "light" "origin" "-1700 1800 -268" } { "light" "82" "classname" "light" "origin" "-832 1088 -388" } { "style" "10" "targetname" "t47" "classname" "func_areaportal" } { "model" "*90" "target" "t47" "_minlight" ".18" "angle" "-1" "spawnflags" "0" "classname" "func_door" "speed" "120" } { "model" "*91" "target" "t46" "_minlight" ".18" "angle" "-1" "spawnflags" "8" "classname" "func_door" "speed" "120" } { "style" "11" "targetname" "t46" "classname" "func_areaportal" } { "model" "*92" "targetname" "t56" "classname" "func_explosive" "mass" "50" "target" "t115" } { "model" "*93" "targetname" "t55" "classname" "func_explosive" "mass" "50" "target" "t118" } { "model" "*94" "targetname" "t54" "classname" "func_explosive" "mass" "50" "target" "t117" } { "light" "70" "classname" "light" "origin" "-1936 752 116" } { "light" "70" "classname" "light" "origin" "-1948 888 116" } { "light" "70" "classname" "light" "origin" "-2028 888 116" } { "light" "70" "classname" "light" "origin" "-2080 888 116" } { "light" "70" "classname" "light" "origin" "-2080 832 116" } { "classname" "light" "light" "94" "origin" "-512 64 132" } { "light" "130" "classname" "light" "origin" "64 224 100" } { "light" "110" "classname" "light" "origin" "64 256 132" } { "origin" "-2032 1384 -332" "classname" "light" "light" "92" } { "classname" "trigger_relay" "delay" "7" "targetname" "t42" "target" "t44" "origin" "-750 1656 -438" "spawnflags" "2048" } { "classname" "trigger_relay" "delay" "6.5" "targetname" "t42" "target" "t43" "origin" "-776 1668 -438" "spawnflags" "2048" } { "classname" "light" "light" "120" "origin" "-876 1696 -410" } { "classname" "trigger_relay" "delay" "2" "targetname" "t42" "target" "t44" "origin" "-750 1684 -432" "spawnflags" "2048" } { "classname" "trigger_relay" "delay" "1" "targetname" "t42" "target" "t43" "origin" "-776 1698 -432" "spawnflags" "2048" } { "dmg" "5" "angle" "-2" "spawnflags" "2114" "classname" "target_laser" "origin" "-546 1760 -340" "targetname" "t44" } { "dmg" "5" "angle" "-2" "spawnflags" "2114" "classname" "target_laser" "origin" "-650 1760 -340" "targetname" "t43" } { "origin" "-546 1759 -404" "classname" "misc_insane" "spawnflags" "16" "angle" "270" "deathtarget" "marduk" } { "origin" "-650 1760 -412" "classname" "misc_insane" "spawnflags" "16" "angle" "270" "deathtarget" "marduk" } { "style" "12" "targetname" "t41" "classname" "func_areaportal" } { "model" "*95" "target" "t41" "spawnflags" "8" "angle" "-1" "_minlight" ".18" "classname" "func_door" "speed" "120" } { "model" "*96" "target" "t40" "spawnflags" "8" "_minlight" ".18" "angle" "-1" "classname" "func_door" "speed" "120" } { "style" "13" "targetname" "t40" "classname" "func_areaportal" } { "origin" "-1568 1396 -368" "classname" "light" "light" "195" "_color" "1.000000 0.000000 0.000000" } { "origin" "-1440 1396 -368" "_color" "1.000000 0.000000 0.000000" "light" "195" "classname" "light" } { "_color" "1.000000 0.000000 0.000000" "light" "195" "classname" "light" "origin" "-1696 1396 -368" } { "angle" "90" "origin" "-1696 1436 -464" "spawnflags" "2064" "classname" "misc_deadsoldier" } { "origin" "-1568 1436 -436" "angle" "90" "spawnflags" "16" "classname" "misc_insane" "deathtarget" "marduk" } { "origin" "-1696 1416 -340" "classname" "misc_insane" "spawnflags" "20" "angle" "90" } { "origin" "-1440 1436 -436" "angle" "90" "spawnflags" "20" "classname" "misc_insane" "deathtarget" "marduk" } { "model" "*97" "wait" "-1" "_minlight" ".2" "dmg" "15" "sounds" "3" "speed" "120" "angle" "-1" "classname" "func_door" "target" "t259" "targetname" "t275" "spawnflags" "16" } { "style" "14" "targetname" "t37" "classname" "func_areaportal" } { "model" "*98" "targetname" "t35" "target" "t34" "spawnflags" "2049" // b#7: 1 -> 2049 "speed" "100" "classname" "func_train" } { "origin" "-28 -344 48" "target" "t34" "targetname" "t33" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "origin" "88 -344 48" "target" "t33" "targetname" "t34" "classname" "path_corner" "spawnflags" "2048" // b#7: added this } { "targetname" "t63" "origin" "24 -408 168" "target" "t35" "spawnflags" "2050" "classname" "target_laser" "dmg" "7" } { "model" "*99" "spawnflags" "2055" "classname" "func_wall" "targetname" "t32" } { "model" "*100" "classname" "func_wall" "spawnflags" "2055" "targetname" "t32" } { "origin" "-584 1760 -268" "_color" "1.000000 0.000000 0.000000" "light" "96" "classname" "light" } { "origin" "-546 1722 -268" "_color" "1.000000 0.000000 0.000000" "light" "96" "classname" "light" } { "origin" "-508 1762 -268" "_color" "1.000000 0.000000 0.000000" "light" "96" "classname" "light" } { "classname" "light" "light" "110" "_color" "1.000000 0.000000 0.000000" "origin" "-546 1800 -268" } { "origin" "-688 1760 -268" "_color" "1.000000 0.000000 0.000000" "light" "96" "classname" "light" } { "origin" "-650 1722 -268" "_color" "1.000000 0.000000 0.000000" "light" "96" "classname" "light" } { "origin" "-612 1762 -288" "_color" "1.000000 0.000000 0.000000" "light" "82" "classname" "light" } { "classname" "light" "light" "110" "_color" "1.000000 0.000000 0.000000" "origin" "-698 1800 -268" } { "origin" "-856 1640 -312" "light" "85" "classname" "light" } { "model" "*101" "classname" "trigger_multiple" "target" "t30" "angle" "270" } { "classname" "target_changelevel" "map" "hangar1$lab" "targetname" "t30" "origin" "16 240 112" } { "light" "130" "classname" "light" "origin" "-624 -496 108" } { "origin" "-544 -568 108" "classname" "light" "light" "130" } { "origin" "-516 -548 208" "classname" "light" "light" "100" } { "model" "*102" "origin" "-1290 -301 100" "_minlight" ".18" "classname" "func_door_rotating" "spawnflags" "2208" "distance" "64" "speed" "96" "targetname" "t27" } { "model" "*103" "classname" "func_explosive" "dmg" "15" "targetname" "t28" "mass" "50" "target" "t116" } { "model" "*104" "spawnflags" "2048" "classname" "trigger_once" "target" "t28" "delay" "0" } { "light" "130" "classname" "light" "origin" "-624 -368 108" } { "light" "100" "classname" "light" "origin" "-456 -616 200" } { "model" "*105" "origin" "-1290 -285 100" "_minlight" ".18" "targetname" "t26" "speed" "96" "distance" "64" "spawnflags" "2208" "classname" "func_door_rotating" } { "wait" "8" "origin" "-1326 -274 98" "spawnflags" "2049" "target" "t26" "classname" "func_timer" "random" "7" } { "wait" "10" "origin" "-1326 -308 98" "spawnflags" "2049" "target" "t27" "random" "9" "classname" "func_timer" } { "spawnflags" "2048" "origin" "-1388 -332 104" "targetname" "t25" "angle" "-2" "sounds" "1" "classname" "target_splash" } { "wait" "5" "origin" "-1440 -396 112" "target" "t25" "spawnflags" "2049" "random" "4" "classname" "func_timer" } { "model" "*106" "origin" "-1388 -332 112" "dmg" "60" "speed" "350" "spawnflags" "2075" "classname" "func_rotating" "_minlight" ".3" } { "origin" "-1432 1852 -328" "classname" "light" "light" "74" } { "origin" "92 -344 224" "classname" "light" "light" "120" "_color" "1.000000 0.000000 0.000000" } { "origin" "-304 -572 224" "classname" "light" "light" "120" "_color" "1.000000 0.000000 0.000000" } { "origin" "-24 688 236" "classname" "light" "light" "120" } { "origin" "-24 880 236" "classname" "light" "light" "120" } { "_color" "1.000000 0.000000 0.000000" "classname" "light" "light" "165" "origin" "-546 1760 -400" } { "_color" "1.000000 0.000000 0.000000" "classname" "light" "light" "165" "origin" "-650 1760 -400" } { "origin" "-404 1740 -288" "_color" "1.000000 0.000000 0.000000" "light" "96" "classname" "light" } { "origin" "-404 1344 -288" "_color" "1.000000 0.000000 0.000000" "light" "96" "classname" "light" } { "origin" "-404 1600 -288" "_color" "1.000000 0.000000 0.000000" "light" "96" "classname" "light" } { "origin" "-404 1480 -288" "_color" "1.000000 0.000000 0.000000" "light" "96" "classname" "light" } { "origin" "-384 1536 -288" "_color" "1.000000 0.000000 0.000000" "light" "130" "classname" "light" } { "classname" "light" "light" "125" "_color" "1.000000 0.000000 0.000000" "origin" "-520 1480 -288" } { "classname" "light" "light" "125" "_color" "1.000000 0.000000 0.000000" "origin" "-552 1512 -288" } { "classname" "light" "light" "130" "_color" "1.000000 0.000000 0.000000" "origin" "-544 1408 -288" } { "classname" "light" "light" "130" "_color" "1.000000 0.000000 0.000000" "origin" "-384 1408 -288" } { "classname" "light" "light" "130" "_color" "1.000000 0.000000 0.000000" "origin" "-384 1664 -288" } { "classname" "light" "light" "130" "_color" "1.000000 0.000000 0.000000" "origin" "-1000 1680 -288" } { "classname" "light" "light" "130" "_color" "1.000000 0.000000 0.000000" "origin" "-1000 1584 -288" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-904 1248 -384" } { "light" "110" "classname" "light" "origin" "-464 1288 -276" } { "light" "110" "classname" "light" "origin" "-464 1344 -276" } { "light" "110" "classname" "light" "origin" "-464 1400 -276" } { "light" "110" "classname" "light" "origin" "-464 1456 -276" } { "light" "110" "classname" "light" "origin" "-464 1512 -276" } { "light" "110" "classname" "light" "origin" "-464 1568 -276" } { "light" "110" "classname" "light" "origin" "-464 1632 -276" } { "origin" "-464 1248 -276" "classname" "light" "light" "110" } { "origin" "-832 1608 -276" "classname" "light" "light" "110" } { "origin" "-832 1544 -276" "classname" "light" "light" "110" } { "origin" "-832 1480 -276" "classname" "light" "light" "110" } { "origin" "-832 1416 -276" "classname" "light" "light" "110" } { "origin" "-832 1416 -324" "classname" "light" "light" "110" } { "origin" "-832 1344 -388" "classname" "light" "light" "82" } { "origin" "-464 1292 -364" "classname" "light" "light" "120" } { "origin" "-832 1368 -388" "classname" "light" "light" "120" } { "origin" "-536 1072 -336" "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" } { "origin" "-1248 1368 -388" "classname" "light" "light" "120" } { "origin" "-1248 1416 -388" "classname" "light" "light" "120" } { "origin" "-832 1280 -388" "classname" "light" "light" "82" } { "origin" "-832 1216 -388" "classname" "light" "light" "82" } { "origin" "-832 1152 -388" "classname" "light" "light" "82" } { "origin" "-888 1088 -388" "classname" "light" "light" "82" } { "origin" "-952 1088 -388" "classname" "light" "light" "82" } { "origin" "-1024 1088 -388" "classname" "light" "light" "82" } { "origin" "-1096 1088 -388" "classname" "light" "light" "82" } { "origin" "-1168 1088 -388" "classname" "light" "light" "82" } { "origin" "-1248 1088 -388" "classname" "light" "light" "82" } { "origin" "-1248 1160 -388" "classname" "light" "light" "82" } { "origin" "-1248 1224 -388" "classname" "light" "light" "82" } { "origin" "-1248 1288 -388" "classname" "light" "light" "82" } { "origin" "-1248 1352 -388" "classname" "light" "light" "82" } { "origin" "-1248 1528 -252" "classname" "light" "light" "110" } { "origin" "-648 1512 -288" "_color" "1.000000 0.000000 0.000000" "light" "125" "classname" "light" } { "origin" "-992 1160 -384" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-1088 1160 -384" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-904 1160 -384" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-1176 1344 -384" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-2200 1288 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-1960 1384 -336" } { "light" "125" "classname" "light" "origin" "-1824 1600 -392" } { "origin" "-1696 1864 -252" "classname" "light" "light" "64" } { "origin" "-1952 1936 -392" "classname" "light" "light" "150" } { "light" "125" "classname" "light" "origin" "-1888 1600 -372" } { "light" "125" "classname" "light" "origin" "-1960 1600 -356" } { "light" "125" "classname" "light" "origin" "-2032 1600 -348" } { "light" "125" "classname" "light" "origin" "-2032 1528 -340" } { "light" "110" "classname" "light" "origin" "-2032 1464 -340" } { "_color" "1.000000 0.000000 0.000000" "light" "75" "classname" "light" "origin" "-1696 1864 -248" } { "classname" "light" "light" "75" "_color" "1.000000 0.000000 0.000000" "origin" "-1440 1864 -248" } { "_color" "1.000000 0.000000 0.000000" "light" "120" "classname" "light" "origin" "-2120 1696 -256" } { "_color" "1.000000 0.000000 0.000000" "light" "120" "classname" "light" "origin" "-2120 1568 -256" } { "classname" "light" "light" "120" "_color" "1.000000 0.000000 0.000000" "origin" "-2120 1824 -256" } { "origin" "-1688 1800 -268" "classname" "light" "light" "95" "_color" "1.000000 0.000000 0.000000" } { "origin" "-1652 1728 -268" "classname" "light" "light" "95" "_color" "1.000000 0.000000 0.000000" } { "origin" "-1568 1700 -268" "classname" "light" "light" "95" "_color" "1.000000 0.000000 0.000000" } { "origin" "-1476 1728 -268" "classname" "light" "light" "95" "_color" "1.000000 0.000000 0.000000" } { "origin" "-1440 1800 -268" "classname" "light" "light" "95" "_color" "1.000000 0.000000 0.000000" } { "origin" "-1432 1804 -328" "classname" "light" "light" "74" } { "origin" "-1420 1760 -328" "classname" "light" "light" "74" } { "origin" "-1364 1708 -328" "classname" "light" "light" "74" } { "origin" "-1396 1732 -328" "classname" "light" "light" "74" } { "origin" "-1312 1704 -328" "classname" "light" "light" "74" } { "origin" "-1252 1708 -328" "classname" "light" "light" "74" } { "origin" "-1220 1732 -328" "classname" "light" "light" "74" } { "origin" "-1196 1760 -328" "classname" "light" "light" "74" } { "origin" "-1184 1804 -328" "classname" "light" "light" "74" } { "origin" "-1184 1852 -328" "classname" "light" "light" "74" } { "light" "95" "classname" "light" "origin" "-1184 1804 -268" "_color" "1.000000 0.000000 0.000000" } { "light" "95" "classname" "light" "origin" "-1432 1804 -268" "_color" "1.000000 0.000000 0.000000" } { "light" "95" "classname" "light" "origin" "-1220 1732 -268" "_color" "1.000000 0.000000 0.000000" } { "light" "95" "classname" "light" "origin" "-1396 1732 -268" "_color" "1.000000 0.000000 0.000000" } { "light" "95" "classname" "light" "origin" "-1312 1704 -268" "_color" "1.000000 0.000000 0.000000" } { "light" "100" "classname" "light" "origin" "-1976 1532 -268" } { "origin" "-1400 1600 -252" "classname" "light" "light" "110" } { "origin" "-1568 1472 -284" "classname" "light" "light" "165" "_color" "1.000000 0.000000 0.000000" } { "origin" "-1696 1472 -284" "classname" "light" "light" "165" "_color" "1.000000 0.000000 0.000000" } { "origin" "-1696 1408 -444" "classname" "light" "light" "160" "_color" "1.000000 0.000000 0.000000" } { "light" "64" "classname" "light" "origin" "-1440 1864 -252" } { "classname" "light" "light" "150" "origin" "-1440 1928 -408" } { "light" "130" "classname" "light" "origin" "-464 1248 -332" } { "light" "110" "classname" "light" "origin" "-1248 1424 -308" } { "light" "110" "classname" "light" "origin" "-1248 1424 -252" } { "light" "110" "classname" "light" "origin" "-1248 1464 -252" } { "light" "110" "classname" "light" "origin" "-1248 1568 -252" } { "light" "110" "classname" "light" "origin" "-1280 1600 -252" } { "light" "110" "classname" "light" "origin" "-1248 1600 -252" } { "light" "110" "classname" "light" "origin" "-1336 1600 -252" } { "light" "110" "classname" "light" "origin" "-1464 1600 -252" } { "light" "110" "classname" "light" "origin" "-1528 1600 -252" } { "light" "110" "classname" "light" "origin" "-1592 1600 -252" } { "light" "110" "classname" "light" "origin" "-1656 1600 -252" } { "light" "110" "classname" "light" "origin" "-1720 1600 -252" } { "light" "110" "classname" "light" "origin" "-1776 1600 -252" } { "light" "110" "classname" "light" "origin" "-1832 1600 -252" } { "light" "110" "classname" "light" "origin" "-1896 1600 -252" } { "light" "110" "classname" "light" "origin" "-1960 1600 -252" } { "light" "110" "classname" "light" "origin" "-2032 1848 -252" } { "light" "110" "classname" "light" "origin" "-2032 1784 -252" } { "light" "110" "classname" "light" "origin" "-2032 1720 -252" } { "light" "110" "classname" "light" "origin" "-2032 1656 -252" } { "light" "110" "classname" "light" "origin" "-2032 1592 -252" } { "light" "110" "classname" "light" "origin" "-2032 1528 -252" } { "light" "110" "classname" "light" "origin" "-2032 1472 -252" } { "light" "110" "classname" "light" "origin" "-2032 1388 -352" } { "light" "92" "classname" "light" "origin" "-2032 1328 -332" } { "_color" "1.000000 0.000000 0.000000" "light" "160" "classname" "light" "origin" "-1568 1408 -444" } { "_color" "1.000000 0.000000 0.000000" "light" "165" "classname" "light" "origin" "-1440 1472 -284" } { "classname" "light" "light" "160" "_color" "1.000000 0.000000 0.000000" "origin" "-1440 1408 -444" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-1960 1288 -336" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-904 1344 -384" } { "origin" "-1152 448 220" "light" "110" "classname" "light" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-1960 1192 -336" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-2008 1144 -336" } { "light" "92" "classname" "light" "origin" "-2032 1280 -332" } { "light" "92" "classname" "light" "origin" "-2032 1216 -332" } { "light" "92" "classname" "light" "origin" "-2080 1216 -332" } { "light" "92" "classname" "light" "origin" "-2144 1216 -332" } { "light" "92" "classname" "light" "origin" "-2208 1216 -332" } { "light" "92" "classname" "light" "origin" "-2272 1216 -332" } { "light" "92" "classname" "light" "origin" "-2336 1216 -332" } { "light" "92" "classname" "light" "origin" "-2408 1216 -332" } { "light" "92" "classname" "light" "origin" "-2408 1144 -332" } { "light" "92" "classname" "light" "origin" "-2408 1072 -332" } { "light" "92" "classname" "light" "origin" "-2408 1008 -332" } { "light" "92" "classname" "light" "origin" "-2408 936 -332" } { "light" "92" "classname" "light" "origin" "-2408 864 -332" } { "light" "92" "classname" "light" "origin" "-2408 792 -332" } { "origin" "-1664 1120 -392" "classname" "light" "light" "130" } { "light" "130" "classname" "light" "origin" "-1664 1120 56" } { "origin" "-536 856 -336" "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" } { "origin" "-656 856 -336" "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" } { "origin" "-2120 160 -336" "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-1976 160 -336" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-1976 256 -336" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-1976 352 -336" } { "light" "92" "classname" "light" "origin" "-2048 224 -332" } { "light" "92" "classname" "light" "origin" "-2048 288 -332" } { "light" "92" "classname" "light" "origin" "-2048 344 -332" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-2048 416 -336" } { "light" "92" "classname" "light" "origin" "-2048 160 -332" } { "light" "92" "classname" "light" "origin" "-2048 80 -332" } { "spawnflags" "2048" "angle" "90" "origin" "-2008 480 53" "targetname" "t2" "classname" "monster_commander_body" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-2296 1288 -336" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-2392 1288 -336" } { "origin" "-2104 1384 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-2480 1200 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-2336 1104 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-2336 1008 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "light" "130" "classname" "light" "origin" "-864 784 -392" } { "origin" "-864 784 56" "classname" "light" "light" "130" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-2104 1144 -336" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-2200 1144 -336" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-2296 1144 -336" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-2336 912 -336" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-2336 816 -336" } { "origin" "-2480 1104 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-2480 1008 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-2480 912 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-2592 552 -240" "_color" "1.000000 0.000000 0.000000" "light" "130" "classname" "light" } { "origin" "-2048 -48 -392" "classname" "light" "light" "150" } { "origin" "-1664 1052 -324" "classname" "light" "light" "120" } { "light" "130" "classname" "light" "origin" "-2184 664 -380" } { "origin" "-2320 664 -332" "classname" "light" "light" "82" } { "origin" "-1984 712 -312" "light" "80" "classname" "light" } { "classname" "light" "light" "80" "origin" "-1984 612 -312" } { "origin" "-1712 884 -312" "light" "80" "classname" "light" } { "classname" "light" "light" "80" "origin" "-1612 884 -312" } { "origin" "-1712 824 -312" "light" "80" "classname" "light" } { "classname" "light" "light" "80" "origin" "-1612 824 -312" } { "origin" "-1712 764 -312" "light" "80" "classname" "light" } { "classname" "light" "light" "80" "origin" "-1612 764 -312" } { "origin" "-1716 616 -312" "light" "80" "classname" "light" } { "classname" "light" "light" "80" "origin" "-1764 716 -312" } { "origin" "-1820 616 -312" "light" "80" "classname" "light" } { "classname" "light" "light" "80" "origin" "-1820 716 -312" } { "origin" "-1900 612 -312" "light" "80" "classname" "light" } { "classname" "light" "light" "80" "origin" "-1900 712 -312" } { "origin" "-2064 612 -312" "light" "80" "classname" "light" } { "classname" "light" "light" "80" "origin" "-2064 712 -312" } { "origin" "-1612 944 -312" "light" "80" "classname" "light" } { "_color" "1.000000 0.015686 0.015686" "origin" "-1984 664 -308" "light" "85" "classname" "light" } { "light" "150" "classname" "light" "origin" "-2048 -48 56" } { "_color" "1.000000 0.015686 0.015686" "classname" "light" "light" "85" "origin" "-1664 760 -308" } { "_color" "1.000000 0.015686 0.015686" "classname" "light" "light" "85" "origin" "-1776 664 -308" } { "classname" "light" "light" "80" "origin" "-1712 944 -312" } { "_color" "1.000000 0.015686 0.015686" "light" "85" "classname" "light" "origin" "-1664 968 -308" } { "origin" "-1660 656 -236" "_color" "1.000000 0.000000 0.000000" "light" "240" "classname" "light" } { "origin" "-1884 664 -236" "_color" "1.000000 0.000000 0.000000" "light" "240" "classname" "light" } { "origin" "-2080 664 -236" "_color" "1.000000 0.000000 0.000000" "light" "240" "classname" "light" } { "light" "70" "classname" "light" "origin" "-2080 752 116" } { "light" "82" "classname" "light" "origin" "-1860 840 136" } { "origin" "-1780 840 104" "classname" "light" "light" "130" } { "origin" "-1664 1012 104" "classname" "light" "light" "130" } { "classname" "light" "light" "64" "origin" "-1664 1092 252" } { "origin" "-1724 1088 268" "classname" "light" "light" "105" } { "light" "105" "classname" "light" "origin" "-1604 1088 268" } { "origin" "-1604 1160 268" "classname" "light" "light" "105" } { "light" "105" "classname" "light" "origin" "-1724 1160 268" } { "classname" "light" "light" "64" "origin" "-1664 1052 184" } { "classname" "light" "light" "64" "origin" "-1664 1052 252" } { "origin" "-1728 1136 -144" "light" "120" "classname" "light" } { "origin" "-1600 1136 -144" "light" "120" "classname" "light" } { "origin" "-1664 988 -324" "classname" "light" "light" "120" } { "origin" "-1664 972 136" "classname" "light" "light" "82" } { "origin" "-1664 912 136" "classname" "light" "light" "82" } { "origin" "-1744 840 136" "classname" "light" "light" "82" } { "origin" "-1664 840 136" "classname" "light" "light" "82" } { "origin" "-1960 840 136" "classname" "light" "light" "95" } { "origin" "-1912 840 136" "classname" "light" "light" "95" } { "origin" "-2008 840 136" "classname" "light" "light" "95" } { "origin" "-1768 1600 -408" "classname" "light" "light" "125" } { "origin" "-2408 704 -332" "classname" "light" "light" "92" } { "origin" "-2408 536 -332" "classname" "light" "light" "92" } { "origin" "-2408 440 -332" "classname" "light" "light" "92" } { "origin" "-2408 344 -332" "classname" "light" "light" "92" } { "origin" "-2312 344 -332" "classname" "light" "light" "92" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-2336 512 -336" } { "classname" "light" "light" "150" "_color" "1.000000 0.000000 0.000000" "origin" "-2240 416 -336" } { "origin" "-2480 544 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-2480 416 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-2480 320 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-2432 272 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-2336 272 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-2240 272 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "origin" "-2200 344 -332" "classname" "light" "light" "92" } { "origin" "-2120 344 -332" "classname" "light" "light" "92" } { "origin" "-512 760 -332" "classname" "light" "light" "96" } { "origin" "-440 1136 -332" "classname" "light" "light" "96" } { "origin" "-576 760 -332" "classname" "light" "light" "96" } { "origin" "-640 760 -332" "classname" "light" "light" "96" } { "origin" "-720 760 -332" "classname" "light" "light" "96" } { "origin" "-2144 416 -336" "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" } { "_color" "1.000000 0.000000 0.000000" "light" "150" "classname" "light" "origin" "-536 1168 -336" } { "light" "140" "classname" "light" "origin" "-2448 664 -348" } { "light" "130" "classname" "light" "origin" "-2048 20 -324" } { "classname" "light" "light" "240" "_color" "1.000000 0.000000 0.000000" "origin" "-1664 864 -236" } { "model" "*107" "spawnflags" "0" "target" "t37" "classname" "func_door" "angle" "-1" "speed" "120" "sounds" "3" "dmg" "15" "_minlight" ".2" } { "classname" "light" "light" "130" "origin" "-2048 88 136" } { "classname" "light" "light" "130" "origin" "-1888 224 136" } { "origin" "-2048 96 164" "light" "94" "classname" "light" } { "origin" "-2048 96 220" "light" "94" "classname" "light" } { "origin" "-2048 160 220" "light" "94" "classname" "light" } { "origin" "-2048 224 220" "light" "94" "classname" "light" } { "origin" "-1984 224 220" "light" "94" "classname" "light" } { "origin" "-1920 224 220" "light" "94" "classname" "light" } { "origin" "-1664 224 220" "light" "110" "classname" "light" } { "origin" "-904 844 268" "classname" "light" "light" "115" } { "light" "115" "classname" "light" "origin" "-832 844 268" } { "light" "115" "classname" "light" "origin" "-904 724 268" } { "origin" "-832 724 268" "classname" "light" "light" "115" } { "classname" "light" "light" "74" "origin" "-796 784 252" } { "classname" "light" "light" "74" "origin" "-836 784 252" } { "classname" "light" "light" "74" "origin" "-796 784 184" } { "origin" "-880 720 -144" "light" "120" "classname" "light" } { "origin" "-880 848 -144" "light" "120" "classname" "light" } { "_color" "1.000000 0.022727 0.022727" "light" "150" "classname" "light" "origin" "-2104 352 208" } { "origin" "-2008 668 220" "classname" "light" "light" "85" } { "origin" "-2008 372 144" "classname" "light" "light" "110" } { "origin" "-2132 426 148" "sounds" "1" "targetname" "mac2" "classname" "target_splash" "spawnflags" "2048" // b#7: added this } { "origin" "-1848 600 220" "classname" "light" "light" "150" "_color" "1.000000 0.022727 0.022727" } { "origin" "-1848 424 220" "classname" "light" "light" "150" "_color" "1.000000 0.022727 0.022727" } { "targetname" "t201" "spawnflags" "2048" "origin" "-2172 504 130" "random" "4" "target" "mac2" "classname" "func_timer" "wait" "5" } { "model" "*108" "origin" "-2132 425 136" "_minlight" ".18" "targetname" "mac2" "speed" "78" "distance" "64" "spawnflags" "162" "classname" "func_door_rotating" } { "model" "*109" "origin" "-1872 422 78" "_minlight" ".18" "distance" "-64" "speed" "96" "targetname" "mac1" "spawnflags" "32" "classname" "func_door_rotating" } { "targetname" "t201" "origin" "-1848 576 72" "target" "mac1" "random" "4" "spawnflags" "2048" "classname" "func_timer" "wait" "5" } { "origin" "-2080 688 116" "classname" "light" "light" "70" } { "origin" "-1868 888 116" "classname" "light" "light" "70" } { "origin" "-1896 792 116" "classname" "light" "light" "70" } { "origin" "-2008 736 136" "classname" "light" "light" "95" } { "_color" "1.000000 0.022727 0.022727" "light" "150" "classname" "light" "origin" "-1912 352 208" } { "_color" "1.000000 0.022727 0.022727" "light" "150" "classname" "light" "origin" "-2168 600 220" } { "origin" "-2168 424 220" "_color" "1.000000 0.022727 0.022727" "light" "150" "classname" "light" } { "classname" "light" "light" "86" "origin" "-1918 408 108" } { "origin" "-2106 422 156" "light" "86" "classname" "light" } { "origin" "-1968 424 16" "light" "96" "classname" "light" } { "origin" "-2048 424 16" "classname" "light" "light" "96" } { "origin" "-1968 512 -8" "classname" "light" "light" "96" } { "origin" "-2048 512 -8" "light" "96" "classname" "light" } { "origin" "-2008 608 216" "classname" "light" "light" "85" } { "light" "85" "classname" "light" "origin" "-2008 544 216" } { "light" "85" "classname" "light" "origin" "-2008 696 172" } { "light" "70" "classname" "light" "origin" "-1936 688 116" } { "light" "95" "classname" "light" "origin" "-2008 800 136" } { "light" "130" "classname" "light" "origin" "-1836 840 104" } { "origin" "-2008 480 216" "classname" "light" "light" "85" } { "light" "96" "classname" "light" "origin" "-1696 560 220" } { "classname" "light" "light" "130" "origin" "-1984 -64 -144" } { "classname" "light" "light" "130" "origin" "-2112 -64 -144" } { "classname" "light" "light" "130" "origin" "-796 784 112" } { "origin" "-1472 224 220" "light" "110" "classname" "light" } { "light" "88" "classname" "light" "origin" "-1520 136 92" } { "origin" "-1424 136 92" "classname" "light" "light" "88" } { "light" "96" "classname" "light" "origin" "-1592 484 228" } { "light" "96" "classname" "light" "origin" "-1592 572 228" } { "light" "96" "classname" "light" "origin" "-1696 400 220" } { "classname" "light" "light" "110" "origin" "-1792 224 220" } { "classname" "light" "light" "130" "origin" "-1152 432 112" } { "light" "110" "classname" "light" "origin" "-8 688 164" } { "light" "110" "classname" "light" "origin" "-8 880 164" } { "classname" "light" "light" "130" "origin" "-1152 368 112" } { "classname" "light" "light" "110" "origin" "-1728 224 220" } { "classname" "light" "light" "94" "origin" "-1904 224 164" } { "classname" "light" "light" "110" "origin" "-1600 224 220" } { "classname" "light" "light" "110" "origin" "-1536 224 220" } { "classname" "light" "light" "110" "origin" "-1472 320 220" } { "classname" "light" "light" "110" "origin" "-1472 384 220" } { "classname" "light" "light" "110" "origin" "-1472 448 220" } { "classname" "light" "light" "110" "origin" "-1472 512 220" } { "classname" "light" "light" "110" "origin" "-1472 576 220" } { "classname" "light" "light" "110" "origin" "-1408 576 220" } { "classname" "light" "light" "110" "origin" "-1344 576 220" } { "classname" "light" "light" "110" "origin" "-1280 576 220" } { "classname" "light" "light" "110" "origin" "-1216 576 220" } { "classname" "light" "light" "110" "origin" "-1152 576 220" } { "classname" "light" "light" "110" "origin" "-1152 512 220" } { "classname" "light" "light" "110" "origin" "-1088 64 220" } { "light" "175" "classname" "light" "origin" "-1184 -40 212" "_color" "0.989189 0.989189 1.000000" } { "light" "175" "classname" "light" "origin" "-1256 32 212" "_color" "0.989189 0.989189 1.000000" } { "light" "175" "classname" "light" "origin" "-1256 160 212" "_color" "0.989189 0.989189 1.000000" } { "light" "175" "classname" "light" "origin" "-1056 264 212" "_color" "0.989189 0.989189 1.000000" } { "origin" "-2048 -20 252" "light" "76" "classname" "light" } { "origin" "-2048 20 252" "light" "76" "classname" "light" } { "origin" "-2048 20 184" "light" "76" "classname" "light" } { "origin" "-556 784 220" "light" "110" "classname" "light" } { "classname" "light" "light" "130" "origin" "-760 784 112" } { "origin" "-2108 -16 268" "classname" "light" "light" "115" } { "light" "115" "classname" "light" "origin" "-2108 -88 268" } { "origin" "-1988 -88 268" "classname" "light" "light" "115" } { "origin" "-536 608 212" "classname" "light" "light" "120" "_color" "0.989189 0.989189 1.000000" } { "classname" "light" "light" "130" "origin" "-880 64 112" } { "classname" "light" "light" "130" "origin" "-816 64 112" } { "light" "90" "classname" "light" "origin" "124 432 164" } { "origin" "-1152 256 220" "light" "110" "classname" "light" } { "origin" "-1152 192 220" "light" "110" "classname" "light" } { "origin" "-1152 128 220" "light" "110" "classname" "light" } { "origin" "-1152 64 220" "light" "110" "classname" "light" } { "origin" "-1792 224 172" "light" "110" "classname" "light" } { "origin" "-1024 64 220" "light" "110" "classname" "light" } { "origin" "-960 64 220" "light" "110" "classname" "light" } { "origin" "-888 64 176" "light" "110" "classname" "light" } { "origin" "-768 64 132" "light" "94" "classname" "light" } { "origin" "-1056 -40 212" "classname" "light" "light" "175" "_color" "0.989189 0.989189 1.000000" } { "origin" "-960 160 212" "light" "175" "classname" "light" "_color" "0.989189 0.989189 1.000000" } { "origin" "-1468 -640 220" "classname" "light" "light" "110" } { "origin" "-1468 -704 220" "classname" "light" "light" "110" } { "origin" "-1468 -768 220" "classname" "light" "light" "110" } { "origin" "-1216 -880 196" "classname" "light" "light" "115" } { "origin" "-1040 -880 196" "classname" "light" "light" "115" } { "origin" "-896 -744 196" "classname" "light" "light" "115" } { "origin" "-612 -368 208" "classname" "light" "light" "100" } { "origin" "-716 -216 128" "light" "74" "classname" "light" } { "origin" "-96 48 200" "classname" "light" "light" "100" } { "origin" "-1212 -312 180" "light" "100" "classname" "light" } { "origin" "-1212 -384 180" "light" "100" "classname" "light" } { "origin" "-1212 -456 180" "light" "100" "classname" "light" } { "origin" "-1212 -520 180" "light" "100" "classname" "light" } { "origin" "-1212 -584 180" "light" "100" "classname" "light" } { "origin" "-1212 -648 180" "light" "100" "classname" "light" } { "origin" "-1188 -696 180" "light" "100" "classname" "light" } { "origin" "-1132 -696 180" "light" "100" "classname" "light" } { "origin" "-1076 -696 180" "light" "100" "classname" "light" } { "origin" "-1200 -428 216" "light" "140" "classname" "light" } { "origin" "-1272 -468 216" "light" "140" "classname" "light" } { "origin" "-1424 -468 216" "light" "140" "classname" "light" } { "origin" "-1352 -468 216" "light" "150" "classname" "light" } { "classname" "light" "light" "110" "origin" "-1344 -712 220" } { "classname" "light" "light" "110" "origin" "-1344 -776 220" } { "origin" "-1064 -776 220" "light" "110" "classname" "light" } { "classname" "light" "light" "110" "origin" "-1128 -776 220" } { "classname" "light" "light" "110" "origin" "-1192 -776 220" } { "origin" "-1000 -648 220" "light" "110" "classname" "light" } { "classname" "light" "light" "110" "origin" "-1000 -712 220" } { "classname" "light" "light" "110" "origin" "-1000 -776 220" } { "origin" "-1344 -648 220" "light" "110" "classname" "light" } { "origin" "-1000 -544 132" "light" "130" "classname" "light" } { "light" "130" "classname" "light" "origin" "-1348 -340 72" } { "light" "56" "classname" "light" "origin" "-1308 -304 224" } { "classname" "light" "light" "56" "origin" "-1388 -304 224" } { "light" "56" "classname" "light" "origin" "-1308 -364 224" } { "classname" "light" "light" "56" "origin" "-1388 -364 224" } { "light" "100" "classname" "light" "origin" "-92 -80 200" } { "origin" "-1000 -480 132" "light" "84" "classname" "light" } { "origin" "-1000 -416 132" "light" "84" "classname" "light" } { "origin" "-1000 -352 132" "light" "84" "classname" "light" } { "origin" "-1000 -288 132" "light" "84" "classname" "light" } { "origin" "-1000 -216 132" "light" "84" "classname" "light" } { "origin" "-936 -216 132" "light" "84" "classname" "light" } { "origin" "-872 -216 132" "light" "84" "classname" "light" } { "origin" "-800 -216 100" "light" "130" "classname" "light" } { "origin" "-512 -216 220" "light" "110" "classname" "light" } { "classname" "light" "light" "130" "origin" "-1000 -632 128" } { "classname" "light" "light" "84" "origin" "-976 -216 220" } { "classname" "light" "light" "84" "origin" "-896 -216 220" } { "classname" "light" "light" "84" "origin" "-832 -216 220" } { "light" "110" "classname" "light" "origin" "-1468 -576 220" } { "classname" "light" "light" "132" "origin" "-228 -564 68" } { "classname" "light" "light" "132" "origin" "88 -268 68" } { "classname" "light" "light" "150" "origin" "-72 -348 216" } { "classname" "light" "light" "110" "origin" "-224 -172 220" } { "light" "100" "classname" "light" "origin" "28 -80 200" } { "light" "100" "classname" "light" "origin" "-92 -560 200" } { "light" "100" "classname" "light" "origin" "136 -192 200" } { "origin" "-208 168 200" "classname" "light" "light" "100" } { "classname" "light" "light" "130" "origin" "-224 -488 212" } { "origin" "-228 -408 212" "light" "130" "classname" "light" } { "origin" "-304 -404 216" "light" "150" "classname" "light" } { "origin" "-380 -408 212" "light" "130" "classname" "light" } { "origin" "-384 -488 212" "light" "130" "classname" "light" } { "origin" "-72 -268 212" "light" "135" "classname" "light" } { "classname" "light" "light" "135" "origin" "8 -264 212" } { "origin" "-72 -420 212" "light" "135" "classname" "light" } { "origin" "8 -424 212" "light" "135" "classname" "light" } { "classname" "light" "light" "140" "origin" "-1496 -428 216" } { "origin" "-336 168 200" "classname" "light" "light" "100" } { "origin" "28 -560 200" "classname" "light" "light" "100" } { "origin" "-224 -216 220" "light" "110" "classname" "light" } { "light" "140" "origin" "-1348 -256 104" "classname" "light" "light" "130" } { "origin" "-304 -568 104" "classname" "light" "light" "130" } { "origin" "88 -344 104" "light" "130" "classname" "light" } { "light" "56" "classname" "light" "origin" "-304 -488 224" } { "light" "56" "classname" "light" "origin" "8 -344 224" } { "origin" "-152 -616 200" "classname" "light" "light" "100" } { "light" "100" "classname" "light" "origin" "-600 -256 148" } { "classname" "light" "light" "84" "origin" "-768 -216 220" } { "classname" "light" "light" "110" "origin" "-704 -216 220" } { "classname" "light" "light" "110" "origin" "-640 -216 220" } { "classname" "light" "light" "110" "origin" "-576 -216 220" } { "classname" "light" "light" "110" "origin" "-1264 -776 220" } { "classname" "light" "light" "110" "origin" "-448 -216 220" } { "classname" "light" "light" "110" "origin" "-384 -216 220" } { "classname" "light" "light" "110" "origin" "-320 -216 220" } { "classname" "light" "light" "110" "origin" "-264 -216 220" } { "classname" "light" "light" "110" "origin" "-224 -128 220" } { "classname" "light" "light" "110" "origin" "-224 -64 220" } { "origin" "-1308 -240 224" "classname" "light" "light" "56" } { "origin" "-1388 -240 224" "light" "56" "classname" "light" } { "origin" "-536 480 212" "classname" "light" "light" "120" "_color" "0.989189 0.989189 1.000000" } { "origin" "-716 -216 100" "light" "130" "classname" "light" } { "origin" "-224 0 220" "light" "110" "classname" "light" } { "origin" "-224 64 220" "light" "110" "classname" "light" } { "origin" "-308 64 220" "light" "110" "classname" "light" } { "origin" "-384 64 220" "light" "110" "classname" "light" } { "origin" "-640 400 220" "light" "110" "classname" "light" } { "origin" "-744 480 212" "classname" "light" "light" "120" "_color" "0.989189 0.989189 1.000000" } { "origin" "-1808 224 112" "light" "130" "classname" "light" } { "origin" "-640 200 112" "light" "130" "classname" "light" } { "origin" "-704 64 132" "light" "94" "classname" "light" } { "origin" "-576 64 132" "light" "94" "classname" "light" } { "origin" "-640 64 132" "light" "94" "classname" "light" } { "origin" "-640 128 132" "light" "94" "classname" "light" } { "origin" "-640 192 132" "light" "94" "classname" "light" } { "light" "120" "classname" "light" "origin" "-464 680 212" "_color" "0.989189 0.989189 1.000000" } { "light" "120" "classname" "light" "origin" "-536 352 212" "_color" "0.989189 0.989189 1.000000" } { "light" "100" "classname" "light" "origin" "136 -496 200" } { "light" "115" "classname" "light" "origin" "-1988 -16 268" } { "light" "120" "classname" "light" "origin" "-744 608 212" "_color" "0.989189 0.989189 1.000000" } { "light" "120" "classname" "light" "origin" "-688 888 212" "_color" "0.989189 0.989189 1.000000" } { "light" "120" "classname" "light" "origin" "-560 888 212" "_color" "0.989189 0.989189 1.000000" } { "origin" "-1592 396 228" "classname" "light" "light" "96" } { "origin" "-744 352 212" "classname" "light" "light" "120" "_color" "0.989189 0.989189 1.000000" } { "origin" "-756 784 172" "light" "110" "classname" "light" } { "origin" "-728 784 220" "light" "110" "classname" "light" } { "origin" "-640 784 220" "light" "110" "classname" "light" } { "origin" "-796 784 -348" "light" "130" "classname" "light" } { "classname" "light" "light" "110" "origin" "-1152 368 192" } { "classname" "light" "light" "110" "origin" "-640 476 220" } { "classname" "light" "light" "110" "origin" "-640 552 220" } { "classname" "light" "light" "110" "origin" "-640 624 220" } { "classname" "light" "light" "110" "origin" "-640 696 220" } { "classname" "light" "light" "110" "origin" "-640 276 172" } { "classname" "light" "light" "74" "origin" "-892 784 252" } { "classname" "light" "light" "110" "origin" "-480 784 220" } { "classname" "light" "light" "110" "origin" "-404 784 220" } { "classname" "light" "light" "110" "origin" "-320 784 220" } { "origin" "-640 312 220" "light" "110" "classname" "light" } { "classname" "light" "light" "110" "origin" "-320 992 220" } { "origin" "-320 856 220" "light" "110" "classname" "light" } { "origin" "-320 924 220" "light" "110" "classname" "light" } { "origin" "-320 992 168" "light" "110" "classname" "light" } { "origin" "-320 1088 132" "light" "94" "classname" "light" } { "origin" "-216 896 212" "classname" "light" "light" "120" "_color" "0.989189 0.989189 1.000000" } { "model" "*110" "spawnflags" "8" "target" "t57" "sounds" "3" "speed" "120" "angle" "-1" "classname" "func_door" "_minlight" ".2" } { "origin" "-320 1152 132" "light" "94" "classname" "light" } { "origin" "-320 1280 132" "light" "94" "classname" "light" } { "origin" "-320 1344 132" "light" "94" "classname" "light" } { "origin" "-256 1344 132" "light" "94" "classname" "light" } { "origin" "-192 1344 132" "light" "94" "classname" "light" } { "origin" "-128 1344 132" "light" "94" "classname" "light" } { "origin" "-64 1344 132" "light" "94" "classname" "light" } { "origin" "64 536 100" "classname" "light" "light" "130" } { "origin" "64 1344 132" "classname" "light" "light" "94" } { "light" "94" "classname" "light" "origin" "0 1344 132" } { "light" "110" "classname" "light" "origin" "64 520 180" } { "light" "94" "classname" "light" "origin" "64 1280 132" } { "light" "94" "classname" "light" "origin" "64 1216 132" } { "light" "94" "classname" "light" "origin" "64 1152 132" } { "light" "94" "classname" "light" "origin" "64 1088 132" } { "classname" "light" "light" "130" "origin" "-464 64 96" } { "model" "*111" "target" "t278" "spawnflags" "2048" "_minlight" ".18" "classname" "func_button" "angle" "0" "speed" "75" "wait" "-1" //"sounds" "0" // b#9: was 1 } { "model" "*112" "targetname" "t279" "classname" "func_door" "angle" "-1" "sounds" "3" "speed" "160" "spawnflags" "2088" "_minlight" ".2" "target" "t255" } { "light" "110" "classname" "light" "origin" "64 952 240" } { "light" "110" "classname" "light" "origin" "64 896 248" } { "light" "110" "classname" "light" "origin" "64 832 248" } { "light" "110" "classname" "light" "origin" "64 768 248" } { "light" "110" "classname" "light" "origin" "64 704 248" } { "light" "110" "classname" "light" "origin" "64 640 180" } { "light" "110" "classname" "light" "origin" "64 640 240" } { "light" "130" "classname" "light" "origin" "64 936 112" } { "light" "120" "classname" "light" "origin" "208 716 112" } { "light" "120" "classname" "light" "origin" "-456 64 24" } { "origin" "404 784 16" "classname" "light" "light" "125" } { "origin" "400 864 48" "classname" "light" "light" "125" } { "origin" "400 704 48" "classname" "light" "light" "125" } { "origin" "208 852 112" "classname" "light" "light" "120" } { "origin" "64 952 184" "classname" "light" "light" "110" } { "origin" "352 864 124" "classname" "light" "light" "130" } { "light" "110" "classname" "light" "origin" "240 680 260" } { "light" "110" "classname" "light" "origin" "240 896 260" } { "light" "125" "classname" "light" "origin" "440 696 116" } { "angle" "90" "origin" "64 504 40" "classname" "info_player_start" "targetname" "lstart" } { "light" "130" "classname" "light" "origin" "352 704 124" } { "light" "125" "classname" "light" "origin" "-88 704 108" } { "origin" "240 784 260" "classname" "light" "light" "110" } { "origin" "-24 368 236" "classname" "light" "light" "120" } { "origin" "-24 432 236" "light" "120" "classname" "light" } { "light" "125" "classname" "light" "origin" "-88 864 108" } { "origin" "-24 496 236" "classname" "light" "light" "120" } { "origin" "4 432 164" "classname" "light" "light" "90" } { "origin" "4 368 164" "classname" "light" "light" "90" } { "origin" "124 368 164" "light" "90" "classname" "light" } { "origin" "152 496 236" "classname" "light" "light" "120" } { "origin" "152 432 236" "classname" "light" "light" "120" } { "origin" "152 368 236" "classname" "light" "light" "120" } { "classname" "func_group" } { "model" "*113" "classname" "func_wall" } { "model" "*114" "spawnflags" "2048" "_minlight" ".18" "wait" "-1" "angle" "180" "classname" "func_button" "target" "t328" } { "origin" "-1471 -756 44" "light" "48" "classname" "light" } { "origin" "-1471 -732 44" "classname" "light" "light" "48" } { "model" "*115" "classname" "func_wall" } { "classname" "light" "light" "64" "origin" "-188 -539 44" } { "light" "64" "classname" "light" "origin" "-212 -539 44" }yquake2-QUAKE2_8_40/stuff/mapfixes/baseq2/train.ent000066400000000000000000003240351465112212000220650ustar00rootroot00000000000000// FIXED ENTITY STRING (by BjossiAlfreds) // // 1. Fixed spawnflags of DM-only weapon_supershotgun (1180) // // Was set to spawn in SP and co-op but is inaccessible due to // func_wall (1190). { "message" "Lost Station" "classname" "worldspawn" "spawnflags" "0" "sky" "unit1_" "sounds" "10" "nextmap" "fact3" } { "spawnflags" "2048" "origin" "-1720 -632 1032" "killtarget" "dullsign" "targetname" "t433" "classname" "trigger_relay" } { "classname" "item_health_small" "spawnflags" "2048" "origin" "256 -600 112" } { "classname" "item_health_small" "spawnflags" "2048" "origin" "256 -680 112" } { "classname" "item_health_small" "origin" "256 -720 112" } { "classname" "item_health_small" "spawnflags" "2048" "origin" "256 -640 112" } { "classname" "item_health_small" "spawnflags" "1024" "origin" "216 -720 112" } { "classname" "item_health_small" "origin" "256 -560 112" } { "classname" "item_health_small" "spawnflags" "1024" "origin" "216 -560 112" } { "origin" "-752 -832 904" "classname" "monster_infantry" "angle" "90" "item" "ammo_bullets" } { "origin" "-624 -832 904" "angle" "90" "classname" "monster_infantry" } { "classname" "ammo_bullets" "origin" "-848 -288 176" "spawnflags" "3584" } { "spawnflags" "2048" "classname" "ammo_bullets" "origin" "-848 -232 176" } { "classname" "weapon_grenadelauncher" "spawnflags" "1792" "origin" "-1328 760 192" } { "origin" "-96 -1952 864" "classname" "item_armor_jacket" "spawnflags" "3072" } { "classname" "trigger_relay" "target" "BLR" "targetname" "t444" "origin" "-1096 -272 244" } { "model" "*1" "classname" "trigger_once" "target" "t443" "targetname" "t444" } { "model" "*2" "origin" "-1088 -316 88" "classname" "func_train" "target" "t442" "noise" "world/mach1.wav" "spawnflags" "2048" "speed" "50" "_minlight" "0.4" "targetname" "t443" } { "classname" "path_corner" "target" "t441" "targetname" "t442" "origin" "-1120 -332 32" } { "classname" "path_corner" "targetname" "t441" "target" "t442" "origin" "-1120 -311 192" "wait" "-1" } { "targetname" "t269" "classname" "trigger_relay" "killtarget" "trigbut" "origin" "-828 -368 -126" "spawnflags" "2048" } { "item" "item_armor_shard" "classname" "monster_soldier_light" "origin" "-692 -772 116" "spawnflags" "769" "targetname" "t377" "angle" "270" } { "classname" "point_combat" "targetname" "t440" "origin" "-728 -1128 -24" } { "classname" "monster_soldier_light" "origin" "-692 -884 68" "spawnflags" "257" "targetname" "t377" "target" "t440" "angle" "270" } { "classname" "monster_soldier" "spawnflags" "769" "origin" "-1380 -944 -40" "angle" "45" } { "model" "*3" "classname" "func_wall" "spawnflags" "1792" } { "classname" "ammo_bullets" "origin" "-1508 568 -164" } { "noise" "world/train2.wav" "volume" "0.6" "classname" "target_speaker" "attenuation" "3" "origin" "-600 -728 -72" } { "targetname" "corner0" "noise" "world/train2.wav" "volume" "0.6" "classname" "target_speaker" "attenuation" "3" "origin" "-536 -552 -104" } { "targetname" "corner8" "noise" "world/train2.wav" "volume" "0.6" "classname" "target_speaker" "attenuation" "3" "origin" "8 -744 -584" } { "targetname" "corner7" "classname" "target_speaker" "volume" "0.6" "noise" "world/train2.wav" "attenuation" "3" "origin" "40 -744 -536" } { "targetname" "corner7" "noise" "world/train2.wav" "volume" "0.6" "classname" "target_speaker" "attenuation" "3" "origin" "40 -536 -536" } { "targetname" "corner8" "classname" "target_speaker" "volume" "0.6" "noise" "world/train2.wav" "attenuation" "3" "origin" "8 -536 -584" } { "targetname" "close_out" "noise" "world/train2.wav" "volume" "0.6" "classname" "target_speaker" "attenuation" "3" "origin" "8 -744 1080" } { "targetname" "corner6" "classname" "target_speaker" "volume" "0.6" "noise" "world/train2.wav" "attenuation" "3" "origin" "40 -744 1032" } { "targetname" "corner6" "noise" "world/train2.wav" "volume" "0.6" "classname" "target_speaker" "attenuation" "3" "origin" "40 -536 1032" } { "targetname" "close_out" "classname" "target_speaker" "volume" "0.6" "noise" "world/train2.wav" "attenuation" "3" "origin" "8 -536 1080" } { "targetname" "open_in" "origin" "-1672 -744 1080" "noise" "world/train2.wav" "volume" "0.6" "classname" "target_speaker" "attenuation" "3" } { "targetname" "corner3" "origin" "-1704 -744 1032" "classname" "target_speaker" "volume" "0.6" "noise" "world/train2.wav" "attenuation" "3" } { "targetname" "corner3" "origin" "-1704 -536 1032" "noise" "world/train2.wav" "volume" "0.6" "classname" "target_speaker" "attenuation" "3" } { "targetname" "open_in" "origin" "-1672 -536 1080" "classname" "target_speaker" "volume" "0.6" "noise" "world/train2.wav" "attenuation" "3" } { "targetname" "corner1" "origin" "-1672 -744 -72" "noise" "world/train2.wav" "volume" "0.6" "classname" "target_speaker" "attenuation" "3" } { "targetname" "corner2" "origin" "-1704 -744 -24" "classname" "target_speaker" "volume" "0.6" "noise" "world/train2.wav" "attenuation" "3" } { "targetname" "corner2" "origin" "-1704 -536 -24" "noise" "world/train2.wav" "volume" "0.6" "classname" "target_speaker" "attenuation" "3" } { "targetname" "corner1" "origin" "-1672 -536 -72" "classname" "target_speaker" "volume" "0.6" "noise" "world/train2.wav" "attenuation" "3" } { "targetname" "corner9" "origin" "-520 -760 -584" "attenuation" "3" "classname" "target_speaker" "volume" "0.6" "noise" "world/train2.wav" } { "targetname" "corner0" "origin" "-536 -624 -144" "attenuation" "3" "noise" "world/train2.wav" "volume" "0.6" "classname" "target_speaker" } { "targetname" "corner0B" "origin" "-600 -520 -72" "attenuation" "3" "classname" "target_speaker" "volume" "0.6" "noise" "world/train2.wav" } { "targetname" "corner9" "origin" "-520 -552 -584" "attenuation" "3" "noise" "world/train2.wav" "volume" "0.6" "classname" "target_speaker" } { "model" "*4" "origin" "420 388 -64" "speed" "290" "dmg" "10" "_minlight" "0.2" "team" "train1" "spawnflags" "1" "target" "t20" "classname" "func_train" } { "model" "*5" "origin" "36 388 -76" "noise" "world/train1.wav" "target" "t3" "team" "train1" "speed" "290" "dmg" "10" "spawnflags" "1" "classname" "func_train" "_minlight" "0.2" } { "spawnflags" "1" "classname" "func_timer" "random" "3" "wait" "8" "origin" "-1350 756 2" "target" "t439" } { "noise" "world/drip1.wav" "spawnflags" "0" "classname" "target_speaker" "attenuation" "2" "volume" "0.6" "origin" "-1330 774 2" "targetname" "t439" } { "noise" "world/drip3.wav" "spawnflags" "0" "classname" "target_speaker" "attenuation" "2" "volume" "0.7" "origin" "-1382 732 2" "targetname" "t438" } { "spawnflags" "1" "classname" "func_timer" "random" "4" "wait" "12" "origin" "-1350 724 2" "target" "t438" } { "classname" "info_player_deathmatch" "origin" "-616 -616 88" } { "model" "*6" "origin" "-772 -1526 916" "classname" "func_door_rotating" "spawnflags" "2048" "distance" "90" "message" "ACCESS DENIED\nDoor is opened elsewhere." "wait" "4" "speed" "40" "targetname" "access_panel" "_minlight" "0.2" } { "origin" "-1414 812 2" "volume" "0.3" "attenuation" "2" "classname" "target_speaker" "spawnflags" "0" "noise" "world/drip2.wav" "targetname" "t437" } { "origin" "-1382 804 2" "wait" "6" "random" "3" "classname" "func_timer" "spawnflags" "1" "target" "t437" } { "origin" "-1286 796 -30" "wait" "6" "random" "4" "target" "t435" "classname" "func_timer" "spawnflags" "1" "volume" "0.5" } { "origin" "-1266 814 -30" "volume" "0.5" "attenuation" "2" "targetname" "t435" "classname" "target_speaker" "spawnflags" "0" "noise" "world/drip1.wav" } { "_color" "1.000000 0.880000 0.192000" "classname" "light" "light" "80" "origin" "-1144 -1120 -104" } { "origin" "-1064 -1120 -104" "light" "80" "classname" "light" "_color" "1.000000 0.880000 0.192000" } { "model" "*7" "spawnflags" "2049" "target" "t433" "count" "13" "targetname" "goons1" "classname" "trigger_counter" } { "model" "*8" "targetname" "t433" "spawnflags" "2051" "classname" "func_wall" } { "model" "*9" "spawnflags" "0" "targetname" "dullsign" "classname" "func_wall" } { "light" "170" "angle" "90" "origin" "-912 -1182 -36" "classname" "light_mine2" } { "classname" "ammo_shells" "origin" "-1360 724 -60" "spawnflags" "2048" } { "classname" "ammo_slugs" "origin" "-1336 668 -60" "spawnflags" "1792" } { "spawnflags" "2048" "origin" "-1864 918 178" "classname" "ammo_shells" } { "model" "*10" "classname" "func_button" "angle" "90" "_minlight" "0.8" "lip" "3" "target" "water_secret" "spawnflags" "2048" } { "model" "*11" "classname" "func_wall" "spawnflags" "2048" "_minlight" "0.2" } { "model" "*12" "classname" "func_wall" "spawnflags" "2048" "_minlight" "0.2" } { "origin" "-672 -616 688" "spawnflags" "1792" "classname" "ammo_bullets" } { "origin" "-624 -624 688" "spawnflags" "1792" "classname" "ammo_bullets" } { "origin" "-480 -848 888" "spawnflags" "1792" "classname" "weapon_machinegun" } { "origin" "-528 -1488 872" "spawnflags" "1792" "classname" "weapon_shotgun" } { "origin" "-544 -1184 896" "classname" "info_player_deathmatch" } { "angle" "45" "origin" "-96 -1680 880" "targetname" "base3c" "classname" "info_player_coop" } { "angle" "90" "origin" "-112 -1736 880" "targetname" "base3c" "classname" "info_player_coop" } { "origin" "-128 -1800 880" "targetname" "base3c" "classname" "info_player_coop" } { "origin" "-1472 -528 816" "spawnflags" "1792" "classname" "weapon_grenadelauncher" } { "origin" "-1360 -528 816" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-1152 -528 824" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "-1440 -536 -328" "angle" "315" "classname" "info_player_deathmatch" } { "origin" "-1120 -976 -256" "classname" "info_player_deathmatch" } { "origin" "-976 -752 -176" "noise" "world/amb2.wav" "spawnflags" "1" "classname" "target_speaker" } { "targetname" "hiss" "classname" "target_speaker" "spawnflags" "0" "noise" "world/steam2.wav" "origin" "-976 -752 -272" "attenuation" "2" "volume" "1" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb2.wav" "origin" "-676 -760 976" } { "targetname" "hiss2" "volume" "1" "attenuation" "2" "origin" "-700 -752 884" "noise" "world/steam2.wav" "spawnflags" "0" "classname" "target_speaker" } { "classname" "light" "light" "100" "origin" "-996 -572 -256" } { "style" "1" "classname" "func_areaportal" "targetname" "t432" } { "origin" "424 416 -200" "classname" "light" "light" "64" } { "origin" "40 416 -200" "light" "64" "classname" "light" } { "origin" "336 368 -216" "light" "48" "classname" "light" } { "origin" "336 464 -216" "classname" "light" "light" "48" } { "origin" "-48 368 -216" "classname" "light" "light" "48" } { "origin" "-48 464 -216" "light" "48" "classname" "light" } { "classname" "light" "light" "125" "origin" "72 552 -8" } { "classname" "light" "light" "125" "origin" "-8 552 -8" } { "light" "125" "classname" "light" "origin" "-8 216 -8" } { "light" "125" "classname" "light" "origin" "72 216 -8" } { "classname" "light" "light" "125" "origin" "456 216 -8" } { "classname" "light" "light" "125" "origin" "376 216 -8" } { "classname" "light" "light" "135" "origin" "104 392 -8" } { "light" "135" "classname" "light" "origin" "552 392 -8" } { "light" "135" "classname" "light" "origin" "456 392 -8" } { "light" "135" "classname" "light" "origin" "360 392 -8" } { "light" "160" "classname" "light" "origin" "224 392 -8" } { "light" "125" "classname" "light" "origin" "456 552 -8" } { "light" "125" "classname" "light" "origin" "376 552 -8" } { "light" "135" "classname" "light" "origin" "24 392 -8" } { "light" "135" "classname" "light" "origin" "-72 392 -8" } { "light" "135" "classname" "light" "origin" "-152 392 -8" } { "_color" "1.000000 0.274510 0.035294" "origin" "-636 -640 808" "classname" "light" "light" "120" } { "_color" "1.000000 0.274510 0.035294" "origin" "-508 -640 808" "classname" "light" "light" "120" } { "_color" "1.000000 0.274510 0.035294" "origin" "-380 -640 808" "classname" "light" "light" "120" } { "_color" "1.000000 0.517647 0.223529" "origin" "-568 -640 744" "classname" "light" "light" "80" } { "_color" "1.000000 0.517647 0.223529" "origin" "-632 -640 744" "classname" "light" "light" "120" } { "light" "64" "classname" "light" "origin" "-504 -640 744" "_color" "1.000000 0.517647 0.223529" } { "light" "80" "classname" "light" "origin" "-760 -640 712" "_color" "1.000000 0.517647 0.223529" } { "light" "130" "classname" "light" "origin" "-888 -640 680" "_color" "1.000000 0.517647 0.223529" } { "model" "*13" "team" "door1" "lip" "12" "angle" "0" "speed" "100" "wait" "4" "sounds" "0" "classname" "func_door" "light" "64" "_minlight" "0.2" } { "model" "*14" "team" "door1" "lip" "12" "classname" "func_door" "sounds" "2" "wait" "4" "speed" "100" "angle" "180" "light" "64" "_minlight" "0.2" } { "model" "*15" "spawnflags" "2048" "classname" "func_wall" } { "model" "*16" "classname" "func_wall" "spawnflags" "2048" } { "classname" "info_player_intermission" "origin" "-1044 416 256" "angles" "20 135 0" } { "target" "t431" "targetname" "t430" "spawnflags" "2048" "classname" "point_combat" "origin" "-1664 1084 -64" } { "targetname" "t431" "classname" "point_combat" "origin" "-1656 320 140" "spawnflags" "2048" } { "origin" "-1088 -952 -272" "target" "t293" "targetname" "t239" "classname" "path_corner" } { "model" "*17" "spawnflags" "2048" "message" "This item must be activated to use it." "targetname" "t428" "classname" "trigger_once" } { "delay" "0.5" "killtarget" "redkeygate" "spawnflags" "2048" "origin" "-1584 976 184" "targetname" "rusty_gate" "classname" "trigger_relay" } { "model" "*18" "wait" "1" "targetname" "redkeygate" "target" "keyhole1" "spawnflags" "2048" "classname" "trigger_multiple" } { "origin" "-1164 -1172 -96" "light" "110" "classname" "light" "_color" "1.000000 0.880000 0.192000" } { "origin" "-1224 -1120 -104" "light" "80" "classname" "light" "_color" "1.000000 0.880000 0.192000" } { "origin" "-1304 -1120 -104" "light" "120" "classname" "light" "_color" "1.000000 0.880000 0.192000" } { "light" "115" "classname" "light" "origin" "-992 -1648 936" } { "light" "115" "classname" "light" "origin" "-992 -1520 936" } { "spawnflags" "1" "classname" "monster_parasite" "target" "t364" "origin" "-324 -714 -288" } { "model" "*19" "classname" "func_wall" "_minlight" "0.2" "spawnflags" "2048" } { "classname" "misc_teleporter_dest" "angle" "90" "targetname" "t427" "origin" "-1064 230 166" "spawnflags" "1792" } { "classname" "misc_teleporter" "spawnflags" "1792" "angle" "90" "target" "t426" "origin" "-1920 904 180" } { "classname" "misc_teleporter_dest" "origin" "-992 -536 824" "targetname" "t426" "angle" "270" "spawnflags" "1792" } { "classname" "misc_teleporter_dest" "origin" "-160 -536 824" "targetname" "t426" "angle" "270" "spawnflags" "1792" } { "classname" "misc_teleporter" "origin" "-384 -928 896" "target" "t427" "spawnflags" "1792" } { "classname" "target_secret" "targetname" "t425" "spawnflags" "2048" "origin" "-968 -1120 -248" } { "model" "*20" "classname" "trigger_once" "target" "t425" "message" "You have found a secret area." "spawnflags" "2048" } { "_color" "1.000000 0.929412 0.435294" "light" "90" "classname" "light" "origin" "-720 -1424 960" } { "_color" "1.000000 0.929412 0.435294" "light" "90" "classname" "light" "origin" "-760 -1424 960" } { "_color" "1.000000 0.929412 0.435294" "light" "90" "classname" "light" "origin" "-752 -1384 960" } { "_color" "1.000000 0.929412 0.435294" "light" "90" "classname" "light" "origin" "-736 -1408 960" } { "_color" "1.000000 0.929412 0.435294" "light" "90" "classname" "light" "origin" "-672 -1408 960" } { "spawnflags" "1792" "classname" "ammo_slugs" "team" "ammoland" "origin" "-1360 712 184" } { "spawnflags" "1792" "classname" "ammo_rockets" "team" "ammoland" "origin" "-1272 680 184" } { "spawnflags" "1792" "classname" "ammo_grenades" "team" "ammoland" "origin" "-1240 752 184" } { "spawnflags" "1792" "classname" "ammo_cells" "team" "ammoland" "origin" "-1280 824 184" } { "spawnflags" "1792" "classname" "ammo_bullets" "team" "ammoland" "origin" "-1384 824 184" } { "spawnflags" "1792" "classname" "ammo_shells" "team" "ammoland" "origin" "-1416 744 184" } { "spawnflags" "1792" "classname" "ammo_shells" "origin" "-1632 1184 -64" } { "classname" "weapon_shotgun" "spawnflags" "1792" "origin" "-1672 1144 -64" } { "classname" "weapon_railgun" "spawnflags" "1792" "origin" "-888 -928 -440" } { "classname" "ammo_cells" "spawnflags" "1792" "origin" "-384 -968 968" } { "spawnflags" "2048" "classname" "item_health_large" "origin" "-232 -920 888" } { "origin" "-1310 328 168" "angle" "180" "classname" "info_player_deathmatch" } { "model" "*21" "_minlight" "0.1" "spawnflags" "1792" "classname" "func_wall" } { "classname" "ammo_bullets" "spawnflags" "1792" "origin" "-1400 440 -40" } { "classname" "weapon_machinegun" "spawnflags" "1792" "origin" "-1352 440 -40" } { "spawnflags" "2048" "origin" "-928 -392 -176" "classname" "item_armor_shard" } { "classname" "item_armor_shard" "origin" "-888 -392 -176" "spawnflags" "2048" } { "light" "160" "classname" "light" "origin" "-952 -640 212" } { "light" "120" "classname" "light" "origin" "-888 -640 212" } { "classname" "item_armor_shard" "origin" "-1280 -848 0" "spawnflags" "2048" } { "spawnflags" "1792" "origin" "-976 -1152 -256" "classname" "weapon_hyperblaster" } { "spawnflags" "1792" "origin" "-1024 -1152 -256" "classname" "ammo_cells" } { "classname" "item_health_large" "origin" "-972 -1084 -264" } { "origin" "-304 -416 -304" "classname" "ammo_bullets" "spawnflags" "1792" } { "origin" "-304 -368 -304" "classname" "weapon_machinegun" "spawnflags" "1792" } { "origin" "-304 -464 -304" "spawnflags" "1792" "classname" "ammo_bullets" } { "origin" "-512 -256 -698" "_color" "1.000000 0.796078 0.003922" "classname" "light" "light" "64" } { "origin" "-448 -256 -698" "_color" "1.000000 0.796078 0.003922" "light" "64" "classname" "light" } { "classname" "item_armor_shard" "origin" "104 -588 -1000" "spawnflags" "2048" } { "classname" "item_armor_shard" "origin" "-944 -1856 888" "spawnflags" "2048" } { "origin" "-56 -1808 872" "angle" "45" "classname" "info_player_deathmatch" } { "spawnflags" "1" "item" "ammo_bullets" "classname" "monster_infantry" "angle" "270" "origin" "-897 -485 190" } { "spawnflags" "1792" "origin" "-1020 -1084 -264" "classname" "ammo_rockets" } { "model" "*22" "_minlight" "0.8" "target" "wallramp" "lip" "8" "angle" "0" "classname" "func_button" } { "model" "*23" "spawnflags" "1792" "classname" "func_wall" } { "model" "*24" "sounds" "2" "wait" "5" "spawnflags" "2049" "height" "684" "speed" "250" "classname" "func_plat" } { "origin" "-480 -280 -792" "classname" "weapon_supershotgun" "spawnflags" "1792" } { "origin" "-480 -232 -792" "angle" "270" "classname" "info_player_deathmatch" } { "model" "*25" "spawnflags" "2048" "classname" "func_wall" } { "origin" "-896 -896 -144" "spawnflags" "1792" "classname" "weapon_shotgun" } { "spawnflags" "1792" "origin" "-1152 560 -128" "classname" "ammo_grenades" } { "origin" "-1568 768 -144" "classname" "ammo_slugs" "spawnflags" "1792" } { "spawnflags" "1792" "origin" "-1088 896 -144" "classname" "ammo_cells" } { "model" "*26" "spawnflags" "1792" "classname" "func_wall" } { "origin" "-1056 -532 96" "angle" "270" "classname" "info_player_deathmatch" } { "classname" "light" "light" "100" "spawnflags" "1792" "origin" "-1864 -640 728" } { "model" "*27" "classname" "func_wall" "spawnflags" "2048" } { "classname" "item_quad" "origin" "-1864 -640 680" "spawnflags" "1792" } { "spawnflags" "2048" "classname" "item_silencer" "origin" "-216 -1112 952" } { "spawnflags" "2048" "classname" "weapon_machinegun" "origin" "-320 -976 888" } { "classname" "weapon_shotgun" "spawnflags" "1792" "origin" "-640 -1448 1168" } { "classname" "item_armor_combat" "origin" "-1008 -1840 944" "spawnflags" "1792" } { "classname" "item_armor_jacket" "spawnflags" "1792" "origin" "-640 -648 -1008" } { "classname" "info_player_deathmatch" "origin" "-313 -1245 -312" "angle" "180" } { "classname" "info_player_deathmatch" "angle" "135" "origin" "-624 -1144 -24" } { "classname" "info_player_deathmatch" "angle" "270" "origin" "-1416 -896 -40" } { "origin" "-1520 1060 144" "spawnflags" "1792" "classname" "item_armor_combat" } { "model" "*28" "_minlight" "0.1" "spawnflags" "1792" "classname" "func_wall" } { "origin" "-1224 640 -64" "classname" "info_player_deathmatch" "angle" "315" } { "origin" "-1232 880 -64" "classname" "info_player_deathmatch" "angle" "45" } { "origin" "-1520 1144 144" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "-980 284 68" "spawnflags" "1792" "classname" "ammo_rockets" } { "origin" "-1304 440 -40" "spawnflags" "1792" "classname" "ammo_bullets" } { "origin" "-912 716 -24" "angle" "135" "classname" "info_player_deathmatch" } { "origin" "-1944 744 184" "classname" "ammo_shells" "spawnflags" "2048" } { "spawnflags" "2048" "origin" "-1328 772 -60" "classname" "ammo_shells" } { "team" "dm1" "origin" "-1340 836 32" "spawnflags" "1792" "classname" "item_health_mega" } { "origin" "-840 -436 184" "classname" "info_player_deathmatch" "angle" "180" } { "origin" "-680 -800 -24" "spawnflags" "1792" "classname" "weapon_machinegun" } { "origin" "-1920 792 236" "spawnflags" "1792" "classname" "weapon_rocketlauncher" } { "model" "*29" "spawnflags" "1792" "speed" "200" "lip" "24" "classname" "func_plat" } { "model" "*30" "spawnflags" "2048" "classname" "func_wall" } { "origin" "-232 -920 928" "classname" "item_power_shield" "spawnflags" "1792" } { "origin" "-32 -1272 984" "spawnflags" "1792" "classname" "item_invulnerability" } { "model" "*31" "spawnflags" "2048" "classname" "func_wall" } { "origin" "-888 -256 -160" "spawnflags" "1792" "classname" "weapon_chaingun" } { "model" "*32" "spawnflags" "2048" "classname" "func_wall" } { "spawnflags" "1792" "origin" "-1056 -272 88" "target" "BLR" "classname" "trigger_always" } { "spawnflags" "1792" "origin" "-1520 904 160" "target" "rusty_gate" "classname" "trigger_always" } { "origin" "-648 -1384 1148" "angle" "180" "classname" "info_player_deathmatch" } { "model" "*33" "spawnflags" "2048" "classname" "func_wall" } { "classname" "ammo_bullets" "spawnflags" "1792" "origin" "-848 -392 -176" } { "classname" "weapon_grenadelauncher" "spawnflags" "1792" "origin" "-1216 -928 96" } { "classname" "ammo_cells" "origin" "168 -712 112" "spawnflags" "1792" } { "classname" "weapon_bfg" "spawnflags" "1792" "origin" "152 -560 112" } { "classname" "info_player_deathmatch" "angle" "180" "origin" "238 -638 112" } { "origin" "-328 -1084 920" "classname" "light" "light" "120" } { "origin" "-328 -1164 920" "classname" "light" "light" "120" } { "origin" "-400 -1164 920" "classname" "light" "light" "110" } { "light" "100" "classname" "light" "origin" "-512 -1076 928" } { "light" "135" "classname" "light" "origin" "-656 -844 984" } { "origin" "-544 -908 984" "classname" "light" "light" "110" } { "_color" "1.000000 0.388235 0.239216" "light" "80" "classname" "light" "origin" "-480 -1204 888" } { "origin" "-352 -1204 888" "classname" "light" "light" "80" "_color" "1.000000 0.156863 0.125490" } { "origin" "-408 -988 888" "classname" "light" "light" "100" "_color" "1.000000 0.388235 0.239216" } { "origin" "-568 -1116 888" "classname" "light" "light" "80" "_color" "1.000000 0.388235 0.239216" } { "origin" "-608 -1004 888" "classname" "light" "light" "100" "_color" "1.000000 0.388235 0.239216" } { "_color" "1.000000 0.274510 0.035294" "origin" "196 -544 128" "classname" "light" "light" "105" } { "origin" "196 -648 128" "classname" "light" "light" "105" "_color" "1.000000 0.274510 0.035294" } { "_color" "1.000000 0.262745 0.243137" "origin" "148 -640 64" "classname" "light" "light" "105" } { "model" "*34" "sounds" "2" "lip" "0" "spawnflags" "0" "wait" "1" "_minlight" "0.2" "speed" "150" "classname" "func_plat" } { "spawnflags" "2048" "origin" "-1344 756 -84" "targetname" "t424" "classname" "target_secret" } { "model" "*35" "spawnflags" "2048" "message" "You have found a secret area." "target" "t424" "classname" "trigger_once" } { "spawnflags" "2" "origin" "-978 -894 -14" "targetname" "Response_troops" "classname" "monster_flyer" "angle" "0" } { "spawnflags" "2" "origin" "-1026 -922 -14" "targetname" "Response_troops" "classname" "monster_flyer" "angle" "225" } { "spawnflags" "2" "origin" "-1022 -874 -14" "targetname" "Response_troops" "angle" "135" "classname" "monster_flyer" } { "spawnflags" "2" "origin" "-1136 548 124" "targetname" "Response_troops" "angle" "180" "classname" "monster_flyer" } { "spawnflags" "2" "origin" "-1324 1124 124" "targetname" "Response_troops" "angle" "180" "classname" "monster_flyer" } { "spawnflags" "2" "origin" "-1520 536 116" "targetname" "Response_troops" "angle" "180" "classname" "monster_flyer" } { "spawnflags" "2" "origin" "-640 -1352 1156" "targetname" "Response_troops" "classname" "monster_flyer" "angle" "180" } { "spawnflags" "2" "origin" "-640 -1308 1160" "targetname" "Response_troops" "angle" "180" "classname" "monster_flyer" } { "spawnflags" "2" "origin" "-640 -1444 1156" "targetname" "Response_troops" "angle" "180" "classname" "monster_flyer" } { "light" "110" "classname" "light" "origin" "-764 -1132 984" } { "light" "110" "classname" "light" "origin" "-752 -908 984" } { "origin" "-656 -908 984" "classname" "light" "light" "110" } { "target" "monster_soldier" "origin" "-744 -1112 900" "angle" "90" "targetname" "goons2" "classname" "target_spawner" } { "target" "monster_soldier" "origin" "-788 -1080 904" "angle" "90" "targetname" "goons2" "classname" "target_spawner" } { "light" "200" "classname" "light_mine2" "angle" "270" "origin" "-1343 366 244" } { "angle" "180" "light" "200" "classname" "light_mine2" "origin" "-1222 258 316" } { "light" "200" "classname" "light_mine2" "angle" "0" "origin" "-1466 257 316" } { "light" "200" "classname" "light_mine2" "angle" "270" "origin" "-1283 366 244" } { "spawnflags" "2048" "classname" "trigger_relay" "origin" "-926 -239 93" "target" "BLRguns" "targetname" "t423" } { "spawnflags" "2048" "classname" "target_help" "targetname" "t422" "message" "Locate a powerful weapon." "origin" "-216 -1832 896" } { "model" "*36" "spawnflags" "2048" "classname" "trigger_once" "target" "t422" } { "spawnflags" "1792" "classname" "item_armor_body" "origin" "-96 -1952 904" } { "classname" "light" "light" "90" "origin" "-1048 -1664 960" "_color" "1.000000 0.517647 0.223529" } { "classname" "light" "light" "90" "origin" "-1048 -1792 960" "_color" "1.000000 0.517647 0.223529" } { "classname" "info_notnull" "targetname" "t419" "origin" "-728 -1416 884" } { "classname" "info_notnull" "targetname" "t421" "origin" "-728 -1392 884" } { "classname" "info_notnull" "targetname" "t420" "origin" "-752 -1392 884" } { "classname" "info_notnull" "targetname" "t418" "origin" "-752 -1416 884" } { "_color" "1.000000 0.262745 0.243137" "origin" "98 -574 -832" "light" "120" "classname" "light" } { "light" "120" "classname" "light" "origin" "148 -640 -832" "_color" "1.000000 0.262745 0.243137" } { "_color" "1.000000 0.262745 0.243137" "origin" "96 -708 -832" "classname" "light" "light" "120" } { "spawnflags" "2048" "origin" "-324 -360 -184" "targetname" "t417" "target" "t345" "classname" "trigger_relay" } { "spawnflags" "2048" "origin" "-308 -384 -184" "killtarget" "lsecret" "targetname" "t416" "classname" "trigger_relay" } { "spawnflags" "2048" "origin" "-324 -384 -184" "target" "t345" "targetname" "t416" "classname" "trigger_relay" } { "spawnflags" "2048" "origin" "-308 -360 -184" "targetname" "t417" "killtarget" "hsecret" "classname" "trigger_relay" } { "origin" "-392 -976 992" "classname" "weapon_supershotgun" "spawnflags" "1792" } { "origin" "-296 -912 872" "spawnflags" "2064" "angle" "270" "classname" "misc_deadsoldier" } { "spawnflags" "2048" "origin" "-312 -984 976" "targetname" "t415" "classname" "target_secret" } { "model" "*37" "spawnflags" "2048" "message" "You have found a secret." "target" "t415" "classname" "trigger_once" } { "origin" "-1320 -848 -8" "classname" "item_armor_shard" } { "classname" "item_health_small" "origin" "104 -632 -1000" } { "origin" "-492 -508 -972" "spawnflags" "1" "classname" "monster_flipper" "angle" "270" } { "origin" "-448 -484 -972" "spawnflags" "1" "angle" "270" "classname" "monster_flipper" } { "origin" "-832 -244 64" "classname" "item_armor_shard" } { "origin" "-876 -212 64" "classname" "item_armor_shard" } { "origin" "-872 -260 48" "spawnflags" "2050" "classname" "misc_deadsoldier" } { "origin" "0 -1764 864" "classname" "item_armor_shard" } { "team" "dm1" "target" "t428" "spawnflags" "2048" "classname" "item_invulnerability" "origin" "-1340 836 -68" } { "origin" "-1404 984 184" "classname" "path_corner" "targetname" "t392" "target" "t393" "spawnflags" "2048" } { "origin" "-1584 272 184" "classname" "path_corner" "target" "t386" "targetname" "t400" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1640 376 184" "targetname" "t399" "target" "t400" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1600 544 184" "targetname" "t395" "target" "t396" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1464 592 184" "targetname" "t398" "target" "t399" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1448 504 184" "targetname" "t397" "target" "t398" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1520 496 184" "targetname" "t396" "target" "t397" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1616 696 184" "targetname" "t394" "target" "t395" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1536 1000 184" "targetname" "t393" "target" "t394" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1212 880 184" "targetname" "t391" "target" "t392" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1104 792 184" "targetname" "t390" "target" "t391" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1112 640 184" "targetname" "t389" "target" "t390" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1056 424 184" "targetname" "t388" "target" "t389" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1088 256 184" "targetname" "t387" "target" "t388" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1392 272 184" "targetname" "t386" "target" "t387" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1056 944 88" "targetname" "t385" "target" "t313" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1080 664 88" "targetname" "t384" "target" "t385" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1176 520 88" "targetname" "t383" "target" "t384" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1512 528 88" "targetname" "t382" "target" "t383" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1592 720 88" "targetname" "t381" "target" "t382" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1568 952 88" "targetname" "t380" "target" "t381" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1432 1032 88" "targetname" "t305" "target" "t380" "spawnflags" "2048" } { "origin" "-1000 -648 -360" "noise" "world/curnt2.wav" "spawnflags" "1" "classname" "target_speaker" } { "spawnflags" "2048" "origin" "-906 -275 81" "target" "BLR" "targetname" "t379" "classname" "trigger_relay" "delay" "1" } { "origin" "-926 -218 97" "targetname" "t379" "delay" "1" "target" "BLRguns" "classname" "trigger_relay" } { "spawnflags" "2048" "origin" "-679 -800 -62" "classname" "item_health" } { "origin" "-1256 -936 64" "targetname" "t378" "classname" "target_goal" "spawnflags" "2048" } { "model" "*38" "target" "t377" "classname" "trigger_once" } { "origin" "-676 -1116 -36" "targetname" "t376" "classname" "point_combat" "spawnflags" "2048" } { "spawnflags" "2048" "origin" "-1408 704 -72" "classname" "item_health_large" } { "spawnflags" "2048" "origin" "-1292 676 -72" "classname" "item_health_large" } { "angle" "90" "targetname" "t347" "origin" "-1312 752 -144" "classname" "monster_flipper" } { "angle" "90" "targetname" "t347" "origin" "-1372 784 -152" "classname" "monster_flipper" } { "spawnflags" "2048" "origin" "-1300 836 -68" "classname" "item_armor_jacket" } { "classname" "ammo_bullets" "origin" "-928 -392 -112" "spawnflags" "1792" } { "origin" "-888 -320 -152" "noise" "world/curnt2.wav" "spawnflags" "1" "classname" "target_speaker" } { "classname" "target_speaker" "spawnflags" "1" "origin" "-1536 552 -88" "noise" "world/curnt2.wav" } { "classname" "target_speaker" "spawnflags" "1" "origin" "-1568 736 -88" "noise" "world/curnt2.wav" } { "classname" "target_speaker" "spawnflags" "1" "origin" "-1496 952 -88" "noise" "world/curnt2.wav" } { "classname" "target_speaker" "spawnflags" "1" "origin" "-1280 984 -88" "noise" "world/curnt2.wav" } { "classname" "target_speaker" "spawnflags" "1" "origin" "-1096 928 -88" "noise" "world/curnt2.wav" } { "classname" "target_speaker" "spawnflags" "1" "origin" "-1056 736 -88" "noise" "world/curnt2.wav" } { "classname" "target_speaker" "spawnflags" "1" "origin" "-1136 520 -88" "noise" "world/curnt2.wav" } { "noise" "world/curnt2.wav" "origin" "-1344 536 -88" "spawnflags" "1" "classname" "target_speaker" } { "spawnflags" "2560" "origin" "-1288 440 -72" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "-1328 440 -72" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "-1368 440 -72" "classname" "item_health_small" } { "spawnflags" "2560" "origin" "-1408 440 -72" "classname" "item_health_small" } { "origin" "-1336 776 216" "target" "t261" "classname" "monster_infantry" "spawnflags" "0" } { "item" "ammo_bullets" "target" "t258" "origin" "-1336 720 216" "classname" "monster_infantry" "spawnflags" "0" } { "classname" "light" "light" "120" "origin" "-1361 784 -50" "_color" "0.465649 1.000000 0.671756" } { "classname" "light" "light" "80" "origin" "-1368 784 -152" "_color" "0.465649 1.000000 0.671756" } { "light" "80" "classname" "light" "origin" "-1304 784 -152" "_color" "0.465649 1.000000 0.671756" } { "light" "80" "classname" "light" "origin" "-1304 720 -152" "_color" "0.465649 1.000000 0.671756" } { "origin" "-1432 -536 256" "classname" "target_speaker" "noise" "world/short1.wav" "spawnflags" "1" } { "origin" "-1448 -648 256" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" } { "origin" "-61 -1240 944" "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-45 -1976 944" "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" } { "noise" "world/short1.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "35 -1784 944" } { "noise" "world/amb7.wav" "spawnflags" "1" "classname" "target_speaker" "origin" "40 -1776 928" } { "classname" "target_speaker" "spawnflags" "1" "noise" "world/amb7.wav" "origin" "35 -1416 944" } { "targetname" "Response_troops" "origin" "-280 -1422 888" "spawnflags" "3" "angle" "180" "classname" "monster_infantry" } { "origin" "-1916 900 204" "message" "Resume previous mission." "targetname" "Response_troops" "classname" "target_help" } { "origin" "-1888 812 188" "targetname" "Response_troops" "classname" "target_goal" } { "model" "*39" "target" "t372" "classname" "trigger_once" "spawnflags" "2048" } { "targetname" "t370" "classname" "point_combat" "origin" "-1672 320 140" "spawnflags" "2048" } { "target" "t430" "targetname" "t372" "origin" "-1516 1148 -52" "spawnflags" "0" "angle" "270" "classname" "monster_soldier_light" } { "targetname" "t372" "target" "t370" "origin" "-1660 924 0" "spawnflags" "0" "angle" "135" "classname" "monster_soldier_light" } { "targetname" "t372" "origin" "-1332 1168 -52" "spawnflags" "0" "angle" "225" "classname" "monster_soldier_light" } { "angle" "270" "spawnflags" "769" "classname" "monster_soldier" "origin" "-1316 -232 184" } { "origin" "-884 -236 -176" "target" "t269" "classname" "item_adrenaline" "spawnflags" "2048" } { "origin" "-1216 -928 4" "targetname" "t369" "classname" "info_notnull" "spawnflags" "2048" } { "origin" "-1216 -928 80" "_cone" "30" "light" "200" "target" "t369" "classname" "light" "spawnflags" "2048" } { "target" "t378" "origin" "-1216 -928 56" "classname" "key_red_key" "spawnflags" "2048" } { "classname" "monster_soldier_light" "angle" "270" "spawnflags" "0" "origin" "-744 -1320 896" } { "spawnflags" "1792" "classname" "ammo_cells" "origin" "-1944 744 224" } { "spawnflags" "1536" "classname" "item_health_large" "origin" "-1904 704 176" } { "spawnflags" "2048" "classname" "item_health_large" "origin" "-1944 704 176" } { "origin" "-1848 656 172" "_color" "1.000000 0.858268 0.279528" "light" "100" "classname" "light" } { "origin" "-1904 656 172" "classname" "light" "light" "100" "_color" "1.000000 0.858268 0.279528" } { "classname" "item_armor_shard" "origin" "-1376 -200 168" } { "classname" "item_armor_shard" "origin" "-1384 16 168" } { "classname" "item_armor_shard" "origin" "-1376 -384 168" } { "classname" "path_corner" "targetname" "t365" "origin" "-332 -600 -304" "target" "t364" } { "classname" "path_corner" "targetname" "t364" "target" "t365" "origin" "-332 -904 -312" } { "origin" "-768 -1120 -464" "classname" "light" "light" "90" "_color" "1.000000 0.432000 0.268000" } { "origin" "-768 -1056 -464" "classname" "light" "light" "90" "_color" "1.000000 0.432000 0.268000" } { "origin" "-768 -992 -464" "classname" "light" "light" "90" "_color" "1.000000 0.432000 0.268000" } { "origin" "-768 -928 -464" "classname" "light" "light" "90" "_color" "1.000000 0.432000 0.268000" } { "origin" "-768 -864 -464" "classname" "light" "light" "90" "_color" "1.000000 0.432000 0.268000" } { "origin" "-768 -800 -464" "classname" "light" "light" "90" "_color" "1.000000 0.432000 0.268000" } { "_color" "1.000000 0.432000 0.268000" "light" "90" "classname" "light" "origin" "-768 -736 -464" } { "origin" "-792 -640 -464" "light" "90" "classname" "light" "_color" "1.000000 0.517647 0.223529" } { "origin" "-768 -1184 -464" "classname" "light" "light" "90" "_color" "1.000000 0.432000 0.268000" } { "origin" "-768 -1248 -464" "classname" "light" "light" "75" "_color" "1.000000 0.432000 0.268000" } { "spawnflags" "2048" "classname" "item_health" "origin" "-1024 -636 -228" } { "style" "2" "classname" "func_areaportal" "targetname" "t356" } { "style" "3" "classname" "func_areaportal" "targetname" "t355" } { "classname" "trigger_relay" "targetname" "close_out" "target" "t354" "origin" "-88 -664 824" } { "classname" "trigger_relay" "targetname" "open_in" "target" "t353" "origin" "-1768 -696 824" } { "origin" "-760 -680 888" "classname" "trigger_relay" "targetname" "close_in" "delay" "4" "target" "t354" } { "classname" "trigger_relay" "targetname" "close_in" "target" "t353" "origin" "-744 -664 824" } { "origin" "-848 -760 832" "classname" "path_corner" "targetname" "t151" "target" "t352" "pathtarget" "close_in" } { "model" "*40" "sounds" "4" "angle" "270" "classname" "func_door" "team" "in" "targetname" "t353" "spawnflags" "44" "speed" "100" "target" "t355" "_minlight" "0.2" } { "model" "*41" "angle" "90" "classname" "func_door" "team" "in" "targetname" "t353" "spawnflags" "44" "speed" "100" "target" "t355" "_minlight" "0.2" } { "model" "*42" "sounds" "4" "classname" "func_door" "angle" "270" "team" "out" "targetname" "t354" "target" "t356" "spawnflags" "32" } { "model" "*43" "classname" "func_door" "angle" "90" "team" "out" "targetname" "t354" "target" "t356" "spawnflags" "32" } { "classname" "path_corner" "origin" "-292 -1399 867" "targetname" "t351" "target" "t244" } { "origin" "-225 -1433 867" "classname" "path_corner" "targetname" "t350" "target" "t351" } { "origin" "-632 -1456 864" "classname" "path_corner" "targetname" "t349" "target" "t244" } { "classname" "item_health_small" "origin" "-746 -560 88" } { "classname" "item_health_small" "origin" "-712 -560 88" } { "classname" "item_health_small" "origin" "-780 -560 88" } { "style" "4" "classname" "func_areaportal" "targetname" "t348" } { "model" "*44" "classname" "func_door" "angle" "90" "team" "sgundoor" "speed" "100" "sounds" "2" "wait" "3" "spawnflags" "12" "target" "t348" "_minlight" "0.2" } { "model" "*45" "classname" "func_door" "angle" "270" "team" "sgundoor" "speed" "100" "sounds" "2" "wait" "3" "spawnflags" "12" "target" "t348" "_minlight" "0.2" } { "model" "*46" "wait" "-1" "health" "5" "classname" "func_door" "angle" "-2" "spawnflags" "2056" "speed" "50" "sounds" "2" "lip" "12" "_minlight" "0.2" "target" "t347" } { "origin" "-1348 108 152" "classname" "path_corner" "target" "t346" "targetname" "t254" } { "classname" "monster_infantry" "target" "t266" "angle" "180" "origin" "-1328 248 172" "spawnflags" "1" } { "origin" "-976 -1008 8" "light" "100" "classname" "light" } { "origin" "-1016 -640 212" "classname" "light" "light" "160" } { "origin" "-1064 -640 212" "light" "120" "classname" "light" } { "origin" "-296 -888 -292" "classname" "item_health" } { "spawnflags" "1536" "origin" "-292 -620 -292" "classname" "item_health" } { "origin" "-292 -580 -292" "classname" "item_health" } { "message" "You have found a secret area." "spawnflags" "2048" "origin" "-308 -408 -184" "targetname" "t345" "classname" "target_secret" } { "model" "*47" "spawnflags" "2048" "targetname" "hsecret" "target" "t416" "message" "You have found a secret area." "classname" "trigger_once" } { "spawnflags" "2048" "origin" "-864 -360 -172" "targetname" "t343" "classname" "target_secret" } { "model" "*48" "spawnflags" "2048" "message" "You have found a secret area." "target" "t343" "classname" "trigger_once" } { "style" "5" "targetname" "t342" "classname" "func_areaportal" } { "model" "*49" "_minlight" "0.2" "sounds" "0" "team" "door4" "wait" "4" "speed" "100" "angle" "90" "classname" "func_door" "lip" "12" } { "model" "*50" "_minlight" "0.2" "classname" "func_door" "angle" "270" "speed" "100" "wait" "4" "team" "door4" "sounds" "2" "lip" "12" } { "spawnflags" "2048" "origin" "-760 -888 952" "classname" "ammo_bullets" } { "spawnflags" "1792" "origin" "-944 -1856 928" "classname" "ammo_rockets" } { "spawnflags" "2048" "origin" "-952 -1408 916" "message" "You have found a secret area." "targetname" "t341" "classname" "target_secret" } { "model" "*51" "spawnflags" "2048" "target" "t341" "classname" "trigger_once" } { "origin" "100 -676 -1000" "classname" "item_health_small" } { "origin" "62 -632 -1000" "classname" "ammo_grenades" } { "spawnflags" "1792" "origin" "104 -588 -952" "classname" "ammo_rockets" } { "spawnflags" "1024" "origin" "-456 -636 -1008" "classname" "ammo_bullets" } { "origin" "-384 -576 -828" "_minlight" "0.2" "spawnflags" "2080" "classname" "misc_deadsoldier" } { "origin" "0 -1648 864" "classname" "ammo_shells" } { "origin" "16 -1696 848" "angle" "180" "spawnflags" "2064" "classname" "misc_deadsoldier" } { "origin" "-496 -1088 -288" "classname" "ammo_bullets" } { "origin" "-258 -992 -234" "angle" "180" "classname" "light_mine2" } { "origin" "-258 -544 -234" "classname" "light_mine2" "angle" "180" } { "origin" "-258 -1184 -234" "angle" "180" "classname" "light_mine2" } { "light" "200" "angle" "0" "classname" "light_mine2" "origin" "-1470 -1088 -190" } { "light" "175" "angle" "0" "classname" "light_mine2" "origin" "-1470 -1088 2" } { "light" "175" "origin" "-1370 -1182 2" "classname" "light_mine2" } { "light" "175" "angle" "0" "origin" "-1470 -960 2" "classname" "light_mine2" } { "light" "200" "angle" "90" "origin" "-1370 -1182 -190" "classname" "light_mine2" } { "light" "170" "angle" "90" "origin" "-810 -1182 -4" "classname" "light_mine2" } { "light" "170" "angle" "90" "origin" "-682 -1182 28" "classname" "light_mine2" } { "light" "175" "angle" "180" "origin" "-578 -1120 28" "classname" "light_mine2" } { "light" "175" "angle" "180" "origin" "-578 -960 116" "classname" "light_mine2" } { "light" "175" "angle" "180" "origin" "-578 -832 188" "classname" "light_mine2" } { "light" "200" "angle" "0" "origin" "-1470 -960 -190" "classname" "light_mine2" } { "origin" "-864 -928 -440" "light" "64" "classname" "light" } { "classname" "item_armor_shard" "origin" "-1364 -848 -22" "spawnflags" "1024" } { "model" "*52" "targetname" "rusty_gate" "spawnflags" "2051" "classname" "func_wall" } { "origin" "-1876 740 176" "targetname" "t337" "classname" "point_combat" } { "origin" "-1872 872 192" "target" "t337" "spawnflags" "3" "angle" "270" "targetname" "rusty_gate" "classname" "monster_infantry" } { "origin" "-1952 812 192" "spawnflags" "2" "angle" "315" "targetname" "rusty_gate" "classname" "monster_infantry" } { "origin" "-1920 856 160" "targetname" "t336" "classname" "info_notnull" } { "origin" "-1920 856 268" "_cone" "25" "target" "t336" "classname" "light" } { "light" "150" "origin" "-1336 612 280" "_color" "0.000000 1.000000 1.000000" "classname" "light" } { "light" "150" "origin" "-1464 756 264" "_color" "0.000000 1.000000 1.000000" "classname" "light" } { "light" "150" "origin" "-1336 868 272" "_color" "0.000000 1.000000 1.000000" "classname" "light" } { "light" "150" "origin" "-1208 756 272" "_color" "0.000000 1.000000 1.000000" "classname" "light" } { "light" "200" "_cone" "15" "origin" "-1376 752 276" "target" "t334" "_color" "0.343874 0.707510 1.000000" "classname" "light" } { "origin" "-1080 -208 64" "classname" "item_health" } { "origin" "-1048 -1152 960" "light" "90" "classname" "light" "_color" "1.000000 0.517647 0.223529" } { "classname" "light" "light" "90" "origin" "-1048 -1280 960" "_color" "1.000000 0.517647 0.223529" } { "classname" "light" "light" "125" "origin" "-64 -1664 984" } { "classname" "light" "light" "125" "origin" "-48 -1536 984" } { "light" "175\" "classname" "light" "origin" "-1656 -640 944" "angle" "-2" "_color" "1.000000 0.274510 0.035294" } { "light" "175\" "classname" "light" "origin" "-1528 -640 944" "angle" "-2" "_color" "1.000000 0.274510 0.035294" } { "_color" "1.000000 0.274510 0.035294" "classname" "light" "light" "150" "origin" "-896 -632 -384" } { "_color" "1.000000 0.274510 0.035294" "light" "150" "classname" "light" "origin" "-1176 -640 -208" } { "_color" "1.000000 0.274510 0.035294" "light" "175" "classname" "light" "origin" "-920 -632 -328" } { "_color" "1.000000 0.274510 0.035294" "classname" "light" "light" "175" "origin" "-1560 -640 -208" } { "_color" "1.000000 0.274510 0.035294" "light" "175" "classname" "light" "origin" "-1432 -640 -208" } { "_color" "1.000000 0.274510 0.035294" "classname" "light" "light" "175" "origin" "-1304 -640 -208" } { "_color" "1.000000 0.274510 0.035294" "light" "175" "classname" "light" "origin" "-1176 -640 -208" } { "_color" "1.000000 0.274510 0.035294" "light" "130" "classname" "light" "origin" "-1432 -640 -336" } { "_color" "1.000000 0.274510 0.035294" "classname" "light" "light" "130" "origin" "-1304 -640 -336" } { "_color" "1.000000 0.274510 0.035294" "light" "130" "classname" "light" "origin" "-1176 -640 -336" } { "light" "105" "classname" "light" "origin" "-596 -640 -664" "_color" "1.000000 0.274510 0.035294" } { "light" "105" "classname" "light" "origin" "-508 -640 -664" "_color" "1.000000 0.274510 0.035294" } { "light" "105" "classname" "light" "origin" "-124 -640 -664" "_color" "1.000000 0.274510 0.035294" } { "light" "105" "classname" "light" "origin" "-252 -640 -664" "_color" "1.000000 0.274510 0.035294" } { "origin" "-184 -640 936" "classname" "light" "light" "150" "_color" "1.000000 0.274510 0.035294" } { "_color" "1.000000 0.274510 0.035294" "origin" "-380 -640 936" "classname" "light" "light" "175" } { "light" "105" "classname" "light" "origin" "-380 -640 -664" "_color" "1.000000 0.274510 0.035294" } { "origin" "-504 -640 944" "classname" "light" "light" "175" "_color" "1.000000 0.274510 0.035294" } { "origin" "-632 -640 944" "classname" "light" "light" "175" "_color" "1.000000 0.274510 0.035294" } { "origin" "-760 -640 944" "classname" "light" "light" "175" "_color" "1.000000 0.274510 0.035294" } { "origin" "-888 -640 944" "classname" "light" "light" "175" "_color" "1.000000 0.274510 0.035294" } { "light" "64" "classname" "light" "origin" "-748 -640 808" "_color" "1.000000 0.274510 0.035294" } { "origin" "-184 -640 808" "classname" "light" "light" "120" "_color" "1.000000 0.274510 0.035294" } { "origin" "-888 -640 808" "classname" "light" "light" "120" "_color" "1.000000 0.274510 0.035294" } { "light" "120" "classname" "light" "origin" "-1020 -640 808" "_color" "1.000000 0.274510 0.035294" } { "light" "120" "classname" "light" "origin" "-1144 -640 808" "_color" "1.000000 0.274510 0.035294" } { "light" "120" "classname" "light" "origin" "-1272 -640 808" "_color" "1.000000 0.274510 0.035294" } { "origin" "-1404 -640 808" "classname" "light" "light" "120" "_color" "1.000000 0.274510 0.035294" } { "_color" "1.000000 0.929412 0.435294" "light" "90" "classname" "light" "origin" "-608 -1408 960" } { "_color" "1.000000 0.929412 0.435294" "light" "90" "classname" "light" "origin" "-544 -1408 960" } { "_color" "1.000000 0.929412 0.435294" "light" "90" "classname" "light" "origin" "-480 -1408 960" } { "_color" "1.000000 0.929412 0.435294" "light" "90" "classname" "light" "origin" "-416 -1408 960" } { "_color" "1.000000 0.929412 0.435294" "light" "90" "classname" "light" "origin" "-352 -1408 960" } { "_color" "1.000000 0.929412 0.435294" "light" "90" "classname" "light" "origin" "-288 -1408 960" } { "_color" "1.000000 0.929412 0.435294" "light" "90" "classname" "light" "origin" "-224 -1408 960" } { "origin" "-720 -1376 960" "classname" "light" "light" "90" "_color" "1.000000 0.929412 0.435294" } { "origin" "-64 -1416 960" "classname" "light" "light" "125" "_color" "1.000000 0.929412 0.435294" } { "origin" "-1288 616 4" "classname" "info_notnull" "targetname" "t320" } { "classname" "info_notnull" "origin" "-1288 888 4" "targetname" "t328" } { "classname" "info_notnull" "origin" "-1384 616 4" "targetname" "t322" } { "classname" "info_notnull" "origin" "-1336 616 4" "targetname" "t321" } { "classname" "info_notnull" "origin" "-1184 664 4" "targetname" "t319" } { "classname" "info_notnull" "origin" "-1384 888 4" "targetname" "t326" } { "classname" "info_notnull" "origin" "-1336 888 4" "targetname" "t327" } { "classname" "info_notnull" "origin" "-1184 840 4" "targetname" "t329" } { "classname" "info_notnull" "origin" "-1472 760 4" "targetname" "t324" } { "classname" "info_notnull" "origin" "-1472 712 4" "targetname" "t323" } { "classname" "info_notnull" "origin" "-1472 808 4" "targetname" "t325" } { "classname" "item_health_small" "origin" "-1384 1200 -16" } { "classname" "item_health_small" "origin" "-880 824 -24" } { "classname" "ammo_bullets" "origin" "-888 784 -16" } { "classname" "item_health_small" "origin" "-1280 1200 -24" } { "origin" "-1224 1032 88" "classname" "path_corner" "target" "t305" "targetname" "t313" "spawnflags" "2048" } { "classname" "monster_flyer" "origin" "-1176 1032 88" "angle" "180" "target" "t313" "spawnflags" "0" } { "target" "t391" "classname" "monster_flyer" "origin" "-1220 968 208" "angle" "180" "spawnflags" "0" } { "origin" "-1380 -316 184" "classname" "monster_soldier" "spawnflags" "257" "angle" "270" } { "spawnflags" "1" "classname" "monster_soldier" "angle" "0" "origin" "-1284 -936 12" } { "classname" "path_corner" "origin" "-896 -804 -256" "target" "t294" "targetname" "t295" } { "target" "t295" "origin" "-1080 -796 -272" "classname" "path_corner" "targetname" "t239" } { "angle" "135" "origin" "-968 -936 -240" "classname" "monster_infantry" "spawnflags" "256" "target" "t293" } { "spawnflags" "2048" "classname" "item_health" "origin" "-896 -896 -192" } { "classname" "misc_deadsoldier" "spawnflags" "2080" "origin" "-1760 -592 -368" } { "spawnflags" "2048" "classname" "path_corner" "targetname" "t291" "target" "t292" "origin" "-280 -664 -888" } { "spawnflags" "2048" "classname" "path_corner" "targetname" "t290" "target" "t291" "origin" "-184 -680 -888" } { "spawnflags" "2048" "classname" "path_corner" "targetname" "t289" "target" "t290" "origin" "-200 -616 -888" } { "spawnflags" "2048" "classname" "path_corner" "targetname" "t288" "target" "t289" "origin" "-328 -664 -888" } { "spawnflags" "2048" "classname" "path_corner" "targetname" "t292" "target" "t287" "origin" "-104 -600 -888" } { "spawnflags" "2048" "classname" "path_corner" "origin" "-232 -664 -888" "targetname" "t285" "target" "t286" } { "spawnflags" "2048" "classname" "path_corner" "origin" "-424 -600 -888" "targetname" "t284" "target" "t285" } { "spawnflags" "2048" "classname" "path_corner" "origin" "-600 -648 -888" "targetname" "t284" "target" "t288" } { "spawnflags" "2048" "classname" "path_corner" "origin" "-600 -600 -888" "targetname" "t283" "target" "t284" } { "spawnflags" "2048" "classname" "path_corner" "origin" "-472 -696 -888" "targetname" "t282" "target" "t283" } { "spawnflags" "2048" "classname" "path_corner" "origin" "-296 -616 -888" "targetname" "t281" "target" "t282" } { "spawnflags" "2048" "classname" "path_corner" "origin" "-88 -696 -888" "targetname" "t280" "target" "t281" } { "spawnflags" "2048" "classname" "path_corner" "origin" "40 -632 -888" "targetname" "t279" "target" "t280" } { "spawnflags" "2048" "classname" "path_corner" "origin" "56 -680 -888" "target" "t279" "targetname" "t287" } { "spawnflags" "2048" "classname" "path_corner" "origin" "-56 -600 -888" "targetname" "t286" "target" "t287" } { "classname" "monster_flipper" "origin" "-144 -624 -872" "target" "t290" } { "classname" "monster_flipper" "origin" "-32 -656 -872" "target" "t279" } { "spawnflags" "2048" "origin" "-32 -1272 916" "targetname" "t278" "classname" "info_notnull" } { "style" "32" "spawnflags" "2048" "targetname" "t338" "origin" "-32 -1272 1048" "_cone" "15" "target" "t278" "classname" "light" } { "origin" "-1408 -1088 -56" "targetname" "t224" "target" "t225" "classname" "path_corner" "spawnflags" "2048" } { "classname" "monster_soldier" "target" "t249" "angle" "270" "origin" "-1352 -1056 -4" } { "targetname" "t377" "spawnflags" "1" "target" "t376" "origin" "-612 -880 68" "classname" "monster_soldier_light" "angle" "270" } { "spawnflags" "1792" "origin" "-1280 -848 48" "classname" "ammo_grenades" } { "origin" "-1424 -880 -40" "classname" "item_health" "spawnflags" "2048" } { "spawnflags" "1792" "origin" "-888 -392 -112" "classname" "ammo_bullets" } { "origin" "-848 -232 216" "classname" "weapon_shotgun" "spawnflags" "1792" } { "spawnflags" "1792" "origin" "-848 -288 224" "classname" "ammo_shells" } { "origin" "-1088 -472 152" "targetname" "t277" "classname" "point_combat" } { "origin" "-1232 -856 28" "angle" "0" "classname" "monster_soldier" "spawnflags" "769" } { "_color" "1.000000 0.274510 0.035294" "light" "80" "classname" "light" "origin" "-660 -640 -960" } { "_color" "1.000000 0.274510 0.035294" "origin" "-660 -640 -832" "classname" "light" "light" "80" } { "_color" "1.000000 0.274510 0.035294" "origin" "-660 -640 -704" "classname" "light" "light" "100" } { "_color" "1.000000 0.274510 0.035294" "origin" "-660 -640 -576" "classname" "light" "light" "100" } { "_color" "1.000000 0.274510 0.035294" "light" "100" "classname" "light" "origin" "-660 -640 -448" } { "classname" "ammo_bullets" "origin" "-856 -1856 888" } { "origin" "-896 -1856 888" "classname" "ammo_shells" } { "spawnflags" "1024" "origin" "-480 -1064 904" "classname" "item_health_small" } { "origin" "-480 -1016 904" "classname" "item_health_small" } { "classname" "light" "light" "90" "_color" "1.000000 0.854902 0.278431" "origin" "40 -1408 960" } { "classname" "light" "light" "90" "_color" "1.000000 0.854902 0.278431" "origin" "40 -1408 944" } { "classname" "light" "light" "90" "_color" "1.000000 0.854902 0.278431" "origin" "40 -1408 928" } { "classname" "light" "light" "90" "_color" "1.000000 0.854902 0.278431" "origin" "40 -1408 912" } { "classname" "light" "light" "90" "_color" "1.000000 0.854902 0.278431" "origin" "40 -1792 960" } { "classname" "light" "light" "90" "_color" "1.000000 0.854902 0.278431" "origin" "40 -1792 928" "_style" "10" } { "classname" "light" "light" "90" "_color" "1.000000 0.854902 0.278431" "origin" "40 -1792 912" } { "classname" "light" "light" "150" "_color" "1.000000 0.854902 0.278431" "origin" "-56 -1984 944" } { "classname" "light" "light" "150" "_color" "1.000000 0.854902 0.278431" "origin" "-56 -1984 928" } { "origin" "-56 -1232 944" "_color" "1.000000 0.854902 0.278431" "light" "90" "classname" "light" } { "origin" "-56 -1232 928" "_color" "1.000000 0.854902 0.278431" "light" "90" "classname" "light" } { "origin" "-56 -1232 912" "_color" "1.000000 0.854902 0.278431" "light" "90" "classname" "light" } { "origin" "-56 -1232 960" "_color" "1.000000 0.854902 0.278431" "light" "90" "classname" "light" } { "classname" "ammo_bullets" "origin" "-736 -832 -64" } { "classname" "item_health" "origin" "-596 -788 -64" } { "classname" "ammo_grenades" "origin" "-722 -799 -62" } { "classname" "item_health" "origin" "-816 -816 -464" } { "classname" "item_health" "origin" "-888 -928 -464" "spawnflags" "2048" } { "classname" "ammo_shells" "origin" "-1648 -688 -512" } { "classname" "ammo_bullets" "origin" "-1696 -688 -528" } { "classname" "item_health_small" "origin" "-1792 -672 -528" } { "classname" "item_health" "origin" "-1680 -576 -528" } { "origin" "-1536 -640 -496" "classname" "light" "light" "80" "_color" "1.000000 0.274510 0.035294" } { "origin" "-1664 -640 -528" "classname" "light" "light" "80" "_color" "1.000000 0.274510 0.035294" } { "item" "ammo_bullets" "classname" "monster_infantry" "angle" "90" "targetname" "Response_troops" "spawnflags" "3" "origin" "-66 -1596 884" } { "item" "ammo_bullets" "classname" "monster_infantry" "targetname" "goons1" "angle" "90" "origin" "-504 -1128 904" "spawnflags" "3" } { "classname" "item_health" "origin" "24 -1256 864" } { "classname" "ammo_shells" "origin" "-96 -1256 864" "spawnflags" "0" } { "spawnflags" "1536" "classname" "ammo_shells" "origin" "-104 -1296 864" } { "spawnflags" "2048" "classname" "weapon_shotgun" "origin" "-32 -1272 936" } { "classname" "item_health_small" "origin" "-1024 -1608 888" } { "classname" "item_health_small" "origin" "-1024 -1552 888" } { "classname" "item_armor_shard" "origin" "-1024 -1504 888" } { "classname" "item_armor_shard" "origin" "-1024 -1448 888" } { "classname" "item_armor_shard" "origin" "-1024 -1392 888" } { "spawnflags" "2048" "classname" "item_breather" "origin" "-1004 -1843 902" } { "origin" "-1184 840 -76" "light" "200" "classname" "light" "_cone" "25" "target" "t329" } { "origin" "-1288 888 -76" "light" "200" "classname" "light" "_cone" "25" "target" "t328" } { "origin" "-1336 888 -76" "light" "200" "classname" "light" "_cone" "25" "target" "t327" } { "origin" "-1384 888 -76" "light" "200" "classname" "light" "_cone" "25" "target" "t326" } { "origin" "-1472 760 -76" "classname" "light" "light" "200" "_cone" "25" "target" "t324" } { "origin" "-1472 808 -76" "classname" "light" "light" "200" "_cone" "25" "target" "t325" } { "origin" "-1384 616 -76" "classname" "light" "light" "200" "_cone" "25" "target" "t322" } { "origin" "-1336 616 -76" "classname" "light" "light" "200" "_cone" "25" "target" "t321" } { "origin" "-1288 616 -76" "classname" "light" "light" "200" "_cone" "25" "target" "t320" } { "origin" "-1184 664 -76" "classname" "light" "light" "200" "_cone" "25" "target" "t319" } { "origin" "-1472 712 -76" "light" "200" "classname" "light" "_cone" "25" "target" "t323" } { "spawnflags" "2048" "origin" "-1136 -256 56" "targetname" "BL6" "sounds" "1" "classname" "target_splash" } { "spawnflags" "2048" "origin" "-1136 -256 72" "targetname" "BL5" "sounds" "1" "classname" "target_splash" } { "spawnflags" "2048" "origin" "-1136 -224 72" "targetname" "BL4" "sounds" "1" "classname" "target_splash" } { "spawnflags" "2048" "origin" "-1136 -224 56" "targetname" "BL2" "angle" "0" "sounds" "1" "classname" "target_splash" } { "spawnflags" "2048" "origin" "-1136 -288 72" "targetname" "BL1" "classname" "target_splash" "angle" "0" "sounds" "1" } { "spawnflags" "2048" "origin" "-1136 -288 56" "targetname" "BL3" "sounds" "1" "angle" "0" "classname" "target_splash" } { "spawnflags" "2048" "origin" "-852 -376 -126" "targetname" "t269" "killtarget" "trig1" "classname" "trigger_relay" } { "target" "t277" "origin" "-1137 -405 170" "targetname" "s3" "angle" "315" "classname" "monster_infantry" "item" "ammo_bullets" "spawnflags" "1" } { "classname" "monster_soldier_light" "angle" "45" "origin" "-980 748 -12" "spawnflags" "0" } { "classname" "path_corner" "targetname" "t267" "target" "t268" "origin" "-1608 272 132" "spawnflags" "2048" } { "classname" "path_corner" "target" "t267" "origin" "-1356 276 152" "targetname" "t266" "spawnflags" "2048" } { "classname" "path_corner" "target" "t255" "origin" "-1368 108 152" "targetname" "t346" } { "spawnflags" "2048" "classname" "path_corner" "target" "t266" "targetname" "t268" "origin" "-1064 252 152" } { "classname" "path_corner" "targetname" "t258" "target" "t259" "origin" "-1256 656 176" "spawnflags" "2048" } { "classname" "path_corner" "target" "t261" "targetname" "t264" "origin" "-1272 864 176" "spawnflags" "2048" } { "classname" "path_corner" "target" "t257" "targetname" "t260" "origin" "-1432 848 176" "spawnflags" "2048" } { "classname" "path_corner" "targetname" "t262" "target" "t263" "origin" "-1384 624 176" "spawnflags" "2048" } { "classname" "path_corner" "targetname" "t263" "target" "t264" "origin" "-1456 792 176" "spawnflags" "2048" } { "classname" "path_corner" "targetname" "t259" "target" "t260" "origin" "-1232 840 176" "spawnflags" "2048" } { "classname" "path_corner" "targetname" "t261" "target" "t262" "origin" "-1216 680 176" "spawnflags" "2048" } { "classname" "path_corner" "targetname" "t257" "target" "t258" "origin" "-1424 664 176" "spawnflags" "2048" } { "classname" "monster_infantry" "spawnflags" "3" "targetname" "Response_troops" "origin" "-1344 96 184" "item" "ammo_bullets" "angle" "90" } { "classname" "monster_infantry" "target" "t255" "origin" "-1344 40 184" "item" "ammo_bullets" "angle" "270" "spawnflags" "1" } { "spawnflags" "2048" "classname" "trigger_relay" "delay" "1" "origin" "-1008 -200 152" "targetname" "BLRguns" "target" "BL5" } { "spawnflags" "2048" "classname" "trigger_relay" "delay" "1.25" "origin" "-976 -200 152" "targetname" "BLRguns" "target" "BL6" } { "spawnflags" "2048" "origin" "-1136 -256 72" "classname" "target_blaster" "angle" "0" "dmg" "5" "targetname" "BL5" } { "spawnflags" "2048" "origin" "-1136 -256 56" "classname" "target_blaster" "angle" "0" "dmg" "5" "targetname" "BL6" } { "model" "*53" "wait" "-1" "targetname" "BLR" "classname" "func_door" "angle" "-1" "lip" "1" } { "classname" "light" "light" "100" "_color" "1.000000 0.858268 0.279528" "origin" "-1960 960 172" } { "classname" "light" "light" "100" "_color" "1.000000 0.858268 0.279528" "origin" "-1904 960 172" } { "_color" "1.000000 0.858268 0.279528" "light" "100" "classname" "light" "origin" "-1848 960 172" } { "_color" "1.000000 0.858268 0.279528" "light" "150" "classname" "light" "origin" "-1904 872 228" } { "_color" "1.000000 0.858268 0.279528" "light" "100" "classname" "light" "origin" "-1960 656 172" } { "classname" "light" "light" "150" "_color" "1.000000 0.858268 0.279528" "origin" "-1904 744 228" } { "origin" "-1356 -344 168" "targetname" "t255" "target" "t254" "classname" "path_corner" } { "origin" "-1016 -200 224" "killtarget" "fl1" "targetname" "BLR" "classname" "trigger_relay" "target" "t423" } { "model" "*54" "target" "t379" "targetname" "trig1" "wait" "1.25" "classname" "trigger_multiple" "spawnflags" "2048" } { "spawnflags" "2048" "origin" "-1056 -200 152" "delay" ".5" "target" "BL3" "targetname" "BLRguns" "classname" "trigger_relay" } { "spawnflags" "2048" "origin" "-1080 -200 152" "delay" ".25" "target" "BL2" "targetname" "BLRguns" "classname" "trigger_relay" } { "spawnflags" "2048" "origin" "-1112 -200 152" "delay" "0" "target" "BL1" "targetname" "BLRguns" "classname" "trigger_relay" } { "spawnflags" "2048" "origin" "-1032 -200 152" "delay" ".75" "target" "BL4" "targetname" "BLRguns" "classname" "trigger_relay" } { "model" "*55" "targetname" "fl1" "classname" "func_wall" "spawnflags" "2048" } { "_color" "1.000000 0.929412 0.443137" "light" "80" "classname" "light" "origin" "-1096 -224 232" } { "spawnflags" "2048" "dmg" "5" "angle" "0" "targetname" "BL4" "classname" "target_blaster" "origin" "-1136 -224 72" } { "spawnflags" "2048" "dmg" "5" "angle" "0" "targetname" "BL2" "classname" "target_blaster" "origin" "-1136 -224 56" } { "spawnflags" "2048" "dmg" "5" "angle" "0" "targetname" "BL3" "classname" "target_blaster" "origin" "-1136 -288 56" } { "spawnflags" "2048" "dmg" "5" "targetname" "BL1" "angle" "0" "classname" "target_blaster" "origin" "-1136 -288 72" } { "model" "*56" "targetname" "trigbut" "spawnflags" "2048" "classname" "func_button" "angle" "180" "lip" "8" "health" "1" "_minlight" "0.3" "target" "t444" } { "angle" "180" "classname" "monster_soldier_light" "targetname" "Response_troops" "origin" "-912 -672 104" "spawnflags" "259" } { "angle" "180" "classname" "monster_soldier_light" "targetname" "Response_troops" "origin" "-992 -608 104" "spawnflags" "3" } { "classname" "monster_soldier_light" "angle" "180" "targetname" "Response_troops" "origin" "-976 -688 104" "spawnflags" "3" } { "classname" "monster_soldier_light" "angle" "180" "targetname" "Response_troops" "origin" "-928 -624 104" "spawnflags" "3" } { "spawnflags" "1" "origin" "-1016 -662 88" "targetname" "s3" "classname" "monster_infantry" "angle" "315" } { "model" "*57" "spawnflags" "2048" "classname" "trigger_multiple" "target" "s3" } { "model" "*58" "classname" "trigger_multiple" "delay" ".2" "target" "s2" } { "spawnflags" "2048" "classname" "trigger_relay" "targetname" "scum1" "target" "s2" "delay" "2" "origin" "-376 -1096 -248" } { "_color" "1.000000 0.432000 0.268000" "light" "64" "classname" "light" "origin" "-768 -800 -400" } { "_color" "1.000000 0.432000 0.268000" "light" "64" "classname" "light" "origin" "-768 -864 -400" } { "_color" "1.000000 0.432000 0.268000" "light" "64" "classname" "light" "origin" "-768 -928 -400" } { "_color" "1.000000 0.432000 0.268000" "light" "64" "classname" "light" "origin" "-768 -992 -400" } { "_color" "1.000000 0.432000 0.268000" "light" "64" "classname" "light" "origin" "-768 -1056 -400" } { "_color" "1.000000 0.432000 0.268000" "light" "64" "classname" "light" "origin" "-768 -1120 -400" } { "_color" "1.000000 0.432000 0.268000" "light" "64" "classname" "light" "origin" "-768 -1184 -400" } { "_color" "1.000000 0.432000 0.268000" "light" "64" "classname" "light" "origin" "-768 -1248 -400" } { "classname" "light" "light" "140" "origin" "-1660 -641 -456" } { "light" "140" "classname" "light" "origin" "-1720 -638 -459" } { "light" "140" "classname" "light" "origin" "-1584 -641 -456" } { "classname" "monster_flipper" "targetname" "mf" "origin" "-1760 -624 -512" } { "model" "*59" "spawnflags" "2048" "classname" "trigger_multiple" "target" "mf" } { "classname" "monster_flipper" "target" "t253" "origin" "-728 -648 -440" } { "classname" "path_corner" "targetname" "t252" "target" "t253" "origin" "-760 -904 -456" "spawnflags" "2048" } { "classname" "path_corner" "targetname" "t252" "target" "t253" "origin" "-800 -896 -456" "spawnflags" "2048" } { "classname" "path_corner" "target" "t252" "targetname" "t253" "origin" "-784 -672 -456" "spawnflags" "2048" } { "classname" "path_corner" "targetname" "t253" "target" "t252" "origin" "-768 -1200 -456" "spawnflags" "2048" } { "_style" "10" "origin" "-1456 -528 264" "classname" "light" "light" "175" "_color" "1.000000 0.929412 0.435294" } { "origin" "-1456 -688 264" "classname" "light" "light" "125" "_color" "1.000000 0.929412 0.435294" } { "classname" "path_corner" "targetname" "t249" "target" "t225" "origin" "-1352 -1132 -52" "spawnflags" "2048" } { "classname" "path_corner" "targetname" "t224" "target" "t249" "origin" "-1440 -1128 -52" "spawnflags" "2048" } { "classname" "path_corner" "origin" "-1088 -1000 -272" "targetname" "t240" "target" "t239" } { "spawnflags" "256" "angle" "45" "classname" "monster_infantry" "origin" "-984 -864 -256" "target" "t240" } { "model" "*60" "spawnflags" "2048" "classname" "trigger_push" "angle" "180" "speed" "50" } { "model" "*61" "spawnflags" "2048" "classname" "trigger_multiple" "target" "paraspawn" } { "classname" "monster_parasite" "targetname" "paraspawn" "spawnflags" "2" "origin" "-640 -1400 1156" "angle" "225" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1361 720 -50" "light" "120" "classname" "light" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1297 784 -50" "classname" "light" "light" "120" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1297 720 -50" "classname" "light" "light" "120" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1432 720 -152" "light" "80" "classname" "light" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1240 784 -152" "classname" "light" "light" "80" } { "classname" "light" "light" "80" "origin" "-1240 720 -152" "_color" "0.465649 1.000000 0.671756" } { "light" "80" "classname" "light" "origin" "-1304 656 -152" "_color" "0.465649 1.000000 0.671756" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1368 848 -152" "classname" "light" "light" "80" } { "light" "80" "classname" "light" "origin" "-1304 848 -152" "_color" "0.465649 1.000000 0.671756" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1160 552 -152" "classname" "light" "light" "80" } { "light" "80" "classname" "light" "origin" "-1096 552 -152" "_color" "0.465649 1.000000 0.671756" } { "classname" "light" "light" "80" "origin" "-1160 488 -152" "_color" "0.465649 1.000000 0.671756" } { "light" "80" "classname" "light" "origin" "-1096 488 -152" "_color" "0.465649 1.000000 0.671756" } { "_color" "0.465649 1.000000 0.671756" "light" "80" "classname" "light" "origin" "-1544 552 -152" } { "classname" "light" "light" "80" "_color" "0.465649 1.000000 0.671756" "origin" "-1496 1000 -152" } { "light" "80" "classname" "light" "_color" "0.465649 1.000000 0.671756" "origin" "-1496 936 -152" } { "classname" "light" "light" "80" "_color" "0.465649 1.000000 0.671756" "origin" "-1560 936 -152" } { "light" "80" "classname" "light" "_color" "0.465649 1.000000 0.671756" "origin" "-1560 872 -152" } { "origin" "-1560 808 -152" "classname" "light" "light" "80" "_color" "0.465649 1.000000 0.671756" } { "origin" "-1560 744 -152" "light" "80" "classname" "light" "_color" "0.465649 1.000000 0.671756" } { "origin" "-1560 680 -152" "classname" "light" "light" "80" "_color" "0.465649 1.000000 0.671756" } { "origin" "-1560 616 -152" "light" "80" "classname" "light" "_color" "0.465649 1.000000 0.671756" } { "origin" "-1504 600 -152" "classname" "light" "light" "80" "_color" "0.465649 1.000000 0.671756" } { "origin" "-1544 488 -152" "light" "80" "classname" "light" "_color" "0.465649 1.000000 0.671756" } { "origin" "-1480 552 -152" "classname" "light" "light" "80" "_color" "0.465649 1.000000 0.671756" } { "origin" "-1480 488 -152" "light" "80" "classname" "light" "_color" "0.465649 1.000000 0.671756" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1416 552 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1392 520 -160" "light" "70" "classname" "light" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1328 520 -160" "light" "70" "classname" "light" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1264 520 -160" "light" "70" "classname" "light" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1200 520 -160" "light" "70" "classname" "light" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1152 600 -152" "classname" "light" "light" "80" } { "origin" "-1432 1000 -152" "_color" "0.465649 1.000000 0.671756" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1032 424 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1032 488 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1032 744 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1032 680 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1096 616 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1096 680 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1096 744 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1096 808 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1032 872 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1096 872 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1160 936 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1096 936 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1096 1000 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1160 1000 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1224 1000 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1288 1000 -152" "classname" "light" "light" "80" } { "_color" "0.465649 1.000000 0.671756" "origin" "-1352 1000 -152" "classname" "light" "light" "80" } { "origin" "-1432 1064 -152" "_color" "0.465649 1.000000 0.671756" "light" "80" "classname" "light" } { "origin" "-1392 904 192" "classname" "light" "light" "150" } { "origin" "-1488 808 192" "classname" "light" "light" "150" } { "origin" "-1392 600 192" "classname" "light" "light" "150" } { "origin" "-1280 600 192" "classname" "light" "light" "150" } { "origin" "-1184 680 192" "classname" "light" "light" "150" } { "origin" "-1184 824 192" "classname" "light" "light" "150" } { "origin" "-1280 904 192" "light" "150" "classname" "light" } { "classname" "light" "light" "90" "_color" "1.000000 0.299180 0.000000" "origin" "-1008 840 -16" } { "_color" "1.000000 0.299180 0.000000" "classname" "light" "light" "90" "origin" "-992 824 -16" } { "light" "90" "classname" "light" "_color" "1.000000 0.299180 0.000000" "origin" "-992 856 -16" } { "light" "90" "classname" "light" "_color" "1.000000 0.299180 0.000000" "origin" "-976 840 -16" } { "origin" "-976 664 -16" "_color" "1.000000 0.299180 0.000000" "classname" "light" "light" "90" } { "origin" "-992 648 -16" "light" "90" "classname" "light" "_color" "1.000000 0.299180 0.000000" } { "origin" "-1008 664 -16" "classname" "light" "light" "90" "_color" "1.000000 0.299180 0.000000" } { "origin" "-992 680 -16" "light" "90" "classname" "light" "_color" "1.000000 0.299180 0.000000" } { "origin" "-1008 224 176" "light" "125" "classname" "light" "_color" "1.000000 0.299180 0.000000" } { "origin" "-992 208 176" "classname" "light" "light" "125" "_color" "1.000000 0.299180 0.000000" } { "origin" "-1024 208 176" "light" "125" "classname" "light" "_color" "1.000000 0.299180 0.000000" } { "origin" "-1488 400 144" "classname" "light" "light" "100" "_color" "1.000000 0.299180 0.000000" } { "origin" "-1504 416 144" "light" "100" "classname" "light" "_color" "1.000000 0.299180 0.000000" } { "origin" "-1520 400 144" "light" "100" "classname" "light" "_color" "1.000000 0.299180 0.000000" } { "model" "*62" "wait" "1" "delay" ".25" "classname" "func_plat" "sounds" "1" "speed" "150" "lip" "0" "accel" "250" } { "light" "100" "classname" "light" "origin" "-104 -1760 1088" } { "light" "80" "classname" "light" "origin" "-104 -1632 1088" } { "classname" "light" "light" "100" "origin" "24 -1760 1088" } { "classname" "info_player_start" "angle" "0" "origin" "-248 -1792 880" "targetname" "base3c" } { "model" "*63" "classname" "func_button" "angle" "90" "lip" "4" "sounds" "4" "_minlight" "0.8" "target" "access_panel" "spawnflags" "2048" } { "spawnflags" "1" "origin" "-328 -428 -294" "targetname" "s2" "classname" "monster_soldier" "angle" "270" } { "_color" "1.000000 0.929412 0.435294" "light" "125" "classname" "light" "origin" "-1456 -608 264" } { "origin" "-888 -184 -196" "classname" "light" "light" "64" "_color" "1.000000 0.529881 0.306773" } { "item" "key_red_key" "classname" "trigger_key" "spawnflags" "2049" "target" "rusty_gate" "targetname" "keyhole1" "origin" "-1584 952 184" } { "angle" "90" "spawnflags" "2" "classname" "monster_soldier" "targetname" "goons1" "origin" "-808 -1368 912" } { "classname" "monster_soldier" "spawnflags" "2" "angle" "270" "targetname" "goons1" "origin" "-760 -1368 912" "item" "ammo_shells" } { "model" "*64" "spawnflags" "2048" "_minlight" "0.2" "wait" "-1" "classname" "func_door" "lip" "8" "targetname" "water_secret" "angle" "90" } { "model" "*65" "targetname" "lsecret" "target" "t417" "classname" "trigger_once" "spawnflags" "2048" } { "spawnflags" "1" "item" "ammo_bullets" "classname" "monster_infantry" "angle" "225" "targetname" "scum1" "origin" "-448 -1080 -280" } { "model" "*66" "classname" "trigger_counter" "targetname" "goons1" "target" "goons2" "spawnflags" "2049" "count" "2" } { "classname" "path_corner" "targetname" "t245" "origin" "-219 -1385 856" "target" "t350" } { "classname" "path_corner" "targetname" "t244" "target" "t245" "origin" "-560 -1368 856" } { "classname" "path_corner" "targetname" "t245" "origin" "-590 -1456 864" "target" "t349" } { "classname" "monster_soldier_light" "origin" "-536 -1456 882" "target" "t349" } { "light" "80" "classname" "light" "origin" "-376 -1460 864" "_color" "1.000000 0.360000 0.268000" } { "classname" "light" "light" "80" "origin" "-376 -1356 864" "_color" "1.000000 0.360000 0.268000" } { "origin" "-1032 -224 232" "light" "80" "classname" "light" "_color" "1.000000 0.929412 0.443137" } { "_style" "11" "origin" "-1168 -256 192" "classname" "light" "light" "80" "_color" "1.000000 0.833992 0.276680" } { "classname" "light" "light" "80" "origin" "-1096 -192 176" "_color" "1.000000 0.929412 0.443137" } { "light" "80" "classname" "light" "origin" "-1032 -192 176" "_color" "1.000000 0.929412 0.443137" } { "origin" "-56 -1872 984" "light" "125" "classname" "light" } { "origin" "-684 -788 872" "classname" "misc_explobox" "health" "50" "dmg" "200" "spawnflags" "2048" } { "spawnflags" "2048" "dmg" "150" "health" "80" "classname" "misc_explobox" "origin" "-580 -764 876" } { "spawnflags" "2048" "origin" "-816 -840 872" "classname" "misc_explobox" } { "light" "80" "classname" "light" "origin" "-512 -1308 864" "_color" "1.000000 0.360000 0.268000" } { "light" "80" "classname" "light" "origin" "-512 -1516 864" "_color" "1.000000 0.360000 0.268000" } { "spawnflags" "2048" "classname" "weapon_supershotgun" "origin" "-1920 856 184" "target" "Response_troops" } { "model" "*67" "_minlight" "0.2" "target" "t342" "lip" "12" "classname" "func_door" "angle" "0" "speed" "100" "wait" "4" "team" "doorN" "sounds" "0" "spawnflags" "12" } { "model" "*68" "_minlight" "0.2" "lip" "12" "sounds" "2" "team" "doorN" "wait" "4" "speed" "100" "angle" "180" "classname" "func_door" "spawnflags" "12" "target" "t342" } { "light" "150" "classname" "light" "origin" "-1169 -642 268" "_color" "1.000000 0.992157 0.701961" } { "_color" "1.000000 0.262745 0.243137" "origin" "148 -640 -960" "classname" "light" "light" "120" } { "_color" "1.000000 0.262745 0.243137" "classname" "light" "light" "105" "origin" "98 -574 -64" } { "light" "105" "classname" "light" "origin" "148 -640 -64" "_color" "1.000000 0.262745 0.243137" } { "_color" "1.000000 0.262745 0.243137" "light" "105" "classname" "light" "origin" "96 -708 -64" } { "_color" "1.000000 0.262745 0.243137" "classname" "light" "light" "90" "origin" "98 -574 192" } { "light" "90" "classname" "light" "origin" "148 -640 192" "_color" "1.000000 0.262745 0.243137" } { "_color" "1.000000 0.262745 0.243137" "light" "90" "classname" "light" "origin" "96 -708 192" } { "_color" "1.000000 0.262745 0.243137" "origin" "96 -708 320" "classname" "light" "light" "105" } { "_color" "1.000000 0.262745 0.243137" "origin" "148 -640 320" "classname" "light" "light" "105" } { "origin" "98 -574 320" "light" "105" "classname" "light" "_color" "1.000000 0.262745 0.243137" } { "model" "*69" "_minlight" "0.2" "lip" "12" "sounds" "2" "team" "door3" "wait" "4" "speed" "100" "angle" "270" "classname" "func_door" "target" "t432" } { "model" "*70" "_minlight" "0.2" "lip" "12" "classname" "func_door" "angle" "90" "speed" "100" "wait" "4" "team" "door3" "sounds" "0" "target" "t432" } { "classname" "light" "light" "100" "origin" "-1232 -896 -136" } { "model" "*71" "origin" "-932 -1120 -128" "speed" "70" "dmg" "5" "wait" "6" "targetname" "wallramp" "distance" "33" "sounds" "1" "angle" "-1" "classname" "func_door_rotating" "spawnflags" "128" } { "model" "*72" "_minlight" "0.2" "lip" "12" "classname" "func_door" "angle" "90" "speed" "100" "wait" "4" "team" "door2" "sounds" "0" } { "model" "*73" "_minlight" "0.2" "lip" "12" "sounds" "2" "team" "door2" "wait" "4" "speed" "100" "angle" "270" "classname" "func_door" } { "origin" "-1396 -376 160" "light" "75" "classname" "light" "_color" "1.000000 0.582677 0.204724" } { "origin" "-1396 -248 160" "light" "75" "classname" "light" "_color" "1.000000 0.582677 0.204724" } { "light" "75" "classname" "light" "origin" "-1396 -104 160" "_color" "1.000000 0.582677 0.204724" } { "light" "75" "classname" "light" "origin" "-1396 8 160" "_color" "1.000000 0.582677 0.204724" } { "light" "75" "classname" "light" "origin" "-1292 8 160" "_color" "1.000000 0.582677 0.204724" } { "light" "75" "classname" "light" "origin" "-1292 -104 160" "_color" "1.000000 0.582677 0.204724" } { "origin" "-1292 -248 160" "light" "75" "classname" "light" "_color" "1.000000 0.582677 0.204724" } { "origin" "-1292 -376 160" "classname" "light" "light" "75" "_color" "1.000000 0.582677 0.204724" } { "_color" "1.000000 0.262745 0.243137" "origin" "-1762 -574 -320" "light" "100" "classname" "light" } { "origin" "-1762 -574 -192" "light" "105" "classname" "light" "_color" "1.000000 0.262745 0.243137" } { "origin" "-1432 -640 -464" "light" "120" "classname" "light" "_color" "1.000000 0.274510 0.035294" } { "origin" "-1304 -640 -464" "classname" "light" "light" "120" "_color" "1.000000 0.274510 0.035294" } { "origin" "-1176 -640 -464" "light" "120" "classname" "light" "_color" "1.000000 0.274510 0.035294" } { "model" "*74" "origin" "-1552 808 160" "wait" "-1" "spawnflags" "137" "angle" "-1" "sounds" "1" "distance" "90" "speed" "75" "classname" "func_door_rotating" "targetname" "rusty_gate" "_minlight" "0.3" } { "classname" "light" "light" "70" "origin" "-1088 -1008 -240" "_color" "1.000000 0.517647 0.223529" } { "_color" "1.000000 0.992157 0.701961" "origin" "-1169 -642 228" "classname" "light" "light" "150" } { "light" "200" "classname" "light" "origin" "-496 -392 -240" } { "origin" "-610 -574 -832" "light" "80" "classname" "light" "_color" "1.000000 0.274510 0.035294" } { "_color" "1.000000 0.529881 0.306773" "origin" "-888 -312 -196" "classname" "light" "light" "64" } { "_color" "1.000000 0.529881 0.306773" "light" "32" "classname" "light" "origin" "-890 -152 -192" } { "_color" "1.000000 0.529881 0.306773" "origin" "-888 -248 -200" "classname" "light" "light" "64" } { "model" "*75" "spawnflags" "2048" "light" "90" "target" "water_secret" "lip" "4" "health" "1" "angle" "180" "classname" "func_button" "_color" "1.000000 0.262745 0.243137" } { "origin" "-320 -976 -280" "classname" "light" "light" "80" } { "origin" "-320 -848 -280" "classname" "light" "light" "80" } { "origin" "-320 -720 -280" "classname" "light" "light" "80" } { "origin" "-320 -592 -280" "classname" "light" "light" "80" } { "item" "item_armor_shard" "origin" "-320 -464 -280" "classname" "light" "light" "80" } { "_color" "1.000000 0.517647 0.223529" "origin" "-320 -336 -280" "classname" "light" "light" "150" } { "model" "*76" "spawnflags" "2048" "angle" "0" "target" "scum1" "classname" "trigger_multiple" } { "origin" "-984 -796 -256" "classname" "path_corner" "targetname" "t294" "target" "t239" } { "origin" "-852 -980 -272" "target" "t240" "targetname" "t293" "classname" "path_corner" } { "origin" "-640 -896 -272" "classname" "light" "light" "100" } { "origin" "-1416 792 152" "classname" "light" "light" "200" } { "light" "100" "classname" "light" "origin" "-992 -1136 920" } { "light" "110" "classname" "light" "origin" "-992 -1200 936" } { "light" "110" "classname" "light" "origin" "-992 -1328 936" } { "light" "115" "classname" "light" "origin" "-992 -1392 936" } { "_color" "1.000000 0.929412 0.443137" "origin" "-1032 -960 232" "light" "150" "classname" "light" } { "_color" "1.000000 0.929412 0.443137" "origin" "-1096 -960 232" "classname" "light" "light" "150" } { "_color" "1.000000 0.929412 0.443137" "classname" "light" "light" "150" "origin" "-856 -960 232" } { "_color" "1.000000 0.929412 0.443137" "light" "150" "classname" "light" "origin" "-920 -960 232" } { "_color" "1.000000 0.929412 0.443137" "origin" "-856 -224 232" "light" "80" "classname" "light" } { "_color" "1.000000 0.929412 0.443137" "origin" "-920 -224 232" "classname" "light" "light" "80" } { "_color" "1.000000 0.929412 0.443137" "origin" "-856 -992 200" "light" "150" "classname" "light" } { "_color" "1.000000 0.929412 0.443137" "origin" "-920 -992 200" "classname" "light" "light" "150" } { "origin" "-1060 -392 -88" "light" "120" "classname" "light" } { "origin" "-1060 -392 -152" "light" "150" "classname" "light" } { "classname" "light" "light" "120" "origin" "-1060 -392 -24" } { "origin" "-984 -392 -152" "light" "150" "classname" "light" } { "classname" "light" "light" "150" "origin" "-904 -392 -152" } { "_color" "1.000000 0.517647 0.223529" "classname" "light" "light" "90" "origin" "-1048 -896 960" } { "_color" "1.000000 0.517647 0.223529" "origin" "-1048 -1024 832" "light" "90" "classname" "light" } { "_color" "1.000000 0.517647 0.223529" "origin" "-1048 -1152 832" "light" "90" "classname" "light" } { "_color" "1.000000 0.517647 0.223529" "origin" "-1048 -1024 960" "light" "90" "classname" "light" } { "_color" "1.000000 0.517647 0.223529" "origin" "-1048 -1408 960" "light" "90" "classname" "light" } { "_color" "1.000000 0.517647 0.223529" "origin" "-1048 -1536 960" "light" "90" "classname" "light" } { "_color" "1.000000 0.517647 0.223529" "origin" "-816 -1664 960" "light" "90" "classname" "light" } { "_color" "1.000000 0.517647 0.223529" "origin" "-960 -1880 960" "light" "90" "classname" "light" } { "_color" "1.000000 0.517647 0.223529" "origin" "-832 -1880 960" "light" "90" "classname" "light" } { "_color" "1.000000 0.517647 0.223529" "origin" "-704 -1880 960" "light" "90" "classname" "light" } { "origin" "-760 -1808 936" "light" "115" "classname" "light" } { "origin" "-776 -1584 1012" "light" "125" "classname" "light" } { "classname" "light" "light" "125" "origin" "-712 -1584 1012" } { "origin" "-728 -1564 896" "light" "32" "classname" "light" } { "origin" "-744 -1564 936" "light" "64" "classname" "light" } { "origin" "-742 -1568 988" "light" "75" "classname" "light" } { "classname" "light" "light" "32" "origin" "-760 -1564 896" } { "_color" "1.000000 0.517647 0.223529" "classname" "light" "light" "64" "origin" "-1048 -896 704" } { "origin" "-864 -1808 936" "classname" "light" "light" "115" } { "origin" "-992 -1792 936" "classname" "light" "light" "110" } { "origin" "-992 -1264 936" "classname" "light" "light" "110" } { "origin" "-992 -1296 888" "classname" "light" "light" "50" } { "origin" "-992 -1200 840" "classname" "light" "light" "50" } { "origin" "-992 -1168 824" "light" "50" "classname" "light" } { "origin" "-992 -1136 808" "light" "50" "classname" "light" } { "origin" "-992 -1104 792" "classname" "light" "light" "50" } { "origin" "-992 -1072 776" "light" "50" "classname" "light" } { "origin" "-992 -1040 760" "light" "50" "classname" "light" } { "origin" "-992 -1008 744" "classname" "light" "light" "50" } { "origin" "-992 -976 728" "light" "50" "classname" "light" } { "origin" "-992 -944 712" "light" "50" "classname" "light" } { "origin" "-992 -1264 872" "classname" "light" "light" "50" } { "origin" "-992 -1232 856" "classname" "light" "light" "50" } { "_color" "1.000000 0.517647 0.223529" "origin" "-992 -904 680" "classname" "light" "light" "90" } { "_color" "1.000000 0.517647 0.223529" "origin" "-992 -776 680" "classname" "light" "light" "130" } { "_color" "1.000000 0.517647 0.223529" "origin" "-1016 -640 680" "classname" "light" "light" "130" } { "_color" "1.000000 0.517647 0.223529" "origin" "-696 -640 744" "classname" "light" "light" "120" } { "origin" "-728 -1392 1208" "classname" "light" "light" "350" "target" "t421" "_cone" "15" } { "model" "*77" "spawnflags" "2048" "classname" "trigger_multiple" "delay" ".25" "target" "goons1" "wait" "5" } { "origin" "-720 -780 936" "classname" "light" "light" "90" } { "origin" "-496 -1148 928" "classname" "light" "light" "100" } { "light" "90" "classname" "light" "origin" "-624 -780 936" } { "light" "120" "classname" "light" "origin" "-320 -972 920" } { "_color" "1.000000 0.388235 0.239216" "light" "100" "classname" "light" "origin" "-344 -908 888" } { "classname" "light" "light" "350" "origin" "-728 -1416 1208" "target" "t419" "_cone" "15" } { "light" "100" "classname" "light" "origin" "-592 -1400 944" } { "light" "350" "classname" "light" "origin" "-752 -1392 1208" "target" "t420" "_cone" "15" } { "light" "350" "classname" "light" "origin" "-752 -1416 1208" "target" "t418" "_cone" "15" } { "_color" "1.000000 0.360000 0.268000" "origin" "-328 -1460 864" "classname" "light" "light" "80" } { "light" "80" "classname" "light" "origin" "-649 -1347 1134" } { "light" "80" "classname" "light" "origin" "-719 -1481 1134" } { "light" "80" "classname" "light" "origin" "-827 -1369 1134" } { "light" "80" "classname" "light" "origin" "-799 -1471 1134" } { "classname" "light" "light" "80" "origin" "-649 -1471 1134" } { "origin" "-768 -1076 984" "classname" "light" "light" "110" } { "_color" "1.000000 0.388235 0.239216" "origin" "-680 -1084 888" "classname" "light" "light" "96" } { "_color" "1.000000 0.388235 0.239216" "origin" "-856 -1084 888" "classname" "light" "light" "96" } { "_color" "1.000000 0.388235 0.239216" "light" "100" "classname" "light" "origin" "-856 -956 888" } { "_color" "1.000000 0.388235 0.239216" "light" "96" "classname" "light" "origin" "-856 -1340 888" } { "_color" "1.000000 0.388235 0.239216" "light" "96" "classname" "light" "origin" "-824 -1460 888" } { "classname" "light" "light" "175" "origin" "-770 -1240 1024" } { "classname" "func_group" "light" "64" } { "_style" "6" "light" "150" "classname" "light" "origin" "-1344 -176 388" } { "classname" "light" "light" "100" "origin" "-1344 -256 388" } { "light" "100" "classname" "light" "origin" "-1344 -336 388" } { "light" "75" "classname" "light" "origin" "-376 -1136 -304" } { "_color" "1.000000 0.517647 0.223529" "light" "90" "classname" "light" "origin" "-448 -1040 -280" } { "origin" "-336 -1192 -296" "classname" "light" "light" "80" } { "_color" "1.000000 0.517647 0.223529" "light" "150" "classname" "light" "origin" "-368 -512 -280" } { "light" "80" "classname" "light" "origin" "-320 -1104 -280" } { "_color" "1.000000 0.517647 0.223529" "origin" "-536 -1088 -280" "classname" "light" "light" "90" } { "light" "75" "classname" "light" "origin" "-464 -1088 -264" } { "light" "100" "classname" "light" "origin" "-512 -1208 -352" } { "_color" "1.000000 0.929412 0.443137" "origin" "-1096 -360 216" "light" "175" "classname" "light" } { "_color" "1.000000 0.929412 0.443137" "origin" "-1032 -360 216" "classname" "light" "light" "175" } { "_color" "1.000000 0.929412 0.443137" "classname" "light" "light" "80" "origin" "-864 -192 176" } { "_color" "1.000000 0.929412 0.443137" "light" "80" "classname" "light" "origin" "-920 -192 176" } { "_color" "1.000000 0.929412 0.443137" "origin" "-1032 -992 200" "classname" "light" "light" "150" } { "_color" "1.000000 0.929412 0.443137" "origin" "-1088 -992 200" "light" "150" "classname" "light" } { "_color" "1.000000 0.517647 0.223529" "origin" "-1136 -824 -240" "light" "70" "classname" "light" } { "_color" "1.000000 0.517647 0.223529" "origin" "-960 -1008 -240" "classname" "light" "light" "70" } { "_color" "1.000000 0.517647 0.223529" "origin" "-816 -960 -240" "light" "70" "classname" "light" } { "_color" "1.000000 0.517647 0.223529" "origin" "-816 -832 -240" "classname" "light" "light" "70" } { "origin" "-832 -1008 -240" "light" "70" "classname" "light" } { "_color" "1.000000 0.929412 0.443137" "light" "175" "classname" "light" "origin" "-1096 -392 272" } { "_color" "1.000000 0.929412 0.443137" "classname" "light" "light" "175" "origin" "-1032 -392 272" } { "origin" "-768 -736 -400" "classname" "light" "light" "64" "_color" "1.000000 0.432000 0.268000" } { "_color" "1.000000 0.517647 0.223529" "origin" "-600 -896 -464" "classname" "light" "light" "64" } { "light" "105" "classname" "light" "origin" "-404 -640 -360" "_color" "1.000000 0.256917 0.007905" } { "light" "105" "classname" "light" "origin" "-404 -640 -488" "_color" "1.000000 0.256917 0.007905" } { "light" "80" "classname" "light" "origin" "-420 -392 -688" "_color" "1.000000 0.274510 0.035294" } { "_color" "1.000000 0.517647 0.223529" "origin" "-368 -768 -280" "classname" "light" "light" "150" } { "origin" "-480 -392 -968" "light" "150" "classname" "light" } { "origin" "-496 -392 -175" "classname" "light" "light" "200" } { "_color" "1.000000 0.517647 0.223529" "origin" "-288 -1248 -280" "classname" "light" "light" "130" } { "model" "*78" "angle" "45" "sounds" "2" "wait" "3" "spawnflags" "1" "height" "386" "speed" "100" "classname" "func_plat" } { "origin" "-1424 -924 -56" "target" "t224" "targetname" "t225" "classname" "path_corner" "spawnflags" "2048" } { "classname" "path_corner" "targetname" "t221" "target" "t223" "origin" "-1408 -640 128" } { "classname" "path_corner" "targetname" "t223" "target" "t221" "origin" "-1392 -448 152" } { "classname" "path_corner" "targetname" "t221" "target" "t222" "origin" "-1224 -448 152" } { "classname" "path_corner" "targetname" "t222" "target" "t223" "origin" "-1336 -480 152" } { "classname" "monster_soldier" "target" "t221" "origin" "-1328 -432 172" "spawnflags" "1" } { "_color" "1.000000 0.517647 0.223529" "classname" "light" "light" "64" "origin" "-792 -640 -400" } { "_color" "1.000000 0.517647 0.223529" "classname" "light" "light" "64" "origin" "-728 -640 -464" } { "light" "64" "classname" "light" "origin" "-856 -640 -464" } { "_color" "1.000000 0.517647 0.223529" "light" "64" "classname" "light" "origin" "-664 -896 -464" } { "_color" "1.000000 0.274510 0.035294" "classname" "light" "light" "80" "origin" "-1792 -640 -528" } { "light" "100" "classname" "light" "origin" "-640 -896 -336" } { "light" "100" "classname" "light" "origin" "-640 -896 -144" } { "light" "64" "classname" "light" "origin" "-640 -896 -14" } { "classname" "light" "light" "100" "origin" "-648 -888 -400" } { "model" "*79" "classname" "func_plat" "speed" "150" "height" "684" "spawnflags" "1793" "wait" "5" "sounds" "1" } { "model" "*80" "spawnflags" "8" "sounds" "1" "wait" "6" "speed" "125" "angle" "-2" "classname" "func_door" } { "origin" "-1060 -416 76" "light" "140" "classname" "light" } { "origin" "-536 -640 -208" "light" "175" "classname" "light" "_color" "1.000000 0.274510 0.035294" } { "origin" "-1024 -632 -384" "classname" "light" "light" "150" "_color" "1.000000 0.274510 0.035294" } { "classname" "light" "light" "130" "origin" "-792 -640 -328" "_color" "1.000000 0.274510 0.035294" } { "classname" "monster_soldier" "angle" "0" "origin" "-976 -240 76" "spawnflags" "0" } { "origin" "-794 -640 242" "light" "150" "classname" "light" } { "classname" "light" "light" "125" "origin" "-606 -641 179" } { "_color" "1.000000 0.880000 0.192000" "classname" "light" "light" "110" "origin" "-1036 -1172 -64" } { "origin" "-768 -1320 1128" "light" "80" "classname" "light" } { "origin" "148 -640 576" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "148 -640 960" "_color" "1.000000 0.262745 0.243137" } { "origin" "96 -708 192" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "148 -640 -192" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "98 -574 192" "light" "105" "classname" "light" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "96 -708 64" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "196 -736 128" "_color" "1.000000 0.274510 0.035294" } { "classname" "light" "light" "105" "origin" "98 -574 64" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "96 -708 448" "_color" "1.000000 0.262745 0.243137" } { "classname" "light" "light" "105" "origin" "98 -574 448" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "96 -708 576" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "148 -640 448" "_color" "1.000000 0.262745 0.243137" } { "classname" "light" "light" "105" "origin" "98 -574 576" "_color" "1.000000 0.262745 0.243137" } { "origin" "96 -708 704" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "148 -640 704" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "98 -574 704" "light" "105" "classname" "light" "_color" "1.000000 0.262745 0.243137" } { "origin" "96 -708 -320" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "148 -640 -320" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "98 -574 -320" "light" "105" "classname" "light" "_color" "1.000000 0.262745 0.243137" } { "origin" "96 -708 -192" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "148 -640 192" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "98 -574 -192" "light" "105" "classname" "light" "_color" "1.000000 0.262745 0.243137" } { "light" "120" "classname" "light" "origin" "96 -708 -960" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "148 -640 -704" "_color" "1.000000 0.262745 0.243137" } { "classname" "light" "light" "120" "origin" "98 -574 -960" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "96 -708 -704" "_color" "1.000000 0.262745 0.243137" } { "classname" "light" "light" "105" "origin" "98 -574 -704" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "96 -708 -576" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "148 -640 -576" "_color" "1.000000 0.262745 0.243137" } { "classname" "light" "light" "105" "origin" "98 -574 -576" "_color" "1.000000 0.262745 0.243137" } { "origin" "96 -708 -448" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "148 -640 -448" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "98 -574 -448" "light" "105" "classname" "light" "_color" "1.000000 0.262745 0.243137" } { "origin" "-608 -708 -960" "classname" "light" "light" "80" "_color" "1.000000 0.274510 0.035294" } { "origin" "-420 -392 -944" "classname" "light" "light" "80" "_color" "1.000000 0.274510 0.035294" } { "origin" "-610 -574 -960" "light" "80" "classname" "light" "_color" "1.000000 0.274510 0.035294" } { "light" "80" "classname" "light" "origin" "-420 -392 -816" "_color" "1.000000 0.274510 0.035294" } { "classname" "light" "light" "80" "origin" "-608 -708 -832" "_color" "1.000000 0.274510 0.035294" } { "light" "100" "classname" "light" "origin" "-608 -708 -704" "_color" "1.000000 0.274510 0.035294" } { "classname" "light" "light" "100" "origin" "-610 -574 -704" "_color" "1.000000 0.274510 0.035294" } { "light" "100" "classname" "light" "origin" "-608 -708 -576" "_color" "1.000000 0.274510 0.035294" } { "light" "80" "classname" "light" "origin" "-420 -392 -560" "_color" "1.000000 0.274510 0.035294" } { "classname" "light" "light" "100" "origin" "-610 -574 -576" "_color" "1.000000 0.274510 0.035294" } { "origin" "-608 -708 -448" "classname" "light" "light" "100" "_color" "1.000000 0.274510 0.035294" } { "origin" "-420 -392 -432" "classname" "light" "light" "80" "_color" "1.000000 0.274510 0.035294" } { "origin" "-610 -574 -448" "light" "100" "classname" "light" "_color" "1.000000 0.274510 0.035294" } { "origin" "-1203 -901 58" "light" "100" "classname" "light" } { "classname" "light" "light" "175" "origin" "-794 -640 210" } { "pathtarget" "hiss2" "wait" "5" "target" "t3" "classname" "path_corner" "origin" "-768 -764 832" "targetname" "t352" } { "pathtarget" "hiss" "target" "t150" "targetname" "t20" "wait" "3" "classname" "path_corner" "origin" "-1092 -760 -320" } { "_color" "1.000000 0.274510 0.035294" "origin" "-1272 -640 944" "classname" "light" "light" "175\" "target" "t38" } { "_color" "1.000000 0.274510 0.035294" "origin" "4 -640 -664" "classname" "light" "light" "105" "target" "t46" } { "_color" "1.000000 0.274510 0.035294" "light" "175\" "classname" "light" "origin" "-1404 -640 944" "target" "t27" } { "_color" "1.000000 0.262745 0.243137" "light" "105" "classname" "light" "origin" "-1760 -708 960" } { "_color" "1.000000 0.262745 0.243137" "light" "105" "classname" "light" "origin" "-1812 -640 960" } { "_color" "1.000000 0.262745 0.243137" "classname" "light" "light" "105" "origin" "-1762 -574 960" } { "light" "105" "classname" "light" "origin" "-1760 -708 704" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "-1812 -640 704" "_color" "1.000000 0.262745 0.243137" } { "classname" "light" "light" "105" "origin" "-1762 -574 704" "_color" "1.000000 0.262745 0.243137" } { "origin" "-1760 -708 576" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "-1812 -640 576" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "-1762 -574 576" "light" "105" "classname" "light" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "-1760 -708 448" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "-1812 -640 448" "_color" "1.000000 0.262745 0.243137" } { "classname" "light" "light" "105" "origin" "-1762 -574 448" "_color" "1.000000 0.262745 0.243137" } { "origin" "-1760 -708 320" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "-1812 -640 320" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "-1762 -574 320" "light" "105" "classname" "light" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "-1760 -708 192" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "-1812 -640 192" "_color" "1.000000 0.262745 0.243137" } { "classname" "light" "light" "105" "origin" "-1762 -574 192" "_color" "1.000000 0.262745 0.243137" } { "origin" "-1760 -708 64" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "-1812 -640 64" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "-1762 -574 64" "light" "105" "classname" "light" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "-1760 -708 -64" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "-1812 -640 -64" "_color" "1.000000 0.262745 0.243137" } { "classname" "light" "light" "105" "origin" "-1762 -574 -64" "_color" "1.000000 0.262745 0.243137" } { "origin" "-1760 -708 -192" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "-1812 -640 -192" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "light" "100" "classname" "light" "origin" "-1760 -708 -320" "_color" "1.000000 0.262745 0.243137" } { "light" "100" "classname" "light" "origin" "-1812 -640 -320" "_color" "1.000000 0.262745 0.243137" } { "origin" "-1760 -708 -448" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "-1804 -640 -448" "classname" "light" "light" "105" "_color" "1.000000 0.274510 0.035294" } { "origin" "-1762 -574 -448" "light" "105" "classname" "light" "_color" "1.000000 0.262745 0.243137" } { "_color" "1.000000 0.262745 0.243137" "light" "105" "classname" "light" "origin" "-1760 -708 832" } { "light" "105" "classname" "light" "origin" "-1812 -640 832" "_color" "1.000000 0.262745 0.243137" } { "classname" "light" "light" "105" "origin" "-1762 -574 832" "_color" "1.000000 0.262745 0.243137" } { "origin" "96 -708 960" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "148 -640 1088" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "98 -574 960" "light" "105" "classname" "light" "_color" "1.000000 0.262745 0.243137" } { "origin" "96 -708 1088" "classname" "light" "light" "105" "_color" "1.000000 0.262745 0.243137" } { "origin" "-404 -640 -104" "classname" "light" "light" "64" } { "origin" "98 -574 1088" "light" "105" "classname" "light" "_color" "1.000000 0.262745 0.243137" } { "_color" "1.000000 0.262745 0.243137" "origin" "-1760 -708 1088" "classname" "light" "light" "105" } { "_color" "1.000000 0.262745 0.243137" "origin" "-1812 -640 1088" "classname" "light" "light" "105" } { "_color" "1.000000 0.262745 0.243137" "origin" "-1762 -574 1088" "light" "105" "classname" "light" } { "light" "105" "classname" "light" "origin" "96 -708 832" "_color" "1.000000 0.262745 0.243137" } { "light" "105" "classname" "light" "origin" "148 -640 832" "_color" "1.000000 0.262745 0.243137" } { "classname" "light" "light" "105" "origin" "98 -574 832" "_color" "1.000000 0.262745 0.243137" } { "classname" "func_group" } { "wait" ".09" "pathtarget" "corner2" "origin" "-1820 -760 -288" "classname" "path_corner" "targetname" "t18" "target" "t13" "light" "64" } { "wait" ".09" "pathtarget" "corner1" "origin" "-1788 -760 -320" "classname" "path_corner" "targetname" "t150" "target" "t18" } { "wait" ".09" "pathtarget" "corner3" "classname" "path_corner" "origin" "-1820 -760 832" "target" "t12" "targetname" "t13" "light" "64" } { "wait" ".09" "classname" "path_corner" "origin" "-1788 -760 848" "targetname" "t12" "target" "t151" "light" "64" "pathtarget" "open_in" } { "wait" ".09" "pathtarget" "corner9" "classname" "path_corner" "origin" "-652 -760 -832" "targetname" "t24" "target" "t23" "light" "64" } { "wait" ".09" "pathtarget" "corner0B" "target" "t20" "classname" "path_corner" "origin" "-680 -760 -320" "targetname" "t21" "light" "64" } { "wait" ".09" "pathtarget" "corner0" "classname" "path_corner" "origin" "-652 -760 -336" "target" "t21" "targetname" "t22" "light" "64" } { "classname" "path_corner" "origin" "-656 -760 -784" "target" "t22" "targetname" "t23" "light" "64" } { "_color" "1.000000 0.360000 0.268000" "origin" "-328 -1356 864" "light" "80" "classname" "light" } { "_color" "1.000000 0.388235 0.239216" "origin" "-856 -828 888" "classname" "light" "light" "100" } { "origin" "-1232 -896 -168" "light" "100" "classname" "light" } { "wait" ".09" "pathtarget" "corner7" "target" "t7" "origin" "-80 -760 -800" "targetname" "t8" "classname" "path_corner" "light" "64" } { "wait" ".09" "pathtarget" "corner8" "target" "t24" "targetname" "t7" "classname" "path_corner" "origin" "-100 -760 -832" "light" "64" } { "wait" ".09" "pathtarget" "corner6" "target" "t8" "origin" "-80 -760 816" "targetname" "t4" "classname" "path_corner" } { "wait" ".09" "origin" "-104 -760 832" "target" "t4" "targetname" "t3" "classname" "path_corner" "light" "64" "pathtarget" "close_out" } { "model" "*81" "_minlight" "0.2" "sounds" "0" "health" "1" "lip" "8" "angle" "180" "target" "ws_trgr" "classname" "func_button" } { "origin" "-1344 80 388" "classname" "light" "light" "100" } { "_style" "6" "origin" "-1344 0 388" "light" "150" "classname" "light" } { "origin" "-1344 -80 388" "classname" "light" "light" "100" } { "origin" "-336 -1408 1092" "classname" "light" "light" "64" } { "targetname" "ws_trgr" "classname" "trigger_relay" "target" "wallramp" "origin" "-1268 -1040 -224" } { "light" "100" "classname" "light" "origin" "-1344 176 388" } { "origin" "-244 -1792 952" "light" "150" "classname" "light" } { "model" "*82" "classname" "func_door" "angle" "-1" "_minlight" ".2" "targetname" "t247" } { "model" "*83" "classname" "func_button" "angle" "180" "target" "t247" "_minlight" "0.3" } { "model" "*84" "spawnflags" "2048" "classname" "trigger_multiple" "target" "t246" } { "spawnflags" "2048" "classname" "target_changelevel" "map" "base3$train" "origin" "-248 -1748 932" "targetname" "t246" }yquake2-QUAKE2_8_40/stuff/mapfixes/baseq2/waste3.ent000066400000000000000000002156001465112212000221530ustar00rootroot00000000000000// FIXED ENTITY STRING (by BjossiAlfreds) // // 1. Fixed uncounted secret (b#11) // // This secret has a targetname (t117) but no entity // targets this targetname. Changed the // item_armor_body (b#12) to target it // // 2. Swapped waste1 and waste2 player starts // (b#2) so the map beginning comes first // // 3. Fixed wrong secret message sound (b#3) // // 4. Added spawnflag 2048 to a bunch of SP/co-op only entities (b#4) // // 5. Added difficulty spawnflags to monster-related entities (b#5) // // 6. Fixed 2x monster_gunner with weird difficulty spawnflags (b#6) // // They had spawnflags 1024 (not hard/nightmare). It is much more likely // the intention was 768 (not easy + not medium) and the mapper thought // for a moment the spawnflags were inclusive instead of exclusive. // // 7. Fixed missing pumping sounds in DM (b#7) // // In DM the big pump has already been activated but is missing its sound // effect. Added a trigger_always that starts those sounds { "sounds" "3" "classname" "worldspawn" "light" "80" "message" "Pumping Station 2" "sky" "unit6_" "nextmap" "biggun" } { "origin" "-848 -1520 -176" "spawnflags" "2048" "targetname" "t121" "message" "You have found a secret." "classname" "target_secret" } { "model" "*1" "target" "t121" "spawnflags" "2048" "classname" "trigger_once" } { "model" "*2" "classname" "func_door" "angle" "360" "team" "td5" "_minlight" ".18" } { "model" "*3" "team" "thor1" "_minlight" ".18" "angle" "360" "classname" "func_door" } { "model" "*4" "wait" "-1" "spawnflags" "8" "targetname" "t73" "classname" "func_door" "angle" "270" "team" "ky1" "_minlight" ".18" } { "classname" "light" "light" "200" "_color" "1.000000 1.000000 0.168627" "_style" "4" "origin" "-448 -2014 -180" } { "classname" "light" "light" "200" "_color" "1.000000 1.000000 0.168627" "_style" "4" "origin" "-588 -2014 -180" } { "classname" "light" "light" "200" "_color" "1.000000 1.000000 0.168627" "_style" "4" "origin" "-544 -1856 -180" } { "model" "*5" "target" "t120" "health" "20" "_minlight" ".2" "mass" "200" "classname" "func_explosive" } { "origin" "-174 -2340 26" "target" "t119" "targetname" "t2" "spawnflags" "2048" "wait" "8" "random" "6" "classname" "func_timer" } { "origin" "-148 -2334 58" "targetname" "t119" "sounds" "1" "angle" "-2" "classname" "target_splash" } { "origin" "-160 -2348 76" "classname" "misc_explobox" } { "origin" "-232 -2388 76" "classname" "misc_explobox" } { "model" "*6" "target" "t2" "mass" "200" "health" "10" "classname" "func_explosive" } { "targetname" "waste2" "angle" "225" "origin" "-624 -752 -64" "classname" "info_player_coop" } { "targetname" "waste2" "classname" "info_player_coop" "origin" "-720 -808 -64" "angle" "225" } { "targetname" "waste2" "angle" "270" "origin" "-592 -584 -64" "classname" "info_player_coop" } { "origin" "-2720 -1376 180" "targetname" "waste1" "classname" "info_player_coop" "angle" "270" } { "origin" "-2832 -1376 180" "targetname" "waste1" "angle" "270" "classname" "info_player_coop" } { "origin" "-2776 -1480 180" "targetname" "waste1" "angle" "270" "classname" "info_player_coop" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "-232 -2464 164" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "-232 -2504 164" } { "model" "*7" "classname" "func_wall" "spawnflags" "1792" } { "origin" "-1136 -2620 -64" "spawnflags" "16" "angle" "180" "classname" "misc_deadsoldier" } { "origin" "-1152 -2656 -40" "spawnflags" "2048" "classname" "item_health_mega" } { "origin" "-1572 -1852 -268" "angle" "45" "spawnflags" "8" "classname" "misc_deadsoldier" } { "model" "*8" "spawnflags" "2048" "classname" "func_wall" } { "origin" "-1616 -1400 92" "angle" "315" "spawnflags" "8" "classname" "misc_deadsoldier" } { "spawnflags" "2048" "attenuation" "-1" "targetname" "t4" "noise" "world/pumping.wav" "origin" "-48 -2200 104" "classname" "target_speaker" } { "model" "*9" "classname" "func_wall" "spawnflags" "1792" } { "model" "*10" "classname" "func_wall" "spawnflags" "1792" } { "model" "*11" "classname" "func_wall" "spawnflags" "1792" } { "model" "*12" "spawnflags" "8" "health" "1" "angle" "-1" "classname" "func_door" } { "classname" "item_quad" "spawnflags" "2048" "origin" "-2656 -1668 -40" } { "model" "*13" "classname" "trigger_once" "spawnflags" "2048" "target" "t118" } { "model" "*14" "classname" "func_button" "wait" "-1" "angle" "0" "spawnflags" "2048" "lip" "8" "targetname" "t4" } { "model" "*15" "classname" "func_door" "spawnflags" "2056" "angle" "-2" "speed" "500" "target" "t4" "message" "Pumping Station Two\nactivated." "wait" "-1" "targetname" "t118" } { "origin" "-248 -2130 122" "target" "t5" "spawnflags" "1792" "classname" "trigger_always" } { "origin" "-1896 -1456 116" "spawnflags" "1792" "classname" "weapon_grenadelauncher" } { // b#11 "message" "You have found a secret." "origin" "-1640 -1300 156" "spawnflags" "2048" "targetname" "t117" "classname" "target_secret" } { "origin" "-2844 -1700 116" "spawnflags" "1792" "classname" "ammo_slugs" } { "origin" "-1624 -1412 116" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "0 -2528 108" "angle" "135" "classname" "info_player_deathmatch" } { "origin" "296 -2696 212" "angle" "180" "classname" "info_player_deathmatch" } { "origin" "-656 -3040 -36" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-656 -2932 -36" "angle" "180" "classname" "info_player_deathmatch" } { "origin" "-1332 -2404 -244" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "-1848 -1024 -56" "angle" "180" "classname" "info_player_deathmatch" } { "origin" "-2300 -1448 116" "classname" "info_player_deathmatch" "angle" "270" } { "origin" "-2296 -1872 116" "angle" "90" "classname" "info_player_deathmatch" } { "origin" "-2760 -1792 240" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-2832 -1864 248" "angle" "45" "classname" "info_player_deathmatch" } { "origin" "-2792 -1400 180" "angle" "270" "classname" "info_player_deathmatch" } { "origin" "-1696 -1984 116" "angle" "45" "classname" "info_player_deathmatch" } { "origin" "-744 -1992 116" "angle" "135" "classname" "info_player_deathmatch" } { "origin" "-520 -828 -80" "angle" "180" "classname" "info_player_deathmatch" } { "origin" "-1622 -1238 156" "spawnflags" "2048" "classname" "item_armor_body" "target" "t117" // b#12 added this } { "origin" "-1622 -1238 146" "spawnflags" "1792" "classname" "item_invulnerability" } { "origin" "-2444 -1888 112" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-1828 -1272 116" "spawnflags" "1792" "classname" "ammo_slugs" } { "origin" "-788 -800 -88" "spawnflags" "1792" "classname" "ammo_bullets" } { "origin" "-824 -824 -88" "spawnflags" "1792" "classname" "weapon_machinegun" } { "origin" "-744 -1328 120" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-824 -1140 120" "spawnflags" "1792" "classname" "ammo_cells" } { "classname" "item_armor_shard" "origin" "-844 -1140 116" "spawnflags" "2048" } { "classname" "item_armor_shard" "origin" "-884 -1140 116" "spawnflags" "0" } { "model" "*16" "spawnflags" "2048" "classname" "func_wall" } { "style" "1" "targetname" "t116" "classname" "func_areaportal" } { "origin" "-2600 -1328 100" "target" "t115" "spawnflags" "1792" "classname" "trigger_always" } { "model" "*17" "lip" "-8" "target" "t116" "targetname" "t115" "spawnflags" "8" "wait" "-1" "angle" "-1" "classname" "func_door" } { "origin" "-2592 -1244 -48" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-2792 -1372 -48" "classname" "item_health_small" "spawnflags" "1792" } { "origin" "-2792 -1332 -48" "classname" "item_armor_shard" "spawnflags" "1792" } { "origin" "-2792 -1452 -48" "classname" "item_health_small" "spawnflags" "1792" } { "origin" "-2792 -1412 -48" "classname" "item_armor_shard" "spawnflags" "1792" } { "origin" "-2792 -1532 -48" "classname" "item_health_small" "spawnflags" "1792" } { "origin" "-2792 -1492 -48" "classname" "item_armor_shard" "spawnflags" "1792" } { "origin" "-2792 -1252 -48" "spawnflags" "1792" "classname" "item_armor_shard" } { "origin" "-2792 -1292 -48" "spawnflags" "1792" "classname" "item_health_small" } { "model" "*18" "spawnflags" "2048" "classname" "func_wall" } { "model" "*19" "spawnflags" "1792" "classname" "func_wall" } { "model" "*20" "spawnflags" "1792" "classname" "func_wall" } { "classname" "trigger_always" "spawnflags" "1792" "target" "t73" "origin" "-1800 -1456 172" } { "classname" "item_health" "origin" "-2844 -1740 116" } { "spawnflags" "1792" "classname" "ammo_grenades" "origin" "-2528 -1868 124" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "-2568 -1868 124" } { "classname" "ammo_bullets" "spawnflags" "1792" "origin" "-2576 -1424 120" } { "classname" "weapon_machinegun" "spawnflags" "1792" "origin" "-2612 -1456 120" } { "spawnflags" "1792" "classname" "ammo_bullets" "origin" "-2152 -860 -28" } { "classname" "ammo_bullets" "spawnflags" "1792" "origin" "-2072 -860 -28" } { "classname" "weapon_machinegun" "spawnflags" "1792" "origin" "-2112 -876 -28" } { "classname" "weapon_railgun" "spawnflags" "1792" "origin" "-2112 -668 0" } { "classname" "ammo_shells" "spawnflags" "1792" "origin" "-1196 -1960 112" } { "classname" "weapon_shotgun" "spawnflags" "1792" "origin" "-1180 -1920 112" } { "spawnflags" "1792" "classname" "ammo_grenades" "origin" "-596 -1568 108" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "-364 -1568 108" } { "classname" "ammo_bullets" "spawnflags" "1792" "origin" "-80 -2088 100" } { "classname" "weapon_chaingun" "spawnflags" "1792" "origin" "-80 -2124 100" } { "spawnflags" "1792" "classname" "ammo_bullets" "origin" "-408 -2708 216" } { "classname" "ammo_bullets" "spawnflags" "1792" "origin" "-368 -2708 216" } { "classname" "item_armor_combat" "spawnflags" "1792" "origin" "-372 -2792 212" } { "classname" "ammo_shells" "spawnflags" "1792" "origin" "184 -2492 -204" } { "classname" "weapon_supershotgun" "spawnflags" "1792" "origin" "224 -2492 -204" } { "classname" "item_health_mega" "spawnflags" "1792" "origin" "-588 -1872 -236" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "-176 -2672 -224" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "-216 -2672 -224" } { "spawnflags" "1792" "classname" "item_armor_shard" "origin" "-256 -2672 -224" } { "classname" "item_armor_shard" "spawnflags" "1792" "origin" "-136 -2672 -224" } { "classname" "item_health" "spawnflags" "1792" "origin" "-796 -2932 -252" } { "spawnflags" "1792" "classname" "item_health_small" "origin" "-1552 -2240 -40" } { "spawnflags" "1792" "classname" "item_health_small" "origin" "-1516 -2240 -40" } { "classname" "item_health_small" "spawnflags" "1792" "origin" "-1588 -2240 -40" } { "spawnflags" "1792" "classname" "ammo_grenades" "origin" "-1248 -3128 -44" } { "classname" "ammo_grenades" "spawnflags" "1792" "origin" "-1288 -3128 -44" } { "classname" "item_quad" "spawnflags" "1792" "team" "w2" "origin" "-1152 -2656 -48" } { "model" "*21" "classname" "func_wall" "spawnflags" "1792" } { "model" "*22" "spawnflags" "2048" "classname" "func_wall" } { "model" "*23" "spawnflags" "2048" "classname" "func_wall" } { "origin" "-1820 -2912 -248" "classname" "item_health" "spawnflags" "1792" } { "origin" "-1820 -2952 -248" "spawnflags" "1792" "classname" "item_health" } { "origin" "-1608 -2904 -248" "spawnflags" "1792" "classname" "ammo_bullets" } { "origin" "-1568 -2932 -252" "spawnflags" "1792" "classname" "weapon_machinegun" } { "origin" "-1548 -2100 -248" "classname" "item_armor_shard" "spawnflags" "1792" } { "origin" "-1548 -2056 -248" "classname" "item_armor_shard" "spawnflags" "1792" } { "origin" "-1548 -2144 -248" "spawnflags" "1792" "classname" "item_armor_shard" } { "origin" "-1576 -1784 -240" "spawnflags" "1792" "classname" "ammo_grenades" } { "origin" "-1552 -1824 -240" "spawnflags" "1792" "classname" "weapon_grenadelauncher" } { "origin" "-1628 -1752 -252" "spawnflags" "1792" "classname" "item_health_large" } { "origin" "-1616 -1440 -152" "spawnflags" "1792" "classname" "item_enviro" } { "origin" "-800 -1400 -240" "spawnflags" "1792" "classname" "ammo_cells" } { "origin" "-824 -1440 -240" "spawnflags" "1792" "team" "w1" "classname" "weapon_bfg" } { "origin" "-824 -1440 -240" "team" "w1" "spawnflags" "1792" "classname" "item_armor_body" } { "origin" "-2534 -1668 72" "angles" "80 0 0" "classname" "info_player_intermission" } { "light" "140" "classname" "light" "origin" "-1624 -1432 176" } { "spawnflags" "0" "origin" "-2280 -1888 112" "classname" "item_health_large" } { "spawnflags" "0" "origin" "-2280 -1848 112" "classname" "item_health_large" } { "classname" "point_combat" "targetname" "t114" "spawnflags" "769" // b#5: was 1 "origin" "-832 -1320 120" } { "classname" "monster_gladiator" "angle" "270" "spawnflags" "769" "target" "t114" "origin" "-832 -1288 136" } { "spawnflags" "1" "classname" "point_combat" "targetname" "t112" "origin" "-200 -2432 -248" } { "classname" "point_combat" "spawnflags" "1" "targetname" "t113" "origin" "-200 -2672 -248" } { "spawnflags" "1" "angle" "180" "classname" "monster_soldier" "target" "t112" "origin" "-128 -2432 -224" } { "classname" "monster_soldier" "angle" "180" "spawnflags" "1" "target" "t113" "origin" "-128 -2672 -224" } { "classname" "monster_soldier" "angle" "180" "spawnflags" "1" "origin" "-448 -2536 -232" } { "classname" "monster_soldier" "angle" "180" "spawnflags" "1" "origin" "-448 -2576 -232" } { "classname" "point_combat" "spawnflags" "1" "targetname" "t111" "origin" "-1608 -2968 -256" } { "classname" "point_combat" "spawnflags" "1" "targetname" "t110" "origin" "-1816 -2768 -256" } { "origin" "-440 -2312 -272" "angle" "270" "spawnflags" "1" "classname" "misc_deadsoldier" } { "origin" "-896 -2936 -248" "target" "t105" "targetname" "t104" "wait" "4" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-896 -2576 -248" "target" "t106" "targetname" "t105" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-640 -2576 -248" "target" "t107" "targetname" "t106" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-964 -2936 -248" "target" "t101" "targetname" "t100" "classname" "path_corner" "spawnflags" "2048" // b#4: added this "wait" "4" } { "origin" "-964 -2524 -248" "target" "t100" "targetname" "t99" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-640 -2524 -248" "target" "t99" "targetname" "t98" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-896 -2576 -248" "target" "t109" "targetname" "t108" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-896 -2936 -248" "target" "t104" "targetname" "t109" "classname" "path_corner" "spawnflags" "2048" // b#4: added this "wait" "4" } { "origin" "-640 -2576 -248" "target" "t108" "targetname" "t107" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-964 -2524 -248" "target" "t103" "targetname" "t102" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-640 -2524 -248" "targetname" "t103" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-964 -2936 -248" "target" "t102" "targetname" "t101" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "180 -2548 -204" "target" "t97" "targetname" "t96" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-324 -2548 -244" "target" "t96" "targetname" "t97" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "208 -2716 -188" "classname" "monster_soldier" "angle" "90" } { "spawnflags" "1" "target" "t96" "origin" "204 -2548 -188" "classname" "monster_soldier" "angle" "180" } { "target" "t98" "origin" "-608 -2524 -232" "classname" "monster_soldier" "angle" "180" } { "target" "t104" "origin" "-896 -2968 -232" "classname" "monster_soldier" "angle" "90" } { "origin" "252 -2716 -188" "angle" "90" "classname" "monster_soldier" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "origin" "-2656 -1728 120" "targetname" "t95" "spawnflags" "1" "classname" "point_combat" } { "targetname" "t94" "origin" "-2784 -1664 120" "spawnflags" "1" "classname" "point_combat" } { "targetname" "t32" "target" "t95" "origin" "-2600 -1856 136" "classname" "monster_soldier" "angle" "90" } { "item" "ammo_shells" "targetname" "t32" "target" "t94" "origin" "-2784 -1424 192" "angle" "270" "classname" "monster_soldier" } { "origin" "-2656 -1584 104" "targetname" "t93" "spawnflags" "1" "classname" "point_combat" } { "targetname" "t32" "origin" "-2656 -1480 120" "target" "t93" "angle" "270" "classname" "monster_soldier" } { "spawnflags" "0" "classname" "ammo_bullets" "origin" "8 -1884 96" } { "origin" "-128 -2204 120" "targetname" "t3" "delay" "3" "target" "t92" "classname" "trigger_relay" "spawnflags" "2048" // b#4: added this } { "spawnflags" "2048" "origin" "-1928 -640 -24" "classname" "item_health" } { "spawnflags" "2048" "origin" "-2296 -640 -24" "classname" "item_health" } { "origin" "-2152 -544 200" "targetname" "t90" "classname" "point_combat" } { "origin" "-2072 -544 200" "targetname" "t91" "classname" "point_combat" } { "origin" "-2144 -376 216" "targetname" "t59" "angle" "270" "target" "t90" "spawnflags" "2" "classname" "monster_floater" } { "origin" "-2080 -376 216" "targetname" "t59" "angle" "270" "target" "t91" "spawnflags" "770" "classname" "monster_floater" } { "light" "150" "origin" "-2152 -356 264" "classname" "light" } { "light" "150" "origin" "-2040 -740 136" "classname" "light" } { "target" "t89" "targetname" "t88" "origin" "-2176 -448 168" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "target" "t88" "targetname" "t87" "origin" "-2176 -480 168" "classname" "path_corner" } { "targetname" "t89" "origin" "-2176 -448 272" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "model" "*24" "targetname" "t59" "speed" "50" "target" "t87" "classname" "func_train" } { "model" "*25" "spawnflags" "2048" "angle" "90" "classname" "trigger_push" } { "spawnflags" "2048" "origin" "-2804 -1436 172" "classname" "ammo_shells" } { "origin" "-2832 -1456 156" "angle" "225" "spawnflags" "2048" "classname" "misc_deadsoldier" } { "spawnflags" "0" "origin" "-2428 -1432 120" "classname" "item_armor_shard" } { "spawnflags" "0" "origin" "-2388 -1432 120" "classname" "item_armor_shard" } { "spawnflags" "0" "origin" "-2348 -1432 120" "classname" "item_armor_shard" } { "spawnflags" "2048" "origin" "-1828 -1272 116" "classname" "ammo_rockets" } { "spawnflags" "0" "origin" "-2392 -1272 116" "classname" "item_health" } { "spawnflags" "0" "origin" "-1928 -1496 -88" "classname" "item_health_small" } { "spawnflags" "0" "origin" "-1888 -1496 -88" "classname" "item_health_small" } { "spawnflags" "0" "origin" "-1968 -1496 -88" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "-1472 -1912 -252" "classname" "item_health_large" } { "spawnflags" "2048" "origin" "-1620 -1912 -252" "classname" "item_health_large" } { "origin" "-816 -1456 -272" "angle" "225" "spawnflags" "2048" "classname" "misc_deadsoldier" } { "spawnflags" "2048" "origin" "-848 -1448 -240" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "-800 -1448 -240" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "-1816 -2400 -248" "classname" "item_health" } { "spawnflags" "2048" "origin" "-1336 -2400 -248" "classname" "item_health" } { "origin" "-1816 -2968 -240" "classname" "monster_gunner" "angle" "45" "spawnflags" "1" "target" "t110" } { "origin" "-1760 -2968 -240" "spawnflags" "1" "angle" "45" "classname" "monster_gunner" "target" "t111" } { "origin" "228 -2556 -112" "spawnflags" "1" "classname" "monster_floater" "angle" "180" } { "origin" "240 -2632 -112" "spawnflags" "1" "angle" "225" "classname" "monster_floater" } { "spawnflags" "0" "origin" "320 -2648 204" "classname" "ammo_shells" } { "spawnflags" "0" "origin" "280 -2648 204" "classname" "ammo_shells" } { "spawnflags" "2048" "origin" "-32 -1884 96" "classname" "ammo_bullets" } { "spawnflags" "0" "origin" "-512 -2524 108" "classname" "item_health_large" } { "spawnflags" "0" "origin" "-364 -1608 108" "classname" "item_health" } { "spawnflags" "0" "origin" "-596 -1608 108" "classname" "item_health" } { "spawnflags" "2048" "origin" "-804 -1140 116" "classname" "item_armor_shard" } { "spawnflags" "0" "origin" "-764 -1140 116" "classname" "item_armor_shard" } { "origin" "-892 -1160 100" "angle" "135" "spawnflags" "2048" "classname" "misc_deadsoldier" } { "origin" "-1172 -1332 104" "targetname" "t86" "spawnflags" "769" // b#5: was 1 "classname" "point_combat" } { "model" "*26" "spawnflags" "2048" "target" "t85" "classname" "trigger_once" } { "spawnflags" "768" // b#6: was 1024 "origin" "-1104 -1232 120" "target" "t86" "targetname" "t85" "classname" "monster_gunner" "angle" "180" } { "origin" "-1144 -1260 120" "item" "ammo_grenades" "targetname" "t85" "angle" "180" "classname" "monster_gunner" } { "spawnflags" "0" "origin" "-1660 -1368 124" "classname" "item_health" } { "spawnflags" "0" "origin" "-1588 -1368 124" "classname" "item_health" } { "spawnflags" "2048" "origin" "-748 -1988 116" "classname" "item_health_large" } { "spawnflags" "2048" "origin" "-588 -848 -88" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "-672 -852 -88" "classname" "item_health_small" } { "spawnflags" "2048" "origin" "-528 -828 -88" "classname" "ammo_cells" } { "origin" "-516 -812 -104" "angle" "180" "spawnflags" "2048" "classname" "misc_deadsoldier" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" "origin" "48 -2560 -144" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb15.wav" "spawnflags" "1" "origin" "-588 -1920 -200" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb15.wav" "spawnflags" "1" "origin" "-520 -2020 -200" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb15.wav" "spawnflags" "1" "origin" "-440 -2152 -200" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb15.wav" "spawnflags" "1" "origin" "-440 -2316 -200" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" "origin" "-256 -2560 -200" } { "origin" "12 -2204 16" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-316 -2560 -200" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-560 -2560 -200" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "volume" ".3" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-512 -2560 -180" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-592 -2212 -136" } { "origin" "232 -2660 -144" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-588 -1992 -200" "spawnflags" "1" "noise" "world/drip_amb.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-68 -2560 -132" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-832 -2560 -200" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-924 -2808 -200" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-924 -2760 -128" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-992 -2932 -128" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-840 -2920 -268" "angle" "135" "spawnflags" "2052" "classname" "misc_deadsoldier" } { "origin" "-820 -2904 -240" "classname" "item_pack" "spawnflags" "2048" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-776 -2932 -128" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "232 -2488 -176" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-1252 -2932 -128" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-1568 -2932 -128" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-1564 -2880 -228" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-1340 -2344 -228" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-1556 -2456 -228" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-1360 -2652 -228" } { "origin" "-1796 -2344 -228" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-1764 -2656 -228" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-792 -2932 -256" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" "volume" ".3" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump1.wav" "spawnflags" "1" "origin" "-1564 -2880 -164" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump1.wav" "spawnflags" "1" "origin" "-1764 -2656 -164" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump1.wav" "spawnflags" "1" "origin" "-1556 -2456 -164" } { "origin" "-1552 -2260 -228" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-1552 -2136 -228" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-1360 -2652 -164" "spawnflags" "1" "noise" "world/pump1.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-1552 -2028 -228" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-1624 -1904 -108" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-1052 -1908 -184" "wait" "13" "random" "6" "spawnflags" "1" "target" "t83" "classname" "func_timer" } { "origin" "-812 -1804 -192" "wait" "9" "random" "5" "spawnflags" "1" "target" "t84" "classname" "func_timer" } { "origin" "-1280 -1920 -204" "wait" "12" "random" "4" "spawnflags" "1" "target" "t82" "classname" "func_timer" } { "targetname" "t82" "origin" "-1300 -1916 -184" "spawnflags" "0" "noise" "world/drip1.wav" "classname" "target_speaker" "volume" ".3" } { "targetname" "t83" "origin" "-1048 -1896 -184" "spawnflags" "0" "noise" "world/drip1.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-1060 -1896 -108" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "volume" ".3" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" "origin" "-824 -1832 -108" } { "targetname" "t84" "volume" ".3" "classname" "target_speaker" "noise" "world/drip1.wav" "spawnflags" "0" "origin" "-816 -1784 -192" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" "origin" "-1288 -1900 -108" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-1552 -2124 -228" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" "origin" "-1620 -1504 -108" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" "origin" "-1980 -1500 -40" } { "target" "t80" "classname" "func_timer" "origin" "-1624 -1344 -96" "spawnflags" "1" "random" "6" "wait" "13" } { "target" "t81" "classname" "func_timer" "origin" "-1684 -1336 -96" "spawnflags" "1" "random" "3" "wait" "10" } { "target" "t79" "wait" "11" "random" "8" "spawnflags" "1" "origin" "-1560 -1344 -96" "classname" "func_timer" } { "targetname" "t80" "origin" "-1624 -1320 -96" "spawnflags" "0" "noise" "world/steam1.wav" "classname" "target_speaker" "volume" ".3" } { "targetname" "t81" "origin" "-1688 -1320 -96" "spawnflags" "0" "noise" "world/steam1.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-1828 -1500 -40" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-824 -1420 -248" "spawnflags" "1" "noise" "world/amb16.wav" "classname" "target_speaker" "volume" ".3" } { "targetname" "t79" "volume" ".3" "classname" "target_speaker" "noise" "world/steam1.wav" "spawnflags" "0" "origin" "-1560 -1320 -96" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb16.wav" "spawnflags" "1" "origin" "-2144 -1304 -40" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-2416 -1232 -40" } { "origin" "-2112 -516 208" "spawnflags" "1" "noise" "world/pump2.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-2288 -1204 -40" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-2120 -1500 -40" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" "volume" ".3" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-1932 -1200 -40" } { "volume" ".3" "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-2272 -620 28" } { "origin" "-2112 -516 168" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-1952 -616 28" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-1808 -1232 -40" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-2240 -808 52" "spawnflags" "1" "noise" "world/pump1.wav" "classname" "target_speaker" "volume" ".3" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-2240 -964 224" } { "origin" "-2240 -964 124" "spawnflags" "1" "noise" "world/pump1.wav" "classname" "target_speaker" "volume" ".3" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump1.wav" "spawnflags" "1" "origin" "-1984 -964 124" } { "origin" "-1984 -964 224" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "volume" ".3" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-2240 -1204 224" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-2112 -1204 224" } { "origin" "-2240 -1204 124" "spawnflags" "1" "noise" "world/pump1.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-2112 -1204 124" "spawnflags" "1" "noise" "world/pump1.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-1984 -1204 224" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "volume" ".3" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump1.wav" "spawnflags" "1" "origin" "-1984 -1204 124" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-1984 -808 152" } { "origin" "-2240 -808 152" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "volume" ".3" } { "volume" ".3" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" "origin" "-2112 -656 312" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-2120 -1420 224" } { "origin" "-2784 -1376 228" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-2712 -1668 124" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-2668 -1420 124" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" } { "origin" "-2752 -1476 428" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "origin" "-2748 -1728 428" "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" } { "origin" "-2608 -1896 148" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" } { "origin" "-2352 -1816 148" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/fan1.wav" "spawnflags" "1" "origin" "-2480 -1668 -28" } { "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-2780 -1184 232" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "-2144 -1656 148" } { "origin" "-2476 -1584 296" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "volume" ".3" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-2476 -1584 64" } { "origin" "-2476 -1740 64" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-2308 -1452 224" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-2312 -1668 224" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-2124 -1664 224" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-1984 -808 52" "spawnflags" "1" "noise" "world/pump1.wav" "classname" "target_speaker" "volume" ".3" } { "origin" "-2120 -1420 148" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-2480 -1668 64" "spawnflags" "1" "noise" "world/fan1.wav" "classname" "target_speaker" } { "origin" "-1928 -1656 148" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-1832 -1464 148" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-1624 -1504 148" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-232 -2780 264" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-380 -2656 264" "spawnflags" "1" "noise" "world/comp_hum3.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/comp_hum2.wav" "spawnflags" "1" "origin" "-16 -2648 264" } { "classname" "target_speaker" "noise" "world/comp_hum2.wav" "spawnflags" "1" "origin" "-240 -2912 264" } { "origin" "64 -2928 244" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" } { "origin" "-208 -2928 244" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" } { "origin" "-440 -2752 244" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/comp_hum3.wav" "spawnflags" "1" "origin" "-384 -2868 264" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "244 -2804 264" } { "origin" "128 -2684 264" "spawnflags" "1" "noise" "world/comp_hum3.wav" "classname" "target_speaker" } { "origin" "-128 -2756 264" "spawnflags" "1" "noise" "world/comp_hum2.wav" "classname" "target_speaker" } { "origin" "-128 -2756 236" "spawnflags" "1" "noise" "world/comp_hum1.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/comp_hum1.wav" "spawnflags" "1" "origin" "-128 -2860 236" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "336 -2752 244" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-28 -2372 112" } { "targetname" "t4" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "2" "origin" "-84 -2012 160" } { "targetname" "t4" "origin" "-400 -2016 112" "spawnflags" "2" "noise" "world/pump1.wav" "classname" "target_speaker" } { "targetname" "t4" "classname" "target_speaker" "noise" "world/pump1.wav" "spawnflags" "2" "origin" "-236 -2172 112" } { "targetname" "t4" "classname" "target_speaker" "noise" "world/pump1.wav" "spawnflags" "2" "origin" "-84 -2012 112" } { "targetname" "t4" "classname" "target_speaker" "noise" "world/pump1.wav" "spawnflags" "2" "origin" "-240 -1856 112" } { "targetname" "t4" "origin" "-236 -2172 160" "spawnflags" "2" "noise" "world/pump3.wav" "classname" "target_speaker" } { "targetname" "t4" "origin" "-240 -2012 200" "spawnflags" "2" "noise" "world/pump2.wav" "classname" "target_speaker" } { "targetname" "t4" "origin" "-240 -1856 160" "spawnflags" "2" "noise" "world/pump3.wav" "classname" "target_speaker" } { "origin" "-28 -2048 112" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" } { "targetname" "t4" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "2" "origin" "-400 -2016 160" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-8 -2208 112" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "-480 -1760 148" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "-476 -1568 148" } { "origin" "-344 -1684 220" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-344 -1572 220" } { "origin" "-612 -1572 220" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" } { "origin" "-128 -2860 264" "spawnflags" "1" "noise" "world/comp_hum2.wav" "classname" "target_speaker" } { "origin" "-480 -1368 148" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-612 -1684 220" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "-672 -1232 148" } { "origin" "-480 -1236 148" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" } { "origin" "-980 -1232 148" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-1172 -1288 148" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-1304 -1484 148" } { "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" "origin" "-824 -1140 148" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "-1172 -1484 148" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "-1408 -1484 148" } { "volume" ".3" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" "origin" "-2476 -1740 296" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "-1624 -1728 148" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "-1488 -1920 148" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "-1228 -1920 148" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "-992 -1920 148" } { "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" "origin" "-828 -1780 148" } { "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" "volume" ".2" "origin" "-1624 -1668 240" } { "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" "volume" ".2" "origin" "-1584 -1920 240" } { "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" "volume" ".2" "origin" "-1344 -1920 240" } { "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" "volume" ".2" "origin" "-1112 -1920 240" } { "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" "volume" ".2" "origin" "-828 -1880 236" } { "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" "volume" ".2" "origin" "-828 -1628 236" } { "spawnflags" "1" "noise" "world/wind2.wav" "classname" "target_speaker" "volume" ".2" "origin" "-828 -1368 236" } { "origin" "-828 -1404 16" "spawnflags" "1" "noise" "world/amb14.wav" "classname" "target_speaker" } { "origin" "-828 -1572 84" "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" } { "origin" "-1624 -1384 240" "volume" ".2" "classname" "target_speaker" "noise" "world/wind2.wav" "spawnflags" "1" } { "origin" "-824 -1228 -28" "classname" "target_speaker" "noise" "world/amb14.wav" "spawnflags" "1" } { "origin" "-1172 -1384 148" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" } { "origin" "-824 -1128 -28" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" } { "origin" "-824 -948 -28" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" } { "origin" "-728 -824 -28" "spawnflags" "1" "noise" "world/pump3.wav" "classname" "target_speaker" } { "origin" "-616 -824 -28" "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" } { "origin" "-824 -1100 12" "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" } { "origin" "-592 -760 -28" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" } { "origin" "-592 -548 -28" "classname" "target_speaker" "noise" "world/pump3.wav" "spawnflags" "1" } { "origin" "-592 -560 68" "classname" "target_speaker" "noise" "world/amb7.wav" "spawnflags" "1" } { "origin" "-824 -904 -28" "spawnflags" "1" "noise" "world/amb7.wav" "classname" "target_speaker" } { "spawnflags" "2048" "classname" "ammo_bullets" "origin" "-816 -1948 108" } { "spawnflags" "2048" "classname" "ammo_bullets" "origin" "-904 -1976 108" } { "classname" "misc_deadsoldier" "spawnflags" "2050" "angle" "0" "origin" "-788 -1936 92" } { "classname" "trigger_relay" "spawnflags" "2816" // b#4, b#5: added this "targetname" "t77" "target" "t78" "delay" "4" "origin" "-832 -1352 0" } { "classname" "point_combat" "targetname" "t75" "spawnflags" "769" // b#5: was 1 "origin" "-832 -1212 120" } { "spawnflags" "768" // b#6: was 1024 "classname" "monster_gunner" "angle" "0" "target" "t75" "targetname" "t78" "item" "ammo_grenades" "origin" "-1052 -1236 120" } { "model" "*27" "spawnflags" "3072" // b#5: was 2048 "classname" "trigger_once" "target" "t77" } { "classname" "target_secret" "targetname" "t74" "origin" "-2632 -1672 -48" "spawnflags" "2048" "message" "You have found a secret." // b#3: moved this here from the trigger } { "model" "*28" "spawnflags" "2048" "classname" "trigger_once" "target" "t74" } { "classname" "trigger_key" "targetname" "t72" "item" "key_blue_key" "target" "t73" "origin" "-1800 -1448 184" "spawnflags" "2048" } { "model" "*29" "classname" "trigger_multiple" "target" "t72" "spawnflags" "2048" } { "classname" "target_goal" "targetname" "t71" "origin" "-344 -2772 224" "spawnflags" "2048" } { "spawnflags" "2048" "message" "You have found a secret." "classname" "target_secret" "targetname" "t70" "origin" "-628 -1916 -228" } { "model" "*30" "targetname" "t120" "spawnflags" "2048" "classname" "trigger_once" "target" "t70" } { "spawnflags" "2048" "classname" "key_blue_key" "origin" "-392 -2792 232" "target" "t71" } { "spawnflags" "2048" "targetname" "t92" "classname" "target_help" "message" "Proceed to reactor core.\nKill all resistance." "origin" "-98 -2232 136" } { "spawnflags" "2048" "classname" "target_goal" "targetname" "t4" "origin" "-8 -2196 120" "message" "Pumping Station Two\nactivated." } { "spawnflags" "2048" "origin" "-2112 -672 40" "target" "t59" "classname" "item_enviro" } { "style" "2" "targetname" "t67" "classname" "func_areaportal" } { "spawnflags" "2048" "classname" "misc_explobox" "origin" "72 -2812 200" } { "spawnflags" "2048" "classname" "misc_explobox" "origin" "92 -2864 200" } { "classname" "point_combat" "targetname" "t65" "origin" "-2156 -992 120" } { "classname" "monster_gunner" "angle" "90" "spawnflags" "3" "targetname" "t59" "target" "t65" "origin" "-2372 -1148 152" } { "classname" "point_combat" "targetname" "t64" "origin" "-1984 -756 152" } { "model" "*31" "spawnflags" "2048" "classname" "trigger_once" "target" "t63" } { "classname" "monster_floater" "angle" "90" "spawnflags" "3" "targetname" "t60" "origin" "-1984 -892 152" "target" "t64" } { "dmg" "10" "classname" "target_explosion" "spawnflags" "2048" // b#4: added this "delay" ".2" "targetname" "t61" "origin" "-1944 -824 116" } { "dmg" "10" "classname" "target_explosion" "spawnflags" "2048" // b#4: added this "targetname" "t60" "target" "t61" "origin" "-2036 -828 148" } { "classname" "light" "origin" "-2072 -356 264" "light" "150" } { "model" "*32" "classname" "func_explosive" "dmg" "35" "mass" "400" "targetname" "t59" "target" "t60" } { "classname" "func_group" } { "team" "w2" "origin" "-2656 -1668 -40" "classname" "item_quad" "spawnflags" "1792" } { "spawnflags" "2048" "classname" "item_adrenaline" "origin" "-824 -1400 -248" } { "spawnflags" "0" "classname" "item_health" "origin" "304 -2920 208" } { "spawnflags" "0" "classname" "item_health" "origin" "264 -2920 208" } { "item" "ammo_cells" "classname" "monster_floater" "angle" "270" "spawnflags" "2" "targetname" "t48" "origin" "-800 -1600 -120" } { "classname" "point_combat" "spawnflags" "1" "targetname" "t56" "origin" "-2192 -640 0" } { "item" "ammo_bullets" "classname" "monster_gunner" "angle" "90" "spawnflags" "0" "target" "t56" "origin" "-2112 -1008 120" } { "classname" "monster_berserk" "spawnflags" "1" "angle" "270" "targetname" "t39" "origin" "-2376 -1064 136" } { "classname" "monster_berserk" "targetname" "t39" "angle" "270" "spawnflags" "1" "origin" "-1848 -1064 136" } { "classname" "monster_berserk" "angle" "180" "spawnflags" "1" "target" "t12" "origin" "-1144 -1896 136" } { "classname" "monster_berserk" "angle" "270" "spawnflags" "1" "target" "t20" "origin" "-1680 -1448 136" } { "item" "ammo_bullets" "origin" "-20 -2648 224" "angle" "315" "spawnflags" "1" "classname" "monster_gunner" "targetname" "t63" } { "origin" "-460 -2020 112" "target" "t26" "angle" "90" "spawnflags" "1" "classname" "monster_tank" } { "origin" "-2312 -1760 104" "targetname" "t52" "spawnflags" "1" "classname" "point_combat" } { "spawnflags" "2048" "origin" "-1844 -1500 -64" "classname" "item_armor_body" } { "origin" "-624 -1856 -180" "classname" "light" "light" "200" "_color" "1.000000 1.000000 0.168627" "_style" "4" } { "origin" "-442 -2194 -180" "_style" "4" "_color" "1.000000 1.000000 0.168627" "light" "200" "classname" "light" } { "spawnflags" "2048" "origin" "-588 -1876 -236" "classname" "item_invulnerability" } { "model" "*33" "target" "t51" "classname" "trigger_once" "spawnflags" "2048" // b#4: added this } { "item" "ammo_shells" "origin" "-584 -1544 128" "targetname" "t51" "classname" "monster_soldier" "angle" "0" "spawnflags" "1" } { "item" "ammo_shells" "origin" "-376 -1544 128" "targetname" "t51" "spawnflags" "1" "angle" "180" "classname" "monster_soldier" } { "origin" "-64 -2904 208" "targetname" "t50" "classname" "point_combat" } { "target" "t50" "origin" "-128 -2904 224" "spawnflags" "1" "angle" "0" "classname" "monster_gunner" } { "angle" "270" "origin" "-872 -1632 -120" "targetname" "t48" "classname" "monster_floater" "spawnflags" "2" } { "angle" "270" "origin" "-872 -1744 -120" "targetname" "t48" "spawnflags" "2" "classname" "monster_floater" } { "spawnflags" "2048" "origin" "-1552 -1824 -248" "target" "t48" "classname" "weapon_bfg" } { "model" "*34" "spawnflags" "2048" "target" "t47" "classname" "trigger_once" } { "item" "ammo_bullets" "origin" "-776 -1140 -64" "targetname" "t47" "spawnflags" "1" "angle" "90" "classname" "monster_gunner" } { "origin" "-1904 -1456 104" "target" "t40" "targetname" "t46" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-1904 -1672 104" "target" "t46" "targetname" "t45" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-1984 -1672 104" "target" "t45" "targetname" "t44" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-1984 -1672 104" "target" "t44" "targetname" "t43" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-1904 -1672 104" "target" "t43" "targetname" "t42" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-1904 -1456 104" "target" "t42" "targetname" "t41" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-1864 -1456 104" "target" "t41" "targetname" "t40" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "model" "*35" "spawnflags" "2048" "target" "t39" "classname" "trigger_once" } { "model" "*36" "angle" "90" "target" "t38" "classname" "trigger_multiple" } { "origin" "-2788 -1144 212" "targetname" "t38" "map" "waste1$waste3" "classname" "target_changelevel" } { "model" "*37" "target" "t37" "classname" "trigger_multiple" "angle" "90" } { "origin" "-592 -504 -40" "targetname" "t37" "map" "waste2$waste3" "classname" "target_changelevel" } { "origin" "-1798 -1476 -36" "targetname" "t34" "dmg" "5" "delay" ".3" "classname" "target_explosion" "spawnflags" "2048" // b#4: added this } { "model" "*38" "mass" "200" "target" "t34" "dmg" "20" "health" "40" "classname" "func_explosive" "spawnflags" "0" } { "spawnflags" "2048" "classname" "misc_explobox" "angle" "270" "origin" "-2616 -1440 100" } { "classname" "light" "light" "72" "_color" "1.000000 1.000000 0.000000" "origin" "-1688 -1268 -108" } { "_color" "1.000000 1.000000 0.000000" "light" "72" "classname" "light" "origin" "-1624 -1268 -108" } { "classname" "light" "light" "72" "_color" "1.000000 1.000000 0.000000" "origin" "-1560 -1268 -108" } { "spawnflags" "2048" "origin" "-524 -2484 92" "classname" "misc_explobox" } { "spawnflags" "2048" "origin" "176 -2708 200" "classname" "misc_explobox" } { "spawnflags" "2048" "origin" "-468 -2532 92" "classname" "misc_explobox" } { "model" "*39" "spawnflags" "1" "classname" "func_plat" "sounds" "1" } { "spawnflags" "2048" "origin" "-2408 -1468 100" "angle" "270" "classname" "misc_explobox" } { "spawnflags" "2048" "origin" "-2808 -1728 100" "angle" "270" "classname" "misc_explobox" } { "spawnflags" "2048" "origin" "-2508 -1356 100" "classname" "misc_explobox" "angle" "270" } { "spawnflags" "2048" "origin" "-2300 -1436 100" "classname" "misc_explobox" "angle" "270" } { "spawnflags" "2048" "origin" "-2656 -1352 100" "classname" "misc_explobox" "angle" "315" } { "spawnflags" "2048" "origin" "-2540 -1404 100" "angle" "270" "classname" "misc_explobox" } { "origin" "-1624 -1316 188" "classname" "light" "light" "180" } { "origin" "-824 -1092 188" "light" "150" "classname" "light" } { "origin" "-1504 -2468 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1424 -2520 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1380 -2592 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1380 -2720 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1432 -2792 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1504 -2844 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1636 -2844 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1708 -2792 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1756 -2720 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1756 -2592 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1704 -2516 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1680 -2340 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1804 -2340 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1868 -2408 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1868 -2520 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1868 -2624 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1868 -2688 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1868 -2780 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1868 -2880 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1868 -2980 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1776 -3020 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1680 -3020 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1600 -3020 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1504 -3020 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1392 -3020 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1288 -3020 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1192 -3020 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1080 -3020 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-952 -3020 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-824 -3020 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-824 -2852 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-840 -2748 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-840 -2648 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-708 -2648 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-604 -2648 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-512 -2680 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-512 -2432 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-400 -2432 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-1632 -2468 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-584 -2472 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-712 -2472 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-840 -2472 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-952 -2472 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-1016 -2540 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-1016 -2652 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-1016 -2784 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-1096 -2852 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-1212 -2852 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-1268 -2760 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-1268 -2632 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-1268 -2504 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-1268 -2384 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-1352 -2340 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-1452 -2340 -272" } { "classname" "light" "light" "90" "_color" "0.123223 1.000000 0.094787" "origin" "-1544 -1716 -272" } { "light" "145" "classname" "light" "origin" "-1272 -1912 176" } { "classname" "light" "light" "72" "_color" "0.123223 1.000000 0.094787" "origin" "-1688 -1264 -104" } { "_color" "0.123223 1.000000 0.094787" "light" "72" "classname" "light" "origin" "-1624 -1264 -104" } { "classname" "light" "light" "72" "_color" "0.123223 1.000000 0.094787" "origin" "-1560 -1264 -104" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "classname" "func_group" } { "origin" "-824 -1496 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-824 -1552 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-824 -1612 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-824 -1680 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-824 -1748 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-824 -1820 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-824 -1900 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-880 -1904 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-956 -1904 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1028 -1904 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1108 -1904 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1200 -1904 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1284 -1904 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1364 -1908 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1428 -1844 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1428 -1952 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1664 -1948 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1664 -1828 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-400 -2680 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1428 -1716 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1664 -1716 -272" "_color" "0.123223 1.000000 0.094787" "light" "90" "classname" "light" } { "origin" "-1560 -1380 -108" "_color" "1.000000 1.000000 0.000000" "light" "72" "classname" "light" } { "origin" "-1624 -1380 -108" "classname" "light" "light" "72" "_color" "1.000000 1.000000 0.000000" } { "origin" "-1688 -1380 -108" "_color" "1.000000 1.000000 0.000000" "light" "72" "classname" "light" } { "target" "t40" "spawnflags" "1" "origin" "-1808 -1456 120" "angle" "0" "classname" "monster_berserk" } { "model" "*40" "spawnflags" "2048" "target" "t33" "classname" "trigger_once" } { "spawnflags" "1" "origin" "-2112 -1440 120" "targetname" "t33" "angle" "270" "classname" "monster_gunner" } { "model" "*41" "spawnflags" "2048" "target" "t32" "classname" "trigger_once" } { "spawnflags" "1" "origin" "-2288 -1520 120" "targetname" "t32" "classname" "monster_gunner" "angle" "270" } { "item" "ammo_grenades" "target" "t52" "spawnflags" "1" "origin" "-2432 -1872 120" "targetname" "t32" "angle" "90" "classname" "monster_gunner" } { "item" "ammo_rockets" "spawnflags" "1" "origin" "-184 -2232 96" "target" "t29" "classname" "monster_tank" } { "origin" "-128 -2240 88" "target" "t29" "targetname" "t28" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-360 -2240 88" "targetname" "t29" "target" "t28" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-472 -1872 104" "target" "t27" "targetname" "t26" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-488 -2400 104" "targetname" "t27" "target" "t26" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "item" "ammo_bullets" "origin" "-496 -1376 128" "target" "t23" "classname" "monster_gunner" "spawnflags" "1" } { "origin" "-488 -1240 112" "target" "t25" "targetname" "t24" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-472 -1224 112" "target" "t23" "targetname" "t22" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "origin" "-488 -1400 112" "target" "t24" "targetname" "t23" "classname" "path_corner" "spawnflags" "2048" // b#4 added this } { "origin" "-608 -1232 112" "targetname" "t25" "target" "t22" "classname" "path_corner" "spawnflags" "2048" // b#4: added this } { "classname" "path_corner" "spawnflags" "2048" // b#4: added this "targetname" "t19" "target" "t20" "origin" "-1680 -1936 120" } { "classname" "path_corner" "spawnflags" "2048" // b#4: added this "targetname" "t18" "target" "t19" "origin" "-1208 -1960 120" } { "classname" "path_corner" "spawnflags" "2048" // b#4: added this "targetname" "t11" "target" "t12" "origin" "-1592 -1848 120" } { "classname" "path_corner" "spawnflags" "2048" // b#4: added this "targetname" "t13" "target" "t14" "origin" "-1568 -1888 120" } { "classname" "path_corner" "spawnflags" "2048" // b#4: added this "targetname" "t12" "target" "t13" "origin" "-1160 -1896 120" } { "classname" "path_corner" "spawnflags" "2048" // b#4: added this "targetname" "t16" "target" "t17" "origin" "-1240 -1960 120" } { "classname" "path_corner" "spawnflags" "2048" // b#4: added this "targetname" "t15" "target" "t16" "origin" "-1656 -1928 112" } { "classname" "path_corner" "spawnflags" "2048" // b#4: added this "targetname" "t17" "target" "t18" "origin" "-1656 -1952 120" } { "classname" "path_corner" "spawnflags" "2048" // b#4: added this "target" "t15" "targetname" "t20" "origin" "-1672 -1480 120" } { "classname" "path_corner" "spawnflags" "2048" // b#4: added this "target" "t11" "targetname" "t14" "origin" "-1576 -1488 120" } { "spawnflags" "1" "classname" "monster_gladiator" "target" "t9" "origin" "-832 -1160 -64" "item" "ammo_slugs" } { "classname" "path_corner" "spawnflags" "2048" // b#4: added this "target" "t9" "targetname" "t10" "origin" "-816 -1920 104" } { "classname" "path_corner" "spawnflags" "2048" // b#4: added this "targetname" "t9" "target" "t10" "origin" "-832 -1208 -88" } { "model" "*42" "spawnflags" "2048" "classname" "trigger_once" "targetname" "t4" "target" "t5" } { "model" "*43" "spawnflags" "2048" "classname" "trigger_once" "targetname" "t4" "target" "t1" } { "origin" "-584 -568 0" "classname" "target_crosslevel_target" "spawnflags" "2176" // b#4: was 128 "target" "t1" } { "origin" "-584 -620 0" "classname" "target_crosslevel_trigger" "spawnflags" "2052" // b#4: was 4 "targetname" "t3" } { "model" "*44" "lip" "24" "classname" "func_water" "angle" "-2" "spawnflags" "2049" "targetname" "t3" } { "model" "*45" "spawnflags" "2049" "classname" "trigger_counter" "targetname" "t1" "target" "t3" } { "classname" "light" "light" "100" "origin" "0 -2208 120" } { "classname" "light" "light" "80" "origin" "-2112 -976 20" } { "light" "80" "classname" "light" "origin" "-2372 -1048 -40" } { "light" "80" "classname" "light" "origin" "-1944 -1000 -40" } { "light" "80" "classname" "light" "origin" "-1872 -1016 -40" } { "classname" "light" "light" "80" "origin" "-2316 -1012 -40" } { "_color" "0.113725 1.000000 0.011765" "classname" "light" "light" "100" "origin" "-2330 -738 -72" } { "_color" "0.113725 1.000000 0.011765" "light" "100" "classname" "light" "origin" "-1892 -734 -72" } { "classname" "light" "origin" "-832 -1300 -4" "light" "145" } { "classname" "light" "origin" "-832 -1216 -4" "light" "145" } { "origin" "-592 -672 -16" "light" "110" "classname" "light" } { "classname" "light" "light" "100" "origin" "-592 -732 -16" } { "light" "95" "classname" "light" "origin" "-2216 -1664 244" } { "classname" "light" "light" "150" "origin" "-2348 -1668 160" } { "classname" "light" "light" "80" "origin" "-2784 -1280 212" } { "classname" "light" "light" "100" "origin" "-2784 -1236 280" } { "classname" "light" "light" "100" "origin" "-2784 -1132 280" } { "origin" "-1840 -1464 256" "classname" "light" "light" "95" } { "light" "110" "classname" "light" "origin" "-1084 -1236 272" } { "light" "90" "classname" "light" "origin" "-764 -824 72" } { "light" "90" "classname" "light" "origin" "-608 -824 72" } { "classname" "light" "light" "90" "origin" "-824 -928 72" } { "classname" "light" "light" "110" "origin" "-588 -448 -12" } { // b#2: was waste1's spot "classname" "info_player_start" "angle" "270" "origin" "-592 -648 -64" "targetname" "waste2" } { "model" "*46" "_minlight" ".18" "target" "t67" "classname" "func_door" "angle" "-1" } { "origin" "-2784 -1100 212" "light" "80" "classname" "light" } { "origin" "-2784 -1332 236" "light" "100" "classname" "light" } { "model" "*47" "origin" "-2480 -1668 -140" "_minlight" "0.15" "dmg" "100" "spawnflags" "17" "speed" "150" "classname" "func_rotating" } { "model" "*48" "origin" "-2480 -1668 -180" "classname" "func_rotating" "speed" "250" "spawnflags" "3" "dmg" "100" "_minlight" "0.15" } { // b#2: was the waste2 spot "classname" "info_player_start" "angle" "270" "targetname" "waste1" "origin" "-2784 -1260 180" } { "light" "120" "classname" "light" "origin" "-344 -2576 -208" } { "light" "120" "classname" "light" "origin" "-508 -2572 -208" } { "classname" "light" "light" "120" "origin" "-128 -2572 -160" } { "light" "145" "origin" "-832 -1416 36" "classname" "light" } { "model" "*49" "wait" "-1" "spawnflags" "8" "classname" "func_door" "angle" "90" "team" "ky1" "targetname" "t73" "_minlight" ".18" } { "light" "130" "classname" "light" "origin" "-1864 -2744 -208" } { "light" "130" "classname" "light" "origin" "-1704 -2952 -208" } { "light" "130" "classname" "light" "origin" "-1480 -2944 -208" } { "light" "130" "classname" "light" "origin" "-1184 -2952 -208" } { "light" "130" "classname" "light" "origin" "-992 -2944 -208" } { "light" "130" "classname" "light" "origin" "-920 -2864 -208" } { "light" "130" "classname" "light" "origin" "-928 -2688 -208" } { "classname" "light" "light" "130" "origin" "-1864 -2528 -208" } { "light" "100" "classname" "light" "origin" "-1604 -1888 -236" } { "light" "100" "classname" "light" "origin" "-820 -1380 -236" } { "classname" "light" "light" "100" "origin" "-1496 -1776 -236" } { "light" "100" "classname" "light" "origin" "-808 -1664 -72" } { "light" "100" "classname" "light" "origin" "-808 -1816 -72" } { "light" "100" "classname" "light" "origin" "-920 -1892 -72" } { "light" "100" "classname" "light" "origin" "-1124 -1892 -72" } { "light" "100" "classname" "light" "origin" "-1364 -1892 -72" } { "light" "100" "classname" "light" "origin" "-1612 -1888 -72" } { "light" "100" "classname" "light" "origin" "-1612 -1708 -72" } { "classname" "light" "light" "100" "origin" "-812 -1544 -124" } { "light" "170" "classname" "light" "origin" "-480 -1864 240" } { "light" "200" "classname" "light" "origin" "-480 -2112 240" } { "light" "180" "classname" "light" "origin" "-480 -2496 240" } { "light" "180" "classname" "light" "origin" "-136 -2504 240" } { "light" "110" "origin" "-480 -1432 172" "classname" "light" } { "model" "*50" "team" "td5" "spawnflags" "8" "classname" "func_door" "angle" "180" "sounds" "4" "_minlight" ".18" } { "origin" "-480 -2216 404" "classname" "light" "light" "150" } { "model" "*51" "speed" "165" "classname" "func_train" "sounds" "1" "targetname" "t5" "dmg" "9999" "target" "t35" } { "classname" "path_corner" "origin" "-328 -2096 80" "targetname" "t36" "target" "t35" } { "classname" "path_corner" "origin" "-328 -2096 -64" "targetname" "t35" "target" "t36" } { "light" "150" "classname" "light" "origin" "-480 -1960 404" } { "light" "150" "classname" "light" "origin" "-480 -1736 404" } { "light" "110" "classname" "light" "origin" "-480 -1528 196" } { "origin" "-244 -2008 10" "classname" "light" "light" "160" } { "origin" "-56 -1896 404" "light" "80" "classname" "light" } { "origin" "-40 -2528 404" "classname" "light" "light" "80" } { "origin" "-40 -2256 404" "classname" "light" "light" "80" } { "origin" "-40 -2144 404" "classname" "light" "light" "80" } { "origin" "-480 -1648 236" "classname" "light" "light" "80" } { "origin" "-824 -1076 -16" "light" "100" "classname" "light" } { "origin" "-816 -1536 152" "light" "145" "classname" "light" } { "origin" "-1720 -1464 184" "classname" "light" "light" "140" } { "origin" "-1624 -1568 176" "classname" "light" "light" "145" } { "origin" "-1624 -1712 176" "classname" "light" "light" "145" } { "origin" "-1496 -1760 176" "classname" "light" "light" "140" } { "origin" "-1600 -1872 176" "classname" "light" "light" "145" } { "origin" "-1472 -1888 176" "classname" "light" "light" "145" } { "origin" "-1088 -1904 176" "classname" "light" "light" "145" } { "origin" "-832 -1904 176" "classname" "light" "light" "145" } { "origin" "-816 -1752 176" "classname" "light" "light" "145" } { "classname" "light" "origin" "-824 -1208 212" "light" "150" } { "classname" "light" "origin" "-648 -1232 172" "light" "110" } { "classname" "light" "light" "100" "origin" "-824 -1016 -16" } { "origin" "-1172 -1268 272" "classname" "light" "light" "110" } { "light" "110" "origin" "-1176 -1460 272" "classname" "light" } { "light" "110" "classname" "light" "origin" "-1344 -1464 272" } { "light" "95" "classname" "light" "origin" "-1896 -1648 256" } { "light" "120" "origin" "-1880 -1240 160" "classname" "light" } { "light" "120" "origin" "-1944 -712 312" "classname" "light" } { "light" "120" "origin" "-1864 -976 160" "classname" "light" } { "light" "100" "origin" "-1984 -860 232" "classname" "light" } { "light" "120" "origin" "-2360 -976 160" "classname" "light" } { "light" "120" "origin" "-2352 -1224 160" "classname" "light" } { "light" "120" "origin" "-2112 -1232 160" "classname" "light" } { "model" "*52" "team" "thor1" "angle" "180" "classname" "func_door" "_minlight" ".18" } { "light" "120" "classname" "light" "origin" "-2280 -712 312" } { "classname" "light" "origin" "-2184 -740 136" "light" "150" } { "classname" "light" "origin" "-2184 -640 248" "light" "125" } { "classname" "light" "origin" "-2040 -628 248" "light" "125" } { "origin" "-440 -2288 -256" "light" "64" "classname" "light" } { "origin" "-608 -2008 -256" "classname" "light" "light" "64" } { "origin" "-508 -2012 -256" "classname" "light" "light" "64" } { "origin" "-440 -2024 -256" "classname" "light" "light" "64" } { "origin" "-444 -2100 -256" "classname" "light" "light" "64" } { "origin" "-440 -2164 -256" "classname" "light" "light" "64" } { "origin" "-440 -2224 -256" "classname" "light" "light" "64" } { "origin" "176 -2768 224" "light" "100" "classname" "light" } { "origin" "280 -2768 224" "classname" "light" "light" "100" } { "origin" "280 -2856 224" "classname" "light" "light" "100" } { "origin" "176 -2856 224" "classname" "light" "light" "100" } { "origin" "-88 -2864 232" "light" "80" "classname" "light" } { "origin" "160 -2672 232" "classname" "light" "light" "80" } { "origin" "104 -2672 232" "classname" "light" "light" "100" } { "origin" "-96 -2752 232" "classname" "light" "light" "80" } { "origin" "-400 -2656 232" "classname" "light" "light" "80" } { "origin" "-160 -2864 232" "classname" "light" "light" "80" } { "origin" "-160 -2752 232" "light" "80" "classname" "light" } { "origin" "-344 -2880 232" "light" "80" "classname" "light" } { "origin" "-416 -2880 232" "light" "80" "classname" "light" } { "origin" "-304 -2904 392" "light" "64" "classname" "light" } { "origin" "-312 -2680 392" "classname" "light" "light" "64" } { "origin" "-312 -2792 392" "classname" "light" "light" "64" } { "origin" "128 -2672 392" "light" "80" "classname" "light" } { "origin" "128 -2784 392" "light" "64" "classname" "light" } { "origin" "136 -2896 392" "classname" "light" "light" "64" } { "origin" "-16 -2672 392" "light" "64" "classname" "light" } { "origin" "-16 -2784 392" "light" "64" "classname" "light" } { "origin" "-8 -2896 392" "classname" "light" "light" "64" } { "origin" "-176 -2680 392" "light" "64" "classname" "light" } { "origin" "-168 -2904 392" "classname" "light" "light" "64" } { "targetname" "t2" "origin" "-192 -2368 24" "classname" "target_explosion" "spawnflags" "2048" // b#4: added this } { "delay" ".4" "targetname" "t2" "origin" "-256 -2320 40" "classname" "target_explosion" "spawnflags" "2048" // b#4: added this } { // b#7 "classname" "trigger_always" "spawnflags" "1792" "target" "t4" }yquake2-QUAKE2_8_40/stuff/mapfixes/juggernaut/000077500000000000000000000000001465112212000212275ustar00rootroot00000000000000yquake2-QUAKE2_8_40/stuff/mapfixes/juggernaut/jug13.ent000066400000000000000000001255651465112212000227060ustar00rootroot00000000000000// FIXED ENTITY STRING (by BjossiAlfreds) // // 1. Removed unused entities (b#1) // // 2. Renamed targets for clarity // g_key* -> rkey* (red key) // s_key* -> bkey* (blue key) // bkey -> bkey_tr // r_key* -> pkey* (security pass) // // 3. Fixed broken trigger chain due to r_cl / r_key_cl confusion (b#3) // Renamed both to pkey_cl. // // 4. Shootable button that reveals red key remains permanently pressed (b#4) // // 5. Cleared unused 4096 spawnflag (b#5) // // 6. Fixed help icon appearing on every savegame load (b#6) { "sky" "env4_" "light" "0" "sounds" "5" "message" "THE CIVIC CENTER" "classname" "worldspawn" } { "origin" "139 196 -128" "angle" "270" "classname" "info_player_start" } { "origin" "-264 128 96" "_color" ".7 .4 0" "style" "0" "light" "350" "classname" "light" } { "origin" "-280 128 352" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "origin" "-248 128 352" "_color" ".7 .4 0" "style" "0" "light" "300" "classname" "light" } { "origin" "-280 -128 352" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "origin" "-248 -128 352" "_color" ".7 .4 0" "style" "0" "light" "300" "classname" "light" } { "origin" "-264 -128 96" "_color" ".7 .4 0" "style" "0" "light" "350" "classname" "light" } { "origin" "264 128 96" "_color" ".7 .4 0" "style" "0" "light" "350" "classname" "light" } { "origin" "280 128 352" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "origin" "248 128 352" "_color" ".7 .4 0" "style" "0" "light" "300" "classname" "light" } { "origin" "280 -128 352" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "origin" "248 -128 352" "_color" ".7 .4 0" "style" "0" "light" "300" "classname" "light" } { "origin" "264 -128 96" "_color" ".7 .4 0" "style" "0" "light" "350" "classname" "light" } { "origin" "-128 -248 352" "_color" ".7 .4 0" "style" "0" "light" "300" "classname" "light" } { "origin" "-128 -280 352" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "origin" "128 -248 352" "_color" ".7 .4 0" "style" "0" "light" "300" "classname" "light" } { "origin" "128 -280 352" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "origin" "-128 -264 96" "_color" ".7 .4 0" "style" "0" "light" "350" "classname" "light" } { "origin" "128 -264 96" "_color" ".7 .4 0" "style" "0" "light" "350" "classname" "light" } { "origin" "-128 248 352" "_color" ".7 .4 0" "style" "0" "light" "300" "classname" "light" } { "origin" "-128 280 352" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "origin" "128 248 352" "_color" ".7 .4 0" "style" "0" "light" "300" "classname" "light" } { "origin" "128 280 352" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "origin" "-128 264 96" "_color" ".7 .4 0" "style" "0" "light" "350" "classname" "light" } { "origin" "128 264 96" "_color" ".7 .4 0" "style" "0" "light" "350" "classname" "light" } { "origin" "202 122 332" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "202 -126 332" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "88 -32 592" "target" "bkey_cl" "classname" "key_blue_key" } { "origin" "-176 0 360" "style" "0" "light" "200" "classname" "light" } { "origin" "-72 -80 432" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "88 32 336" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "target" "bkey" "targetname" "bkey_cl" "classname" "trigger_relay" } { "origin" "88 32 592" "classname" "key_red_key" } { "target" "overhead" "targetname" "bkey1" "classname" "trigger_relay" } { "target" "bkey1" "targetname" "bkey0" "classname" "trigger_relay" } { "delay" "1" "target" "bkey" "targetname" "bkey1" "classname" "trigger_relay" } { "target" "rkey1" "targetname" "rkey0" "classname" "trigger_relay" "spawnflags" "3840" // b#1: added this } { "target" "overhead" "targetname" "rkey1" "classname" "trigger_relay" } { "delay" "1" "target" "rkey" "targetname" "rkey1" "classname" "trigger_relay" } { "origin" "56 -40 612" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "delay" "1" "target" "overhead" "targetname" "bkey_cl" "classname" "trigger_relay" } { "target" "rkey" "targetname" "rkey_cl" "classname" "trigger_relay" } { "delay" "1" "target" "overhead" "targetname" "rkey_cl" "classname" "trigger_relay" } { "origin" "56 32 612" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "model" "*1" "lip" "0" "wait" "-1" "sounds" "1" "speed" "100" "targetname" "overhead" "spawnflags" "32" "angle" "180" "classname" "func_door" } { "model" "*2" "dmg" "1000" "lip" "160" "wait" "4" "sounds" "4" "speed" "200" "targetname" "rkey" "spawnflags" "32" "angle" "-2" "classname" "func_door" } { "model" "*3" "dmg" "1000" "lip" "160" "wait" "4" "sounds" "4" "speed" "200" "targetname" "bkey" "spawnflags" "32" "angle" "-2" "classname" "func_door" } { "model" "*4" "dmg" "1000" "lip" "160" "wait" "4" "sounds" "4" "speed" "200" "targetname" "pkey" "spawnflags" "32" "angle" "-2" "classname" "func_door" } { "origin" "88 0 592" "classname" "key_pass" } { "origin" "0 632 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-288 632 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-576 632 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-576 -632 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-288 -632 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "0 -632 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-576 0 296" "target" "sham2" "targetname" "sham1" "classname" "path_corner" } { "origin" "0 0 296" "target" "sham3" "targetname" "sham2" "classname" "path_corner" } { "origin" "0 -576 296" "_color" ".7 .4 0" "target" "sham4" "targetname" "sham3" "classname" "path_corner" } { "origin" "-576 576 296" "target" "sham1" "targetname" "sham0" "classname" "path_corner" } { "origin" "-576 -576 296" "_color" ".7 .4 0" "target" "sham5" "targetname" "sham4" "classname" "path_corner" } { "origin" "-576 0 296" "target" "sham6" "targetname" "sham5" "classname" "path_corner" } { "origin" "0 0 296" "target" "sham7" "targetname" "sham6" "classname" "path_corner" } { "origin" "0 576 296" "target" "sham0" "targetname" "sham7" "classname" "path_corner" } { "origin" "864 -56 64" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "864 504 64" "_color" ".7 .4 0" "style" "0" "light" "350" "classname" "light" } { "model" "*5" "wait" "-1" // b#4: 0.2 -> -1 "target" "rkey1" // b#1: counter -> rkey1 "health" "25" "lip" "0" "sounds" "2" "speed" "100" "angle" "270" "message" "The red key is in the Jupiter Room." // b#1: moved from trigger_relay "classname" "func_button" } { "target" "rkey1" // b#1: rkey0 -> rkey1 "targetname" "rkey0" // b#1: counter -> rkey0 "classname" "trigger_relay" // b#1: counter -> relay "spawnflags" "3840" // b#1: added this } { "origin" "864 0 192" "_color" ".7 .4 0" "style" "0" "light" "100" "classname" "light" } { "origin" "864 448 192" "_color" ".7 .4 0" "style" "0" "light" "100" "classname" "light" } { "origin" "648 448 64" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "1080 448 64" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "model" "*7" "target" "lift1" "lip" "0" "wait" "6" "sounds" "2" "speed" "100" "angle" "270" "classname" "func_button" } { "origin" "910 650 196" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "818 650 196" "_color" ".7 .4 0" "classname" "light" } { "origin" "864 608 47" "_color" ".7 .4 0" "style" "0" "light" "125" "classname" "light" } { "origin" "864 712 -88" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "674 670 148" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "674 834 148" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "1054 834 148" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "1054 670 148" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "model" "*8" "targetname" "lift1" "dmg" "25" "lip" "-120" "wait" "2" "sounds" "4" "speed" "100" "angle" "-2" "classname" "func_door" } { "origin" "790 954 148" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "938 954 148" "_color" ".7 .4 0" "classname" "light" } { "origin" "864 808 88" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "1248 552 -8" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "480 552 -8" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "1024 576 -24" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "704 576 -24" "_color" ".7 .4 0" "classname" "light" } { "origin" "1056 896 -112" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "672 896 -112" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "model" "*9" "message" "Find a switch." "lip" "0" "wait" "-1" "sounds" "1" "speed" "100" "targetname" "2nd" "spawnflags" "2048" "angle" "90" "classname" "func_door" } { "model" "*10" "message" "Find a switch." "lip" "0" "wait" "-1" "sounds" "1" "speed" "100" "targetname" "2nd" "spawnflags" "2048" "angle" "270" "classname" "func_door" } { "model" "*11" "message" "Opens nearby." "lip" "0" "wait" "3" "sounds" "1" "speed" "100" "spawnflags" "2048" "angle" "90" "classname" "func_door" } { "model" "*12" "message" "Opens nearby." "lip" "0" "wait" "3" "sounds" "1" "speed" "100" "spawnflags" "2048" "angle" "270" "classname" "func_door" } { "origin" "-160 160 -64" "_color" ".7 .4 0" "style" "0" "light" "300" "classname" "light" } { "origin" "-160 -160 -64" "_color" ".7 .4 0" "style" "0" "light" "300" "classname" "light" } { "origin" "392 1 -96" "_color" ".7 .4 0" "style" "0" "light" "350" "classname" "light" } { "origin" "-218 90 404" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-218 -90 404" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "206 122 84" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "206 -122 84" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-208 -208 96" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-208 208 96" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "model" "*13" "sounds" "3" "count" "2" "target" "1st" "lip" "0" "wait" "4" "speed" "100" "targetname" "1st_c" "classname" "trigger_counter" "spawnflags" "3840" // b#1: added this } { "origin" "-1462 -82 340" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1462 82 340" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1610 82 340" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1610 -82 340" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1640 200 8" "_color" ".7 .4 0" "style" "0" "light" "400" "classname" "light" } { "origin" "-1640 -200 8" "_color" ".7 .4 0" "style" "0" "light" "400" "classname" "light" } { "origin" "-1464 0 8" "_color" ".7 .4 0" "style" "0" "light" "350" "classname" "light" } { "model" "*14" "height" "288" "lip" "0" "wait" "4" "sounds" "2" "speed" "100" "classname" "func_plat" } { "origin" "-1344 0 -8" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "model" "*15" "style" "32" //"target" "shaft" // b#1: unused target "lip" "0" "wait" "1" "classname" "trigger_multiple" "spawnflags" "3840" // b#1: added this } { "origin" "-1344 0 400" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1312 192 112" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1312 -192 112" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "model" "*16" "message" "A door has opened." "target" "2nd" "lip" "0" "wait" "-1" "sounds" "2" "speed" "100" "angle" "180" "classname" "func_button" } { "origin" "-1848 -352 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-1848 0 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-1848 352 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-1344 0 80" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "model" "*17" "delay" "2" "targetname" "rkey_cl" "lip" "0" "wait" "-1" "sounds" "1" "speed" "100" "spawnflags" "2048" "angle" "180" "classname" "func_door" } { "model" "*18" "target" "6_cnt" "lip" "0" "wait" "-1" "sounds" "3" "speed" "100" "angle" "270" "classname" "func_button" } { "model" "*19" "target" "6_cnt" "lip" "0" "wait" "-1" "sounds" "3" "speed" "100" "angle" "90" "classname" "func_button" } { "model" "*20" "target" "6_cnt" "lip" "0" "wait" "-1" "sounds" "3" "speed" "100" "classname" "func_button" } { "model" "*21" "target" "6_cnt" "lip" "0" "wait" "-1" "sounds" "3" "speed" "100" "angle" "180" "classname" "func_button" } { "model" "*22" "target" "6_cnt" "lip" "0" "wait" "-1" "sounds" "3" "speed" "100" "angle" "-1" "classname" "func_button" } { "model" "*23" "target" "6_cnt" "lip" "0" "wait" "-1" "sounds" "3" "speed" "100" "angle" "-2" "classname" "func_button" } { "model" "*24" "count" "6" "target" "bkey0" "message" "The blue key is available." "wait" "4" "targetname" "6_cnt" "classname" "trigger_counter" } { "origin" "-1274 70 108" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-1274 -54 108" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "model" "*25" "height" "88" "lip" "0" "wait" "4" "sounds" "1" "speed" "100" "classname" "func_plat" } { "origin" "-1200 192 416" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-1200 -192 416" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-1200 -64 416" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-1360 -296 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-1616 -296 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-1200 -352 432" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1648 -16 -184" "_color" ".7 .4 0" "target" "brij1" "targetname" "brij0" "classname" "path_corner" } { "origin" "-1200 -352 224" "_color" ".7 .4 0" "style" "0" "light" "300" "classname" "light" } { "origin" "-1200 352 224" "_color" ".7 .4 0" "style" "0" "light" "300" "classname" "light" } { "origin" "-1360 296 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-1616 296 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-928 408 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-928 -56 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-928 -72 32" "_color" ".7 .4 0" "style" "0" "light" "350" "classname" "light" } { "origin" "-352 -1 -24" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-544 0 -24" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "model" "*26" "delay" "2" "message" "The sewer doors open elsewhere..." "lip" "0" "wait" "-1" "sounds" "1" "speed" "100" "targetname" "end" "spawnflags" "2048" "angle" "90" "classname" "func_door" } { "model" "*27" "message" "The sewer doors open elsewhere..." "lip" "0" "wait" "-1" "sounds" "1" "speed" "100" "targetname" "end" "spawnflags" "2048" "angle" "270" "classname" "func_door" } { "model" "*28" "lip" "0" "wait" "-1" "sounds" "1" "speed" "100" "targetname" "pkey_cl" // b#3: r_cl -> pkey_cl "angle" "90" "classname" "func_door" } { "origin" "8 928 200" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "model" "*29" "target" "pkey1" // b#1: pkey0 -> pkey1 "message" "The passkey is available." "lip" "0" "wait" "-1" "sounds" "2" "speed" "100" "angle" "180" "classname" "func_button" } { "origin" "-320 832 340" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "target" "pkey1" "targetname" "pkey0" "classname" "trigger_relay" "spawnflags" "3840" // b#1: added this } { "delay" "1" "target" "pkey" "targetname" "pkey1" "classname" "trigger_relay" } { "target" "overhead" "targetname" "pkey1" "classname" "trigger_relay" } { "target" "pkey" "targetname" "pkey_cl" // b#3: r_key_cl -> pkey_cl "classname" "trigger_relay" } { "delay" "1" "target" "overhead" "targetname" "pkey_cl" // b#3: r_key_cl -> pkey_cl "classname" "trigger_relay" } { "model" "*30" "targetname" "bluedoor" "lip" "0" "wait" "-1" "sounds" "1" "speed" "100" "spawnflags" "2064" "angle" "270" "classname" "func_door" } { "model" "*31" "delay" "2" "targetname" "bluedoor" "lip" "0" "wait" "-1" "sounds" "1" "speed" "100" "spawnflags" "2064" "angle" "90" "classname" "func_door" } { "model" "*32" "targetname" "bluedoor" "delay" "2" "lip" "0" "wait" "-1" "sounds" "1" "speed" "100" "spawnflags" "2056" "angle" "90" "classname" "func_door" } { "model" "*33" "delay" "2" "targetname" "bluedoor" "lip" "0" "wait" "-1" "sounds" "1" "speed" "100" "spawnflags" "2056" "angle" "270" "classname" "func_door" } { "model" "*34" "target" "keyed" "lip" "0" "wait" "-1" "sounds" "2" "speed" "100" "angle" "180" "classname" "func_button" } { "origin" "922 810 -60" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "810 810 -60" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "model" "*35" "target" "keyed" "lip" "0" "wait" "-1" "sounds" "2" "speed" "100" "classname" "func_button" } { "model" "*36" "message" "A door is now open!" "count" "2" "style" "32" "target" "end" "lip" "0" "wait" "4" "speed" "100" "targetname" "keyed" "classname" "trigger_counter" } { "origin" "-192 728 288" "_color" ".7 .4 0" "style" "0" "light" "400" "classname" "light" } { "origin" "-192 1128 288" "_color" ".7 .4 0" "style" "0" "light" "400" "classname" "light" } { "origin" "-104 1056 167" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-160 776 248" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "origin" "0 1040 464" "_color" ".7 .4 0" "style" "0" "light" "300" "target" "f1" "angle" "30" "classname" "light" } { "origin" "0 1040 448" "_color" ".7 .4 0" "style" "0" "light" "100" "classname" "light" } { "origin" "0 1040 312" "_color" ".7 .4 0" "targetname" "f1" "style" "32" "light" "200" "classname" "light" } { "origin" "0 816 312" "_color" ".7 .4 0" "targetname" "f2" "style" "33" "light" "200" "classname" "light" } { "origin" "0 816 448" "_color" ".7 .4 0" "style" "0" "light" "100" "classname" "light" } { "origin" "0 816 464" "_color" ".7 .4 0" "style" "0" "light" "300" "target" "f2" "angle" "30" "classname" "light" } { "model" "*37" "message" "Opens nearby." "dmg" "20" "lip" "0" "wait" "4" "sounds" "1" "speed" "100" "spawnflags" "2048" "angle" "180" "classname" "func_door" } { "model" "*38" "message" "Opens nearby." "dmg" "20" "lip" "0" "wait" "4" "sounds" "1" "speed" "100" "spawnflags" "2048" "classname" "func_door" } { "origin" "864 1008 136" "_color" ".7 .4 0" "style" "0" "light" "180" "classname" "light" } { "origin" "768 1008 136" "_color" ".7 .4 0" "style" "0" "light" "180" "classname" "light" } { "origin" "960 1008 136" "_color" ".7 .4 0" "style" "0" "light" "180" "classname" "light" } { "origin" "1056 1008 136" "_color" ".7 .4 0" "style" "0" "light" "180" "classname" "light" } { "origin" "1064 1056 72" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "8 928 448" "_color" ".7 .4 0" "style" "0" "light" "125" "classname" "light" } { "origin" "-320 1024 340" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "model" "*39" "height" "128" "lip" "0" "wait" "4" "sounds" "2" "speed" "100" "classname" "func_plat" } { "origin" "312 928 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "312 576 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-522 138 412" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-1072 -64 416" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-1200 64 416" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-1072 64 416" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-150 214 228" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-150 -214 228" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "0 -240 152" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "origin" "0 240 152" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "origin" "-32 784 360" "_color" ".7 .4 0" "style" "0" "light" "120" "classname" "light" } { "origin" "128 928 488" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-152 0 40" "style" "0" "light" "200" "classname" "light" } { "origin" "592 -56 64" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "0 -320 168" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "0 320 168" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "344 -576 352" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-984 0 -96" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "model" "*40" "target" "brij0" "lip" "0" "wait" "4" "sounds" "1" "speed" "50" "targetname" "2nd" "classname" "func_train" } { "origin" "-56 0 200" "_color" ".7 .4 0" "classname" "light" } { "origin" "-522 -130 412" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-872 -352 32" "_color" ".7 .4 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-1032 0 64" "_color" ".7 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1244 0 292" "_color" ".7 .4 0" "style" "0" "light" "100" "classname" "light" } { "origin" "-480 0 328" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "origin" "-800 0 328" "_color" ".7 .4 0" "style" "0" "light" "150" "classname" "light" } { "origin" "-1648 -16 -128" "_color" ".7 .4 0" "wait" "-1" "target" "brij1" "targetname" "brij1" "classname" "path_corner" } { "model" "*41" "delay" "1" "target" "jug14" "spawnflags" "2048" "classname" "trigger_multiple" } { "origin" "0 908 240" "map" "jug14" "targetname" "jug14" "classname" "target_changelevel" } { "origin" "-1225 -306 380" "item" "key_red_key" "wait" "0.2" "target" "rkey_cl" "targetname" "5butt" "classname" "trigger_key" } { "model" "*42" "target" "5butt" "spawnflags" "2048" "classname" "trigger_multiple" } { "origin" "-104 841 178" "item" "key_pass" "wait" "0.2" "delay" "1" "target" "pkey_cl" // b#3: r_cl -> pkey_cl "targetname" "endkey" "classname" "trigger_key" } { "model" "*43" "wait" "0.2" "target" "endkey" "spawnflags" "2048" "classname" "trigger_multiple" } { "model" "*44" "lip" "0" "wait" "-1" "sounds" "1" "speed" "100" "targetname" "pkey_cl" // b#3: r_cl -> pkey_cl "spawnflags" "2048" "angle" "270" "classname" "func_door" } { "origin" "684 845 -82" "item" "key_blue_key" "target" "bluedoor" "targetname" "bkey_tr" "classname" "trigger_key" } { "model" "*45" "wait" "0.2" "target" "bkey_tr" "spawnflags" "2048" "classname" "trigger_multiple" } { "model" "*46" "wait" "0.2" "target" "bkey_tr" "spawnflags" "2048" "classname" "trigger_multiple" } { "origin" "-166 188 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-82" "classname" "weapon_grenadelauncher" } { "origin" "-163 -144 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-86" "classname" "ammo_grenades" } { "origin" "178 -182 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "24" "classname" "item_armor_combat" } { "origin" "-829 -4 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "1" "classname" "monster_soldier_light" } { "origin" "-762 -1 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "1" "classname" "monster_soldier_light" } { "origin" "-641 1 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "1" "classname" "monster_soldier_light" } { "origin" "-539 2 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "monster_soldier_light" } { "origin" "-431 1 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "monster_soldier_light" } { "origin" "-332 0 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "monster_soldier_light" } { "origin" "-928 -349 -23" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "92" "classname" "monster_infantry" } { "origin" "-1177 -355 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "3" "classname" "monster_gunner" } { "origin" "-1739 -6 312" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "5" "classname" "monster_gunner" } { "origin" "0 -335 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-87" "classname" "monster_gunner" } { "origin" "0 333 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "90" "classname" "monster_gunner" } { "origin" "-167 1 320" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "178" "classname" "monster_gunner" } { "origin" "247 873 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-91" "classname" "monster_soldier_light" } { "origin" "244 790 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-91" "classname" "monster_soldier_light" } { "origin" "241 697 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-91" "classname" "monster_soldier_light" } { "origin" "237 611 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-102" "classname" "monster_soldier_light" } { "origin" "-1757 264 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "-58" "classname" "monster_soldier_light" } { "origin" "-1584 299 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "-98" "classname" "monster_soldier_light" } { "origin" "-1746 -9 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "1" "classname" "monster_soldier_light" } { "origin" "-1751 -305 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "34" "classname" "monster_soldier_light" } { "origin" "-1543 -331 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "59" "classname" "monster_soldier_light" } { "origin" "-1365 -190 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "classname" "monster_soldier_light" } { "origin" "-580 -437 312" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "89" "classname" "monster_mutant" } { "origin" "-578 428 312" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "-87" "classname" "monster_mutant" } { "origin" "839 6 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "178" "classname" "monster_soldier_light" } { "origin" "756 8 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "178" "classname" "monster_soldier_light" } { "origin" "636 10 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "178" "classname" "monster_soldier_light" } { "origin" "491 13 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "178" "classname" "monster_soldier_light" } { "origin" "6 -331 152" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "177" "classname" "monster_gunner" } { "origin" "4 -231 152" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "91" "classname" "monster_gunner" } { "origin" "-12 328 152" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-179" "classname" "monster_gunner" } { "origin" "-1 199 152" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-92" "classname" "monster_gunner" } { "origin" "-1068 -44 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "-149" "classname" "monster_soldier_light" } { "origin" "-1069 87 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "133" "classname" "monster_soldier_light" } { "origin" "-1210 377 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "-88" "classname" "monster_soldier_light" } { "origin" "-1387 190 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-11" "classname" "monster_soldier_light" } { "origin" "-1475 309 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-108" "classname" "monster_soldier_light" } { "origin" "-1751 72 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "monster_soldier_light" } { "origin" "-1742 -154 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-12" "classname" "monster_soldier_light" } { "origin" "-1689 343 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-1" "classname" "item_health_small" } { "origin" "-1624 341 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-1" "classname" "item_health_small" } { "origin" "-1537 338 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-1445 337 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-1358 335 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-1279 334 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-1193 332 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-1107 330 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-1021 329 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-942 327 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-1293 354 312" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "-177" "classname" "monster_mutant" } { "origin" "-955 366 312" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "-179" "classname" "monster_mutant" } { "origin" "-922 7 312" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "89" "classname" "monster_mutant" } { "origin" "475 434 -103" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "-1" "classname" "monster_mutant" } { "origin" "1232 459 -103" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "179" "classname" "monster_mutant" } { "origin" "1049 906 -135" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "-88" "classname" "monster_infantry" } { "origin" "898 732 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-118" "classname" "monster_soldier_light" } { "origin" "819 720 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-54" "classname" "monster_soldier_light" } { "origin" "-556 -594 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-1" "classname" "item_health_small" } { "origin" "-491 -595 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-411 -595 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-331 -595 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-252 -595 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-172 -595 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-99 -595 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-26 -595 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "45 -595 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "117 -595 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "188 -595 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "279 -595 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_armor_combat" } { "origin" "862 749 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "86" "classname" "monster_medic" } { "origin" "989 832 168" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "89" "classname" "item_armor_combat" } { "origin" "987 623 168" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-90" "classname" "item_health_large" } { "origin" "744 611 168" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "171" "classname" "item_health_large" } { "origin" "738 806 168" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "93" "classname" "item_armor_combat" } { "origin" "315 203 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "89" "classname" "item_health_small" } { "origin" "315 296 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "89" "classname" "item_health_small" } { "origin" "217 317 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "178" "classname" "item_health_small" } { "origin" "131 319 -104" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "178" "classname" "item_health_small" } { "origin" "51 320 -70" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "178" "classname" "item_health_small" } { "origin" "-34 320 -34" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-178" "classname" "item_health_small" } { "origin" "-112 317 -2" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-178" "classname" "item_health_small" } { "origin" "-190 316 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-178" "classname" "item_health_small" } { "origin" "-281 314 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-178" "classname" "item_health_small" } { "origin" "-325 125 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-89" "classname" "item_health_small" } { "origin" "-325 41 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-90" "classname" "item_health_small" } { "origin" "-325 -37 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-90" "classname" "item_health_small" } { "origin" "-325 -121 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-90" "classname" "item_health_small" } { "origin" "-325 -199 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-90" "classname" "item_health_small" } { "origin" "-325 -277 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-90" "classname" "item_health_small" } { "origin" "-223 -322 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "2" "classname" "item_health_small" } { "origin" "-96 -318 -3" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-17 -316 -42" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "54 -316 -66" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "125 -316 -97" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "223 -316 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "301 -316 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "item_health_small" } { "origin" "-211 -4 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "2" "classname" "ammo_bullets" } { "origin" "-133 0 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "2" "classname" "ammo_bullets" } { "origin" "16 2 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-1" "classname" "ammo_bullets" } { "origin" "142 0 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "ammo_bullets" } { "origin" "311 0 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "weapon_chaingun" } { "origin" "-939 4 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-91" "classname" "ammo_bullets" } { "origin" "-939 -188 -65" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-87" "classname" "ammo_bullets" } { "origin" "-1193 -93 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "91" "classname" "ammo_bullets" } { "origin" "-1194 -16 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "91" "classname" "ammo_bullets" } { "origin" "-1195 55 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "91" "classname" "ammo_bullets" } { "origin" "-1044 14 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-1" "classname" "item_health_large" } { "origin" "-1404 343 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "91" "classname" "item_health_large" } { "origin" "-1566 -160 -55" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-96" "classname" "item_armor_combat" } { "origin" "-1569 176 -55" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "88" "classname" "item_armor_combat" } { "origin" "-1799 356 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "93" "classname" "ammo_grenades" } { "origin" "-1812 -366 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-88" "classname" "ammo_grenades" } { "origin" "-1676 351 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "ammo_grenades" } { "origin" "-1175 359 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "171" "classname" "weapon_hyperblaster" } { "origin" "-748 -1 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "ammo_grenades" } { "origin" "-577 3 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "2" "classname" "ammo_grenades" } { "origin" "-325 -325 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-2" "classname" "monster_soldier_light" } { "origin" "-199 -327 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "monster_soldier_light" } { "origin" "-72 -327 -16" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "monster_soldier_light" } { "origin" "40 -327 -61" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "monster_soldier_light" } { "origin" "138 -327 -104" "spawnflags" "2048" // b#5: 6144 -> 2048 "classname" "monster_soldier_light" } { "origin" "295 -300 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "84" "classname" "monster_soldier_light" } { "origin" "-163 310 23" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-1" "classname" "monster_soldier_light" } { "origin" "-84 307 -10" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-1" "classname" "monster_soldier_light" } { "origin" "-5 304 -42" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-1" "classname" "monster_soldier_light" } { "origin" "81 301 -83" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-1" "classname" "monster_soldier_light" } { "origin" "323 280 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-90" "classname" "monster_soldier_light" } { "origin" "147 1 24" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "178" "classname" "monster_infantry" } { "origin" "-366 367 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-36" "classname" "ammo_grenades" } { "origin" "-363 -367 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-139" "classname" "ammo_grenades" } { "origin" "563 14 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "89" "classname" "ammo_cells" } { "origin" "707 9 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "94" "classname" "ammo_cells" } { "origin" "317 0 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "178" "classname" "item_health_large" } { "origin" "555 6 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "177" "classname" "item_health_large" } { "origin" "856 175 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-94" "classname" "ammo_grenades" } { "origin" "1051 614 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-178" "classname" "item_health_large" } { "origin" "660 611 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "176" "classname" "item_health_large" } { "origin" "-603 324 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-89" "classname" "monster_soldier_light" } { "origin" "-602 213 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-89" "classname" "monster_soldier_light" } { "origin" "-579 -339 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "89" "classname" "monster_soldier_light" } { "origin" "-575 -179 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "88" "classname" "monster_soldier_light" } { "origin" "-122 -572 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-179" "classname" "monster_soldier_light" } { "origin" "-226 -570 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "179" "classname" "monster_soldier_light" } { "origin" "-409 -567 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "179" "classname" "monster_soldier_light" } { "origin" "-190 578 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "178" "classname" "monster_soldier_light" } { "origin" "-426 570 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-177" "classname" "monster_soldier_light" } { "origin" "1 8 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-176" "classname" "item_health_large" } { "origin" "-87 2 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-176" "classname" "item_health_large" } { "origin" "-430 0 312" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "179" "classname" "monster_medic" } { "origin" "450 592 -103" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-87" "classname" "monster_soldier_light" } { "origin" "709 601 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "172" "classname" "monster_soldier_light" } { "origin" "782 598 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-174" "classname" "monster_soldier_light" } { "origin" "857 590 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "3" "classname" "monster_soldier_light" } { "origin" "982 598 -135" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "4" "classname" "monster_soldier_light" } { "origin" "1237 589 -103" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-85" "classname" "monster_soldier_light" } { "origin" "904 21 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "90" "classname" "item_health_large" } { "origin" "895 316 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "90" "classname" "item_health_large" } { "origin" "822 119 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "80" "classname" "ammo_grenades" } { "origin" "1062 766 -135" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "-89" "classname" "monster_soldier_ss" } { "origin" "669 892 -135" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "-83" "classname" "monster_medic" } { "origin" "670 746 -135" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "-92" "classname" "monster_soldier_ss" } { "origin" "553 600 -103" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "-175" "classname" "monster_soldier_ss" } { "origin" "855 668 -135" "spawnflags" "2049" // b#5: 6145 -> 2049 "angle" "-39" "classname" "monster_soldier_ss" } { "origin" "733 717 168" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "109" "classname" "ammo_rockets" } { "origin" "995 765 168" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "89" "classname" "weapon_rocketlauncher" } { "origin" "1271 625 -103" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "178" "classname" "ammo_grenades" } { "origin" "1282 418 -103" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-89" "classname" "ammo_grenades" } { "origin" "850 449 24" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-177" "classname" "ammo_grenades" } { "origin" "501 594 -103" "spawnflags" "2048" // b#5: 6144 -> 2048 "angle" "-76" "classname" "ammo_grenades" } { "origin" "-8 104 376" "target" "helpmsg" "delay" "0.3" // b#6: added this "classname" "trigger_always" } { "origin" "4 108 372" "message" "The Callistans have\nthe PCD. Yet still\nare conducting searches." "targetname" "helpmsg" "spawnflags" "1" "classname" "target_help" } { "origin" "-8 108 348" "message" "They have sabotaged the\ndoors and elevator\nsystems to slow your\nprogress." "targetname" "helpmsg" "classname" "target_help" } { "origin" "49 199 -135" "angle" "-98" "classname" "info_player_coop" } { "origin" "-72 204 -135" "angle" "-93" "classname" "info_player_coop" } { "origin" "-51 18 -135" "angle" "1" "classname" "info_player_coop" } { "origin" "-63 -153 -135" "angle" "91" "classname" "info_player_coop" } { "origin" "65 -157 -135" "angle" "91" "classname" "info_player_coop" } { "origin" "-184 -196 -135" "angle" "58" "classname" "info_player_coop" }yquake2-QUAKE2_8_40/stuff/mapfixes/juggernaut/jug19.ent000066400000000000000000001651421465112212000227070ustar00rootroot00000000000000// FIXED ENTITY STRING (by BjossiAlfreds) // // 1. Moved monster_soldier_ss out of solid space (b#1) // // 2. Added misc_teleporter and destination to get out of sled area (b#2) // // 3. Removed func_door and trigger_key (b#3) // // There is no power cube in the level and the doors served no purpose. // // 4. Added Power Shield to sled area to give it a purpose (b#4) // // 5. Removed unused entities and targets (b#5) // // The targetless trigger_relay might have something to do with // the light entities with unused targetnames, judging by origins. // Not enough context to know what the intention was. // // 6. Fixed help icon appearing after every savegame load (b#6) { "sky" "env2_" "skyrotate" "0" "skyaxis" "0" "gravity" "800" "message" "THE HEART OF DARKNESS" "light" "0" "sounds" "5" "classname" "worldspawn" } { "origin" "-79 949 30" "style" "0" "light" "200" "classname" "light" } { "origin" "-80 688 32" "_color" ".4 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-176 484 96" "style" "0" "light" "250" "classname" "light" } { "origin" "16 472 48" "_color" ".4 1 0" "style" "0" "light" "250" "classname" "light" } { "origin" "111 159 96" "_color" "0 .7 .6" "style" "0" "light" "200" "classname" "light" } { "origin" "-81 160 96" "_color" "1 .4 0" "style" "0" "light" "200" "classname" "light" } { "origin" "112 368 216" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "-80 367 216" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "16 216 216" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "-248 288 216" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "16 816 160" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "-176 815 160" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "-24 1088 -160" "_color" "1 .5 0" "style" "0" "light" "175" "classname" "light" } { "origin" "-216 1088 -160" "_color" "1 .5 0" "style" "0" "light" "175" "classname" "light" } { "origin" "320 976 -158" "_color" "1 .5 0" "style" "0" "light" "175" "classname" "light" } { "origin" "192 1088 -158" "_color" "1 .5 0" "style" "0" "light" "175" "classname" "light" } { "origin" "193 656 -168" "_color" "1 .5 0" "style" "0" "light" "175" "classname" "light" } { "origin" "-16 656 -168" "_color" "1 .5 0" "style" "0" "light" "175" "classname" "light" } { "origin" "-225 656 -168" "_color" "1 .5 0" "style" "0" "light" "175" "classname" "light" } { "origin" "-225 912 -168" "style" "0" "light" "200" "classname" "light" } { "origin" "192 288 216" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "232 1064 152" "_color" "1 .6 .1" "style" "0" "light" "400" "classname" "light" } { "origin" "-316 288 24" "_color" ".4 1 0" "style" "0" "light" "300" "classname" "light" } { "origin" "-80 732 159" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "-80 900 175" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "80 816 -16" "_color" ".4 1 0" "style" "0" "light" "250" "classname" "light" } { "origin" "208 473 48" "style" "0" "light" "200" "classname" "light" } { "origin" "1052 -592 12" "_color" ".2 .4 .6" "style" "0" "light" "300" "classname" "light" } { "origin" "1051 -435 12" "_color" ".2 .4 .6" "style" "0" "light" "300" "classname" "light" } { "origin" "838 -592 11" "_color" ".2 .4 .6" "style" "0" "light" "300" "classname" "light" } { "origin" "841 -435 12" "_color" ".2 .4 .6" "style" "0" "light" "300" "classname" "light" } { "origin" "806 -504 -180" "_color" ".2 .4 .6" "style" "0" "light" "150" "classname" "light" } { "origin" "1010 -504 -180" "_color" ".2 .6 .4" "style" "0" "light" "150" "classname" "light" } { "origin" "1088 -1296 -141" "_color" ".8 .5 .8" //"targetname" "part" // b#5: never targeted "style" "32" "light" "500" "classname" "light" } { "origin" "683 -1280 -1" "_color" "1 0 0" //"targetname" "full" // b#5: never targeted "style" "33" "light" "300" "spawnflags" "1" "classname" "light" } { "origin" "1004 -1364 4" "_color" ".1 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "1172 -1196 4" "_color" ".1 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "969 -1276 4" "_color" ".1 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "1196 -1275 4" "_color" ".1 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "1085 -1165 4" "_color" ".1 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "1087 -1392 4" "_color" ".1 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "1004 -1196 4" "_color" ".1 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "1172 -1360 4" "_color" ".1 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "-632 312 104" "style" "0" "light" "250" "classname" "light" } { "origin" "-864 432 104" "_color" ".1 .5 .4" "style" "0" "light" "200" "classname" "light" } { "origin" "-712 312 104" "style" "0" "light" "250" "classname" "light" } { "origin" "-564 686 32" "_color" "0 .8 .5" "style" "0" "light" "350" "classname" "light" } { "origin" "-864 312 104" "_color" ".1 .5 .4" "style" "0" "light" "200" "classname" "light" } { "origin" "-576 528 64" "_color" ".1 .5 .4" "style" "0" "light" "200" "classname" "light" } { "origin" "-896 -8 0" "_color" ".3 .6 .5" "style" "0" "light" "350" "classname" "light" } { "origin" "66 816 -212" "style" "0" "light" "200" "classname" "light" } { "origin" "-938 418 -24" "_color" ".1 .5 .4" "style" "0" "light" "200" "classname" "light" } { "origin" "-940 546 -24" "_color" ".1 .5 .4" "style" "0" "light" "200" "classname" "light" } { "origin" "-1240 8 40" "_color" ".5 1 3" "style" "10" "light" "200" "classname" "light" } { "origin" "-880 808 104" "_color" ".1 .5 .4" "style" "0" "light" "200" "classname" "light" } { "origin" "-880 720 192" "_color" ".1 .5 .4" "style" "0" "light" "200" "classname" "light" } { "origin" "-1296 80 72" "_color" ".5 1 3" "style" "0" "light" "200" "classname" "light" } { "origin" "-1168 80 72" "_color" ".5 1 3" "style" "0" "light" "200" "classname" "light" } { "origin" "-1168 -64 72" "_color" ".5 1 3" "style" "0" "light" "200" "classname" "light" } { "origin" "-1296 -64 72" "_color" ".5 1 3" "style" "0" "light" "200" "classname" "light" } { "origin" "-1295 144 200" "_color" ".5 1 3" "style" "0" "light" "200" "classname" "light" } { "origin" "-1167 144 200" "_color" ".5 1 3" "style" "0" "light" "200" "classname" "light" } { "origin" "-1169 -128 200" "style" "0" "light" "200" "classname" "light" } { "origin" "-1297 -128 200" "style" "0" "light" "200" "classname" "light" } { "origin" "-1392 65 200" "_color" ".5 1 3" "style" "0" "light" "200" "classname" "light" } { "origin" "-1392 -63 200" "_color" ".5 1 3" "style" "0" "light" "200" "classname" "light" } { "origin" "1088 -1256 -139" "_color" ".8 .5 .8" //"targetname" "full" // b#5: never targeted "style" "33" "light" "500" "spawnflags" "1" "classname" "light" } { "origin" "-971 -995 -148" "_color" ".6 .8 .1" "style" "5" "light" "200" "classname" "light" } { "origin" "-968 -995 -52" "_color" ".6 .8 .1" "style" "5" "light" "200" "classname" "light" } { "origin" "-750 -987 -53" "_color" ".6 .8 .1" "style" "5" "light" "200" "classname" "light" } { "origin" "-753 -987 -149" "_color" ".6 .8 .1" "style" "5" "light" "200" "classname" "light" } { "origin" "-1104 -631 -144" "style" "0" "light" "200" "classname" "light" } { "origin" "-1104 -553 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1104 -503 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1104 -425 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1104 -375 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1104 -297 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1104 -247 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1104 -169 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1104 -119 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-1104 -41 -144" "_color" ".2 .5 .6" "style" "0" "light" "200" "classname" "light" } { "origin" "-944 -502 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-944 -424 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-944 -374 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-944 -296 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-944 -246 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-944 -168 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-944 -118 -144" "_color" ".6 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-944 -40 -144" "_color" ".2 .5 .6" "style" "0" "light" "200" "classname" "light" } { "origin" "-920 -640 56" "_color" ".6 .8 .1" "style" "0" "light" "350" "classname" "light" } { "origin" "-1024 -688 56" "_color" ".6 .8 .1" "style" "0" "light" "400" "classname" "light" } { "origin" "320 -948 -200" "_color" ".3 .5 .4" "style" "0" "light" "150" "classname" "light" } { "origin" "188 -948 -200" "_color" ".3 .5 .4" "style" "0" "light" "150" "classname" "light" } { "origin" "60 -948 -200" "_color" ".3 .5 .4" "style" "0" "light" "150" "classname" "light" } { "origin" "-56 -944 -200" "_color" ".3 .5 .4" "style" "0" "light" "150" "classname" "light" } { "origin" "-1024 176 -288" "style" "0" "light" "300" "classname" "light" } { "origin" "-1024 112 -287" "style" "0" "light" "300" "classname" "light" } { "origin" "-1168 656 -287" "style" "0" "light" "300" "classname" "light" } { "origin" "-752 464 -168" "_color" ".2 .5 .6" "style" "0" "light" "400" "classname" "light" } { "origin" "-576 240 -181" "_color" ".2 .5 .6" "style" "0" "light" "200" "classname" "light" } { "origin" "-392 -72 -160" "style" "0" "light" "250" "classname" "light" } { "origin" "-168 -64 -240" "style" "0" "light" "200" "classname" "light" } { "origin" "24 -470 -368" "_color" "1 .7 0" "style" "0" "light" "200" "classname" "light" } { "origin" "176 -470 -368" "_color" "1 .7 0" "style" "0" "light" "200" "classname" "light" } { "origin" "327 -470 -368" "_cone" "10" "_color" "1 .7 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-127 -470 -368" "_color" "1 .7 0" "style" "0" "light" "200" "classname" "light" } { "origin" "24 -307 -368" "_color" "0 .7 .6" "style" "0" "light" "200" "classname" "light" } { "origin" "176 -307 -368" "_color" "0 .7 .6" "style" "0" "light" "200" "classname" "light" } { "origin" "327 -307 -368" "_color" "1 .7 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-127 -307 -368" "_color" "1 .7 0" "style" "0" "light" "200" "classname" "light" } { "origin" "24 -145 -368" "_color" "0 .7 .6" "style" "0" "light" "200" "classname" "light" } { "origin" "176 -145 -368" "_color" "0 .7 .6" "style" "0" "light" "200" "classname" "light" } { "origin" "327 -145 -368" "_color" "1 .7 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-127 -145 -368" "_color" "1 .7 0" "style" "0" "light" "200" "classname" "light" } { "origin" "24 16 -368" "_color" ".5 .5 1" "style" "0" "light" "200" "classname" "light" } { "origin" "176 16 -368" "_color" ".5 .5 1" "style" "0" "light" "200" "classname" "light" } { "origin" "327 16 -368" "_color" "1 .7 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-127 16 -368" "_color" "1 .7 0" "style" "0" "light" "200" "classname" "light" } { "origin" "24 -470 -304" "_color" ".5 .5 1" "style" "0" "light" "200" "classname" "light" } { "origin" "176 -470 -304" "_color" ".5 .5 1" "style" "0" "light" "200" "classname" "light" } { "origin" "327 -470 -304" "style" "0" "light" "200" "classname" "light" } { "origin" "-127 -470 -304" "style" "0" "light" "200" "classname" "light" } { "origin" "24 -307 -304" "style" "0" "light" "200" "classname" "light" } { "origin" "176 -307 -304" "style" "0" "light" "200" "classname" "light" } { "origin" "327 -307 -304" "style" "0" "light" "200" "classname" "light" } { "origin" "-127 -307 -304" "style" "0" "light" "200" "classname" "light" } { "origin" "24 -145 -304" "style" "0" "light" "200" "classname" "light" } { "origin" "176 -145 -304" "style" "0" "light" "200" "classname" "light" } { "origin" "327 -145 -304" "style" "0" "light" "200" "classname" "light" } { "origin" "-127 -145 -304" "style" "0" "light" "200" "classname" "light" } { "origin" "24 16 -304" "style" "0" "light" "200" "classname" "light" } { "origin" "176 16 -304" "style" "0" "light" "200" "classname" "light" } { "origin" "327 16 -304" "style" "0" "light" "200" "classname" "light" } { "origin" "-127 16 -304" "style" "0" "light" "200" "classname" "light" } { "model" "*1" "delay" "2" "lip" "16" "wait" "4" "sounds" "1" "speed" "56" "angle" "-2" "classname" "func_door" } { "model" "*2" "lip" "16" "wait" "4" "sounds" "1" "speed" "112" "angle" "-1" "classname" "func_door" } { "origin" "-696 -424 -128" "_color" "0 .8 .7" "style" "0" "light" "350" "classname" "light" } { "origin" "-456 -696 -168" "_color" "0 .8 .7" "style" "0" "light" "200" "classname" "light" } { "origin" "-464 -320 -168" "_color" "0 .8 .7" "style" "0" "light" "300" "classname" "light" } { "origin" "-1055 143 -287" "style" "0" "light" "300" "classname" "light" } { "origin" "-991 143 -286" "style" "0" "light" "300" "classname" "light" } { "origin" "-352 -632 -166" "_color" "0 .8 .7" "style" "0" "light" "200" "classname" "light" } { "origin" "-744 288 -288" "style" "0" "light" "200" "classname" "light" } { "origin" "-743 232 -288" "style" "0" "light" "200" "classname" "light" } { "origin" "-743 176 -288" "style" "0" "light" "200" "classname" "light" } { "origin" "-920 672 -264" "style" "0" "light" "200" "classname" "light" } { "model" "*3" "targetname" "p1" "lip" "96" "wait" "4" "sounds" "2" "speed" "100" "angle" "-1" "classname" "func_door" } { "model" "*4" "target" "p1" "wait" "0.2" "delay" "2" "classname" "trigger_multiple" } { "origin" "-1019 -675 -184" "_color" ".6 .8 .1" "classname" "light" } { "origin" "-987 -810 -155" "_color" ".6 .8 .1" "style" "0" "light" "150" "classname" "light" } { "model" "*5" "delay" "2" "dmg" "220" "lip" "-960" "wait" "3" "sounds" "1" "speed" "600" "targetname" "sled" "spawnflags" "32" "angle" "90" "classname" "func_door" } { "origin" "-200 -800 -166" "_color" "1 .7 .5" "style" "0" "light" "250" "classname" "light" } { "origin" "196 -768 -184" "_cone" "10" "_color" ".6 .6 1" "style" "0" "light" "600" "classname" "light" } { "origin" "-64 -656 -165" "_color" "1 .7 .5" "style" "0" "light" "250" "classname" "light" } { "origin" "692 -1280 0" "_color" "1 0 0" //"targetname" "part" // b#5: never targeted "style" "32" "light" "250" "classname" "light" } { "origin" "544 -1104 -80" "_color" ".7 .5 0" "style" "0" "light" "200" "classname" "light" } { "origin" "576 -848 -80" "_color" ".7 .5 0" "style" "0" "light" "250" "classname" "light" } { "origin" "576 -592 -80" "_color" ".7 .5 0" "style" "0" "light" "250" "classname" "light" } { "origin" "560 -1296 -80" "_color" ".7 .5 0" "style" "0" "light" "200" "classname" "light" } { "origin" "552 -336 -64" "_color" ".7 .5 0" "style" "0" "light" "200" "classname" "light" } { "origin" "560 -64 -16" "_color" ".7 .5 0" "style" "0" "light" "250" "classname" "light" } { "origin" "576 192 -16" "_color" ".7 .5 0" "style" "0" "light" "250" "classname" "light" } { "origin" "640 48 -16" "_color" ".7 .5 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-446 -62 171" "_color" "0 .2 .7" "classname" "light" } { "origin" "-446 -62 27" "classname" "light" } { "origin" "-734 816 190" "_color" "0 .3 .6" "style" "0" "light" "200" "classname" "light" } { "origin" "-766 528 254" "_color" "0 .3 .6" "classname" "light" } { "origin" "-686 272 254" "_color" "0 .3 .6" "classname" "light" } { "origin" "-702 64 254" "classname" "light" } { "origin" "-702 -62 27" "_color" ".2 .6 .4" "classname" "light" } { "origin" "-702 194 75" "_color" ".2 .6 .4" "classname" "light" } { "origin" "-512 1104 -168" "_color" ".7 1 .3" "style" "0" "light" "150" "classname" "light" } { "origin" "-344 1088 -160" "_color" "1 .5 0" "style" "0" "light" "175" "classname" "light" } { "origin" "-664 528 64" "_color" ".1 .5 .4" "style" "0" "light" "200" "classname" "light" } { "origin" "-664 448 64" "_color" ".1 .5 .4" "style" "0" "light" "200" "classname" "light" } { "origin" "-576 448 64" "_color" ".1 .5 .4" "style" "0" "light" "200" "classname" "light" } { "model" "*6" "wait" "5" "delay" ".5" "target" "m" "classname" "trigger_multiple" } { "origin" "-592 688 144" "_cone" "10" "_color" "1 .5 0" "style" "1" "light" "250" "classname" "light" } { "origin" "-784 734 44" "_color" ".1 .5 .4" "style" "0" "light" "200" "classname" "light" } { "origin" "-816 496 32" "_color" ".1 .5 .4" "style" "0" "light" "200" "classname" "light" } { "model" "*7" "delay" "2" "lip" "16" "wait" "4" "sounds" "1" "speed" "112" "angle" "-1" "classname" "func_door" } { "model" "*8" "lip" "16" "wait" "4" "sounds" "1" "speed" "56" "angle" "-2" "classname" "func_door" } { "model" "*9" "delay" "2" "lip" "16" "wait" "4" "sounds" "1" "speed" "112" "angle" "-1" "classname" "func_door" } { "model" "*10" "lip" "16" "wait" "4" "sounds" "1" "speed" "56" "angle" "-2" "classname" "func_door" } { "model" "*11" "message" "Contamination - Sect4AQ Sealed" "lip" "16" "wait" "4" "sounds" "1" "speed" "112" "angle" "-1" "classname" "func_door" } { "model" "*12" "lip" "16" "wait" "4" "sounds" "1" "speed" "56" "angle" "-2" "classname" "func_door" } { "model" "*13" "delay" "4" "target" "sled" "wait" "4" "sounds" "1" "speed" "100" "angle" "-2" "classname" "func_button" } { "origin" "-446 -382 91" "_color" "0 .8 .7" "classname" "light" } { "origin" "-264 -912 -224" "_color" ".2 .6 .5" "style" "0" "light" "200" "classname" "light" } { "origin" "-496 -912 -292" "_color" ".2 .6 .5" "style" "0" "light" "200" "classname" "light" } { "origin" "-576 -930 -304" "style" "0" "light" "200" "classname" "light" } { "origin" "-576 -802 -304" "_color" ".2 .6 .5" "light" "200" "classname" "light" } { "origin" "-576 -672 -304" "_color" ".2 .6 .5" "style" "0" "light" "200" "classname" "light" } { "origin" "-576 -516 -304" "_color" ".2 .6 .5" "light" "200" "classname" "light" } { "origin" "-576 -360 -304" "_color" ".2 .6 .5" "style" "0" "light" "200" "classname" "light" } { "model" "*14" "target" "sled" "wait" "5" "delay" "2" "classname" "trigger_multiple" } { "model" "*15" "targetname" "enddoor" "lip" "24" "wait" "-1" "sounds" "1" "speed" "112" "spawnflags" "3840" // b#3: added this "angle" "-1" "classname" "func_door" } { "model" "*16" "targetname" "enddoor" "lip" "24" "wait" "-1" "sounds" "1" "speed" "56" "spawnflags" "3840" // b#3: added this "angle" "-2" "classname" "func_door" } { "origin" "-904 340 256" "style" "0" "light" "200" "classname" "light" } { "origin" "448 -624 -112" "style" "0" "light" "250" "classname" "light" } { "origin" "320 -768 -160" "style" "0" "light" "170" "classname" "light" } { "origin" "-372 -912 -256" "_color" ".2 .6 .5" "style" "0" "light" "200" "classname" "light" } { "origin" "-1388 -528 222" "_color" "1 .6 0" "light" "150" "classname" "light" } { "origin" "-1324 -456 222" "_color" "1 .6 0" "style" "0" "light" "150" "classname" "light" } { "origin" "-1236 -408 222" "_color" "1 .6 0" "style" "0" "light" "150" "classname" "light" } { "origin" "-1100 -400 222" "light" "150" "_color" "1 .6 0" "classname" "light" } { "origin" "-988 -432 222" "_color" "1 .6 0" "style" "0" "light" "150" "classname" "light" } { "origin" "-992 -536 136" "_color" "1 .6 0" "style" "0" "light" "150" "classname" "light" } { "origin" "-1054 -536 135" "_color" "1 .6 0" "style" "0" "light" "150" "classname" "light" } { "origin" "-1236 -304 222" "_color" "1 .6 0" "style" "0" "light" "150" "classname" "light" } { "origin" "-848 1040 0" "_color" ".7 1 .3" "style" "0" "light" "150" "classname" "light" } { "origin" "-720 1032 0" "_color" ".7 1 .3" "style" "0" "light" "150" "classname" "light" } { "origin" "-520 1104 80" "_color" "1 .7 .2" "style" "0" "light" "150" "classname" "light" } { "model" "*17" "lip" "16" "wait" "10" "sounds" "1" "speed" "112" "angle" "-2" "classname" "func_door" } { "model" "*18" "lip" "16" "wait" "10" "sounds" "1" "speed" "224" "angle" "-1" "classname" "func_door" } { "origin" "416 192 12" "_color" ".4 1 0" "style" "0" "light" "250" "classname" "light" } { "origin" "-216 1104 144" "_color" "1 .6 .1" "style" "0" "light" "350" "classname" "light" } { "origin" "320 520 153" "_color" "1 .6 .1" "style" "0" "light" "450" "classname" "light" } { "origin" "329 264 160" "_color" "1 .6 .1" "style" "0" "light" "250" "classname" "light" } { "origin" "-304 624 52" "style" "0" "light" "300" "classname" "light" } { "origin" "-304 964 52" "style" "0" "light" "300" "classname" "light" } { "origin" "-408 -72 -296" "style" "0" "light" "250" "classname" "light" } { "origin" "-560 -40 -235" "_color" "0 .2 .7" "style" "0" "light" "200" "classname" "light" } { "origin" "-568 112 -195" "_color" "0 .2 .7" "style" "0" "light" "200" "classname" "light" } { "origin" "160 -302 0" "_color" "0 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "159 -146 0" "_color" "0 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "176 -162 0" "_color" "0 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "176 -285 0" "_color" "0 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "17 -286 0" "_color" "0 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "34 -146 0" "_color" "0 .5 1" "style" "0" "light" "300" "classname" "light" } { "origin" "18 -163 0" "_color" "0 .5 1" "style" "0" "light" "300" "classname" "light" } { "origin" "34 -302 0" "_color" "0 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "160 -302 128" "_color" "0 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "184 -112 128" "style" "0" "light" "250" "classname" "light" } { "origin" "176 -162 128" "_color" "0 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "208 -312 128" "style" "0" "light" "250" "classname" "light" } { "origin" "17 -286 128" "_color" "0 .5 1" "style" "0" "light" "300" "classname" "light" } { "origin" "34 -146 128" "_color" "0 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "-15 -136 128" "style" "0" "light" "250" "classname" "light" } { "origin" "8 -336 128" "style" "0" "light" "300" "classname" "light" } { "origin" "160 -302 -128" "_color" "0 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "184 -112 -128" "style" "0" "light" "250" "classname" "light" } { "origin" "176 -162 -128" "_color" "0 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "208 -312 -128" "style" "0" "light" "250" "classname" "light" } { "origin" "17 -286 -128" "_color" "0 .5 1" "style" "0" "light" "300" "classname" "light" } { "origin" "34 -146 -128" "_color" "0 .5 1" "style" "0" "light" "250" "classname" "light" } { "origin" "-15 -136 -128" "style" "0" "light" "250" "classname" "light" } { "origin" "8 -336 -128" "style" "0" "light" "300" "classname" "light" } { "origin" "-752 -800 -32" "_color" "0 .5 .6" "style" "0" "light" "350" "classname" "light" } { "origin" "-910 0 206" "style" "0" "light" "200" "classname" "light" } { "origin" "56 1152 -8" "_color" "1 .7 .2" "style" "0" "light" "250" "classname" "light" } { "origin" "344 808 -48" "_color" ".4 1 0" "style" "0" "light" "200" "classname" "light" } { "origin" "-288 1168 -64" "_color" "1 .7 .2" "style" "0" "light" "200" "classname" "light" } { "model" "*19" "dmg" "0" "wait" "4" "spawnflags" "2" "classname" "func_door_secret" } { "origin" "-1350 -302 52" "_color" "1 .6 0" //"targetname" "secret5" // b#5: never targeted "style" "34" "light" "150" "classname" "light" } { "model" "*20" "lip" "16" "wait" "10" "sounds" "1" "speed" "112" "angle" "-2" "classname" "func_door" } { "model" "*21" "lip" "16" "wait" "10" "sounds" "1" "speed" "224" "angle" "-1" "classname" "func_door" } { "origin" "1088 -1584 -241" "style" "0" "light" "250" "classname" "light" } { "origin" "-864 -976 32" "_color" ".6 1 0" "style" "5" "light" "200" "classname" "light" } { "model" "*22" "targetname" "o" "lip" "-1" "sounds" "1" "speed" "100" "spawnflags" "32" "angle" "-2" "classname" "func_door" } { "model" "*23" "targetname" "m" "lip" "16" "sounds" "1" "speed" "100" "spawnflags" "32" "angle" "-1" "classname" "func_door" } { "origin" "-702 -62 203" "classname" "light" } { "model" "*24" "wait" "0.2" "target" "endkey" "classname" "trigger_multiple" "spawnflags" "3840" // b#5: added this } { "origin" "672 -1132 -224" "classname" "trigger_relay" "spawnflags" "3840" // b#5: added this } { "origin" "-204 12 -147" "classname" "trigger_relay" "spawnflags" "3840" // b#5: added this } { "origin" "-204 -478 68" "classname" "trigger_relay" "spawnflags" "3840" // b#5: added this } { "origin" "-184 40 -146" "classname" "trigger_relay" "spawnflags" "3840" // b#5: added this } { "origin" "-1320 16 40" "classname" "info_player_start" } { "origin" "1520 -1280 -48" "light" "400" "classname" "light" } { "origin" "106 -770 -168" "angle" "180" "classname" "key_commander_head" } { "origin" "584 -1104 -112" "item" "key_commander_head" "wait" "0.2" "target" "headdoor" "targetname" "headkey" "classname" "trigger_key" } { "model" "*25" "wait" "5" "delay" "2" "target" "o" "classname" "trigger_multiple" } { "origin" "-512 1216 16" "_color" "1 .7 .2" "style" "0" "light" "150" "classname" "light" } { "origin" "-1024 320 -176" "_color" ".2 .5 .6" "style" "0" "light" "400" "classname" "light" } { "model" "*26" "message" "The Head of Gorok!!" "classname" "trigger_once" } { "model" "*27" "message" "A foul stench of rotting flesh . . ." "classname" "trigger_once" } { "model" "*28" "delay" "2" "target" "rumb" "message" "oh oh. It's a trap--get out!" "classname" "trigger_once" } { "origin" "-132 -764 -184" "speed" "400" "count" "5" "targetname" "rumb" "classname" "target_earthquake" } { "origin" "-84 -768 -184" "dmg" "150" "delay" "5" "targetname" "rumb" "classname" "target_explosion" } { "origin" "-112 -812 -184" "speed" "5" "message" "az" "target" "rumblite" "targetname" "rumb" "classname" "target_lightramp" } { "origin" "-76 -812 -184" "_color" "1 0 0" "_cone" "10" "style" "35" "light" "600" "targetname" "rumblite" "spawnflags" "1" "classname" "light" } { "origin" "-240 -768 -184" "dmg" "150" "delay" "4" "targetname" "rumb" "classname" "target_explosion" } { "origin" "-64 -684 -184" "dmg" "150" "delay" "5" "targetname" "rumb" "classname" "target_explosion" } { "origin" "16 -748 -184" "dmg" "150" "delay" "5.5" "targetname" "rumb" "classname" "target_explosion" } { "origin" "-68 -512 -184" "dmg" "150" "delay" "6" "targetname" "rumb" "classname" "target_explosion" } { "origin" "92 -768 -236" "_color" "1 .7 .5" "style" "0" "light" "250" "classname" "light" } { "origin" "132 -500 -80" "dmg" "150" "delay" "6.1" "targetname" "rumb" "classname" "target_explosion" } { "origin" "100 -372 -80" "dmg" "150" "delay" "6.3" "targetname" "rumb" "classname" "target_explosion" } { "origin" "-76 -208 -80" "dmg" "150" "delay" "6.5" "targetname" "rumb" "classname" "target_explosion" } { "origin" "56 -388 8" "dmg" "150" "delay" "7" "targetname" "rumb" "classname" "target_explosion" } { "origin" "344 -804 -188" "item" "key_power_cube" "wait" "0.2" "target" "enddoor" "targetname" "endkey" "classname" "trigger_key" "spawnflags" "3840" // b#3: added this } { "model" "*29" "dmg" "200" "targetname" "rumboom" "mass" "800" "classname" "func_explosive" } { "origin" "-500 996 80" "_color" ".7 1 .3" "style" "0" "light" "300" "classname" "light" } { "origin" "-1344 -384 64" "_color" "1 .6 0" //"targetname" "secret5" // b#5: never targeted "style" "34" "light" "150" "classname" "light" } { "origin" "-48 -448 -48" "dmg" "150" "delay" "6" "targetname" "rumb" "classname" "target_explosion" } { "origin" "-96 -432 -40" "dmg" "150" "delay" "6" "targetname" "rumb" "classname" "target_explosion" } { "origin" "-112 -512 -112" "dmg" "150" "delay" "6" "targetname" "rumb" "classname" "target_explosion" } { "origin" "-32 -520 -96" "dmg" "150" "delay" "6" "targetname" "rumb" "classname" "target_explosion" } { "origin" "944 -784 -160" "_color" "1 .5 0" "light" "250" "classname" "light" } { "origin" "1200 -784 -160" "_color" "1 .5 0" "light" "250" "classname" "light" } { "origin" "944 -256 -160" "_color" "1 .5 0" "light" "250" "classname" "light" } { "origin" "1200 -256 -160" "_color" "1 .5 0" "light" "250" "classname" "light" } { "origin" "728 -792 -152" "_color" "1 .5 0" "light" "250" "classname" "light" } { "origin" "728 -264 -152" "_color" "1 .5 0" "light" "250" "classname" "light" } { "origin" "-200 1160 456" "_color" "1 .6 .1" "style" "0" "light" "350" "classname" "light" } { "origin" "-64 956 487" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "32 872 472" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "-64 788 471" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "128 424 528" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "345 320 472" "_color" "1 .6 .1" "style" "0" "light" "250" "classname" "light" } { "origin" "208 344 528" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "32 272 528" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "-64 423 528" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "-232 344 528" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "336 576 465" "_color" "1 .6 .1" "style" "0" "light" "450" "classname" "light" } { "origin" "248 1120 464" "_color" "1 .6 .1" "style" "0" "light" "400" "classname" "light" } { "origin" "-160 871 472" "_color" "1 .6 .1" "style" "0" "light" "200" "classname" "light" } { "origin" "112 -416 -112" "delay" "7" "target" "rumboom" "targetname" "rumb" "classname" "trigger_relay" } { "origin" "-1220 0 40" "classname" "weapon_machinegun" } { "origin" "-1264 44 40" "classname" "ammo_bullets" } { "origin" "-980 -4 32" "angle" "183" "classname" "monster_soldier_light" } { "origin" "-1036 -4 32" "angle" "158" "classname" "monster_soldier_light" } { "origin" "-1308 152 152" "classname" "item_health_large" } { "origin" "-1232 148 152" "classname" "ammo_bullets" } { "origin" "-1308 -136 148" "classname" "ammo_bullets" } { "origin" "-1224 -132 152" "classname" "ammo_bullets" } { "origin" "-900 492 0" "spawnflags" "1" "angle" "274" "classname" "monster_soldier_light" } { "origin" "-896 324 0" "spawnflags" "1" "angle" "274" "classname" "monster_soldier_light" } { "origin" "-576 512 4" "spawnflags" "1" "angle" "232" "classname" "monster_soldier_light" } { "origin" "-712 468 12" "angle" "238" "classname" "monster_soldier_light" } { "origin" "-800 864 -12" "spawnflags" "1" "angle" "274" "classname" "monster_soldier_light" } { "origin" "-760 780 -36" "spawnflags" "1" "angle" "274" "classname" "monster_soldier_light" } { "origin" "-788 724 -36" "spawnflags" "1" "angle" "274" "classname" "monster_soldier_light" } { "origin" "-632 492 -16" "classname" "ammo_bullets" } { "origin" "-632 440 -16" "classname" "ammo_bullets" } { "origin" "-580 468 -16" "classname" "item_health_large" } { "origin" "-632 996 12" "spawnflags" "1" "angle" "185" "classname" "monster_infantry" } { "origin" "-572 1008 0" "spawnflags" "1" "angle" "185" "classname" "monster_infantry" } { "origin" "-700 1004 8" "angle" "176" "classname" "monster_soldier_light" } { "origin" "-892 1016 4" "classname" "weapon_supershotgun" } { "origin" "-504 1056 -200" "spawnflags" "1" "angle" "99" "classname" "monster_infantry" } { "origin" "-384 1056 -204" "angle" "182" "classname" "monster_infantry" } { "origin" "232 996 -192" "spawnflags" "1" "angle" "177" "classname" "monster_hover" } { "origin" "172 960 -180" "spawnflags" "1" "angle" "177" "classname" "monster_hover" } { "origin" "140 1020 -180" "spawnflags" "1" "angle" "183" "classname" "monster_soldier_light" } { "origin" "116 948 -168" "spawnflags" "1" "angle" "183" "classname" "monster_soldier_light" } { "origin" "280 520 -36" "spawnflags" "1" "angle" "90" "classname" "monster_soldier_light" } { "origin" "288 580 -32" "spawnflags" "1" "angle" "90" "classname" "monster_soldier_light" } { "origin" "88 572 -60" "spawnflags" "1" "angle" "42" "classname" "monster_soldier_light" } { "origin" "96 512 -64" "spawnflags" "1" "angle" "42" "classname" "monster_soldier_light" } { "origin" "-92 572 -72" "spawnflags" "1" "angle" "42" "classname" "monster_soldier_light" } { "origin" "-84 512 -76" "spawnflags" "1" "angle" "42" "classname" "monster_soldier_light" } { "origin" "412 1020 544" "angle" "183" "classname" "monster_gunner" } { "origin" "412 636 544" "angle" "183" "classname" "monster_gunner" } { "origin" "-336 472 548" "angle" "354" "classname" "monster_gunner" } { "origin" "-352 852 548" "angle" "183" "classname" "monster_gunner" } { "origin" "-68 1248 552" "angle" "183" "classname" "monster_gunner" } { "origin" "128 1248 552" "angle" "183" "classname" "monster_gunner" } { "origin" "-72 812 488" "targetname" "fball1" "dmg" "100" "classname" "target_explosion" } { "origin" "-100 812 492" "random" "2" "target" "fball1" "pausetime" "0" "wait" "5" "spawnflags" "1" "classname" "func_timer" } { "origin" "-60 296 520" "targetname" "fball3" "dmg" "100" "classname" "target_explosion" } { "origin" "-88 296 524" "random" "1" "target" "fball3" "pausetime" "0" "wait" "4" "spawnflags" "1" "classname" "func_timer" } { "origin" "132 292 468" "targetname" "fball2" "dmg" "100" "classname" "target_explosion" } { "origin" "104 292 472" "random" "1" "target" "fball2" "pausetime" "1" "wait" "3" "spawnflags" "1" "classname" "func_timer" } { "origin" "1088 -1280 -196" "target" "theend" "spawnflags" "1" "angle" "182" "classname" "monster_supertank" } { "origin" "336 296 -80" "classname" "item_health_large" } { "origin" "392 296 -80" "classname" "ammo_bullets" } { "origin" "336 224 -88" "classname" "ammo_bullets" } { "origin" "-328 336 -80" "classname" "weapon_hyperblaster" } { "origin" "-320 232 -80" "classname" "ammo_cells" } { "origin" "-328 384 -80" "classname" "ammo_cells" } { "origin" "-320 568 -24" "spawnflags" "1" "angle" "317" "classname" "monster_hover" } { "origin" "-320 192 -16" "spawnflags" "1" "angle" "39" "classname" "monster_hover" } { "origin" "632 104 -88" "spawnflags" "1" "angle" "90" "classname" "monster_soldier_light" } { "origin" "632 48 -80" "spawnflags" "1" "angle" "90" "classname" "monster_soldier_light" } { "origin" "632 -8 -80" "spawnflags" "1" "angle" "90" "classname" "monster_soldier_light" } { "origin" "552 -144 -96" "spawnflags" "1" "angle" "88" "classname" "monster_soldier_light" } { "origin" "552 -216 -112" "spawnflags" "1" "angle" "88" "classname" "monster_soldier_light" } { "origin" "552 -288 -88" "spawnflags" "1" "angle" "88" "classname" "monster_soldier_light" } { "origin" "832 -552 -176" "spawnflags" "1" "angle" "183" "classname" "monster_gunner" } { "origin" "968 -584 -176" "spawnflags" "1" "angle" "183" "classname" "monster_gunner" } { "origin" "960 -440 -160" "spawnflags" "1" "angle" "183" "classname" "monster_gunner" } { "origin" "600 -744 -152" "spawnflags" "1" "classname" "monster_infantry" } { "origin" "592 -888 -136" "angle" "95" "classname" "monster_soldier_ss" } { "origin" "728 -1288 -136" "classname" "weapon_rocketlauncher" } { "origin" "960 -1496 -152" "classname" "weapon_grenadelauncher" } { "origin" "1488 -1408 -136" "classname" "item_health_mega" } { "origin" "1296 -1256 -160" "spawnflags" "1" "angle" "180" "classname" "monster_gunner" } { "origin" "1304 -1344 -168" "spawnflags" "1" "angle" "180" "classname" "monster_gunner" } { "origin" "1272 -1176 -144" "spawnflags" "1" "angle" "180" "classname" "monster_gunner" } { "origin" "1376 -1200 -136" "spawnflags" "1" "angle" "180" "classname" "monster_gunner" } { "origin" "208 -952 -208" "classname" "monster_soldier_light" } { "origin" "112 -960 -208" "classname" "monster_soldier_light" } { "origin" "-568 -736 -336" "spawnflags" "1" "angle" "267" "classname" "monster_flipper" } { "origin" "-720 -456 -208" // b#1: -320 -> -208 "spawnflags" "1" "classname" "monster_soldier_ss" } { "origin" "-720 -504 -208" "spawnflags" "1" "classname" "monster_soldier_ss" } { "origin" "-608 -480 -224" "classname" "monster_soldier_ss" } { "origin" "-560 -440 -216" "classname" "monster_soldier_ss" } { "origin" "-240 1056 -200" "classname" "weapon_rocketlauncher" } { "origin" "-120 1056 -216" "classname" "weapon_rocketlauncher" } { "origin" "-48 1048 -184" "classname" "weapon_rocketlauncher" } { "origin" "128 776 -216" "classname" "item_health_large" } { "origin" "128 824 -224" "classname" "item_health_large" } { "origin" "256 -384 -152" "spawnflags" "1" "angle" "96" "classname" "monster_medic" } { "origin" "288 -56 -80" "spawnflags" "1" "angle" "180" "classname" "monster_medic" } { "origin" "-32 -64 -8" "spawnflags" "1" "angle" "180" "classname" "monster_soldier_ss" } { "origin" "80 -64 -56" "spawnflags" "1" "angle" "180" "classname" "monster_soldier_ss" } { "origin" "-888 -744 -176" "angle" "142" "classname" "monster_soldier_ss" } { "origin" "-888 -824 -160" "angle" "116" "classname" "monster_soldier_ss" } { "origin" "-824 -800 -176" "angle" "129" "classname" "monster_soldier_ss" } { "origin" "-704 -920 -160" "angle" "138" "classname" "monster_soldier_ss" } { "origin" "-704 -840 -176" "angle" "147" "classname" "monster_soldier_ss" } { "origin" "-768 -784 -176" "angle" "145" "classname" "monster_soldier_ss" } { "origin" "-768 -864 -160" "angle" "152" "classname" "monster_soldier_ss" } { "origin" "-992 -576 -152" "classname" "monster_soldier_ss" } { "origin" "-992 -496 -168" "classname" "monster_soldier_ss" } { "origin" "-1056 -440 -168" "angle" "86" "classname" "monster_soldier_ss" } { "origin" "-1056 -520 -152" "classname" "monster_soldier_ss" } { "origin" "-1000 -328 -160" "angle" "89" "classname" "monster_soldier_ss" } { "origin" "-1000 -248 -176" "angle" "88" "classname" "monster_soldier_ss" } { "origin" "-1064 -192 -176" "angle" "86" "classname" "monster_soldier_ss" } { "origin" "-1064 -272 -160" "angle" "82" "classname" "monster_soldier_ss" } { "origin" "-776 72 -296" "spawnflags" "1" "angle" "270" "classname" "monster_flipper" } { "origin" "-896 256 -304" "angle" "270" "classname" "monster_flipper" } { "origin" "-1024 320 -200" "classname" "weapon_hyperblaster" } { "origin" "-976 312 -200" "classname" "ammo_cells" } { "origin" "-88 -56 -32" "classname" "item_health_large" } { "origin" "-144 -392 24" "classname" "weapon_hyperblaster" } { "origin" "-56 -392 40" "classname" "ammo_cells" } { "origin" "-144 -64 -16" "classname" "weapon_grenadelauncher" } { "origin" "40 -56 -8" "classname" "item_health_large" } { "origin" "732 -1332 -156" "classname" "ammo_rockets" } { "origin" "724 -1188 -132" "classname" "item_health_large" } { "origin" "152 520 -64" "classname" "item_health_large" } { "origin" "-40 508 -68" "classname" "item_health_large" } { "origin" "1196 -468 -128" "classname" "item_armor_body" } { "origin" "1200 -552 -132" "classname" "item_health_large" } { "origin" "-595 689 -31" "angle" "176" "classname" "item_health_large" } { "origin" "293 720 -231" "angle" "178" "classname" "ammo_rockets" } { "origin" "-213 654 -231" "angle" "114" "classname" "ammo_rockets" } { "origin" "-224 972 -231" "classname" "item_health_small" } { "origin" "-120 972 -231" "angle" "1" "classname" "item_health_small" } { "origin" "-21 974 -231" "angle" "2" "classname" "item_health_small" } { "origin" "-311 1170 -103" "angle" "-51" "classname" "ammo_bullets" } { "origin" "-82 1180 -103" "angle" "35" "classname" "ammo_bullets" } { "origin" "337 1236 -103" "spawnflags" "1" "angle" "-104" "classname" "monster_soldier_light" } { "origin" "67 1223 -103" "spawnflags" "1" "angle" "-77" "classname" "monster_soldier_light" } { "origin" "-293 1140 -103" "spawnflags" "1" "angle" "-90" "classname" "monster_soldier_light" } { "origin" "-330 755 -103" "spawnflags" "1" "angle" "-53" "classname" "monster_soldier_light" } { "origin" "-986 99 -7" "angle" "137" "classname" "item_health_small" } { "origin" "-833 -69 -7" "angle" "-57" "classname" "item_health_small" } { "origin" "-841 122 -7" "angle" "90" "classname" "item_health_small" } { "origin" "-905 137 -7" "angle" "137" "classname" "item_health_small" } { "origin" "-736 820 112" "spawnflags" "1" "angle" "-7" "classname" "monster_soldier_light" } { "origin" "-344 832 112" "spawnflags" "1" "angle" "178" "classname" "monster_soldier_light" } { "origin" "-92 705 112" "spawnflags" "1" "angle" "-95" "classname" "monster_soldier_ss" } { "origin" "-718 247 176" "spawnflags" "1" "angle" "111" "classname" "monster_soldier_light" } { "origin" "-318 297 176" "spawnflags" "1" "angle" "85" "classname" "monster_soldier_light" } { "origin" "-486 -74 120" "spawnflags" "1" "angle" "-176" "classname" "monster_soldier_light" } { "origin" "-635 -16 120" "angle" "-171" "classname" "ammo_grenades" } { "origin" "-360 -378 24" "spawnflags" "1" "angle" "178" "classname" "monster_infantry" } { "origin" "-71 -212 -6" "spawnflags" "1" "angle" "-98" "classname" "monster_soldier_light" } { "origin" "357 24 -391" "angle" "-178" "classname" "item_health_large" } { "origin" "338 -455 -391" "angle" "-88" "classname" "item_health_large" } { "origin" "-152 -462 -391" "angle" "-87" "classname" "weapon_chaingun" } { "origin" "-414 60 -327" "angle" "-169" "classname" "item_health_large" } { "origin" "259 -443 17" "spawnflags" "1" "angle" "147" "classname" "monster_flyer" } { "origin" "315 -22 16" "spawnflags" "1" "angle" "-162" "classname" "monster_flyer" } { "origin" "-903 49 -7" "angle" "86" "classname" "ammo_bullets" } { "origin" "-443 837 112" "spawnflags" "1" "angle" "-157" "classname" "monster_soldier_light" } { "origin" "-68 933 112" "spawnflags" "1" "angle" "85" "classname" "monster_soldier_light" } { "origin" "157 388 176" "spawnflags" "1" "angle" "108" "classname" "monster_soldier_light" } { "origin" "-66 384 176" "spawnflags" "1" "angle" "94" "classname" "monster_soldier_light" } { "origin" "-466 293 176" "spawnflags" "1" "angle" "-179" "classname" "monster_soldier_light" } { "origin" "-900 211 176" "spawnflags" "1" "angle" "92" "classname" "monster_soldier_ss" } { "origin" "-1017 -80 176" "spawnflags" "1" "angle" "8" "classname" "monster_soldier_light" } { "origin" "-59 -391 24" "spawnflags" "1" "angle" "-177" "classname" "monster_soldier_light" } { "origin" "-209 -783 -231" "spawnflags" "1" "angle" "1" "classname" "monster_soldier_light" } { "origin" "-551 158 -31" "spawnflags" "1" "angle" "-173" "classname" "monster_soldier_light" } { "origin" "-413 79 -39" "angle" "-97" "classname" "item_health_large" } { "origin" "-495 79 -39" "angle" "-64" "classname" "item_health_large" } { "origin" "-21 -938 -231" "spawnflags" "1" "angle" "-5" "classname" "monster_soldier_light" } { "origin" "-1030 -871 -167" "spawnflags" "1" "angle" "90" "classname" "monster_soldier_light" } { "origin" "-756 -509 -199" "spawnflags" "1" "angle" "1" "classname" "monster_soldier_light" } { "origin" "-577 290 176" "classname" "ammo_bullets" } { "origin" "-415 286 176" "classname" "ammo_bullets" } { "origin" "-287 286 176" "classname" "ammo_bullets" } { "origin" "-919 -88 176" "angle" "178" "classname" "item_health_small" } { "origin" "-903 103 176" "angle" "-176" "classname" "item_health_small" } { "origin" "-911 135 176" "spawnflags" "1" "angle" "-73" "classname" "monster_soldier_light" } { "origin" "-650 -30 120" "angle" "38" "classname" "weapon_rocketlauncher" } { "origin" "-693 98 -31" "angle" "-85" "classname" "item_health_large" } { "origin" "-583 161 -31" "angle" "-24" "classname" "weapon_chaingun" } { "origin" "857 -1494 -167" "angle" "-1" "classname" "item_health_large" } { "origin" "1345 -1474 -167" "angle" "-150" "classname" "ammo_rockets" } { "origin" "1461 -1102 -167" "angle" "164" "classname" "ammo_rockets" } { "origin" "580 -1285 -167" "angle" "3" "classname" "ammo_grenades" } { "origin" "-789 -717 -183" "spawnflags" "1" "angle" "160" "classname" "monster_soldier_light" } { "origin" "-897 -685 -199" "spawnflags" "1" "angle" "126" "classname" "monster_soldier_light" } { "origin" "-695 -765 -183" "spawnflags" "1" "angle" "149" "classname" "monster_soldier_light" } { "origin" "896 -1368 -64" "map" "jug20" "targetname" "te" "classname" "target_changelevel" } { "origin" "-468 1086 -206" "_color" ".7 1 .3" "style" "0" "light" "150" "classname" "light" } { "model" "*30" "targetname" "headdoor" "lip" "16" "wait" "-1" "sounds" "1" "speed" "56" "angle" "-2" "classname" "func_door" } { "model" "*31" "targetname" "headdoor" "lip" "16" "wait" "-1" "sounds" "1" "speed" "112" "angle" "-1" "classname" "func_door" } { "model" "*32" "target" "headkey" "classname" "trigger_multiple" } { "origin" "928 -1320 -64" "wait" "0.2" "delay" "6" "target" "te" "targetname" "theend" "classname" "trigger_relay" } { "origin" "-1319 16 -7" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-907 25 -7" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-741 342 -23" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-848 736 -55" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-798 1027 -39" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-523 1066 -39" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-486 1088 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-277 1058 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "224 869 -65" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-224 667 -42" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-310 279 -38" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-260 1047 -22" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-50 1076 110" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "78 594 154" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-141 470 240" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-260 249 219" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-256 1115 -10" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "206 939 -109" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-245 655 -89" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "584 -141 -103" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "585 -300 -135" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "583 -517 -167" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "542 -751 -167" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "552 -969 -167" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1126 -532 -167" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "941 -228 -167" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "945 -732 -167" "noise" "world/drip_amb.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "822 -1482 -167" "noise" "world/klaxon1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1101 -1522 -167" "noise" "world/klaxon1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1410 -1441 -167" "noise" "world/klaxon1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1429 -1087 -167" "noise" "world/klaxon1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "949 -1109 -247" "noise" "world/klaxon1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1106 -1415 -247" "noise" "world/klaxon1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1229 -1224 -247" "noise" "world/klaxon1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1371 -1360 -247" "noise" "world/klaxon1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "868 -1266 -247" "noise" "world/klaxon1.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1283 11 -7" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1283 11 -7" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1283 11 -7" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-900 159 -7" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-868 276 -23" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-832 341 -23" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-688 368 -7" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-606 372 -7" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-618 481 -7" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-685 525 -7" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-702 607 -10" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-803 978 -39" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-752 1063 -39" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-619 1034 -39" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-577 1066 -39" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-515 1141 -39" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-491 1155 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-447 1063 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-416 1046 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-203 1057 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-67 1058 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "84 1011 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "131 980 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "280 903 -218" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "257 756 -140" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "205 603 -103" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "91 619 -103" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "540 181 -103" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "649 152 -103" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "684 31 -103" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "650 -80 -103" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "584 -168 -105" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "590 -299 -132" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "590 -455 -166" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "570 -600 -167" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "553 -746 -167" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "553 -899 -167" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "568 -1020 -167" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "743 -497 -167" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "856 -476 -193" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "983 -501 -199" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "1098 -553 -174" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "30 629 -103" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "244 635 -103" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "375 752 -103" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "343 965 -103" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "222 1129 -103" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-7 1141 -103" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-203 1123 -103" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-280 926 -103" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-247 706 -103" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-106 614 -103" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "70 773 -231" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "227 713 -231" "noise" "world/amb12.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-102 653 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-181 656 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-255 728 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-242 847 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-231 935 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-555 857 112" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-617 848 112" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-703 784 112" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-726 663 150" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-760 539 184" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-763 411 184" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-711 294 176" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-569 282 176" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-498 276 176" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-685 169 170" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-724 22 168" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-769 -68 168" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-896 -69 176" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1006 -35 176" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1001 82 176" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-636 -110 154" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-515 -16 120" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-426 -104 120" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-422 -235 89" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-321 -426 24" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-135 -393 24" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-83 -241 3" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-121 -70 -39" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-121 -70 -39" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-121 -70 -39" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "196 -45 -100" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "197 -44 -100" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "197 -44 -100" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "269 -343 -167" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "269 -343 -167" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "269 -343 -167" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-57 -396 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-57 -396 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-57 -396 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-16 -778 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-16 -778 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-16 -778 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-16 -778 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-16 -778 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-16 -778 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-134 -782 -231" "noise" "world/amb18.wav" "spawnflags" "1" "classname" "target_speaker" } { "origin" "-1334 4 36" "target" "helpmsg" "delay" "0.3" // b#x: added this "classname" "trigger_always" } { "origin" "-1326 -12 24" "message" "Ship...leave..\nstop... st... sh..." "targetname" "helpmsg" "spawnflags" "1" "classname" "target_help" } { "origin" "-1350 26 24" "message" "..." "targetname" "helpmsg" "classname" "target_help" } { "origin" "-1323 -68 -7" "angle" "22" "classname" "info_player_coop" } { "origin" "-1334 80 -7" "angle" "-6" "classname" "info_player_coop" } { "origin" "-1232 96 -7" "angle" "-28" "classname" "info_player_coop" } { "origin" "-1204 -73 -7" "angle" "37" "classname" "info_player_coop" } { "origin" "-1129 -63 -7" "angle" "47" "classname" "info_player_coop" } { "origin" "-1123 59 -7" "angle" "-29" "classname" "info_player_coop" } { // b#2: added this "classname" "path_corner" "targetname" "teledest" "origin" "-1320 16 0" } { // b#2: added this "classname" "misc_teleporter" "origin" "315 -635 -223" "target" "teledest" } { // b#4: added this "classname" "item_power_shield" "origin" "-845 -965 8" }yquake2-QUAKE2_8_40/stuff/misc/000077500000000000000000000000001465112212000161735ustar00rootroot00000000000000yquake2-QUAKE2_8_40/stuff/misc/Dockerfile000066400000000000000000000011671465112212000201720ustar00rootroot00000000000000FROM ubuntu:18.04 AS build WORKDIR /yquake2 RUN apt-get update RUN apt-get install libcurl4-openssl-dev build-essential -y COPY . . RUN make server game FROM ubuntu:18.04 AS runtime ENV server_cfg="server.cfg" EXPOSE 27910 # Add the user UID:1000, GID:1000, home at /yquake2 RUN groupadd -r yquake2 -g 1000 && useradd -u 1000 -r -g yquake2 -m -d /yquake2 -s /sbin/nologin -c "yquake2 user" yquake2 && \ chmod 755 /yquake2 WORKDIR /yquake2 COPY --from=build /yquake2/release ./ # Specify the user to execute all commands below USER yquake2 RUN mkdir .yq2 VOLUME ["/yquake2/.yq2"] ENTRYPOINT ./q2ded +exec ${server_cfg} yquake2-QUAKE2_8_40/stuff/misc/uncrustify.cfg000066400000000000000000001130621465112212000210720ustar00rootroot00000000000000# # 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_8_40/stuff/models/000077500000000000000000000000001465112212000165235ustar00rootroot00000000000000yquake2-QUAKE2_8_40/stuff/models/crosshair/000077500000000000000000000000001465112212000205205ustar00rootroot00000000000000yquake2-QUAKE2_8_40/stuff/models/crosshair/skin.pcx000066400000000000000000000102671465112212000222060ustar00rootroot00000000000000 ,,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_8_40/stuff/models/crosshair/tris.md2000066400000000000000000000022001465112212000220770ustar00rootroot00000000000000IDP24$ 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_8_40/stuff/quake2-start.sh000077500000000000000000000013611465112212000201230ustar00rootroot00000000000000#!/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_8_40/stuff/yq2.cfg000066400000000000000000000025621465112212000164410ustar00rootroot00000000000000// // 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