Merge branch 'master' into biglolly

This commit is contained in:
Andrey Akhmichin 2024-02-19 22:13:56 +05:00
commit 0d9597fb56
60 changed files with 2767 additions and 335 deletions

View File

@ -47,6 +47,7 @@ option(USE_NOVGUI_SCOREBOARD "Prefer non-VGUI Scoreboard when USE_VGUI is enable
option(USE_VOICEMGR "Enable VOICE MANAGER." OFF)
option(BUILD_CLIENT "Build client dll" ON)
option(BUILD_SERVER "Build server dll" ON)
option(LTO "Enable interprocedural optimization" OFF)
option(POLLY "Enable pollyhedral optimization" OFF)
if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR
@ -75,7 +76,10 @@ if((WIN32 OR ${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
OR CMAKE_SYSTEM_PROCESSOR STREQUAL "EM64T") AND NOT 64BIT)
OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86"
OR CMAKE_SYSTEM_PROCESSOR STREQUAL "X86"
OR CMAKE_SYSTEM_PROCESSOR STREQUAL "i386"))
OR CMAKE_SYSTEM_PROCESSOR STREQUAL "i386"
OR CMAKE_SYSTEM_PROCESSOR STREQUAL "i486"
OR CMAKE_SYSTEM_PROCESSOR STREQUAL "i586"
OR CMAKE_SYSTEM_PROCESSOR STREQUAL "i686"))
option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" ON)
else()
option(GOLDSOURCE_SUPPORT "Build goldsource compatible client library" OFF)
@ -153,11 +157,13 @@ if(VITA)
add_compile_options(-fno-use-cxa-atexit)
endif()
check_ipo_supported(RESULT HAVE_LTO OUTPUT LTO_ERROR)
if(HAVE_LTO)
message(STATUS "IPO / LTO enabled")
else()
message(STATUS "IPO / LTO not supported: <${LTO_ERROR}>")
if(LTO)
check_ipo_supported(RESULT HAVE_LTO OUTPUT LTO_ERROR)
if(HAVE_LTO)
message(STATUS "IPO / LTO enabled")
else()
message(STATUS "IPO / LTO not supported: ${LTO_ERROR}")
endif()
endif()
check_include_file("tgmath.h" HAVE_TGMATH_H)

View File

@ -70,6 +70,7 @@ SRCS+=./in_camera.cpp
SRCS+=./input.cpp
SRCS+=./input_goldsource.cpp
SRCS+=./input_mouse.cpp
SRCS+=./interpolation.cpp
#SRCS+=./inputw32.cpp
SRCS+=./menu.cpp
SRCS+=./message.cpp

View File

@ -43,10 +43,6 @@ if(NOT MSVC)
add_compile_options(-fomit-frame-pointer) # GCC/Clang flag
add_compile_options(-ftree-vectorize) # GCC/Clang flag
add_compile_options(-funsafe-math-optimizations) # GCC/Clang flag
if(CMAKE_SYSTEM_NAME STREQUAL "Android")
add_compile_options(-O3 -DNDEBUG) # gradle compiles release builds with RelWithDebInfo(-O2 -g) and strips debug symbols.
target_link_options(${CLDLL_LIBRARY} PUBLIC "LINKER:-O3")
endif()
endif()
else()
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE)
@ -111,6 +107,7 @@ set (CLDLL_SOURCES
input_goldsource.cpp
input_mouse.cpp
input_xash3d.cpp
interpolation.cpp
menu.cpp
message.cpp
parsemsg.cpp

View File

@ -52,6 +52,7 @@ set SOURCES=../dlls/crossbow.cpp ^
input_goldsource.cpp ^
input_mouse.cpp ^
input_xash3d.cpp ^
interpolation.cpp ^
menu.cpp ^
message.cpp ^
parsemsg.cpp ^

View File

@ -70,6 +70,7 @@ void EV_TripmineFire( struct event_args_s *args );
void EV_SnarkFire( struct event_args_s *args );
void EV_TrainPitchAdjust( struct event_args_s *args );
void EV_VehiclePitchAdjust( event_args_t *args );
}
#define VECTOR_CONE_1DEGREES Vector( 0.00873f, 0.00873f, 0.00873f )
@ -98,6 +99,7 @@ float EV_HLDM_PlayTextureSound( int idx, pmtrace_t *ptr, float *vecSrc, float *v
int cnt;
float fattn = ATTN_NORM;
int entity;
cl_entity_t *ent;
char *pTextureName;
char texname[64];
char szbuffer[64];
@ -109,7 +111,8 @@ float EV_HLDM_PlayTextureSound( int idx, pmtrace_t *ptr, float *vecSrc, float *v
chTextureType = 0;
// Player
if( entity >= 1 && entity <= gEngfuncs.GetMaxClients() )
if( ( entity >= 1 && entity <= gEngfuncs.GetMaxClients() )
|| ( ( ent = gEngfuncs.GetEntityByIndex( entity )) && ( ent->curstate.eflags & EFLAG_MONSTER )))
{
// hit body
chTextureType = CHAR_TEX_FLESH;
@ -1752,6 +1755,65 @@ void EV_TrainPitchAdjust( event_args_t *args )
}
}
void EV_VehiclePitchAdjust( event_args_t *args )
{
int idx;
vec3_t origin;
unsigned short us_params;
int noise;
float m_flVolume;
int pitch;
int stop;
const char *pszSound;
idx = args->entindex;
VectorCopy( args->origin, origin );
us_params = (unsigned short)args->iparam1;
stop = args->bparam1;
m_flVolume = (float)( us_params & 0x003f ) / 40.0f;
noise = (int)( ( ( us_params ) >> 12 ) & 0x0007 );
pitch = (int)( 10.0f * (float)( ( us_params >> 6 ) & 0x003f ) );
switch( noise )
{
case 1:
pszSound = "plats/vehicle1.wav";
break;
case 2:
pszSound = "plats/vehicle2.wav";
break;
case 3:
pszSound = "plats/vehicle3.wav";
break;
case 4:
pszSound = "plats/vehicle4.wav";
break;
case 5:
pszSound = "plats/vehicle6.wav";
break;
case 6:
pszSound = "plats/vehicle7.wav";
break;
default:
// no sound
return;
}
if( stop )
{
gEngfuncs.pEventAPI->EV_StopSound( idx, CHAN_STATIC, pszSound );
}
else
{
gEngfuncs.pEventAPI->EV_PlaySound( idx, origin, CHAN_STATIC, pszSound, m_flVolume, ATTN_NORM, SND_CHANGE_PITCH, pitch );
}
}
int EV_TFC_IsAllyTeam( int iTeam1, int iTeam2 )
{
return 0;

View File

@ -207,9 +207,9 @@ BOOL CBaseMonster::FindLateralCover( const Vector &vecThreat, const Vector &vecV
Vector CBaseMonster::ShootAtEnemy( const Vector &shootOrigin ) { return g_vecZero; }
BOOL CBaseMonster::FacingIdeal( void ) { return FALSE; }
BOOL CBaseMonster::FCanActiveIdle( void ) { return FALSE; }
void CBaseMonster::PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ) { }
void CBaseMonster::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ) { }
void CBaseMonster::SentenceStop( void ) { }
void CBaseToggle::PlaySentence( const char *pszSentence, float duration, float volume, float attenuation ) { }
void CBaseToggle::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener ) { }
void CBaseToggle::SentenceStop( void ) { }
void CBaseMonster::CorpseFallThink( void ) { }
void CBaseMonster::MonsterInitDead( void ) { }
BOOL CBaseMonster::BBoxFlat( void ) { return TRUE; }

View File

