From 0ccdeddcea3f3d345a37c1a556b162a44b7fecef Mon Sep 17 00:00:00 2001 From: g-cont Date: Mon, 10 Oct 2011 00:00:00 +0400 Subject: [PATCH] 10 Oct 2011 --- change.log | 12 +- cl_dll/cl_dll.plg | 104 ------- cl_dll/hud_spectator.cpp | 2 +- dlls/hl.plg | 9 - engine/client/cl_demo.c | 3 +- engine/client/cl_events.c | 454 +++++++++++++++++++++++++++++ engine/client/cl_frame.c | 23 +- engine/client/cl_game.c | 380 +++--------------------- engine/client/cl_main.c | 48 +++ engine/client/cl_menu.c | 8 +- engine/client/cl_parse.c | 34 +-- engine/client/cl_pmove.c | 2 +- engine/client/cl_tent.c | 109 ++++++- engine/client/client.h | 29 +- engine/client/gl_beams.c | 20 +- engine/client/gl_decals.c | 300 ++++++++++--------- engine/client/gl_image.c | 27 +- engine/client/gl_local.h | 37 ++- engine/client/gl_mirror.c | 2 - engine/client/gl_rmain.c | 4 +- engine/client/gl_rpart.c | 17 +- engine/client/gl_studio.c | 16 +- engine/client/gl_vidnt.c | 3 +- engine/client/s_main.c | 22 +- engine/client/s_mix.c | 5 +- engine/client/s_mouth.c | 49 ++++ engine/client/sound.h | 1 + engine/common/common.h | 5 +- engine/common/con_utils.c | 4 +- engine/common/imagelib/imagelib.h | 2 + engine/common/imagelib/img_bmp.c | 8 +- engine/common/imagelib/img_main.c | 77 ++++- engine/common/imagelib/img_tga.c | 4 +- engine/common/imagelib/img_utils.c | 57 ++-- engine/common/imagelib/img_wad.c | 11 + engine/common/infostring.c | 41 ++- engine/common/mathlib.c | 13 +- engine/common/mathlib.h | 2 +- engine/common/net_encode.c | 3 +- engine/common/network.c | 4 +- engine/common/protocol.h | 6 +- engine/common/titles.c | 7 + engine/engine.dsp | 4 + engine/menu_int.h | 8 +- engine/server/server.h | 2 +- engine/server/sv_client.c | 68 ++++- engine/server/sv_cmds.c | 4 +- engine/server/sv_frame.c | 99 ++++++- engine/server/sv_game.c | 281 +++++++++--------- engine/server/sv_main.c | 9 +- engine/server/sv_phys.c | 1 - engine/server/sv_pmove.c | 6 +- engine/server/sv_world.c | 4 +- mainui/basemenu.cpp | 2 +- mainui/enginecallback.h | 9 +- mainui/menu_vidoptions.cpp | 2 +- 56 files changed, 1508 insertions(+), 955 deletions(-) create mode 100644 engine/client/cl_events.c diff --git a/change.log b/change.log index 1a3cf4ae..7e7d9733 100644 --- a/change.log +++ b/change.log @@ -11,7 +11,7 @@ Render: remove cvar gl_texturebits Render: allow 16-bit color mode when decktop has same Render: rename "vid_gamma" to "gamma", make backward compatibility with GoldSource config Sound: get support for automatic ambient sounds like in Quake -Sound: add cvar "s_combine" that trying to combine mutilpe channels with same sounds into one +Sound: add cvar "s_combine_channels" that trying to combine mutilpe channels with same sounds into one Engine: add "secure" option support for both liblist.gam and gameinfo.txt Engine: fix bug determine current directory Server: fix bug when some sound messages can't be sended to client (e.g. not vised map) @@ -39,7 +39,15 @@ Engine: fix Host_Error issues Network: add IPX and IPX_BROADCAST for backward compatibility with GoldSrc Engine: do revision for 'allow_studio_scale' cvar in trace code GameUI: added support for Steam backgrounds -GameUI: hide input symbols for "password" field in "create server" menu page +GameUI: hide input symbols for "password" field in "create server" menu page +Client: initial implementation of NetAPI +Render: clear decals code. Add transparent rendering for 'glass' decals +GameUI: added new function into menu interface called a pfnProcessImage, added new argument for pfnPIC_Load. +GameUI: fix loading flipped Steam background images +Client: remove gravity for R_Implosion effect +Sound: add SND_MoveMouth16 for support 16-bit sounds lip-sync +Engine: fix potentially crash during parsing titles.txt when file is empty +Engine: increase MAX_VALUE field up to 2048 characters build 1662 diff --git a/cl_dll/cl_dll.plg b/cl_dll/cl_dll.plg index a40ed3b5..f9d4a2cd 100644 --- a/cl_dll/cl_dll.plg +++ b/cl_dll/cl_dll.plg @@ -6,110 +6,6 @@ --------------------Configuration: cl_dll - Win32 Release--------------------

Command Lines

-Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP1399.tmp" with contents -[ -/nologo /MT /W3 /GX /O2 /I "..\utils\vgui\include" /I "..\engine" /I "..\common" /I "..\pm_shared" /I "..\dlls" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "CLIENT_DLL" /D "CLIENT_WEAPONS" /Fp"..\temp\cl_dll\!release/cl_dll.pch" /YX /Fo"..\temp\cl_dll\!release/" /Fd"..\temp\cl_dll\!release/" /FD /c -"D:\Xash3D\src_main\cl_dll\vgui_ServerBrowser.cpp" -] -Creating command line "cl.exe @"C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP1399.tmp"" -Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP139A.tmp" with contents -[ -kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib winmm.lib ../utils/vgui/lib/win32_vc6/vgui.lib wsock32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"..\temp\cl_dll\!release/client.pdb" /machine:I386 /out:"..\temp\cl_dll\!release/client.dll" /implib:"..\temp\cl_dll\!release/client.lib" -"\Xash3D\src_main\temp\cl_dll\!release\crossbow.obj" -"\Xash3D\src_main\temp\cl_dll\!release\crowbar.obj" -"\Xash3D\src_main\temp\cl_dll\!release\egon.obj" -"\Xash3D\src_main\temp\cl_dll\!release\ev_hldm.obj" -"\Xash3D\src_main\temp\cl_dll\!release\gauss.obj" -"\Xash3D\src_main\temp\cl_dll\!release\handgrenade.obj" -"\Xash3D\src_main\temp\cl_dll\!release\hl_baseentity.obj" -"\Xash3D\src_main\temp\cl_dll\!release\hl_events.obj" -"\Xash3D\src_main\temp\cl_dll\!release\hl_objects.obj" -"\Xash3D\src_main\temp\cl_dll\!release\hl_weapons.obj" -"\Xash3D\src_main\temp\cl_dll\!release\hl_wpn_glock.obj" -"\Xash3D\src_main\temp\cl_dll\!release\hornetgun.obj" -"\Xash3D\src_main\temp\cl_dll\!release\mp5.obj" -"\Xash3D\src_main\temp\cl_dll\!release\python.obj" -"\Xash3D\src_main\temp\cl_dll\!release\rpg.obj" -"\Xash3D\src_main\temp\cl_dll\!release\satchel.obj" -"\Xash3D\src_main\temp\cl_dll\!release\shotgun.obj" -"\Xash3D\src_main\temp\cl_dll\!release\squeakgrenade.obj" -"\Xash3D\src_main\temp\cl_dll\!release\tripmine.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_scrollbar2.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_slider2.obj" -"\Xash3D\src_main\temp\cl_dll\!release\voice_banmgr.obj" -"\Xash3D\src_main\temp\cl_dll\!release\voice_status.obj" -"\Xash3D\src_main\temp\cl_dll\!release\ammo.obj" -"\Xash3D\src_main\temp\cl_dll\!release\ammo_secondary.obj" -"\Xash3D\src_main\temp\cl_dll\!release\ammohistory.obj" -"\Xash3D\src_main\temp\cl_dll\!release\battery.obj" -"\Xash3D\src_main\temp\cl_dll\!release\cdll_int.obj" -"\Xash3D\src_main\temp\cl_dll\!release\com_weapons.obj" -"\Xash3D\src_main\temp\cl_dll\!release\death.obj" -"\Xash3D\src_main\temp\cl_dll\!release\demo.obj" -"\Xash3D\src_main\temp\cl_dll\!release\entity.obj" -"\Xash3D\src_main\temp\cl_dll\!release\ev_common.obj" -"\Xash3D\src_main\temp\cl_dll\!release\events.obj" -"\Xash3D\src_main\temp\cl_dll\!release\flashlight.obj" -"\Xash3D\src_main\temp\cl_dll\!release\GameStudioModelRenderer.obj" -"\Xash3D\src_main\temp\cl_dll\!release\geiger.obj" -"\Xash3D\src_main\temp\cl_dll\!release\health.obj" -"\Xash3D\src_main\temp\cl_dll\!release\hud.obj" -"\Xash3D\src_main\temp\cl_dll\!release\hud_msg.obj" -"\Xash3D\src_main\temp\cl_dll\!release\hud_redraw.obj" -"\Xash3D\src_main\temp\cl_dll\!release\hud_servers.obj" -"\Xash3D\src_main\temp\cl_dll\!release\hud_spectator.obj" -"\Xash3D\src_main\temp\cl_dll\!release\hud_update.obj" -"\Xash3D\src_main\temp\cl_dll\!release\in_camera.obj" -"\Xash3D\src_main\temp\cl_dll\!release\input.obj" -"\Xash3D\src_main\temp\cl_dll\!release\inputw32.obj" -"\Xash3D\src_main\temp\cl_dll\!release\menu.obj" -"\Xash3D\src_main\temp\cl_dll\!release\message.obj" -"\Xash3D\src_main\temp\cl_dll\!release\parsemsg.obj" -"\Xash3D\src_main\temp\cl_dll\!release\pm_debug.obj" -"\Xash3D\src_main\temp\cl_dll\!release\pm_math.obj" -"\Xash3D\src_main\temp\cl_dll\!release\pm_shared.obj" -"\Xash3D\src_main\temp\cl_dll\!release\saytext.obj" -"\Xash3D\src_main\temp\cl_dll\!release\status_icons.obj" -"\Xash3D\src_main\temp\cl_dll\!release\statusbar.obj" -"\Xash3D\src_main\temp\cl_dll\!release\studio_util.obj" -"\Xash3D\src_main\temp\cl_dll\!release\StudioModelRenderer.obj" -"\Xash3D\src_main\temp\cl_dll\!release\text_message.obj" -"\Xash3D\src_main\temp\cl_dll\!release\train.obj" -"\Xash3D\src_main\temp\cl_dll\!release\tri.obj" -"\Xash3D\src_main\temp\cl_dll\!release\util.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_checkbutton2.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_ClassMenu.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_ConsolePanel.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_ControlConfigPanel.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_CustomObjects.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_grid.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_helpers.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_int.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_listbox.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_loadtga.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_MOTDWindow.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_SchemeManager.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_ScorePanel.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_ServerBrowser.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_SpectatorPanel.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_TeamFortressViewport.obj" -"\Xash3D\src_main\temp\cl_dll\!release\vgui_teammenu.obj" -"\Xash3D\src_main\temp\cl_dll\!release\view.obj" -] -Creating command line "link.exe @"C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP139A.tmp"" -Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP139B.bat" with contents -[ -@echo off -copy \Xash3D\src_main\temp\cl_dll\!release\client.dll "D:\Xash3D\valve\cl_dlls\client.dll" -] -Creating command line ""C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP139B.bat"" -Compiling... -vgui_ServerBrowser.cpp -Linking... - Creating library ..\temp\cl_dll\!release/client.lib and object ..\temp\cl_dll\!release/client.exp -

Output Window

