Index: README =================================================================== --- README (revision 1270) +++ README (working copy) @@ -88,6 +88,7 @@ USE_CURL - use libcurl for http/ftp download support USE_CURL_DLOPEN - link with libcurl at runtime USE_CODEC_VORBIS - enable Ogg Vorbis support + USE_CODEC_MP3 - enable MP3 support. Requires libmad to be installed. USE_LOCAL_HEADERS - use headers local to ioq3 instead of system ones COPYDIR - the target installation directory Index: README-EF.txt =================================================================== --- README-EF.txt (revision 0) +++ README-EF.txt (revision 0) @@ -0,0 +1,386 @@ +README for StarTrek Voyager - Elite Force engine release +Project page: http://thilo.kickchat.com/efport-progress/ + - last updated 13.11.2006 + +This project is a modification to the icculus.org maintained quake3 engine +to make it possible to run EliteForce holomatch. This means: you can *not* +play single player missions with this project. +This engine has full compatibility for the newer _and_ the original +EliteForce protocol, clients using the original engine can use +newer servers, and clients using my engine can use old servers, too. + +Now, the next few paragraphs are just some random blabla about the +advantages of this release, but if you want to you can just skip it and go +right to the installation instructions. + +What was the motivation for me doing this project? +It actually started pretty small.. The Quake3 source has been released more +than half a year ago and I got the crazy idea to try the quake3 engine with +EliteForce on my trusted Gentoo Linux. +When I started the program first the screen went black and nothing +happened... nearly nothing. I heard some sounds and when I moved the cursor +I heard random click-clicks from the mouse going over some menu buttons. +This meant this stuff is not completely incompatible after all, it didn't +crash right away though that was exactly what I expected it to do. +I digged in Raven's multiplayer source that they released (only the virtual +machine stuff, to allow for building modifications) and compared it to +quake3's virtual machines and realized that there was a small +incompatibility in one of the data structures used to pass information +between engine and virtual machine. It was nothing big and after fixing this +the menu worked! +There were some quirks here or there, but most of the menu really worked. +So it got me thinking: If I fix all these incompatibilities.. maybe I can +finally play my beloved multiplayer game I've been clinging to for over 5 +years on my favorite OS natively.. and IPv6 support for EliteForce would be +pretty cool too... +So a crazy idea was turned into a serious project. I had to add support for +Raven's models, playing mp3s, EliteForce's own network protocol. My goal was +to make this release as close to the original as possible. Most of the time, +I only could guess what names and functions would mean to the engine or I +had to compare the original game and then try to make it work the same way +in my engine. + +The quake3 game relies heavily on floating point operations. Unfortunately, +the original EliteForce engine does non-ISO compliant rounding of floating +point numbers to integers in the VMs. +The game VM uses that flaw which will result in higher jumping for certain +com_maxfps settings. As with the new engine there are many platforms that +can be supported, there are probably many different ways one has to take to +revert the rounding to the old behaviour. This is nearly impossible to do, +so I had to use a different approach: build new VMs. + +############ +### NEW! ### +############ + +Version 1.37: +- Sound attenuation has been changed to nearly the original behaviour. + +- Ogg vorbis sound format is now supported natively by the engine. + +- The Ioquake3 project has included a download redirect option! Server + admins can now point with an URL where to download files from and the + client can download them. See the README-IO.txt file for more info on how + to use this. + +- Antiwarp feature: Makes movement of people with wrong packet settings + smooth. Enable it by setting cvar sv_antiWarp to 1. + It comes in two flavours: + sv_antiWarpBias 0 will hardly affect response to + user commands (shoot, move, etc.), but will lead to many prediction + errors. + sv_antiWarpBias 1 won't have the problem with prediction errors but will + add *a lot* of latency on misconfigured clients and slightly noticeable + latency even on correctly configured client. + A constant framerate and good connection is a must. This ensures that all + players appear smoothly and those that do not - if their command rate is + too unstable to compensate for - gain no advantage. + A correctly configured client is one that uses the same value or a divider + of com_maxfps for cl_maxpackets. + +Version 1.36: + +- There now is the possibility to revert the fastsky to EF's original color. + set cvar r_origfastsky and the color of fastsky will be the normal beige + you know. + +- Ignore system. This is actually not specific to my engine but included in + the VM updates in pak91.pk3. You can use it to ignore chat messages from + abusive players. The ingame menu has been extended so you can conveniently + add/remove players to/from your ignore list. + Three new commands have been introduced: ignore, unignore and unignoreall, + their names pretty much speak out for themselves. + If the ignore or unignore command is called without playername, the person + the crosshair is pointing at is affected. If you run it like: + "/ignore evil 1", all players with the string "evil" in their name are + affected. Colors in names will be stripped and don't affect the result at + all. + Of course, the ignore system is only available if the server has been + updated with the pak91.pk3. + +- On Windows, the settings and user specific data are saved in the "Document + and Settings" folder. The fs_homepath cvar indicates the exact position. + On most english systems, the folder is called: + C:\Documents and Settings\\Application Data\STVEF + See README-IO.txt for more information. + +############################ +### GENERAL INSTRUCTIONS ### +############################ + +All players and server admins, no matter whether users of the old or new +EliteForce engine should add the pak92.pk3 to their baseEF directories. +If you have old pak files from older versions of this engine, delete them. +The files to be deleted - if present - are: +pak9.pk3, pak90.pk3 and pak91.pk3 +If this README is from an archive (zip or bzip2) or an automated installer +the pak92.pk3 will already be included.. You can also find the file here: +http://thilo.kickchat.com/efport-progress/bin/vm/ +Dedicated servers using my engine will yield a greatly impaired movement if +this pak file is not installed. +This pak only fixes a few obvious bugs but otherwise doesn't change gameplay +at all. +Modders please read information about this further below. + +######################################### +### WINDOWS INSTALLATION INSTRUCTIONS ### +######################################### + +Installing this on windows is pretty straightforward. +If you don't have it already on your harddrive, you can install EliteForce +using Raven's original installer. You must also install either the expansion +pack or the official patch to upgrade your installation to version 1.2. + +Once you have done all this, run the windows installer. The installer +will ask you for an installation path. This path should be where your +stvoyHM.exe file resides. The default is to the Elite Force default +installation folder. The installer will also double check to be sure +you've chosen the correct location. Once the installer completes you +will find a new group in your start menu called "Elite Force Holomatch 1.37" +With shortcuts to run the game using the new patch and shortcuts to this +read me file. + +After installing, a new file named iostvoyHM-1.37.exe has been created. If +you want to use my improved engine you must start this one instead of the +original stvoyHM.exe. + +This release comes with OpenAL support for sound which improves sound for +most users. An OpenAL installation tool comes with this installer and can +optionally be run from it. If you have got the latest version installed +already, of course you do not need to reinstall OpenAL. +Keep in mind, that OpenAL support is still somewhat experimental. For alot +of people OpenAL gives a dramatical improvement in sound. +Still, depending on the hardware and drivers in use, sound may be unusual or +even worse with OpenAL. If you don't like it, you can always turn off OpenAL +support by disabling it in the sound settings. +Since version 1.35 some changes have happened in the way OpenAL gets used. +Default settings will now make OpenAL work well on most hardware setups. If +you had sound problems with previous versions, this release will likely fix +them for you. Device enumeration support has been implemented, so you now +have the choice between different devices. +For most people, two devices are available: "Generic Hardware" and +"Generic Software". Previous versions had "Generic Hardware" as default +which uses DirectSound 3D and was often causing troubles with OpenAL. If you +don't belong to these users, you can switch back by selecting the device in +the sound configuration menu. + +######################################## +### MACOSX INSTALLATION INSTRUCTIONS ### +######################################## + +Install the normal game from CD if you have not already done so. Make also +sure to have the expansion pack, or at least the latest patch version 1.2 +that is available for EliteForce, installed. +Now extract the .dmg file to the installation directory of your EliteForce +game. Run iostvoyHM-1.37 to start the game. + +There have been problems with the MacOSX version of OpenAL and ioquake3. +This is why OpenAL is disabled per default in this release. +You can still enable OpenAL sound in the sound configuration menu, though. +You will have to click the sound quality setting in the sound configuration +menu to change the backend used. Maybe it works fine for you so you won't +have to play with the standard sound. + +####################################### +### LINUX INSTALLATION INSTRUCTIONS ### +####################################### + +Prerequisites: + * A working hardware accelerated OpenGL setup + (DRI, fireglx, nvidia-glx...) + * libSDL - Simple Direct Layer libraries + * libmad - MPEG Audio Decoder (also known as MAD sometimes). + * libvorbis, libogg, libvorbisfile - OGG Vorbis audio decoder +Optional: + * OpenAL - OpenAL sound libraries + * libcurl - FTP/HTTP download support. + +You can fullfill these dependencies by installing the correct packages from +your favorite distribution's packet manager. + +There's a loki installer available that installs all the relevant files for +you in an automated manner. The installer covers both, binaries for 64 bit +and 32 bit systems so there's no split-up into two archives anymore. +The installer offers you to copy the pak*.pk3 files from the original +EliteForce CDs. If you don't want this to happen for some reason, see the +.pk3 file copying instructions in the manual installation instructions a few +lines down. + +This is the location of the installer: +http://thilo.kickchat.com/efport-progress/bin/linux/io_eliteforce-1.37.run +Run it in a shell like: +sh iostvoyHM-1.37.run + +If you have an old hmconfig.cfg from windows, don't add it to baseef but to +your home directory in ~/.stvef/baseEF/ +Speaking of that dir, the hidden directory where user specific config +files/maps/mods will be stored is - as you may have guessed by now - +.stvef in your home directory. + +Should the installer fail for some reason you can still install manually: + +Create a new eliteforce directory, you will probably want to use a dir like: +/usr/local/games/stvef + +Copy a build of this engine that you can get from +http://thilo.kickchat.com/efport-progress/bin/ +into the new directory. +Also copy any directories from MODs you installed previously on windows to +this directory. + +Create a subdirectory named "baseEF" (case sensitive!!) and copy the +pak0.pk3, pak1.pk3 and pak2.pk3 from the original eliteforce +release in there and don't forget the new pak91.pk3! +pak1.pk3 and pak2.pk3 are files that have been released after the game went +gold. You must either get them by installing the latest official EF patch on +windows or you can get them from the expansion pack cd. +pak3.pk3 can only be found on the expansion pack CD. If you want to have the +maps and additions from the expension pack, you must copy this file as well. + +######################## +### For MOD creators ### +######################## + +I spoke about modifications necessary to get the old movement. +Unfortunately, you will have to do these modifications to your mods too if +you want your mod to run on my dedicated server version and don't want the +users to get too frustrated over slowness. + +The problem is in the SnapVector macro located in q_shared.h: +It uses casting a float to int using the (int) cast. The ISO standard +requires the decimal places to be cut off and the number truncated. Thus, +3,4 is converted to 3 or 5,9 is converted to 5. +This is what happens in my engine. +In EliteForce though, a 5,9 will be converted to 6 which is not standard +compliant and will ultimately result in faster movement. This - among other +factors - is the cause for framerate dependent jumping out of tranches, etc. +What one could see as flaw many players now have learnt to rely upon. + +I have started a codebase where all modders can start from that fixes some +known bugs and the movement so that everything works like in the original +EF. Even if you have already changed much code, there is a way you can +quickly insert the changes into your MOD, too. I created sourcecode patches +that take care of the bugs. + +At this time, there are already a couple of bugfixes that have been added to +the codebase. For instance, bots can now join password protected servers, +the demo selection menu now has scrollbars (thanks to TiM), the EF LCARS +patch from Lt. Cmdr. Salinga has been applied. A more complete +list can be found here: +http://svn.kickchat.com/index.cgi/stvoy/trunk/ChangeLog?view=log + +The patch can be found here: +http://thilo.kickchat.com/efport-progress/patches/stvoyVM-bugfixes-1.37.diff +You can also check out my source code on +http://svn.kickchat.com/index.cgi/stvoy/trunk/ + +The patch can be applied using the "patch" tool under Linux. For Windows, +I recommend that you install TortoiseSVN which is an excellent tool for +accessing subversion projects. It has also got the ability to create and +merge patches: +http://tortoisesvn.tigris.org/ + +Quick instructions for Windows users to integrate my improvements into your +mod: + +- Check out the repository at revision 1: + svn co svn://kickchat.com/stvoy/trunk stvoy + You must make sure that you really check out the very first revision! +- Overwrite all source files in that newly checked out directory with the + source files from your mod. Make sure the ".svn" directories are left + intact. +- Create a patch using the TortoiseSVN context menu in Windows Explorer. The + patch should then contain all modifications you did to Raven's original + VMs. +- Check out the same repository into a new directory, but this time the + latest revision (HEAD). +- Apply the patch you created to the new dir using TortoiseSVN. + If you're lucky and didn't change too much, everything will work fine. If + you're unlucky, there will be conflicts that you have to resolve manually. + For that you need to go into the conflicted files with an editor and + search for the string "mine". It will first show you your version, then + the one from the repository. Try to integrate these changes into your MOD. +- Build +- Be sure to add the language .dat files (you find them in baseef/ext_data + in the subversion repository) in your pk3. + +That's pretty much all you have to do. If you see that there are more +bugfixes included in my repository at a later time, you can use TortoiseSVN +to upgrade to this version. All changes are automatically merged into your +mod. + +######################################################################## +## For Engine fiddlers and Gentoo-I-compile-everything-myself-freaks ### +######################################################################## + +Since icculus.org's quake3 engine is GPL'ed, all my modifications are freely +available in the form of patches. + +a) You must get a recent revision of ioquake3 + - see http://www.icculus.org/quake3 + +b) Download the most recent patch that modifies the code + - see http://thilo.kickchat.com/efport-progress/patches/ + +c) Apply the patch + - Linux users do: patch -p0 < thepatchname.diff in the main sourcedir + - Windows users must install seperate tools like TortoiseSVN that have + the ability to apply unix-style patches. + +Linux users: +d) Make sure you got all packages needed, especially the -dev packages. + You need the header files for libmad, libSDL, libopenal and libGL + +e) You may need to edit the Makefile to make use of a gcc version < 4. + gcc 4.0.x is not advisable if you need stability. + +f) Type in "make debug" for a version with debugging symbols or "make" for a + release version. If all goes well, the new binaries will be found in the + build/ subdir. + +Windows users (will take a bit longer, experienced coders only): +d) Open up the project (project files can be found in code\win32\msvc\) +e) You need to compile the libmad library from: + http://sourceforge.net/projects/mad + so that you have a .lib file you can link against +f) If you compile and it complains about a missing dinput.h you need the + DirectX SDK +g) If you want to have OpenAL you need the OpenAL SDK, too. +h) Back to your MSVC environment: + look at the renderer and botlib subproject and modify the preprocessor + definitions in a way so that ELITEFORCE is defined (#define ELITEFORCE), + remove _DEBUG in the release version and add NDEBUG (only in release) +i) Same goes to the main quake3 subproject, also add the define: + USE_CODEC_MP3=1 and USE_OPENAL=1;USE_OPENAL_DLOPEN=1 (the last two ones + only when you want OpenAL support). +j) Add all include and library directories to the compiler/linker like + OpenAL include/library dir, MAD include/library dir, etc... +k) You may need to specify additional libraries in the linker dependencies + like user32.lib Advapi32.lib ole32.lib etc... +l) Build. + +Good luck! + +############### +### Credits ### +############### + +My thanks go out to alot of people who were helpful in creating my mod: + + - A special thanks goes to TiM from the RPG-X project who helped with alot + of information + + - The guys on the RPG-X board (Sharky, Scooter...) + + - CoreFusion for a bit of testing in the beginning :) + + - Skinner&Neural Link for infos about the MDR model format + + - Thomas Sahling aka Lt. Cmdr. Salinga for the source code to his LCARS + patch + + - Brian Shaffer aka Shafe for the windows installer + + - Derek O. for a PPC Linux and MacOSX shell + + -- Thilo Schulz (arny@ats.s.bawue.de) Index: Makefile.local =================================================================== --- Makefile.local (revision 0) +++ Makefile.local (revision 0) @@ -0,0 +1,11 @@ +BUILD_GAME_SO=0 +BUILD_GAME_QVM=0 +BUILD_CLIENT_SMP=0 +#BUILD_CLIENT=0 +USE_CODEC_VORBIS=1 +USE_CODEC_MP3=1 +USE_OPENAL=1 +USE_OPENAL_DLOPEN=1 +USE_CURL=1 +USE_CURL_DLOPEN=1 +BUILD_ELITEFORCE=1 Index: code/server/sv_world.c =================================================================== --- code/server/sv_world.c (revision 1270) +++ code/server/sv_world.c (working copy) @@ -222,28 +222,88 @@ // encode the size into the entityState_t for client prediction if ( gEnt->r.bmodel ) { gEnt->s.solid = SOLID_BMODEL; // a solid_box will never create this value +#ifdef ELITEFORCE + } else if ( gEnt->r.contents & ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_SHOTCLIP ) ) { + if(gEnt->r.svFlags & SVF_SHIELD_BBOX) + { + if(gEnt->s.time2 & (1 << 24)) + { + i = gEnt->r.maxs[0]; + if (i<1) + i = 1; + if (i>255) + i = 255; + + // z is not symetric + j = -gEnt->r.mins[0]; + if (j<1) + j = 1; + if (j>255) + j = 255; + + // and z maxs can be negative... + k = gEnt->r.maxs[2]; + if (k<1) + k = 1; + if (k>255) + k = 255; + + gEnt->s.eFlags |= EF_SHIELD_BOX_X; + } + else + { + i = gEnt->r.maxs[1]; + if (i<1) + i = 1; + if (i>255) + i = 255; + + // z is not symetric + j = -gEnt->r.mins[1]; + if (j<1) + j = 1; + if (j>255) + j = 255; + + // and z maxs can be negative... + k = gEnt->r.maxs[2]; + if (k<1) + k = 1; + if (k>255) + k = 255; + + gEnt->s.eFlags |= EF_SHIELD_BOX_Y; + } + } + else + { +#else } else if ( gEnt->r.contents & ( CONTENTS_SOLID | CONTENTS_BODY ) ) { - // assume that x/y are equal and symetric - i = gEnt->r.maxs[0]; - if (i<1) - i = 1; - if (i>255) - i = 255; +#endif + // assume that x/y are equal and symetric + i = gEnt->r.maxs[0]; + if (i<1) + i = 1; + if (i>255) + i = 255; - // z is not symetric - j = (-gEnt->r.mins[2]); - if (j<1) - j = 1; - if (j>255) - j = 255; + // z is not symetric + j = (-gEnt->r.mins[2]); + if (j<1) + j = 1; + if (j>255) + j = 255; - // and z maxs can be negative... - k = (gEnt->r.maxs[2]+32); - if (k<1) - k = 1; - if (k>255) - k = 255; - + // and z maxs can be negative... + k = (gEnt->r.maxs[2]+32); + if (k<1) + k = 1; + if (k>255) + k = 255; +#ifdef ELITEFORCE + } +#endif + gEnt->s.solid = (k<<16) | (j<<8) | i; } else { gEnt->s.solid = 0; Index: code/server/sv_game.c =================================================================== --- code/server/sv_game.c (revision 1270) +++ code/server/sv_game.c (working copy) @@ -351,9 +351,10 @@ return 0; case G_FS_GETFILELIST: return FS_GetFileList( VMA(1), VMA(2), VMA(3), args[4] ); +#ifndef ELITEFORCE case G_FS_SEEK: return FS_Seek( args[1], args[2], args[3] ); - +#endif case G_LOCATE_GAME_DATA: SV_LocateGameData( VMA(1), args[2], args[3], VMA(4), args[5] ); return 0; @@ -373,14 +374,18 @@ return SV_AreaEntities( VMA(1), VMA(2), VMA(3), args[4] ); case G_ENTITY_CONTACT: return SV_EntityContact( VMA(1), VMA(2), VMA(3), /*int capsule*/ qfalse ); +#ifndef ELITEFORCE case G_ENTITY_CONTACTCAPSULE: return SV_EntityContact( VMA(1), VMA(2), VMA(3), /*int capsule*/ qtrue ); +#endif case G_TRACE: SV_Trace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], /*int capsule*/ qfalse ); return 0; +#ifndef ELITEFORCE case G_TRACECAPSULE: SV_Trace( VMA(1), VMA(2), VMA(3), VMA(4), VMA(5), args[6], args[7], /*int capsule*/ qtrue ); return 0; +#endif case G_POINT_CONTENTS: return SV_PointContents( VMA(1), args[2] ); case G_SET_BRUSH_MODEL: @@ -439,12 +444,13 @@ case G_DEBUG_POLYGON_DELETE: BotImport_DebugPolygonDelete( args[1] ); return 0; +#ifndef ELITEFORCE case G_REAL_TIME: return Com_RealTime( VMA(1) ); case G_SNAPVECTOR: Sys_SnapVector( VMA(1) ); return 0; - +#endif //==================================== case BOTLIB_SETUP: @@ -456,6 +462,10 @@ case BOTLIB_LIBVAR_GET: return botlib_export->BotLibVarGet( VMA(1), VMA(2), args[3] ); +#ifdef ELITEFORCE + case BOTLIB_DEFINE: + return botlib_export->PC_AddGlobalDefine( VMA(1) ); +#else case BOTLIB_PC_ADD_GLOBAL_DEFINE: return botlib_export->PC_AddGlobalDefine( VMA(1) ); case BOTLIB_PC_LOAD_SOURCE: @@ -466,7 +476,7 @@ return botlib_export->PC_ReadTokenHandle( args[1], VMA(2) ); case BOTLIB_PC_SOURCE_FILE_AND_LINE: return botlib_export->PC_SourceFileAndLine( args[1], VMA(2), VMA(3) ); - +#endif case BOTLIB_START_FRAME: return botlib_export->BotLibStartFrame( VMF(1) ); case BOTLIB_LOAD_MAP: @@ -483,13 +493,14 @@ case BOTLIB_USER_COMMAND: SV_ClientThink( &svs.clients[args[1]], VMA(2) ); return 0; - +#ifndef ELITEFORCE case BOTLIB_AAS_BBOX_AREAS: return botlib_export->aas.AAS_BBoxAreas( VMA(1), VMA(2), VMA(3), args[4] ); case BOTLIB_AAS_AREA_INFO: return botlib_export->aas.AAS_AreaInfo( args[1], VMA(2) ); case BOTLIB_AAS_ALTERNATIVE_ROUTE_GOAL: return botlib_export->aas.AAS_AlternativeRouteGoals( VMA(1), args[2], VMA(3), args[4], args[5], VMA(6), args[7], args[8] ); +#endif case BOTLIB_AAS_ENTITY_INFO: botlib_export->aas.AAS_EntityInfo( args[1], VMA(2) ); return 0; @@ -504,8 +515,10 @@ case BOTLIB_AAS_POINT_AREA_NUM: return botlib_export->aas.AAS_PointAreaNum( VMA(1) ); +#ifndef ELITEFORCE case BOTLIB_AAS_POINT_REACHABILITY_AREA_INDEX: return botlib_export->aas.AAS_PointReachabilityAreaIndex( VMA(1) ); +#endif case BOTLIB_AAS_TRACE_AREAS: return botlib_export->aas.AAS_TraceAreas( VMA(1), VMA(2), VMA(3), VMA(4), args[5] ); @@ -527,11 +540,12 @@ case BOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA: return botlib_export->aas.AAS_AreaTravelTimeToGoalArea( args[1], VMA(2), args[3], args[4] ); +#ifndef ELITEFORCE case BOTLIB_AAS_ENABLE_ROUTING_AREA: return botlib_export->aas.AAS_EnableRoutingArea( args[1], args[2] ); case BOTLIB_AAS_PREDICT_ROUTE: return botlib_export->aas.AAS_PredictRoute( VMA(1), args[2], VMA(3), args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11] ); - +#endif case BOTLIB_AAS_SWIMMING: return botlib_export->aas.AAS_Swimming( VMA(1) ); case BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT: @@ -548,9 +562,11 @@ botlib_export->ea.EA_Command( args[1], VMA(2) ); return 0; +#ifndef ELITEFORCE case BOTLIB_EA_ACTION: botlib_export->ea.EA_Action( args[1], args[2] ); break; +#endif case BOTLIB_EA_GESTURE: botlib_export->ea.EA_Gesture( args[1] ); return 0; @@ -680,7 +696,11 @@ botlib_export->ai.BotSetChatGender( args[1], args[2] ); return 0; case BOTLIB_AI_SET_CHAT_NAME: +#ifdef ELITEFORCE + botlib_export->ai.BotSetChatName( args[1], VMA(2)); +#else botlib_export->ai.BotSetChatName( args[1], VMA(2), args[3] ); +#endif return 0; case BOTLIB_AI_RESET_GOAL_STATE: @@ -730,9 +750,11 @@ return botlib_export->ai.BotGetMapLocationGoal( VMA(1), VMA(2) ); case BOTLIB_AI_AVOID_GOAL_TIME: return FloatAsInt( botlib_export->ai.BotAvoidGoalTime( args[1], args[2] ) ); +#ifndef ELITEFORCE case BOTLIB_AI_SET_AVOID_GOAL_TIME: botlib_export->ai.BotSetAvoidGoalTime( args[1], args[2], VMF(3)); return 0; +#endif case BOTLIB_AI_INIT_LEVEL_ITEMS: botlib_export->ai.BotInitLevelItems(); return 0; @@ -762,9 +784,11 @@ case BOTLIB_AI_RESET_MOVE_STATE: botlib_export->ai.BotResetMoveState( args[1] ); return 0; +#ifndef ELITEFORCE case BOTLIB_AI_ADD_AVOID_SPOT: botlib_export->ai.BotAddAvoidSpot( args[1], VMA(2), VMF(3), args[4] ); return 0; +#endif case BOTLIB_AI_MOVE_TO_GOAL: botlib_export->ai.BotMoveToGoal( VMA(1), args[2], VMA(3), args[4] ); return 0; @@ -852,7 +876,23 @@ case TRAP_CEIL: return FloatAsInt( ceil( VMF(1) ) ); - +#ifdef ELITEFORCE + case BOTLIB_EA_USE_ITEM: + botlib_export->ea.EA_UseItem(args[1], VMA(2)); + return 0; + case BOTLIB_EA_DROP_ITEM: + botlib_export->ea.EA_DropItem(args[1], VMA(2)); + return 0; + case BOTLIB_EA_USE_INV: + botlib_export->ea.EA_UseInv(args[1], VMA(2)); + return 0; + case BOTLIB_EA_DROP_INV: + botlib_export->ea.EA_DropInv(args[1], VMA(2)); + return 0; + case BOTLIB_EA_ALT_ATTACK: + botlib_export->ea.EA_Attack( args[1] ); + return 0; +#endif default: Com_Error( ERR_DROP, "Bad game system trap: %ld", (long int) args[0] ); } Index: code/server/sv_client.c =================================================================== --- code/server/sv_client.c (revision 1270) +++ code/server/sv_client.c (working copy) @@ -88,6 +88,17 @@ return; } +#ifdef ELITEFORCE + NET_OutOfBandPrint( NS_SERVER, challenge->adr, + "challengeResponse %i", challenge->challenge ); +#else + // if they are on a lan address, send the challengeResponse immediately + if ( Sys_IsLANAddress( from ) ) { + challenge->pingTime = svs.time; + NET_OutOfBandPrint( NS_SERVER, from, "challengeResponse %i", challenge->challenge ); + return; + } + // look up the authorize server's IP if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) { Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); @@ -133,6 +144,7 @@ "getIpAuthorize %i %i.%i.%i.%i %s 0 %s", svs.challenges[i].challenge, from.ip[0], from.ip[1], from.ip[2], from.ip[3], game, sv_strictAuth->string ); } +#endif } /* @@ -239,7 +251,12 @@ Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) ); version = atoi( Info_ValueForKey( userinfo, "protocol" ) ); - if ( version != PROTOCOL_VERSION ) { +#ifdef ELITEFORCE + if(version != EFPROTOCOL_VERSION && version != PROTOCOL_VERSION) +#else + if ( version != PROTOCOL_VERSION ) +#endif + { NET_OutOfBandPrint( NS_SERVER, from, "print\nServer uses protocol version %i.\n", PROTOCOL_VERSION ); Com_DPrintf (" rejected connect from version %i\n", version); return; @@ -408,6 +425,7 @@ ent = SV_GentityNum( clientNum ); newcl->gentity = ent; + // save the challenge newcl->challenge = challenge; @@ -416,6 +434,15 @@ // init the netchan queue newcl->netchan_end_queue = &newcl->netchan_start_queue; +#ifdef ELITEFORCE + if(version == EFPROTOCOL_VERSION) + { + newcl->compat = qtrue; + newcl->netchan.compat = qtrue; + } +#endif + + // save the userinfo Q_strncpyz( newcl->userinfo, userinfo, sizeof(newcl->userinfo) ); @@ -507,7 +534,12 @@ VM_Call( gvm, GAME_CLIENT_DISCONNECT, drop - svs.clients ); // add the disconnect command - SV_SendServerCommand( drop, "disconnect \"%s\"", reason); +#ifdef ELITEFORCE + if(drop->compat) + SV_SendServerCommand( drop, "disconnect %s", reason); + else +#endif + SV_SendServerCommand( drop, "disconnect \"%s\"", reason); if ( drop->netchan.remoteAddress.type == NA_BOT ) { SV_BotFreeClient( drop - svs.clients ); @@ -561,11 +593,23 @@ // gamestate message was not just sent, forcing a retransmit client->gamestateMessageNum = client->netchan.outgoingSequence; - MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) ); +#ifdef ELITEFORCE + if(client->compat) + MSG_InitOOB(&msg, msgBuffer, sizeof( msgBuffer ) ); + else +#endif + MSG_Init( &msg, msgBuffer, sizeof( msgBuffer ) ); +#ifdef ELITEFORCE + msg.compat = client->compat; +#endif + // NOTE, MRE: all server->client messages now acknowledge // let the client know which reliable clientCommands we have received - MSG_WriteLong( &msg, client->lastClientCommand ); +#ifdef ELITEFORCE + if(!msg.compat) +#endif + MSG_WriteLong( &msg, client->lastClientCommand ); // send any server commands waiting to be sent first. // we have to do this cause we send the client->reliableSequence @@ -597,9 +641,17 @@ MSG_WriteDeltaEntity( &msg, &nullstate, base, qtrue ); } - MSG_WriteByte( &msg, svc_EOF ); +#ifdef ELITEFORCE + if(msg.compat) + MSG_WriteByte(&msg, 0); + else +#endif + MSG_WriteByte( &msg, svc_EOF ); - MSG_WriteLong( &msg, client - svs.clients); +#ifdef ELITEFORCE + if(!msg.compat) +#endif + MSG_WriteLong( &msg, client - svs.clients); // write the checksum feed MSG_WriteLong( &msg, sv.checksumFeed); @@ -761,7 +813,10 @@ int curindex; int rate; int blockspersnap; - int idPack = 0, missionPack = 0, unreferenced = 1; + int idPack = 0, unreferenced = 1; +#ifndef ELITEFORCE + int missionPack = 0; +#endif char errorMessage[1024]; char pakbuf[MAX_QPATH], *pakptr; int numRefPaks; @@ -796,8 +851,12 @@ // now that we know the file is referenced, // check whether it's legal to download it. + #ifdef ELITEFORCE + idPack = FS_idPak(pakbuf, BASEGAME); + #else missionPack = FS_idPak(pakbuf, "missionpack"); idPack = missionPack || FS_idPak(pakbuf, BASEGAME); + #endif break; } @@ -817,6 +876,10 @@ Com_sprintf(errorMessage, sizeof(errorMessage), "File \"%s\" is not referenced and cannot be downloaded.", cl->downloadName); } else if (idPack) { +#ifdef ELITEFORCE + Com_Printf("clientDownload: %d : \"%s\" cannot download Raven pk3 files\n", (int) (cl - svs.clients), cl->downloadName); + Com_sprintf(errorMessage, sizeof(errorMessage), "Cannot autodownload Raven pk3 file \"%s\"", cl->downloadName); +#else Com_Printf("clientDownload: %d : \"%s\" cannot download id pk3 files\n", (int) (cl - svs.clients), cl->downloadName); if (missionPack) { Com_sprintf(errorMessage, sizeof(errorMessage), "Cannot autodownload Team Arena file \"%s\"\n" @@ -825,6 +888,7 @@ else { Com_sprintf(errorMessage, sizeof(errorMessage), "Cannot autodownload id pk3 file \"%s\"", cl->downloadName); } +#endif } else if ( !(sv_allowDownload->integer & DLF_ENABLE) || (sv_allowDownload->integer & DLF_NO_UDP) ) { @@ -849,7 +913,10 @@ MSG_WriteByte( msg, svc_download ); MSG_WriteShort( msg, 0 ); // client is expecting block zero MSG_WriteLong( msg, -1 ); // illegal file size - MSG_WriteString( msg, errorMessage ); + #ifdef ELITEFORCE + if(!msg->compat) + #endif + MSG_WriteString( msg, errorMessage ); *cl->downloadName = 0; return; @@ -1023,21 +1090,28 @@ // start at arg 2 ( skip serverId cl_paks ) nCurArg = 1; - pArg = Cmd_Argv(nCurArg++); - if(!pArg) { - bGood = qfalse; - } - else + #ifdef ELITEFORCE + if(!cl->compat) { - // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475 - // we may get incoming cp sequences from a previous checksumFeed, which we need to ignore - // since serverId is a frame count, it always goes up - if (atoi(pArg) < sv.checksumFeedServerId) + #endif + pArg = Cmd_Argv(nCurArg++); + if(!pArg) { + bGood = qfalse; + } + else { - Com_DPrintf("ignoring outdated cp command from client %s\n", cl->name); - return; + // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475 + // we may get incoming cp sequences from a previous checksumFeed, which we need to ignore + // since serverId is a frame count, it always goes up + if (atoi(pArg) < sv.checksumFeedServerId) + { + Com_DPrintf("ignoring outdated cp command from client %s\n", cl->name); + return; + } } + #ifdef ELITEFORCE } + #endif // we basically use this while loop to avoid using 'goto' :) while (bGood) { @@ -1391,7 +1465,10 @@ ================== */ static void SV_UserMove( client_t *cl, msg_t *msg, qboolean delta ) { - int i, key; + int i; +#ifndef ELITEFORCE + int key; +#endif int cmdCount; usercmd_t nullcmd; usercmd_t cmds[MAX_PACKET_USERCMDS]; @@ -1415,6 +1492,7 @@ return; } + #ifndef ELITEFORCE // use the checksum feed in the key key = sv.checksumFeed; // also use the message acknowledge @@ -1422,11 +1500,17 @@ // also use the last acknowledged server command in the key key ^= Com_HashKey(cl->reliableCommands[ cl->reliableAcknowledge & (MAX_RELIABLE_COMMANDS-1) ], 32); + #endif + Com_Memset( &nullcmd, 0, sizeof(nullcmd) ); oldcmd = &nullcmd; for ( i = 0 ; i < cmdCount ; i++ ) { cmd = &cmds[i]; + #ifdef ELITEFORCE + MSG_ReadDeltaUsercmd( msg, oldcmd, cmd ); + #else MSG_ReadDeltaUsercmdKey( msg, key, oldcmd, cmd ); + #endif oldcmd = cmd; } @@ -1506,7 +1590,10 @@ int c; int serverId; - MSG_Bitstream(msg); +#ifdef ELITEFORCE + if(!msg->compat) + MSG_Bitstream(msg); +#endif serverId = MSG_ReadLong( msg ); cl->messageAcknowledge = MSG_ReadLong( msg ); @@ -1571,9 +1658,12 @@ // read optional clientCommand strings do { c = MSG_ReadByte( msg ); - if ( c == clc_EOF ) { + #ifdef ELITEFORCE + if(msg->compat && c == -1) + c = clc_EOF; + #endif + if ( c == clc_EOF ) break; - } if ( c != clc_clientCommand ) { break; } Index: code/server/sv_net_chan.c =================================================================== --- code/server/sv_net_chan.c (revision 1270) +++ code/server/sv_net_chan.c (working copy) @@ -24,6 +24,7 @@ #include "../qcommon/qcommon.h" #include "server.h" +#ifndef ELITEFORCE /* ============== SV_Netchan_Encode @@ -125,6 +126,7 @@ *(msg->data + i) = *(msg->data + i) ^ key; } } +#endif /* ================= @@ -144,7 +146,9 @@ netchan_buffer_t *netbuf; Com_DPrintf("#462 Netchan_TransmitNextFragment: popping a queued message for transmit\n"); netbuf = client->netchan_start_queue; + #ifndef ELITEFORCE SV_Netchan_Encode( client, &netbuf->msg ); + #endif Netchan_Transmit( &client->netchan, netbuf->msg.cursize, netbuf->msg.data ); // pop from queue client->netchan_start_queue = netbuf->next; @@ -172,7 +176,11 @@ */ void SV_Netchan_Transmit( client_t *client, msg_t *msg) { //int length, const byte *data ) { - MSG_WriteByte( msg, svc_EOF ); + #ifdef ELITEFORCE + if(!msg->compat) + #endif + MSG_WriteByte( msg, svc_EOF ); + if (client->netchan.unsentFragments) { netchan_buffer_t *netbuf; Com_DPrintf("#462 SV_Netchan_Transmit: unsent fragments, stacked\n"); @@ -186,7 +194,9 @@ // emit the next fragment of the current message for now Netchan_TransmitNextFragment(&client->netchan); } else { +#ifndef ELITEFORCE SV_Netchan_Encode( client, msg ); +#endif Netchan_Transmit( &client->netchan, msg->cursize, msg->data ); } } @@ -201,7 +211,9 @@ ret = Netchan_Process( &client->netchan, msg ); if (!ret) return qfalse; +#ifndef ELITEFORCE SV_Netchan_Decode( client, msg ); +#endif return qtrue; } Index: code/server/sv_snapshot.c =================================================================== --- code/server/sv_snapshot.c (revision 1270) +++ code/server/sv_snapshot.c (working copy) @@ -154,6 +154,12 @@ MSG_WriteByte (msg, svc_snapshot); + #ifdef ELITEFORCE + if(msg->compat) + #endif + MSG_WriteLong( msg, client->lastClientCommand ); + + // NOTE, MRE: now sent at the start of every message from server to client // let the client know which reliable clientCommands we have received //MSG_WriteLong( msg, client->lastClientCommand ); @@ -417,6 +423,7 @@ // if its a portal entity, add everything visible from its camera position if ( ent->r.svFlags & SVF_PORTAL ) { +#ifndef ELITEFORCE if ( ent->s.generic1 ) { vec3_t dir; VectorSubtract(ent->s.origin, origin, dir); @@ -424,6 +431,7 @@ continue; } } +#endif SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue ); } @@ -636,12 +644,23 @@ return; } - MSG_Init (&msg, msg_buf, sizeof(msg_buf)); +#ifdef ELITEFORCE + if(client->compat) + { + MSG_InitOOB(&msg, msg_buf, sizeof(msg_buf)); + msg.compat = qtrue; + } + else +#endif + MSG_Init (&msg, msg_buf, sizeof(msg_buf)); msg.allowoverflow = qtrue; // NOTE, MRE: all server->client messages now acknowledge // let the client know which reliable clientCommands we have received - MSG_WriteLong( &msg, client->lastClientCommand ); +#ifdef ELITEFORCE + if(!client->compat) +#endif + MSG_WriteLong( &msg, client->lastClientCommand ); // (re)send any reliable server commands SV_UpdateServerCommandsToClient( client, &msg ); Index: code/server/server.h =================================================================== --- code/server/server.h (revision 1270) +++ code/server/server.h (working copy) @@ -169,6 +169,8 @@ int oldServerTime; qboolean csUpdated[MAX_CONFIGSTRINGS+1]; + + qboolean compat; // compatibility mode for original EliteForce } client_t; //============================================================================= Index: code/server/sv_init.c =================================================================== --- code/server/sv_init.c (revision 1270) +++ code/server/sv_init.c (working copy) @@ -635,7 +635,11 @@ Cvar_Get ("timelimit", "0", CVAR_SERVERINFO); sv_gametype = Cvar_Get ("g_gametype", "0", CVAR_SERVERINFO | CVAR_LATCH ); Cvar_Get ("sv_keywords", "", CVAR_SERVERINFO); +#ifdef ELITEFORCE + Cvar_Get ("protocol", va("%i", EFPROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_ROM); +#else Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_ROM); +#endif sv_mapname = Cvar_Get ("mapname", "nomap", CVAR_SERVERINFO | CVAR_ROM); sv_privateClients = Cvar_Get ("sv_privateClients", "0", CVAR_SERVERINFO); sv_hostname = Cvar_Get ("sv_hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE ); @@ -659,7 +663,11 @@ // server vars sv_rconPassword = Cvar_Get ("rconPassword", "", CVAR_TEMP ); sv_privatePassword = Cvar_Get ("sv_privatePassword", "", CVAR_TEMP ); + #ifdef ELITEFORCE + sv_fps = Cvar_Get ("sv_fps", "20", CVAR_SYSTEMINFO | CVAR_TEMP); + #else sv_fps = Cvar_Get ("sv_fps", "20", CVAR_TEMP ); + #endif sv_timeout = Cvar_Get ("sv_timeout", "200", CVAR_TEMP ); sv_zombietime = Cvar_Get ("sv_zombietime", "2", CVAR_TEMP ); Cvar_Get ("nextmap", "", CVAR_TEMP ); @@ -667,8 +675,13 @@ sv_allowDownload = Cvar_Get ("sv_allowDownload", "0", CVAR_SERVERINFO); Cvar_Get ("sv_dlURL", "", CVAR_SERVERINFO | CVAR_ARCHIVE); sv_master[0] = Cvar_Get ("sv_master1", MASTER_SERVER_NAME, 0 ); + #ifdef ELITEFORCE + sv_master[1] = Cvar_Get ("sv_master2", "master.thewizclan.com", CVAR_ARCHIVE ); + sv_master[2] = Cvar_Get ("sv_master3", "efmaster.kickchat.com", CVAR_ARCHIVE ); + #else sv_master[1] = Cvar_Get ("sv_master2", "", CVAR_ARCHIVE ); sv_master[2] = Cvar_Get ("sv_master3", "", CVAR_ARCHIVE ); + #endif sv_master[3] = Cvar_Get ("sv_master4", "", CVAR_ARCHIVE ); sv_master[4] = Cvar_Get ("sv_master5", "", CVAR_ARCHIVE ); sv_reconnectlimit = Cvar_Get ("sv_reconnectlimit", "3", 0); @@ -708,7 +721,13 @@ // don't send a disconnect to a local client if ( cl->netchan.remoteAddress.type != NA_LOOPBACK ) { SV_SendServerCommand( cl, "print \"%s\n\"\n", message ); - SV_SendServerCommand( cl, "disconnect \"%s\"", message ); + + #ifdef ELITEFORCE + if(cl->compat) + SV_SendServerCommand(cl, "disconnect Server shutdown: %s", message); + else + #endif + SV_SendServerCommand( cl, "disconnect \"%s\"", message ); } // force a snapshot to be sent cl->nextSnapshotTime = -1; Index: code/server/sv_main.c =================================================================== --- code/server/sv_main.c (revision 1270) +++ code/server/sv_main.c (working copy) @@ -222,7 +222,11 @@ ================ */ #define HEARTBEAT_MSEC 300*1000 +#ifdef ELITEFORCE +#define HEARTBEAT_GAME "STEF1" +#else #define HEARTBEAT_GAME "QuakeArena-1" +#endif void SV_MasterHeartbeat( void ) { static netadr_t adr[MAX_MASTER_SERVERS]; int i; @@ -272,7 +276,12 @@ Com_Printf ("Sending heartbeat to %s\n", sv_master[i]->string ); // this command should be changed if the server info / status format // ever incompatably changes + #ifdef ELITEFORCE + NET_OutOfBandPrint(NS_SERVER, adr[i], "\\heartbeat\\%d\\gamename\\%s\\", + Cvar_VariableIntegerValue("net_port"), HEARTBEAT_GAME); + #else NET_OutOfBandPrint( NS_SERVER, adr[i], "heartbeat %s\n", HEARTBEAT_GAME ); + #endif } } @@ -393,17 +402,22 @@ infostring[0] = 0; - // echo back the parameter to status. so servers can use it as a challenge - // to prevent timed spoofed reply packets that add ghost servers - Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) ); - +#ifdef ELITEFORCE + Info_SetValueForKey( infostring, "protocol", va("%i", EFPROTOCOL_VERSION) ); +#else Info_SetValueForKey( infostring, "protocol", va("%i", PROTOCOL_VERSION) ); +#endif Info_SetValueForKey( infostring, "hostname", sv_hostname->string ); Info_SetValueForKey( infostring, "mapname", sv_mapname->string ); Info_SetValueForKey( infostring, "clients", va("%i", count) ); Info_SetValueForKey( infostring, "sv_maxclients", va("%i", sv_maxclients->integer - sv_privateClients->integer ) ); Info_SetValueForKey( infostring, "gametype", va("%i", sv_gametype->integer ) ); + + // echo back the parameter to status. so servers can use it as a challenge + // to prevent timed spoofed reply packets that add ghost servers + Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) ); + Info_SetValueForKey( infostring, "pure", va("%i", sv_pure->integer ) ); if( sv_minPing->integer ) { @@ -417,7 +431,15 @@ Info_SetValueForKey( infostring, "game", gamedir ); } + // echo back the parameter to status. so servers can use it as a challenge + // to prevent timed spoofed reply packets that add ghost servers + Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) ); + +#ifdef ELITEFORCE + NET_OutOfBandPrint( NS_SERVER, from, "infoResponse \"%s\"", infostring ); +#else NET_OutOfBandPrint( NS_SERVER, from, "infoResponse\n%s", infostring ); +#endif } /* @@ -516,9 +538,11 @@ MSG_BeginReadingOOB( msg ); MSG_ReadLong( msg ); // skip the -1 marker +#ifndef ELITEFORCE if (!Q_strncmp("connect", (char *) &msg->data[4], 7)) { Huff_Decompress(msg, 12); } +#endif s = MSG_ReadStringLine( msg ); Cmd_TokenizeString( s ); @@ -594,6 +618,10 @@ cl->netchan.remoteAddress.port = from.port; } + #ifdef ELITEFORCE + msg->compat = cl->compat; + #endif + // make sure it is a valid, in sequence packet if (SV_Netchan_Process(cl, msg)) { // zombie clients still need to do the Netchan_Process Index: code/renderer/tr_local.h =================================================================== --- code/renderer/tr_local.h (revision 1270) +++ code/renderer/tr_local.h (working copy) @@ -149,7 +149,8 @@ GF_SAWTOOTH, GF_INVERSE_SAWTOOTH, - GF_NOISE + GF_NOISE, + GF_RANDOM } genFunc_t; @@ -962,6 +963,7 @@ float triangleTable[FUNCTABLE_SIZE]; float sawToothTable[FUNCTABLE_SIZE]; float inverseSawToothTable[FUNCTABLE_SIZE]; + float noiseTable[FUNCTABLE_SIZE]; float fogTable[FOG_TABLE_SIZE]; } trGlobals_t; @@ -1019,6 +1021,9 @@ extern cvar_t *r_inGameVideo; // controls whether in game video should be draw extern cvar_t *r_fastsky; // controls whether sky should be cleared or drawn +#ifdef ELITEFORCE +extern cvar_t *r_origfastsky; // controls whether fastsky color is like in original EF. +#endif extern cvar_t *r_drawSun; // controls drawing of sun quad extern cvar_t *r_dynamiclight; // dynamic lights enabled/disabled extern cvar_t *r_dlightBacks; // dlight non-facing surfaces for continuity @@ -1107,6 +1112,7 @@ //==================================================================== float R_NoiseGet4f( float x, float y, float z, float t ); +int R_RandomOn( float t ); void R_NoiseInit( void ); void R_SwapBuffers( int ); @@ -1234,6 +1240,9 @@ qhandle_t RE_RegisterShaderLightMap( const char *name, int lightmapIndex ); qhandle_t RE_RegisterShader( const char *name ); qhandle_t RE_RegisterShaderNoMip( const char *name ); +#ifdef ELITEFORCE +qhandle_t RE_RegisterShader3D( const char *name ); +#endif qhandle_t RE_RegisterShaderFromImage(const char *name, int lightmapIndex, image_t *image, qboolean mipRawImage); shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImage ); Index: code/renderer/tr_model.c =================================================================== --- code/renderer/tr_model.c (revision 1270) +++ code/renderer/tr_model.c (working copy) @@ -154,7 +154,9 @@ filesize = ri.FS_ReadFile(name, (void **) &buf); if(!buf) { + #ifdef _DEBUG ri.Printf (PRINT_WARNING,"RE_RegisterModel: couldn't load %s\n", name); + #endif mod->type = MOD_BAD; return 0; } Index: code/renderer/tr_init.c =================================================================== --- code/renderer/tr_init.c (revision 1270) +++ code/renderer/tr_init.c (working copy) @@ -60,6 +60,9 @@ cvar_t *r_inGameVideo; cvar_t *r_fastsky; +#ifdef ELITEFORCE +cvar_t *r_origfastsky; +#endif cvar_t *r_drawSun; cvar_t *r_dynamiclight; cvar_t *r_dlightBacks; @@ -206,6 +209,10 @@ GLimp_Init(); +#ifdef ELITEFORCE + glConfig.textureFilterAnisotropicAvailable = textureFilterAnisotropic; +#endif + strcpy( renderer_buffer, glConfig.renderer_string ); Q_strlwr( renderer_buffer ); @@ -939,7 +946,11 @@ r_displayRefresh = ri.Cvar_Get( "r_displayRefresh", "0", CVAR_LATCH ); AssertCvarRange( r_displayRefresh, 0, 200, qtrue ); r_fullbright = ri.Cvar_Get ("r_fullbright", "0", CVAR_LATCH|CVAR_CHEAT ); +#ifdef ELITEFORCE + r_mapOverBrightBits = ri.Cvar_Get ("r_mapOverBrightBits", "1", CVAR_LATCH ); +#else r_mapOverBrightBits = ri.Cvar_Get ("r_mapOverBrightBits", "2", CVAR_LATCH ); +#endif r_intensity = ri.Cvar_Get ("r_intensity", "1", CVAR_LATCH ); r_singleShader = ri.Cvar_Get ("r_singleShader", "0", CVAR_CHEAT | CVAR_LATCH ); @@ -953,6 +964,9 @@ AssertCvarRange( r_znear, 0.001f, 200, qtrue ); r_ignoreGLErrors = ri.Cvar_Get( "r_ignoreGLErrors", "1", CVAR_ARCHIVE ); r_fastsky = ri.Cvar_Get( "r_fastsky", "0", CVAR_ARCHIVE ); +#ifdef ELITEFORCE + r_origfastsky = ri.Cvar_Get( "r_origfastsky", "0", CVAR_ARCHIVE ); +#endif r_inGameVideo = ri.Cvar_Get( "r_inGameVideo", "1", CVAR_ARCHIVE ); r_drawSun = ri.Cvar_Get( "r_drawSun", "0", CVAR_ARCHIVE ); r_dynamiclight = ri.Cvar_Get( "r_dynamiclight", "1", CVAR_ARCHIVE ); @@ -1051,10 +1065,17 @@ Com_Memset( &backEnd, 0, sizeof( backEnd ) ); Com_Memset( &tess, 0, sizeof( tess ) ); +#ifdef ELITEFORCE + if(sizeof(glconfig_t) != 5192) + { + ri.Error( ERR_FATAL, "Mod ABI incompatible: sizeof(glconfig_t) == %zd != 5192", sizeof(glconfig_t)); + } +#else if(sizeof(glconfig_t) != 11332) { ri.Error( ERR_FATAL, "Mod ABI incompatible: sizeof(glconfig_t) == %zd != 11332", sizeof(glconfig_t)); } +#endif // Swap_Init(); @@ -1063,6 +1084,8 @@ } Com_Memset( tess.constantColor255, 255, sizeof( tess.constantColor255 ) ); + R_NoiseInit(); + // // init function tables // @@ -1072,6 +1095,7 @@ tr.squareTable[i] = ( i < FUNCTABLE_SIZE/2 ) ? 1.0f : -1.0f; tr.sawToothTable[i] = (float)i / FUNCTABLE_SIZE; tr.inverseSawToothTable[i] = 1.0f - tr.sawToothTable[i]; + tr.noiseTable[i] = R_NoiseGet4f(0, 0, 0, i); if ( i < FUNCTABLE_SIZE / 2 ) { @@ -1092,8 +1116,6 @@ R_InitFogTable(); - R_NoiseInit(); - R_Register(); max_polys = r_maxpolys->integer; @@ -1218,6 +1240,9 @@ re.RegisterSkin = RE_RegisterSkin; re.RegisterShader = RE_RegisterShader; re.RegisterShaderNoMip = RE_RegisterShaderNoMip; +#ifdef ELITEFORCE + re.RegisterShader3D = RE_RegisterShader3D; +#endif re.LoadWorld = RE_LoadWorldMap; re.SetWorldVisData = RE_SetWorldVisData; re.EndRegistration = RE_EndRegistration; Index: code/renderer/tr_shade_calc.c =================================================================== --- code/renderer/tr_shade_calc.c (revision 1270) +++ code/renderer/tr_shade_calc.c (working copy) @@ -43,6 +43,8 @@ return tr.sawToothTable; case GF_INVERSE_SAWTOOTH: return tr.inverseSawToothTable; + case GF_NOISE: + return tr.noiseTable; case GF_NONE: default: break; @@ -577,7 +579,9 @@ case DEFORM_TEXT5: case DEFORM_TEXT6: case DEFORM_TEXT7: +#ifndef ELITEFORCE DeformText( backEnd.refdef.text[ds->deformation - DEFORM_TEXT0] ); +#endif break; } } @@ -688,6 +692,8 @@ if ( wf->func == GF_NOISE ) { glow = wf->base + R_NoiseGet4f( 0, 0, 0, ( tess.shaderTime + wf->phase ) * wf->frequency ) * wf->amplitude; + } else if( wf->func == GF_RANDOM ) { + glow = wf->base + R_RandomOn( (tess.shaderTime + wf->phase) * wf->frequency ) * wf->amplitude; } else { glow = EvalWaveForm( wf ) * tr.identityLight; } Index: code/renderer/tr_noise.c =================================================================== --- code/renderer/tr_noise.c (revision 1270) +++ code/renderer/tr_noise.c (working copy) @@ -22,15 +22,18 @@ // tr_noise.c #include "tr_local.h" -#define NOISE_SIZE 256 +#define NOISE_SIZE FUNCTABLE_SIZE #define NOISE_MASK ( NOISE_SIZE - 1 ) #define VAL( a ) s_noise_perm[ ( a ) & ( NOISE_MASK )] +#define VALR( a ) s_random[ ( a ) & ( NOISE_MASK )] #define INDEX( x, y, z, t ) VAL( x + VAL( y + VAL( z + VAL( t ) ) ) ) static float s_noise_table[NOISE_SIZE]; static int s_noise_perm[NOISE_SIZE]; +static int s_random[NOISE_SIZE]; + #define LERP( a, b, w ) ( a * ( 1.0f - w ) + b * w ) static float GetNoiseValue( int x, int y, int z, int t ) @@ -50,6 +53,8 @@ { s_noise_table[i] = ( float ) ( ( ( rand() / ( float ) RAND_MAX ) * 2.0 - 1.0 ) ); s_noise_perm[i] = ( unsigned char ) ( rand() / ( float ) RAND_MAX * 255 ); + + s_random[i] = rand() & 0x01; } } @@ -93,3 +98,9 @@ return finalvalue; } + +// used in the shader functions (GF_RANDOM) to implement a quasi random flickering. +int R_RandomOn(float t) +{ + return VALR((unsigned int) floor(t)); +} Index: code/renderer/tr_surface.c =================================================================== --- code/renderer/tr_surface.c (revision 1270) +++ code/renderer/tr_surface.c (working copy) @@ -156,17 +156,25 @@ static void RB_SurfaceSprite( void ) { vec3_t left, up; float radius; + float rotation; // calculate the xyz locations for the four corners + #ifdef ELITEFORCE + radius = backEnd.currentEntity->e.data.sprite.radius; + rotation = backEnd.currentEntity->e.data.sprite.rotation; + #else radius = backEnd.currentEntity->e.radius; - if ( backEnd.currentEntity->e.rotation == 0 ) { + rotation = backEnd.currentEntity->e.rotation; + #endif + + if ( rotation == 0 ) { VectorScale( backEnd.viewParms.or.axis[1], radius, left ); VectorScale( backEnd.viewParms.or.axis[2], radius, up ); } else { float s, c; float ang; - ang = M_PI * backEnd.currentEntity->e.rotation / 180; + ang = M_PI * rotation / 180; s = sin( ang ); c = cos( ang ); @@ -549,7 +557,577 @@ } } +#ifdef ELITEFORCE + /* +============== +RB_SurfaceOrientedSprite +============== +*/ +static void RB_SurfaceOrientedSprite( void ) +{ + vec3_t left, up; + float radius; + float rotation; + + // calculate the xyz locations for the four corners + radius = backEnd.currentEntity->e.data.sprite.radius; + rotation = backEnd.currentEntity->e.data.sprite.rotation; + + if (rotation == 0) + { + VectorScale( backEnd.currentEntity->e.axis[1], radius, left ); + VectorScale( backEnd.currentEntity->e.axis[2], radius, up ); + } + else + { + float s, c; + float ang; + + ang = M_PI * rotation / 180; + s = sin( ang ); + c = cos( ang ); + + VectorScale( backEnd.currentEntity->e.axis[1], c * radius, left ); + VectorMA( left, -s * radius, backEnd.currentEntity->e.axis[2], left ); + + VectorScale( backEnd.currentEntity->e.axis[2], c * radius, up ); + VectorMA( up, s * radius, backEnd.currentEntity->e.axis[1], up ); + } + if ( backEnd.viewParms.isMirror ) + VectorSubtract( vec3_origin, left, left ); + + RB_AddQuadStamp( backEnd.currentEntity->e.origin, left, up, backEnd.currentEntity->e.shaderRGBA ); +} + +void RB_Line(vec3_t start, vec3_t end, vec3_t linedirection, vec3_t left, + vec3_t *corners, float starttex, float endtex, refEntity_t *e) +{ + int ndx, numind; + color4ub_t *vertcols; + + RB_CHECKOVERFLOW( 4, 6 ); + + // Set up the triangles .. + ndx = tess.numVertexes; + numind = tess.numIndexes; + + tess.indexes[ numind ] = ndx; + tess.indexes[ numind + 1 ] = ndx + 1; + tess.indexes[ numind + 2 ] = ndx + 3; + + tess.indexes[ numind + 3 ] = ndx + 3; + tess.indexes[ numind + 4 ] = ndx + 1; + tess.indexes[ numind + 5 ] = ndx + 2; + + // now create the corner vertices + + if(corners) + { + // we have the corner points for the start already given. + VectorCopy(corners[0], tess.xyz[ndx]); + VectorCopy(corners[1], tess.xyz[ndx+1]); + } + else + { + // start left corner + VectorAdd(start, left, tess.xyz[ndx]); + // start right corner + VectorSubtract(start, left, tess.xyz[ndx+1]); + } + // end right corner + VectorSubtract(end, left, tess.xyz[ndx+2]); + // end left corner + VectorAdd(end, left, tess.xyz[ndx+3]); + + if(corners) + { + // save the end corner points here. + VectorCopy(tess.xyz[ndx+3], corners[0]); + VectorCopy(tess.xyz[ndx+2], corners[1]); + } + + // Texture stuff.... + tess.texCoords[ndx][0][0] = 0; + tess.texCoords[ndx][0][1] = starttex; + + tess.texCoords[ndx+1][0][0] = 1; + tess.texCoords[ndx+1][0][1] = starttex; + + tess.texCoords[ndx+2][0][0] = 1; + tess.texCoords[ndx+2][0][1] = endtex; + + tess.texCoords[ndx+3][0][0] = 0; + tess.texCoords[ndx+3][0][1] = endtex; + + vertcols = tess.vertexColors; + + vertcols[ndx][0] = vertcols[ndx+1][0] = vertcols[ndx+2][0] = vertcols[ndx+3][0] = e->shaderRGBA[0]; + vertcols[ndx][1] = vertcols[ndx+1][1] = vertcols[ndx+2][1] = vertcols[ndx+3][1] = e->shaderRGBA[1]; + vertcols[ndx][2] = vertcols[ndx+1][2] = vertcols[ndx+2][2] = vertcols[ndx+3][2] = e->shaderRGBA[2]; + vertcols[ndx][3] = vertcols[ndx+1][3] = vertcols[ndx+2][3] = vertcols[ndx+3][3] = e->shaderRGBA[3]; + + tess.numVertexes += 4; + tess.numIndexes += 6; + + +} + +// Create a normal vector and scale it to the right length +void RB_LineNormal(vec3_t vec1, vec3_t vec2, float scale, vec3_t result) +{ + // Create the offset vector for the width of the line + CrossProduct(vec1, vec2, result); + // Normalize the offset vector first. + VectorNormalize(result); + // Scale the offset vector to the intended width. + VectorScale(result, scale / 2, result); +} + +// Let's draw a thick line from A to B and dance happily around a christmas tree! +void RB_SurfaceLine( void ) +{ + refEntity_t *e; + vec3_t left; // I vote the green party... + vec3_t start, end, linedirection, start2origin; + shader_t *surfshader; + + e = &backEnd.currentEntity->e; + + // Getting up before 1:00 pm to attend HM I lectures finally paid off.. + + // Get the start and end point of the line + VectorCopy(e->origin, start); + VectorCopy(e->oldorigin, end); + + // Direction vector for the line: + VectorSubtract(end, start, linedirection); + // Direction vector for the start to current point of view + VectorSubtract(backEnd.viewParms.or.origin, start, start2origin); + + RB_LineNormal(start2origin, linedirection, e->data.line.width, left); + RB_Line(start, end, linedirection, left, NULL, 0, e->data.line.stscale, e); + + // Hack to make the dreadnought lightning bolt work: set the correct cull type... + if((surfshader = R_GetShaderByHandle(e->customShader))) + surfshader->cullType = CT_TWO_SIDED; +} + +void RB_SurfaceOrientedLine(void) +{ + refEntity_t *e; + vec3_t left; + vec3_t linedirection; + shader_t *surfshader; + + e = &backEnd.currentEntity->e; + + VectorSubtract(e->oldorigin, e->origin, linedirection); + + VectorCopy(e->axis[1], left); + VectorNormalize(left); + VectorScale(left, e->data.line.width / 2, left); + + RB_Line(e->origin, e->oldorigin, linedirection, left, NULL, 0, 1, e); + + surfshader = R_GetShaderByHandle(e->customShader); + surfshader->cullType = CT_TWO_SIDED; + +} + +// This time it's not a rectangle but a trapezoid I guess ... +void RB_SurfaceLine2( void ) +{ + refEntity_t *e; + vec3_t startleft, endleft; // I still vote the green party... + vec3_t start, end, linedirection, start2origin; + color4ub_t *vertcols; + int ndx, numind; + + RB_CHECKOVERFLOW( 6, 12 ); + + e = &backEnd.currentEntity->e; + + // Set up the triangle .. + ndx = tess.numVertexes; + numind = tess.numIndexes; + + tess.indexes[ numind ] = ndx; + tess.indexes[ numind + 1 ] = ndx + 1; + tess.indexes[ numind + 2 ] = ndx + 5; + tess.indexes[ numind + 3 ] = ndx + 5; + tess.indexes[ numind + 4 ] = ndx + 4; + tess.indexes[ numind + 5 ] = ndx + 1; + tess.indexes[ numind + 6 ] = ndx + 1; + tess.indexes[ numind + 7 ] = ndx + 4; + tess.indexes[ numind + 8 ] = ndx + 3; + tess.indexes[ numind + 9 ] = ndx + 3; + tess.indexes[ numind + 10 ] = ndx + 2; + tess.indexes[ numind + 11 ] = ndx + 1; + + + // Get the start and end point of the line + VectorCopy(e->origin, start); + VectorCopy(e->oldorigin, end); + + // Direction vector for the line: + VectorSubtract(end, start, linedirection); + // Direction vector for the start to current point of view + VectorSubtract(backEnd.viewParms.or.origin, start, start2origin); + + // Create the offset vector for the width of the line + CrossProduct(start2origin, linedirection, startleft); + // Normalize the offset vector first. + VectorNormalize(startleft); + // The normal of the triangle we just thought up: + + + // Scale the offset vector to the intended width. + VectorScale(startleft, e->data.line.width2 / 2, endleft); + VectorScale(startleft, e->data.line.width / 2, startleft); + + // now create the corner vertices + + // start left corner + VectorAdd(start, startleft, tess.xyz[ndx]); + // start point + VectorCopy(start, tess.xyz[ndx+1]); + // start right corner + VectorSubtract(start, startleft, tess.xyz[ndx+2]); + // end right corner + VectorSubtract(end, endleft, tess.xyz[ndx+3]); + // end point + VectorCopy(end, tess.xyz[ndx+4]); + // end left corner + VectorAdd(end, endleft, tess.xyz[ndx+5]); + + // Texture stuff.... + tess.texCoords[ndx][0][0] = 0; + tess.texCoords[ndx][0][1] = 0; + + tess.texCoords[ndx+1][0][0] = 0.5; + tess.texCoords[ndx+1][0][1] = 0; + + tess.texCoords[ndx+2][0][0] = 1; + tess.texCoords[ndx+2][0][1] = 0; + + tess.texCoords[ndx+3][0][0] = 1; + tess.texCoords[ndx+3][0][1] = 1; + + tess.texCoords[ndx+4][0][0] = 0.5; + tess.texCoords[ndx+4][0][1] = 1; + + tess.texCoords[ndx+5][0][0] = 0; + tess.texCoords[ndx+5][0][1] = 1; + + vertcols = tess.vertexColors; + + vertcols[ndx][0] = vertcols[ndx+1][0] = vertcols[ndx+2][0] = + vertcols[ndx+3][0] = vertcols[ndx+4][0] = vertcols[ndx+5][0] = e->shaderRGBA[0]; + vertcols[ndx][1] = vertcols[ndx+1][1] = vertcols[ndx+2][1] = + vertcols[ndx+3][1] = vertcols[ndx+4][1] = vertcols[ndx+5][1] = e->shaderRGBA[1]; + vertcols[ndx][2] = vertcols[ndx+1][2] = vertcols[ndx+2][2] = + vertcols[ndx+3][2] = vertcols[ndx+4][2] = vertcols[ndx+5][2] = e->shaderRGBA[2]; + vertcols[ndx][3] = vertcols[ndx+1][3] = vertcols[ndx+2][3] = + vertcols[ndx+3][3] = vertcols[ndx+4][3] = vertcols[ndx+5][3] = e->shaderRGBA[3]; + + tess.numVertexes += 6; + tess.numIndexes += 12; +} + +// Draw a cubic bezier curve for the imod weapon. +#define BEZIER_SEGMENTS 32 +#define BEZIER_STEPSIZE 1.0 / BEZIER_SEGMENTS + +void RB_SurfaceBezier(void) +{ + refEntity_t *e = &backEnd.currentEntity->e; + double tvar = 0, one_tvar; // use double to not lose as much precision on cubing and squaring. + float starttc, endtc; + vec3_t segstartpos, segendpos, prevend[2]; + vec3_t linedirection, start2origin, left; + int index; + + VectorCopy(e->origin, segstartpos); + + // start of the bezier curve is pointy + VectorCopy(segstartpos, prevend[0]); + VectorCopy(segstartpos, prevend[1]); + + // iterate through all segments we have to create. + while(tvar <= 1.0) + { + // get the texture position for the rectangular approximating the bezier curve. + starttc = tvar / 2.0; + tvar += BEZIER_STEPSIZE; + endtc = tvar / 2.0; + + one_tvar = 1.0 - tvar; + + // get the endpoint for this segment. + + for(index = 0; index < 3; index++) + { + // C(t) = P0 (t-1)^3 + 3 P1 t (t-1)^2 + 3 P2 t^2 (t-1) + P3 t^3 + segendpos[index] = + e->origin[index] * one_tvar * one_tvar * one_tvar + + 3 * e->data.bezier.control1[index] * tvar * one_tvar * one_tvar + + 3 * e->data.bezier.control2[index] * tvar * tvar * one_tvar + + e->oldorigin[index] * tvar * tvar * tvar; + } + + // Direction vector for the line: + VectorSubtract(segendpos, segstartpos, linedirection); + // Direction vector for the start to current point of view + VectorSubtract(backEnd.viewParms.or.origin, segstartpos, start2origin); + + RB_LineNormal(start2origin, linedirection, e->data.bezier.width, left); + RB_Line(segstartpos, segendpos, linedirection, left, prevend, starttc, endtc, e); + + // Our next segment starts where the old one ends. + VectorCopy(segendpos, segstartpos); + } +} + +// this actually isn't a cylinder but a frustum, but for the sake of naming +// conventions we'll keep calling it a cylinder. + +// number of rectangles we'll use to approximate a frustum of a cone. +#define CYLINDER_MAXPLANES 64 +#define LOD_CONSTANT 4.0f + +void RB_SurfaceCylinder(void) +{ + int planes, index = 0; // calculate the number of planes based on the level of detail. + vec3_t bottom, top, bottom2top, zerodeg_bottom, zerodeg_top, faxis; + vec3_t bottomcorner, topcorner, vieworigin; + float anglestep, tcstep = 0, width, width2, height; + int ndx, numind; + color4ub_t *vertcols; + refEntity_t *e = &backEnd.currentEntity->e; + // for LOD calculation: + vec3_t bottom2origin, top2origin, lodcalc, projection; + float maxradius, startdistance, enddistance, distance; + + height = e->data.cylinder.height; + width = e->data.cylinder.width; + width2 = e->data.cylinder.width2; + maxradius = (width > width2) ? width : width2; + + // get the start and end point of the frustum: + + VectorCopy(backEnd.viewParms.or.origin, vieworigin); + VectorCopy(e->axis[0], faxis); + VectorCopy(e->origin, bottom); + VectorScale(faxis, height, bottom2top); + VectorAdd(bottom, bottom2top, top); + + // Get distance from frustum: + // first distance from endpoints + VectorSubtract(vieworigin, bottom, bottom2origin); + VectorSubtract(vieworigin, top, top2origin); + + // get projection of view origin on the middle line: + distance = DotProduct(bottom2origin, faxis); + + startdistance = VectorLength(bottom2origin); + enddistance = VectorLength(top2origin); + + if(distance < 0 || distance > height) + { + // projected point is not between bottom and top. + distance = (startdistance < enddistance) ? startdistance : enddistance; + } + else + { + VectorMA(bottom, distance, faxis, projection); + VectorSubtract(vieworigin, projection, lodcalc); + distance = VectorLength(lodcalc); + + if(startdistance < distance) + distance = startdistance; + if(enddistance < distance) + distance = enddistance; + } + + planes = LOD_CONSTANT * maxradius/distance * CYLINDER_MAXPLANES; + + if(planes < 8) + planes = 8; + else if(planes > CYLINDER_MAXPLANES) + planes = CYLINDER_MAXPLANES; + + RB_CHECKOVERFLOW(4 * planes, 6 * planes); + + // create a normalized perpendicular vector to the frustum height. + PerpendicularVector(zerodeg_bottom, faxis); + + // This vector gets rotated to create the top ring + VectorScale(zerodeg_bottom, width2 / 2.0f, zerodeg_top); + // Likewise the vector for the lower ring: + VectorScale(zerodeg_bottom, width / 2.0f, zerodeg_bottom); + + anglestep = 360.0f / planes; + if(e->data.cylinder.wrap) + tcstep = e->data.cylinder.stscale / planes; + + ndx = tess.numVertexes; + numind = tess.numIndexes; + vertcols = tess.vertexColors; + + // this loop is creating all surface planes. + while(index < planes) + { + index++; + + // Set up the indexes for the new plane: + tess.indexes[numind++] = ndx; + tess.indexes[numind++] = ndx + 2; + tess.indexes[numind++] = ndx + 3; + + tess.indexes[numind++] = ndx; + tess.indexes[numind++] = ndx + 3; + tess.indexes[numind++] = ndx + 1; + + if(index <= 1) + { + VectorAdd(bottom, zerodeg_bottom, tess.xyz[ndx]); + VectorAdd(top, zerodeg_top, tess.xyz[ndx + 1]); + } + else + { + VectorCopy(bottomcorner, tess.xyz[ndx]); + VectorCopy(topcorner, tess.xyz[ndx + 1]); + } + + // Create on the right side two new vertices, first the bottom, then the top one. + if(index >= planes) + { + VectorAdd(bottom, zerodeg_bottom, bottomcorner); + VectorAdd(top, zerodeg_top, topcorner); + } + else + { + RotatePointAroundVector(bottomcorner, faxis, zerodeg_bottom, anglestep * index); + VectorAdd(bottom, bottomcorner, bottomcorner); + RotatePointAroundVector(topcorner, faxis, zerodeg_top, index * anglestep); + VectorAdd(top, topcorner, topcorner); + } + + VectorCopy(bottomcorner, tess.xyz[ndx + 2]); + VectorCopy(topcorner, tess.xyz[ndx + 3]); + + // Take care about the texture stuff + if(e->data.cylinder.wrap) + { + tess.texCoords[ndx][0][0] = tcstep * (index - 1); + tess.texCoords[ndx][0][1] = 0; + + tess.texCoords[ndx+1][0][0] = tcstep * (index - 1); + tess.texCoords[ndx+1][0][1] = 1; + + tess.texCoords[ndx+2][0][0] = tcstep * index; + tess.texCoords[ndx+2][0][1] = 0; + + tess.texCoords[ndx+3][0][0] = tcstep * index; + tess.texCoords[ndx+3][0][1] = 1; + } + else + { + tess.texCoords[ndx][0][0] = 0; + tess.texCoords[ndx][0][1] = 0; + + tess.texCoords[ndx+1][0][0] = 0; + tess.texCoords[ndx+1][0][1] = 1; + + tess.texCoords[ndx+2][0][0] = e->data.cylinder.stscale; + tess.texCoords[ndx+2][0][1] = 0; + + tess.texCoords[ndx+3][0][0] = e->data.cylinder.stscale; + tess.texCoords[ndx+3][0][1] = 1; + } + + vertcols[ndx][0] = vertcols[ndx+1][0] = vertcols[ndx+2][0] = vertcols[ndx+3][0] = e->shaderRGBA[0]; + vertcols[ndx][1] = vertcols[ndx+1][1] = vertcols[ndx+2][1] = vertcols[ndx+3][1] = e->shaderRGBA[1]; + vertcols[ndx][2] = vertcols[ndx+1][2] = vertcols[ndx+2][2] = vertcols[ndx+3][2] = e->shaderRGBA[2]; + vertcols[ndx][3] = vertcols[ndx+1][3] = vertcols[ndx+2][3] = vertcols[ndx+3][3] = 0xff; + + ndx += 4; + } + + tess.numVertexes = ndx; + tess.numIndexes = numind; +} + +// Draws lightning using a multisegment line with jumpy connecting points. +#define ELECTRICITY_SEGMENTS 16 +#define ELECTRICITY_STEPSIZE 1.0 / ELECTRICITY_SEGMENTS + +void RB_SurfaceElectricity(void) +{ + vec3_t start, end, linedirection, segstartpos, segendpos; + vec3_t prevend[2], left, start2origin, start2end; + refEntity_t *e = &backEnd.currentEntity->e; + float width, deviation, veclen; + int index; + + VectorCopy(e->origin, start); + VectorCopy(e->oldorigin, end); + VectorSubtract(end, start, start2end); + veclen = VectorLength(start2end); + + width = e->data.electricity.width; + deviation = e->data.electricity.deviation; + + VectorCopy(start, segendpos); + + for(index = 0; index < ELECTRICITY_SEGMENTS; index++) + { + VectorCopy(segendpos, segstartpos); + + if(index > ELECTRICITY_SEGMENTS - 2) + { + // This is the last segment. + VectorCopy(end, segendpos); + } + else + { + if(index > ELECTRICITY_SEGMENTS - 3) + { + // The line should have somewhat deviated by now. + // Make the last two segments go to the endpoint + + // get the middle point between startpos and endpos + VectorAdd(segstartpos, end, segendpos); + VectorScale(segendpos, 0.5f, segendpos); + } + + // Slightly misplace the next point on the line. + segendpos[PITCH] += (start2end[PITCH] + crandom() * deviation * veclen) * ELECTRICITY_STEPSIZE; + segendpos[YAW] += (start2end[YAW] + crandom() * deviation * veclen) * ELECTRICITY_STEPSIZE; + segendpos[ROLL] += (start2end[ROLL] + crandom() * deviation * veclen / 2.0) * ELECTRICITY_STEPSIZE; + } + + // Direction vector for the line: + VectorSubtract(segendpos, segstartpos, linedirection); + // Direction vector for the start to current point of view + VectorSubtract(backEnd.viewParms.or.origin, segstartpos, start2origin); + + RB_LineNormal(start2origin, linedirection, width, left); + + if(!index) + { + // this is our first segment, create the starting points + VectorAdd(segstartpos, left, prevend[0]); + VectorSubtract(segstartpos, left, prevend[1]); + } + + RB_Line(segstartpos, segendpos, linedirection, left, prevend, 0, e->data.electricity.stscale, e); + } +} +#endif + +/* ** VectorArrayNormalize * * The inputs to this routing seem to always be close to length = 1.0 (about 0.6 to 2.0) @@ -1144,7 +1722,6 @@ } } - /* =========================================================================== @@ -1188,6 +1765,9 @@ */ void RB_SurfaceEntity( surfaceType_t *surfType ) { switch( backEnd.currentEntity->e.reType ) { +#ifdef ELITEFORCE + case RT_ALPHAVERTPOLY: +#endif case RT_SPRITE: RB_SurfaceSprite(); break; @@ -1203,6 +1783,29 @@ case RT_LIGHTNING: RB_SurfaceLightningBolt(); break; +#ifdef ELITEFORCE + case RT_ORIENTEDSPRITE: + RB_SurfaceOrientedSprite(); + break; + case RT_LINE: + RB_SurfaceLine(); + break; + case RT_ORIENTEDLINE: + RB_SurfaceOrientedLine(); + break; + case RT_LINE2: + RB_SurfaceLine2(); + break; + case RT_BEZIER: + RB_SurfaceBezier(); + break; + case RT_CYLINDER: + RB_SurfaceCylinder(); + break; + case RT_ELECTRICITY: + RB_SurfaceElectricity(); + break; +#endif default: RB_SurfaceAxis(); break; Index: code/renderer/tr_scene.c =================================================================== --- code/renderer/tr_scene.c (revision 1270) +++ code/renderer/tr_scene.c (working copy) @@ -304,7 +304,9 @@ ri.Error (ERR_DROP, "R_RenderScene: NULL worldmodel"); } + #ifndef ELITEFORCE Com_Memcpy( tr.refdef.text, fd->text, sizeof( tr.refdef.text ) ); + #endif tr.refdef.x = fd->x; tr.refdef.y = fd->y; Index: code/renderer/tr_shader.c =================================================================== --- code/renderer/tr_shader.c (revision 1270) +++ code/renderer/tr_shader.c (working copy) @@ -294,6 +294,10 @@ { return GF_NOISE; } + else if( !Q_stricmp( funcname, "random" ) ) + { + return GF_RANDOM; + } ri.Printf( PRINT_WARNING, "WARNING: invalid genfunc name '%s' in shader '%s'\n", funcname, shader.name ); return GF_SIN; @@ -742,6 +746,12 @@ { depthFuncBits = 0; } +#ifdef ELITEFORCE + if ( !Q_stricmp( token, "disable" ) ) + { + depthFuncBits = 0; + } +#endif else if ( !Q_stricmp( token, "equal" ) ) { depthFuncBits = GLS_DEPTHFUNC_EQUAL; @@ -2759,7 +2769,38 @@ return sh->index; } +#ifdef ELITEFORCE +/* +==================== +RE_RegisterShader3D +For explicitly defined shaders that need LIGHTMAP_NONE +as lightmapIndex. +==================== +*/ +qhandle_t RE_RegisterShader3D( const char *name ) { + shader_t *sh; + + if ( strlen( name ) >= MAX_QPATH ) { + Com_Printf( "Shader name exceeds MAX_QPATH\n" ); + return 0; + } + + sh = R_FindShader( name, LIGHTMAP_NONE, qfalse ); + + // we want to return 0 if the shader failed to + // load for some reason, but R_FindShader should + // still keep a name allocated for it, so if + // something calls RE_RegisterShader again with + // the same name, we don't try looking for it again + if ( sh->defaultShader ) { + return 0; + } + + return sh->index; +} +#endif + /* ==================== R_GetShaderByHandle Index: code/renderer/tr_light.c =================================================================== --- code/renderer/tr_light.c (revision 1270) +++ code/renderer/tr_light.c (working copy) @@ -294,6 +294,20 @@ } ent->lightingCalculated = qtrue; +#ifdef ELITEFORCE + if(ent->e.renderfx & RF_FULLBRIGHT) + { +// ent->ambientLight[0] = ent->ambientLight[1] = ent->ambientLight[1] = 0xFF; + ((byte *)&ent->ambientLightInt)[0] = 0x7F; + ((byte *)&ent->ambientLightInt)[1] = 0x7F; + ((byte *)&ent->ambientLightInt)[2] = 0x7F; + ((byte *)&ent->ambientLightInt)[3] = 0xFF; + + ent->lightDir[0] = ent->lightDir[1] = ent->lightDir[2] = 0; + return; + } +#endif + // // trace a sample point down to find ambient light // Index: code/renderer/tr_types.h =================================================================== --- code/renderer/tr_types.h (revision 1270) +++ code/renderer/tr_types.h (working copy) @@ -32,6 +32,9 @@ #define RF_THIRD_PERSON 2 // don't draw through eyes, only mirrors (player bodies, chat sprites) #define RF_FIRST_PERSON 4 // only draw through eyes (view weapon, damage blood blob) #define RF_DEPTHHACK 8 // for view weapon Z crunching +#ifdef ELITEFORCE +#define RF_FULLBRIGHT 0x0010 +#endif #define RF_NOSHADOW 64 // don't add stencil shadows #define RF_LIGHTING_ORIGIN 128 // use refEntity->lightingOrigin instead of refEntity->origin @@ -42,6 +45,10 @@ #define RF_WRAP_FRAMES 512 // mod the model frames by the maxframes to allow continuous // animation without needing to know the frame count +#ifdef ELITEFORCE +#define RF_FORCE_ENT_ALPHA 0x0800 // override shader alpha value and take the one from the entity. +#endif + // refdef flags #define RDF_NOWORLDMODEL 1 // used for player configuration screen #define RDF_HYPERSPACE 4 // teleportation effect @@ -58,7 +65,28 @@ polyVert_t *verts; } poly_t; +#ifdef ELITEFORCE typedef enum { + RT_MODEL, + RT_SPRITE, + RT_ORIENTEDSPRITE, // Replaces RT_POLY, which wasn't used. --Pat + RT_ALPHAVERTPOLY, // Individual alpha levels on each vertex + RT_BEAM, + RT_RAIL_CORE, + RT_RAIL_RINGS, + RT_LIGHTNING, + RT_PORTALSURFACE, // doesn't draw anything, just info for portals + RT_LINE, // New type for Trek MP --Pat + RT_ORIENTEDLINE, + RT_LINE2, // New line type for Trek MP, with taper support --Pat + RT_BEZIER, // what he said --keith + RT_CYLINDER, // Yet another Trek primitive! + RT_ELECTRICITY, // Yet another Trek primitive! + + RT_MAX_REF_ENTITY_TYPE +} refEntityType_t; +#else +typedef enum { RT_MODEL, RT_POLY, RT_SPRITE, @@ -70,6 +98,7 @@ RT_MAX_REF_ENTITY_TYPE } refEntityType_t; +#endif typedef struct { refEntityType_t reType; @@ -102,8 +131,49 @@ float shaderTime; // subtracted from refdef time to control effect start times // extra sprite information +#ifdef ELITEFORCE + union + { + struct + { + float rotation; + float radius; + byte vertRGBA[4][4]; + } sprite; + struct + { + float width; + float width2; + float stscale; + } line; + struct // that whole put-the-opening-brace-on-the-same-line-as-the-beginning-of-the-definition coding style is fecal // I agree. + { + float width; + vec3_t control1; + vec3_t control2; + } bezier; + struct + { + float width; + float width2; + float stscale; + float height; + float bias; + qboolean wrap; + } cylinder; + struct + { + float width; + float deviation; + float stscale; + qboolean wrap; + qboolean taper; + } electricity; + } data; +#else float radius; float rotation; +#endif } refEntity_t; @@ -124,8 +194,10 @@ // 1 bits will prevent the associated area from rendering at all byte areamask[MAX_MAP_AREA_BYTES]; +#ifndef ELITEFORCE // text messages for deform text shaders char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH]; +#endif } refdef_t; @@ -145,7 +217,10 @@ */ typedef enum { TC_NONE, - TC_S3TC + TC_S3TC, +#ifdef ELITEFORCE + TC_S3TC_DXT +#endif } textureCompression_t; typedef enum { @@ -172,7 +247,11 @@ char renderer_string[MAX_STRING_CHARS]; char vendor_string[MAX_STRING_CHARS]; char version_string[MAX_STRING_CHARS]; +#ifdef ELITEFORCE + char extensions_string[2*MAX_STRING_CHARS]; +#else char extensions_string[BIG_INFO_STRING]; +#endif int maxTextureSize; // queried from GL int numTextureUnits; // multitexture ability @@ -185,6 +264,9 @@ qboolean deviceSupportsGamma; textureCompression_t textureCompression; qboolean textureEnvAddAvailable; +#ifdef ELITEFORCE + qboolean textureFilterAnisotropicAvailable; +#endif int vidWidth, vidHeight; // aspect is the screen's physical width / height, which may be different Index: code/renderer/tr_public.h =================================================================== --- code/renderer/tr_public.h (revision 1270) +++ code/renderer/tr_public.h (working copy) @@ -49,6 +49,9 @@ qhandle_t (*RegisterSkin)( const char *name ); qhandle_t (*RegisterShader)( const char *name ); qhandle_t (*RegisterShaderNoMip)( const char *name ); +#ifdef ELITEFORCE + qhandle_t (*RegisterShader3D)( const char *name ); +#endif void (*LoadWorld)( const char *name ); // the vis data is a large enough block of data that we go to the trouble Index: code/renderer/tr_main.c =================================================================== --- code/renderer/tr_main.c (revision 1270) +++ code/renderer/tr_main.c (working copy) @@ -968,18 +968,25 @@ int R_SpriteFogNum( trRefEntity_t *ent ) { int i, j; fog_t *fog; + float radius; if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { return 0; } + #ifdef ELITEFORCE + radius = ent->e.data.sprite.radius; + #else + radius = ent->e.radius; + #endif + for ( i = 1 ; i < tr.world->numfogs ; i++ ) { fog = &tr.world->fogs[i]; for ( j = 0 ; j < 3 ; j++ ) { - if ( ent->e.origin[j] - ent->e.radius >= fog->bounds[1][j] ) { + if ( ent->e.origin[j] - radius >= fog->bounds[1][j] ) { break; } - if ( ent->e.origin[j] + ent->e.radius <= fog->bounds[0][j] ) { + if ( ent->e.origin[j] + radius <= fog->bounds[0][j] ) { break; } } @@ -1177,6 +1184,18 @@ switch ( ent->e.reType ) { case RT_PORTALSURFACE: break; // don't draw anything + +#ifdef ELITEFORCE + case RT_ORIENTEDSPRITE: + case RT_ALPHAVERTPOLY: + case RT_LINE: + case RT_ORIENTEDLINE: + case RT_LINE2: + case RT_BEZIER: + case RT_CYLINDER: + case RT_ELECTRICITY: +#endif + case RT_SPRITE: case RT_BEAM: case RT_LIGHTNING: Index: code/renderer/tr_shade.c =================================================================== --- code/renderer/tr_shade.c (revision 1270) +++ code/renderer/tr_shade.c (working copy) @@ -1047,6 +1047,10 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input ) { int stage; +#ifdef ELITEFORCE + qboolean overridealpha; + int oldalphaGen = 0; +#endif for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) { @@ -1057,7 +1061,25 @@ break; } + #ifdef ELITEFORCE + // Override the shader alpha channel if requested. + if(backEnd.currentEntity->e.renderfx & RF_FORCE_ENT_ALPHA) + { + overridealpha = qtrue; + oldalphaGen = pStage->alphaGen; + pStage->alphaGen = AGEN_ENTITY; + } + else + overridealpha = qfalse; + #endif + ComputeColors( pStage ); + + #ifdef ELITEFORCE + if(overridealpha) + pStage->alphaGen = oldalphaGen; + #endif + ComputeTexCoords( pStage ); if ( !setArraysOnce ) @@ -1090,7 +1112,15 @@ else R_BindAnimatedImage( &pStage->bundle[0] ); - GL_State( pStage->stateBits ); + #ifdef ELITEFORCE + if(overridealpha && backEnd.currentEntity->e.shaderRGBA[3] < 0xFF && !(pStage->stateBits & GLS_ATEST_BITS)) + { + GL_State((pStage->stateBits & ~(GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS | GLS_ATEST_BITS)) // remove the shader set values. + | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_ATEST_GT_0); // Now add the default values. + } + else + #endif + GL_State( pStage->stateBits ); // // draw Index: code/renderer/tr_backend.c =================================================================== --- code/renderer/tr_backend.c (revision 1270) +++ code/renderer/tr_backend.c (working copy) @@ -462,11 +462,18 @@ if ( r_fastsky->integer && !( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) { clearBits |= GL_COLOR_BUFFER_BIT; // FIXME: only if sky shaders have been used +#ifdef ELITEFORCE + if(r_origfastsky->integer) + qglClearColor(0.8f, 0.7f, 0.4f, 1.0f); + else + qglClearColor(0.0f, 0.0f, 0.0f, 1.0f); +#else #ifdef _DEBUG qglClearColor( 0.8f, 0.7f, 0.4f, 1.0f ); // FIXME: get color of sky #else qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); // FIXME: get color of sky #endif +#endif } qglClear( clearBits ); Index: code/qcommon/q_math.c =================================================================== --- code/qcommon/q_math.c (revision 1270) +++ code/qcommon/q_math.c (working copy) @@ -1270,7 +1270,11 @@ /* ** find the smallest magnitude axially aligned vector */ +#ifdef ELITEFORCE + for ( pos = 0, i = 2; i >= 0; i-- ) +#else for ( pos = 0, i = 0; i < 3; i++ ) +#endif { if ( fabs( src[i] ) < minelem ) { Index: code/qcommon/surfaceflags.h =================================================================== --- code/qcommon/surfaceflags.h (revision 1270) +++ code/qcommon/surfaceflags.h (working copy) @@ -41,6 +41,10 @@ #define CONTENTS_PLAYERCLIP 0x10000 #define CONTENTS_MONSTERCLIP 0x20000 +#ifdef ELITEFORCE +#define CONTENTS_SHOTCLIP 0x40000 +#endif + //bot specific contents types #define CONTENTS_TELEPORTER 0x40000 #define CONTENTS_JUMPPAD 0x80000 Index: code/qcommon/cm_local.h =================================================================== --- code/qcommon/cm_local.h (revision 1270) +++ code/qcommon/cm_local.h (working copy) @@ -24,11 +24,16 @@ #include "qcommon.h" #include "cm_polylib.h" -#define MAX_SUBMODELS 256 -#define BOX_MODEL_HANDLE 255 -#define CAPSULE_MODEL_HANDLE 254 +#ifdef ELITEFORCE + #define MAX_SUBMODELS 8192 + #define BOX_MODEL_HANDLE MAX_SUBMODELS-1 + #define CAPSULE_MODEL_HANDLE BOX_MODEL_HANDLE-2 +#else + #define MAX_SUBMODELS 256 + #define BOX_MODEL_HANDLE 255 + #define CAPSULE_MODEL_HANDLE 254 +#endif - typedef struct { cplane_t *plane; int children[2]; // negative numbers are leafs Index: code/qcommon/net_chan.c =================================================================== --- code/qcommon/net_chan.c (revision 1270) +++ code/qcommon/net_chan.c (working copy) @@ -94,7 +94,7 @@ } // TTimo: unused, commenting out to make gcc happy -#if 0 +#ifdef ELITEFORCE /* ============== Netchan_ScramblePacket @@ -109,7 +109,7 @@ int i, j, c, mask, temp; int seq[MAX_PACKETLEN]; - seed = ( LittleLong( *(unsigned *)buf->data ) * 3 ) ^ ( buf->cursize * 123 ); + seed = ( LittleLong( *(unsigned *)buf->data ) * 3 ) ^ ( buf->cursize * 123 ) ^ 0x87243987; c = buf->cursize; if ( c <= SCRAMBLE_START ) { return; @@ -120,10 +120,15 @@ // generate a sequence of "random" numbers for (i = 0 ; i < c ; i++) { - seed = (119 * seed + 1); + seed = (69069 * seed + 1); seq[i] = seed; } + // byte xor the data after the header + for (i = SCRAMBLE_START ; i < c ; i++) { + buf->data[i] ^= seq[i]; + } + // transpose each character for ( mask = 1 ; mask < c-SCRAMBLE_START ; mask = ( mask << 1 ) + 1 ) { } @@ -136,9 +141,9 @@ } // byte xor the data after the header - for (i = SCRAMBLE_START ; i < c ; i++) { - buf->data[i] ^= seq[i]; - } +// for (i = SCRAMBLE_START ; i < c ; i++) { +// buf->data[i] ^= seq[i]; +// } } static void Netchan_UnScramblePacket( msg_t *buf ) { @@ -146,7 +151,7 @@ int i, j, c, mask, temp; int seq[MAX_PACKETLEN]; - seed = ( LittleLong( *(unsigned *)buf->data ) * 3 ) ^ ( buf->cursize * 123 ); + seed = ( LittleLong( *(unsigned *)buf->data ) * 3 ) ^ ( buf->cursize * 123 ) ^ 0x87243987; c = buf->cursize; if ( c <= SCRAMBLE_START ) { return; @@ -157,14 +162,14 @@ // generate a sequence of "random" numbers for (i = 0 ; i < c ; i++) { - seed = (119 * seed + 1); + seed = (69069 * seed + 1); seq[i] = seed; } // byte xor the data after the header - for (i = SCRAMBLE_START ; i < c ; i++) { - buf->data[i] ^= seq[i]; - } +// for (i = SCRAMBLE_START ; i < c ; i++) { +// buf->data[i] ^= seq[i]; +// } // transpose each character in reverse order for ( mask = 1 ; mask < c-SCRAMBLE_START ; mask = ( mask << 1 ) + 1 ) { @@ -176,6 +181,12 @@ buf->data[j] = buf->data[i]; buf->data[i] = temp; } + + + // byte xor the data after the header + for (i = SCRAMBLE_START ; i < c ; i++) { + buf->data[i] ^= seq[i]; + } } #endif @@ -211,6 +222,14 @@ MSG_WriteShort( &send, fragmentLength ); MSG_WriteData( &send, chan->unsentBuffer + chan->unsentFragmentStart, fragmentLength ); + #ifdef ELITEFORCE + if(chan->compat) + { + // the original eliteforce uses the old scrambling routines only slightly modified. + Netchan_ScramblePacket( &send ); + } + #endif + // send the datagram NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress ); @@ -277,6 +296,14 @@ MSG_WriteData( &send, data, length ); + #ifdef ELITEFORCE + if(chan->compat) + { + // the original eliteforce uses the old scrambling routines only slightly modified. + Netchan_ScramblePacket( &send ); + } + #endif + // send the datagram NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress ); @@ -308,7 +335,10 @@ qboolean fragmented; // XOR unscramble all data in the packet after the header -// Netchan_UnScramblePacket( msg ); +#ifdef ELITEFORCE + if(chan->compat) + Netchan_UnScramblePacket( msg ); +#endif // get sequence numbers MSG_BeginReadingOOB( msg ); @@ -444,7 +474,10 @@ // TTimo // clients were not acking fragmented messages - chan->incomingSequence = sequence; + #ifdef ELITEFORCE + if(!chan->compat) + #endif + chan->incomingSequence = sequence; return qtrue; } Index: code/qcommon/cm_patch.h =================================================================== --- code/qcommon/cm_patch.h (revision 1270) +++ code/qcommon/cm_patch.h (working copy) @@ -60,8 +60,8 @@ */ -#define MAX_FACETS 1024 -#define MAX_PATCH_PLANES 2048 +#define MAX_FACETS 4096 +#define MAX_PATCH_PLANES 8192 typedef struct { float plane[4]; Index: code/qcommon/q_shared.h =================================================================== --- code/qcommon/q_shared.h (revision 1270) +++ code/qcommon/q_shared.h (working copy) @@ -26,8 +26,13 @@ // q_shared.h -- included first by ALL program modules. // A user mod should never modify this file +#ifdef ELITEFORCE +#define PRODUCT_NAME "ioST:V HM" +#define PRODUCT_VERSION "1.38" +#else #define PRODUCT_NAME "ioq3" #define PRODUCT_VERSION "1.35" +#endif #ifdef SVN_VERSION # define Q3_VERSION PRODUCT_NAME " " SVN_VERSION @@ -35,11 +40,20 @@ # define Q3_VERSION PRODUCT_NAME " " PRODUCT_VERSION #endif +#ifdef ELITEFORCE +#define CLIENT_WINDOW_TITLE "iostvoyHM" +#define CLIENT_WINDOW_MIN_TITLE "iostvoyHM" +#else #define CLIENT_WINDOW_TITLE "ioquake3" #define CLIENT_WINDOW_MIN_TITLE "ioq3" +#endif // 1.32 released 7-10-2002 +#ifdef ELITEFORCE +#define BASEGAME "baseEF" +#else #define BASEGAME "baseq3" +#endif #define MAX_TEAMNAME 32 @@ -948,7 +962,11 @@ #define MAX_POWERUPS 16 #define MAX_WEAPONS 16 +#ifdef ELITEFORCE +#define MAX_PS_EVENTS 4 +#else #define MAX_PS_EVENTS 2 +#endif #define PS_PMOVEFRAMECOUNTBITS 6 @@ -972,6 +990,11 @@ vec3_t origin; vec3_t velocity; int weaponTime; +#ifdef ELITEFORCE + int rechargeTime; // for the phaser + short useTime; // use debounce + int introTime; // for the holodoor +#endif int gravity; int speed; int delta_angles[3]; // add to command angles to get view direction @@ -990,7 +1013,9 @@ // when at rest, the value will remain unchanged // used to twist the legs during strafing +#ifndef ELITEFORCE vec3_t grapplePoint; // location of grapple to pull towards if PMF_GRAPPLE_PULL +#endif int eFlags; // copied to entityState_t->eFlags @@ -1014,20 +1039,27 @@ int damageYaw; int damagePitch; int damageCount; +#ifdef ELITEFORCE + int damageShieldCount; +#endif int stats[MAX_STATS]; int persistant[MAX_PERSISTANT]; // stats that aren't cleared on death int powerups[MAX_POWERUPS]; // level.time that the powerup runs out int ammo[MAX_WEAPONS]; +#ifndef ELITEFORCE int generic1; int loopSound; int jumppad_ent; // jumppad entity hit this frame +#endif // not communicated over the net at all int ping; // server to game info for scoreboard +#ifndef ELITEFORCE int pmove_framecount; // FIXME: don't transmit over the network int jumppad_frame; +#endif int entityEventSequence; } playerState_t; @@ -1062,14 +1094,23 @@ // then BUTTON_WALKING should be set // usercmd_t is sent to the server each client frame +#ifdef ELITEFORCE typedef struct usercmd_s { + int serverTime; + byte buttons; + byte weapon; + int angles[3]; + signed char forwardmove, rightmove, upmove; +} usercmd_t; +#else +typedef struct usercmd_s { int serverTime; int angles[3]; int buttons; byte weapon; // weapon signed char forwardmove, rightmove, upmove; } usercmd_t; - +#endif //=================================================================== // if entityState->solid == SOLID_BMODEL, modelindex is an inline model number @@ -1140,7 +1181,9 @@ int legsAnim; // mask off ANIM_TOGGLEBIT int torsoAnim; // mask off ANIM_TOGGLEBIT +#ifndef ELITEFORCE int generic1; +#endif } entityState_t; typedef enum { Index: code/qcommon/files.c =================================================================== --- code/qcommon/files.c (revision 1270) +++ code/qcommon/files.c (working copy) @@ -174,6 +174,15 @@ // every time a new demo pk3 file is built, this checksum must be updated. // the easiest way to get it is to just run the game and see what it spits out +#ifdef ELITEFORCE +static const unsigned pak_checksums[] = +{ + 3376297517u, + 596947475u, + 3960871590u, + 1592359207u, +}; +#else #define DEMO_PAK0_CHECKSUM 2985612116u static const unsigned pak_checksums[] = { 1566731103u, @@ -186,6 +195,7 @@ 908855077u, 977125798u }; +#endif // if this is defined, the executable positively won't work with any paks other // than the demo pak, even if productid is present. This is only used for our @@ -2532,9 +2542,14 @@ havepak = qfalse; // never autodownload any of the id paks +#ifdef ELITEFORCE + if(FS_idPak(fs_serverReferencedPakNames[i], BASEGAME)) + continue; +#else if ( FS_idPak(fs_serverReferencedPakNames[i], BASEGAME) || FS_idPak(fs_serverReferencedPakNames[i], "missionpack") ) { continue; } +#endif // Make sure the server cannot make us write to non-quake3 directories. if(FS_CheckDirTraversal(fs_serverReferencedPakNames[i])) @@ -2763,6 +2778,7 @@ } Com_ReadCDKey(BASEGAME); + fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); if (fs && fs->string[0] != 0) { Com_AppendCDKey( fs->string ); @@ -2793,6 +2809,7 @@ Com_Printf( "%d files in pk3 files\n", fs_packFiles ); } +#ifdef ELITEFORCE /* =================== FS_CheckPak0 @@ -2802,9 +2819,87 @@ Q3 media pak0.pk3, you'll want to remove this function =================== */ + static void FS_CheckPak0( void ) { searchpath_t *path; + unsigned foundPak = 0; + + for( path = fs_searchpaths; path; path = path->next ) + { + const char* pakBasename = path->pack->pakBasename; + + if(!path->pack) + continue; + + if(!Q_stricmpn(path->pack->pakGamename, BASEGAME, MAX_OSPATH) + && strlen(pakBasename) == 4 && !Q_stricmpn(pakBasename, "pak", 3) + && pakBasename[3] >= '0' && pakBasename[3] <= '4') + { + if(path->pack->checksum != pak_checksums[pakBasename[3]-'0']) + { + if(pakBasename[0] == '0') + { + Com_Printf("\n\n" + "**************************************************\n" + "WARNING: pak0.pk3 is present but its checksum (%u)\n" + "is not correct. Please re-copy pak0.pk3 from your\n" + "legitimate EF CDROM.\n" + "**************************************************\n\n\n", + path->pack->checksum); + } + else + { + Com_Printf("\n\n" + "**************************************************\n" + "WARNING: pak%d.pk3 is present but its checksum (%u)\n" + "is not correct. Please copy the .pk3 files from\n" + "Elite Force patches 1.1 and 1.2 to the baseEF directory.\n" + "**************************************************\n\n\n", + pakBasename[3]-'0', path->pack->checksum ); + } + } + + foundPak |= 1<<(pakBasename[3]-'0'); + } + } + + if((foundPak & 0x07) != 0x07) + { + if(!(foundPak & 1)) + { + Com_Printf("\n\n" + "pak0.pk3 is missing. Please copy it\n" + "from your legitimate EliteForce CDROM.\n"); + } + + if((foundPak & 0x06) != 0x06) + { + Com_Printf("\n\n" + "Patch files are missing. Please\n" + "copy the .pk3 files from EliteForce patch 1.2 to baseEF.\n"); + } + Com_Printf("\n\n" + "Also check that your EliteForce executable is in\n" + "the correct place and that every file\n" + "in the %s directory is present and readable.\n", BASEGAME); + } +} + +#else +/* +=================== +FS_CheckPak0 + +Checks that pak0.pk3 is present and its checksum is correct +Note: If you're building a game that doesn't depend on the +Q3 media pak0.pk3, you'll want to remove this function +=================== +*/ + +static void FS_CheckPak0( void ) +{ + searchpath_t *path; qboolean founddemo = qfalse; unsigned foundPak = 0; @@ -2819,7 +2914,6 @@ && !Q_stricmpn( pakBasename, "pak0", MAX_OSPATH )) { founddemo = qtrue; - if( path->pack->checksum == DEMO_PAK0_CHECKSUM ) { Com_Printf( "\n\n" @@ -2839,6 +2933,7 @@ { if(pakBasename[0] == '0') { + Com_Printf("\n\n" "**************************************************\n" "WARNING: pak0.pk3 is present but its checksum (%u)\n" @@ -2877,7 +2972,6 @@ "Point Release files are missing. Please\n" "re-install the 1.32 point release.\n"); } - Com_Printf("\n\n" "Also check that your Q3 executable is in\n" "the correct place and that every file\n" @@ -2889,6 +2983,7 @@ Com_Error(ERR_FATAL, "You need to install Quake III Arena in order to play"); } } +#endif /* ===================== @@ -3437,4 +3532,5 @@ callback( filename ); } FS_FreeFileList( filenames ); + } Index: code/qcommon/cm_trace.c =================================================================== --- code/qcommon/cm_trace.c (revision 1270) +++ code/qcommon/cm_trace.c (working copy) @@ -1158,6 +1158,12 @@ // fill in a default trace Com_Memset( &tw, 0, sizeof(tw) ); tw.trace.fraction = 1; // assume it goes the entire distance until shown otherwise +#ifdef ELITEFORCE + // obviously Raven fucked this up. They seem to expect a SURF_NOIMPACT flag if the trace + // went through to the end, or the game will crash when firing the dreadnought weapon and + // it doesn't hit anything. + tw.trace.surfaceFlags = SURF_NOIMPACT; +#endif VectorCopy(origin, tw.modelOrigin); if (!cm.numNodes) { Index: code/qcommon/qcommon.h =================================================================== --- code/qcommon/qcommon.h (revision 1270) +++ code/qcommon/qcommon.h (working copy) @@ -23,6 +23,10 @@ #ifndef _QCOMMON_H_ #define _QCOMMON_H_ +#if defined(ELITEFORCE) && defined(MISSIONPACK) + #undef MISSIONPACK +#endif + #include "../qcommon/cm_public.h" //Ignore __attribute__ on non-gcc platforms @@ -43,6 +47,9 @@ qboolean allowoverflow; // if false, do a Com_Error qboolean overflowed; // set to true if the buffer size failed (with allowoverflow set) qboolean oob; // set to true if the buffer size failed (with allowoverflow set) +#ifdef ELITEFORCE + qboolean compat; // Compatibility mode for old EliteForce servers. +#endif byte *data; int maxsize; int cursize; @@ -204,6 +211,10 @@ int unsentFragmentStart; int unsentLength; byte unsentBuffer[MAX_MSGLEN]; + +#ifdef ELITEFORCE + qboolean compat; +#endif } netchan_t; void Netchan_Init( int qport ); @@ -223,23 +234,45 @@ ============================================================== */ +#ifdef ELITEFORCE +#define EFPROTOCOL_VERSION 24 +#define PROTOCOL_VERSION 25 +#else #define PROTOCOL_VERSION 68 +#endif // 1.31 - 67 // maintain a list of compatible protocols for demo playing // NOTE: that stuff only works with two digits protocols extern int demo_protocols[]; -#define UPDATE_SERVER_NAME "update.quake3arena.com" // override on command line, config files etc. -#ifndef MASTER_SERVER_NAME -#define MASTER_SERVER_NAME "master.quake3arena.com" +#ifdef ELITEFORCE + + #define UPDATE_SERVER_NAME "motd.stef1.ravensoft.com" + #ifndef MASTER_SERVER_NAME + #define MASTER_SERVER_NAME "master.stef1.ravensoft.com" + #endif + #ifndef AUTHORIZE_SERVER_NAME + #define AUTHORIZE_SERVER_NAME "authenticate.stef1.ravensoft.com" + #endif + + #define PORT_MASTER 27953 + +#else + + #define UPDATE_SERVER_NAME "update.quake3arena.com" + #ifndef MASTER_SERVER_NAME + #define MASTER_SERVER_NAME "master.quake3arena.com" + #endif + #ifndef AUTHORIZE_SERVER_NAME + #define AUTHORIZE_SERVER_NAME "authorize.quake3arena.com" + #endif + + #define PORT_MASTER 27950 + #endif -#ifndef AUTHORIZE_SERVER_NAME -#define AUTHORIZE_SERVER_NAME "authorize.quake3arena.com" -#endif -#define PORT_MASTER 27950 #define PORT_UPDATE 27951 #ifndef PORT_AUTHORIZE #define PORT_AUTHORIZE 27952 @@ -532,14 +565,22 @@ #define FS_CGAME_REF 0x04 #define FS_QAGAME_REF 0x08 // number of id paks that will never be autodownloaded from baseq3 +#ifdef ELITEFORCE +#define NUM_ID_PAKS 4 +#else #define NUM_ID_PAKS 9 +#endif #define MAX_FILE_HANDLES 64 -#ifdef DEDICATED -# define Q3CONFIG_CFG "q3config_server.cfg" +#ifdef ELITEFORCE + #define Q3CONFIG_CFG "hmconfig.cfg" #else -# define Q3CONFIG_CFG "q3config.cfg" + #ifdef DEDICATED + # define Q3CONFIG_CFG "q3config_server.cfg" + #else + # define Q3CONFIG_CFG "q3config.cfg" + #endif #endif qboolean FS_Initialized( void ); Index: code/qcommon/msg.c =================================================================== --- code/qcommon/msg.c (revision 1270) +++ code/qcommon/msg.c (working copy) @@ -103,6 +103,15 @@ int overflows; +#ifdef ELITEFORCE +// Apparently, eliteforce only sends stuff like strings on 1 byte boundaries. +void MSG_RoundBits(msg_t *msg) +{ + if(msg->bit & 0x07) + msg->bit = ++msg->readcount << 3; +} +#endif + // negative bit values include signs void MSG_WriteBits( msg_t *msg, int value, int bits ) { int i; @@ -140,23 +149,58 @@ bits = -bits; } if (msg->oob) { - if (bits==8) { - msg->data[msg->cursize] = value; - msg->cursize += 1; - msg->bit += 8; - } else if (bits==16) { - unsigned short *sp = (unsigned short *)&msg->data[msg->cursize]; - *sp = LittleShort(value); - msg->cursize += 2; - msg->bit += 16; - } else if (bits==32) { - unsigned int *ip = (unsigned int *)&msg->data[msg->cursize]; - *ip = LittleLong(value); - msg->cursize += 4; - msg->bit += 32; - } else { - Com_Error(ERR_DROP, "can't read %d bits\n", bits); + #ifdef ELITEFORCE + if(msg->compat) + { + int write, leftover, nbits = bits, location; + + // make sure to set all non-used space in value to zero. + if(bits < 32) + value &= ((1 << bits) - 1); + + while(nbits) + { + leftover = msg->bit & 0x07; + write = 8 - leftover; + location = msg->bit >> 3; + + if(write > nbits) + write = nbits; + + msg->data[location] &= (1 << leftover) - 1; + msg->data[location] |= (value & ((1 << write) - 1)) << (leftover); + nbits -= write; + value >>= write; + msg->bit += write; + } + + msg->cursize = (msg->bit >> 3) + ((msg->bit & 0x07) ? 1 : 0); } + else + { + #endif + if (bits==8) { + msg->data[msg->cursize] = value; + msg->cursize += 1; + msg->bit += 8; + } else if (bits==16) { + unsigned short *sp = (unsigned short *)&msg->data[msg->cursize]; + *sp = LittleShort(value); + msg->cursize += 2; + msg->bit += 16; + } else if (bits==32) { + unsigned int *ip = (unsigned int *)&msg->data[msg->cursize]; + *ip = LittleLong(value); + msg->cursize += 4; + msg->bit += 32; + } + else + { + Com_Error(ERR_DROP, "can't write %d bits\n", bits); + } + #ifdef ELITEFORCE + } + #endif } else { // fp = fopen("c:\\netchan.bin", "a"); value &= (0xffffffff>>(32-bits)); @@ -198,23 +242,51 @@ } if (msg->oob) { - if (bits==8) { - value = msg->data[msg->readcount]; - msg->readcount += 1; - msg->bit += 8; - } else if (bits==16) { - unsigned short *sp = (unsigned short *)&msg->data[msg->readcount]; - value = LittleShort(*sp); - msg->readcount += 2; - msg->bit += 16; - } else if (bits==32) { - unsigned int *ip = (unsigned int *)&msg->data[msg->readcount]; - value = LittleLong(*ip); - msg->readcount += 4; - msg->bit += 32; - } else { - Com_Error(ERR_DROP, "can't read %d bits\n", bits); + #ifdef ELITEFORCE + if(msg->compat) + { + nbits = 0; + + while(nbits < bits) + { + i = msg->bit & 0x07; + get = 8 - i; + + if(get > bits - nbits) + get = bits - nbits; + + value |= ((msg->data[msg->bit >> 3] >> i) & ((1 << get) - 1)) << nbits; + msg->bit += get; + nbits += get; + } + + msg->readcount = (msg->bit >> 3) + ((msg->bit & 0x07) ? 1 : 0); } + else + { + #endif + if (bits==8) { + value = msg->data[msg->readcount]; + msg->readcount += 1; + msg->bit += 8; + } else if (bits==16) { + unsigned short *sp = (unsigned short *)&msg->data[msg->readcount]; + value = LittleShort(*sp); + msg->readcount += 2; + msg->bit += 16; + } else if (bits==32) { + unsigned int *ip = (unsigned int *)&msg->data[msg->readcount]; + value = LittleLong(*ip); + msg->readcount += 4; + msg->bit += 32; + } + else + { + Com_Error(ERR_DROP, "can't read %d bits\n", bits); + } + #ifdef ELITEFORCE + } + #endif } else { nbits = 0; if (bits&7) { @@ -245,7 +317,6 @@ } - //================================================================================ // @@ -272,6 +343,16 @@ void MSG_WriteData( msg_t *buf, const void *data, int length ) { int i; + +#ifdef ELITEFORCE + if(buf->compat) + { + // Start writing on a whole-byte boundary + if(buf->bit & 0x07) + buf->bit = ++buf->cursize << 3; + } +#endif + for(i=0;i 127 ) { string[i] = '.'; } } +#endif MSG_WriteData (sb, string, l+1); } @@ -330,7 +417,11 @@ if ( !s ) { MSG_WriteData (sb, "", 1); } else { +#ifdef ELITEFORCE + int l; +#else int l,i; +#endif char string[BIG_INFO_STRING]; l = strlen( s ); @@ -341,12 +432,14 @@ } Q_strncpyz( string, s, sizeof( string ) ); +#ifndef ELITEFORCE // get rid of 0xff chars, because old clients don't like them for ( i = 0 ; i < l ; i++ ) { if ( ((byte *)string)[i] > 127 ) { string[i] = '.'; } } +#endif MSG_WriteData (sb, string, l+1); } @@ -430,6 +523,11 @@ static char string[MAX_STRING_CHARS]; int l,c; +#ifdef ELITEFORCE + if(msg->compat) + MSG_RoundBits(msg); +#endif + l = 0; do { c = MSG_ReadByte(msg); // use ReadByte so -1 is out of bounds @@ -440,11 +538,12 @@ if ( c == '%' ) { c = '.'; } +#ifndef ELITEFORCE // don't allow higher ascii values if ( c > 127 ) { c = '.'; } - +#endif string[l] = c; l++; } while (l < sizeof(string)-1); @@ -458,6 +557,12 @@ static char string[BIG_INFO_STRING]; int l,c; +#ifdef ELITEFORCE + if(msg->compat) + MSG_RoundBits(msg); +#endif + + l = 0; do { c = MSG_ReadByte(msg); // use ReadByte so -1 is out of bounds @@ -468,10 +573,12 @@ if ( c == '%' ) { c = '.'; } +#ifndef ELITEFORCE // don't allow higher ascii values if ( c > 127 ) { c = '.'; } +#endif string[l] = c; l++; @@ -486,6 +593,11 @@ static char string[MAX_STRING_CHARS]; int l,c; +#ifdef ELITEFORCE + if(msg->compat) + MSG_RoundBits(msg); +#endif + l = 0; do { c = MSG_ReadByte(msg); // use ReadByte so -1 is out of bounds @@ -496,11 +608,12 @@ if ( c == '%' ) { c = '.'; } +#ifndef ELITEFORCE // don't allow higher ascii values if ( c > 127 ) { c = '.'; } - +#endif string[l] = c; l++; } while (l < sizeof(string)-1); @@ -517,6 +630,11 @@ void MSG_ReadData( msg_t *msg, void *data, int len ) { int i; +#ifdef ELITEFORCE + if(msg->compat) + MSG_RoundBits(msg); +#endif + for (i=0 ; iforwardmove, to->forwardmove, 8 ); MSG_WriteDelta( msg, from->rightmove, to->rightmove, 8 ); MSG_WriteDelta( msg, from->upmove, to->upmove, 8 ); +#ifdef ELITEFORCE + MSG_WriteDelta( msg, from->buttons, to->buttons, 8 ); +#else MSG_WriteDelta( msg, from->buttons, to->buttons, 16 ); +#endif MSG_WriteDelta( msg, from->weapon, to->weapon, 8 ); } @@ -684,7 +806,11 @@ to->forwardmove = MSG_ReadDelta( msg, from->forwardmove, 8); to->rightmove = MSG_ReadDelta( msg, from->rightmove, 8); to->upmove = MSG_ReadDelta( msg, from->upmove, 8); +#ifdef ELITEFORCE + to->buttons = MSG_ReadDelta( msg, from->buttons, 8); +#else to->buttons = MSG_ReadDelta( msg, from->buttons, 16); +#endif to->weapon = MSG_ReadDelta( msg, from->weapon, 8); } @@ -721,7 +847,11 @@ MSG_WriteDeltaKey( msg, key, from->forwardmove, to->forwardmove, 8 ); MSG_WriteDeltaKey( msg, key, from->rightmove, to->rightmove, 8 ); MSG_WriteDeltaKey( msg, key, from->upmove, to->upmove, 8 ); +#ifdef ELITEFORCE + MSG_WriteDeltaKey( msg, key, from->buttons, to->buttons, 8 ); +#else MSG_WriteDeltaKey( msg, key, from->buttons, to->buttons, 16 ); +#endif MSG_WriteDeltaKey( msg, key, from->weapon, to->weapon, 8 ); } @@ -745,7 +875,11 @@ to->forwardmove = MSG_ReadDeltaKey( msg, key, from->forwardmove, 8); to->rightmove = MSG_ReadDeltaKey( msg, key, from->rightmove, 8); to->upmove = MSG_ReadDeltaKey( msg, key, from->upmove, 8); +#ifdef ELITEFORCE + to->buttons = MSG_ReadDeltaKey( msg, key, from->buttons, 8); +#else to->buttons = MSG_ReadDeltaKey( msg, key, from->buttons, 16); +#endif to->weapon = MSG_ReadDeltaKey( msg, key, from->weapon, 8); } else { to->angles[0] = from->angles[0]; @@ -767,6 +901,48 @@ ============================================================================= */ +#ifdef ELITEFORCE +#define PVECTOR_BITS 5 // amount of bits we need to reference all predefined vectors. +#define PVECTOR_BYTES 8 // length of predefined vector. +#define PVECTOR_NUM (1 << PVECTOR_BITS) - 1 // number of existing predefined vectors. + +byte pVectors[PVECTOR_NUM][PVECTOR_BYTES] = +{ + {0x60, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x20, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x40, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x60, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x60, 0x80, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00}, + {0x60, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00}, + {0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xe0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x60, 0xc0, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, + {0x60, 0xc0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00}, + {0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x60, 0xc0, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00}, + {0x60, 0xc0, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00}, + {0x60, 0x80, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00}, + {0x60, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00}, + {0xe1, 0x00, 0xc0, 0x01, 0x90, 0x00, 0x00, 0x00}, + {0xed, 0x07, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00}, + {0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00}, + {0x62, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00}, + {0x02, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00}, + {0xe0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x60, 0x80, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00}, +}; + +#endif + /* ================= MSG_ReportChangeVectors_f @@ -792,6 +968,61 @@ // using the stringizing operator to save typing... #define NETF(x) #x,(size_t)&((entityState_t*)0)->x +#ifdef ELITEFORCE +netField_t entityStateFields[] = +{ +{ NETF(eType), 8 }, +{ NETF(eFlags), 24 }, +{ NETF(pos.trType), 8 }, +{ NETF(pos.trTime), 32 }, +{ NETF(pos.trDuration), 32 }, +{ NETF(pos.trBase[0]), 0 }, +{ NETF(pos.trBase[1]), 0 }, +{ NETF(pos.trBase[2]), 0 }, +{ NETF(pos.trDelta[0]), 0 }, +{ NETF(pos.trDelta[1]), 0 }, +{ NETF(pos.trDelta[2]), 0 }, +{ NETF(apos.trType), 8 }, +{ NETF(apos.trTime), 32 }, +{ NETF(apos.trDuration), 32 }, +{ NETF(apos.trBase[0]), 0 }, +{ NETF(apos.trBase[1]), 0 }, +{ NETF(apos.trBase[2]), 0 }, +{ NETF(apos.trDelta[0]), 0 }, +{ NETF(apos.trDelta[1]), 0 }, +{ NETF(apos.trDelta[2]), 0 }, +{ NETF(time), 32 }, +{ NETF(time2), 32 }, +{ NETF(origin[0]), 0 }, +{ NETF(origin[1]), 0 }, +{ NETF(origin[2]), 0 }, +{ NETF(origin2[0]), 0 }, +{ NETF(origin2[1]), 0 }, +{ NETF(origin2[2]), 0 }, +{ NETF(angles[0]), 0 }, +{ NETF(angles[1]), 0 }, +{ NETF(angles[2]), 0 }, +{ NETF(angles2[0]), 0 }, +{ NETF(angles2[1]), 0 }, +{ NETF(angles2[2]), 0 }, +{ NETF(otherEntityNum), GENTITYNUM_BITS }, +{ NETF(otherEntityNum2), GENTITYNUM_BITS }, +{ NETF(groundEntityNum), GENTITYNUM_BITS }, +{ NETF(loopSound), 16 }, +{ NETF(constantLight), 32 }, +{ NETF(modelindex), 8 }, +{ NETF(modelindex2), 8 }, +{ NETF(frame), 16 }, +{ NETF(clientNum), 8 }, +{ NETF(solid), 24 }, +{ NETF(event), 10 }, +{ NETF(eventParm), 8 }, +{ NETF(powerups), 16 }, +{ NETF(weapon), 8 }, +{ NETF(legsAnim), 8 }, +{ NETF(torsoAnim), 8 }, +}; +#else netField_t entityStateFields[] = { { NETF(pos.trTime), 32 }, @@ -811,7 +1042,7 @@ { NETF(legsAnim), 8 }, { NETF(groundEntityNum), GENTITYNUM_BITS }, { NETF(pos.trType), 8 }, -{ NETF(eFlags), 19 }, +{ NETF(eFlags), 27 }, { NETF(otherEntityNum), GENTITYNUM_BITS }, { NETF(weapon), 8 }, { NETF(clientNum), 8 }, @@ -846,8 +1077,8 @@ { NETF(constantLight), 32 }, { NETF(frame), 16 } }; +#endif - // if (int)f == f and (int)f + ( 1<<(FLOAT_INT_BITS-1) ) < ( 1 << FLOAT_INT_BITS ) // the float will be sent with FLOAT_INT_BITS, otherwise all 32 bits will be sent #define FLOAT_INT_BITS 13 @@ -872,6 +1103,10 @@ int trunc; float fullFloat; int *fromF, *toF; +#ifdef ELITEFORCE + byte vector[PVECTOR_BYTES]; + int vectorIndex = -1; +#endif numFields = sizeof(entityStateFields)/sizeof(entityStateFields[0]); @@ -895,17 +1130,33 @@ Com_Error (ERR_FATAL, "MSG_WriteDeltaEntity: Bad entity number: %i", to->number ); } + #ifdef ELITEFORCE + if(msg->compat) + Com_Memset(vector, 0, sizeof(vector)); + #endif + lc = 0; // build the change vector as bytes so it is endien independent for ( i = 0, field = entityStateFields ; i < numFields ; i++, field++ ) { fromF = (int *)( (byte *)from + field->offset ); toF = (int *)( (byte *)to + field->offset ); if ( *fromF != *toF ) { - lc = i+1; + #ifdef ELITEFORCE + if(msg->compat) + vector[i >> 3] |= 1 << (i & 0x07); + else + #endif + lc = i+1; } } - if ( lc == 0 ) { + #ifdef ELITEFORCE + if((msg->compat && !((int *) vector)[0] && !((int *) vector)[1]) || (!msg->compat && !lc)) + { + #else + if ( lc == 0 ) + { + #endif // nothing at all changed if ( !force ) { return; // nothing at all @@ -917,35 +1168,87 @@ return; } + #ifdef ELITEFORCE + if(msg->compat) + { + for (i = 0; i < PVECTOR_NUM; i++) + { + if( ((int *) vector)[0] == ((int *)pVectors[i])[0] && + ((int *) vector)[1] == ((int *)pVectors[i])[1] + ) + { + vectorIndex = i; + break; + } + } + } + #endif + MSG_WriteBits( msg, to->number, GENTITYNUM_BITS ); MSG_WriteBits( msg, 0, 1 ); // not removed MSG_WriteBits( msg, 1, 1 ); // we have a delta - MSG_WriteByte( msg, lc ); // # of changes +#ifdef ELITEFORCE + if(msg->compat) + { + MSG_WriteBits(msg, vectorIndex, PVECTOR_BITS); + if (vectorIndex < 0) + { + for ( i = 0 ; i + 8 <= numFields ; i += 8 ) + MSG_WriteByte( msg, vector[i >> 3] ); + if ( numFields & 7 ) + MSG_WriteBits( msg, vector[i >> 3], numFields & 7 ); + } + + } + else +#endif + MSG_WriteByte( msg, lc ); // # of changes + oldsize += numFields; +#ifdef ELITEFORCE + for ( i = 0, field = entityStateFields ; msg->compat ? (i < numFields) : (i < lc) ; i++, field++ ) { +#else for ( i = 0, field = entityStateFields ; i < lc ; i++, field++ ) { +#endif fromF = (int *)( (byte *)from + field->offset ); toF = (int *)( (byte *)to + field->offset ); if ( *fromF == *toF ) { - MSG_WriteBits( msg, 0, 1 ); // no change + + #ifdef ELITEFORCE + if(!msg->compat) + #endif + MSG_WriteBits( msg, 0, 1 ); // no change continue; } - MSG_WriteBits( msg, 1, 1 ); // changed + #ifdef ELITEFORCE + if(!msg->compat) + #endif + MSG_WriteBits( msg, 1, 1 ); // changed if ( field->bits == 0 ) { // float fullFloat = *(float *)toF; trunc = (int)fullFloat; - if (fullFloat == 0.0f) { - MSG_WriteBits( msg, 0, 1 ); - oldsize += FLOAT_INT_BITS; +#ifdef ELITEFORCE + if(!msg->compat && fullFloat == 0.0f) +#else + if (fullFloat == 0.0f) +#endif + { + MSG_WriteBits( msg, 0, 1 ); + oldsize += FLOAT_INT_BITS; } else { - MSG_WriteBits( msg, 1, 1 ); + #ifdef ELITEFORCE + if(!msg->compat) + #endif + MSG_WriteBits( msg, 1, 1 ); + if ( trunc == fullFloat && trunc + FLOAT_INT_BIAS >= 0 && trunc + FLOAT_INT_BIAS < ( 1 << FLOAT_INT_BITS ) ) { // send as small integer @@ -958,13 +1261,23 @@ } } } else { - if (*toF == 0) { - MSG_WriteBits( msg, 0, 1 ); - } else { - MSG_WriteBits( msg, 1, 1 ); - // integer - MSG_WriteBits( msg, *toF, field->bits ); + #ifdef ELITEFORCE + if(msg->compat) + MSG_WriteBits(msg, *toF, field->bits); + else + { + #endif + if (*toF == 0) + MSG_WriteBits( msg, 0, 1 ); + else + { + MSG_WriteBits( msg, 1, 1 ); + // integer + MSG_WriteBits( msg, *toF, field->bits ); + } + #ifdef ELITEFORCE } + #endif } } } @@ -985,13 +1298,18 @@ void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to, int number) { - int i, lc; + int i, lc = 0; int numFields; netField_t *field; int *fromF, *toF; int print; int trunc; int startBit, endBit; +#ifdef ELITEFORCE + int vectorIndex; + byte vector_space[PVECTOR_BYTES]; + byte *vector = vector_space; +#endif if ( number < 0 || number >= MAX_GENTITIES) { Com_Error( ERR_DROP, "Bad delta entity number: %i", number ); @@ -1021,7 +1339,10 @@ } numFields = sizeof(entityStateFields)/sizeof(entityStateFields[0]); - lc = MSG_ReadByte(msg); + #ifdef ELITEFORCE + if(!msg->compat) + #endif + lc = MSG_ReadByte(msg); // shownet 2/3 will interleave with other printed info, -1 will // just print the delta records` @@ -1034,19 +1355,48 @@ to->number = number; +#ifdef ELITEFORCE + if(msg->compat) + { + // Read in the vector index and check whether there is a predefined one or not. + vectorIndex = MSG_ReadBits( msg, PVECTOR_BITS ); + + if (vectorIndex == PVECTOR_NUM) + { + for (i = 0; i + 8 < numFields; i += 8) + vector[i >> 3] = MSG_ReadByte(msg); + if (numFields & 7) + vector[i>>3] = MSG_ReadBits( msg, numFields & 7 ); + } + else + vector = pVectors[vectorIndex]; + } + + for ( i = 0, field = entityStateFields ; msg->compat ? (i < numFields) : (i < lc) ; i++, field++ ) { +#else for ( i = 0, field = entityStateFields ; i < lc ; i++, field++ ) { +#endif fromF = (int *)( (byte *)from + field->offset ); toF = (int *)( (byte *)to + field->offset ); - if ( ! MSG_ReadBits( msg, 1 ) ) { + #ifdef ELITEFORCE + if((msg->compat && ! (vector[i >> 3] & (1 << (i & 7)) )) || (!msg->compat && !MSG_ReadBits(msg, 1))) + #else + if ( ! MSG_ReadBits( msg, 1 ) ) + #endif + { // no change *toF = *fromF; } else { if ( field->bits == 0 ) { // float - if ( MSG_ReadBits( msg, 1 ) == 0 ) { +#ifdef ELITEFORCE + if(!msg->compat && !MSG_ReadBits( msg, 1 )) +#else + if ( MSG_ReadBits( msg, 1 ) == 0 ) +#endif *(float *)toF = 0.0f; - } else { + else { if ( MSG_ReadBits( msg, 1 ) == 0 ) { // integral float trunc = MSG_ReadBits( msg, FLOAT_INT_BITS ); @@ -1065,9 +1415,13 @@ } } } else { - if ( MSG_ReadBits( msg, 1 ) == 0 ) { + #ifdef ELITEFORCE + if(!msg->compat && !MSG_ReadBits(msg, 1)) + #else + if ( MSG_ReadBits( msg, 1 ) == 0 ) + #endif *toF = 0; - } else { + else { // integer *toF = MSG_ReadBits( msg, field->bits ); if ( print ) { @@ -1078,12 +1432,19 @@ // pcount[i]++; } } - for ( i = lc, field = &entityStateFields[lc] ; i < numFields ; i++, field++ ) { - fromF = (int *)( (byte *)from + field->offset ); - toF = (int *)( (byte *)to + field->offset ); - // no change - *toF = *fromF; + #ifdef ELITEFORCE + if(!msg->compat) + { + #endif + for ( i = lc, field = &entityStateFields[lc] ; i < numFields ; i++, field++ ) { + fromF = (int *)( (byte *)from + field->offset ); + toF = (int *)( (byte *)to + field->offset ); + // no change + *toF = *fromF; + } + #ifdef ELITEFORCE } + #endif if ( print ) { if ( msg->bit == 0 ) { @@ -1107,6 +1468,59 @@ // using the stringizing operator to save typing... #define PSF(x) #x,(size_t)&((playerState_t*)0)->x +#ifdef ELITEFORCE +netField_t playerStateFields[] = +{ +{ PSF(commandTime), 32 }, +{ PSF(pm_type), 8 }, +{ PSF(bobCycle), 8 }, +{ PSF(pm_flags), 16 }, +{ PSF(pm_time), -16 }, +{ PSF(origin[0]), 0 }, +{ PSF(origin[1]), 0 }, +{ PSF(origin[2]), 0 }, +{ PSF(velocity[0]), 0 }, +{ PSF(velocity[1]), 0 }, +{ PSF(velocity[2]), 0 }, +{ PSF(weaponTime), -16 }, +{ PSF(gravity), 16 }, +{ PSF(speed), 16 }, +{ PSF(delta_angles[0]), 16 }, +{ PSF(delta_angles[1]), 16 }, +{ PSF(delta_angles[2]), 16 }, +{ PSF(groundEntityNum), GENTITYNUM_BITS }, +{ PSF(legsTimer), 8 }, +{ PSF(torsoTimer), 12 }, +{ PSF(legsAnim), 8 }, +{ PSF(torsoAnim), 8 }, +{ PSF(movementDir), 4 }, +{ PSF(eFlags), 16 }, +{ PSF(eventSequence), 16 }, +{ PSF(events[0]), 8 }, +{ PSF(events[1]), 8 }, +{ PSF(events[2]), 8 }, +{ PSF(events[3]), 8 }, +{ PSF(eventParms[0]), 8 }, +{ PSF(eventParms[1]), 8 }, +{ PSF(eventParms[2]), 8 }, +{ PSF(eventParms[3]), 8 }, +{ PSF(externalEvent), 10 }, +{ PSF(externalEventParm), 8 }, +{ PSF(clientNum), 8 }, +{ PSF(weapon), 5 }, +{ PSF(weaponstate), 4 }, +{ PSF(viewangles[0]), 0 }, +{ PSF(viewangles[1]), 0 }, +{ PSF(viewangles[2]), 0 }, +{ PSF(viewheight), -8 }, +{ PSF(damageEvent), 8 }, +{ PSF(damageYaw), 8 }, +{ PSF(damagePitch), 8 }, +{ PSF(damageCount), 8 }, +{ PSF(damageShieldCount), 8 }, +{ PSF(introTime), 32}, +}; +#else netField_t playerStateFields[] = { { PSF(commandTime), 32 }, @@ -1158,7 +1572,7 @@ { PSF(jumppad_ent), 10 }, { PSF(loopSound), 16 } }; - +#endif /* ============= MSG_WriteDeltaPlayerstate @@ -1177,7 +1591,7 @@ netField_t *field; int *fromF, *toF; float fullFloat; - int trunc, lc; + int trunc, lc = 0; if (!from) { from = &dummy; @@ -1188,20 +1602,33 @@ numFields = sizeof( playerStateFields ) / sizeof( playerStateFields[0] ); - lc = 0; - for ( i = 0, field = playerStateFields ; i < numFields ; i++, field++ ) { - fromF = (int *)( (byte *)from + field->offset ); - toF = (int *)( (byte *)to + field->offset ); - if ( *fromF != *toF ) { - lc = i+1; + #ifdef ELITEFORCE + if(!msg->compat) + { + #endif + lc = 0; + for ( i = 0, field = playerStateFields ; i < numFields ; i++, field++ ) { + fromF = (int *)( (byte *)from + field->offset ); + toF = (int *)( (byte *)to + field->offset ); + if ( *fromF != *toF ) { + lc = i+1; + } } + + MSG_WriteByte( msg, lc ); // # of changes + + #ifdef ELITEFORCE } - - MSG_WriteByte( msg, lc ); // # of changes - + #endif + oldsize += numFields - lc; - for ( i = 0, field = playerStateFields ; i < lc ; i++, field++ ) { + #ifdef ELITEFORCE + for ( i = 0, field = playerStateFields ; msg->compat ? (i < numFields) : (i < lc) ; i++, field++ ) + #else + for ( i = 0, field = playerStateFields ; i < lc ; i++, field++ ) + #endif + { fromF = (int *)( (byte *)from + field->offset ); toF = (int *)( (byte *)to + field->offset ); @@ -1264,13 +1691,22 @@ } } - if (!statsbits && !persistantbits && !ammobits && !powerupbits) { + #ifdef ELITEFORCE + if (!msg->compat && !statsbits && !persistantbits && !ammobits && !powerupbits) + #else + if (!statsbits && !persistantbits && !ammobits && !powerupbits) + #endif + { MSG_WriteBits( msg, 0, 1 ); // no change oldsize += 4; return; } - MSG_WriteBits( msg, 1, 1 ); // changed + #ifdef ELITEFORCE + if(!msg->compat) + #endif + MSG_WriteBits( msg, 1, 1 ); // changed + if ( statsbits ) { MSG_WriteBits( msg, 1, 1 ); // changed MSG_WriteBits( msg, statsbits, MAX_STATS ); @@ -1322,7 +1758,7 @@ =================== */ void MSG_ReadDeltaPlayerstate (msg_t *msg, playerState_t *from, playerState_t *to ) { - int i, lc; + int i, lc = 0; int bits; netField_t *field; int numFields; @@ -1354,9 +1790,18 @@ } numFields = sizeof( playerStateFields ) / sizeof( playerStateFields[0] ); - lc = MSG_ReadByte(msg); - for ( i = 0, field = playerStateFields ; i < lc ; i++, field++ ) { + #ifdef ELITEFORCE + if(!msg->compat) + #endif + lc = MSG_ReadByte(msg); + +#ifdef ELITEFORCE + for ( i = 0, field = playerStateFields ; msg->compat ? (i < numFields) : (i < lc) ; i++, field++ ) +#else + for ( i = 0, field = playerStateFields ; i < lc ; i++, field++ ) +#endif + { fromF = (int *)( (byte *)from + field->offset ); toF = (int *)( (byte *)to + field->offset ); @@ -1391,16 +1836,27 @@ } } } - for ( i=lc,field = &playerStateFields[lc];ioffset ); - toF = (int *)( (byte *)to + field->offset ); - // no change - *toF = *fromF; + #ifdef ELITEFORCE + if(!msg->compat) + { + #endif + for ( i=lc,field = &playerStateFields[lc];ioffset ); + toF = (int *)( (byte *)to + field->offset ); + // no change + *toF = *fromF; + } + #ifdef ELITEFORCE } - - + #endif + // read the arrays - if (MSG_ReadBits( msg, 1 ) ) { +#ifdef ELITEFORCE + if(msg->compat || MSG_ReadBits( msg, 1 )) +#else + if (MSG_ReadBits( msg, 1 ) ) +#endif + { // parse stats if ( MSG_ReadBits( msg, 1 ) ) { LOG("PS_STATS"); Index: code/qcommon/qfiles.h =================================================================== --- code/qcommon/qfiles.h (revision 1270) +++ code/qcommon/qfiles.h (working copy) @@ -283,7 +283,7 @@ // If you want to enable support for Raven's .mdr / md4 format, uncomment the next // line. -//#define RAVENMD4 +#define RAVENMD4 #ifdef RAVENMD4 Index: code/qcommon/common.c =================================================================== --- code/qcommon/common.c (revision 1270) +++ code/qcommon/common.c (working copy) @@ -31,15 +31,24 @@ #include #endif +#ifdef ELITEFORCE int demo_protocols[] = +{ 25, 0 }; +#else +int demo_protocols[] = { 66, 67, 68, 0 }; +#endif #define MAX_NUM_ARGVS 50 #define MIN_DEDICATED_COMHUNKMEGS 1 #define MIN_COMHUNKMEGS 56 #define DEF_COMHUNKMEGS 64 +#ifdef ELITEFORCE +#define DEF_COMZONEMEGS 32 +#else #define DEF_COMZONEMEGS 24 +#endif #define XSTRING(x) STRING(x) #define STRING(x) #x #define DEF_COMHUNKMEGS_S XSTRING(DEF_COMHUNKMEGS) @@ -74,6 +83,7 @@ cvar_t *com_blood; cvar_t *com_buildScript; // for automated data building scripts cvar_t *com_introPlayed; +cvar_t *com_novmcompat; // set to 1 to indicate VMs are run by the new engine. cvar_t *cl_paused; cvar_t *sv_paused; cvar_t *cl_packetdelay; @@ -2368,8 +2378,14 @@ fileHandle_t f; char buffer[33]; char fbuffer[MAX_OSPATH]; - +#ifdef ELITEFORCE + int index = 0; + char curchar; + + sprintf(fbuffer, "%s/efq3.key", filename); +#else sprintf(fbuffer, "%s/q3key", filename); +#endif FS_SV_FOpenFileRead( fbuffer, &f ); if ( !f ) { @@ -2379,14 +2395,68 @@ Com_Memset( buffer, 0, sizeof(buffer) ); +#ifdef ELITEFORCE + // check for the normal CD key + while(index < 16) + { + if(FS_Read(&curchar, 1, f) != 1) + { + Q_strncpyz( cl_cdkey, " ", 17 ); + FS_FCloseFile(f); + return; + } + + curchar = toupper(curchar); + + if(curchar < '0' || (curchar > '9' && curchar < 'A') || curchar > 'Z') + continue; + + buffer[index] = toupper(curchar); + + index++; + } + FS_FCloseFile(f); + + // check for the expansion pack cd key + sprintf(fbuffer, "%s/expefq3.key", filename); + FS_SV_FOpenFileRead(fbuffer, &f); + + if(f) + { + while(index < 32) + { + // same game + + if(FS_Read(&curchar, 1, f) != 1) + { + Q_strncpyz( cl_cdkey, " ", 17 ); + FS_FCloseFile(f); + return; + } + + curchar = toupper(curchar); + + if(curchar < '0' || (curchar > '9' && curchar < 'A') || curchar > 'Z') + continue; + + buffer[index] = toupper(curchar); + + index++; + } + FS_FCloseFile(f); + } + + Q_strncpyz(cl_cdkey, buffer, index+1); +#else FS_Read( buffer, 16, f ); FS_FCloseFile( f ); - + if (CL_CDKeyValidate(buffer, NULL)) { Q_strncpyz( cl_cdkey, buffer, 17 ); } else { Q_strncpyz( cl_cdkey, " ", 17 ); } +#endif } /* @@