@ -40,6 +40,7 @@ void EV_TripmineFire( struct event_args_s *args );
void EV_SnarkFire( struct event_args_s *args );
void EV_TrainPitchAdjust( struct event_args_s *args );
void EV_VehiclePitchAdjust( event_args_t *args );
}
/*
@ -76,4 +77,5 @@ void Game_HookEvents( void )
gEngfuncs.pfnHookEvent( "events/firehornet.sc", EV_HornetGunFire );
gEngfuncs.pfnHookEvent( "events/tripfire.sc", EV_TripmineFire );
gEngfuncs.pfnHookEvent( "events/snarkfire.sc", EV_SnarkFire );
gEngfuncs.pfnHookEvent( "events/vehicle.sc", EV_VehiclePitchAdjust );
}

View File

@ -380,7 +380,7 @@ void CHud::Init( void )
m_pCvarStealMouse = CVAR_CREATE( "hud_capturemouse", "1", FCVAR_ARCHIVE );
m_pCvarDraw = CVAR_CREATE( "hud_draw", "1", FCVAR_ARCHIVE );
cl_lw = gEngfuncs.pfnGetCvarPointer( "cl_lw" );
cl_viewbob = CVAR_CREATE( "cl_viewbob", "0", FCVAR_ARCHIVE );
cl_viewbob = CVAR_CREATE( "cl_viewbob", "1", FCVAR_ARCHIVE );
m_pSpriteList = NULL;
@ -428,6 +428,8 @@ void CHud::Init( void )
m_Menu.Init();
MsgFunc_ResetHUD( 0, 0, NULL );
ClientCmd( "richpresence_gamemode\n" );
ClientCmd( "richpresence_update\n" );
}
// CHud destructor

View File

@ -87,6 +87,11 @@ int CHud::MsgFunc_GameMode( const char *pszName, int iSize, void *pbuf )
BEGIN_READ( pbuf, iSize );
m_Teamplay = READ_BYTE();
if( m_Teamplay )
ClientCmd( "richpresence_gamemode Teamplay\n" );
else
ClientCmd( "richpresence_gamemode\n" );
ClientCmd( "richpresence_update\n" );
return 1;
}

View File

@ -87,6 +87,11 @@ void CHud::Think( void )
// only let players adjust up in fov, and only if they are not overriden by something else
m_iFOV = Q_max( default_fov->value, 90 );
}
if( gEngfuncs.IsSpectateOnly() )
{
m_iFOV = gHUD.m_Spectator.GetFOV(); // default_fov->value;
}
}
// Redraw

View File

@ -37,8 +37,6 @@ extern "C" float vJumpAngles[3];
extern void V_GetInEyePos( int entity, float * origin, float * angles );
extern void V_ResetChaseCam();
extern void V_GetChasePos( int target, float * cl_angles, float * origin, float * angles );
extern void VectorAngles( const float *forward, float *angles );
extern "C" void NormalizeAngles( float *angles );
extern float * GetClientColor( int clientIndex );
extern vec3_t v_origin; // last view origin
@ -46,6 +44,37 @@ extern vec3_t v_angles; // last view angle
extern vec3_t v_cl_angles; // last client/mouse angle
extern vec3_t v_sim_org; // last sim origin
#if 0
const char *GetSpectatorLabel( int iMode )
{
switch( iMode )
{
case OBS_CHASE_LOCKED:
return "#OBS_CHASE_LOCKED";
case OBS_CHASE_FREE:
return "#OBS_CHASE_FREE";
case OBS_ROAMING:
return "#OBS_ROAMING";
case OBS_IN_EYE:
return "#OBS_IN_EYE";
case OBS_MAP_FREE:
return "#OBS_MAP_FREE";
case OBS_MAP_CHASE:
return "#OBS_MAP_CHASE";
case OBS_NONE:
default:
return "#OBS_NONE";
}
}
#endif
void SpectatorMode( void )
{
if( gEngfuncs.Cmd_Argc() <= 1 )
@ -152,6 +181,7 @@ int CHudSpectator::Init()
m_flNextObserverInput = 0.0f;
m_zoomDelta = 0.0f;
m_moveDelta = 0.0f;
m_FOV = 90.0f;
m_chatEnabled = ( gHUD.m_SayText.m_HUD_saytext->value != 0 );
iJumpSpectator = 0;
@ -363,6 +393,178 @@ void CHudSpectator::SetSpectatorStartPosition()
iJumpSpectator = 1; // jump anyway
}
void CHudSpectator::SetCameraView( vec3_t pos, vec3_t angle, float fov )
{
m_FOV = fov;
VectorCopy( pos, vJumpOrigin );
VectorCopy( angle, vJumpAngles );
gEngfuncs.SetViewAngles( vJumpAngles );
iJumpSpectator = 1; // jump anyway
}
void CHudSpectator::AddWaypoint( float time, vec3_t pos, vec3_t angle, float fov, int flags )
{
if( flags == 0 && time == 0.0f )
{
// switch instantly to this camera view
SetCameraView( pos, angle, fov );
return;
}
if( m_NumWayPoints >= MAX_CAM_WAYPOINTS )
{
gEngfuncs.Con_Printf( "Too many camera waypoints!\n" );
return;
}
VectorCopy( angle, m_CamPath[m_NumWayPoints].angle );
VectorCopy( pos, m_CamPath[m_NumWayPoints].position );
m_CamPath[m_NumWayPoints].flags = flags;
m_CamPath[m_NumWayPoints].fov = fov;
m_CamPath[m_NumWayPoints].time = time;
gEngfuncs.Con_DPrintf( "Added waypoint %i\n", m_NumWayPoints );
m_NumWayPoints++;
}
void CHudSpectator::SetWayInterpolation( cameraWayPoint_t *prev, cameraWayPoint_t *start, cameraWayPoint_t *end, cameraWayPoint_t *next )
{
m_WayInterpolation.SetViewAngles( start->angle, end->angle );
m_WayInterpolation.SetFOVs( start->fov, end->fov );
m_WayInterpolation.SetSmoothing( ( start->flags & DRC_FLAG_SLOWSTART ) != 0,
( start->flags & DRC_FLAG_SLOWEND ) != 0 );
if( prev && next )
{
m_WayInterpolation.SetWaypoints( &prev->position, start->position, end->position, &next->position );
}
else if( prev )
{
m_WayInterpolation.SetWaypoints( &prev->position, start->position, end->position, NULL );
}
else if( next )
{
m_WayInterpolation.SetWaypoints( NULL, start->position, end->position, &next->position );
}
else
{
m_WayInterpolation.SetWaypoints( NULL, start->position, end->position, NULL );
}
}
bool CHudSpectator::GetDirectorCamera( vec3_t &position, vec3_t &angle )
{
float now = gHUD.m_flTime;
float fov = 90.0f;
if( m_ChaseEntity )
{
cl_entity_t *ent = gEngfuncs.GetEntityByIndex( m_ChaseEntity );
if( ent )
{
vec3_t vt = ent->curstate.origin;
if( m_ChaseEntity <= gEngfuncs.GetMaxClients())
{
if( ent->curstate.solid == SOLID_NOT )
{
vt[2]+= -8 ; // PM_DEAD_VIEWHEIGHT
}
else if( ent->curstate.usehull == 1 )
{
vt[2]+= 12; // VEC_DUCK_VIEW;
}
else
{
vt[2]+= 28; // DEFAULT_VIEWHEIGHT
}
}
vt = vt - position;
VectorAngles( vt, angle );
angle[0] = -angle[0];
return true;
}
else
{
return false;
}
}
if( !m_IsInterpolating )
return false;
if( m_WayPoint < 0 || m_WayPoint >= ( m_NumWayPoints - 1 ))
return false;
cameraWayPoint_t *wp1 = &m_CamPath[m_WayPoint];
cameraWayPoint_t *wp2 = &m_CamPath[m_WayPoint+1];
if( now < wp1->time )
return false;
while( now > wp2->time )
{
// go to next waypoint, if possible
m_WayPoint++;
if( m_WayPoint >= ( m_NumWayPoints - 1 ))
{
m_IsInterpolating = false;
return false; // there is no following waypoint
}
wp1 = wp2;
wp2 = &m_CamPath[m_WayPoint + 1];
if( m_WayPoint > 0 )
{
// we have a predecessor
if( m_WayPoint < ( m_NumWayPoints - 1 ))
{
// we have also a successor
SetWayInterpolation( &m_CamPath[m_WayPoint - 1], wp1, wp2, &m_CamPath[m_WayPoint + 2] );
}
else
{
SetWayInterpolation( &m_CamPath[m_WayPoint - 1], wp1, wp2, NULL );
}
}
else if( m_WayPoint < ( m_NumWayPoints - 1 ))
{
// we only have a successor
SetWayInterpolation( NULL, wp1, wp2, &m_CamPath[m_WayPoint + 2] );
}
else
{
// we have only two waypoints
SetWayInterpolation( NULL, wp1, wp2, NULL );
}
}
if( wp2->time <= wp1->time )
return false;
float fraction = ( now - wp1->time ) / ( wp2->time - wp1->time );
if( fraction < 0.0f )
fraction = 0.0f;
else if( fraction > 1.0f )
fraction = 1.0f;
m_WayInterpolation.Interpolate( fraction, position, angle, &fov );
// gEngfuncs.Con_Printf( "Interpolate time: %.2f, fraction %.2f, point : %.2f,%.2f,%.2f\n", now, fraction, position[0], position[1], position[2] );
SetCameraView( position, angle, fov );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Loads new icons
//-----------------------------------------------------------------------------
@ -377,9 +579,21 @@ int CHudSpectator::VidInit()
m_hsprCamera = SPR_Load( "sprites/camera.spr" );
m_hCrosshair = SPR_Load( "sprites/crosshairs.spr" );
m_lastPrimaryObject = m_lastSecondaryObject = 0;
m_flNextObserverInput = 0.0f;
m_lastHudMessage = 0;
m_iSpectatorNumber = 0;
iJumpSpectator = 0;
g_iUser1 = g_iUser2 = 0;
return 1;
}
float CHudSpectator::GetFOV( void )
{
return m_FOV;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : flTime -
@ -427,8 +641,11 @@ int CHudSpectator::Draw( float flTime )
return 1;
// make sure we have player info
#if USE_VGUI
gViewPort->GetAllPlayersInfo();
#else
gHUD.GetAllPlayersInfo();
#endif
// loop through all the players and draw additional infos to their sprites on the map
for( int i = 0; i < MAX_PLAYERS; i++ )
{
@ -461,8 +678,10 @@ int CHudSpectator::Draw( float flTime )
void CHudSpectator::DirectorMessage( int iSize, void *pbuf )
{
float value;
float f1, f2;
char *string;
vec3_t v1, v2;
int i1, i2, i3;
BEGIN_READ( pbuf, iSize );
@ -479,7 +698,7 @@ void CHudSpectator::DirectorMessage( int iSize, void *pbuf )
gHUD.MsgFunc_InitHUD( NULL, 0, NULL );
gHUD.MsgFunc_ResetHUD( NULL, 0, NULL );
break;
case DRC_CMD_EVENT:
case DRC_CMD_EVENT: // old director style message
m_lastPrimaryObject = READ_WORD();
m_lastSecondaryObject = READ_WORD();
m_iObserverFlags = READ_LONG();
@ -502,19 +721,22 @@ void CHudSpectator::DirectorMessage( int iSize, void *pbuf )
}
break;
case DRC_CMD_CAMERA:
v1[0] = READ_COORD(); // position
v1[1] = READ_COORD();
v1[2] = READ_COORD(); // vJumpOrigin
v1[0] = READ_COORD(); // view angle
v1[1] = READ_COORD(); // vJumpAngles
v1[2] = READ_COORD();
f1 = READ_BYTE(); // fov
i1 = READ_WORD(); // target
if( m_autoDirector->value )
{
vJumpOrigin[0] = READ_COORD(); // position
vJumpOrigin[1] = READ_COORD();
vJumpOrigin[2] = READ_COORD();
vJumpAngles[0] = READ_COORD(); // view angle
vJumpAngles[1] = READ_COORD();
vJumpAngles[2] = READ_COORD();
gEngfuncs.SetViewAngles( vJumpAngles );
iJumpSpectator = 1;
SetModes( OBS_ROAMING, -1 );
SetCameraView( v1, v2, f1 );
m_ChaseEntity = i1;
}
break;
case DRC_CMD_MESSAGE:
@ -551,13 +773,13 @@ void CHudSpectator::DirectorMessage( int iSize, void *pbuf )
break;
case DRC_CMD_SOUND:
string = READ_STRING();
value = READ_FLOAT();
f1 = READ_FLOAT();
// gEngfuncs.Con_Printf("DRC_CMD_FX_SOUND: %s %.2f\n", string, value );
gEngfuncs.pEventAPI->EV_PlaySound( 0, v_origin, CHAN_BODY, string, value, ATTN_NORM, 0, PITCH_NORM );
// gEngfuncs.Con_Printf("DRC_CMD_FX_SOUND: %s %.2f\n", string, f1 );
gEngfuncs.pEventAPI->EV_PlaySound( 0, v_origin, CHAN_BODY, string, f1, ATTN_NORM, 0, PITCH_NORM );
break;
case DRC_CMD_TIMESCALE:
value = READ_FLOAT();
f1 = READ_FLOAT();
break;
case DRC_CMD_STATUS:
READ_LONG(); // total number of spectator slots
@ -574,13 +796,71 @@ void CHudSpectator::DirectorMessage( int iSize, void *pbuf )
gViewPort->UpdateSpectatorPanel();
#endif
break;
case DRC_CMD_FADE:
break;
case DRC_CMD_STUFFTEXT:
gEngfuncs.pfnFilteredClientCmd( READ_STRING() );
break;
case DRC_CMD_CAMPATH:
v1[0] = READ_COORD(); // position
v1[1] = READ_COORD();
v1[2] = READ_COORD(); // vJumpOrigin
v2[0] = READ_COORD(); // view angle
v2[1] = READ_COORD(); // vJumpAngles
v2[2] = READ_COORD();
f1 = READ_BYTE(); // FOV
i1 = READ_BYTE(); // flags
if( m_autoDirector->value )
{
SetModes( OBS_ROAMING, -1 );
SetCameraView( v1, v2, f1 );
}
break;
case DRC_CMD_WAYPOINTS:
i1 = READ_BYTE();
m_NumWayPoints = 0;
m_WayPoint = 0;
for( i2 = 0; i2 < i1; i2++ )
{
f1 = gHUD.m_flTime + (float)( READ_SHORT()) / 100.0f;
v1[0] = READ_COORD(); // position
v1[1] = READ_COORD();
v1[2] = READ_COORD(); // vJumpOrigin
v2[0] = READ_COORD(); // view angle
v2[1] = READ_COORD(); // vJumpAngles
v2[2] = READ_COORD();
f2 = READ_BYTE(); // fov
i3 = READ_BYTE(); // flags
AddWaypoint( f1, v1, v2, f2, i3 );
}
// gEngfuncs.Con_Printf( "CHudSpectator::DirectorMessage: waypoints %i.\n", m_NumWayPoints );
if( !m_autoDirector->value )
{
// ignore waypoints
m_NumWayPoints = 0;
break;
}
SetModes( OBS_ROAMING, -1 );
m_IsInterpolating = true;
if( m_NumWayPoints > 2 )
{
SetWayInterpolation( NULL, &m_CamPath[0], &m_CamPath[1], &m_CamPath[2] );
}
else
{
SetWayInterpolation( NULL, &m_CamPath[0], &m_CamPath[1], NULL );
}
break;
default:
gEngfuncs.Con_DPrintf( "CHudSpectator::DirectorMessage: unknown command %i.\n", cmd );
break;
}
}
@ -595,9 +875,10 @@ void CHudSpectator::FindNextPlayer( bool bReverse )
// if we are NOT in HLTV mode, spectator targets are set on server
if( !gEngfuncs.IsSpectateOnly() )
{
char cmdstring[32];
char cmdstring[256];
// forward command to server
sprintf( cmdstring, "follownext %i", bReverse ? 1 : 0 );
_snprintf( cmdstring, sizeof( cmdstring ) - 1,"follownext %i", bReverse ? 1 : 0 );
cmdstring[sizeof( cmdstring ) - 1] = '\0';
gEngfuncs.pfnServerCmd( cmdstring );
return;
}
@ -614,8 +895,11 @@ void CHudSpectator::FindNextPlayer( bool bReverse )
int iDir = bReverse ? -1 : 1;
// make sure we have player info
#if USE_VGUI
gViewPort->GetAllPlayersInfo();
#else
gHUD.GetAllPlayersInfo();
#endif
do
{
iCurrent += iDir;
@ -656,7 +940,7 @@ void CHudSpectator::FindNextPlayer( bool bReverse )
#endif
}
void CHudSpectator::FindPlayer(const char *name)
void CHudSpectator::FindPlayer( const char *name )
{
// MOD AUTHORS: Modify the logic of this function if you want to restrict the observer to watching
// only a subset of the players. e.g. Make it check the target's team.
@ -664,18 +948,22 @@ void CHudSpectator::FindPlayer(const char *name)
// if we are NOT in HLTV mode, spectator targets are set on server
if ( !gEngfuncs.IsSpectateOnly() )
{
char cmdstring[32];
char cmdstring[256];
// forward command to server
sprintf(cmdstring,"follow %s",name);
gEngfuncs.pfnServerCmd(cmdstring);
_snprintf( cmdstring, sizeof( cmdstring ) - 1, "follow %s", name );
cmdstring[sizeof( cmdstring ) - 1] = '\0';
gEngfuncs.pfnServerCmd( cmdstring );
return;
}
g_iUser2 = 0;
// make sure we have player info
#if USE_VGUI
gViewPort->GetAllPlayersInfo();
#else
gHUD.GetAllPlayersInfo();
#endif
cl_entity_t * pEnt = NULL;
for (int i = 1; i < MAX_PLAYERS; i++ )
@ -847,12 +1135,20 @@ void CHudSpectator::SetModes( int iNewMainMode, int iNewInsetMode )
return;
}
// main modes ettings will override inset window settings
m_IsInterpolating = false;
m_ChaseEntity = 0;
// main modes settings will override inset window settings
if( iNewMainMode != g_iUser1 )
{
// if we are NOT in HLTV mode, main spectator mode is set on server
if( !gEngfuncs.IsSpectateOnly() )
{
char cmdstring[256];
// forward command to server
_snprintf( cmdstring, sizeof( cmdstring ) - 1,"specmode %i", iNewMainMode );
cmdstring[sizeof( cmdstring ) - 1] = '\0';
gEngfuncs.pfnServerCmd( cmdstring );
return;
}
@ -1548,8 +1844,9 @@ void CHudSpectator::CheckSettings()
if( gEngfuncs.IsSpectateOnly() )
{
// tell proxy our new chat mode
char chatcmd[32];
sprintf( chatcmd, "ignoremsg %i", m_chatEnabled ? 0 : 1 );
char chatcmd[256];
_snprintf( chatcmd, sizeof( chatcmd ) - 1, "ignoremsg %i", m_chatEnabled ? 0 : 1 );
chatcmd[sizeof( chatcmd ) - 1] = '\0';
gEngfuncs.pfnServerCmd( chatcmd );
}
}
@ -1626,6 +1923,12 @@ void CHudSpectator::Reset()
memset( &m_OverviewEntities, 0, sizeof(m_OverviewEntities) );
m_FOV = 90.0f;
m_IsInterpolating = false;
m_ChaseEntity = 0;
SetSpectatorStartPosition();
}
@ -1648,7 +1951,7 @@ void CHudSpectator::InitHUDData()
Reset();
SetModes( OBS_CHASE_FREE, INSET_OFF );
SetModes( OBS_CHASE_LOCKED, INSET_OFF );
g_iUser2 = 0; // fake not target until first camera command

View File

@ -10,6 +10,7 @@
#define HUD_SPECTATOR_H
#include "cl_entity.h"
#include "interpolation.h"
#define INSET_OFF 0
#define INSET_CHASE_FREE 1
@ -22,6 +23,9 @@
#define OVERVIEW_TILE_SIZE 128 // don't change this
#define OVERVIEW_MAX_LAYERS 1
extern void VectorAngles( const float *forward, float *angles );
extern "C" void NormalizeAngles( float *angles );
//-----------------------------------------------------------------------------
// Purpose: Handles the drawing of the spectator stuff (camera & top-down map and all the things on it )
//-----------------------------------------------------------------------------
@ -49,7 +53,17 @@ typedef struct overviewEntity_s
double killTime;
} overviewEntity_t;
typedef struct cameraWayPoint_s
{
float time;
vec3_t position;
vec3_t angle;
float fov;
int flags;
} cameraWayPoint_t;
#define MAX_OVERVIEW_ENTITIES 128
#define MAX_CAM_WAYPOINTS 32
class CHudSpectator : public CHudBase
{
@ -73,7 +87,7 @@ public:
void HandleButtonsDown( int ButtonPressed );
void HandleButtonsUp( int ButtonPressed );
void FindNextPlayer( bool bReverse );
void FindPlayer(const char *name);
void FindPlayer( const char *name );
void DirectorMessage( int iSize, void *pbuf );
void SetSpectatorStartPosition();
int Init();
@ -81,6 +95,13 @@ public:
int Draw( float flTime );
void AddWaypoint( float time, vec3_t pos, vec3_t angle, float fov, int flags );
void SetCameraView( vec3_t pos, vec3_t angle, float fov );
float GetFOV();
bool GetDirectorCamera( vec3_t &position, vec3_t &angle );
void SetWayInterpolation( cameraWayPoint_t *prev, cameraWayPoint_t *start, cameraWayPoint_t *end, cameraWayPoint_t *next );
int m_iDrawCycle;
client_textmessage_t m_HUDMessages[MAX_SPEC_HUD_MESSAGES];
char m_HUDMessageText[MAX_SPEC_HUD_MESSAGES][128];
@ -100,8 +121,13 @@ public:
qboolean m_chatEnabled;
qboolean m_IsInterpolating;
int m_ChaseEntity; // if != 0, follow this entity with viewangles
int m_WayPoint; // current waypoint 1
int m_NumWayPoints; // current number of waypoints
vec3_t m_cameraOrigin; // a help camera
vec3_t m_cameraAngles; // and it's angles
CInterpolation m_WayInterpolation;
private:
vec3_t m_vPlayerPos[MAX_PLAYERS];
@ -119,9 +145,11 @@ private:
struct model_s *m_MapSprite; // each layer image is saved in one sprite, where each tile is a sprite frame
float m_flNextObserverInput;
float m_FOV;
float m_zoomDelta;
float m_moveDelta;
int m_lastPrimaryObject;
int m_lastSecondaryObject;
cameraWayPoint_t m_CamPath[MAX_CAM_WAYPOINTS];
};
#endif // SPECTATOR_H

217
cl_dll/interpolation.cpp Normal file
View File

@ -0,0 +1,217 @@
/************ (C) Copyright 2003 Valve, L.L.C. All rights reserved. ***********
**
** The copyright to the contents herein is the property of Valve, L.L.C.
** The contents may be used and/or copied only with the written permission of
** Valve, L.L.C., or in accordance with the terms and conditions stipulated in
** the agreement/contract under which the contents have been supplied.
**
*******************************************************************************
**
** Contents:
**
** interpolation.cpp: implementation of the interpolation class
**
******************************************************************************/
#include "hud.h"
#include "cl_util.h"
#include "interpolation.h"
// = determinant of matrix a,b,c
#define Determinant( a, b, c ) ( (a)[2] * ( (b)[0]*(c)[1] - (b)[1]*(c)[0] ) + \
(a)[1] * ( (b)[2]*(c)[0] - (b)[0]*(c)[2] ) + \
(a)[0] * ( (b)[1]*(c)[2] - (b)[2]*(c)[1] ) )
// solve 3 vector linear system of equations v0 = x*v1 + y*v2 + z*v3 (if possible)
bool SolveLSE( vec3_t v0, vec3_t v1, vec3_t v2, vec3_t v3, float * x, float * y, float * z )
{
float d = Determinant( v1, v2, v3 );
if( d == 0.0f )
return false;
if( x )
*x = Determinant( v0, v2, v3 ) / d;
if( y )
*y= Determinant( v1, v0, v3 ) / d;
if( z )
*z= Determinant( v1, v2, v0 ) / d;
return true;
}
// p = closest point between vector lines a1+x*m1 and a2+x*m2
bool GetPointBetweenLines( vec3_t &p, vec3_t a1, vec3_t m1, vec3_t a2, vec3_t m2 )
{
float x, z;
vec3_t t1 = CrossProduct( m1, m2 );
vec3_t t2 = a2 - a1;
if( !SolveLSE( t2, m1, t1, m2, &x , NULL, &z ) )
return false;
t1 = a1 + x * m1;
t2 = a2 + ( -z ) * m2;
p = ( t1 + t2 ) / 2.0f;
return true;
}
// Bernstein Poynom B(u) with n = 2, i = 0
#define BernsteinPolynom20(u) ( ( 1.0f - u ) * ( 1.0f - u ))
#define BernsteinPolynom21(u) ( 2.0f * u * ( 1.0f - u ) )
#define BernsteinPolynom22(u) ( u * u )
CInterpolation::CInterpolation()
{
}
CInterpolation::~CInterpolation()
{
m_SmoothStart = m_SmoothEnd = false;
}
void CInterpolation::SetViewAngles( vec3_t start, vec3_t end )
{
m_StartAngle = start;
m_EndAngle = end;
NormalizeAngles( m_StartAngle );
NormalizeAngles( m_EndAngle );
}
void CInterpolation::SetFOVs( float start, float end )
{
m_StartFov = start;
m_EndFov = end;
}
void CInterpolation::SetWaypoints( vec3_t *prev, vec3_t start, vec3_t end, vec3_t *next )
{
m_StartPoint = start;
m_EndPoint = end;
vec3_t a, b, c, d;
if( !prev && !next )
{
// no direction given, straight linear interpolation
m_Center = ( m_StartPoint + m_EndPoint ) / 2.0f;
}
else if( !prev )
{
a = start - end;
float dist = a.Length() / 2.0f;
a = a.Normalize();
b = *next - end;
b = b.Normalize();
c = a - b;
c = c.Normalize();
m_Center = end + c * dist;
}
else if( !next )
{
a = *prev - start;
a = a.Normalize();
b = end - start;
float dist = b.Length() / 2.0f;
b = b.Normalize();
c = b - a;
c = c.Normalize();
m_Center = start + c * dist;
}
else
{
// we have a previous and a next point, great!
a = *prev - start;
a = a.Normalize();
b = end - start;
b = b.Normalize();
c = b - a;
a = start - end;
a = a.Normalize();
b = *next - end;
b = b.Normalize();
d = a - b;
GetPointBetweenLines( m_Center, start, c, end, d );
}
}
void CInterpolation::Interpolate( float t, vec3_t &point, vec3_t &angle, float *fov )
{
if( m_SmoothStart && m_SmoothEnd )
{
t = ( 1.0f - t ) * ( t * t ) + t * ( 1.0f - ( ( t - 1.0f ) * ( t - 1.0f )));
}
else if( m_SmoothStart )
{
t = t * t;
}
else if( m_SmoothEnd )
{
t = t - 1.0f;
t = -( t * t ) + 1;
}
if( point )
{
BezierInterpolatePoint( t, point );
}
if( angle )
{
InterpolateAngle( t, angle );
}
if( fov )
{
*fov = m_StartFov + ( t * ( m_EndFov - m_StartFov ));
}
}
void CInterpolation::BezierInterpolatePoint( float t, vec3_t &point )
{
point = m_StartPoint * BernsteinPolynom20( t );
point = point + m_Center * BernsteinPolynom21( t );
point = point + m_EndPoint * BernsteinPolynom22( t );
}
void CInterpolation::SetSmoothing( bool start, bool end )
{
m_SmoothStart = start;
m_SmoothEnd = end;
}
void CInterpolation::InterpolateAngle( float t, vec3_t &angle )
{
int i;
float ang1, ang2;
float d;
for( i = 0; i < 3; i++ )
{
ang1 = m_StartAngle[i];
ang2 = m_EndAngle[i];
d = ang2 - ang1;
if ( d > 180 )
{
d -= 360;
}
else if ( d < -180 )
{
d += 360;
}
angle[i] = ang1 + d * t;
}
NormalizeAngles( angle );
}

52
cl_dll/interpolation.h Normal file
View File

@ -0,0 +1,52 @@
/************ (C) Copyright 2003 Valve, L.L.C. All rights reserved. ***********
**
** The copyright to the contents herein is the property of Valve, L.L.C.
** The contents may be used and/or copied only with the written permission of
** Valve, L.L.C., or in accordance with the terms and conditions stipulated in
** the agreement/contract under which the contents have been supplied.
**
*******************************************************************************
**
** Contents:
**
** interpolation.h: Bezier inpolation classes
**
******************************************************************************/
#pragma once
#if !defined(INTERPOLATION_H)
#define INTERPOLATION_H
// interpolation class
class CInterpolation
{
public:
CInterpolation();
virtual ~CInterpolation();
void SetWaypoints( vec3_t *prev, vec3_t start, vec3_t end, vec3_t *next );
void SetViewAngles( vec3_t start, vec3_t end );
void SetFOVs( float start, float end );
void SetSmoothing( bool start, bool end );
// get interpolated point 0 =< t =< 1, 0 = start, 1 = end
void Interpolate( float t, vec3_t &point, vec3_t &angle, float * fov );
protected:
void BezierInterpolatePoint( float t, vec3_t &point );
void InterpolateAngle( float t, vec3_t &angle );
vec3_t m_StartPoint;
vec3_t m_EndPoint;
vec3_t m_StartAngle;
vec3_t m_EndAngle;
vec3_t m_Center;
float m_StartFov;
float m_EndFov;
bool m_SmoothStart;
bool m_SmoothEnd;
};
#endif // INTERPOLATION_H

View File