-Performing Custom Build Step on \Xash3D\src_main\temp\cl_dll\!release\client.dll -‘ª®¯¨à®¢ ­® ä ©«®¢: 1. diff --git a/cl_dll/hud_spectator.cpp b/cl_dll/hud_spectator.cpp index c0509824..0860277e 100644 --- a/cl_dll/hud_spectator.cpp +++ b/cl_dll/hud_spectator.cpp @@ -206,7 +206,7 @@ int UTIL_FindEntityInMap(char * name, float * origin, float * angle) { int n,found = 0; char keyname[256]; - char token[1024]; + char token[2048]; cl_entity_t * pEnt = gEngfuncs.GetEntityByIndex( 0 ); // get world model diff --git a/dlls/hl.plg b/dlls/hl.plg index 67e43085..5b05c94d 100644 --- a/dlls/hl.plg +++ b/dlls/hl.plg @@ -6,15 +6,6 @@ --------------------Configuration: hl - Win32 Release--------------------

Command Lines

-Creating temporary file "C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP614.bat" with contents -[ -@echo off -copy \Xash3D\src_main\temp\dlls\!release\hl.dll "D:\Xash3D\valve\dlls\hl.dll" -] -Creating command line ""C:\DOCUME~1\ÌÈØÀ\LOCALS~1\Temp\RSP614.bat"" -

Output Window

-Performing Custom Build Step on \Xash3D\src_main\temp\dlls\!release\hl.dll -‘ª®¯¨à®¢ ­® ä ©«®¢: 1. diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 41777a11..7cdc5ffa 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -298,7 +298,7 @@ void CL_WriteDemoHeader( const char *name ) cls.lastoutgoingcommand = -1; // we don't have a backed up cmd history yet cls.nextcmdtime = host.realtime; // we can send a cmd right away - // FIXME: current demo implementation is completely wrong + // UNDONE: current demo implementation is completely wrong // it's support only uncompressed demos at he moment Cvar_SetFloat( "cl_nodelta", 1.0f ); } @@ -482,7 +482,6 @@ void CL_StopRecord( void ) cls.demoname[0] = '\0'; menu.globals->demoname[0] = '\0'; - // FIXME: current demo implementation is completely wrong // it's support only uncompressed demos at he moment // enable delta-compression here at end of the demo record Cvar_SetFloat( "cl_nodelta", 0.0f ); diff --git a/engine/client/cl_events.c b/engine/client/cl_events.c new file mode 100644 index 00000000..7c888f40 --- /dev/null +++ b/engine/client/cl_events.c @@ -0,0 +1,454 @@ +/* +cl_events.c - client-side event system implementation +Copyright (C) 2011 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "common.h" +#include "client.h" +#include "event_flags.h" +#include "net_encode.h" + +/* +=============== +CL_ResetEvent + +=============== +*/ +void CL_ResetEvent( event_info_t *ei ) +{ + Q_memset( ei, 0, sizeof( *ei )); +} + +/* +============= +CL_SetEventIndex + +============= +*/ +void CL_SetEventIndex( const char *szEvName, int ev_index ) +{ + cl_user_event_t *ev; + int i; + + if( !szEvName || !*szEvName ) + return; // ignore blank names + + // search event by name to link with + for( i = 0; i < MAX_EVENTS; i++ ) + { + ev = clgame.events[i]; + if( !ev ) break; + + if( !Q_stricmp( ev->name, szEvName )) + { + ev->index = ev_index; + return; + } + } +} + +/* +============= +CL_EventIndex + +============= +*/ +word CL_EventIndex( const char *name ) +{ + int i; + + if( !name || !name[0] ) + return 0; + + for( i = 1; i < MAX_EVENTS && cl.event_precache[i][0]; i++ ) + { + if( !Q_stricmp( cl.event_precache[i], name )) + return i; + } + return 0; +} + +/* +============= +CL_RegisterEvent + +============= +*/ +void CL_RegisterEvent( int lastnum, const char *szEvName, pfnEventHook func ) +{ + cl_user_event_t *ev; + + if( lastnum == MAX_EVENTS ) + { + MsgDev( D_ERROR, "CL_RegisterEvent: MAX_EVENTS hit!\n" ); + return; + } + + // clear existing or allocate new one + if( !clgame.events[lastnum] ) + clgame.events[lastnum] = Mem_Alloc( cls.mempool, sizeof( cl_user_event_t )); + else Q_memset( clgame.events[lastnum], 0, sizeof( cl_user_event_t )); + + ev = clgame.events[lastnum]; + + // NOTE: ev->index will be set later + Q_strncpy( ev->name, szEvName, CS_SIZE ); + ev->func = func; +} + +/* +============= +CL_FireEvent + +============= +*/ +qboolean CL_FireEvent( event_info_t *ei ) +{ + cl_user_event_t *ev; + const char *name; + int i, idx; + + if( !ei || !ei->index ) + return false; + + // get the func pointer + for( i = 0; i < MAX_EVENTS; i++ ) + { + ev = clgame.events[i]; + + if( !ev ) + { + idx = bound( 1, ei->index, MAX_EVENTS ); + MsgDev( D_ERROR, "CL_FireEvent: %s not precached\n", cl.event_precache[idx] ); + break; + } + + if( ev->index == ei->index ) + { + if( ev->func ) + { + ev->func( &ei->args ); + return true; + } + + name = cl.event_precache[ei->index]; + MsgDev( D_ERROR, "CL_FireEvent: %s not hooked\n", name ); + break; + } + } + + return false; +} + +/* +============= +CL_FireEvents + +called right before draw frame +============= +*/ +void CL_FireEvents( void ) +{ + int i; + event_state_t *es; + event_info_t *ei; + qboolean success; + + es = &cl.events; + + for( i = 0; i < MAX_EVENT_QUEUE; i++ ) + { + ei = &es->ei[i]; + + if( ei->index == 0 ) + continue; + + // delayed event! + if( ei->fire_time && ( ei->fire_time > cl.time )) + continue; + + success = CL_FireEvent( ei ); + + // zero out the remaining fields + CL_ResetEvent( ei ); + } +} + +/* +============= +CL_FindEvent + +find first empty event +============= +*/ +event_info_t *CL_FindEmptyEvent( void ) +{ + int i; + event_state_t *es; + event_info_t *ei; + + es = &cl.events; + + // look for first slot where index is != 0 + for( i = 0; i < MAX_EVENT_QUEUE; i++ ) + { + ei = &es->ei[i]; + if( ei->index != 0 ) + continue; + return ei; + } + + // no slots available + return NULL; +} + +/* +============= +CL_FindEvent + +replace only unreliable events +============= +*/ +event_info_t *CL_FindUnreliableEvent( void ) +{ + event_state_t *es; + event_info_t *ei; + int i; + + es = &cl.events; + + for ( i = 0; i < MAX_EVENT_QUEUE; i++ ) + { + ei = &es->ei[i]; + if( ei->index != 0 ) + { + // it's reliable, so skip it + if( ei->flags & FEV_RELIABLE ) + continue; + } + return ei; + } + + // this should never happen + return NULL; +} + +/* +============= +CL_QueueEvent + +============= +*/ +void CL_QueueEvent( int flags, int index, float delay, event_args_t *args ) +{ + qboolean unreliable = (flags & FEV_RELIABLE) ? false : true; + event_info_t *ei; + + // find a normal slot + ei = CL_FindEmptyEvent(); + + if( !ei && unreliable ) + return; + + // okay, so find any old unreliable slot + if( !ei ) + { + ei = CL_FindUnreliableEvent(); + if( !ei ) return; + } + + ei->index = index; + ei->fire_time = delay ? (cl.time + delay) : 0.0f; + ei->flags = flags; + ei->args = *args; +} + +/* +============= +CL_ParseReliableEvent + +============= +*/ +void CL_ParseReliableEvent( sizebuf_t *msg ) +{ + int event_index; + event_args_t nullargs, args; + float delay = 0.0f; + cl_entity_t *pEnt; + + Q_memset( &nullargs, 0, sizeof( nullargs )); + + event_index = BF_ReadUBitLong( msg, MAX_EVENT_BITS ); + + if( BF_ReadOneBit( msg )) + delay = (float)BF_ReadWord( msg ) * (1.0f / 100.0f); + + // reliable events not use delta-compression just null-compression + MSG_ReadDeltaEvent( msg, &nullargs, &args ); + + if(( pEnt = CL_GetEntityByIndex( args.entindex )) != NULL ) + { + if( VectorIsNull( args.origin )) + VectorCopy( pEnt->curstate.origin, args.origin ); + if( VectorIsNull( args.angles )) + VectorCopy( pEnt->curstate.angles, args.angles ); + if( VectorIsNull( args.velocity )) + VectorCopy( pEnt->curstate.velocity, args.velocity ); + } + + CL_QueueEvent( FEV_RELIABLE|FEV_SERVER, event_index, delay, &args ); +} + + +/* +============= +CL_ParseEvent + +============= +*/ +void CL_ParseEvent( sizebuf_t *msg ) +{ + int event_index; + int i, num_events; + int packet_ent; + event_args_t nullargs, args; + qboolean has_update; + entity_state_t *state; + cl_entity_t *pEnt; + float delay; + + Q_memset( &nullargs, 0, sizeof( nullargs )); + + num_events = BF_ReadUBitLong( msg, 5 ); + + // parse events queue + for( i = 0 ; i < num_events; i++ ) + { + event_index = BF_ReadUBitLong( msg, MAX_EVENT_BITS ); + has_update = false; + + if( BF_ReadOneBit( msg )) + { + packet_ent = BF_ReadUBitLong( msg, MAX_ENTITY_BITS ); + + if( BF_ReadOneBit( msg )) + { + MSG_ReadDeltaEvent( msg, &nullargs, &args ); + has_update = true; + } + } + else packet_ent = -1; + + if( packet_ent != -1 ) + state = &cls.packet_entities[(cl.frame.first_entity+packet_ent)%cls.num_client_entities]; + else state = NULL; + + // it's a client. Override some params + if( args.entindex >= 1 && args.entindex <= cl.maxclients ) + { + if(( args.entindex - 1 ) == cl.playernum ) + { + // get the predicted angles + VectorCopy( cl.refdef.cl_viewangles, args.angles ); + VectorCopy( cl.frame.local.client.origin, args.origin ); + VectorCopy( cl.frame.local.client.velocity, args.velocity ); + } + else if( state ) + { + // restore viewangles from angles + args.angles[PITCH] = -state->angles[PITCH] * 3; + args.angles[YAW] = state->angles[YAW]; + args.angles[ROLL] = 0; // no roll + } + } + else if( state ) + { + if( VectorIsNull( args.origin )) + VectorCopy( state->origin, args.origin ); + if( VectorIsNull( args.angles )) + VectorCopy( state->angles, args.angles ); + if( VectorIsNull( args.velocity )) + VectorCopy( state->velocity, args.velocity ); + } + else if(( pEnt = CL_GetEntityByIndex( args.entindex )) != NULL ) + { + if( VectorIsNull( args.origin )) + VectorCopy( pEnt->curstate.origin, args.origin ); + if( VectorIsNull( args.angles )) + VectorCopy( pEnt->curstate.angles, args.angles ); + if( VectorIsNull( args.velocity )) + VectorCopy( pEnt->curstate.velocity, args.velocity ); + } + + if( BF_ReadOneBit( msg )) + delay = (float)BF_ReadWord( msg ) * (1.0f / 100.0f); + else delay = 0.0f; + + // g-cont. should we need find the event with same index? + CL_QueueEvent( 0, event_index, delay, &args ); + } +} + +/* +============= +CL_PlaybackEvent + +============= +*/ +void CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, float delay, float *origin, + float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ) +{ + event_args_t args; + int invokerIndex = 0; + + // first check event for out of bounds + if( eventindex < 1 || eventindex > MAX_EVENTS ) + { + MsgDev( D_ERROR, "CL_PlaybackEvent: invalid eventindex %i\n", eventindex ); + return; + } + // check event for precached + if( !CL_EventIndex( cl.event_precache[eventindex] )) + { + MsgDev( D_ERROR, "CL_PlaybackEvent: event %i was not precached\n", eventindex ); + return; + } + + flags |= FEV_CLIENT; // it's a client event + flags &= ~(FEV_NOTHOST|FEV_HOSTONLY|FEV_GLOBAL); + + if( delay < 0.0f ) delay = 0.0f; // fixup negative delays + invokerIndex = cl.playernum + 1; // only local client can issue client events + + args.flags = 0; + args.entindex = invokerIndex; + +// UNDONE: restore checks when predicting will be done +// if( !angles || VectorIsNull( angles )) + VectorCopy( cl.refdef.cl_viewangles, args.angles ); + +// if( !origin || VectorIsNull( origin )) + VectorCopy( cl.frame.local.client.origin, args.origin ); + + VectorCopy( cl.frame.local.client.velocity, args.velocity ); + args.ducking = cl.frame.local.playerstate.usehull; + + args.fparam1 = fparam1; + args.fparam2 = fparam2; + args.iparam1 = iparam1; + args.iparam2 = iparam2; + args.bparam1 = bparam1; + args.bparam2 = bparam2; + + CL_QueueEvent( flags, eventindex, delay, &args ); +} \ No newline at end of file diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 56d4b802..61a46240 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -881,28 +881,27 @@ void CL_AddEntities( void ) // // sound engine implementation // -qboolean CL_GetEntitySpatialization( int entnum, vec3_t origin, vec3_t velocity ) +qboolean CL_GetEntitySpatialization( int entnum, vec3_t origin ) { cl_entity_t *ent; qboolean valid_origin; + ASSERT( origin != NULL ); + valid_origin = VectorIsNull( origin ) ? false : true; ent = CL_GetEntityByIndex( entnum ); + + // entity is not present on the client but has valid origin if( !ent || !ent->index ) return valid_origin; - // setup origin and velocity - if( origin ) VectorCopy( ent->curstate.origin, origin ); - if( velocity ) VectorCopy( ent->curstate.velocity, velocity ); + // entity is present on the client but out of PVS + if( ent->curstate.messagenum != cl.parsecount ) + return false; - // if a brush model, offset the origin - if( origin && Mod_GetType( ent->curstate.modelindex ) == mod_brush ) - { - vec3_t mins, maxs, midPoint; + // setup origin + VectorAverage( ent->curstate.mins, ent->curstate.maxs, origin ); + VectorAdd( origin, ent->curstate.origin, origin ); - Mod_GetBounds( ent->curstate.modelindex, mins, maxs ); - VectorAverage( mins, maxs, midPoint ); - VectorAdd( origin, midPoint, origin ); - } return true; } diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 3e736abc..fbca4454 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -19,7 +19,6 @@ GNU General Public License for more details. #include "triangleapi.h" #include "r_efx.h" #include "demo_api.h" -#include "event_flags.h" #include "ivoicetweak.h" #include "pm_local.h" #include "cl_tent.h" @@ -1032,314 +1031,6 @@ void CL_LinkUserMessage( char *pszName, const int svc_num, int iSize ) clgame.msg[i].size = iSize; } -static void CL_RegisterEvent( int lastnum, const char *szEvName, pfnEventHook func ) -{ - user_event_t *ev; - - if( lastnum == MAX_EVENTS ) - { - MsgDev( D_ERROR, "CL_RegisterEvent: MAX_EVENTS hit!\n" ); - return; - } - - ev = clgame.events[lastnum]; - - // clear existing or allocate new one - if( ev ) Q_memset( ev, 0, sizeof( *ev )); - else ev = clgame.events[lastnum] = Mem_Alloc( cls.mempool, sizeof( *ev )); - - Q_strncpy( ev->name, szEvName, CS_SIZE ); - ev->func = func; - // ev->index will be set later -} - -void CL_SetEventIndex( const char *szEvName, int ev_index ) -{ - user_event_t *ev; - int i; - - if( !szEvName || !*szEvName ) return; // ignore blank names - - // search event by name to link with - for( i = 0; i < MAX_EVENTS; i++ ) - { - ev = clgame.events[i]; - if( !ev ) break; - - if( !Q_stricmp( ev->name, szEvName )) - { - ev->index = ev_index; - return; - } - } -} - -/* -=============== -CL_ResetEvent - -=============== -*/ -void CL_ResetEvent( event_info_t *ei ) -{ - Q_memset( ei, 0, sizeof( *ei )); -} - -/* -============= -CL_EventIndex - -============= -*/ -word CL_EventIndex( const char *name ) -{ - int i; - - if( !name || !name[0] ) - return 0; - - for( i = 1; i < MAX_EVENTS && cl.event_precache[i][0]; i++ ) - { - if( !Q_stricmp( cl.event_precache[i], name )) - return i; - } - return 0; -} - -/* -============= -CL_FireEvent - -============= -*/ -qboolean CL_FireEvent( event_info_t *ei ) -{ - user_event_t *ev; - const char *name; - int i, idx; - - if( !ei || !ei->index ) - return false; - - // get the func pointer - for( i = 0; i < MAX_EVENTS; i++ ) - { - ev = clgame.events[i]; - if( !ev ) - { - idx = bound( 1, ei->index, MAX_EVENTS ); - MsgDev( D_ERROR, "CL_FireEvent: %s not precached\n", cl.event_precache[idx] ); - break; - } - - if( ev->index == ei->index ) - { - if( ev->func ) - { - ev->func( &ei->args ); - return true; - } - - name = cl.event_precache[ei->index]; - MsgDev( D_ERROR, "CL_FireEvent: %s not hooked\n", name ); - break; - } - } - return false; -} - -/* -============= -CL_FireEvents - -called right before draw frame -============= -*/ -void CL_FireEvents( void ) -{ - int i; - event_state_t *es; - event_info_t *ei; - qboolean success; - - es = &cl.events; - - for( i = 0; i < MAX_EVENT_QUEUE; i++ ) - { - ei = &es->ei[i]; - - if( ei->index == 0 ) - continue; - - if( cls.state == ca_disconnected ) - { - CL_ResetEvent( ei ); - continue; - } - - // delayed event! - if( ei->fire_time && ( ei->fire_time > cl.time )) - continue; - - success = CL_FireEvent( ei ); - - // zero out the remaining fields - CL_ResetEvent( ei ); - } -} - -/* -============= -CL_FindEvent - -find first empty event -============= -*/ -event_info_t *CL_FindEmptyEvent( void ) -{ - int i; - event_state_t *es; - event_info_t *ei; - - es = &cl.events; - - // look for first slot where index is != 0 - for( i = 0; i < MAX_EVENT_QUEUE; i++ ) - { - ei = &es->ei[i]; - if( ei->index != 0 ) - continue; - return ei; - } - - // no slots available - return NULL; -} - -/* -============= -CL_FindEvent - -replace only unreliable events -============= -*/ -event_info_t *CL_FindUnreliableEvent( void ) -{ - int i; - event_state_t *es; - event_info_t *ei; - - es = &cl.events; - for ( i = 0; i < MAX_EVENT_QUEUE; i++ ) - { - ei = &es->ei[i]; - if( ei->index != 0 ) - { - // it's reliable, so skip it - if( ei->flags & FEV_RELIABLE ) - continue; - } - return ei; - } - - // this should never happen - return NULL; -} - -/* -============= -CL_QueueEvent - -============= -*/ -void CL_QueueEvent( int flags, int index, float delay, event_args_t *args ) -{ - qboolean unreliable = (flags & FEV_RELIABLE) ? false : true; - event_info_t *ei; - - // find a normal slot - ei = CL_FindEmptyEvent(); - if( !ei && unreliable ) - { - return; - } - - // okay, so find any old unreliable slot - if( !ei ) - { - ei = CL_FindUnreliableEvent(); - if( !ei ) return; - } - - ei->index = index; - ei->fire_time = delay ? (cl.time + delay) : 0.0f; - ei->flags = flags; - - // copy in args event data - Q_memcpy( &ei->args, args, sizeof( ei->args )); -} - -/* -============= -CL_PlaybackEvent - -============= -*/ -void CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, float delay, float *origin, - float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ) -{ - event_args_t args; - int invokerIndex = 0; - - // first check event for out of bounds - if( eventindex < 1 || eventindex > MAX_EVENTS ) - { - MsgDev( D_ERROR, "CL_PlaybackEvent: invalid eventindex %i\n", eventindex ); - return; - } - // check event for precached - if( !CL_EventIndex( cl.event_precache[eventindex] )) - { - MsgDev( D_ERROR, "CL_PlaybackEvent: event %i was not precached\n", eventindex ); - return; - } - - flags |= FEV_CLIENT; // it's a client event - flags &= ~(FEV_NOTHOST|FEV_HOSTONLY|FEV_GLOBAL); - - if( delay < 0.0f ) delay = 0.0f; // fixup negative delays - - invokerIndex = cl.playernum + 1; // only local client can issue client events - - args.flags = 0; - args.entindex = invokerIndex; - VectorCopy( origin, args.origin ); - VectorCopy( angles, args.angles ); - - args.fparam1 = fparam1; - args.fparam2 = fparam2; - args.iparam1 = iparam1; - args.iparam2 = iparam2; - args.bparam1 = bparam1; - args.bparam2 = bparam2; - - if( flags & FEV_RELIABLE ) - { - args.ducking = 0; - VectorClear( args.velocity ); - } - else if( invokerIndex ) - { - // get up some info from invoker - VectorCopy( cl.data.origin, args.origin ); - VectorCopy( cl.data.viewangles, args.angles ); - VectorCopy( cl.frame.local.playerstate.velocity, args.velocity ); - args.ducking = cl.frame.local.playerstate.usehull; - } - - CL_QueueEvent( flags, eventindex, delay, &args ); -} - void CL_FreeEntity( cl_entity_t *pEdict ) { ASSERT( pEdict ); @@ -2315,34 +2006,26 @@ pfnHookEvent */ static void pfnHookEvent( const char *filename, pfnEventHook pfn ) { - word event_index; char name[64]; - user_event_t *ev; - int i, j; + cl_user_event_t *ev; + int i; // ignore blank names - if( !filename || !*filename ) return; + if( !filename || !*filename ) + return; - for( i = j = 0; i < Q_strlen( filename ); i++ ) - { - if( filename[i] == '\\' ) name[j] = '/'; - else name[j] = filename[i]; - j++; - } - name[j] = '\0'; + Q_strncpy( name, filename, sizeof( name )); + COM_FixSlashes( name ); - event_index = CL_EventIndex( name ); - - // second call can change EventFunc + // find an empty slot for( i = 0; i < MAX_EVENTS; i++ ) { ev = clgame.events[i]; if( !ev ) break; - if( !Q_stricmp( name, ev->name )) + if( !Q_stricmp( name, ev->name ) && ev->func != NULL ) { - if( ev->func != pfn ) - ev->func = pfn; + MsgDev( D_WARN, "CL_HookEvent: %s already hooked!\n" ); return; } } @@ -2376,9 +2059,11 @@ static void pfnKillEvents( int entnum, const char *eventname ) { ei = &es->ei[i]; - if( ei->index != eventIndex || ei->entity_index != entnum ) - continue; - CL_ResetEvent( ei ); + if( ei->index == eventIndex && ei->entity_index == entnum ) + { + CL_ResetEvent( ei ); + break; + } } } @@ -2759,11 +2444,13 @@ PlayerInfo_SetValueForKey */ void PlayerInfo_SetValueForKey( const char *key, const char *value ) { - // TODO: implement + cvar_t *var; - // NOTE: Xash3D doesn't have local userinfo. It build when changed from cvars with flag CVAR_USERINFO. - // should we search for cvar here and change it? - MsgDev( D_INFO, "SetInfo: %s %s\n", key, value ); + var = (cvar_t *)Cvar_FindVar( key ); + if( !var || !(var->flags & CVAR_USERINFO )) + return; + + Cvar_DirectSet( var, value ); } /* @@ -3291,7 +2978,15 @@ NetAPI_InitNetworking */ void NetAPI_Status( net_status_t *status ) { - // TODO: implement + ASSERT( status != NULL ); + + status->connected = NET_IsLocalAddress( cls.netchan.remote_address ) ? false : true; + status->local_address = cls.netchan.remote_address; // FIXME: get local address + status->remote_address = cls.netchan.remote_address; + status->packet_loss = cls.packet_loss / 100; // percent + status->latency = cl.frame.latency; + status->connection_time = cl.time; // FIXME: replace with netchan.first_received + status->rate = cls.netchan.rate; } /* @@ -3349,9 +3044,16 @@ void NetAPI_SendRequest( int context, int request, int flags, double timeout, ne nr->resp.remote_address = *remote_address; nr->flags = flags; - // send request over the net - Q_snprintf( req, sizeof( req ), "netinfo %i %i %i", PROTOCOL_VERSION, context, request ); - Netchan_OutOfBandPrint( NS_CLIENT, nr->resp.remote_address, req ); + if( request == NETAPI_REQUEST_SERVERLIST ) + { + // UNDONE: build request for master-server + } + else + { + // send request over the net + Q_snprintf( req, sizeof( req ), "netinfo %i %i %i", PROTOCOL_VERSION, context, request ); + Netchan_OutOfBandPrint( NS_CLIENT, nr->resp.remote_address, req ); + } } /* @@ -3450,8 +3152,8 @@ NetAPI_SetValueForKey */ void NetAPI_SetValueForKey( char *s, const char *key, const char *value, int maxsize ) { - if( maxsize > MAX_INFO_STRING ) return; - Info_SetValueForKey( s, key, value ); + if( key[0] == '*' ) return; + Info_SetValueForStarKey( s, key, value, maxsize ); } diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index ff20afa3..30070824 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -971,6 +971,49 @@ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg ) UI_AddServerToList( from, s ); } +/* +================= +CL_ParseNETInfoMessage + +Handle a reply from a netinfo +================= +*/ +void CL_ParseNETInfoMessage( netadr_t from, sizebuf_t *msg ) +{ + char *s; + net_request_t *nr; + int context, type; + int i, count = 0; + + context = Q_atoi( Cmd_Argv( 1 )); + type = Q_atoi( Cmd_Argv( 2 )); + s = Cmd_Argv( 3 ); + + // find a request with specified context + for( i = 0; i < MAX_REQUESTS; i++ ) + { + nr = &clgame.net_requests[i]; + + if( nr->resp.context == context && nr->resp.type == type ) + { + if( nr->timeout > host.realtime ) + { + // setup the answer + nr->resp.response = s; + nr->resp.remote_address = from; + nr->resp.error = NET_SUCCESS; + nr->resp.ping = host.realtime - nr->timesend; + nr->pfnFunc( &nr->resp ); + + if(!( nr->flags & FNETAPI_MULTIPLE_RESPONSE )) + Q_memset( nr, 0, sizeof( *nr )); // done + } + else Q_memset( nr, 0, sizeof( *nr )); + return; + } + } +} + //=================================================================== /* @@ -1189,6 +1232,11 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) // server responding to a status broadcast CL_ParseStatusMessage( from, msg ); } + else if( !Q_strcmp( c, "netinfo" )) + { + // server responding to a status broadcast + CL_ParseNETInfoMessage( from, msg ); + } else if( !Q_strcmp( c, "cmd" )) { // remote command from gui front end diff --git a/engine/client/cl_menu.c b/engine/client/cl_menu.c index 3e4a404a..cc2e8ae2 100644 --- a/engine/client/cl_menu.c +++ b/engine/client/cl_menu.c @@ -345,10 +345,9 @@ pfnPIC_Load ========= */ -static HIMAGE pfnPIC_Load( const char *szPicName, const byte *image_buf, long image_size ) +static HIMAGE pfnPIC_Load( const char *szPicName, const byte *image_buf, long image_size, long flags ) { HIMAGE tx; - int flags = TF_IMAGE; if( !szPicName || !*szPicName ) { @@ -356,9 +355,8 @@ static HIMAGE pfnPIC_Load( const char *szPicName, const byte *image_buf, long im return 0; } - // HACKHACK: keep source for gfx\shell\gamma - if( !glConfig.deviceSupportsGamma && Q_stristr( szPicName, "gamma" )) - flags |= TF_KEEP_RGBDATA; + // add default parms to image + flags |= TF_IMAGE; host.decal_loading = true; tx = GL_LoadTexture( szPicName, image_buf, image_size, flags ); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 984fe003..6699a64b 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -16,7 +16,6 @@ GNU General Public License for more details. #include "common.h" #include "client.h" #include "net_encode.h" -#include "event_flags.h" #include "particledef.h" #include "gl_local.h" #include "cl_tent.h" @@ -436,6 +435,12 @@ void CL_ParseStaticDecal( sizebuf_t *msg ) CL_DecalShoot( CL_DecalIndex( decalIndex ), entityIndex, modelIndex, origin, flags ); } +/* +================== +CL_ParseSoundFade + +================== +*/ void CL_ParseSoundFade( sizebuf_t *msg ) { float fadePercent, fadeOutSeconds; @@ -449,31 +454,6 @@ void CL_ParseSoundFade( sizebuf_t *msg ) S_FadeClientVolume( fadePercent, fadeOutSeconds, holdTime, fadeInSeconds ); } -void CL_ParseReliableEvent( sizebuf_t *msg, int flags ) -{ - int event_index; - event_args_t nullargs, args; - float delay; - - Q_memset( &nullargs, 0, sizeof( nullargs )); - event_index = BF_ReadWord( msg ); // read event index - delay = (float)BF_ReadWord( msg ) / 100.0f; // read event delay - MSG_ReadDeltaEvent( msg, &nullargs, &args ); // reliable events not use delta - - CL_QueueEvent( flags, event_index, delay, &args ); -} - -void CL_ParseEvent( sizebuf_t *msg ) -{ - int i, num_events; - - num_events = BF_ReadByte( msg ); - - // parse events queue - for( i = 0 ; i < num_events; i++ ) - CL_ParseReliableEvent( msg, 0 ); -} - void CL_ParseCustomization( sizebuf_t *msg ) { // TODO: ??? @@ -1355,7 +1335,7 @@ void CL_ParseServerMessage( sizebuf_t *msg ) CL_ParseEvent( msg ); break; case svc_event_reliable: - CL_ParseReliableEvent( msg, FEV_RELIABLE ); + CL_ParseReliableEvent( msg ); break; case svc_updateuserinfo: CL_UpdateUserinfo( msg ); diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index 24acd4ab..083b4ec1 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -687,7 +687,7 @@ void CL_PredictMovement( void ) if( cls.demoplayback && viewent ) { // restore viewangles from angles - cl.refdef.cl_viewangles[PITCH] = viewent->angles[PITCH] * 6; // FIXME... + cl.refdef.cl_viewangles[PITCH] = viewent->angles[PITCH] * 6; cl.refdef.cl_viewangles[YAW] = viewent->angles[YAW]; cl.refdef.cl_viewangles[ROLL] = 0; // roll will be computed in view.cpp } diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 58545695..f19d4862 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -23,6 +23,7 @@ GNU General Public License for more details. #include "gl_local.h" #include "studio.h" #include "wadfile.h" // acess decal size +#include "sound.h" /* ============================================================== @@ -228,7 +229,7 @@ void CL_TEntPlaySound( TEMPENTITY *pTemp, float damp ) else pitch = PITCH_NORM; handle = S_RegisterSound( soundname ); - S_StartSound( pTemp->entity.origin, 0, CHAN_AUTO, handle, fvol, ATTN_NORM, pitch, 0 ); + S_StartSound( pTemp->entity.origin, 0, CHAN_STATIC, handle, fvol, ATTN_NORM, pitch, SND_STOP_LOOPING ); } } @@ -1169,7 +1170,6 @@ void CL_Spray( const vec3_t pos, const vec3_t dir, int modelIndex, int count, in // more vertical displacement znoise = noise * 1.5f; - if( znoise > 1 ) znoise = 1; if( Mod_GetType( modelIndex ) == mod_bad ) @@ -1191,6 +1191,7 @@ void CL_Spray( const vec3_t pos, const vec3_t dir, int modelIndex, int count, in pTemp->entity.curstate.rendermode = renderMode; pTemp->entity.curstate.renderfx = kRenderFxNoDissipation; pTemp->entity.curstate.scale = 0.5f; + pTemp->entity.baseline.renderamt = 255; pTemp->flags |= FTENT_FADEOUT|FTENT_SLOWGRAVITY; pTemp->fadeSpeed = 2.0f; @@ -1655,14 +1656,52 @@ void CL_PlayerSprites( int client, int modelIndex, int count, int size ) /* ============== -CL_Sprite_WallPuff +CL_FireField -Create a wallpuff +Makes a field of fire ============== */ -void CL_Sprite_WallPuff( TEMPENTITY *pTemp, float scale ) +void CL_FireField( float *org, int radius, int modelIndex, int count, int flags, float life ) { - // TODO: implement + TEMPENTITY *pTemp; + float radius2; + vec3_t dir, m_vecPos; + int i; + + for( i = 0; i < count; i++ ) + { + dir[0] = Com_RandomFloat( -1.0f, 1.0f ); + dir[1] = Com_RandomFloat( -1.0f, 1.0f ); + + if( flags & TEFIRE_FLAG_PLANAR ) dir[2] = 0.0f; + else dir[2] = Com_RandomFloat( -1.0f, 1.0f ); + VectorNormalize( dir ); + + radius2 = Com_RandomFloat( 0.0f, radius ); + VectorMA( org, -radius2, dir, m_vecPos ); + + pTemp = CL_DefaultSprite( m_vecPos, modelIndex, 0 ); + if( !pTemp ) return; + + if( flags & TEFIRE_FLAG_ALLFLOAT ) + pTemp->entity.baseline.origin[2] = 30; // drift sprite upward + else if( flags & TEFIRE_FLAG_SOMEFLOAT && Com_RandomLong( 0, 1 )) + pTemp->entity.baseline.origin[2] = 30; // drift sprite upward + + if( flags & TEFIRE_FLAG_LOOP ) + { + pTemp->entity.curstate.framerate = 15; + pTemp->flags |= FTENT_SPRANIMATELOOP; + } + + if( flags & TEFIRE_FLAG_ALPHA ) + { + pTemp->entity.curstate.rendermode = kRenderTransTexture; + pTemp->entity.curstate.renderamt = 128; + } + + pTemp->die += life; + } } /* @@ -1674,19 +1713,62 @@ Client version of shotgun shot */ void CL_MultiGunshot( const vec3_t org, const vec3_t dir, const vec3_t noise, int count, int decalCount, int *decalIndices ) { - // TODO: implement + pmtrace_t trace; + vec3_t right, up; + vec3_t vecSrc, vecDir, vecEnd; + int i, j, decalIndex; + + VectorVectors( dir, right, up ); + VectorCopy( org, vecSrc ); + + for( i = 1; i <= count; i++ ) + { + // get circular gaussian spread + float x, y, z; + do { + x = Com_RandomFloat( -0.5f, 0.5f ) + Com_RandomFloat( -0.5f, 0.5f ); + y = Com_RandomFloat( -0.5f, 0.5f ) + Com_RandomFloat( -0.5f, 0.5f ); + z = x * x + y * y; + } while( z > 1.0f ); + + for( j = 0; j < 3; j++ ) + { + vecDir[j] = dir[i] + x * noise[0] * right[j] + y * noise[1] * up[j]; + vecEnd[j] = vecSrc[j] + 2048.0f * vecDir[j]; + } + + trace = PM_PlayerTrace( clgame.pmove, vecSrc, vecEnd, PM_STUDIO_BOX, 2, -1, NULL ); + + // paint decals + if( trace.fraction != 1.0f ) + { + physent_t *pe; + cl_entity_t *e; + + if( trace.ent >= 0 && trace.ent < clgame.pmove->numphysent ) + pe = &clgame.pmove->physents[trace.ent]; + + if( pe && ( pe->solid == SOLID_BSP || pe->movetype == MOVETYPE_PUSHSTEP )) + { + e = CL_GetEntityByIndex( pe->info ); + decalIndex = CL_DecalIndex( decalIndices[Com_RandomLong( 0, decalCount-1 )] ); + CL_DecalShoot( decalIndex, e->index, 0, trace.endpos, 0 ); + } + } + } } /* ============== -CL_FireField +CL_Sprite_WallPuff -Makes a field of fire +Create a wallpuff ============== */ -void CL_FireField( float *org, int radius, int modelIndex, int count, int flags, float life ) +void CL_Sprite_WallPuff( TEMPENTITY *pTemp, float scale ) { - // TODO: implement + // UNDONE: g-cont. i'm dont know what this doing + Msg( "CL_Sprite_WallPuff: %g\n", scale ); } /* @@ -1706,6 +1788,7 @@ void CL_ParseTempEntity( sizebuf_t *msg ) float scale, life, frameRate, vel, random; float brightness, r, g, b; vec3_t pos, pos2, ang; + int decalIndices[1]; // just stub TEMPENTITY *pTemp; cl_entity_t *pEnt; dlight_t *dl; @@ -2152,8 +2235,8 @@ void CL_ParseTempEntity( sizebuf_t *msg ) ang[1] = BF_ReadCoord( &buf ) * 0.01f; ang[2] = 0.0f; count = BF_ReadByte( &buf ); - decalIndex = BF_ReadByte( &buf ); - CL_MultiGunshot( pos, pos2, ang, count, 1, &decalIndex ); + decalIndices[0] = BF_ReadByte( &buf ); + CL_MultiGunshot( pos, pos2, ang, count, 1, decalIndices ); break; case TE_USERTRACER: pos[0] = BF_ReadCoord( &buf ); diff --git a/engine/client/client.h b/engine/client/client.h index 36db622e..95b93e45 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -199,7 +199,7 @@ typedef struct char name[CS_SIZE]; word index; // event index pfnEventHook func; // user-defined function -} user_event_t; +} cl_user_event_t; typedef struct { @@ -239,7 +239,7 @@ typedef struct typedef struct { - int gl_texturenum; // this is a real texnum + int gl_texturenum; // this is a real texnum // scissor test int scissor_x; @@ -381,7 +381,7 @@ typedef struct vec3_t player_maxs[4]; // 4 hulls allowed cl_user_message_t msg[MAX_USER_MESSAGES]; // keep static to avoid fragment memory - user_event_t *events[MAX_EVENTS]; + cl_user_event_t *events[MAX_EVENTS]; string cdtracks[MAX_CDTRACKS]; // 32 cd-tracks read from cdaudio.txt @@ -601,6 +601,20 @@ void CL_DeleteDemo_f( void ); void CL_Record_f( void ); void CL_Stop_f( void ); +// +// cl_events.c +// +void CL_ParseEvent( sizebuf_t *msg ); +void CL_ParseReliableEvent( sizebuf_t *msg ); +void CL_SetEventIndex( const char *szEvName, int ev_index ); +void CL_QueueEvent( int flags, int index, float delay, event_args_t *args ); +void CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, float delay, float *origin, + float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ); +void CL_RegisterEvent( int lastnum, const char *szEvName, pfnEventHook func ); +word CL_EventIndex( const char *name ); +void CL_ResetEvent( event_info_t *ei ); +void CL_FireEvents( void ); + // // cl_game.c // @@ -615,7 +629,6 @@ void CL_FreeEdicts( void ); void CL_ClearWorld( void ); void CL_FreeEntity( cl_entity_t *pEdict ); void CL_CenterPrint( const char *text, float y ); -void CL_SetEventIndex( const char *szEvName, int ev_index ); void CL_TextMessageParse( byte *pMemFile, int fileSize ); client_textmessage_t *CL_TextMessageGet( const char *pName ); int pfnDecalIndexFromName( const char *szDecalName ); @@ -697,8 +710,8 @@ qboolean CL_InitStudioAPI( void ); // void CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta ); qboolean CL_AddVisibleEntity( cl_entity_t *ent, int entityType ); -qboolean CL_GetEntitySpatialization( int ent, vec3_t origin, vec3_t velocity ); void CL_UpdateStudioVars( cl_entity_t *ent, entity_state_t *newstate, qboolean noInterp ); +qboolean CL_GetEntitySpatialization( int entnum, vec3_t origin ); void CL_UpdateEntityFields( cl_entity_t *ent ); qboolean CL_IsPlayerIndex( int idx ); @@ -721,12 +734,6 @@ void CL_ClearEffects( void ); void CL_TestLights( void ); void CL_DecalShoot( int textureIndex, int entityIndex, int modelIndex, float *pos, int flags ); void CL_PlayerDecal( int textureIndex, int entityIndex, float *pos ); -void CL_QueueEvent( int flags, int index, float delay, event_args_t *args ); -void CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, float delay, float *origin, - float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ); -word CL_EventIndex( const char *name ); -void CL_ResetEvent( event_info_t *ei ); -void CL_FireEvents( void ); void CL_InitParticles( void ); void CL_ClearParticles( void ); void CL_FreeParticles( void ); diff --git a/engine/client/gl_beams.c b/engine/client/gl_beams.c index 52569750..8b22d709 100644 --- a/engine/client/gl_beams.c +++ b/engine/client/gl_beams.c @@ -1099,6 +1099,18 @@ qboolean CL_CullBeam( const vec3_t start, const vec3_t end, qboolean pvsOnly ) vec3_t mins, maxs; int i; + // support for custom mirror management + if( RI.currententity != NULL ) + { + // don't reflect this entity in mirrors + if( RI.currententity->curstate.effects & EF_NOREFLECT && RI.params & RP_MIRRORVIEW ) + return true; + + // draw only in mirrors + if( RI.currententity->curstate.effects & EF_REFLECTONLY && !( RI.params & RP_MIRRORVIEW )) + return true; + } + for( i = 0; i < 3; i++ ) { if( start[i] < end[i] ) @@ -1561,7 +1573,10 @@ void CL_DrawBeams( int fTrans ) // all params are stored in cl_entity_t for( i = 0; i < cl.num_custombeams; i++ ) { - cl_entity_t *pBeam = cl_custombeams[i]; + cl_entity_t *pBeam; + + RI.currententity = pBeam = cl_custombeams[i]; + RI.currentmodel = RI.currententity->model; if( fTrans && pBeam->curstate.renderfx & FBEAM_SOLID ) continue; @@ -1573,6 +1588,9 @@ void CL_DrawBeams( int fTrans ) r_stats.c_view_beams_count++; } + RI.currententity = NULL; + RI.currentmodel = NULL; + if( !cl_active_beams ) return; diff --git a/engine/client/gl_decals.c b/engine/client/gl_decals.c index 1a4a5be9..14c0be38 100644 --- a/engine/client/gl_decals.c +++ b/engine/client/gl_decals.c @@ -21,6 +21,7 @@ GNU General Public License for more details. #define DECAL_DISTANCE 4 // too big values produce more clipped polygons #define MAX_DECALCLIPVERT 32 // produced vertexes of fragmented decal #define DECAL_CACHEENTRY 256 // MUST BE POWER OF 2 or code below needs to change! +#define DECAL_TRANSPARENT_THRESHOLD 230 // transparent decals draw with GL_MODULATE // empirically determined constants for minimizing overalpping decals #define MAX_OVERLAP_DECALS 6 @@ -28,87 +29,40 @@ GNU General Public License for more details. #define FLOAT_TO_SHORT( x ) (short)(x * 32) #define SHORT_TO_FLOAT( x ) ((float)x * (1.0f/32.0f)) -typedef struct -{ - vec3_t m_vPos; - vec2_t m_tCoords; // these are the texcoords for the decal itself - vec2_t m_LMCoords; // lightmap texcoords for the decal. -} decalvert_t; - -typedef struct -{ - qboolean (*pfnInside)( decalvert_t *pVert ); - float (*pfnClip)( decalvert_t *one, decalvert_t *two ); -} decal_clip_t; - -static qboolean Top_Inside( decalvert_t *pVert ){ return pVert->m_tCoords[1] < 1.0f; } -static float Top_Clip( decalvert_t *one, decalvert_t *two ){ return ( 1.0f - one->m_tCoords[1] ) / ( two->m_tCoords[1] - one->m_tCoords[1] ); } -static qboolean Left_Inside( decalvert_t *pVert ){ return pVert->m_tCoords[0] > 0.0f; } -static float Left_Clip( decalvert_t *one, decalvert_t *two ){ return one->m_tCoords[0] / ( one->m_tCoords[0] - two->m_tCoords[0] ); } -static qboolean Right_Inside( decalvert_t *pVert ){ return pVert->m_tCoords[0] < 1.0f; } -static float Right_Clip( decalvert_t *one, decalvert_t *two ){ return ( 1.0f - one->m_tCoords[0] ) / ( two->m_tCoords[0] - one->m_tCoords[0] ); } -static qboolean Bottom_Inside( decalvert_t *pVert ){ return pVert->m_tCoords[1] > 0.0f; } -static float Bottom_Clip( decalvert_t *one, decalvert_t *two ){ return one->m_tCoords[1] / ( one->m_tCoords[1] - two->m_tCoords[1] ); } - -// clippanes -static decal_clip_t PlaneTop = { Top_Inside, Top_Clip }; -static decal_clip_t PlaneLeft = { Left_Inside, Left_Clip }; -static decal_clip_t PlaneRight = { Right_Inside, Right_Clip }; -static decal_clip_t PlaneBottom = { Bottom_Inside, Bottom_Clip }; - -static void Intersect( decal_clip_t clipFunc, decalvert_t *one, decalvert_t *two, decalvert_t *out ) -{ - float t = clipFunc.pfnClip( one, two ); - - VectorLerp( one->m_vPos, t, two->m_vPos, out->m_vPos ); - Vector2Lerp( one->m_LMCoords, t, two->m_LMCoords, out->m_LMCoords ); - Vector2Lerp( one->m_tCoords, t, two->m_tCoords, out->m_tCoords ); -} +// clip edges +#define LEFT_EDGE 0 +#define RIGHT_EDGE 1 +#define TOP_EDGE 2 +#define BOTTOM_EDGE 3 // This structure contains the information used to create new decals typedef struct { vec3_t m_Position; // world coordinates of the decal center vec3_t m_SAxis; // the s axis for the decal in world coordinates - model_t* m_pModel; // the model the decal is going to be applied in + model_t *m_pModel; // the model the decal is going to be applied in int m_iTexture; // The decal material int m_Size; // Size of the decal (in world coords) int m_Flags; int m_Entity; // Entity the decal is applied to. float m_scale; - float m_flFadeTime; - float m_flFadeDuration; int m_decalWidth; int m_decalHeight; vec3_t m_Basis[3]; } decalinfo_t; -static decalvert_t g_DecalClipVerts[MAX_DECALCLIPVERT]; -static decalvert_t g_DecalClipVerts2[MAX_DECALCLIPVERT]; -static byte *r_decalPool; // pool of decal meshes +static float g_DecalClipVerts[MAX_DECALCLIPVERT][VERTEXSIZE]; +static float g_DecalClipVerts2[MAX_DECALCLIPVERT][VERTEXSIZE]; static decal_t gDecalPool[MAX_RENDER_DECALS]; static int gDecalCount; void R_ClearDecals( void ) { - Mem_EmptyPool( r_decalPool ); Q_memset( gDecalPool, 0, sizeof( gDecalPool )); gDecalCount = 0; } -// Init the decal pool -void R_InitDecals( void ) -{ - r_decalPool = Mem_AllocPool( "Decals Mesh Pool" ); - R_ClearDecals (); -} - -void R_ShutdownDecals( void ) -{ - Mem_FreePool( &r_decalPool ); -} - // unlink pdecal from any surface it's attached to static void R_DecalUnlink( decal_t *pdecal ) { @@ -142,8 +96,8 @@ static void R_DecalUnlink( decal_t *pdecal ) // Just reuse next decal in list -// A decal that spans multiple surfaces will use multiple decal_t pool entries, as each surface needs -// it's own. +// A decal that spans multiple surfaces will use multiple decal_t pool entries, +// as each surface needs it's own. static decal_t *R_DecalAlloc( decal_t *pdecal ) { int limit = MAX_RENDER_DECALS; @@ -249,27 +203,23 @@ void R_SetupDecalTextureSpaceBasis( decal_t *pDecal, msurface_t *surf, int textu } // Build the initial list of vertices from the surface verts into the global array, 'verts'. -void R_SetupDecalVertsForMSurface( decal_t *pDecal, msurface_t *surf, vec3_t textureSpaceBasis[3], decalvert_t *pVerts ) +void R_SetupDecalVertsForMSurface( decal_t *pDecal, msurface_t *surf, vec3_t textureSpaceBasis[3], float *verts ) { float *v; - int j; + int i; - v = surf->polys->verts[0]; - for( j = 0; j < surf->polys->numverts; j++, v += VERTEXSIZE ) + for( i = 0, v = surf->polys->verts[0]; i < surf->polys->numverts; i++, v += VERTEXSIZE, verts += VERTEXSIZE ) { - VectorCopy( v, pVerts[j].m_vPos ); // copy model space coordinates - pVerts[j].m_tCoords[0] = DotProduct( pVerts[j].m_vPos, textureSpaceBasis[0] ) - SHORT_TO_FLOAT( pDecal->dx ) + 0.5f; - pVerts[j].m_tCoords[1] = DotProduct( pVerts[j].m_vPos, textureSpaceBasis[1] ) - SHORT_TO_FLOAT( pDecal->dy ) + 0.5f; - pVerts[j].m_LMCoords[0] = pVerts[j].m_LMCoords[1] = 0.0f; + VectorCopy( v, verts ); // copy model space coordinates + verts[3] = DotProduct( verts, textureSpaceBasis[0] ) - SHORT_TO_FLOAT( pDecal->dx ) + 0.5f; + verts[4] = DotProduct( verts, textureSpaceBasis[1] ) - SHORT_TO_FLOAT( pDecal->dy ) + 0.5f; + verts[5] = verts[6] = 0.0f; } } // Figure out where the decal maps onto the surface. -void R_SetupDecalClip( decalvert_t *pOutVerts, decal_t *pDecal, msurface_t *surf, int texture, vec3_t textureSpaceBasis[3], float decalWorldScale[2] ) +void R_SetupDecalClip( decal_t *pDecal, msurface_t *surf, int texture, vec3_t textureSpaceBasis[3], float decalWorldScale[2] ) { -// if ( pOutVerts == NULL ) -// pOutVerts = &g_DecalClipVerts[0]; - R_SetupDecalTextureSpaceBasis( pDecal, surf, texture, textureSpaceBasis, decalWorldScale ); // Generate texture coordinates for each vertex in decal s,t space @@ -279,64 +229,146 @@ void R_SetupDecalClip( decalvert_t *pOutVerts, decal_t *pDecal, msurface_t *surf pDecal->dy = FLOAT_TO_SHORT( DotProduct( pDecal->position, textureSpaceBasis[1] )); } -static int SHClip( decalvert_t *g_DecalClipVerts, int vertCount, decalvert_t *out, decal_clip_t clipFunc ) +// Quick and dirty sutherland Hodgman clipper +// Clip polygon to decal in texture space +// JAY: This code is lame, change it later. It does way too much work per frame +// It can be made to recursively call the clipping code and only copy the vertex list once +int R_ClipInside( float *vert, int edge ) { - int j, outCount; - decalvert_t *s, *p; + switch( edge ) + { + case LEFT_EDGE: + if( vert[3] > 0.0f ) + return 1; + return 0; + case RIGHT_EDGE: + if( vert[3] < 1.0f ) + return 1; + return 0; + case TOP_EDGE: + if( vert[4] > 0.0f ) + return 1; + return 0; + case BOTTOM_EDGE: + if( vert[4] < 1.0f ) + return 1; + return 0; + } + return 0; +} + + +void R_ClipIntersect( float *one, float *two, float *out, int edge ) +{ + float t; + + // t is the parameter of the line between one and two clipped to the edge + // or the fraction of the clipped point between one & two + // vert[0], vert[1], vert[2] is X, Y, Z + // vert[3] is u + // vert[4] is v + // vert[5] is lightmap u + // vert[6] is lightmap v + + if( edge < TOP_EDGE ) + { + if( edge == LEFT_EDGE ) + { + // left + t = ((one[3] - 0.0f) / (one[3] - two[3])); + out[3] = out[5] = 0.0f; + } + else + { + // right + t = ((one[3] - 1.0f) / (one[3] - two[3])); + out[3] = out[5] = 1.0f; + } + + out[4] = one[4] + (two[4] - one[4]) * t; + out[6] = one[6] + (two[6] - one[6]) * t; + } + else + { + if( edge == TOP_EDGE ) + { + // top + t = ((one[4] - 0.0f) / (one[4] - two[4])); + out[4] = out[6] = 0.0f; + } + else + { + // bottom + t = ((one[4] - 1.0f) / (one[4] - two[4])); + out[4] = out[6] = 1.0f; + } + + out[3] = one[3] + (two[3] - one[3]) * t; + out[5] = one[5] + (two[4] - one[5]) * t; + } + + VectorLerp( one, t, two, out ); +} + +static int SHClip( float *vert, int vertCount, float *out, int edge ) +{ + int j, outCount; + float *s, *p; outCount = 0; - s = &g_DecalClipVerts[vertCount - 1]; + s = &vert[(vertCount - 1) * VERTEXSIZE]; - for( j = 0; j < vertCount; j++ ) + for( j = 0; j < vertCount; j++ ) { - p = &g_DecalClipVerts[j]; + p = &vert[j * VERTEXSIZE]; - if( clipFunc.pfnInside( p )) + if( R_ClipInside( p, edge )) { - if( clipFunc.pfnInside( s )) + if( R_ClipInside( s, edge )) { - Q_memcpy( out, p, sizeof( *out )); + // Add a vertex and advance out to next vertex + Q_memcpy( out, p, sizeof( float ) * VERTEXSIZE ); + out += VERTEXSIZE; outCount++; - out++; } - else + else { - Intersect( clipFunc, s, p, out ); - out++; + R_ClipIntersect( s, p, out, edge ); + out += VERTEXSIZE; outCount++; - Q_memcpy( out, p, sizeof( *out )); - outCount++; - out++; - } - } - else - { - if( clipFunc.pfnInside( s )) - { - Intersect( clipFunc, p, s, out ); - out++; + Q_memcpy( out, p, sizeof( float ) * VERTEXSIZE ); + out += VERTEXSIZE; outCount++; } } + else + { + if( R_ClipInside( s, edge )) + { + R_ClipIntersect( p, s, out, edge ); + out += VERTEXSIZE; + outCount++; + } + } + s = p; } + return outCount; } -decalvert_t *R_DoDecalSHClip( decalvert_t *pInVerts, decalvert_t *pOutVerts, decal_t *pDecal, int nStartVerts, int *pVertCount ) +float *R_DoDecalSHClip( float *pInVerts, decal_t *pDecal, int nStartVerts, int *pVertCount ) { int outCount; - - if( pOutVerts == NULL ) - pOutVerts = &g_DecalClipVerts[0]; + float *pOutVerts = g_DecalClipVerts[0]; // clip the polygon to the decal texture space - outCount = SHClip( pInVerts, nStartVerts, &g_DecalClipVerts2[0], PlaneTop ); - outCount = SHClip( &g_DecalClipVerts2[0], outCount, &g_DecalClipVerts[0], PlaneLeft ); - outCount = SHClip( &g_DecalClipVerts[0], outCount, &g_DecalClipVerts2[0], PlaneRight ); - outCount = SHClip( &g_DecalClipVerts2[0], outCount, pOutVerts, PlaneBottom ); + outCount = SHClip( pInVerts, nStartVerts, g_DecalClipVerts2[0], LEFT_EDGE ); + outCount = SHClip( g_DecalClipVerts2[0], outCount, g_DecalClipVerts[0], RIGHT_EDGE ); + outCount = SHClip( g_DecalClipVerts[0], outCount, g_DecalClipVerts2[0], TOP_EDGE ); + outCount = SHClip( g_DecalClipVerts2[0], outCount, pOutVerts, BOTTOM_EDGE ); if( outCount ) { @@ -349,16 +381,12 @@ decalvert_t *R_DoDecalSHClip( decalvert_t *pInVerts, decalvert_t *pOutVerts, dec // should work as well. if( outCount == 4 ) { - int j, clipped = 0; - decalvert_t *v = pOutVerts; + int j, clipped = 0; + float *v = pOutVerts; - for( j = 0; j < outCount && !clipped; j++, v++ ) + for( j = 0; j < outCount && !clipped; j++, v += VERTEXSIZE ) { - float s, t; - s = v->m_tCoords[0]; - t = v->m_tCoords[1]; - - if (( s != 0.0f && s != 1.0f ) || ( t != 0.0f && t != 1.0f )) + if(( v[3] != 0.0f && v[3] != 1.0f ) || ( v[4] != 0.0f && v[4] != 1.0f )) clipped = 1; } @@ -370,28 +398,29 @@ decalvert_t *R_DoDecalSHClip( decalvert_t *pInVerts, decalvert_t *pOutVerts, dec } *pVertCount = outCount; + return pOutVerts; } //----------------------------------------------------------------------------- // Generate clipped vertex list for decal pdecal projected onto polygon psurf //----------------------------------------------------------------------------- -decalvert_t *R_DecalVertsClip( decalvert_t *pOutVerts, decal_t *pDecal, msurface_t *surf, int texture, int *pVertCount ) +float *R_DecalVertsClip( decal_t *pDecal, msurface_t *surf, int texture, int *pVertCount ) { float decalWorldScale[2]; vec3_t textureSpaceBasis[3]; // figure out where the decal maps onto the surface. - R_SetupDecalClip( pOutVerts, pDecal, surf, texture, textureSpaceBasis, decalWorldScale ); + R_SetupDecalClip( pDecal, surf, texture, textureSpaceBasis, decalWorldScale ); // build the initial list of vertices from the surface verts. - R_SetupDecalVertsForMSurface( pDecal, surf, textureSpaceBasis, g_DecalClipVerts ); + R_SetupDecalVertsForMSurface( pDecal, surf, textureSpaceBasis, g_DecalClipVerts[0] ); - return R_DoDecalSHClip( g_DecalClipVerts, pOutVerts, pDecal, surf->polys->numverts, pVertCount ); + return R_DoDecalSHClip( g_DecalClipVerts[0], pDecal, surf->polys->numverts, pVertCount ); } // Generate lighting coordinates at each vertex for decal vertices v[] on surface psurf -static void R_DecalVertsLight( decalvert_t *v, msurface_t *surf, int vertCount ) +static void R_DecalVertsLight( float *v, msurface_t *surf, int vertCount ) { float s, t; mtexinfo_t *tex; @@ -399,31 +428,31 @@ static void R_DecalVertsLight( decalvert_t *v, msurface_t *surf, int vertCount ) tex = surf->texinfo; - for( j = 0; j < vertCount; j++, v++ ) + for( j = 0; j < vertCount; j++, v += VERTEXSIZE ) { // lightmap texture coordinates - s = DotProduct( v->m_vPos, tex->vecs[0] ) + tex->vecs[0][3] - surf->texturemins[0]; + s = DotProduct( v, tex->vecs[0] ) + tex->vecs[0][3] - surf->texturemins[0]; s += surf->light_s * LM_SAMPLE_SIZE; s += LM_SAMPLE_SIZE >> 1; s /= BLOCK_WIDTH * LM_SAMPLE_SIZE; //fa->texinfo->texture->width; - t = DotProduct( v->m_vPos, tex->vecs[1] ) + tex->vecs[1][3] - surf->texturemins[1]; + t = DotProduct( v, tex->vecs[1] ) + tex->vecs[1][3] - surf->texturemins[1]; t += surf->light_t * LM_SAMPLE_SIZE; t += LM_SAMPLE_SIZE >> 1; t /= BLOCK_HEIGHT * LM_SAMPLE_SIZE; //fa->texinfo->texture->height; - v->m_LMCoords[0] = s; - v->m_LMCoords[1] = t; + v[5] = s; + v[6] = t; } } -static decalvert_t* R_DecalVertsNoclip( decal_t *pdecal, msurface_t *surf, int texture ) +static float *R_DecalVertsNoclip( decal_t *pdecal, msurface_t *surf, int texture ) { - decalvert_t *vlist; - int outCount; + float *vlist; + int outCount; // use the old code for now, and just cache them - vlist = R_DecalVertsClip( NULL, pdecal, surf, texture, &outCount ); + vlist = R_DecalVertsClip( pdecal, surf, texture, &outCount ); R_DecalVertsLight( vlist, surf, 4 ); @@ -585,7 +614,7 @@ static void R_DecalCreate( decalinfo_t *decalinfo, msurface_t *surf, float x, fl // check to see if the decal actually intersects the surface // if not, then remove the decal - R_DecalVertsClip( NULL, pdecal, surf, decalinfo->m_iTexture, &vertCount ); + R_DecalVertsClip( pdecal, surf, decalinfo->m_iTexture, &vertCount ); if( !vertCount ) { @@ -820,9 +849,9 @@ void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos // Build the vertex list for a decal on a surface and clip it to the surface. // This is a template so it can work on world surfaces and dynamic displacement // triangles the same way. -decalvert_t *R_DecalSetupVerts( decal_t *pDecal, msurface_t *surf, int texture, int *outCount ) +float *R_DecalSetupVerts( decal_t *pDecal, msurface_t *surf, int texture, int *outCount ) { - decalvert_t *v; + float *v; if( pDecal->flags & FDECAL_NOCLIP ) { @@ -831,28 +860,36 @@ decalvert_t *R_DecalSetupVerts( decal_t *pDecal, msurface_t *surf, int texture, } else { - v = R_DecalVertsClip( NULL, pDecal, surf, texture, outCount ); + v = R_DecalVertsClip( pDecal, surf, texture, outCount ); if( outCount ) R_DecalVertsLight( v, surf, *outCount ); } + return v; } void DrawSingleDecal( decal_t *pDecal, msurface_t *fa ) { - decalvert_t *v; + float *v; + gltexture_t *glt; int i, numVerts; v = R_DecalSetupVerts( pDecal, fa, pDecal->texture, &numVerts ); if( !numVerts ) return; GL_Bind( GL_TEXTURE0, pDecal->texture ); + glt = R_GetTexture( pDecal->texture ); + + // draw transparent decals with GL_MODULATE + if( glt->fogParams[3] > DECAL_TRANSPARENT_THRESHOLD ) + pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + else pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); pglBegin( GL_POLYGON ); - for( i = 0; i < numVerts; i++, v++ ) + for( i = 0; i < numVerts; i++, v += VERTEXSIZE ) { - pglTexCoord2f( v->m_tCoords[0], v->m_tCoords[1] ); - pglVertex3fv( v->m_vPos ); + pglTexCoord2f( v[3], v[4] ); + pglVertex3fv( v ); } pglEnd(); @@ -876,7 +913,6 @@ void DrawSurfaceDecals( msurface_t *fa ) pglEnable( GL_POLYGON_OFFSET_FILL ); pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); // try to use GL_MODULATE ? for( p = fa->pdecals; p; p = p->pnext ) DrawSingleDecal( p, fa ); diff --git a/engine/client/gl_image.c b/engine/client/gl_image.c index f80dd3d9..c10a099d 100644 --- a/engine/client/gl_image.c +++ b/engine/client/gl_image.c @@ -874,6 +874,12 @@ static void GL_UploadTexture( rgbdata_t *pic, gltexture_t *tex, qboolean subImag tex->flags &= ~TF_MAKELUMA; } + if( tex->flags & TF_KEEP_8BIT ) + tex->original = FS_CopyImage( pic ); // because current pic will be expanded to rgba + + if( tex->flags & TF_KEEP_RGBDATA ) + tex->original = pic; // no need to copy + // we need to expand image into RGBA buffer if( pic->type == PF_INDEXED_24 || pic->type == PF_INDEXED_32 ) img_flags |= IMAGE_FORCE_RGBA; @@ -987,6 +993,7 @@ int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ) gltexture_t *tex; rgbdata_t *pic; uint i, hash; + uint picFlags = 0; if( !name || !name[0] || !glw_state.initialized ) return 0; @@ -1015,6 +1022,15 @@ int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ) return tex->texnum; } + if( flags & TF_NOFLIP_TGA ) + picFlags |= IL_DONTFLIP_TGA; + + if( flags & TF_KEEP_8BIT ) + picFlags |= IL_KEEP_8BIT; + + // set some image flags + Image_SetForceFlags( picFlags ); + pic = FS_LoadImage( name, buf, size ); if( !pic ) return 0; // couldn't loading image @@ -1047,16 +1063,15 @@ int GL_LoadTexture( const char *name, const byte *buf, size_t size, int flags ) GL_UploadTexture( pic, tex, false ); GL_TexFilter( tex, false ); // update texture filter, wrap etc - if( flags & TF_KEEP_RGBDATA ) - tex->original = pic; - else FS_FreeImage( pic ); // release source texture + if(!( flags & (TF_KEEP_8BIT|TF_KEEP_RGBDATA))) + FS_FreeImage( pic ); // release source texture // add to hash table hash = Com_HashKey( tex->name, TEXTURES_HASH_SIZE ); tex->nextHash = r_texturesHashTable[hash]; r_texturesHashTable[hash] = tex; - // NOTE: always return texnum as index in array or engine will stop work !!!! + // NOTE: always return texnum as index in array or engine will stop work !!! return i; } @@ -1159,7 +1174,7 @@ void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor ) ASSERT( texnum > 0 && texnum < MAX_TEXTURES ); image = &r_textures[texnum]; - if(!( image->flags & TF_KEEP_RGBDATA ) || !image->original ) + if(!( image->flags & (TF_KEEP_RGBDATA|TF_KEEP_8BIT)) || !image->original ) { MsgDev( D_ERROR, "GL_ProcessTexture: no input data for %s\n", image->name ); return; @@ -1294,7 +1309,7 @@ void GL_FreeTexture( GLenum texnum ) } // release source - if( image->flags & TF_KEEP_RGBDATA && image->original ) + if( image->flags & (TF_KEEP_RGBDATA|TF_KEEP_8BIT) && image->original ) FS_FreeImage( image->original ); pglDeleteTextures( 1, &image->texnum ); diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index 290cd66a..934b1a35 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -73,23 +73,24 @@ typedef enum typedef enum { - TF_STATIC = BIT(0), // don't free until Image_FreeUnused() - TF_NOPICMIP = BIT(1), // ignore r_picmip resample rules - TF_UNCOMPRESSED = BIT(2), // don't compress texture in video memory - TF_CUBEMAP = BIT(3), // it's cubemap texture - TF_DEPTHMAP = BIT(4), // custom texture filter used - TF_INTENSITY = BIT(5), - TF_LUMINANCE = BIT(6), // force image to grayscale - TF_SKYSIDE = BIT(7), - TF_CLAMP = BIT(8), - TF_NOMIPMAP = BIT(9), - TF_NEAREST = BIT(10), // disable texfilter - TF_HAS_LUMA = BIT(11), // sets by GL_UploadTexture - TF_MAKELUMA = BIT(12), // create luma from quake texture - TF_NORMALMAP = BIT(13), // is a normalmap - TF_LIGHTMAP = BIT(14), // is a lightmap - TF_FORCE_COLOR = BIT(15), // force upload monochrome textures as RGB (detail textures) - TF_KEEP_RGBDATA = BIT(16), // some images keep source + TF_NEAREST = BIT(0), // disable texfilter + TF_KEEP_RGBDATA = BIT(1), // some images keep source + TF_NOFLIP_TGA = BIT(2), // Steam background completely ignore tga attribute 0x20 + TF_KEEP_8BIT = BIT(3), // keep original 8-bit image (if present) + TF_NOPICMIP = BIT(4), // ignore r_picmip resample rules + TF_UNCOMPRESSED = BIT(5), // don't compress texture in video memory + TF_CUBEMAP = BIT(6), // it's cubemap texture + TF_DEPTHMAP = BIT(7), // custom texture filter used + TF_INTENSITY = BIT(8), + TF_LUMINANCE = BIT(9), // force image to grayscale + TF_SKYSIDE = BIT(10), + TF_CLAMP = BIT(11), + TF_NOMIPMAP = BIT(12), + TF_HAS_LUMA = BIT(13), // sets by GL_UploadTexture + TF_MAKELUMA = BIT(14), // create luma from quake texture + TF_NORMALMAP = BIT(15), // is a normalmap + TF_LIGHTMAP = BIT(16), // is a lightmap + TF_FORCE_COLOR = BIT(17), // force upload monochrome textures as RGB (detail textures) } texFlags_t; typedef struct gltexture_s @@ -300,8 +301,6 @@ qboolean R_CullSurface( msurface_t *surf, uint clipflags ); // // gl_decals.c // -void R_InitDecals( void ); -void R_ShutdownDecals( void ); void DrawSurfaceDecals( msurface_t *fa ); void R_ClearDecals( void ); diff --git a/engine/client/gl_mirror.c b/engine/client/gl_mirror.c index 62118b21..2c98a83c 100644 --- a/engine/client/gl_mirror.c +++ b/engine/client/gl_mirror.c @@ -256,8 +256,6 @@ void R_DrawMirrors( void ) r_viewleaf = Mod_PointInLeaf( oldRI.pvsorigin, cl.worldmodel->nodes ); r_viewleaf2 = Mod_PointInLeaf( RI.pvsorigin, cl.worldmodel->nodes ); - // FIXME: how to combine two cull origins ? - if( GL_Support( GL_ARB_TEXTURE_NPOT_EXT )) { // allow screen size diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index 528a81e3..102561ea 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -229,7 +229,7 @@ void R_ScreenToWorld( const vec3_t screen, vec3_t point ) if( !point || !screen ) return; - // FIXME: does we need a full invert here? + // g-cont. does we need a full invert here? Matrix4x4_Invert_Simple( screenToWorld, RI.worldviewProjectionMatrix ); temp[0] = 2.0f * (screen[0] - RI.viewport[0]) / RI.viewport[2] - 1; temp[1] = -2.0f * (screen[1] - RI.viewport[1]) / RI.viewport[3] + 1; @@ -859,7 +859,7 @@ static void R_EndGL( void ) R_CheckFog check for underwater fog -FIXME: this code is wrong, we need to compute fog volumes (as water volumes) +UNDONE: this code is wrong, we need to compute fog volumes (as water volumes) and get fog params from texture water on a surface. ============= */ diff --git a/engine/client/gl_rpart.c b/engine/client/gl_rpart.c index f193d944..f4487816 100644 --- a/engine/client/gl_rpart.c +++ b/engine/client/gl_rpart.c @@ -292,7 +292,6 @@ static void CL_SparkTracerDraw( particle_t *p, float frametime ) static void CL_TracerImplosion( particle_t *p, float frametime ) { float lifePerc = p->die - cl.time; - float grav = frametime * clgame.movevars.gravity * 0.05f; float length; int alpha = 255; vec3_t delta; @@ -301,7 +300,6 @@ static void CL_TracerImplosion( particle_t *p, float frametime ) length = VectorLength( delta ); if( lifePerc < 0.5f ) alpha = (lifePerc * 2) * 255; - p->vel[2] -= grav; // use slow gravity CL_DrawTracer( p->org, delta, 1.5f, clgame.palette[p->color], alpha, 0.0f, 0.8f ); VectorMA( p->org, frametime, p->vel, p->org ); @@ -1572,7 +1570,7 @@ CL_Implosion void CL_Implosion( const vec3_t end, float radius, int count, float life ) { particle_t *p; - float vel; + float vel, radius2; vec3_t dir, m_vecPos; int i, colorIndex; @@ -1587,20 +1585,23 @@ void CL_Implosion( const vec3_t end, float radius, int count, float life ) dir[1] = Com_RandomFloat( -1.0f, 1.0f ); dir[2] = Com_RandomFloat( -1.0f, 1.0f ); + radius2 = Com_RandomFloat( radius * 0.9f, radius * 1.1f ); + VectorNormalize( dir ); - VectorMA( end, -radius, dir, m_vecPos ); + VectorMA( end, -radius2, dir, m_vecPos ); // velocity based on how far particle has to travel away from org - vel = Com_RandomFloat( radius * 0.5f, radius * 1.5f ); + if( life ) vel = (radius2 / life); + else vel = Com_RandomFloat( radius2 * 0.5f, radius2 * 1.5f ); VectorCopy( m_vecPos, p->org ); p->color = colorIndex; - p->ramp = 0.15f; // length based on velocity - + p->ramp = (vel / radius2) * 0.1f; // length based on velocity + p->ramp = bound( 0.1f, p->ramp, 1.0f ); VectorScale( dir, vel, p->vel ); // die right when you get there - p->die += ( life != 0.0f ) ? life : ( radius / vel ); + p->die += ( life != 0.0f ) ? life : ( radius2 / vel ); } } diff --git a/engine/client/gl_studio.c b/engine/client/gl_studio.c index 81be272f..da6d1a9b 100644 --- a/engine/client/gl_studio.c +++ b/engine/client/gl_studio.c @@ -1265,9 +1265,17 @@ void R_StudioSetupChrome( float *pchrome, int bone, vec3_t normal ) if( g_nForceFaceFlags & STUDIO_NF_CHROME ) { - float angle = anglemod( RI.refdef.time * 40 ); - RotatePointAroundVector( chromeupvec, tmp, v_left, angle - 180 ); - RotatePointAroundVector( chromerightvec, chromeupvec, v_left, 180 + angle ); + float angle, sr, cr; + int i; + + angle = anglemod( RI.refdef.time * 40 ) * (M_PI * 2.0f / 360.0f); + SinCos( angle, &sr, &cr ); + + for( i = 0; i < 3; i++ ) + { + chromerightvec[i] = (v_left[i] * cr + RI.vup[i] * sr); + chromeupvec[i] = v_left[i] * -sr + RI.vup[i] * cr; + } } else { @@ -1780,7 +1788,7 @@ static void R_StudioDrawPoints( void ) if( g_nForceFaceFlags & STUDIO_NF_CHROME ) { - scale = RI.currententity->curstate.renderamt * (1.0f / 255.0f); + scale = 1.0f + RI.currententity->curstate.renderamt * (1.0f / 255.0f); for( i = 0; i < m_pSubModel->numnorms; i++ ) Matrix3x4_VectorRotate( g_bonestransform[pnormbone[i]], pstudionorms[i], g_xformnorms[i] ); diff --git a/engine/client/gl_vidnt.c b/engine/client/gl_vidnt.c index 522b2c23..63e2652a 100644 --- a/engine/client/gl_vidnt.c +++ b/engine/client/gl_vidnt.c @@ -1665,7 +1665,7 @@ qboolean R_Init( void ) R_InitImages(); R_SpriteInit(); R_StudioInit(); - R_InitDecals(); + R_ClearDecals(); R_ClearScene(); // initialize screen @@ -1696,7 +1696,6 @@ void R_Shutdown( void ) GL_RemoveCommands(); R_ShutdownImages(); - R_ShutdownDecals(); Mem_FreePool( &r_temppool ); diff --git a/engine/client/s_main.c b/engine/client/s_main.c index 63242803..9a9bad9a 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -20,7 +20,7 @@ GNU General Public License for more details. #include "ref_params.h" #define MAX_DUPLICATED_CHANNELS 4 // threshold for identical static channels (probably error) -#define SND_CLIP_DISTANCE 1024.0f +#define SND_CLIP_DISTANCE 1000.0f dma_t dma; byte *sndpool; @@ -188,9 +188,7 @@ channel_t *SND_PickDynamicChannel( int entnum, int channel, sfx_t *sfx ) // Never override a streaming sound that is currently playing or // voice over IP data that is playing or any sound on CHAN_VOICE( acting ) if( ch->sfx && ( ch->entchannel == CHAN_STREAM )) - { continue; - } if( channel != CHAN_AUTO && ch->entnum == entnum && ( ch->entchannel == channel || channel == -1 )) { @@ -371,7 +369,6 @@ void SND_Spatialize( channel_t *ch ) float dist, dot; float lscale, rscale, scale; vec3_t source_vec; - sfx_t *snd; // anything coming from the view entity will allways be full volume if( S_IsClient( ch->entnum )) @@ -383,12 +380,15 @@ void SND_Spatialize( channel_t *ch ) if( !ch->staticsound ) { - if( !CL_GetEntitySpatialization( ch->entnum, ch->origin, NULL )) - return; // entity not exist on client + if( !CL_GetEntitySpatialization( ch->entnum, ch->origin )) + { + // origin is null and entity not exist on client + ch->leftvol = ch->rightvol = 0; + return; + } } // calculate stereo seperation and distance attenuation - snd = ch->sfx; VectorSubtract( ch->origin, s_listener.origin, source_vec ); dist = VectorNormalizeLength( source_vec ) * ch->dist_mult; @@ -400,11 +400,11 @@ void SND_Spatialize( channel_t *ch ) // add in distance effect scale = ( 1.0f - dist ) * rscale; ch->rightvol = (int)( ch->master_vol * scale ); - if( ch->rightvol < 0 ) ch->rightvol = 0; + ch->rightvol = bound( 0, ch->rightvol, 255 ); scale = ( 1.0f - dist ) * lscale; ch->leftvol = (int)( ch->master_vol * scale ); - if( ch->leftvol < 0 ) ch->leftvol = 0; + ch->leftvol = bound( 0, ch->leftvol, 255 ); // if playing a word, set volume VOX_SetChanVol( ch ); @@ -591,7 +591,7 @@ void S_AmbientSound( const vec3_t pos, int ent, sound_t handle, float fvol, floa if( !pos ) pos = origin; - if( ent != 0 ) CL_GetEntitySpatialization( ent, origin, NULL ); + if( ent != 0 ) CL_GetEntitySpatialization( ent, origin ); // pick a channel to play on from the static area ch = SND_PickStaticChannel( ent, sfx, pos ); @@ -1103,7 +1103,7 @@ qboolean S_Init( void ) dsp_off = Cvar_Get( "dsp_off", "0", CVAR_ARCHIVE, "set to 1 to disable all dsp processing" ); s_ambient_level = Cvar_Get( "ambient_level", "0.3", 0, "volume of environment noises (water and wind)" ); s_ambient_fade = Cvar_Get( "ambient_fade", "100", 0, "rate of volume fading when client is moving" ); - s_combine_sounds = Cvar_Get( "s_combine", "0", CVAR_ARCHIVE, "combine the channels with same sounds" ); + s_combine_sounds = Cvar_Get( "s_combine_channels", "1", CVAR_ARCHIVE, "combine the channels with same sounds" ); s_test = Cvar_Get( "s_test", "0", 0, "engine developer cvar for quick testing new features" ); Cmd_AddCommand( "play", S_Play_f, "playing a specified sound file" ); diff --git a/engine/client/s_mix.c b/engine/client/s_mix.c index cb067a2e..d4edc5fa 100644 --- a/engine/client/s_mix.c +++ b/engine/client/s_mix.c @@ -615,8 +615,9 @@ void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate ) if( CL_GetEntityByIndex( ch->entnum ) && ( ch->entchannel == CHAN_VOICE )) { - // UNDONE: implement SND_MoveMouth16 too - SND_MoveMouth8( ch, pSource, sampleCount ); + if( pSource->width == 1 ) + SND_MoveMouth8( ch, pSource, sampleCount ); + else SND_MoveMouth16( ch, pSource, sampleCount ); } // mix channel to all active paintbuffers. diff --git a/engine/client/s_mouth.c b/engine/client/s_mouth.c index d4127467..975ad439 100644 --- a/engine/client/s_mouth.c +++ b/engine/client/s_mouth.c @@ -94,6 +94,55 @@ void SND_MoveMouth8( channel_t *ch, wavdata_t *pSource, int count ) pMouth->sndavg += savg; pMouth->sndcount = (byte)scount; + if( pMouth->sndcount >= CAVGSAMPLES ) + { + pMouth->mouthopen = pMouth->sndavg / CAVGSAMPLES; + pMouth->sndavg = 0; + pMouth->sndcount = 0; + } +} + +void SND_MoveMouth16( channel_t *ch, wavdata_t *pSource, int count ) +{ + cl_entity_t *clientEntity; + short *pdata = NULL; + mouth_t *pMouth = NULL; + int savg, data; + int scount, pos = 0; + uint i; + + clientEntity = CL_GetEntityByIndex( ch->entnum ); + if( !clientEntity ) return; + + pMouth = &clientEntity->mouth; + + if( ch->isSentence ) + { + if( ch->currentWord ) + pos = ch->currentWord->sample; + } + else pos = ch->pMixer.sample; + + count = S_GetOutputData( pSource, &pdata, pos, count, ch->use_loop ); + if( pdata == NULL ) return; + + i = 0; + scount = pMouth->sndcount; + savg = 0; + + while( i < count && scount < CAVGSAMPLES ) + { + data = pdata[i]; + data = (bound( -32767, data, 0x7ffe ) >> 8); + savg += abs( data ); + + i += 80 + ((byte)data & 0x1F); + scount++; + } + + pMouth->sndavg += savg; + pMouth->sndcount = (byte)scount; + if( pMouth->sndcount >= CAVGSAMPLES ) { pMouth->mouthopen = pMouth->sndavg / CAVGSAMPLES; diff --git a/engine/client/sound.h b/engine/client/sound.h index eeeffaf0..15fa1660 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -264,6 +264,7 @@ void S_FreeSounds( void ); // void SND_InitMouth( int entnum, int entchannel ); void SND_MoveMouth8( channel_t *ch, wavdata_t *pSource, int count ); +void SND_MoveMouth16( channel_t *ch, wavdata_t *pSource, int count ); void SND_CloseMouth( channel_t *ch ); // diff --git a/engine/common/common.h b/engine/common/common.h index 09fca756..6c2678fc 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -440,6 +440,7 @@ typedef enum IL_USE_LERPING = BIT(0), // lerping images during resample IL_KEEP_8BIT = BIT(1), // don't expand paletted images IL_ALLOW_OVERWRITE = BIT(2), // allow to overwrite stored images + IL_DONTFLIP_TGA = BIT(3), // Steam background completely ignore tga attribute 0x20 (stupid lammers!) } ilFlags_t; // rgbdata output flags @@ -490,9 +491,11 @@ void Image_Init( void ); void Image_Shutdown( void ); rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size ); qboolean FS_SaveImage( const char *filename, rgbdata_t *pix ); +rgbdata_t *FS_CopyImage( rgbdata_t *in ); void FS_FreeImage( rgbdata_t *pack ); extern const bpc_desc_t PFDesc[]; // image get pixelformat qboolean Image_Process( rgbdata_t **pix, int width, int height, float gamma, uint flags ); +void Image_SetForceFlags( uint flags ); // set image force flags on loading /* ======================================================================== @@ -705,7 +708,6 @@ void CL_CharEvent( int key ); int CL_PointContents( const vec3_t point ); char *COM_ParseFile( char *data, char *token ); byte *COM_LoadFile( const char *filename, int usehunk, int *pLength ); -qboolean CL_GetEntitySpatialization( int entnum, vec3_t origin, vec3_t velocity ); void CL_StudioEvent( struct mstudioevent_s *event, struct cl_entity_s *ent ); qboolean CL_GetComment( const char *demoname, char *comment ); void COM_AddAppDirectoryToSearchPath( const char *pszBaseDir, const char *appName ); @@ -749,6 +751,7 @@ char *Info_ValueForKey( const char *s, const char *key ); void Info_RemovePrefixedKeys( char *start, char prefix ); qboolean Info_RemoveKey( char *s, const char *key ); qboolean Info_SetValueForKey( char *s, const char *key, const char *value ); +qboolean Info_SetValueForStarKey( char *s, const char *key, const char *value, int maxsize ); qboolean Info_Validate( const char *s ); void Info_Print( const char *s ); char *Cvar_Userinfo( void ); diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index 4e6874a8..7d9925ba 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -118,7 +118,7 @@ qboolean Cmd_GetMapList( const char *s, char *completedname, int length ) { // if there are entities to parse, a missing message key just // means there is no title, so clear the message string now - char token[1024]; + char token[2048]; message[0] = 0; pfile = ents; @@ -709,7 +709,7 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) { // if there are entities to parse, a missing message key just // means there is no title, so clear the message string now - char token[1024]; + char token[2048]; qboolean worldspawn = true; Q_strncpy( message, "No Title", MAX_STRING ); diff --git a/engine/common/imagelib/imagelib.h b/engine/common/imagelib/imagelib.h index 94aaf8a1..ddaf66c9 100644 --- a/engine/common/imagelib/imagelib.h +++ b/engine/common/imagelib/imagelib.h @@ -94,6 +94,7 @@ typedef struct imglib_s image_hint_t hint; // hint for some loaders byte *tempbuffer; // for convert operations int cmd_flags; + int force_flags; // user-specified force flags } imglib_t; /* @@ -283,5 +284,6 @@ byte *Image_Copy( size_t size ); void Image_CopyParms( rgbdata_t *src ); qboolean Image_ValidSize( const char *name ); qboolean Image_LumpValidSize( const char *name ); +qboolean Image_CheckFlag( int bit ); #endif//IMAGELIB_H \ No newline at end of file diff --git a/engine/common/imagelib/img_bmp.c b/engine/common/imagelib/img_bmp.c index 776d21a3..12b08f01 100644 --- a/engine/common/imagelib/img_bmp.c +++ b/engine/common/imagelib/img_bmp.c @@ -124,7 +124,7 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize ) } } - if( image.cmd_flags & IL_KEEP_8BIT && bhdr.bitsPerPixel == 8 ) + if( Image_CheckFlag( IL_KEEP_8BIT ) && bhdr.bitsPerPixel == 8 ) { pixbuf = image.palette = Mem_Alloc( host.imagepool, 1024 ); image.flags |= IMAGE_HAS_COLOR; @@ -235,7 +235,7 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize ) blue = palette[palIndex][0]; alpha = palette[palIndex][3]; - if( image.cmd_flags & IL_KEEP_8BIT ) + if( Image_CheckFlag( IL_KEEP_8BIT )) { *pixbuf++ = palIndex; } @@ -280,7 +280,7 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, size_t filesize ) Mem_Free( image.rgba ); return false; } - if(!( image.cmd_flags & IL_KEEP_8BIT ) && ( red != green || green != blue )) + if( !Image_CheckFlag( IL_KEEP_8BIT ) && ( red != green || green != blue )) image.flags |= IMAGE_HAS_COLOR; } buf_p += padSize; // actual only for 4-bit bmps @@ -304,7 +304,7 @@ qboolean Image_SaveBMP( const char *name, rgbdata_t *pix ) int pixel_size; int i, x, y; - if( FS_FileExists( name, false ) && !(image.cmd_flags & IL_ALLOW_OVERWRITE )) + if( FS_FileExists( name, false ) && !Image_CheckFlag( IL_ALLOW_OVERWRITE )) return false; // already existed // bogus parameter check diff --git a/engine/common/imagelib/img_main.c b/engine/common/imagelib/img_main.c index 2db8fa46..9d95cd96 100644 --- a/engine/common/imagelib/img_main.c +++ b/engine/common/imagelib/img_main.c @@ -118,6 +118,9 @@ rgbdata_t *ImagePack( void ) { rgbdata_t *pack = Mem_Alloc( host.imagepool, sizeof( rgbdata_t )); + // clear any force flags + image.force_flags = 0; + if( image.cubemap && image.num_sides != 6 ) { // this neved be happens, just in case @@ -339,6 +342,9 @@ load_internal: else if( filename[0] != '#' ) MsgDev( D_WARN, "FS_LoadImage: couldn't load \"%s\"\n", loadname ); + // clear any force flags + image.force_flags = 0; + return NULL; } @@ -356,7 +362,13 @@ qboolean FS_SaveImage( const char *filename, rgbdata_t *pix ) string path, savename; const savepixformat_t *format; - if( !pix || !pix->buffer || anyformat ) return false; + if( !pix || !pix->buffer || anyformat ) + { + // clear any force flags + image.force_flags = 0; + return false; + } + Q_strncpy( savename, filename, sizeof( savename )); FS_StripExtension( savename ); // remove extension if needed @@ -371,7 +383,12 @@ qboolean FS_SaveImage( const char *filename, rgbdata_t *pix ) box = skybox_qv1; else if( pix->flags & IMAGE_CUBEMAP ) box = cubemap_v2; - else return false; // do not happens + else + { + // clear any force flags + image.force_flags = 0; + return false; // do not happens + } pix->size /= 6; // now set as side size picBuffer = pix->buffer; @@ -392,6 +409,9 @@ qboolean FS_SaveImage( const char *filename, rgbdata_t *pix ) pix->size = realSize; pix->buffer = picBuffer; + // clear any force flags + image.force_flags = 0; + return ( i == 6 ); } } @@ -403,10 +423,19 @@ qboolean FS_SaveImage( const char *filename, rgbdata_t *pix ) if( !Q_stricmp( ext, format->ext )) { Q_sprintf( path, format->formatstring, savename, "", format->ext ); - if( format->savefunc( path, pix )) return true; // saved + if( format->savefunc( path, pix )) + { + // clear any force flags + image.force_flags = 0; + return true; // saved + } } } } + + // clear any force flags + image.force_flags = 0; + return false; } @@ -426,4 +455,46 @@ void FS_FreeImage( rgbdata_t *pack ) Mem_Free( pack ); } else MsgDev( D_WARN, "FS_FreeImage: trying to free NULL image\n" ); +} + +/* +================ +FS_CopyImage + +make an image copy +================ +*/ +rgbdata_t *FS_CopyImage( rgbdata_t *in ) +{ + rgbdata_t *out; + int palSize = 0; + + if( !in ) return NULL; + + out = Mem_Alloc( host.imagepool, sizeof( rgbdata_t )); + *out = *in; + + switch( in->type ) + { + case PF_INDEXED_24: + palSize = 768; + break; + case PF_INDEXED_32: + palSize = 1024; + break; + } + + if( palSize ) + { + out->palette = Mem_Alloc( host.imagepool, palSize ); + Q_memcpy( out->palette, in->palette, palSize ); + } + + if( in->size ) + { + out->buffer = Mem_Alloc( host.imagepool, in->size ); + Q_memcpy( out->buffer, in->buffer, in->size ); + } + + return out; } \ No newline at end of file diff --git a/engine/common/imagelib/img_tga.c b/engine/common/imagelib/img_tga.c index fc5e63f4..932f3870 100644 --- a/engine/common/imagelib/img_tga.c +++ b/engine/common/imagelib/img_tga.c @@ -126,7 +126,7 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, size_t filesize ) targa_rgba = image.rgba = Mem_Alloc( host.imagepool, image.size ); // if bit 5 of attributes isn't set, the image has been stored from bottom to top - if( targa_header.attributes & 0x20 ) + if( !Image_CheckFlag( IL_DONTFLIP_TGA ) && targa_header.attributes & 0x20 ) { pixbuf = targa_rgba; row_inc = 0; @@ -221,7 +221,7 @@ qboolean Image_SaveTGA( const char *name, rgbdata_t *pix ) byte *buffer, *out; const char *comment = "Generated by Xash ImageLib\0"; - if( FS_FileExists( name, false ) && !( image.cmd_flags & IL_ALLOW_OVERWRITE )) + if( FS_FileExists( name, false ) && !Image_CheckFlag( IL_ALLOW_OVERWRITE )) return false; // already existed if( pix->flags & IMAGE_HAS_ALPHA ) diff --git a/engine/common/imagelib/img_utils.c b/engine/common/imagelib/img_utils.c index 111eb25b..1d1526b9 100644 --- a/engine/common/imagelib/img_utils.c +++ b/engine/common/imagelib/img_utils.c @@ -167,28 +167,40 @@ byte *Image_Copy( size_t size ) /* ================= -Image_NearestPOW +Image_CheckFlag ================= */ -int Image_NearestPOW( int value, qboolean roundDown ) +qboolean Image_CheckFlag( int bit ) { - int n = 1; + if( image.force_flags & bit ) + return true; - if( value <= 0 ) return 1; - while( n < value ) n <<= 1; + if( image.cmd_flags & bit ) + return true; - if( roundDown ) - { - if( n > value ) n >>= 1; - } - return n; + return false; } +/* +================= +Image_SetForceFlags +================= +*/ +void Image_SetForceFlags( uint flags ) +{ + image.force_flags = flags; +} + +/* +================= +Image_RoundDimensions +================= +*/ void Image_RoundDimensions( int *width, int *height ) { // find nearest power of two, rounding down if desired - *width = Image_NearestPOW( *width, gl_round_down->integer ); - *height = Image_NearestPOW( *height, gl_round_down->integer ); + *width = NearestPOW( *width, gl_round_down->integer ); + *height = NearestPOW( *height, gl_round_down->integer ); } qboolean Image_ValidSize( const char *name ) @@ -871,7 +883,7 @@ Image_Resample */ byte *Image_ResampleInternal( const void *indata, int inwidth, int inheight, int outwidth, int outheight, int type, qboolean *resampled ) { - qboolean quality = (image.cmd_flags & IL_USE_LERPING); + qboolean quality = Image_CheckFlag( IL_USE_LERPING ); // nothing to resample ? if( inwidth == outwidth && inheight == outheight ) @@ -915,7 +927,7 @@ Image_Flood */ byte *Image_FloodInternal( const byte *indata, int inwidth, int inheight, int outwidth, int outheight, int type, qboolean *resampled ) { - qboolean quality = (image.cmd_flags & IL_USE_LERPING); + qboolean quality = Image_CheckFlag( IL_USE_LERPING ); int samples = PFDesc[type].bpp; int newsize, x, y, i; byte *in, *out; @@ -1087,7 +1099,7 @@ qboolean Image_AddIndexedImageToPack( const byte *in, int width, int height ) int mipsize = width * height; qboolean expand_to_rgba = true; - if( image.cmd_flags & IL_KEEP_8BIT ) + if( Image_CheckFlag( IL_KEEP_8BIT )) expand_to_rgba = false; else if( host.type == HOST_NORMAL && ( image.flags & ( IMAGE_HAS_LUMA|IMAGE_QUAKESKY ))) expand_to_rgba = false; @@ -1243,10 +1255,16 @@ qboolean Image_Process( rgbdata_t **pix, int width, int height, float gamma, uin if( !pic || !pic->buffer ) { MsgDev( D_WARN, "Image_Process: NULL image\n" ); + image.force_flags = 0; return false; } - if( !flags ) return false; // no operation specfied + if( !flags ) + { + // clear any force flags + image.force_flags = 0; + return false; // no operation specfied + } if( flags & IMAGE_MAKE_LUMA ) { @@ -1282,8 +1300,8 @@ qboolean Image_Process( rgbdata_t **pix, int width, int height, float gamma, uin if( flags & IMAGE_ROUNDFILLER ) { // roundfiller always must roundup - w = Image_NearestPOW( w, false ); - h = Image_NearestPOW( h, false ); + w = NearestPOW( w, false ); + h = NearestPOW( h, false ); } else Image_RoundDimensions( &w, &h ); @@ -1313,5 +1331,8 @@ qboolean Image_Process( rgbdata_t **pix, int width, int height, float gamma, uin } *pix = pic; + // clear any force flags + image.force_flags = 0; + return result; } \ No newline at end of file diff --git a/engine/common/imagelib/img_wad.c b/engine/common/imagelib/img_wad.c index 3eefbb76..5a953c21 100644 --- a/engine/common/imagelib/img_wad.c +++ b/engine/common/imagelib/img_wad.c @@ -14,6 +14,7 @@ GNU General Public License for more details. */ #include "imagelib.h" +#include "mathlib.h" #include "wadfile.h" #include "studio.h" #include "sprite.h" @@ -474,6 +475,16 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, size_t filesize ) // grab the fog density image.fogParams[3] = pal[4*3+0]; } + else if( host.decal_loading ) + { + // grab the decal color + image.fogParams[0] = pal[255*3+0]; + image.fogParams[1] = pal[255*3+1]; + image.fogParams[2] = pal[255*3+2]; + + // calc the decal reflectivity + image.fogParams[3] = VectorAvg( image.fogParams ); + } image.type = PF_INDEXED_32; // 32-bit palete return Image_AddIndexedImageToPack( fin, image.width, image.height ); diff --git a/engine/common/infostring.c b/engine/common/infostring.c index 075d4e42..270510fa 100644 --- a/engine/common/infostring.c +++ b/engine/common/infostring.c @@ -214,10 +214,10 @@ qboolean Info_Validate( const char *s ) return true; } -qboolean Info_SetValueForKey( char *s, const char *key, const char *value ) +qboolean Info_SetValueForStarKey( char *s, const char *key, const char *value, int maxsize ) { - char newi[MAX_INFO_STRING], *v; - int c, maxsize = MAX_INFO_STRING; + char new[1024], *v; + int c; if( Q_strstr( key, "\\" ) || Q_strstr( value, "\\" )) { @@ -243,12 +243,24 @@ qboolean Info_SetValueForKey( char *s, const char *key, const char *value ) return false; } - Info_RemoveKey( s, key ); - if( !value || !Q_strlen( value )) - return true; // just clear variable + // this next line is kinda trippy + if( *(v = Info_ValueForKey( s, key ))) + { + // key exists, make sure we have enough room for new value, if we don't, don't change it! + if( Q_strlen( value ) - Q_strlen( v ) + Q_strlen( s ) > maxsize ) + { + MsgDev( D_ERROR, "SetValueForKey: info string length exceeded\n" ); + return false; + } + } - Q_snprintf( newi, sizeof( newi ) - 1, "\\%s\\%s", key, value ); - if( Q_strlen( newi ) + Q_strlen( s ) > maxsize ) + Info_RemoveKey( s, key ); + + if( !value || !Q_strlen( value )) + return true; // just clear variable + + Q_snprintf( new, sizeof( new ) - 1, "\\%s\\%s", key, value ); + if( Q_strlen( new ) + Q_strlen( s ) > maxsize ) { MsgDev( D_ERROR, "SetValueForKey: info string length exceeded\n" ); return true; // info changed, new value can't saved @@ -256,7 +268,7 @@ qboolean Info_SetValueForKey( char *s, const char *key, const char *value ) // only copy ascii values s += Q_strlen( s ); - v = newi; + v = new; while( *v ) { @@ -271,6 +283,17 @@ qboolean Info_SetValueForKey( char *s, const char *key, const char *value ) return true; } +qboolean Info_SetValueForKey( char *s, const char *key, const char *value ) +{ + if( key[0] == '*' ) + { + MsgDev( D_ERROR, "Can't set *keys\n" ); + return false; + } + + return Info_SetValueForStarKey( s, key, value, MAX_INFO_STRING ); +} + static void Cvar_LookupBitInfo( const char *name, const char *string, const char *info, void *unused ) { Info_SetValueForKey( (char *)info, name, string ); diff --git a/engine/common/mathlib.c b/engine/common/mathlib.c index 34a73d2d..da69f38a 100644 --- a/engine/common/mathlib.c +++ b/engine/common/mathlib.c @@ -119,23 +119,22 @@ float VectorNormalizeLength2( const vec3_t v, vec3_t out ) out[1] = v[1] * ilength; out[2] = v[2] * ilength; } - else out[0] = out[1] = out[2] = 0.0f; return length; } -void VectorVectors( vec3_t forward, vec3_t right, vec3_t up ) +void VectorVectors( const vec3_t forward, vec3_t right, vec3_t up ) { - float d; + float d; right[0] = forward[2]; right[1] = -forward[0]; right[2] = forward[1]; - d = DotProduct(forward, right); - VectorMA(right, -d, forward, right); - VectorNormalize(right); - CrossProduct(right, forward, up); + d = DotProduct( forward, right ); + VectorMA( right, -d, forward, right ); + VectorNormalize( right ); + CrossProduct( right, forward, up ); } /* diff --git a/engine/common/mathlib.h b/engine/common/mathlib.h index 268daae7..ab9b74df 100644 --- a/engine/common/mathlib.h +++ b/engine/common/mathlib.h @@ -105,7 +105,7 @@ int SignbitsForPlane( const vec3_t normal ); int NearestPOW( int value, qboolean roundDown ); void SinCos( float radians, float *sine, float *cosine ); float VectorNormalizeLength2( const vec3_t v, vec3_t out ); -void VectorVectors( vec3_t forward, vec3_t right, vec3_t up ); +void VectorVectors( const vec3_t forward, vec3_t right, vec3_t up ); void VectorAngles( const float *forward, float *angles ); void AngleVectors( const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up ); void VectorsAngles( const vec3_t forward, const vec3_t right, const vec3_t up, vec3_t angles ); diff --git a/engine/common/net_encode.c b/engine/common/net_encode.c index 603d7f12..19452a69 100644 --- a/engine/common/net_encode.c +++ b/engine/common/net_encode.c @@ -774,7 +774,6 @@ void Delta_InitFields( void ) Mem_Free( afile ); // adding some requrid fields fields that user may forget or don't know how to specified - Delta_AddField( "event_t", "flags", DT_INTEGER, 8, 1.0f, 1.0f ); Delta_AddField( "event_t", "velocity[0]", DT_SIGNED | DT_FLOAT, 16, 8.0f, 1.0f ); Delta_AddField( "event_t", "velocity[1]", DT_SIGNED | DT_FLOAT, 16, 8.0f, 1.0f ); Delta_AddField( "event_t", "velocity[2]", DT_SIGNED | DT_FLOAT, 16, 8.0f, 1.0f ); @@ -793,7 +792,7 @@ void Delta_Init( void ) dt = Delta_FindStruct( "movevars_t" ); ASSERT( dt != NULL ); - if( dt->bInitialized ) return; // specified by user + if( dt->bInitialized ) return; // "movevars_t" already specified by user // create movevars_t delta internal Delta_AddField( "movevars_t", "gravity", DT_FLOAT|DT_SIGNED, 16, 8.0f, 1.0f ); diff --git a/engine/common/network.c b/engine/common/network.c index ad8ac08f..b770e7d7 100644 --- a/engine/common/network.c +++ b/engine/common/network.c @@ -589,7 +589,7 @@ static int NET_IPSocket( const char *netInterface, int port ) struct sockaddr_in addr; dword _true = 1; - MsgDev( D_INFO, "NET_UDPSocket( %s, %i )\n", netInterface, port ); + MsgDev( D_NOTE, "NET_UDPSocket( %s, %i )\n", netInterface, port ); if(( net_socket = pSocket( PF_INET, SOCK_DGRAM, IPPROTO_UDP )) == SOCKET_ERROR ) { @@ -681,7 +681,7 @@ static int NET_IPXSocket( int port ) int _true = 1; int err; - MsgDev( D_INFO, "NET_IPXSocket( %i )\n", port ); + MsgDev( D_NOTE, "NET_IPXSocket( %i )\n", port ); if(( net_socket = pSocket( PF_IPX, SOCK_DGRAM, NSPROTO_IPX )) == SOCKET_ERROR ) { diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 4cd285fc..47d93f83 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -16,7 +16,7 @@ GNU General Public License for more details. #ifndef PROTOCOL_H #define PROTOCOL_H -#define PROTOCOL_VERSION 42 +#define PROTOCOL_VERSION 43 // server to client #define svc_bad 0 // immediately crash client when received @@ -100,6 +100,9 @@ GNU General Public License for more details. #define MAX_SOUND_BITS 11 #define MAX_SOUNDS (1<integer; i++ ) + { + if( svs.clients[i].state >= cs_connected ) + { + edict_t *ed = svs.clients[i].edict; + float time = host.realtime - svs.clients[i].lastconnect; + Q_strncat( string, va( "%c\\%s\\%i\\%f\\", count, svs.clients[i].name, ed->v.frags, time ), sizeof( string )); + count++; + } + } + + // send playernames + Q_snprintf( answer, sizeof( answer ), "netinfo %i %i %s\n", context, type, string ); + Netchan_OutOfBandPrint( NS_SERVER, from, answer ); // no info string + } + else if( type == NETAPI_REQUEST_DETAILS ) + { + for( i = 0; i < sv_maxclients->integer; i++ ) + if( svs.clients[i].state >= cs_connected ) + count++; + + string[0] = '\0'; + Info_SetValueForKey( string, "hostname", hostname->string ); + Info_SetValueForKey( string, "gamedir", GI->gamefolder ); + Info_SetValueForKey( string, "current", va( "%i", count )); + Info_SetValueForKey( string, "max", va( "%i", sv_maxclients->integer )); + Info_SetValueForKey( string, "map", sv.name ); + + // send serverinfo + Q_snprintf( answer, sizeof( answer ), "netinfo %i %i %s\n", context, type, string ); + Netchan_OutOfBandPrint( NS_SERVER, from, answer ); // no info string + } } /* @@ -2111,6 +2151,10 @@ void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg ) } cl->delta_sequence = -1; // no delta unless requested + + // set the current client + svs.currentPlayer = cl; + svs.currentPlayerNum = (cl - svs.clients); // read optional clientCommand strings while( cl->state != cs_zombie ) diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index bf26049c..8ba03510 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -738,8 +738,8 @@ void SV_PlayersOnly_f( void ) sv.hostflags = sv.hostflags ^ SVF_PLAYERSONLY; if(!( sv.hostflags & SVF_PLAYERSONLY )) - SV_BroadcastPrintf( D_INFO, "Resume server physic" ); - else SV_BroadcastPrintf( D_INFO, "Freeze server physic" ); + SV_BroadcastPrintf( D_INFO, "Resume server physic\n" ); + else SV_BroadcastPrintf( D_INFO, "Freeze server physic\n" ); } void SV_EdictsInfo_f( void ) diff --git a/engine/server/sv_frame.c b/engine/server/sv_frame.c index f8f0c515..524bbd79 100644 --- a/engine/server/sv_frame.c +++ b/engine/server/sv_frame.c @@ -268,45 +268,116 @@ SV_EmitEvents */ static void SV_EmitEvents( sv_client_t *cl, client_frame_t *to, sizebuf_t *msg ) { - int i, ev; event_state_t *es; event_info_t *info; + entity_state_t *state; + event_args_t nullargs; int ev_count = 0; - int c; + int count, ent_index; + int i, j, ev; + + Q_memset( &nullargs, 0, sizeof( nullargs )); es = &cl->events; // count events for( ev = 0; ev < MAX_EVENT_QUEUE; ev++ ) { - info = &es->ei[ev]; - if( info->index == 0 ) - continue; - ev_count++; + if( es->ei[ev].index ) ev_count++; } // nothing to send - if( !ev_count ) return; + if( !ev_count ) return; // nothing to send - if( ev_count >= MAX_EVENT_QUEUE ) - ev_count = MAX_EVENT_QUEUE - 1; + if( ev_count >= 31 ) ev_count = 31; + + for( i = 0; i < MAX_EVENT_QUEUE; i++ ) + { + info = &es->ei[i]; + if( info->index == 0 ) + continue; + + ent_index = info->entity_index; + + for( j = 0; j < to->num_entities; j++ ) + { + state = &svs.packet_entities[(to->first_entity+j)%svs.num_client_entities]; + if( state->number == ent_index ) + break; + } + + if( j >= to->num_entities ) + { + // couldn't find + info->packet_index = to->num_entities; + info->args.entindex = ent_index; + } + else + { + info->packet_index = j; + info->args.ducking = 0; + + if(!( info->args.flags & FEVENT_ORIGIN )) + VectorClear( info->args.origin ); + + if(!( info->args.flags & FEVENT_ANGLES )) + VectorClear( info->args.angles ); + + VectorClear( info->args.velocity ); + } + } BF_WriteByte( msg, svc_event ); // create message - BF_WriteByte( msg, ev_count ); // Up to MAX_EVENT_QUEUE events + BF_WriteUBitLong( msg, ev_count, 5 ); // up to MAX_EVENT_QUEUE events - for( i = c = 0 ; i < MAX_EVENT_QUEUE; i++ ) + for( count = i = 0; i < MAX_EVENT_QUEUE; i++ ) { info = &es->ei[i]; if( info->index == 0 ) + { + info->packet_index = -1; + info->entity_index = -1; continue; + } // only send if there's room - if ( c < ev_count ) - SV_PlaybackEvent( msg, info ); + if( count < ev_count ) + { + BF_WriteUBitLong( msg, info->index, MAX_EVENT_BITS ); // 1024 events + + if( info->packet_index == -1 ) + { + BF_WriteOneBit( msg, 0 ); + } + else + { + BF_WriteOneBit( msg, 1 ); + BF_WriteUBitLong( msg, info->packet_index, MAX_ENTITY_BITS ); + + if( !memcmp( &nullargs, &info->args, sizeof( event_args_t ))) + { + BF_WriteOneBit( msg, 0 ); + } + else + { + BF_WriteOneBit( msg, 1 ); + MSG_WriteDeltaEvent( msg, &nullargs, &info->args ); + } + } + + if( info->fire_time ) + { + BF_WriteOneBit( msg, 1 ); + BF_WriteWord( msg, Q_rint( info->fire_time * 100.0f )); + } + else BF_WriteOneBit( msg, 0 ); + } info->index = 0; - c++; + info->packet_index = -1; + info->entity_index = -1; + count++; } } diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 7c350319..28c445c7 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -605,7 +605,7 @@ int SV_MapIsValid( const char *filename, const char *spawn_entity, const char *l { // if there are entities to parse, a missing message key just // means there is no title, so clear the message string now - char token[1024]; + char token[2048]; string check_name; qboolean need_landmark = Q_strlen( landmark_name ) > 0 ? true : false; @@ -795,18 +795,30 @@ void SV_FreeEdicts( void ) } } -void SV_PlaybackEvent( sizebuf_t *msg, event_info_t *info ) +void SV_PlaybackReliableEvent( sizebuf_t *msg, word eventindex, float delay, event_args_t *args ) { event_args_t nullargs; ASSERT( msg ); - ASSERT( info ); + ASSERT( args ); Q_memset( &nullargs, 0, sizeof( nullargs )); - BF_WriteWord( msg, info->index ); // send event index - BF_WriteWord( msg, (int)( info->fire_time * 100.0f )); // send event delay - MSG_WriteDeltaEvent( msg, &nullargs, &info->args ); // reliable events not use delta + BF_WriteByte( msg, svc_event_reliable ); + + // send event index + BF_WriteUBitLong( msg, eventindex, MAX_EVENT_BITS ); + + if( delay ) + { + // send event delay + BF_WriteOneBit( msg, 1 ); + BF_WriteWord( msg, Q_rint( delay * 100.0f )); + } + else BF_WriteOneBit( msg, 0 ); + + // reliable events not use delta-compression just null-compression + MSG_WriteDeltaEvent( msg, &nullargs, args ); } const char *SV_ClassName( const edict_t *e ) @@ -1761,33 +1773,14 @@ void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float if( attn != ATTN_NONE ) flags |= SND_ATTENUATION; if( pitch != PITCH_NORM ) flags |= SND_PITCH; - // ultimate method for detect bsp models with invalid solidity (e.g. func_pushable) - if( Mod_GetType( ent->v.modelindex ) == mod_brush ) - { - VectorAverage( ent->v.absmin, ent->v.absmax, origin ); + VectorAverage( ent->v.mins, ent->v.maxs, origin ); + VectorAdd( origin, ent->v.origin, origin ); - if( flags & SND_SPAWNING ) - { - msg_dest = MSG_INIT; - } - else - { - if( chan == CHAN_STATIC ) - msg_dest = MSG_ALL; - else msg_dest = MSG_PAS_R; - } - } - else - { - VectorAverage( ent->v.mins, ent->v.maxs, origin ); - VectorAdd( origin, ent->v.origin, origin ); - - if( flags & SND_SPAWNING ) - { - msg_dest = MSG_INIT; - } - else msg_dest = MSG_PAS_R; - } + if( flags & SND_SPAWNING ) + msg_dest = MSG_INIT; + else if( chan == CHAN_STATIC ) + msg_dest = MSG_ALL; + else msg_dest = MSG_PAS_R; // always sending stop sound command if( flags & SND_STOP ) msg_dest = MSG_ALL; @@ -1845,7 +1838,6 @@ void pfnEmitAmbientSound( edict_t *ent, float *pos, const char *sample, float vo { int number = 0, sound_idx; int msg_dest = MSG_PAS_R; - vec3_t origin; if( !sample ) return; @@ -1870,24 +1862,8 @@ void pfnEmitAmbientSound( edict_t *ent, float *pos, const char *sample, float vo msg_dest = MSG_INIT; else msg_dest = MSG_ALL; - // ultimate method for detect bsp models with invalid solidity (e.g. func_pushable) if( SV_IsValidEdict( ent )) - { - if( Mod_GetType( ent->v.modelindex ) == mod_brush ) - { - VectorAverage( ent->v.absmin, ent->v.absmax, origin ); - number = NUM_FOR_EDICT( ent ); - } - else - { - VectorAverage( ent->v.mins, ent->v.maxs, origin ); - VectorAdd( origin, ent->v.origin, origin ); - } - } - else - { - VectorCopy( pos, origin ); - } + number = NUM_FOR_EDICT( ent ); // always sending stop sound command if( flags & SND_STOP ) msg_dest = MSG_ALL; @@ -1929,7 +1905,7 @@ void pfnEmitAmbientSound( edict_t *ent, float *pos, const char *sample, float vo BF_WriteWord( &sv.multicast, number ); BF_WriteBitVec3Coord( &sv.multicast, pos ); - SV_Send( msg_dest, origin, NULL ); + SV_Send( msg_dest, pos, NULL ); } /* @@ -3285,7 +3261,7 @@ pfnRunPlayerMove */ void pfnRunPlayerMove( edict_t *pClient, const float *v_angle, float fmove, float smove, float upmove, word buttons, byte impulse, byte msec ) { - sv_client_t *cl; + sv_client_t *cl, *oldcl; usercmd_t cmd; uint seed; @@ -3300,6 +3276,11 @@ void pfnRunPlayerMove( edict_t *pClient, const float *v_angle, float fmove, floa if( !cl->fakeclient ) return; // only fakeclients allows + oldcl = svs.currentPlayer; + + svs.currentPlayer = SV_ClientFromEdict( pClient, true ); + svs.currentPlayerNum = (svs.currentPlayer - svs.clients); + Q_memset( &cmd, 0, sizeof( cmd )); if( v_angle ) VectorCopy( v_angle, cmd.viewangles ); cmd.forwardmove = fmove; @@ -3317,6 +3298,9 @@ void pfnRunPlayerMove( edict_t *pClient, const float *v_angle, float fmove, floa cl->lastcmd = cmd; cl->lastcmd.buttons = 0; // avoid multiple fires on lag + + svs.currentPlayer = oldcl; + svs.currentPlayerNum = (svs.currentPlayer - svs.clients); } /* @@ -3466,10 +3450,13 @@ void SV_PlaybackEventFull( int flags, const edict_t *pInvoker, word eventindex, event_info_t *ei = NULL; float *viewOrg = NULL; int j, leafnum, slot, bestslot; - int invokerIndex = 0; + int invokerIndex; byte *mask = NULL; vec3_t pvspoint; + if( flags & FEV_CLIENT ) + return; // someone stupid joke + // first check event for out of bounds if( eventindex < 1 || eventindex > MAX_EVENTS ) { @@ -3484,13 +3471,21 @@ void SV_PlaybackEventFull( int flags, const edict_t *pInvoker, word eventindex, return; } - args.flags = 0; - if( SV_IsValidEdict( pInvoker )) - args.entindex = NUM_FOR_EDICT( pInvoker ); - else args.entindex = 0; - VectorCopy( origin, args.origin ); - VectorCopy( angles, args.angles ); + Q_memset( &args, 0, sizeof( args )); + if( origin && !VectorIsNull( origin )) + { + VectorCopy( origin, args.origin ); + args.flags |= FEVENT_ORIGIN; + } + + if( angles && !VectorIsNull( angles )) + { + VectorCopy( angles, args.angles ); + args.flags |= FEVENT_ORIGIN; + } + + // copy other parms args.fparam1 = fparam1; args.fparam2 = fparam2; args.iparam1 = iparam1; @@ -3498,24 +3493,38 @@ void SV_PlaybackEventFull( int flags, const edict_t *pInvoker, word eventindex, args.bparam1 = bparam1; args.bparam2 = bparam2; - if(!( flags & FEV_GLOBAL )) + VectorClear( pvspoint ); + + if( SV_IsValidEdict( pInvoker )) + { + VectorCopy( pInvoker->v.origin, pvspoint ); + args.entindex = invokerIndex = NUM_FOR_EDICT( pInvoker ); + + // g-cont. allow 'ducking' param for all entities + args.ducking = (pInvoker->v.flags & FL_DUCKING) ? true : false; + + // this will be send only for reliable event + if(!( args.flags & FEVENT_ORIGIN )) + VectorCopy( pInvoker->v.origin, args.origin ); + + // this will be send only for reliable event + if(!( args.flags & FEVENT_ANGLES )) + VectorCopy( pInvoker->v.angles, args.angles ); + + if( sv_sendvelocity->integer ) + VectorCopy( pInvoker->v.velocity, args.velocity ); + } + else + { + VectorCopy( args.origin, pvspoint ); + args.entindex = 0; + invokerIndex = -1; + } + + if(!( flags & FEV_GLOBAL ) && VectorIsNull( pvspoint )) { - // PVS message - trying to get a pvspoint - // args.origin always have higher priority than invoker->origin - if( !VectorIsNull( args.origin )) - { - VectorCopy( args.origin, pvspoint ); - } - else if( SV_IsValidEdict( pInvoker )) - { - VectorCopy( pInvoker->v.origin, pvspoint ); - } - else - { - const char *ev_name = sv.event_precache[eventindex]; - MsgDev( D_ERROR, "%s: not a FEV_GLOBAL event missing origin. Ignored.\n", ev_name ); - return; - } + MsgDev( D_ERROR, "%s: not a FEV_GLOBAL event missing origin. Ignored.\n", sv.event_precache[eventindex] ); + return; } // check event for some user errors @@ -3525,47 +3534,21 @@ void SV_PlaybackEventFull( int flags, const edict_t *pInvoker, word eventindex, { const char *ev_name = sv.event_precache[eventindex]; if( flags & FEV_NOTHOST ) + { MsgDev( D_WARN, "%s: specified FEV_NOTHOST when invoker not a client\n", ev_name ); + flags &= ~FEV_NOTHOST; + } + if( flags & FEV_HOSTONLY ) + { MsgDev( D_WARN, "%s: specified FEV_HOSTONLY when invoker not a client\n", ev_name ); - // pInvoker isn't a client - flags &= ~(FEV_NOTHOST|FEV_HOSTONLY); + flags &= ~FEV_HOSTONLY; + } } } - flags |= FEV_SERVER; // it's a server event! - - if( delay < 0.0f ) delay = 0.0f; // fixup negative delays - - if( SV_IsValidEdict( pInvoker )) - invokerIndex = NUM_FOR_EDICT( pInvoker ); - - if( flags & FEV_RELIABLE ) - { - VectorClear( args.velocity ); - args.ducking = 0; - } - else if( invokerIndex ) - { - // get up some info from invoker - if( VectorIsNull( args.origin )) - VectorCopy( pInvoker->v.origin, args.origin ); - if( VectorIsNull( args.angles )) - { - if( SV_ClientFromEdict( pInvoker, true )) - VectorCopy( pInvoker->v.v_angle, args.angles ); - else VectorCopy( pInvoker->v.angles, args.angles ); - } - else if( SV_ClientFromEdict( pInvoker, true ) && VectorCompare( pInvoker->v.angles, args.angles )) - { - // NOTE: if user specified pPlayer->pev->angles - // silently replace it with viewangles, client expected this - VectorCopy( pInvoker->v.v_angle, args.angles ); - } - - if( sv_sendvelocity->integer ) VectorCopy( pInvoker->v.velocity, args.velocity ); - args.ducking = (pInvoker->v.flags & FL_DUCKING) ? true : false; - } + flags |= FEV_SERVER; // it's a server event! + if( delay < 0.0f ) delay = 0.0f; // fixup negative delays if(!( flags & FEV_GLOBAL )) { @@ -3582,19 +3565,14 @@ void SV_PlaybackEventFull( int flags, const edict_t *pInvoker, word eventindex, if( cl->state != cs_spawned || !cl->edict || cl->fakeclient ) continue; - if( flags & FEV_NOTHOST && cl->edict == pInvoker && cl->local_weapons ) - continue; // will be played on client side - - if( flags & FEV_HOSTONLY && cl->edict != pInvoker ) - continue; // sending only to invoker - if( SV_IsValidEdict( pInvoker ) && pInvoker->v.groupinfo && cl->edict->v.groupinfo ) { - if(( !svs.groupop && !(cl->edict->v.groupinfo & pInvoker->v.groupinfo)) || ( svs.groupop == 1 && ( cl->edict->v.groupinfo & pInvoker->v.groupinfo ))) + if(( svs.groupop == 0 && (cl->edict->v.groupinfo & pInvoker->v.groupinfo) == 0 ) + || ( svs.groupop == 1 && (cl->edict->v.groupinfo & pInvoker->v.groupinfo) == 1 )) continue; } - if( mask && !( flags & FEV_GLOBAL )) + if( mask && SV_IsValidEdict( pInvoker )) { int clientnum; @@ -3611,23 +3589,19 @@ void SV_PlaybackEventFull( int flags, const edict_t *pInvoker, word eventindex, continue; } + if( flags & FEV_NOTHOST && cl == svs.currentPlayer && cl->local_weapons ) + continue; // will be played on client side + + if( flags & FEV_HOSTONLY && cl->edict != pInvoker ) + continue; // sending only to invoker + // all checks passed, send the event // reliable event if( flags & FEV_RELIABLE ) { - event_info_t info; - - info.index = eventindex; - info.fire_time = delay; - info.args = args; - info.entity_index = invokerIndex; - info.packet_index = -1; - info.flags = 0; // server ignore flags - - // skipping queue, write in reliable datagram - BF_WriteByte( &cl->netchan.message, svc_event_reliable ); - SV_PlaybackEvent( &cl->netchan.message, &info ); + // skipping queue, write direct into reliable datagram + SV_PlaybackReliableEvent( &cl->netchan.message, eventindex, delay, &args ); continue; } @@ -3635,27 +3609,50 @@ void SV_PlaybackEventFull( int flags, const edict_t *pInvoker, word eventindex, es = &cl->events; bestslot = -1; - for( j = 0; j < MAX_EVENT_QUEUE; j++ ) + if( flags & FEV_UPDATE ) { - ei = &es->ei[j]; - - if( ei->index == 0 ) + for( j = 0; j < MAX_EVENT_QUEUE; j++ ) { - // found an empty slot - bestslot = j; - break; + ei = &es->ei[j]; + if( ei->index == eventindex && invokerIndex != -1 && invokerIndex == ei->entity_index ) + { + bestslot = j; + break; + } + } + } + + if( bestslot == -1 ) + { + for( j = 0; j < MAX_EVENT_QUEUE; j++ ) + { + ei = &es->ei[j]; + + if( ei->index == 0 ) + { + // found an empty slot + bestslot = j; + break; + } + } + + // g-cont. probably this code never calls (and not needs) + if( bestslot != -1 && j == MAX_EVENT_QUEUE ) + { + ei = &es->ei[bestslot]; } } // no slot found for this player, oh well if( bestslot == -1 ) continue; + // add event to queue ei->index = eventindex; ei->fire_time = delay; - ei->args = args; ei->entity_index = invokerIndex; ei->packet_index = -1; - ei->flags = 0; // server ignore flags + ei->flags = flags; + ei->args = args; } } @@ -4335,7 +4332,7 @@ qboolean SV_ParseEdict( char **pfile, edict_t *ent ) KeyValueData pkvd[256]; // per one entity int i, numpairs = 0; const char *classname = NULL; - char token[1024]; + char token[2048]; // go through all the dictionary pairs while( 1 ) @@ -4447,7 +4444,7 @@ parsing textual entity definitions out of an ent file. */ void SV_LoadFromFile( char *entities ) { - string token; + char token[2048]; int inhibited, spawned; qboolean create_world = true; edict_t *ent; diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 82c09fe5..4bf4c1b5 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -361,6 +361,7 @@ void SV_ReadPackets( void ) { cl->lastmessage = host.realtime; // don't timeout SV_ExecuteClientMessage( cl, &net_message ); + svgame.globals->frametime = host.frametime; } } @@ -632,9 +633,9 @@ void SV_Init( void ) SV_InitOperatorCommands(); Cvar_Get ("skill", "1", CVAR_LATCH, "game skill level" ); - Cvar_Get ("deathmatch", "0", CVAR_LATCH|CVAR_SERVERNOTIFY, "displays deathmatch state" ); - Cvar_Get ("teamplay", "0", CVAR_LATCH|CVAR_SERVERNOTIFY, "displays teamplay state" ); - Cvar_Get ("coop", "0", CVAR_LATCH|CVAR_SERVERNOTIFY, "displays cooperative state" ); + Cvar_Get ("deathmatch", "0", CVAR_LATCH|CVAR_SERVERINFO, "displays deathmatch state" ); + Cvar_Get ("teamplay", "0", CVAR_LATCH|CVAR_SERVERINFO, "displays teamplay state" ); + Cvar_Get ("coop", "0", CVAR_LATCH|CVAR_SERVERINFO, "displays cooperative state" ); Cvar_Get ("protocol", va( "%i", PROTOCOL_VERSION ), CVAR_INIT, "displays server protocol version" ); Cvar_Get ("defaultmap", "", CVAR_SERVERNOTIFY, "holds the multiplayer mapname" ); Cvar_Get ("showtriggers", "0", CVAR_LATCH, "debug cvar shows triggers" ); @@ -672,7 +673,7 @@ void SV_Init( void ) rcon_password = Cvar_Get( "rcon_password", "", 0, "remote connect password" ); sv_stepsize = Cvar_Get( "sv_stepsize", "18", CVAR_ARCHIVE|CVAR_PHYSICINFO, "how high you can step up" ); sv_newunit = Cvar_Get( "sv_newunit", "0", 0, "sets to 1 while new unit is loading" ); - hostname = Cvar_Get( "hostname", "unnamed", CVAR_SERVERINFO|CVAR_SERVERNOTIFY|CVAR_ARCHIVE, "host name" ); + hostname = Cvar_Get( "hostname", "unnamed", CVAR_SERVERNOTIFY|CVAR_SERVERNOTIFY|CVAR_ARCHIVE, "host name" ); timeout = Cvar_Get( "timeout", "125", CVAR_SERVERNOTIFY, "connection timeout" ); zombietime = Cvar_Get( "zombietime", "2", CVAR_SERVERNOTIFY, "timeout for clients-zombie (who died but not respawned)" ); sv_pausable = Cvar_Get( "pausable", "1", CVAR_SERVERNOTIFY, "allow players to pause or not" ); diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 5d343f42..dad39017 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -1710,7 +1710,6 @@ void SV_Physics( void ) if( sv_skyspeed->value ) { // evaluate sky rotation. - // FIXME: ignore this feature in multiplayer to save traffic? float skyAngle = sv_skyangle->value + sv_skyspeed->value * host.frametime; Cvar_SetFloat( "sv_skyangle", anglemod( skyAngle )); } diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index eb079a17..b11e64f2 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -17,6 +17,7 @@ GNU General Public License for more details. #include "server.h" #include "const.h" #include "pm_local.h" +#include "event_flags.h" static qboolean has_update = false; @@ -382,6 +383,9 @@ static void pfnPlaybackEventFull( int flags, int clientindex, word eventindex, f ent = EDICT_NUM( clientindex + 1 ); if( !SV_IsValidEdict( ent )) return; + if( host.type == HOST_DEDICATED ) + flags |= FEV_NOTHOST; // no local clients for dedicated server + SV_PlaybackEventFull( flags, ent, eventindex, delay, origin, angles, fparam1, fparam2, @@ -971,8 +975,6 @@ void SV_PostRunCmd( sv_client_t *cl ) svgame.dllFuncs.pfnSpectatorThink( clent ); else svgame.dllFuncs.pfnPlayerPostThink( clent ); - // restore frametime svgame.globals->time = sv.time + host.frametime; - svgame.globals->frametime = host.frametime; svgame.dllFuncs.pfnCmdEnd( cl->edict ); } \ No newline at end of file diff --git a/engine/server/sv_world.c b/engine/server/sv_world.c index 83643d6d..b2c3985d 100644 --- a/engine/server/sv_world.c +++ b/engine/server/sv_world.c @@ -129,8 +129,8 @@ hull_t *SV_HullForEntity( edict_t *ent, int hullNumber, vec3_t mins, vec3_t maxs if( model && ( ent->v.solid == SOLID_BSP || ent->v.skin == CONTENTS_LADDER )) { // explicit hulls in the BSP model - if( ent->v.movetype != MOVETYPE_PUSH ) - Host_Error( "SOLID_BSP without MOVETYPE_PUSH\n" ); + if( ent->v.movetype != MOVETYPE_PUSH && ent->v.movetype != MOVETYPE_PUSHSTEP ) + Host_Error( "SOLID_BSP without MOVETYPE_PUSH or MOVETYPE_PUSHSTEP\n" ); if( model->type != mod_brush ) Host_Error( "MOVETYPE_PUSH with a non bsp model\n" ); diff --git a/mainui/basemenu.cpp b/mainui/basemenu.cpp index ac9dc5b4..a9681f7a 100644 --- a/mainui/basemenu.cpp +++ b/mainui/basemenu.cpp @@ -396,7 +396,7 @@ void UI_LoadBackgroundImage( void ) { bimage_t &bimage = uiStatic.m_SteamBackground[y][x]; sprintf(filename, "resource/background/800_%d_%c_loading.tga", y + 1, 'a' + x); - bimage.hImage = PIC_Load( filename ); + bimage.hImage = PIC_Load( filename, PIC_NOFLIP_TGA ); bimage.width = PIC_Width( bimage.hImage ); bimage.height = PIC_Height( bimage.hImage ); diff --git a/mainui/enginecallback.h b/mainui/enginecallback.h index c0964098..57169829 100644 --- a/mainui/enginecallback.h +++ b/mainui/enginecallback.h @@ -39,14 +39,14 @@ GNU General Public License for more details. #define GetLogoHeight (*g_engfuncs.pfnGetLogoHeight) #define GetLogoLength (*g_engfuncs.pfnGetLogoLength) -inline HIMAGE PIC_Load( const char *szPicName ) +inline HIMAGE PIC_Load( const char *szPicName, long flags = 0 ) { - return g_engfuncs.pfnPIC_Load( szPicName, NULL, 0 ); + return g_engfuncs.pfnPIC_Load( szPicName, NULL, 0, flags ); } -inline HIMAGE PIC_Load( const char *szPicName, const byte *ucRawImage, long ulRawImageSize ) +inline HIMAGE PIC_Load( const char *szPicName, const byte *ucRawImage, long ulRawImageSize, long flags = 0 ) { - return g_engfuncs.pfnPIC_Load( szPicName, ucRawImage, ulRawImageSize ); + return g_engfuncs.pfnPIC_Load( szPicName, ucRawImage, ulRawImageSize, flags ); } #define PIC_Free (*g_engfuncs.pfnPIC_Free) @@ -173,6 +173,7 @@ inline void TextMessageSetColor( int r, int g, int b, int alpha = 255 ) #define DrawConsoleString (*g_engfuncs.pfnDrawConsoleString) #define GetConsoleStringSize (*g_engfuncs.pfnDrawConsoleStringLen) #define ConsoleSetColor (*g_engfuncs.pfnSetConsoleDefaultColor) +#define PIC_SetFlags (*g_engfuncs.pfnPIC_SetFlags) #define RANDOM_LONG (*g_engfuncs.pfnRandomLong) #define RANDOM_FLOAT (*g_engfuncs.pfnRandomFloat) diff --git a/mainui/menu_vidoptions.cpp b/mainui/menu_vidoptions.cpp index d00b84f9..95383a7c 100644 --- a/mainui/menu_vidoptions.cpp +++ b/mainui/menu_vidoptions.cpp @@ -200,7 +200,7 @@ static void UI_VidOptions_Init( void ) { memset( &uiVidOptions, 0, sizeof( uiVidOptions_t )); - uiVidOptions.hTestImage = PIC_Load( ART_GAMMA ); + uiVidOptions.hTestImage = PIC_Load( ART_GAMMA, PIC_KEEP_RGBDATA ); uiVidOptions.menu.vidInitFunc = UI_VidOptions_Init;