@ -295,8 +295,11 @@ void V_CalcGunAngle( struct ref_params_s *pparams )
viewent->angles[PITCH] -= v_idlescale * sin( pparams->time * v_ipitch_cycle.value ) * ( v_ipitch_level.value * 0.5f );
viewent->angles[YAW] -= v_idlescale * sin( pparams->time * v_iyaw_cycle.value ) * v_iyaw_level.value;
VectorCopy( viewent->angles, viewent->curstate.angles );
VectorCopy( viewent->angles, viewent->latched.prevangles );
if( !( cl_viewbob && cl_viewbob->value ))
{
VectorCopy( viewent->angles, viewent->curstate.angles );
VectorCopy( viewent->angles, viewent->latched.prevangles );
}
}
/*
@ -575,6 +578,7 @@ void V_CalcNormalRefdef( struct ref_params_s *pparams )
{
VectorCopy( pparams->cl_viewangles, view->angles );
}
// set up gun position
V_CalcGunAngle( pparams );
@ -597,9 +601,6 @@ void V_CalcNormalRefdef( struct ref_params_s *pparams )
view->angles[ROLL] -= bob * 1.0f;
view->angles[PITCH] -= bob * 0.3f;
if( cl_viewbob && cl_viewbob->value )
VectorCopy( view->angles, view->curstate.angles );
// pushing the view origin down off of the same X/Z plane as the ent's origin will give the
// gun a very nice 'shifting' effect when the player looks up/down. If there is a problem
// with view model distortion, this may be a cause. (SJB).
@ -768,6 +769,14 @@ void V_CalcNormalRefdef( struct ref_params_s *pparams )
}
}
if( cl_viewbob && cl_viewbob->value )
{
VectorCopy( view->origin, view->curstate.origin );
VectorCopy( view->origin, view->latched.prevorigin );
VectorCopy( view->angles, view->curstate.angles );
VectorCopy( view->angles, view->latched.prevangles );
}
lasttime = pparams->time;
v_origin = pparams->vieworg;
@ -1462,6 +1471,8 @@ void V_CalcSpectatorRefdef( struct ref_params_s * pparams )
case OBS_ROAMING:
VectorCopy( v_cl_angles, v_angles );
VectorCopy( v_sim_org, v_origin );
// override values if director is active
gHUD.m_Spectator.GetDirectorCamera(v_origin, v_angles);
break;
case OBS_IN_EYE:
V_CalcNormalRefdef( pparams );

View File

@ -54,7 +54,6 @@ check_group_build_target(XASH_SERENITY XASH_PLATFORM)
check_group_build_target(XASH_IRIX XASH_PLATFORM)
check_group_build_target(XASH_NSWITCH XASH_PLATFORM)
check_group_build_target(XASH_PSVITA XASH_PLATFORM)
check_group_build_target(XASH_LINUX_UNKNOWN XASH_PLATFORM)
check_group_build_target(XASH_X86 XASH_ARCHITECTURE)
check_group_build_target(XASH_AMD64 XASH_ARCHITECTURE)
check_group_build_target(XASH_ARM XASH_ARCHITECTURE)
@ -84,8 +83,6 @@ unset(CMAKE_REQUIRED_INCLUDES)
# engine/common/build.c
if(XASH_ANDROID)
set(BUILDOS "android")
elseif(XASH_LINUX_UNKNOWN)
set(BUILDOS "linuxunkabi")
elseif(XASH_WIN32 OR XASH_LINUX OR XASH_APPLE)
set(BUILDOS "") # no prefix for default OS
elseif(XASH_FREEBSD)

View File

@ -127,7 +127,8 @@
// entity flags
#define EFLAG_SLERP 1 // do studio interpolation of this entity
#define EFLAG_MONSTER 2
//
// temp entity events
//

View File

@ -37,11 +37,14 @@
#define DRC_CMD_SOUND 7 // plays a particular sound
#define DRC_CMD_STATUS 8 // status info about broadcast
#define DRC_CMD_BANNER 9 // banner file name for HLTV gui
#define DRC_CMD_FADE 10 // send screen fade command
#define DRC_CMD_SHAKE 11 // send screen shake command
#define DRC_CMD_STUFFTEXT 12 // like the normal svc_stufftext but as director command
#define DRC_CMD_STUFFTEXT 10 // like the normal svc_stufftext but as director command
#define DRC_CMD_CHASE 11 // chase a certain player
#define DRC_CMD_INEYE 12 // view player through own eyes
#define DRC_CMD_MAP 13 // show overview map
#define DRC_CMD_CAMPATH 14 // define camera waypoint
#define DRC_CMD_WAYPOINTS 15 // start moving camera, inetranl message
#define DRC_CMD_LAST 12
#define DRC_CMD_LAST 15
// HLTV_EVENT event flags
#define DRC_FLAG_PRIO_MASK 0x0F // priorities between 0 and 15 (15 most important)
@ -53,7 +56,9 @@
#define DRC_FLAG_FINAL (1<<9) // is a final scene
#define DRC_FLAG_NO_RANDOM (1<<10) // don't randomize event data
#define MAX_DIRECTOR_CMD_PARAMETERS 4
#define MAX_DIRECTOR_CMD_STRING 128
// DRC_CMD_WAYPOINT flags
#define DRC_FLAG_STARTPATH 1 // end with speed 0.0
#define DRC_FLAG_SLOWSTART 2 // start with speed 0.0
#define DRC_FLAG_SLOWEND 4 // end with speed 0.0
#endif//HLTV_H

View File

@ -219,6 +219,7 @@ files{"dlls/agrunt.cpp",
"dlls/triggers.cpp",
"dlls/turret.cpp",
"dlls/util.cpp",
"dlls/vehicle.cpp",
"dlls/weapons.cpp",
"dlls/world.cpp",
"dlls/xen.cpp",
@ -269,6 +270,7 @@ files{"cl_dll/hl/hl_baseentity.cpp",
"cl_dll/input_goldsource.cpp",
"cl_dll/input_mouse.cpp",
"cl_dll/input_xash3d.cpp",
"cl_dll/interpolation.cpp",
"cl_dll/menu.cpp",
"cl_dll/message.cpp",
"cl_dll/overview.cpp",

View File

@ -122,6 +122,7 @@ LOCAL_SRC_FILES := agrunt.cpp airtank.cpp \
tripmine.cpp \
turret.cpp \
util.cpp \
vehicle.cpp \
weapons.cpp \
world.cpp \
xen.cpp \

View File

@ -39,10 +39,6 @@ if(NOT MSVC)
add_compile_options(-fomit-frame-pointer) # GCC/Clang flag
add_compile_options(-ftree-vectorize) # GCC/Clang flag
add_compile_options(-funsafe-math-optimizations) # GCC/Clang flag
if(CMAKE_SYSTEM_NAME STREQUAL "Android")
add_compile_options(-O3 -DNDEBUG) # gradle compiles release builds with RelWithDebInfo(-O2 -g) and strips debug symbols.
target_link_options(${SVDLL_LIBRARY} PUBLIC "LINKER:-O3")
endif()
endif()
else()
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE)
@ -144,6 +140,7 @@ set (SVDLL_SOURCES
tripmine.cpp
turret.cpp
util.cpp
vehicle.cpp
weapons.cpp
world.cpp
xen.cpp

View File

@ -120,6 +120,7 @@ public:
virtual int BloodColor( void ) { return m_bloodColor; }
virtual CBaseMonster *MyMonsterPointer( void ) { return this; }
virtual BOOL IsAllowedToSpeak( void ) { return IsAlive(); }
virtual void Look( int iDistance );// basic sight function for monsters
virtual void RunAI( void );// core ai function!
void Listen( void );
@ -186,11 +187,7 @@ public:
virtual void ScheduleChange( void ) {}
// virtual int CanPlaySequence( void ) { return ((m_pCine == NULL) && (m_MonsterState == MONSTERSTATE_NONE || m_MonsterState == MONSTERSTATE_IDLE || m_IdealMonsterState == MONSTERSTATE_IDLE)); }
virtual int CanPlaySequence( BOOL fDisregardState, int interruptLevel );
virtual int CanPlaySentence( BOOL fDisregardState ) { return IsAlive(); }
virtual void PlaySentence( const char *pszSentence, float duration, float volume, float attenuation );
virtual void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener );
virtual void SentenceStop( void );
virtual int CanPlaySentence( BOOL fDisregardState ) { return IsAllowedToSpeak(); }
Task_t *GetTask( void );
virtual MONSTERSTATE GetIdealState( void );

View File

@ -103,9 +103,11 @@ typedef void(CBaseEntity::*USEPTR)( CBaseEntity *pActivator, CBaseEntity *pCalle
#define CLASS_PLAYER_ALLY 11
#define CLASS_PLAYER_BIOWEAPON 12 // hornets and snarks.launched by players
#define CLASS_ALIEN_BIOWEAPON 13 // hornets and snarks.launched by the alien menace
#define CLASS_VEHICLE 14
#define CLASS_BARNACLE 99 // special because no one pays attention to it, and it eats a wide cross-section of creatures.
class CBaseEntity;
class CBaseToggle;
class CBaseMonster;
class CBasePlayerItem;
class CSquadMonster;
@ -172,6 +174,7 @@ public:
virtual int BloodColor( void ) { return DONT_BLEED; }
virtual void TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType );
virtual BOOL IsTriggered( CBaseEntity *pActivator ) {return TRUE; }
virtual CBaseToggle *MyTogglePointer( void ) { return NULL; }
virtual CBaseMonster *MyMonsterPointer( void ) { return NULL; }
virtual CSquadMonster *MySquadMonsterPointer( void ) { return NULL; }
virtual int GetToggleState( void ) { return TS_AT_TOP; }
@ -250,7 +253,6 @@ public:
int Intersects( CBaseEntity *pOther );
void MakeDormant( void );
int IsDormant( void );
BOOL IsLockedByMaster( void ) { return FALSE; }
static CBaseEntity *Instance( edict_t *pent )
{
@ -523,9 +525,15 @@ public:
static TYPEDESCRIPTION m_SaveData[];
CBaseToggle *MyTogglePointer( void ) { return this; }
virtual int GetToggleState( void ) { return m_toggle_state; }
virtual float GetDelay( void ) { return m_flWait; }
virtual void PlaySentence( const char *pszSentence, float duration, float volume, float attenuation );
virtual void PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener );
virtual void SentenceStop( void );
virtual BOOL IsAllowedToSpeak( void ) { return FALSE; }
// common member functions
void LinearMove( Vector vecDest, float flSpeed );
void EXPORT LinearMoveDone( void );
@ -692,6 +700,8 @@ public:
// Buttons that don't take damage can be IMPULSE used
virtual int ObjectCaps( void ) { return (CBaseToggle:: ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | (pev->takedamage?0:FCAP_IMPULSE_USE); }
BOOL IsAllowedToSpeak( void ) { return TRUE; }
BOOL m_fStayPushed; // button stays pushed in until touched again?
BOOL m_fRotating; // a rotating button? default is a sliding button.

View File

@ -893,6 +893,8 @@ void ClientPrecache( void )
PRECACHE_SOUND( "plats/train_use1.wav" ); // use a train
PRECACHE_SOUND( "plats/vehicle_ignition.wav" );
PRECACHE_SOUND( "buttons/spark5.wav" ); // hit computer texture
PRECACHE_SOUND( "buttons/spark6.wav" );
PRECACHE_SOUND( "debris/glass1.wav" );
@ -1121,6 +1123,7 @@ we could also use the pas/ pvs that we set in SetupVisibility, if we wanted to.
int AddToFullPack( struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, unsigned char *pSet )
{
int i;
CBaseEntity *Entity;
// don't send if flagged for NODRAW and it's not the host getting the message
if( ( ent->v.effects & EF_NODRAW ) && ( ent != host ) )
@ -1306,6 +1309,17 @@ int AddToFullPack( struct entity_state_s *state, int e, edict_t *ent, edict_t *h
state->health = (int)ent->v.health;
}
if( ( Entity = CBaseEntity::Instance( ent ))
&& Entity->Classify() != CLASS_NONE
&& Entity->Classify() != CLASS_MACHINE )
{
SetBits( state->eflags, EFLAG_MONSTER );
}
else
{
ClearBits( state->eflags, EFLAG_SLERP | EFLAG_MONSTER );
}
return 1;
}

View File

@ -103,6 +103,7 @@ set SOURCES=agrunt.cpp ^
tripmine.cpp ^
turret.cpp ^
util.cpp ^
vehicle.cpp ^
weapons.cpp ^
world.cpp ^
xen.cpp ^

View File

@ -291,7 +291,8 @@ void CEgon::Fire( const Vector &vecOrigSrc, const Vector &vecDir )
// multiplayer uses 1 ammo every 1/10th second
if( gpGlobals->time >= m_flAmmoUseTime )
{
UseAmmo( 1 );
if( !g_pGameRules->IsBustingGame())
UseAmmo( 1 );
m_flAmmoUseTime = gpGlobals->time + 0.1f;
}
}
@ -336,7 +337,8 @@ void CEgon::Fire( const Vector &vecOrigSrc, const Vector &vecDir )
//multiplayer uses 5 ammo/second
if( gpGlobals->time >= m_flAmmoUseTime )
{
UseAmmo( 1 );
if( !g_pGameRules->IsBustingGame())
UseAmmo( 1 );
m_flAmmoUseTime = gpGlobals->time + 0.2f;
}
}
@ -497,6 +499,15 @@ void CEgon::WeaponIdle( void )
m_deployed = TRUE;
}
BOOL CEgon::CanHolster( void )
{
#if CLIENT_DLL
return TRUE;
#else
return !g_pGameRules->IsBustingGame();
#endif
}
void CEgon::EndAttack( void )
{
bool bMakeNoise = false;

View File

@ -928,22 +928,25 @@ void CPushable::Move( CBaseEntity *pOther, int push )
if( pOther->IsPlayer() )
{
// g-cont. fix pushable acceleration bug (now implemented as cvar)
if (pushablemode.value == 1)
{
// Allow player push when moving right, left and back too
if ( push && !(pevToucher->button & (IN_FORWARD|IN_MOVERIGHT|IN_MOVELEFT|IN_BACK)) )
return;
// Require player walking back when applying '+use' on pushable
if ( !push && !(pevToucher->button & (IN_BACK)) )
return;
}
else
if( pushablemode.value == -1 )
{
// Don't push unless the player is pushing forward and NOT use (pull)
if( push && !( pevToucher->button & ( IN_FORWARD | IN_USE ) ) )
if( push && !( pevToucher->button & ( IN_FORWARD | IN_USE )))
return;
}
// g-cont. fix pushable acceleration bug (now implemented as cvar)
else if( pushablemode.value != 0 )
{
// Allow player push when moving right, left and back too
if( push && !( pevToucher->button & ( IN_FORWARD | IN_MOVERIGHT | IN_MOVELEFT | IN_BACK )))
return;
// Require player walking back when applying '+use' on pushable
if( !push && !( pevToucher->button & ( IN_BACK )))
return;
}
// Don't push when +use pressed
else if( push && ( pevToucher->button & ( IN_USE )))
return;
playerTouch = 1;
}
@ -964,30 +967,55 @@ void CPushable::Move( CBaseEntity *pOther, int push )
else
factor = 0.25f;
// Spirit fix for pushable acceleration
if (pushablemode.value == 2)
if( pushablemode.value != 0 )
{
if (!push)
factor *= 0.5f;
pev->velocity.x += pevToucher->velocity.x * factor;
pev->velocity.y += pevToucher->velocity.y * factor;
}
else
{
if( push )
{
factor = 0.25f;
pev->velocity.x += pevToucher->velocity.x * factor;
pev->velocity.y += pevToucher->velocity.y * factor;
}
else
{
// fix for pushable acceleration
if( sv_pushable_fixed_tick_fudge.value >= 0 )
factor *= ( sv_pushable_fixed_tick_fudge.value * gpGlobals->frametime );
pev->velocity.x += pevToucher->velocity.x * factor;
pev->velocity.y += pevToucher->velocity.y * factor;
if( fabs( pev->velocity.x ) < fabs( pevToucher->velocity.x - pevToucher->velocity.x * factor ))
pev->velocity.x += pevToucher->velocity.x * factor;
if( fabs( pev->velocity.y ) < fabs( pevToucher->velocity.y - pevToucher->velocity.y * factor ))
pev->velocity.y += pevToucher->velocity.y * factor;
}
}
float length = sqrt( pev->velocity.x * pev->velocity.x + pev->velocity.y * pev->velocity.y );
if( push && ( length > MaxSpeed() ) )
if( ( push && pushablemode.value != 0 )
|| pushablemode.value == 0 )
{
pev->velocity.x = (pev->velocity.x * MaxSpeed() / length );
pev->velocity.y = (pev->velocity.y * MaxSpeed() / length );
if( length > MaxSpeed())
{
pev->velocity.x = ( pev->velocity.x * MaxSpeed() / length );
pev->velocity.y = ( pev->velocity.y * MaxSpeed() / length );
}
}
if( playerTouch )
{
pevToucher->velocity.x = pev->velocity.x;
pevToucher->velocity.y = pev->velocity.y;
if( push || pushablemode.value != 0 )
{
pevToucher->velocity.x = pev->velocity.x;
pevToucher->velocity.y = pev->velocity.y;
}
if( ( gpGlobals->time - m_soundTime ) > 0.7f )
{
m_soundTime = gpGlobals->time;
if( length > 0 && FBitSet( pev->flags,FL_ONGROUND ) )
if( length > 0 && FBitSet( pev->flags, FL_ONGROUND ))
{
m_lastSound = RANDOM_LONG( 0, 2 );
EMIT_SOUND( ENT( pev ), CHAN_WEAPON, m_soundNames[m_lastSound], 0.5f, ATTN_NORM );

View File

@ -34,7 +34,7 @@ cvar_t falldamage = { "mp_falldamage","0", FCVAR_SERVER };
cvar_t weaponstay = { "mp_weaponstay","0", FCVAR_SERVER };
cvar_t selfgauss = { "selfgauss", "1", FCVAR_SERVER };
cvar_t chargerfix = { "chargerfix", "0", FCVAR_SERVER };
cvar_t satchelfix = { "satchelfix", "0", FCVAR_SERVER };
cvar_t satchelfix = { "satchelfix", "1", FCVAR_SERVER };
cvar_t explosionfix = { "explosionfix", "0", FCVAR_SERVER };
cvar_t monsteryawspeedfix = { "monsteryawspeedfix", "1", FCVAR_SERVER };
cvar_t corpsephysics = { "corpsephysics", "0", FCVAR_SERVER };
@ -457,6 +457,9 @@ cvar_t sk_player_leg3 = { "sk_player_leg3","1" };
// END Cvars for Skill Level settings
cvar_t sv_pushable_fixed_tick_fudge = { "sv_pushable_fixed_tick_fudge", "15" };
cvar_t sv_busters = { "sv_busters", "0" };
// Register your console variables here
// This gets called one time when the game is initialied
void GameDLLInit( void )
@ -505,7 +508,7 @@ void GameDLLInit( void )
CVAR_REGISTER( &multibyte_only );
CVAR_REGISTER( &mp_chattime );
CVAR_REGISTER( &sv_busters );
// REGISTER CVARS FOR SKILL LEVEL STUFF
@ -883,6 +886,8 @@ void GameDLLInit( void )
CVAR_REGISTER( &sk_player_leg3 );
// END REGISTER CVARS FOR SKILL LEVEL STUFF
CVAR_REGISTER( &sv_pushable_fixed_tick_fudge );
SERVER_COMMAND( "exec skill.cfg\n" );
}

View File

@ -43,6 +43,8 @@ extern cvar_t teamoverride;
extern cvar_t defaultteam;
extern cvar_t allowmonsters;
extern cvar_t bhopcap;
extern cvar_t sv_pushable_fixed_tick_fudge;
extern cvar_t sv_busters;
// Engine Cvars
extern cvar_t *g_psv_gravity;

View File

@ -331,6 +331,11 @@ CGameRules *InstallGameRules( void )
g_teamplay = 1;
return new CHalfLifeTeamplay;
}
if( sv_busters.value > 0 )
{
g_teamplay = 0;
return new CMultiplayBusters;
}
if( (int)gpGlobals->deathmatch == 1 )
{
// vanilla deathmatch

View File

@ -162,10 +162,11 @@ public:
// Immediately end a multiplayer game
virtual void EndMultiplayerGame( void ) {}
virtual BOOL IsBustingGame( void ){ return FALSE; };
};
extern CGameRules *InstallGameRules( void );
BOOL HLGetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon );
//=========================================================
// CHalfLifeRules - rules for the single player Half-Life
@ -362,5 +363,30 @@ protected:
void SendMOTDToClient( edict_t *client );
};
bool IsPlayerBusting( CBaseEntity *pPlayer );
BOOL BustingCanHaveItem( CBasePlayer *pPlayer, CBaseEntity *pItem );
class CMultiplayBusters : public CHalfLifeMultiplay
{
public:
CMultiplayBusters();
void Think();
void PlayerSpawn( CBasePlayer *pPlayer );
void ClientUserInfoChanged( CBasePlayer *pPlayer, char *infobuffer );
int IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled );
void PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor );
void DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pevInflictor );
BOOL CanHavePlayerItem( CBasePlayer *pPlayer, CBasePlayerItem *pItem );
void PlayerGotWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon );
int WeaponShouldRespawn( CBasePlayerItem *pWeapon );
BOOL CanHaveItem( CBasePlayer *pPlayer, CItem *pItem );
void CheckForEgons();
void SetPlayerModel( CBasePlayer *pPlayer, BOOL bKnownBuster );
BOOL IsBustingGame( void ){ return TRUE; };
protected:
float m_flEgonBustingCheckTime;
};
extern DLL_GLOBAL CGameRules *g_pGameRules;
#endif // GAMERULES_H

View File

@ -170,6 +170,8 @@ void CGauss::PrimaryAttack()
void CGauss::SecondaryAttack()
{
if( m_pPlayer->m_flStartCharge > gpGlobals->time )
m_pPlayer->m_flStartCharge = gpGlobals->time;
// don't fire underwater
if( m_pPlayer->pev->waterlevel == 3 )
{
@ -315,6 +317,8 @@ void CGauss::StartFire( void )
{
float flDamage;
if( m_pPlayer->m_flStartCharge > gpGlobals->time )
m_pPlayer->m_flStartCharge = gpGlobals->time;
UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle );
Vector vecAiming = gpGlobals->v_forward;
Vector vecSrc = m_pPlayer->GetGunPosition(); // + gpGlobals->v_up * -8 + gpGlobals->v_right * 8;
@ -426,6 +430,8 @@ void CGauss::Fire( Vector vecOrigSrc, Vector vecDir, float flDamage )
if( pEntity->pev->takedamage )
{
ClearMultiDamage();
if( pEntity->pev == m_pPlayer->pev )
tr.iHitgroup = 0;
pEntity->TraceAttack( m_pPlayer->pev, flDamage, vecDir, &tr, DMG_BULLET );
ApplyMultiDamage( m_pPlayer->pev, m_pPlayer->pev );
}

View File

@ -267,6 +267,7 @@ void CGrenade::BounceTouch( CBaseEntity *pOther )
pev->velocity = pev->velocity * 0.8f;
pev->sequence = RANDOM_LONG( 1, 1 );
ResetSequenceInfo();
}
else
{
@ -410,7 +411,9 @@ CGrenade *CGrenade::ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector vec
pGrenade->pev->velocity = Vector( 0, 0, 0 );
}
SET_MODEL( ENT( pGrenade->pev ), "models/w_grenade.mdl" );
pGrenade->pev->sequence = RANDOM_LONG( 3, 6 );
pGrenade->ResetSequenceInfo();
pGrenade->pev->framerate = 1.0f;
// Tumble through the air
@ -419,7 +422,6 @@ CGrenade *CGrenade::ShootTimed( entvars_t *pevOwner, Vector vecStart, Vector vec
pGrenade->pev->gravity = 0.5f;
pGrenade->pev->friction = 0.8f;
SET_MODEL( ENT( pGrenade->pev ), "models/w_grenade.mdl" );
pGrenade->pev->dmg = 100;
return pGrenade;

View File

@ -49,6 +49,8 @@ public:
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
BOOL IsAllowedToSpeak( void ) { return TRUE; }
int m_animate;
};

View File

@ -140,9 +140,9 @@ void CHandGrenade::WeaponIdle( void )
else
angThrow.x = -10.0f + angThrow.x * ( ( 90.0f + 10.0f ) / 90.0f );
float flVel = ( 90.0f - angThrow.x ) * 4.0f;
if( flVel > 500.0f )
flVel = 500.0f;
float flVel = ( 90.0f - angThrow.x ) * 6.5f;
if( flVel > 1000.0f )
flVel = 1000.0f;
UTIL_MakeVectors( angThrow );

View File

@ -732,6 +732,7 @@ void CISlave::ArmBeam( int side )
m_pBeam[m_iBeams]->SetColor( 96, 128, 16 );
m_pBeam[m_iBeams]->SetBrightness( 64 );
m_pBeam[m_iBeams]->SetNoise( 80 );
m_pBeam[m_iBeams]->pev->spawnflags |= SF_BEAM_TEMPORARY; // Flag these to be destroyed on save/restore or level transition
m_iBeams++;
}
@ -776,6 +777,7 @@ void CISlave::WackBeam( int side, CBaseEntity *pEntity )
m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
m_pBeam[m_iBeams]->SetBrightness( 255 );
m_pBeam[m_iBeams]->SetNoise( 80 );
m_pBeam[m_iBeams]->pev->spawnflags |= SF_BEAM_TEMPORARY; // Flag these to be destroyed on save/restore or level transition
m_iBeams++;
}
@ -806,6 +808,7 @@ void CISlave::ZapBeam( int side )
m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
m_pBeam[m_iBeams]->SetBrightness( 255 );
m_pBeam[m_iBeams]->SetNoise( 20 );
m_pBeam[m_iBeams]->pev->spawnflags |= SF_BEAM_TEMPORARY; // Flag these to be destroyed on save/restore or level transition
m_iBeams++;
pEntity = CBaseEntity::Instance( tr.pHit );

View File

@ -1392,7 +1392,7 @@ float CBaseMonster::OpenDoorAndWait( entvars_t *pevDoor )
//ALERT( at_aiconsole, "A door. " );
CBaseEntity *pcbeDoor = CBaseEntity::Instance( pevDoor );
if( pcbeDoor && !pcbeDoor->IsLockedByMaster() )
if( pcbeDoor )
{
//ALERT( at_aiconsole, "unlocked! " );
pcbeDoor->Use( this, this, USE_ON, 0.0 );
@ -3189,27 +3189,6 @@ BOOL CBaseMonster::FCanActiveIdle( void )
return FALSE;
}
void CBaseMonster::PlaySentence( const char *pszSentence, float duration, float volume, float attenuation )
{
if( pszSentence && IsAlive() )
{
if( pszSentence[0] == '!' )
EMIT_SOUND_DYN( edict(), CHAN_VOICE, pszSentence, volume, attenuation, 0, PITCH_NORM );
else
SENTENCEG_PlayRndSz( edict(), pszSentence, volume, attenuation, 0, PITCH_NORM );
}
}
void CBaseMonster::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener )
{
PlaySentence( pszSentence, duration, volume, attenuation );
}
void CBaseMonster::SentenceStop( void )
{
EMIT_SOUND( edict(), CHAN_VOICE, "common/null.wav", 1.0, ATTN_IDLE );
}
void CBaseMonster::CorpseFallThink( void )
{
if( pev->flags & FL_ONGROUND )

View File

@ -30,6 +30,7 @@
#include "voice_gamemgr.h"
#endif
#include "hltv.h"
#include "trains.h"
extern DLL_GLOBAL CGameRules *g_pGameRules;
extern DLL_GLOBAL BOOL g_fGameOver;
@ -344,70 +345,11 @@ BOOL CHalfLifeMultiplay::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBasePlayerI
return FALSE;
}
//=========================================================
//=========================================================
BOOL CHalfLifeMultiplay::GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon )
{
CBasePlayerItem *pCheck;
CBasePlayerItem *pBest;// this will be used in the event that we don't find a weapon in the same category.
int iBestWeight;
int i;
iBestWeight = -1;// no weapon lower than -1 can be autoswitched to
pBest = NULL;
if( !pCurrentWeapon->CanHolster() )
{
// can't put this gun away right now, so can't switch.
return FALSE;
}
for( i = 0; i < MAX_ITEM_TYPES; i++ )
{
pCheck = pPlayer->m_rgpPlayerItems[i];
while( pCheck )
{
if( pCheck->iWeight() > -1 && pCheck->iWeight() == pCurrentWeapon->iWeight() && pCheck != pCurrentWeapon )
{
// this weapon is from the same category.
if ( pCheck->CanDeploy() )
{
if ( pPlayer->SwitchWeapon( pCheck ) )
{
return TRUE;
}
}
}
else if( pCheck->iWeight() > iBestWeight && pCheck != pCurrentWeapon )// don't reselect the weapon we're trying to get rid of
{
//ALERT ( at_console, "Considering %s\n", STRING( pCheck->pev->classname ) );
// we keep updating the 'best' weapon just in case we can't find a weapon of the same weight
// that the player was using. This will end up leaving the player with his heaviest-weighted
// weapon.
if( pCheck->CanDeploy() )
{
// if this weapon is useable, flag it as the best
iBestWeight = pCheck->iWeight();
pBest = pCheck;
}
}
pCheck = pCheck->m_pNext;
}
}
// if we make it here, we've checked all the weapons and found no useable
// weapon in the same catagory as the current weapon.
// if pBest is null, we didn't find ANYTHING. Shouldn't be possible- should always
// at least get the crowbar, but ya never know.
if( !pBest )
{
return FALSE;
}
pPlayer->SwitchWeapon( pBest );
return TRUE;
return HLGetNextBestWeapon( pPlayer, pCurrentWeapon );
}
//=========================================================
@ -647,6 +589,16 @@ void CHalfLifeMultiplay::PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller,
CBaseEntity *ktmp = CBaseEntity::Instance( pKiller );
if( ktmp && (ktmp->Classify() == CLASS_PLAYER ) )
peKiller = (CBasePlayer*)ktmp;
else if( ktmp && ktmp->Classify() == CLASS_VEHICLE )
{
CBasePlayer *pDriver = (CBasePlayer *)( (CFuncVehicle *)ktmp )->m_pDriver;
if( pDriver != NULL )
{
pKiller = pDriver->pev;
peKiller = (CBasePlayer *)pDriver;
}
}
if( pVictim->pev == pKiller )
{
@ -1712,3 +1664,219 @@ void CHalfLifeMultiplay::SendMOTDToClient( edict_t *client )
FREE_FILE( (void*)aFileList );
}
int CMultiplayBusters::WeaponShouldRespawn( CBasePlayerItem *pWeapon )
{
if( pWeapon->m_iId == WEAPON_EGON )
return GR_WEAPON_RESPAWN_NO;
return CHalfLifeMultiplay::WeaponShouldRespawn( pWeapon );
}
BOOL CMultiplayBusters::CanHaveItem( CBasePlayer *pPlayer, CItem *pItem )
{
return BustingCanHaveItem( pPlayer, pItem );
}
BOOL CMultiplayBusters::CanHavePlayerItem( CBasePlayer *pPlayer, CBasePlayerItem *pItem )
{
if( !BustingCanHaveItem( pPlayer, pItem ))
return FALSE;
return CHalfLifeMultiplay::CanHavePlayerItem( pPlayer, pItem );
}
int CMultiplayBusters::IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled )
{
if( IsPlayerBusting( pAttacker ))
return 1;
if( IsPlayerBusting( pKilled ))
return 2;
return 0;
}
void CMultiplayBusters::DeathNotice( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pevInflictor )
{
if( IsPlayerBusting( pVictim )
|| IsPlayerBusting( CBaseEntity::Instance( pKiller )))
CHalfLifeMultiplay::DeathNotice( pVictim, pKiller, pevInflictor );
}
void CMultiplayBusters::PlayerKilled( CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor )
{
if( IsPlayerBusting( pVictim ))
{
UTIL_ClientPrintAll( HUD_PRINTCENTER, "The Buster is dead!!" );
m_flEgonBustingCheckTime = -1.0f;
CBasePlayer *peKiller = NULL;
CBaseEntity *ktmp = CBaseEntity::Instance( pKiller );
if( ktmp && ( ktmp->Classify() == CLASS_PLAYER ) )
peKiller = (CBasePlayer*)ktmp;
else if( ktmp && ktmp->Classify() == CLASS_VEHICLE )
{
CBasePlayer *pDriver = (CBasePlayer *)( (CFuncVehicle *)ktmp )->m_pDriver;
if( pDriver != NULL )
{
pKiller = pDriver->pev;
peKiller = (CBasePlayer *)pDriver;
}
}
if( peKiller && peKiller->IsPlayer() )
{
UTIL_ClientPrintAll( HUD_PRINTTALK, UTIL_VarArgs( "%s has killed the Buster!", STRING( peKiller->pev->netname )));
}
pVictim->pev->renderfx = 0;
pVictim->pev->rendercolor = g_vecZero;
}
CHalfLifeMultiplay::PlayerKilled( pVictim, pKiller, pInflictor );
}
void CMultiplayBusters::ClientUserInfoChanged( CBasePlayer *pPlayer, char *infobuffer )
{
SetPlayerModel( pPlayer, FALSE );
CHalfLifeMultiplay::ClientUserInfoChanged( pPlayer, infobuffer );
}
void CMultiplayBusters::PlayerSpawn( CBasePlayer *pPlayer )
{
CHalfLifeMultiplay::PlayerSpawn( pPlayer );
SetPlayerModel( pPlayer, FALSE );
}
bool IsPlayerBusting( CBaseEntity *pPlayer )
{
if( g_pGameRules->IsBustingGame()
&& pPlayer && pPlayer->IsPlayer()
&& ((CBasePlayer*)pPlayer)->HasPlayerItemFromID( WEAPON_EGON ))
return true;
return false;
}
BOOL BustingCanHaveItem( CBasePlayer *pPlayer, CBaseEntity *pItem )
{
if( IsPlayerBusting( pPlayer )
&& !( strncmp( STRING( pItem->pev->classname ), "weapon_", 7 )
&& strncmp( STRING( pItem->pev->classname ), "ammo_", 5 )))
return FALSE;
return TRUE;
}
CMultiplayBusters::CMultiplayBusters()
{
CHalfLifeMultiplay();
m_flEgonBustingCheckTime = -1.0;
}
void CMultiplayBusters::CheckForEgons( void )
{
CBaseEntity *pPlayer;
CWeaponBox *pWeaponBox = NULL;
CBasePlayerItem *pWeapon;
CBasePlayer *pNewBuster = NULL;
int i, bestfrags = 9999;
if( m_flEgonBustingCheckTime <= 0.0f )
{
m_flEgonBustingCheckTime = gpGlobals->time + 10.0f;
return;
}
if( gpGlobals->time < m_flEgonBustingCheckTime )
return;
m_flEgonBustingCheckTime = -1.0f;
for( i = 1; i <= gpGlobals->maxClients; i++ )
{
pPlayer = UTIL_PlayerByIndex( i );
if( IsPlayerBusting( pPlayer ))
return;
}
for( i = 1; i <= gpGlobals->maxClients; i++ )
{
pPlayer = UTIL_PlayerByIndex( i );
if( pPlayer && pPlayer->pev->frags < bestfrags )
{
pNewBuster = (CBasePlayer*)pPlayer;
bestfrags = pPlayer->pev->frags;
}
}
if( !pNewBuster )
return;
pNewBuster->GiveNamedItem( "weapon_egon" );
while( ( pWeaponBox = (CWeaponBox*)UTIL_FindEntityByClassname( pWeaponBox, "weaponbox" )))
{
// destroy weaponboxes with egons
for( i = 0; i < MAX_ITEM_TYPES; i++ )
{
pWeapon = pWeaponBox->m_rgpPlayerItems[i];
while( pWeapon )
{
if( pWeapon->m_iId != WEAPON_EGON )
{
pWeapon = pWeapon->m_pNext;
continue;
}
pWeaponBox->Kill();
pWeapon = 0;
i = MAX_ITEM_TYPES;
}
}
}
}
void CMultiplayBusters::Think( void )
{
CheckForEgons();
CHalfLifeMultiplay::Think();
}
void CMultiplayBusters::SetPlayerModel( CBasePlayer *pPlayer, BOOL bKnownBuster )
{
const char *pszModel = NULL;
if( bKnownBuster || IsPlayerBusting( pPlayer ))
{
pszModel = "ivan";
}
else
{
pszModel = "skeleton";
}
g_engfuncs.pfnSetClientKeyValue( ENTINDEX( pPlayer->edict()), g_engfuncs.pfnGetInfoKeyBuffer( pPlayer->edict()), "model", pszModel );
}
void CMultiplayBusters::PlayerGotWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pWeapon )
{
if( pWeapon->m_iId != WEAPON_EGON )
return;
pPlayer->RemoveAllItems( FALSE );
UTIL_ClientPrintAll( HUD_PRINTCENTER, "Long live the new Buster!" );
UTIL_ClientPrintAll( HUD_PRINTTALK, UTIL_VarArgs( "%s is busting!\n", STRING( pPlayer->pev->netname )));
SetPlayerModel( pPlayer, TRUE );
pPlayer->pev->health = pPlayer->pev->max_health;
pPlayer->pev->armorvalue = MAX_NORMAL_BATTERY;
pPlayer->pev->renderfx = kRenderFxGlowShell;
pPlayer->pev->renderamt = 25;
pPlayer->pev->rendercolor = Vector( 0, 75, 250 );
pPlayer->m_rgAmmo[pWeapon->PrimaryAmmoIndex()] = pPlayer->ammo_uranium = 100;
}

View File

@ -661,7 +661,7 @@ void CBasePlayer::PackDeadPlayerItems( void )
{
int iWeaponRules;
int iAmmoRules;
int i;
int i, j;
CBasePlayerWeapon *rgpPackWeapons[MAX_WEAPONS] = {0,};
int iPackAmmo[MAX_AMMO_SLOTS];
int iPW = 0;// index into packweapons array
@ -696,16 +696,26 @@ void CBasePlayer::PackDeadPlayerItems( void )
if( m_pActiveItem && pPlayerItem == m_pActiveItem )
{
// this is the active item. Pack it.
rgpPackWeapons[iPW++] = (CBasePlayerWeapon *)pPlayerItem;
rgpPackWeapons[iPW] = (CBasePlayerWeapon *)pPlayerItem;
}
break;
case GR_PLR_DROP_GUN_ALL:
rgpPackWeapons[iPW++] = (CBasePlayerWeapon *)pPlayerItem;
rgpPackWeapons[iPW] = (CBasePlayerWeapon *)pPlayerItem;
break;
default:
break;
}
if( rgpPackWeapons[iPW] )
{
// complete the reload.
j = Q_min( rgpPackWeapons[iPW]->iMaxClip() - rgpPackWeapons[iPW]->m_iClip, m_rgAmmo[rgpPackWeapons[iPW]->m_iPrimaryAmmoType] );
// Add them to the clip
rgpPackWeapons[iPW]->m_iClip += j;
m_rgAmmo[rgpPackWeapons[iPW]->m_iPrimaryAmmoType] -= j;
iPW++;
}
pPlayerItem = pPlayerItem->m_pNext;
}
}
@ -755,24 +765,44 @@ void CBasePlayer::PackDeadPlayerItems( void )
iPA = 0;
iPW = 0;
// pack the ammo
while( iPackAmmo[iPA] != -1 )
if( g_pGameRules->IsBustingGame())
{
pWeaponBox->PackAmmo( MAKE_STRING( CBasePlayerItem::AmmoInfoArray[iPackAmmo[iPA]].pszName ), m_rgAmmo[iPackAmmo[iPA]] );
iPA++;
while( rgpPackWeapons[iPW] )
{
// weapon unhooked from the player. Pack it into der box.
if( FClassnameIs( rgpPackWeapons[iPW]->pev, "weapon_egon" ))
{
pWeaponBox->PackWeapon( rgpPackWeapons[iPW] );
SET_MODEL( pWeaponBox->edict(), "models/w_egon.mdl" );
pWeaponBox->pev->velocity = g_vecZero;
pWeaponBox->pev->renderfx = kRenderFxGlowShell;
pWeaponBox->pev->renderamt = 25;
pWeaponBox->pev->rendercolor = Vector( 0, 75, 250 );
break;
}
iPW++;
}
}
// now pack all of the items in the lists
while( rgpPackWeapons[iPW] )
else
{
// weapon unhooked from the player. Pack it into der box.
pWeaponBox->PackWeapon( rgpPackWeapons[iPW] );
// pack the ammo
while( iPackAmmo[iPA] != -1 )
{
pWeaponBox->PackAmmo( MAKE_STRING( CBasePlayerItem::AmmoInfoArray[iPackAmmo[iPA]].pszName ), m_rgAmmo[iPackAmmo[iPA]] );
iPA++;
}
iPW++;
// now pack all of the items in the lists
while( rgpPackWeapons[iPW] )
{
// weapon unhooked from the player. Pack it into der box.
pWeaponBox->PackWeapon( rgpPackWeapons[iPW] );
iPW++;
}
end:
pWeaponBox->pev->velocity = pev->velocity * 1.2f;// weaponbox has player's velocity, then some.
}
pWeaponBox->pev->velocity = pev->velocity * 1.2;// weaponbox has player's velocity, then some.
RemoveAllItems( TRUE );// now strip off everything that wasn't handled by the code above.
}
@ -867,7 +897,7 @@ void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib )
SetAnimation( PLAYER_DIE );
m_iRespawnFrames = 0;
m_flRespawnTimer = 0;
pev->modelindex = g_ulModelIndexPlayer; // don't use eyes
@ -903,6 +933,9 @@ void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib )
// UNDONE: Put this in, but add FFADE_PERMANENT and make fade time 8.8 instead of 4.12
// UTIL_ScreenFade( edict(), Vector( 128, 0, 0 ), 6, 15, 255, FFADE_OUT | FFADE_MODULATE );
if( g_pGameRules->IsMultiplayer())
pev->solid = SOLID_NOT;
if( ( pev->health < -40 && iGib != GIB_NEVER ) || iGib == GIB_ALWAYS )
{
pev->solid = SOLID_NOT;
@ -1263,23 +1296,30 @@ void CBasePlayer::PlayerDeathThink( void )
PackDeadPlayerItems();
}
if( pev->modelindex && ( !m_fSequenceFinished ) && ( pev->deadflag == DEAD_DYING ) )
if( pev->modelindex && ( !m_fSequenceFinished ) && ( pev->deadflag == DEAD_DYING ))
{
StudioFrameAdvance();
m_iRespawnFrames++; // Note, these aren't necessarily real "frames", so behavior is dependent on # of client movement commands
if( m_iRespawnFrames < 120 ) // Animations should be no longer than this
m_flRespawnTimer = gpGlobals->frametime + m_flRespawnTimer; // Note, these aren't necessarily real "frames", so behavior is dependent on # of client movement commands
if( m_flRespawnTimer < 4.0f ) // Animations should be no longer than this
return;
}
if( pev->deadflag == DEAD_DYING )
{
if( g_pGameRules->IsMultiplayer() && m_fSequenceFinished && pev->movetype == MOVETYPE_NONE )
{
CopyToBodyQue( pev );
pev->modelindex = 0;
}
pev->deadflag = DEAD_DEAD;
}
// once we're done animating our death and we're on the ground, we want to set movetype to None so our dead body won't do collisions and stuff anymore
// this prevents a bug where the dead body would go to a player's head if he walked over it while the dead player was clicking their button to respawn
if( pev->movetype != MOVETYPE_NONE && FBitSet( pev->flags, FL_ONGROUND ) )
pev->movetype = MOVETYPE_NONE;
if( pev->deadflag == DEAD_DYING )
pev->deadflag = DEAD_DEAD;
StopAnimation();
pev->effects |= EF_NOINTERP;
@ -1319,7 +1359,7 @@ void CBasePlayer::PlayerDeathThink( void )
return;
pev->button = 0;
m_iRespawnFrames = 0;
m_flRespawnTimer = 0;
//ALERT( at_console, "Respawn\n" );
@ -1484,18 +1524,31 @@ void CBasePlayer::PlayerUse( void )
{
m_afPhysicsFlags &= ~PFLAG_ONTRAIN;
m_iTrain = TRAIN_NEW|TRAIN_OFF;
CBaseEntity *pTrain = Instance( pev->groundentity );
if( pTrain && pTrain->Classify() == CLASS_VEHICLE )
{
( (CFuncVehicle *)pTrain )->m_pDriver = NULL;
}
return;
}
else
{ // Start controlling the train!
CBaseEntity *pTrain = CBaseEntity::Instance( pev->groundentity );
if( pTrain && !( pev->button & IN_JUMP ) && FBitSet( pev->flags, FL_ONGROUND ) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE ) && pTrain->OnControls( pev ) )
if( pTrain && !( pev->button & IN_JUMP ) && FBitSet( pev->flags, FL_ONGROUND ) && ( pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE ) && pTrain->OnControls( pev ) )
{
m_afPhysicsFlags |= PFLAG_ONTRAIN;
m_iTrain = TrainSpeed( (int)pTrain->pev->speed, pTrain->pev->impulse );
m_iTrain |= TRAIN_NEW;
EMIT_SOUND( ENT( pev ), CHAN_ITEM, "plats/train_use1.wav", 0.8, ATTN_NORM );
if( pTrain->Classify() == CLASS_VEHICLE )
{
EMIT_SOUND( ENT( pev ), CHAN_ITEM, "plats/vehicle_ignition.wav", 0.8, ATTN_NORM );
( (CFuncVehicle *)pTrain )->m_pDriver = this;
}
else
EMIT_SOUND( ENT( pev ), CHAN_ITEM, "plats/train_use1.wav", 0.8, ATTN_NORM );
return;
}
}
@ -1609,9 +1662,17 @@ void CBasePlayer::Jump()
// If you're standing on a conveyor, add it's velocity to yours (for momentum)
entvars_t *pevGround = VARS( pev->groundentity );
if( pevGround && ( pevGround->flags & FL_CONVEYOR ) )
if( pevGround )
{
pev->velocity = pev->velocity + pev->basevelocity;
if( pevGround->flags & FL_CONVEYOR )
{
pev->velocity = pev->velocity + pev->basevelocity;
}
if( FClassnameIs( pevGround, "func_vehicle" ))
{
pev->velocity = pevGround->velocity + pev->velocity;
}
}
}
@ -1876,30 +1937,62 @@ void CBasePlayer::PreThink( void )
//ALERT( at_error, "In train mode with no train!\n" );
m_afPhysicsFlags &= ~PFLAG_ONTRAIN;
m_iTrain = TRAIN_NEW|TRAIN_OFF;
if( pTrain )
( (CFuncVehicle *)pTrain )->m_pDriver = NULL;
return;
}
}
else if( !FBitSet( pev->flags, FL_ONGROUND ) || FBitSet( pTrain->pev->spawnflags, SF_TRACKTRAIN_NOCONTROL ) || ( pev->button & ( IN_MOVELEFT | IN_MOVERIGHT ) ) )
else if( !FBitSet( pev->flags, FL_ONGROUND ) || FBitSet( pTrain->pev->spawnflags, SF_TRACKTRAIN_NOCONTROL )
|| ( ( pev->button & ( IN_MOVELEFT | IN_MOVERIGHT )) && pTrain->Classify() != CLASS_VEHICLE ))
{
// Turn off the train if you jump, strafe, or the train controls go dead
m_afPhysicsFlags &= ~PFLAG_ONTRAIN;
m_iTrain = TRAIN_NEW | TRAIN_OFF;
( (CFuncVehicle *)pTrain )->m_pDriver = NULL;
return;
}
pev->velocity = g_vecZero;
vel = 0;
if( m_afButtonPressed & IN_FORWARD )
{
vel = 1;
pTrain->Use( this, this, USE_SET, (float)vel );
}
else if( m_afButtonPressed & IN_BACK )
{
vel = -1;
pTrain->Use( this, this, USE_SET, (float)vel );
}
if( pTrain->Classify() == CLASS_VEHICLE )
{
if( pev->button & IN_FORWARD )
{
vel = 1;
pTrain->Use( this, this, USE_SET, vel );
}
if( pev->button & IN_BACK )
{
vel = -1;
pTrain->Use( this, this, USE_SET, vel );
}
if( pev->button & IN_MOVELEFT )
{
vel = 20;
pTrain->Use( this, this, USE_SET, vel );
}
if( pev->button & IN_MOVERIGHT )
{
vel = 30;
pTrain->Use( this, this, USE_SET, vel );
}
}
else
{
if( m_afButtonPressed & IN_FORWARD )
{
vel = 1;
pTrain->Use( this, this, USE_SET, vel );
}
else if( m_afButtonPressed & IN_BACK )
{
vel = -1;
pTrain->Use( this, this, USE_SET, vel );
}
}
iGearId = TrainSpeed( pTrain->pev->speed, pTrain->pev->impulse );
if( iGearId != ( m_iTrain & 0x0F ) ) // Vit_amiN: speed changed
@ -2718,7 +2811,7 @@ edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer )
{
pSpot = g_pLastSpawn;
// Randomize the start spot
for( int i = RANDOM_LONG( 1, 5 ); i > 0; i-- )
for( int i = RANDOM_LONG( 1, 9 ); i > 0; i-- )
pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" );
if( FNullEnt( pSpot ) ) // skip over the null point
pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" );
@ -2787,6 +2880,7 @@ ReturnSpot:
void CBasePlayer::Spawn( void )
{
m_flStartCharge = gpGlobals->time;
pev->classname = MAKE_STRING( "player" );
pev->health = 100;
pev->armorvalue = 0;
@ -3002,6 +3096,9 @@ int CBasePlayer::Restore( CRestore &restore )
// Barring that, we clear it out here instead of using the incorrect restored time value.
m_flNextAttack = UTIL_WeaponTimeBase();
#endif
if( m_flFlashLightTime == 0.0f )
m_flFlashLightTime = 1.0f;
return status;
}
@ -4568,6 +4665,31 @@ BOOL CBasePlayer::HasNamedPlayerItem( const char *pszItemName )
return FALSE;
}
//=========================================================
// HasPlayerItemFromID
//=========================================================
BOOL CBasePlayer::HasPlayerItemFromID( int nID )
{
CBasePlayerItem *pItem;
int i;
for( i = 0; i < MAX_ITEM_TYPES; i++ )
{
pItem = m_rgpPlayerItems[i];
while( pItem )
{
if( nID == pItem->m_iId )
{
return TRUE;
}
pItem = pItem->m_pNext;
}
}
return FALSE;
}
//=========================================================
//
//=========================================================

View File

@ -188,7 +188,7 @@ public:
Vector m_vecAutoAim;
BOOL m_fOnTarget;
int m_iDeaths;
float m_iRespawnFrames; // used in PlayerDeathThink() to make sure players can always respawn
float m_flRespawnTimer; // used in PlayerDeathThink() to make sure players can always respawn
int m_lastx, m_lasty; // These are the previous update's crosshair angles, DON"T SAVE/RESTORE
@ -265,6 +265,7 @@ public:
void DropPlayerItem ( char *pszItemName );
BOOL HasPlayerItem( CBasePlayerItem *pCheckItem );
BOOL HasNamedPlayerItem( const char *pszItemName );
BOOL HasPlayerItemFromID( int nID );
BOOL HasWeapons( void );// do I have ANY weapons?
void SelectPrevItem( int iItem );
void SelectNextItem( int iItem );

View File

@ -116,6 +116,25 @@ CRpgRocket *CRpgRocket::CreateRpgRocket( Vector vecOrigin, Vector vecAngles, CBa
return pRocket;
}
void CRpgRocket::Explode( TraceResult *pTrace, int bitsDamageType )
{
if( CRpg *pLauncher = GetLauncher())
{
// my launcher is still around, tell it I'm dead.
pLauncher->m_cActiveRockets--;
m_hLauncher = 0;
}
STOP_SOUND( edict(), CHAN_VOICE, "weapons/rocket1.wav" );
CGrenade::Explode( pTrace, bitsDamageType );
}
CRpg *CRpgRocket::GetLauncher( void )
{
return (CRpg*)( (CBaseEntity*)m_hLauncher );
}
//=========================================================
//=========================================================
void CRpgRocket::Spawn( void )
@ -150,10 +169,11 @@ void CRpgRocket::Spawn( void )
//=========================================================
void CRpgRocket::RocketTouch( CBaseEntity *pOther )
{
if( CRpg* pLauncher = (CRpg*)( (CBaseEntity*)( m_hLauncher ) ) )
if( CRpg *pLauncher = GetLauncher())
{
// my launcher is still around, tell it I'm dead.
pLauncher->m_cActiveRockets--;
m_hLauncher = 0;
}
STOP_SOUND( edict(), CHAN_VOICE, "weapons/rocket1.wav" );
@ -264,17 +284,23 @@ void CRpgRocket::FollowThink( void )
}
pev->velocity = pev->velocity * 0.2f + vecTarget * flSpeed * 0.798f;
if( pev->waterlevel == 0 && pev->velocity.Length() < 1500.0f )
{
if( CRpg *pLauncher = (CRpg*)( (CBaseEntity*)( m_hLauncher ) ) )
{
// my launcher is still around, tell it I'm dead.
pLauncher->m_cActiveRockets--;
}
Detonate();
}
}
// ALERT( at_console, "%.0f\n", flSpeed );
if( CRpg *pLauncher = GetLauncher())
{
if( ( pev->origin - pLauncher->pev->origin ).Length() > 8192 || gpGlobals->time - m_flIgniteTime > 6.0f )
{
// my launcher is still around, tell it I'm dead.
pLauncher->m_cActiveRockets--;
m_hLauncher = 0;
}
}
if( UTIL_PointContents( pev->origin ) == CONTENTS_SKY )
Detonate();
pev->nextthink = gpGlobals->time + 0.1f;
}
#endif
@ -374,7 +400,7 @@ int CRpg::GetItemInfo( ItemInfo *p )
p->iSlot = 3;
p->iPosition = 0;
p->iId = m_iId = WEAPON_RPG;
p->iFlags = 0;
p->iFlags = ITEM_FLAG_NOCHOICE;
p->iWeight = RPG_WEIGHT;
return 1;

View File

@ -22,6 +22,7 @@
#include "nodes.h"
#include "player.h"
#include "gamerules.h"
#include "game.h"
enum satchel_state
{
@ -191,23 +192,39 @@ LINK_ENTITY_TO_CLASS( weapon_satchel, CSatchel )
//=========================================================
int CSatchel::AddDuplicate( CBasePlayerItem *pOriginal )
{
#if !CLIENT_DLL
CSatchel *pSatchel;
int nNumSatchels, nSatchelsInPocket;
CBaseEntity *ent;
#if CLIENT_DLL
if( bIsMultiplayer() )
#else
if( g_pGameRules->IsMultiplayer() )
#endif
{
if( satchelfix.value )
{
if( !pOriginal->m_pPlayer )
return TRUE;
nNumSatchels = 0;
nSatchelsInPocket = pOriginal->m_pPlayer->m_rgAmmo[pOriginal->PrimaryAmmoIndex()];
ent = NULL;
while( ( ent = UTIL_FindEntityInSphere( ent, pOriginal->m_pPlayer->pev->origin, 4096 )) != NULL )
{
if( FClassnameIs( ent->pev, "monster_satchel" ))
nNumSatchels += ent->pev->owner == pOriginal->m_pPlayer->edict();
}
}
pSatchel = (CSatchel *)pOriginal;
if( pSatchel->m_chargeReady != SATCHEL_IDLE )
if( pSatchel->m_chargeReady != SATCHEL_IDLE
&& ( satchelfix.value && nSatchelsInPocket + nNumSatchels > SATCHEL_MAX_CARRY - 1 ))
{
// player has some satchels deployed. Refuse to add more.
return FALSE;
}
}
#endif
return CBasePlayerWeapon::AddDuplicate( pOriginal );
}
@ -330,8 +347,9 @@ void CSatchel::Holster( int skiplocal /* = 0 */ )
}
}
void CSatchel::PrimaryAttack()
void CSatchel::PrimaryAttack( void )
{
#if SATCHEL_OLD_BEHAVIOUR
switch( m_chargeReady )
{
case SATCHEL_IDLE:
@ -347,9 +365,9 @@ void CSatchel::PrimaryAttack()
CBaseEntity *pSatchel = NULL;
while( ( pSatchel = UTIL_FindEntityInSphere( pSatchel, m_pPlayer->pev->origin, 4096 ) ) != NULL )
while( ( pSatchel = UTIL_FindEntityInSphere( pSatchel, m_pPlayer->pev->origin, 4096 )) != NULL )
{
if( FClassnameIs( pSatchel->pev, "monster_satchel" ) )
if( FClassnameIs( pSatchel->pev, "monster_satchel" ))
{
if( pSatchel->pev->owner == pPlayer )
{
@ -368,14 +386,56 @@ void CSatchel::PrimaryAttack()
// we're reloading, don't allow fire
break;
}
}
void CSatchel::SecondaryAttack( void )
{
#else
if( m_chargeReady != SATCHEL_RELOAD )
{
Throw();
}
#endif
}
void CSatchel::SecondaryAttack( void )
{
#if SATCHEL_OLD_BEHAVIOUR
if( m_chargeReady != SATCHEL_RELOAD )
{
Throw();
}
#else
switch( m_chargeReady )
{
case SATCHEL_IDLE:
break;
case SATCHEL_READY:
{
SendWeaponAnim( SATCHEL_RADIO_FIRE );
edict_t *pPlayer = m_pPlayer->edict();
CBaseEntity *pSatchel = NULL;
while( ( pSatchel = UTIL_FindEntityInSphere( pSatchel, m_pPlayer->pev->origin, 4096 )) != NULL )
{
if( FClassnameIs( pSatchel->pev, "monster_satchel" ))
{
if( pSatchel->pev->owner == pPlayer )
{
pSatchel->Use( m_pPlayer, m_pPlayer, USE_ON, 0 );
}
}
}
m_chargeReady = SATCHEL_RELOAD;
m_flNextPrimaryAttack = GetNextAttackDelay( 0.5f );
m_flNextSecondaryAttack = UTIL_WeaponTimeBase() + 0.5f;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.5f;
break;
}
case SATCHEL_RELOAD:
// we're reloading, don't allow fire
break;
}
#endif
}
void CSatchel::Throw( void )

View File

@ -29,6 +29,8 @@
#define NUM_SCIENTIST_HEADS 4 // four heads available for scientist model
static cvar_t *g_psv_override_scientist_mdl;
enum
{
HEAD_GLASSES = 0,
@ -113,6 +115,8 @@ public:
CUSTOM_SCHEDULES
private:
const char *GetScientistModel( void );
float m_painTime;
float m_healTime;
float m_fearTime;
@ -422,6 +426,20 @@ void CScientist::DeclineFollowing( void )
PlaySentence( "SC_POK", 2, VOL_NORM, ATTN_NORM );
}
const char *CScientist::GetScientistModel( void )
{
if( !g_psv_override_scientist_mdl )
g_psv_override_scientist_mdl = CVAR_GET_POINTER( "_sv_override_scientist_mdl" );
if( !( g_psv_override_scientist_mdl && g_psv_override_scientist_mdl->string ))
return "models/scientist.mdl";
if( strlen( g_psv_override_scientist_mdl->string ) < sizeof( "01.mdl" ) - 1 )
return "models/scientist.mdl";
return g_psv_override_scientist_mdl->string;
}
void CScientist::Scream( void )
{
if( FOkToSpeak() )
@ -648,7 +666,7 @@ void CScientist::Spawn( void )
Precache();
SET_MODEL( ENT( pev ), "models/scientist.mdl" );
SET_MODEL( ENT( pev ), GetScientistModel());
UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );
pev->solid = SOLID_SLIDEBOX;
@ -679,7 +697,7 @@ void CScientist::Spawn( void )
//=========================================================
void CScientist::Precache( void )
{
PRECACHE_MODEL( "models/scientist.mdl" );
PRECACHE_MODEL( GetScientistModel());
PRECACHE_SOUND( "scientist/sci_pain1.wav" );
PRECACHE_SOUND( "scientist/sci_pain2.wav" );
PRECACHE_SOUND( "scientist/sci_pain3.wav" );
@ -1097,6 +1115,9 @@ public:
void KeyValue( KeyValueData *pkvd );
int m_iPose;// which sequence to display
static const char *m_szPoses[7];
private:
const char *GetScientistModel( void );
};
const char *CDeadScientist::m_szPoses[] =
@ -1127,8 +1148,10 @@ LINK_ENTITY_TO_CLASS( monster_scientist_dead, CDeadScientist )
//
void CDeadScientist::Spawn()
{
PRECACHE_MODEL( "models/scientist.mdl" );
SET_MODEL( ENT( pev ), "models/scientist.mdl" );
const char *pszModel = GetScientistModel();
PRECACHE_MODEL( pszModel );
SET_MODEL( ENT( pev ), pszModel );
pev->effects = 0;
pev->sequence = 0;
@ -1160,6 +1183,20 @@ void CDeadScientist::Spawn()
MonsterInitDead();
}
const char *CDeadScientist::GetScientistModel( void )
{
if( !g_psv_override_scientist_mdl )
g_psv_override_scientist_mdl = CVAR_GET_POINTER( "_sv_override_scientist_mdl" );
if( !( g_psv_override_scientist_mdl && g_psv_override_scientist_mdl->string ))
return "models/scientist.mdl";
if( strlen( g_psv_override_scientist_mdl->string ) < sizeof( "01.mdl" ) - 1 )
return "models/scientist.mdl";
return g_psv_override_scientist_mdl->string;
}
//=========================================================
// Sitting Scientist PROP
//=========================================================

View File

@ -902,9 +902,9 @@ public:
static TYPEDESCRIPTION m_SaveData[];
CBaseMonster *FindEntity( void );
BOOL AcceptableSpeaker( CBaseMonster *pMonster );
BOOL StartSentence( CBaseMonster *pTarget );
CBaseToggle *FindEntity( void );
BOOL AcceptableSpeaker( CBaseToggle *pTarget );
BOOL StartSentence( CBaseToggle *pTarget );
private:
string_t m_iszSentence; // string index for idle animation
@ -1036,20 +1036,20 @@ void CScriptedSentence::Spawn( void )
void CScriptedSentence::FindThink( void )
{
CBaseMonster *pMonster = FindEntity();
if( pMonster )
CBaseToggle *pTarget = FindEntity();
if( pTarget )
{
StartSentence( pMonster );
StartSentence( pTarget );
if( pev->spawnflags & SF_SENTENCE_ONCE )
UTIL_Remove( this );
SetThink( &CScriptedSentence::DelayThink );
pev->nextthink = gpGlobals->time + m_flDuration + m_flRepeat;
m_active = FALSE;
//ALERT( at_console, "%s: found monster %s\n", STRING( m_iszSentence ), STRING( m_iszEntity ) );
//ALERT( at_console, "%s: found target %s\n", STRING( m_iszSentence ), STRING( m_iszEntity ) );
}
else
{
//ALERT( at_console, "%s: can't find monster %s\n", STRING( m_iszSentence ), STRING( m_iszEntity ) );
//ALERT( at_console, "%s: can't find target %s\n", STRING( m_iszSentence ), STRING( m_iszEntity ) );
pev->nextthink = gpGlobals->time + m_flRepeat + 0.5f;
}
}
@ -1062,45 +1062,57 @@ void CScriptedSentence::DelayThink( void )
SetThink( &CScriptedSentence::FindThink );
}
BOOL CScriptedSentence::AcceptableSpeaker( CBaseMonster *pMonster )
BOOL CScriptedSentence::AcceptableSpeaker( CBaseToggle *pTarget )
{
if( pMonster )
CBaseMonster *pMonster;
EHANDLE hTarget;
if( pTarget )
{
if( pev->spawnflags & SF_SENTENCE_FOLLOWERS )
hTarget = pTarget->MyMonsterPointer();
if( hTarget != 0 )
{
if( pMonster->m_hTargetEnt == 0 || !pMonster->m_hTargetEnt->IsPlayer() )
return FALSE;
CBaseMonster *pMonster = (CBaseMonster*)( (CBaseEntity*)hTarget );
if( pev->spawnflags & SF_SENTENCE_FOLLOWERS )
{
if( pMonster->m_hTargetEnt == 0 || !pMonster->m_hTargetEnt->IsPlayer() )
return FALSE;
}
BOOL override;
if( pev->spawnflags & SF_SENTENCE_INTERRUPT )
override = TRUE;
else
override = FALSE;
if( pMonster->CanPlaySentence( override ) )
return TRUE;
}
BOOL override;
if( pev->spawnflags & SF_SENTENCE_INTERRUPT )
override = TRUE;
else
override = FALSE;
if( pMonster->CanPlaySentence( override ) )
return TRUE;
return pTarget->IsAllowedToSpeak();
}
return FALSE;
}
CBaseMonster *CScriptedSentence::FindEntity( void )
CBaseToggle *CScriptedSentence::FindEntity( void )
{
edict_t *pentTarget;
CBaseMonster *pMonster;
CBaseToggle *pTarget;
pentTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING( m_iszEntity ) );
pMonster = NULL;
pTarget = NULL;
while( !FNullEnt( pentTarget ) )
{
pMonster = GetMonsterPointer( pentTarget );
if( pMonster != NULL )
pTarget = (CBaseToggle*)CBaseEntity::Instance( pentTarget );
if( pTarget != NULL )
{
if( AcceptableSpeaker( pMonster ) )
return pMonster;
//ALERT( at_console, "%s (%s), not acceptable\n", STRING( pMonster->pev->classname ), STRING( pMonster->pev->targetname ) );
if( AcceptableSpeaker( pTarget ) )
return pTarget;
//ALERT( at_console, "%s (%s), not acceptable\n", STRING( pTarget->pev->classname ), STRING( pTarget->pev->targetname ) );
}
pentTarget = FIND_ENTITY_BY_TARGETNAME( pentTarget, STRING( m_iszEntity ) );
}
@ -1112,9 +1124,9 @@ CBaseMonster *CScriptedSentence::FindEntity( void )
{
if( FBitSet( pEntity->pev->flags, FL_MONSTER ) )
{
pMonster = pEntity->MyMonsterPointer();
if( AcceptableSpeaker( pMonster ) )
return pMonster;
pTarget = pEntity->MyTogglePointer();
if( AcceptableSpeaker( pTarget ) )
return pTarget;
}
}
}
@ -1122,7 +1134,7 @@ CBaseMonster *CScriptedSentence::FindEntity( void )
return NULL;
}
BOOL CScriptedSentence::StartSentence( CBaseMonster *pTarget )
BOOL CScriptedSentence::StartSentence( CBaseToggle *pTarget )
{
if( !pTarget )
{

View File

@ -35,6 +35,7 @@ extern int gmsgMOTD;
//=========================================================
CHalfLifeRules::CHalfLifeRules( void )
{
SERVER_COMMAND( "exec spserver.cfg\n" );
RefreshSkillData();
}
@ -94,10 +95,83 @@ BOOL CHalfLifeRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBasePlayerItem
return TRUE;
}
//=========================================================
//=========================================================
BOOL HLGetNextBestWeapon(CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon )
{
CBasePlayerItem *pCheck;
CBasePlayerItem *pBest;// this will be used in the event that we don't find a weapon in the same category.
int iBestWeight;
int i;
iBestWeight = -1;// no weapon lower than -1 can be autoswitched to
pBest = NULL;
if( !pCurrentWeapon->CanHolster() )
{
// can't put this gun away right now, so can't switch.
return FALSE;
}
for( i = 0; i < MAX_ITEM_TYPES; i++ )
{
pCheck = pPlayer->m_rgpPlayerItems[i];
while( pCheck )
{
if( !FBitSet( pCheck->iFlags(), ITEM_FLAG_NOCHOICE ))
{
if( pCheck->iWeight() > -1 && pCheck->iWeight() == pCurrentWeapon->iWeight() && pCheck != pCurrentWeapon )
{
// this weapon is from the same category.
if ( pCheck->CanDeploy() )
{
if ( pPlayer->SwitchWeapon( pCheck ) )
{
return TRUE;
}
}
}
else if( pCheck->iWeight() > iBestWeight && pCheck != pCurrentWeapon )// don't reselect the weapon we're trying to get rid of
{
//ALERT ( at_console, "Considering %s\n", STRING( pCheck->pev->classname ) );
// we keep updating the 'best' weapon just in case we can't find a weapon of the same weight
// that the player was using. This will end up leaving the player with his heaviest-weighted
// weapon.
if( pCheck->CanDeploy() )
{
// if this weapon is useable, flag it as the best
iBestWeight = pCheck->iWeight();
pBest = pCheck;
}
}
}
pCheck = pCheck->m_pNext;
}
}
// if we make it here, we've checked all the weapons and found no useable
// weapon in the same catagory as the current weapon.
// if pBest is null, we didn't find ANYTHING. Shouldn't be possible- should always
// at least get the crowbar, but ya never know.
if( !pBest )
{
return FALSE;
}
pPlayer->SwitchWeapon( pBest );
return TRUE;
}
//=========================================================
//=========================================================
BOOL CHalfLifeRules::GetNextBestWeapon( CBasePlayer *pPlayer, CBasePlayerItem *pCurrentWeapon )
{
if( pCurrentWeapon && FBitSet( pCurrentWeapon->iFlags(), ITEM_FLAG_EXHAUSTIBLE ))
return HLGetNextBestWeapon( pPlayer, pCurrentWeapon );
return FALSE;
}

View File

@ -1341,6 +1341,15 @@ void SENTENCEG_Init()
g_engfuncs.pfnFreeFile( pMemFile );
if( gcallsentences > CVOXFILESENTENCEMAX_GOLDSOURCE_ANNIVERSARY_25 )
{
ALERT( at_warning, "NOTE: this mod might not work properly under GoldSource (post-anniversary update) engine: more than %d sentences\n", CVOXFILESENTENCEMAX_GOLDSOURCE_ANNIVERSARY_25 );
}
else if( gcallsentences > CVOXFILESENTENCEMAX_GOLDSOURCE_LEGACY )
{
ALERT( at_warning, "NOTE: this mod might not work properly under GoldSource (pre-anniversary update) engine: more than %d sentences\n", CVOXFILESENTENCEMAX_GOLDSOURCE_LEGACY );
}
fSentencesInit = TRUE;
// init lru lists

View File

@ -339,7 +339,8 @@ void CSqueakGrenade::SuperBounceTouch( CBaseEntity *pOther )
// higher pitch as squeeker gets closer to detonation time
flpitch = 155.0f - 60.0f * ( ( m_flDie - gpGlobals->time ) / SQUEEK_DETONATE_DELAY );
if( pOther->pev->takedamage && m_flNextAttack < gpGlobals->time )
if( !FBitSet( pOther->pev->flags, FL_WORLDBRUSH )
&& pOther->pev->takedamage && m_flNextAttack < gpGlobals->time )
{
// attack!
@ -494,20 +495,35 @@ void CSqueak::PrimaryAttack()
{
if( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] )
{
UTIL_MakeVectors( m_pPlayer->pev->v_angle );
TraceResult tr;
Vector trace_origin;
Vector trace_origin, forward;
float flVel;
UTIL_MakeVectors( Vector( 0, m_pPlayer->pev->v_angle.y, m_pPlayer->pev->v_angle.z ));
forward = gpGlobals->v_forward;
UTIL_MakeVectors( m_pPlayer->pev->v_angle );
if( m_pPlayer->pev->v_angle.x <= 0 )
{
flVel = 1;
}
else
{
flVel = m_pPlayer->pev->v_angle.x / 90.0f;
}
// HACK HACK: Ugly hacks to handle change in origin based on new physics code for players
// Move origin up if crouched and start trace a bit outside of body ( 20 units instead of 16 )
trace_origin = m_pPlayer->pev->origin;
if( m_pPlayer->pev->flags & FL_DUCKING )
{
trace_origin = trace_origin - ( VEC_HULL_MIN - VEC_DUCK_HULL_MIN );
trace_origin = trace_origin - Vector( 0, 0, 1 ) * ( flVel + 1.0f ) * -18;
}
forward = forward * flVel + gpGlobals->v_forward * ( 1 - flVel );
// find place to toss monster
UTIL_TraceLine( trace_origin + gpGlobals->v_forward * 20.0f, trace_origin + gpGlobals->v_forward * 64.0f, dont_ignore_monsters, NULL, &tr );
UTIL_TraceLine( trace_origin + forward * 24.0f, trace_origin + gpGlobals->v_forward * 60.0f, dont_ignore_monsters, NULL, &tr );
int flags;
#if CLIENT_WEAPONS
@ -517,13 +533,13 @@ void CSqueak::PrimaryAttack()
#endif
PLAYBACK_EVENT_FULL( flags, m_pPlayer->edict(), m_usSnarkFire, 0.0f, g_vecZero, g_vecZero, 0.0f, 0.0f, 0, 0, 0, 0 );
if( tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0.25f )
if( tr.fAllSolid == 0 && tr.fStartSolid == 0 && tr.flFraction > 0 )
{
// player "shoot" animation
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
#if !CLIENT_DLL
CBaseEntity *pSqueak = CBaseEntity::Create( "monster_snark", tr.vecEndPos, m_pPlayer->pev->v_angle, m_pPlayer->edict() );
pSqueak->pev->velocity = gpGlobals->v_forward * 200.0f + m_pPlayer->pev->velocity;
pSqueak->pev->velocity = forward * 200.0f + m_pPlayer->pev->velocity;
#endif
// play hunt sound
float flRndSound = RANDOM_FLOAT( 0.0f, 1.0f );

View File

@ -520,6 +520,27 @@ float CBaseToggle::AxisDelta( int flags, const Vector &angle1, const Vector &ang
return angle1.y - angle2.y;
}
void CBaseToggle::PlaySentence( const char *pszSentence, float duration, float volume, float attenuation )
{
if( pszSentence && IsAllowedToSpeak())
{
if( pszSentence[0] == '!' )
EMIT_SOUND_DYN( edict(), CHAN_VOICE, pszSentence, volume, attenuation, 0, PITCH_NORM );
else
SENTENCEG_PlayRndSz( edict(), pszSentence, volume, attenuation, 0, PITCH_NORM );
}
}
void CBaseToggle::PlayScriptedSentence( const char *pszSentence, float duration, float volume, float attenuation, BOOL bConcurrent, CBaseEntity *pListener )
{
PlaySentence( pszSentence, duration, volume, attenuation );
}
void CBaseToggle::SentenceStop( void )
{
EMIT_SOUND( edict(), CHAN_VOICE, "common/null.wav", 1.0, ATTN_IDLE );
}
/*
=============
FEntIsVisible

View File

@ -122,4 +122,89 @@ public:
private:
unsigned short m_usAdjustPitch;
};
class CFuncVehicle: public CBaseEntity
{
public:
virtual void Spawn();
virtual void Precache();
virtual void Restart();
virtual void KeyValue( KeyValueData *pkvd );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
virtual int ObjectCaps() { return ( CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION ) | FCAP_DIRECTIONAL_USE; }
virtual int Classify();
virtual void OverrideReset();
virtual BOOL OnControls( entvars_t *pev );
virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual void Blocked( CBaseEntity *pOther );
public:
void EXPORT Next();
void EXPORT Find();
void EXPORT NearestPath();
void EXPORT DeadEnd();
void NextThink( float thinkTime, BOOL alwaysThink );
void CollisionDetection();
void TerrainFollowing();
void CheckTurning();
void SetTrack( CPathTrack *track ) { m_ppath = track->Nearest( pev->origin ); }
void SetControls( entvars_t *pevControls );
void StopSound();
void UpdateSound();
public:
static CFuncVehicle *Instance( edict_t *pent );
static TYPEDESCRIPTION m_SaveData[12];
CPathTrack *m_ppath;
float m_length;
float m_width;
float m_height;
float m_speed;
float m_dir;
float m_startSpeed;
Vector m_controlMins;
Vector m_controlMaxs;
int m_soundPlaying;
int m_sounds;
int m_acceleration;
float m_flVolume;
float m_flBank;
float m_oldSpeed;
int m_iTurnAngle;
float m_flSteeringWheelDecay;
float m_flAcceleratorDecay;
float m_flTurnStartTime;
float m_flLaunchTime;
float m_flLastNormalZ;
float m_flCanTurnNow;
float m_flUpdateSound;
Vector m_vFrontLeft;
Vector m_vFront;
Vector m_vFrontRight;
Vector m_vBackLeft;
Vector m_vBack;
Vector m_vBackRight;
Vector m_vSurfaceNormal;
Vector m_vVehicleDirection;
CBaseEntity *m_pDriver;
private:
unsigned short m_usAdjustPitch;
};
class CFuncVehicleControls: public CBaseEntity
{
public:
virtual void Spawn();
virtual int ObjectCaps() { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
public:
void EXPORT Find();
};
#endif

View File

@ -984,7 +984,12 @@ void CBaseTrigger::HurtTouch( CBaseEntity *pOther )
}
#endif
if( fldmg < 0 )
pOther->TakeHealth( -fldmg, m_bitsDamageInflict );
{
if( !( g_pGameRules->IsMultiplayer()
&& pOther->IsPlayer()
&& pOther->pev->deadflag ))
pOther->TakeHealth( -fldmg, m_bitsDamageInflict );
}
else
pOther->TakeDamage( pev, pev, fldmg, m_bitsDamageInflict );

View File

@ -2223,7 +2223,7 @@ int CRestore::ReadField( void *pBaseData, TYPEDESCRIPTION *pFields, int fieldCou
break;
case FIELD_EHANDLE:
// Input and Output sizes are different!
pOutputData = (char *)pOutputData + j * ( sizeof(EHANDLE) - gSizes[pTest->fieldType] );
pInputData = (char*)pData + j * gSizes[pTest->fieldType];
entityIndex = *(int *)pInputData;
pent = EntityFromIndex( entityIndex );
if( pent )

View File

@ -498,7 +498,11 @@ extern DLL_GLOBAL int g_Language;
// sentence groups
#define CBSENTENCENAME_MAX 16
#define CVOXFILESENTENCEMAX 2048 // max number of sentences in game. NOTE: this must match
#define CVOXFILESENTENCEMAX_GOLDSOURCE_LEGACY 1536
#define CVOXFILESENTENCEMAX_GOLDSOURCE_ANNIVERSARY_25 2048
#define CVOXFILESENTENCEMAX_XASH3D 4096
#define CVOXFILESENTENCEMAX CVOXFILESENTENCEMAX_XASH3D // max number of sentences in game. NOTE: this must match
// CVOXFILESENTENCEMAX in engine\sound.h!!!
extern char gszallsentencenames[CVOXFILESENTENCEMAX][CBSENTENCENAME_MAX];

996
dlls/vehicle.cpp Normal file
View File

@ -0,0 +1,996 @@
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "trains.h"
#include "saverestore.h"
#define VEHICLE_SPEED0_ACCELERATION 0.005000000000000000
#define VEHICLE_SPEED1_ACCELERATION 0.002142857142857143
#define VEHICLE_SPEED2_ACCELERATION 0.003333333333333334
#define VEHICLE_SPEED3_ACCELERATION 0.004166666666666667
#define VEHICLE_SPEED4_ACCELERATION 0.004000000000000000
#define VEHICLE_SPEED5_ACCELERATION 0.003800000000000000
#define VEHICLE_SPEED6_ACCELERATION 0.004500000000000000
#define VEHICLE_SPEED7_ACCELERATION 0.004250000000000000
#define VEHICLE_SPEED8_ACCELERATION 0.002666666666666667
#define VEHICLE_SPEED9_ACCELERATION 0.002285714285714286
#define VEHICLE_SPEED10_ACCELERATION 0.001875000000000000
#define VEHICLE_SPEED11_ACCELERATION 0.001444444444444444
#define VEHICLE_SPEED12_ACCELERATION 0.001200000000000000
#define VEHICLE_SPEED13_ACCELERATION 0.000916666666666666
#define VEHICLE_STARTPITCH 60
#define VEHICLE_MAXPITCH 200
#define VEHICLE_MAXSPEED 1500
TYPEDESCRIPTION CFuncVehicle::m_SaveData[] =
{
DEFINE_FIELD( CFuncVehicle, m_ppath, FIELD_CLASSPTR ),
DEFINE_FIELD( CFuncVehicle, m_length, FIELD_FLOAT ),
DEFINE_FIELD( CFuncVehicle, m_height, FIELD_FLOAT ),
DEFINE_FIELD( CFuncVehicle, m_speed, FIELD_FLOAT ),
DEFINE_FIELD( CFuncVehicle, m_dir, FIELD_FLOAT ),
DEFINE_FIELD( CFuncVehicle, m_startSpeed, FIELD_FLOAT ),
DEFINE_FIELD( CFuncVehicle, m_controlMins, FIELD_VECTOR ),
DEFINE_FIELD( CFuncVehicle, m_controlMaxs, FIELD_VECTOR ),
DEFINE_FIELD( CFuncVehicle, m_sounds, FIELD_INTEGER ),
DEFINE_FIELD( CFuncVehicle, m_flVolume, FIELD_FLOAT ),
DEFINE_FIELD( CFuncVehicle, m_flBank, FIELD_FLOAT ),
DEFINE_FIELD( CFuncVehicle, m_oldSpeed, FIELD_FLOAT ),
};
static float Fix2( float angle )
{
while( angle < 0 )
angle += 360;
while( angle > 360 )
angle -= 360;
return angle;
}
static void FixupAngles2( Vector &v )
{
v.x = Fix2( v.x );
v.y = Fix2( v.y );
v.z = Fix2( v.z );
}
IMPLEMENT_SAVERESTORE( CFuncVehicle, CBaseEntity )
LINK_ENTITY_TO_CLASS( func_vehicle, CFuncVehicle )
void CFuncVehicle::KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "length" ))
{
m_length = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "width" ))
{
m_width = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "height" ))
{
m_height = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "startspeed" ))
{
m_startSpeed = atof(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "sounds" ))
{
m_sounds = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "volume" ))
{
m_flVolume = (float)atoi( pkvd->szValue );
m_flVolume *= 0.1f;
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "bank" ))
{
m_flBank = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "acceleration" ))
{
m_acceleration = atoi( pkvd->szValue );
if( m_acceleration < 1 )
m_acceleration = 1;
else if( m_acceleration > 10 )
m_acceleration = 10;
pkvd->fHandled = TRUE;
}
else
CBaseEntity::KeyValue( pkvd );
}
void CFuncVehicle::NextThink( float thinkTime, BOOL alwaysThink )
{
if( alwaysThink )
pev->flags |= FL_ALWAYSTHINK;
else
pev->flags &= ~FL_ALWAYSTHINK;
pev->nextthink = thinkTime;
}
void CFuncVehicle::Blocked( CBaseEntity *pOther )
{
entvars_t *pevOther = pOther->pev;
if( ( pevOther->flags & FL_ONGROUND ) && VARS( pevOther->groundentity ) == pev )
{
pevOther->velocity = pev->velocity;
return;
}
pevOther->velocity = ( pevOther->origin - pev->origin ).Normalize() * pev->dmg;
pevOther->velocity.z += 300;
pev->velocity = pev->velocity * 0.85f;
ALERT( at_aiconsole, "TRAIN(%s): Blocked by %s (dmg:%.2f)\n", STRING( pev->targetname ), STRING( pOther->pev->classname ), pev->dmg );
UTIL_MakeVectors( pev->angles );
Vector forward, right, vOrigin;
Vector vFrontLeft = ( gpGlobals->v_forward * -1 ) * ( m_length * 0.5f );
Vector vFrontRight = ( gpGlobals->v_right * -1 ) * ( m_width * 0.5f );
Vector vBackLeft = pev->origin + vFrontLeft - vFrontRight;
Vector vBackRight = pev->origin - vFrontLeft + vFrontRight;
float minx = Q_min( vBackLeft.x, vBackRight.x );
float miny = Q_min( vBackLeft.y, vBackRight.y );
float maxx = Q_max( vBackLeft.x, vBackRight.x );
float maxy = Q_max( vBackLeft.y, vBackRight.y );
float minz = pev->origin.z;
float maxz = pev->origin.z + ( 2 * abs( (int)( pev->mins.z - pev->maxs.z )));
if ( pOther->pev->origin.x < minx
|| pOther->pev->origin.x > maxx
|| pOther->pev->origin.y < miny
|| pOther->pev->origin.y > maxy
|| pOther->pev->origin.z < pev->origin.z
|| pOther->pev->origin.z > maxz )
{
pOther->TakeDamage( pev, pev, 150, DMG_CRUSH );
}
}
void CFuncVehicle::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
float delta = value;
if( useType != USE_SET )
{
if( ShouldToggle( useType, pev->speed != 0 ))
{
if( pev->speed == 0 )
{
pev->speed = m_dir * m_speed;
Next();
}
else
{
pev->speed = 0;
pev->velocity = g_vecZero;
pev->avelocity = g_vecZero;
StopSound();
SetThink( NULL );
}
}
return;
}
if( delta < 10 )
{
if( delta < 0 )
{
if( pev->speed > 145 )
{
StopSound();
}
}
float flSpeedRatio = delta;
if( delta > 0 )
{
flSpeedRatio = (float)( pev->speed / m_speed );
if( pev->speed < 0 ) flSpeedRatio = m_acceleration * 0.0005 + flSpeedRatio + VEHICLE_SPEED0_ACCELERATION;
else if( pev->speed < 10 ) flSpeedRatio = m_acceleration * 0.0006 + flSpeedRatio + VEHICLE_SPEED1_ACCELERATION;
else if( pev->speed < 20 ) flSpeedRatio = m_acceleration * 0.0007 + flSpeedRatio + VEHICLE_SPEED2_ACCELERATION;
else if( pev->speed < 30 ) flSpeedRatio = m_acceleration * 0.0007 + flSpeedRatio + VEHICLE_SPEED3_ACCELERATION;
else if( pev->speed < 45 ) flSpeedRatio = m_acceleration * 0.0007 + flSpeedRatio + VEHICLE_SPEED4_ACCELERATION;
else if( pev->speed < 60 ) flSpeedRatio = m_acceleration * 0.0008 + flSpeedRatio + VEHICLE_SPEED5_ACCELERATION;
else if( pev->speed < 80 ) flSpeedRatio = m_acceleration * 0.0008 + flSpeedRatio + VEHICLE_SPEED6_ACCELERATION;
else if( pev->speed < 100 ) flSpeedRatio = m_acceleration * 0.0009 + flSpeedRatio + VEHICLE_SPEED7_ACCELERATION;
else if( pev->speed < 150 ) flSpeedRatio = m_acceleration * 0.0008 + flSpeedRatio + VEHICLE_SPEED8_ACCELERATION;
else if( pev->speed < 225 ) flSpeedRatio = m_acceleration * 0.0007 + flSpeedRatio + VEHICLE_SPEED9_ACCELERATION;
else if( pev->speed < 300 ) flSpeedRatio = m_acceleration * 0.0006 + flSpeedRatio + VEHICLE_SPEED10_ACCELERATION;
else if( pev->speed < 400 ) flSpeedRatio = m_acceleration * 0.0005 + flSpeedRatio + VEHICLE_SPEED11_ACCELERATION;
else if( pev->speed < 550 ) flSpeedRatio = m_acceleration * 0.0005 + flSpeedRatio + VEHICLE_SPEED12_ACCELERATION;
else if( pev->speed < 800 ) flSpeedRatio = m_acceleration * 0.0005 + flSpeedRatio + VEHICLE_SPEED13_ACCELERATION;
}
else if( delta < 0 )
{
flSpeedRatio = pev->speed / m_speed;
// TODO: fix float for test demo
if( flSpeedRatio > 0 ) flSpeedRatio = (float)flSpeedRatio - 0.0125f;
else if( flSpeedRatio <= 0 && flSpeedRatio > -0.05f ) flSpeedRatio = (float)flSpeedRatio - 0.0075f;
else if( flSpeedRatio <= 0.05f && flSpeedRatio > -0.1f ) flSpeedRatio = (float)flSpeedRatio - 0.01f;
else if( flSpeedRatio <= 0.15f && flSpeedRatio > -0.15f ) flSpeedRatio = (float)flSpeedRatio - 0.0125f;
else if( flSpeedRatio <= 0.15f && flSpeedRatio > -0.22f ) flSpeedRatio = (float)flSpeedRatio - 0.01375f;
else if( flSpeedRatio <= 0.22f && flSpeedRatio > -0.3f ) flSpeedRatio = (float)flSpeedRatio - 0.0175f;
else if( flSpeedRatio <= 0.3f ) flSpeedRatio = (float)flSpeedRatio - 0.0125f;
}
if( flSpeedRatio > 1 )
{
flSpeedRatio = 1;
}
else if( flSpeedRatio < -0.35f )
{
flSpeedRatio = -0.35f;
}
pev->speed = flSpeedRatio * m_speed;
Next();
m_flAcceleratorDecay = gpGlobals->time + 0.25f;
}
else if( m_flCanTurnNow < gpGlobals->time )
{
if( delta == 20 )
{
m_iTurnAngle++;
m_flSteeringWheelDecay = gpGlobals->time + 0.075f;
if (m_iTurnAngle > 8)
{
m_iTurnAngle = 8;
}
}
else if( delta == 30 )
{
m_iTurnAngle--;
m_flSteeringWheelDecay = gpGlobals->time + 0.075f;
if( m_iTurnAngle < -8 )
{
m_iTurnAngle = -8;
}
}
m_flCanTurnNow = gpGlobals->time + 0.05f;
}
}
void CFuncVehicle::StopSound()
{
if( m_soundPlaying && pev->noise )
{
unsigned short us_sound = ( (unsigned short)m_sounds & 0x0007 ) << 12;
unsigned short us_encode = us_sound;
PLAYBACK_EVENT_FULL( FEV_RELIABLE | FEV_UPDATE, edict(), m_usAdjustPitch, 0, g_vecZero, g_vecZero, 0, 0, us_encode, 0, 1, 0 );
}
m_soundPlaying = 0;
}
void CFuncVehicle::UpdateSound()
{
if( !pev->noise )
return;
float flpitch = VEHICLE_STARTPITCH + ( abs( (int)pev->speed ) * ( VEHICLE_MAXPITCH - VEHICLE_STARTPITCH ) / VEHICLE_MAXSPEED );
if( flpitch > 200 )
flpitch = 200;
if( !m_soundPlaying )
{
if( m_sounds < 5 )
{
EMIT_SOUND_DYN( ENT(pev), CHAN_ITEM, "plats/vehicle_brake1.wav", m_flVolume, ATTN_NORM, 0, PITCH_NORM );
}
EMIT_SOUND_DYN( ENT( pev ), CHAN_STATIC, STRING( pev->noise ), m_flVolume, ATTN_NORM, 0, (int)flpitch );
m_soundPlaying = 1;
}
else
{
unsigned short us_sound = ( (unsigned short)( m_sounds ) & 0x0007 ) << 12;
unsigned short us_pitch = ( (unsigned short)( flpitch / 10.0 ) & 0x003F ) << 6;
unsigned short us_volume = ( (unsigned short)( m_flVolume * 40 ) & 0x003F );
unsigned short us_encode = us_sound | us_pitch | us_volume;
PLAYBACK_EVENT_FULL( FEV_UPDATE, edict(), m_usAdjustPitch, 0.0, g_vecZero, g_vecZero, 0.0, 0.0, us_encode, 0, 0, 0 );
}
}
void CFuncVehicle::CheckTurning()
{
float maxspeed;
TraceResult tr;
bool bTurnIntoWall = false;
if( m_iTurnAngle < 0 )
{
if( pev->speed > 0 )
{
UTIL_TraceLine( m_vFrontRight, m_vFrontRight - ( gpGlobals->v_right * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr );
}
else if( pev->speed < 0 )
{
UTIL_TraceLine( m_vBackLeft, m_vBackLeft + ( gpGlobals->v_right * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr );
}
if( tr.flFraction != 1.0f )
{
m_iTurnAngle = 1;
}
}
else if( m_iTurnAngle > 0 )
{
if( pev->speed > 0 )
{
UTIL_TraceLine( m_vFrontLeft, m_vFrontLeft + ( gpGlobals->v_right * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr );
}
else if( pev->speed < 0 )
{
UTIL_TraceLine( m_vBackRight, m_vBackRight - ( gpGlobals->v_right * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr );
}
if( tr.flFraction != 1.0f )
{
m_iTurnAngle = -1;
}
}
if( pev->speed > 0 )
{
int iCountTurn = abs( m_iTurnAngle );
if( iCountTurn > 4 )
{
if ( m_flTurnStartTime != -1 )
{
float flTurnTime = gpGlobals->time - m_flTurnStartTime;
if( flTurnTime >= 0 ) maxspeed = m_speed * 0.98f;
else if( flTurnTime > 0.3f ) maxspeed = m_speed * 0.95f;
else if( flTurnTime > 0.6f ) maxspeed = m_speed * 0.9f;
else if( flTurnTime > 0.8f ) maxspeed = m_speed * 0.8f;
else if( flTurnTime > 1 ) maxspeed = m_speed * 0.7f;
else if( flTurnTime > 1.2f ) maxspeed = m_speed * 0.5f;
else maxspeed = flTurnTime;
}
else
{
m_flTurnStartTime = gpGlobals->time;
maxspeed = m_speed;
}
}
else
{
m_flTurnStartTime = -1;
if( iCountTurn > 2 )
maxspeed = m_speed * 0.9f;
else
maxspeed = m_speed;
}
if( maxspeed < pev->speed )
{
pev->speed -= m_speed * 0.1f;
}
}
}
void CFuncVehicle::CollisionDetection()
{
TraceResult tr;
bool bHitSomething = false;
if( pev->speed < 0 )
{
UTIL_TraceLine( m_vBackLeft, m_vBackLeft + ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr );
if( tr.flFraction == 1.0f )
{
UTIL_TraceLine( m_vBackRight, m_vBackRight + ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr );
if( tr.flFraction == 1.0f )
{
UTIL_TraceLine( m_vBack, m_vBack + ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr );
if( tr.flFraction == 1.0f )
{
return;
}
}
if( DotProduct( gpGlobals->v_forward, tr.vecPlaneNormal * -1.0f ) < 0.7f && tr.vecPlaneNormal.z < 0.1f )
{
m_vSurfaceNormal = tr.vecPlaneNormal;
m_vSurfaceNormal.z = 0;
pev->speed *= 0.99f;
}
else if( tr.vecPlaneNormal.z < 0.65f || tr.fStartSolid )
{
pev->speed *= -1.0f;
}
else
{
m_vSurfaceNormal = tr.vecPlaneNormal;
}
}
else
{
if( DotProduct( gpGlobals->v_forward, tr.vecPlaneNormal * -1.0f ) < 0.7f && tr.vecPlaneNormal.z < 0.1f )
{
m_vSurfaceNormal = tr.vecPlaneNormal;
m_vSurfaceNormal.z = 0;
pev->speed *= 0.99f;
}
else if( tr.vecPlaneNormal[2] < 0.65f || tr.fStartSolid )
{
pev->speed *= -1.0f;
}
else
{
m_vSurfaceNormal = tr.vecPlaneNormal;
}
CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit );
if( pHit && pHit->Classify() == CLASS_VEHICLE )
{
bHitSomething = true;
ALERT( at_console, "I hit another vehicle\n" );
}
}
}
else if( pev->speed > 0 )
{
UTIL_TraceLine( m_vFrontLeft, m_vFrontLeft - ( gpGlobals->v_forward * 16.0f ), dont_ignore_monsters, dont_ignore_glass, ENT( pev ), &tr );
if( tr.flFraction == 1.0f )
{
UTIL_TraceLine( m_vFrontRight, m_vFrontRight - ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr );
if( tr.flFraction == 1.0f )
{
UTIL_TraceLine( m_vFront, m_vFront - ( gpGlobals->v_forward * 16.0f ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr );
if( tr.flFraction == 1.0f )
{
return;
}
}
}
if( DotProduct( gpGlobals->v_forward, tr.vecPlaneNormal * -1.0f ) > -0.7f && tr.vecPlaneNormal.z < 0.1f )
{
m_vSurfaceNormal = tr.vecPlaneNormal;
m_vSurfaceNormal.z = 0;
pev->speed *= 0.99f;
}
else if( tr.vecPlaneNormal.z < 0.65f || tr.fStartSolid )
{
pev->speed *= -1.0f;
}
else
{
m_vSurfaceNormal = tr.vecPlaneNormal;
}
}
}
void CFuncVehicle::TerrainFollowing()
{
TraceResult tr;
UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, ( m_height + 48 ) * -1 ), ignore_monsters, dont_ignore_glass, ENT( pev ), &tr );
if( tr.flFraction != 1.0f )
{
m_vSurfaceNormal = tr.vecPlaneNormal;
}
else if( tr.fInWater )
{
m_vSurfaceNormal = Vector( 0, 0, 1 );
}
}
void CFuncVehicle::Next()
{
Vector vGravityVector, forward, right, up;
float time = 0.1f;
vGravityVector = g_vecZero;
UTIL_MakeVectors( pev->angles );
forward = ( gpGlobals->v_forward * -1 ) * ( m_length * 0.5f );
right = ( gpGlobals->v_right * -1 ) * ( m_width * 0.5f );
up = gpGlobals->v_up * 16;
m_vFrontLeft = pev->origin + forward - right + up;
m_vFrontRight = pev->origin + forward + right + up;
m_vFront = pev->origin + forward + up;
m_vBackLeft = pev->origin - forward - right + up;
m_vBackRight = pev->origin - forward + right + up;
m_vBack = pev->origin - forward + up;
m_vSurfaceNormal = g_vecZero;
CheckTurning();
if( m_flSteeringWheelDecay < gpGlobals->time )
{
m_flSteeringWheelDecay = gpGlobals->time + 0.1f;
if( m_iTurnAngle < 0 )
m_iTurnAngle++;
else if( m_iTurnAngle > 0 )
m_iTurnAngle--;
}
if( m_flAcceleratorDecay < gpGlobals->time )
{
m_flAcceleratorDecay = gpGlobals->time + 0.1f;
if( pev->speed < 0 )
{
pev->speed += 20;
if( pev->speed > 0 )
pev->speed = 0;
}
else if( pev->speed > 0 )
{
pev->speed -= 20;
if( pev->speed < 0 )
pev->speed = 0;
}
}
if( pev->speed == 0 )
{
m_iTurnAngle = 0;
pev->avelocity = g_vecZero;
pev->velocity = g_vecZero;
SetThink( &CFuncVehicle::Next );
NextThink( pev->ltime + time, TRUE );
return;
}
TerrainFollowing();
CollisionDetection();
Vector temp;
if( m_vSurfaceNormal != g_vecZero )
{
Vector vTargetAngle, vAngle;
float vx;
float vy;
m_vVehicleDirection = CrossProduct( m_vSurfaceNormal, gpGlobals->v_forward );
m_vVehicleDirection = CrossProduct( m_vSurfaceNormal, m_vVehicleDirection );
vTargetAngle = UTIL_VecToAngles( m_vVehicleDirection );
vAngle = pev->angles;
vTargetAngle.y += 180;
if( m_iTurnAngle != 0 )
{
vTargetAngle.y += m_iTurnAngle;
}
FixupAngles2( vTargetAngle );
FixupAngles2( vAngle );
vx = UTIL_AngleDistance( vTargetAngle.x, vAngle.x );
vy = UTIL_AngleDistance( vTargetAngle.y, vAngle.y );
if( vx > 10 )
vx = 10;
else if( vx < -10 )
vx = -10;
if( vy > 10 )
vy = 10;
else if( vy < -10 )
vy = -10;
pev->avelocity.y = (int)( vy * 10 );
pev->avelocity.x = (int)( vx * 10 );
m_flLaunchTime = -1;
m_flLastNormalZ = m_vSurfaceNormal.z;
}
else
{
if( m_flLaunchTime != -1 )
{
vGravityVector.x = 0;
vGravityVector.y = 0;
vGravityVector.z = ( gpGlobals->time - m_flLaunchTime ) * -35;
if( vGravityVector.z < -400 )
{
vGravityVector.z = -400;
}
}
else
{
m_flLaunchTime = gpGlobals->time;
vGravityVector = Vector( 0, 0, 0 );
pev->velocity = pev->velocity * 1.5f;
}
m_vVehicleDirection = gpGlobals->v_forward * -1;
}
UTIL_VecToAngles( m_vVehicleDirection );
if( m_flUpdateSound < gpGlobals->time )
{
UpdateSound();
m_flUpdateSound = gpGlobals->time + 1.0f;
}
if( m_vSurfaceNormal != g_vecZero )
{
pev->velocity = m_vVehicleDirection.Normalize() * pev->speed;
}
else
{
pev->velocity = pev->velocity + vGravityVector;
}
SetThink( &CFuncVehicle::Next );
NextThink( pev->ltime + time, TRUE );
}
void CFuncVehicle::DeadEnd()
{
CPathTrack *pTrack = m_ppath;
ALERT( at_aiconsole, "TRAIN(%s): Dead end ", STRING( pev->targetname ));
if( pTrack != NULL )
{
CPathTrack *pNext;
if( m_oldSpeed < 0 )
{
do
{
pNext = pTrack->ValidPath( pTrack->GetPrevious(), TRUE );
if( pNext != NULL )
{
pTrack = pNext;
}
}
while( pNext != NULL );
}
else
{
do
{
pNext = pTrack->ValidPath( pTrack->GetNext(), TRUE );
if( pNext != NULL )
{
pTrack = pNext;
}
}
while( pNext != NULL );
}
}
pev->velocity = g_vecZero;
pev->avelocity = g_vecZero;
if( pTrack != NULL )
{
ALERT( at_aiconsole, "at %s\n", STRING( pTrack->pev->targetname ));
if( !FStringNull( pTrack->pev->netname ))
{
FireTargets( STRING( pTrack->pev->netname ), this, this, USE_TOGGLE, 0 );
}
}
else
ALERT( at_aiconsole, "\n" );
}
void CFuncVehicle::SetControls(entvars_t *pevControls)
{
Vector offset = pevControls->origin - pev->oldorigin;
m_controlMins = pevControls->mins + offset;
m_controlMaxs = pevControls->maxs + offset;
}
BOOL CFuncVehicle::OnControls(entvars_t *pevTest)
{
if( pev->spawnflags & SF_TRACKTRAIN_NOCONTROL )
return FALSE;
Vector offset = pevTest->origin - pev->origin;
UTIL_MakeVectors( pev->angles );
Vector local;
local.x = DotProduct( offset, gpGlobals->v_forward );
local.y = -DotProduct( offset, gpGlobals->v_right );
local.z = DotProduct( offset, gpGlobals->v_up );
return ( local.x >= m_controlMins.x && local.y >= m_controlMins.y && local.z >= m_controlMins.z
&& local.x <= m_controlMaxs.x && local.y <= m_controlMaxs.y && local.z <= m_controlMaxs.z );
}
void CFuncVehicle::Find()
{
m_ppath = CPathTrack::Instance( FIND_ENTITY_BY_TARGETNAME( NULL, STRING( pev->target )));
if( !m_ppath )
return;
entvars_t *pevTarget = m_ppath->pev;
if( !FClassnameIs( pevTarget, "path_track" ))
{
ALERT( at_error, "func_track_train must be on a path of path_track\n" );
m_ppath = NULL;
return;
}
Vector nextPos = pevTarget->origin;
nextPos.z += m_height;
Vector look = nextPos;
look.z -= m_height;
m_ppath->LookAhead( &look, m_length, 0 );
look.z += m_height;
pev->angles = UTIL_VecToAngles( look - nextPos );
pev->angles.y += 180;
if( pev->spawnflags & SF_TRACKTRAIN_NOPITCH )
{
pev->angles.x = 0;
}
UTIL_SetOrigin( pev, nextPos );
NextThink( pev->ltime + 0.1f, FALSE );
SetThink( &CFuncVehicle::Next );
pev->speed = m_startSpeed;
UpdateSound();
}
void CFuncVehicle::NearestPath()
{
CBaseEntity *pTrack = NULL;
CBaseEntity *pNearest = NULL;
float dist;
float closest = 1024;
while( ( pTrack = UTIL_FindEntityInSphere( pTrack, pev->origin, 1024 )) != NULL )
{
if( !( pTrack->pev->flags & ( FL_CLIENT | FL_MONSTER )) && FClassnameIs( pTrack->pev, "path_track" ))
{
dist = ( pev->origin - pTrack->pev->origin ).Length();
if( dist < closest )
{
closest = dist;
pNearest = pTrack;
}
}
}
if( !pNearest )
{
ALERT( at_console, "Can't find a nearby track !!!\n" );
SetThink( NULL );
return;
}
ALERT( at_aiconsole, "TRAIN: %s, Nearest track is %s\n", STRING( pev->targetname ), STRING( pNearest->pev->targetname ));
pTrack = ( (CPathTrack *)pNearest )->GetNext();
if( pTrack != NULL )
{
if( ( pev->origin - pTrack->pev->origin ).Length() < ( pev->origin - pNearest->pev->origin ).Length())
{
pNearest = pTrack;
}
}
m_ppath = (CPathTrack *)pNearest;
if( pev->speed != 0 )
{
NextThink( pev->ltime + 0.1f, FALSE );
SetThink( &CFuncVehicle::Next );
}
}
void CFuncVehicle::OverrideReset()
{
NextThink( pev->ltime + 0.1f, FALSE );
SetThink( &CFuncVehicle::NearestPath );
}
CFuncVehicle *CFuncVehicle::Instance(edict_t *pent)
{
if( FClassnameIs( pent, "func_vehicle" ))
{
return (CFuncVehicle *)GET_PRIVATE( pent );
}
return NULL;
}
int CFuncVehicle::Classify()
{
return CLASS_VEHICLE;
}
void CFuncVehicle::Spawn()
{
if( pev->speed == 0 )
m_speed = 165;
else
m_speed = pev->speed;
if( !m_sounds )
m_sounds = 3;
ALERT( at_console, "M_speed = %f\n", m_speed );
pev->speed = 0;
pev->velocity = g_vecZero;
pev->avelocity = g_vecZero;
pev->impulse = (int)m_speed;
m_acceleration = 5;
m_dir = 1;
m_flTurnStartTime = -1;
if( FStringNull( pev->target ))
{
ALERT( at_console, "Vehicle with no target" );
}
if( pev->spawnflags & SF_TRACKTRAIN_PASSABLE )
pev->solid = SOLID_NOT;
else
pev->solid = SOLID_BSP;
pev->movetype = MOVETYPE_PUSH;
SET_MODEL( ENT( pev ), STRING(pev->model ));
UTIL_SetSize( pev, pev->mins, pev->maxs );
UTIL_SetOrigin( pev, pev->origin );
pev->oldorigin = pev->origin;
m_controlMins = pev->mins;
m_controlMaxs = pev->maxs;
m_controlMaxs.z += 72;
NextThink( pev->ltime + 0.1f, FALSE );
SetThink( &CFuncVehicle::Find );
Precache();
}
void CFuncVehicle::Restart()
{
ALERT( at_console, "M_speed = %f\n", m_speed );
pev->speed = 0;
pev->velocity = g_vecZero;
pev->avelocity = g_vecZero;
pev->impulse = (int)m_speed;
m_flTurnStartTime = -1;
m_flUpdateSound = -1;
m_dir = 1;
m_pDriver = NULL;
if( FStringNull( pev->target ))
{
ALERT( at_console, "Vehicle with no target" );
}
UTIL_SetOrigin( pev, pev->oldorigin );
STOP_SOUND( ENT( pev ), CHAN_STATIC, STRING( pev->noise ));
NextThink( pev->ltime + 0.1f, FALSE );
SetThink( &CFuncVehicle::Find );
}
void CFuncVehicle::Precache()
{
if( m_flVolume == 0.0f )
m_flVolume = 1.0f;
switch( m_sounds )
{
case 1: PRECACHE_SOUND( "plats/vehicle1.wav" );pev->noise = MAKE_STRING( "plats/vehicle1.wav" ); break;
case 2: PRECACHE_SOUND( "plats/vehicle2.wav" );pev->noise = MAKE_STRING( "plats/vehicle2.wav" ); break;
case 3: PRECACHE_SOUND( "plats/vehicle3.wav" );pev->noise = MAKE_STRING( "plats/vehicle3.wav" ); break;
case 4: PRECACHE_SOUND( "plats/vehicle4.wav" );pev->noise = MAKE_STRING( "plats/vehicle4.wav" ); break;
case 5: PRECACHE_SOUND( "plats/vehicle6.wav" );pev->noise = MAKE_STRING( "plats/vehicle6.wav" ); break;
case 6: PRECACHE_SOUND( "plats/vehicle7.wav" );pev->noise = MAKE_STRING( "plats/vehicle7.wav" ); break;
}
PRECACHE_SOUND( "plats/vehicle_brake1.wav" );
PRECACHE_SOUND( "plats/vehicle_start1.wav" );
m_usAdjustPitch = PRECACHE_EVENT( 1, "events/vehicle.sc" );
}
LINK_ENTITY_TO_CLASS( func_vehiclecontrols, CFuncVehicleControls );
void CFuncVehicleControls::Find()
{
edict_t *pTarget = NULL;
do
{
pTarget = FIND_ENTITY_BY_TARGETNAME( pTarget, STRING( pev->target ));
}
while( !FNullEnt( pTarget ) && !FClassnameIs( pTarget, "func_vehicle" ));
if( FNullEnt( pTarget ))
{
ALERT( at_console, "No vehicle %s\n", STRING( pev->target ));
return;
}
CFuncVehicle *pvehicle = CFuncVehicle::Instance( pTarget );
pvehicle->SetControls( pev );
UTIL_Remove( this );
}
void CFuncVehicleControls::Spawn()
{
pev->solid = SOLID_NOT;
pev->movetype = MOVETYPE_NONE;
SET_MODEL( ENT( pev ), STRING( pev->model ));
UTIL_SetSize( pev, pev->mins, pev->maxs );
UTIL_SetOrigin( pev, pev->origin );
SetThink( &CFuncVehicleControls::Find );
pev->nextthink = gpGlobals->time;
}

View File

@ -471,6 +471,19 @@ void CBasePlayerItem::FallThink( void )
Materialize();
}
else if( m_pPlayer )
{
SetThink( NULL );
}
if( g_pGameRules->IsBustingGame())
{
if( !FNullEnt( pev->owner ))
return;
if( FClassnameIs( pev, "weapon_egon" ))
UTIL_Remove( this );
}
}
//=========================================================
@ -1076,7 +1089,7 @@ void CBasePlayerAmmo::Materialize( void )
void CBasePlayerAmmo::DefaultTouch( CBaseEntity *pOther )
{
if( !pOther->IsPlayer() )
if( !pOther->IsPlayer() || IsPlayerBusting( pOther ))
{
return;
}

View File

@ -37,7 +37,7 @@ public:
static void UseSatchelCharges( entvars_t *pevOwner, SATCHELCODE code );
void Explode( Vector vecSrc, Vector vecAim );
void Explode( TraceResult *pTrace, int bitsDamageType );
virtual void Explode( TraceResult *pTrace, int bitsDamageType );
void EXPORT Smoke( void );
void EXPORT BounceTouch( CBaseEntity *pOther );
@ -95,7 +95,7 @@ public:
#define RPG_WEIGHT 20
#define GAUSS_WEIGHT 20
#define EGON_WEIGHT 20
#define HORNETGUN_WEIGHT 10
#define HORNETGUN_WEIGHT 15
#define HANDGRENADE_WEIGHT 5
#define SNARK_WEIGHT 5
#define SATCHEL_WEIGHT -10
@ -183,6 +183,7 @@ typedef enum
#define ITEM_FLAG_NOAUTOSWITCHEMPTY 4
#define ITEM_FLAG_LIMITINWORLD 8
#define ITEM_FLAG_EXHAUSTIBLE 16 // A player can totally exhaust their ammo supply and lose this weapon
#define ITEM_FLAG_NOCHOICE 32
#define WEAPON_IS_ONTARGET 0x40
@ -723,6 +724,8 @@ public:
void EXPORT IgniteThink( void );
void EXPORT RocketTouch( CBaseEntity *pOther );
static CRpgRocket *CreateRpgRocket( Vector vecOrigin, Vector vecAngles, CBaseEntity *pOwner, CRpg *pLauncher );
void Explode( TraceResult *pTrace, int bitsDamageType );
inline CRpg *GetLauncher( void );
int m_iTrail;
float m_flIgniteTime;
@ -791,13 +794,13 @@ public:
int AddToPlayer( CBasePlayer *pPlayer );
BOOL Deploy( void );
BOOL CanHolster( void );
void Holster( int skiplocal = 0 );
void UpdateEffect( const Vector &startPoint, const Vector &endPoint, float timeBlend );
void CreateEffect ( void );
void DestroyEffect ( void );
void EndAttack( void );
void Attack( void );
void PrimaryAttack( void );

View File

@ -226,7 +226,7 @@ static void InitBodyQue( void )
//
void CopyToBodyQue( entvars_t *pev )
{
if( pev->effects & EF_NODRAW )
if( ( pev->effects & EF_NODRAW ) || !pev->modelindex )
return;
entvars_t *pevHead = VARS( g_pBodyQueueHead );

View File

@ -7,6 +7,7 @@ GAUSS_OVERCHARGE_FIX=OFF # Gauss overcharge fix
TRIPMINE_BEAM_DUPLICATION_FIX=OFF # Fix of tripmine beam duplication on level transition
HANDGRENADE_DEPLOY_FIX=OFF # Handgrenade deploy animation fix after finishing a throw
WEAPONS_ANIMATION_TIMES_FIX=OFF # Animation times fix for some weapons
SATCHEL_OLD_BEHAVIOUR=OFF # Old pre-HL 25th satchel's behaviour
OEM_BUILD=OFF # OEM Build
HLDEMO_BUILD=OFF # Demo Build

View File

@ -167,7 +167,7 @@ void PM_SortTextures( void )
}
}
void PM_InitTextureTypes()
void PM_InitTextureTypes( void )
{
char buffer[512];
int i, j;
@ -718,7 +718,7 @@ PM_CheckVelocity
See if the player has a bogus velocity value.
================
*/
void PM_CheckVelocity()
void PM_CheckVelocity( void )
{
int i;
@ -795,7 +795,7 @@ int PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce )
return blocked;
}
void PM_AddCorrectGravity()
void PM_AddCorrectGravity( void )
{
float ent_gravity;
@ -816,7 +816,7 @@ void PM_AddCorrectGravity()
PM_CheckVelocity();
}
void PM_FixupGravityVelocity()
void PM_FixupGravityVelocity( void )
{
float ent_gravity;
@ -1070,7 +1070,7 @@ PM_WalkMove
Only used by players. Moves along the ground when player is a MOVETYPE_WALK.
======================
*/
void PM_WalkMove()
void PM_WalkMove( void )
{
//int clip;
int oldonground;
@ -1511,7 +1511,7 @@ PM_CheckWater
Sets pmove->waterlevel and pmove->watertype values.
=============
*/
qboolean PM_CheckWater()
qboolean PM_CheckWater( void )
{
vec3_t point;
int cont;
@ -1699,7 +1699,7 @@ int PM_CheckStuck( void )
//
// Deal with precision error in network.
//
if( !pmove->server )
if( !( pmove->server && pmove->multiplayer ))
{
// World or BSP model
if( ( hitent == 0 ) || ( pmove->physents[hitent].model != NULL ) )
@ -2259,7 +2259,7 @@ PM_AddGravity
============
*/
void PM_AddGravity()
void PM_AddGravity( void )
{
float ent_gravity;
@ -2309,7 +2309,7 @@ PM_Physics_Toss()
Dead player flying through air., e.g.
============
*/
void PM_Physics_Toss()
void PM_Physics_Toss( void )
{
pmtrace_t trace;
vec3_t move;
@ -2410,7 +2410,7 @@ PM_NoClip
====================
*/
void PM_NoClip()
void PM_NoClip( void )
{
int i;
vec3_t wishvel;
@ -2570,13 +2570,17 @@ void PM_Jump( void )
if( !bunnyjump )
PM_PreventMegaBunnyJumping();
if( tfc )
// Don't play jump sounds while frozen.
if( !( pmove->flags & FL_FROZEN ))
{
pmove->PM_PlaySound( CHAN_BODY, "player/plyrjmp8.wav", 0.5, ATTN_NORM, 0, PITCH_NORM );
}
else
{
PM_PlayStepSound( PM_MapTextureTypeStepType( pmove->chtexturetype ), 1.0f );
if( tfc )
{
pmove->PM_PlaySound( CHAN_BODY, "player/plyrjmp8.wav", 0.5, ATTN_NORM, 0, PITCH_NORM );
}
else
{
PM_PlayStepSound( PM_MapTextureTypeStepType( pmove->chtexturetype ), 1.0f );
}
}
// See if user can super long jump?

View File

@ -69,7 +69,6 @@ Then you can use another oneliner to query all variables:
#undef XASH_IRIX
#undef XASH_JS
#undef XASH_LINUX
#undef XASH_LINUX_UNKNOWN
#undef XASH_LITTLE_ENDIAN
#undef XASH_MIPS
#undef XASH_MOBILE_PLATFORM
@ -103,13 +102,6 @@ Then you can use another oneliner to query all variables:
#if defined __linux__
#if defined __ANDROID__
#define XASH_ANDROID 1
#else
#include <features.h>
// if our system libc has features.h header
// try to detect it to not confuse other libcs with built with glibc game libraries
#if !defined __GLIBC__
#define XASH_LINUX_UNKNOWN 1
#endif
#endif
#define XASH_LINUX 1
#elif defined __FreeBSD__

View File

@ -44,7 +44,6 @@ DEFINES = [
'XASH_IRIX',
'XASH_JS',
'XASH_LINUX',
'XASH_LINUX_UNKNOWN',
'XASH_LITTLE_ENDIAN',
'XASH_MIPS',
'XASH_MOBILE_PLATFORM',
@ -76,8 +75,6 @@ def configure(conf):
# engine/common/build.c
if conf.env.XASH_ANDROID:
buildos = "android"
elif conf.env.XASH_LINUX_UNKNOWN:
buildos = "linuxunkabi"
elif conf.env.XASH_WIN32 or conf.env.XASH_LINUX or conf.env.XASH_APPLE:
buildos = "" # no prefix for default OS
elif conf.env.XASH_FREEBSD: